From 27ca008f18cd0ceaa931d2c195d239346718a289 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Wed, 18 Feb 2026 21:18:18 -0600 Subject: [PATCH] lower ops directly --- mcode.cm | 94 +++++++++++++++++++ qbe_emit.cm | 185 +++++++++++++++++++++++++++++++++++++- source/mach.c | 181 ++++++++++++++++++++++++++++++++++--- source/quickjs-internal.h | 22 +++++ streamline.cm | 20 +++-- 5 files changed, 482 insertions(+), 20 deletions(-) diff --git a/mcode.cm b/mcode.cm index 8a8362a3..ebad4bf3 100644 --- a/mcode.cm +++ b/mcode.cm @@ -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) diff --git a/qbe_emit.cm b/qbe_emit.cm index a5e2734d..8d103c93 100644 --- a/qbe_emit.cm +++ b/qbe_emit.cm @@ -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") { diff --git a/source/mach.c b/source/mach.c index 0f72afaa..01a1c37b 100644 --- a/source/mach.c +++ b/source/mach.c @@ -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); } - diff --git a/source/quickjs-internal.h b/source/quickjs-internal.h index 48d8d483..7109ddee 100644 --- a/source/quickjs-internal.h +++ b/source/quickjs-internal.h @@ -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", diff --git a/streamline.cm b/streamline.cm index 16fcd8fb..9fe9adb5 100644 --- a/streamline.cm +++ b/streamline.cm @@ -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) {