lower ops directly

This commit is contained in:
2026-02-18 21:18:18 -06:00
parent a05d0e2525
commit 27ca008f18
5 changed files with 482 additions and 20 deletions

View File

@@ -41,6 +41,28 @@ var mcode = function(ast) {
length: "length"
}
// Numeric intrinsic lowering maps (Tier 1 direct mcode).
var intrinsic_num_unary_ops = {
abs: "abs",
sign: "sign",
fraction: "fraction",
integer: "integer",
whole: "integer",
neg: "negate"
}
var intrinsic_num_binary_ops = {
modulo: "modulo",
remainder: "remainder",
max: "max",
min: "min"
}
var intrinsic_num_place_ops = {
floor: "floor",
ceiling: "ceiling",
round: "round",
trunc: "trunc"
}
// Compiler state
var s_instructions = null
var s_data = null
@@ -877,6 +899,56 @@ var mcode = function(ast) {
}
}
// Intrinsic numeric helpers:
// preserve native intrinsic behavior for bad argument types by returning null.
var emit_intrinsic_num_unary = function(op, arg_slot) {
var dest = alloc_slot()
var t = alloc_slot()
var bad = gen_label(op + "_arg_bad")
var done = gen_label(op + "_arg_done")
emit_2("is_num", t, arg_slot)
emit_jump_cond("jump_false", t, bad)
emit_2(op, dest, arg_slot)
emit_jump(done)
emit_label(bad)
emit_1("null", dest)
emit_label(done)
return dest
}
var emit_intrinsic_num_binary = function(op, left_slot, right_slot) {
var dest = alloc_slot()
var t0 = alloc_slot()
var t1 = alloc_slot()
var bad = gen_label(op + "_arg_bad")
var done = gen_label(op + "_arg_done")
emit_2("is_num", t0, left_slot)
emit_jump_cond("jump_false", t0, bad)
emit_2("is_num", t1, right_slot)
emit_jump_cond("jump_false", t1, bad)
emit_3(op, dest, left_slot, right_slot)
emit_jump(done)
emit_label(bad)
emit_1("null", dest)
emit_label(done)
return dest
}
var emit_intrinsic_num_place = function(op, value_slot, place_slot) {
var dest = alloc_slot()
var t = alloc_slot()
var bad = gen_label(op + "_arg_bad")
var done = gen_label(op + "_arg_done")
emit_2("is_num", t, value_slot)
emit_jump_cond("jump_false", t, bad)
emit_3(op, dest, value_slot, place_slot)
emit_jump(done)
emit_label(bad)
emit_1("null", dest)
emit_label(done)
return dest
}
// Scan scope record for variable declarations
var scan_scope = function() {
var scope = find_scope_record(s_function_nr)
@@ -1796,6 +1868,28 @@ var mcode = function(ast) {
if (callee_kind == "name" && callee.intrinsic == true) {
fname = callee.name
nargs = args_list != null ? length(args_list) : 0
mop = intrinsic_num_unary_ops[fname]
if (mop != null && nargs == 1) {
a0 = gen_expr(args_list[0], -1)
return emit_intrinsic_num_unary(mop, a0)
}
mop = intrinsic_num_binary_ops[fname]
if (mop != null && nargs == 2) {
a0 = gen_expr(args_list[0], -1)
a1 = gen_expr(args_list[1], -1)
return emit_intrinsic_num_binary(mop, a0, a1)
}
mop = intrinsic_num_place_ops[fname]
if (mop != null && (nargs == 1 || nargs == 2)) {
a0 = gen_expr(args_list[0], -1)
if (nargs == 2) {
a1 = gen_expr(args_list[1], -1)
} else {
a1 = alloc_slot()
emit_1("null", a1)
}
return emit_intrinsic_num_place(mop, a0, a1)
}
// 1-arg type check intrinsics → direct opcode
if (nargs == 1 && sensory_ops[fname] != null) {
a0 = gen_expr(args_list[0], -1)

View File

@@ -1730,9 +1730,192 @@ var qbe_emit = function(ir, qbe, export_name) {
p = fresh()
lhs_d = emit_num_to_double(lhs)
rhs_d = emit_num_to_double(rhs)
emit(` %${p}_rd =d call $fmod(d ${lhs_d}, d ${rhs_d})`)
emit(` %${p}_lhs_nan =w cned ${lhs_d}, ${lhs_d}`)
emit(` %${p}_rhs_nan =w cned ${rhs_d}, ${rhs_d}`)
emit(` %${p}_has_nan =w or %${p}_lhs_nan, %${p}_rhs_nan`)
emit(` jnz %${p}_has_nan, @${p}_bad, @${p}_chk0`)
emit(`@${p}_chk0`)
emit(` %${p}_rhs0 =w ceqd ${rhs_d}, d_0.0`)
emit(` jnz %${p}_rhs0, @${p}_bad, @${p}_calc`)
emit(`@${p}_calc`)
emit(` %${p}_q =d div ${lhs_d}, ${rhs_d}`)
emit(` %${p}_qf =d call $floor(d %${p}_q)`)
emit(` %${p}_m =d mul ${rhs_d}, %${p}_qf`)
emit(` %${p}_rd =d sub ${lhs_d}, %${p}_m`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
emit(` jmp @${p}_done`)
emit(`@${p}_bad`)
s_write(a1, text(qbe.js_null))
emit(`@${p}_done`)
continue
}
if (op == "remainder") {
lhs = s_read(a2)
rhs = s_read(a3)
p = fresh()
lhs_d = emit_num_to_double(lhs)
rhs_d = emit_num_to_double(rhs)
emit(` %${p}_rhs0 =w ceqd ${rhs_d}, d_0.0`)
emit(` jnz %${p}_rhs0, @${p}_bad, @${p}_calc`)
emit(`@${p}_calc`)
emit(` %${p}_q =d div ${lhs_d}, ${rhs_d}`)
emit(` %${p}_qt =d call $trunc(d %${p}_q)`)
emit(` %${p}_m =d mul ${rhs_d}, %${p}_qt`)
emit(` %${p}_rd =d sub ${lhs_d}, %${p}_m`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
emit(` jmp @${p}_done`)
emit(`@${p}_bad`)
s_write(a1, text(qbe.js_null))
emit(`@${p}_done`)
continue
}
if (op == "max" || op == "min") {
lhs = s_read(a2)
rhs = s_read(a3)
p = fresh()
lhs_d = emit_num_to_double(lhs)
rhs_d = emit_num_to_double(rhs)
if (op == "max") {
emit(` %${p}_take_l =w cgtd ${lhs_d}, ${rhs_d}`)
} else {
emit(` %${p}_take_l =w cltd ${lhs_d}, ${rhs_d}`)
}
emit(` jnz %${p}_take_l, @${p}_lhs, @${p}_rhs`)
emit(`@${p}_lhs`)
emit(` %${p}_rd =d copy ${lhs_d}`)
emit(` jmp @${p}_done_math`)
emit(`@${p}_rhs`)
emit(` %${p}_rd =d copy ${rhs_d}`)
emit(`@${p}_done_math`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
continue
}
if (op == "abs") {
lhs = s_read(a2)
p = fresh()
lhs_d = emit_num_to_double(lhs)
emit(` %${p}_rd =d call $fabs(d ${lhs_d})`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
continue
}
if (op == "sign") {
lhs = s_read(a2)
p = fresh()
lhs_d = emit_num_to_double(lhs)
emit(` %${p}_lt0 =w cltd ${lhs_d}, d_0.0`)
emit(` jnz %${p}_lt0, @${p}_neg, @${p}_chk_pos`)
emit(`@${p}_chk_pos`)
emit(` %${p}_gt0 =w cgtd ${lhs_d}, d_0.0`)
emit(` jnz %${p}_gt0, @${p}_pos, @${p}_zero`)
emit(`@${p}_neg`)
s_write(a1, text(-2))
emit(` jmp @${p}_done`)
emit(`@${p}_pos`)
s_write(a1, text(2))
emit(` jmp @${p}_done`)
emit(`@${p}_zero`)
s_write(a1, text(0))
emit(`@${p}_done`)
continue
}
if (op == "fraction") {
lhs = s_read(a2)
p = fresh()
lhs_d = emit_num_to_double(lhs)
emit(` %${p}_ti =d call $trunc(d ${lhs_d})`)
emit(` %${p}_rd =d sub ${lhs_d}, %${p}_ti`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
continue
}
if (op == "integer") {
lhs = s_read(a2)
p = fresh()
lhs_d = emit_num_to_double(lhs)
emit(` %${p}_rd =d call $trunc(d ${lhs_d})`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
continue
}
if (op == "floor" || op == "ceiling" || op == "round" || op == "trunc") {
lhs = s_read(a2)
rhs = s_read(a3)
p = fresh()
lhs_d = emit_num_to_double(lhs)
emit(` %${p}_lhs_num =w copy ${emit_is_num_w(lhs)}`)
emit(` jnz %${p}_lhs_num, @${p}_place, @${p}_bad`)
emit(`@${p}_place`)
emit(` %${p}_t1 =l and ${rhs}, 1`)
emit(` %${p}_is_int =w ceql %${p}_t1, 0`)
emit(` jnz %${p}_is_int, @${p}_pi_int, @${p}_pi_not_int`)
emit(`@${p}_pi_int`)
emit(` %${p}_pil =l sar ${rhs}, 1`)
emit(` %${p}_piw =w copy %${p}_pil`)
emit(` jmp @${p}_pi_done`)
emit(`@${p}_pi_not_int`)
emit(` %${p}_t5 =l and ${rhs}, 31`)
emit(` %${p}_is_null =w ceql %${p}_t5, 7`)
emit(` jnz %${p}_is_null, @${p}_pi_zero, @${p}_pi_chk_bool`)
emit(`@${p}_pi_zero`)
emit(` %${p}_piw =w copy 0`)
emit(` jmp @${p}_pi_done`)
emit(`@${p}_pi_chk_bool`)
emit(` %${p}_is_bool =w ceql %${p}_t5, 3`)
emit(` jnz %${p}_is_bool, @${p}_pi_bool, @${p}_pi_chk_float`)
emit(`@${p}_pi_bool`)
emit(` %${p}_bl =l shr ${rhs}, 5`)
emit(` %${p}_bw =w copy %${p}_bl`)
emit(` %${p}_piw =w and %${p}_bw, 1`)
emit(` jmp @${p}_pi_done`)
emit(`@${p}_pi_chk_float`)
emit(` %${p}_t3 =l and ${rhs}, 7`)
emit(` %${p}_is_float =w ceql %${p}_t3, 5`)
emit(` jnz %${p}_is_float, @${p}_pi_float, @${p}_bad`)
emit(`@${p}_pi_float`)
rhs_d = emit_num_to_double(rhs)
emit(` %${p}_piw =w dtosi ${rhs_d}`)
emit(`@${p}_pi_done`)
emit(` %${p}_is_zero =w ceqw %${p}_piw, 0`)
emit(` jnz %${p}_is_zero, @${p}_direct, @${p}_scaled`)
emit(`@${p}_direct`)
if (op == "floor") {
emit(` %${p}_rd =d call $floor(d ${lhs_d})`)
} else if (op == "ceiling") {
emit(` %${p}_rd =d call $ceil(d ${lhs_d})`)
} else if (op == "round") {
emit(` %${p}_rd =d call $round(d ${lhs_d})`)
} else {
emit(` %${p}_rd =d call $trunc(d ${lhs_d})`)
}
emit(` jmp @${p}_store`)
emit(`@${p}_scaled`)
emit(` %${p}_pl =l extsw %${p}_piw`)
emit(` %${p}_pd =d sltof %${p}_pl`)
emit(` %${p}_negpd =d neg %${p}_pd`)
emit(` %${p}_mult =d call $pow(d d_10.0, d %${p}_negpd)`)
emit(` %${p}_sd =d mul ${lhs_d}, %${p}_mult`)
if (op == "floor") {
emit(` %${p}_sr =d call $floor(d %${p}_sd)`)
} else if (op == "ceiling") {
emit(` %${p}_sr =d call $ceil(d %${p}_sd)`)
} else if (op == "round") {
emit(` %${p}_sr =d call $round(d %${p}_sd)`)
} else {
emit(` %${p}_sr =d call $trunc(d %${p}_sd)`)
}
emit(` %${p}_rd =d div %${p}_sr, %${p}_mult`)
emit(` jmp @${p}_store`)
emit(`@${p}_bad`)
s_write(a1, text(qbe.js_null))
emit(` jmp @${p}_done`)
emit(`@${p}_store`)
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
s_write(a1, `%${p}_r`)
emit(`@${p}_done`)
continue
}
if (op == "negate") {

View File

@@ -547,7 +547,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
return JS_NewFloat64(ctx, (double)ia / (double)ib);
case MACH_MOD:
if (ib == 0) return JS_NULL;
return JS_NewInt32(ctx, ia % ib);
return JS_NewFloat64(ctx, (double)ia - ((double)ib * floor((double)ia / (double)ib)));
case MACH_EQ:
return JS_NewBool(ctx, ia == ib);
case MACH_NEQ:
@@ -673,8 +673,9 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
}
case MACH_MOD: {
if (db == 0.0) return JS_NULL;
double r = fmod(da, db);
if (!isfinite(r)) return JS_NULL;
if (isnan(da) || isnan(db)) return JS_NULL;
if (da == 0.0) return JS_NewFloat64(ctx, 0.0);
double r = da - (db * floor(da / db));
return JS_NewFloat64(ctx, r);
}
case MACH_POW: {
@@ -704,6 +705,34 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
return JS_RaiseDisrupt(ctx, "type mismatch in binary operation");
}
static inline int mach_get_number(JSValue v, double *out) {
uint32_t tag = JS_VALUE_GET_TAG(v);
if (tag == JS_TAG_INT) {
*out = (double)JS_VALUE_GET_INT(v);
return 0;
}
if (JS_TAG_IS_FLOAT64(tag)) {
*out = JS_VALUE_GET_FLOAT64(v);
return 0;
}
return -1;
}
static inline int mach_get_place(JSContext *ctx, JSValue v, int32_t *out) {
uint32_t tag = JS_VALUE_GET_NORM_TAG(v);
if (tag == JS_TAG_INT || tag == JS_TAG_BOOL || tag == JS_TAG_NULL || tag == JS_TAG_FLOAT64) {
return JS_ToInt32(ctx, out, v);
}
return -1;
}
static inline double mach_apply_place(double d, int32_t place, double (*f)(double)) {
if (place == 0)
return f(d);
double mult = pow(10.0, -(double)place);
return f(d * mult) / mult;
}
#ifdef HAVE_ASAN
void __asan_on_error(void) {
@@ -856,6 +885,12 @@ vm_dispatch:
DT(MACH_MUL), DT(MACH_DIV),
DT(MACH_MOD), DT(MACH_POW),
DT(MACH_NEG),
DT(MACH_REMAINDER), DT(MACH_MAX),
DT(MACH_MIN), DT(MACH_ABS),
DT(MACH_SIGN), DT(MACH_FRACTION),
DT(MACH_INTEGER), DT(MACH_FLOOR),
DT(MACH_CEILING), DT(MACH_ROUND),
DT(MACH_TRUNC),
DT(MACH_EQ), DT(MACH_NEQ),
DT(MACH_LT), DT(MACH_LE),
DT(MACH_GT), DT(MACH_GE),
@@ -1043,17 +1078,15 @@ vm_dispatch:
}
VM_CASE(MACH_MOD): {
JSValue left = frame->slots[b], right = frame->slots[c];
if (JS_VALUE_IS_BOTH_INT(left, right)) {
int32_t ib = JS_VALUE_GET_INT(right);
frame->slots[a] = (ib != 0) ? JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib) : JS_NULL;
double da, db;
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0 || db == 0.0 ||
isnan(da) || isnan(db)) {
frame->slots[a] = JS_NULL;
} else {
double da, db, r;
JS_ToFloat64(ctx, &da, left);
JS_ToFloat64(ctx, &db, right);
if (db == 0.0) { frame->slots[a] = JS_NULL; }
else {
r = fmod(da, db);
frame->slots[a] = !isfinite(r) ? JS_NULL : JS_NewFloat64(ctx, r);
if (da == 0.0) {
frame->slots[a] = JS_NewFloat64(ctx, 0.0);
} else {
frame->slots[a] = JS_NewFloat64(ctx, da - (db * floor(da / db)));
}
}
VM_BREAK();
@@ -1077,6 +1110,116 @@ vm_dispatch:
VM_BREAK();
}
VM_CASE(MACH_REMAINDER): {
JSValue left = frame->slots[b], right = frame->slots[c];
double da, db;
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0 || db == 0.0) {
frame->slots[a] = JS_NULL;
} else {
frame->slots[a] = JS_NewFloat64(ctx, da - (trunc(da / db) * db));
}
VM_BREAK();
}
VM_CASE(MACH_MAX): {
JSValue left = frame->slots[b], right = frame->slots[c];
double da, db;
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0) {
frame->slots[a] = JS_NULL;
} else {
frame->slots[a] = JS_NewFloat64(ctx, da > db ? da : db);
}
VM_BREAK();
}
VM_CASE(MACH_MIN): {
JSValue left = frame->slots[b], right = frame->slots[c];
double da, db;
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0) {
frame->slots[a] = JS_NULL;
} else {
frame->slots[a] = JS_NewFloat64(ctx, da < db ? da : db);
}
VM_BREAK();
}
VM_CASE(MACH_ABS): {
JSValue v = frame->slots[b];
double d;
if (mach_get_number(v, &d) != 0) {
frame->slots[a] = JS_NULL;
} else {
frame->slots[a] = JS_NewFloat64(ctx, fabs(d));
}
VM_BREAK();
}
VM_CASE(MACH_SIGN): {
JSValue v = frame->slots[b];
double d;
if (mach_get_number(v, &d) != 0) {
frame->slots[a] = JS_NULL;
} else if (d < 0) {
frame->slots[a] = JS_NewInt32(ctx, -1);
} else if (d > 0) {
frame->slots[a] = JS_NewInt32(ctx, 1);
} else {
frame->slots[a] = JS_NewInt32(ctx, 0);
}
VM_BREAK();
}
VM_CASE(MACH_FRACTION): {
JSValue v = frame->slots[b];
double d;
if (mach_get_number(v, &d) != 0) {
frame->slots[a] = JS_NULL;
} else {
frame->slots[a] = JS_NewFloat64(ctx, d - trunc(d));
}
VM_BREAK();
}
VM_CASE(MACH_INTEGER): {
JSValue v = frame->slots[b];
double d;
if (mach_get_number(v, &d) != 0) {
frame->slots[a] = JS_NULL;
} else {
frame->slots[a] = JS_NewFloat64(ctx, trunc(d));
}
VM_BREAK();
}
VM_CASE(MACH_FLOOR):
VM_CASE(MACH_CEILING):
VM_CASE(MACH_ROUND):
VM_CASE(MACH_TRUNC): {
JSValue v = frame->slots[b];
JSValue pval = frame->slots[c];
double d, r;
int32_t place = 0;
if (mach_get_number(v, &d) != 0) {
frame->slots[a] = JS_NULL;
VM_BREAK();
}
if (!JS_IsNull(pval) && mach_get_place(ctx, pval, &place) != 0) {
frame->slots[a] = JS_NULL;
VM_BREAK();
}
if (op == MACH_FLOOR) {
r = mach_apply_place(d, place, floor);
} else if (op == MACH_CEILING) {
r = mach_apply_place(d, place, ceil);
} else if (op == MACH_ROUND) {
r = mach_apply_place(d, place, round);
} else {
r = mach_apply_place(d, place, trunc);
}
frame->slots[a] = JS_NewFloat64(ctx, r);
VM_BREAK();
}
/* Comparison — inline integer fast paths */
VM_CASE(MACH_EQ): {
JSValue left = frame->slots[b], right = frame->slots[c];
@@ -2403,6 +2546,17 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "modulo") == 0) { ABC3(MACH_MOD); }
else if (strcmp(op, "pow") == 0) { ABC3(MACH_POW); }
else if (strcmp(op, "negate") == 0) { AB2(MACH_NEG); }
else if (strcmp(op, "remainder") == 0) { ABC3(MACH_REMAINDER); }
else if (strcmp(op, "max") == 0) { ABC3(MACH_MAX); }
else if (strcmp(op, "min") == 0) { ABC3(MACH_MIN); }
else if (strcmp(op, "abs") == 0) { AB2(MACH_ABS); }
else if (strcmp(op, "sign") == 0) { AB2(MACH_SIGN); }
else if (strcmp(op, "fraction") == 0) { AB2(MACH_FRACTION); }
else if (strcmp(op, "integer") == 0) { AB2(MACH_INTEGER); }
else if (strcmp(op, "floor") == 0) { ABC3(MACH_FLOOR); }
else if (strcmp(op, "ceiling") == 0) { ABC3(MACH_CEILING); }
else if (strcmp(op, "round") == 0) { ABC3(MACH_ROUND); }
else if (strcmp(op, "trunc") == 0) { ABC3(MACH_TRUNC); }
/* Typed integer comparisons */
else if (strcmp(op, "eq_int") == 0) { ABC3(MACH_EQ_INT); }
else if (strcmp(op, "ne_int") == 0) { ABC3(MACH_NE_INT); }
@@ -3107,4 +3261,3 @@ void JS_DumpMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue en
dump_register_code(ctx, code, 0);
JS_PopGCRef(ctx, &env_ref);
}

View File

@@ -487,6 +487,17 @@ typedef enum MachOpcode {
MACH_MOD, /* R(A) = R(B) % R(C) */
MACH_POW, /* R(A) = R(B) ** R(C) */
MACH_NEG, /* R(A) = -R(B) */
MACH_REMAINDER, /* R(A) = remainder(R(B), R(C)) */
MACH_MAX, /* R(A) = max(R(B), R(C)) */
MACH_MIN, /* R(A) = min(R(B), R(C)) */
MACH_ABS, /* R(A) = abs(R(B)) */
MACH_SIGN, /* R(A) = sign(R(B)) */
MACH_FRACTION, /* R(A) = fraction(R(B)) */
MACH_INTEGER, /* R(A) = integer(R(B)) */
MACH_FLOOR, /* R(A) = floor(R(B), R(C)) */
MACH_CEILING, /* R(A) = ceiling(R(B), R(C)) */
MACH_ROUND, /* R(A) = round(R(B), R(C)) */
MACH_TRUNC, /* R(A) = trunc(R(B), R(C)) */
MACH__DEAD_INC, /* reserved — was MACH_INC, never emitted */
MACH__DEAD_DEC, /* reserved — was MACH_DEC, never emitted */
@@ -660,6 +671,17 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_MOD] = "mod",
[MACH_POW] = "pow",
[MACH_NEG] = "neg",
[MACH_REMAINDER] = "remainder",
[MACH_MAX] = "max",
[MACH_MIN] = "min",
[MACH_ABS] = "abs",
[MACH_SIGN] = "sign",
[MACH_FRACTION] = "fraction",
[MACH_INTEGER] = "integer",
[MACH_FLOOR] = "floor",
[MACH_CEILING] = "ceiling",
[MACH_ROUND] = "round",
[MACH_TRUNC] = "trunc",
[MACH__DEAD_INC] = "dead_inc",
[MACH__DEAD_DEC] = "dead_dec",
[MACH_EQ] = "eq",

View File

@@ -37,7 +37,8 @@ var streamline = function(ir, log) {
var numeric_ops = {
add: true, subtract: true, multiply: true,
divide: true, modulo: true, pow: true
divide: true, modulo: true, remainder: true,
max: true, min: true, pow: true
}
var bool_result_ops = {
eq_int: true, ne_int: true, lt_int: true, gt_int: true,
@@ -229,7 +230,12 @@ var streamline = function(ir, log) {
add: [2, T_NUM, 3, T_NUM],
subtract: [2, T_NUM, 3, T_NUM], multiply: [2, T_NUM, 3, T_NUM],
divide: [2, T_NUM, 3, T_NUM], modulo: [2, T_NUM, 3, T_NUM],
pow: [2, T_NUM, 3, T_NUM], negate: [2, T_NUM],
remainder: [2, T_NUM, 3, T_NUM], max: [2, T_NUM, 3, T_NUM],
min: [2, T_NUM, 3, T_NUM], pow: [2, T_NUM, 3, T_NUM],
negate: [2, T_NUM], abs: [2, T_NUM], sign: [2, T_NUM],
fraction: [2, T_NUM], integer: [2, T_NUM],
floor: [2, T_NUM], ceiling: [2, T_NUM],
round: [2, T_NUM], trunc: [2, T_NUM],
bitand: [2, T_INT, 3, T_INT], bitor: [2, T_INT, 3, T_INT],
bitxor: [2, T_INT, 3, T_INT], shl: [2, T_INT, 3, T_INT],
shr: [2, T_INT, 3, T_INT], ushr: [2, T_INT, 3, T_INT],
@@ -332,10 +338,14 @@ var streamline = function(ir, log) {
bitnot: [1, T_INT], bitand: [1, T_INT], bitor: [1, T_INT],
bitxor: [1, T_INT], shl: [1, T_INT], shr: [1, T_INT], ushr: [1, T_INT],
negate: [1, T_NUM], concat: [1, T_TEXT],
abs: [1, T_NUM], sign: [1, T_INT], fraction: [1, T_NUM],
integer: [1, T_NUM], floor: [1, T_NUM], ceiling: [1, T_NUM],
round: [1, T_NUM], trunc: [1, T_NUM],
eq: [1, T_BOOL], ne: [1, T_BOOL], lt: [1, T_BOOL],
le: [1, T_BOOL], gt: [1, T_BOOL], ge: [1, T_BOOL], in: [1, T_BOOL],
add: [1, T_NUM], subtract: [1, T_NUM], multiply: [1, T_NUM],
divide: [1, T_NUM], modulo: [1, T_NUM], pow: [1, T_NUM],
divide: [1, T_NUM], modulo: [1, T_NUM], remainder: [1, T_NUM],
max: [1, T_NUM], min: [1, T_NUM], pow: [1, T_NUM],
move: [1, T_UNKNOWN], load_field: [1, T_UNKNOWN],
load_index: [1, T_UNKNOWN], load_dynamic: [1, T_UNKNOWN],
pop: [1, T_UNKNOWN], get: [1, T_UNKNOWN],
@@ -359,8 +369,8 @@ var streamline = function(ir, log) {
var intrinsic_return_types = {
abs: T_NUM, floor: T_NUM, ceiling: T_NUM,
round: T_NUM, trunc: T_NUM, fraction: T_NUM,
integer: T_NUM, sign: T_NUM,
max: T_NUM, min: T_NUM
integer: T_NUM, whole: T_NUM, sign: T_NUM,
max: T_NUM, min: T_NUM, remainder: T_NUM, modulo: T_NUM
}
var infer_slot_write_types = function(func, param_types) {