diff --git a/mcode.cm b/mcode.cm index a4720e73..d8a30ecd 100644 --- a/mcode.cm +++ b/mcode.cm @@ -227,6 +227,463 @@ var mcode = function(ast) { add_instr([op, slot, label]) } + // ---- Decomposed op emission helpers ---- + + // Helper: check if an AST node is a known-int literal + var is_known_int = function(node) { + if (node == null) { return false } + return node.kind == "number" && is_integer(node.number) + } + + // Helper: check if an AST node is a known-text literal + var is_known_text = function(node) { + if (node == null) { return false } + return node.kind == "text" || node.kind == "text literal" + } + + // Helper: check if an AST node is a known-number literal (int or float) + var is_known_number = function(node) { + if (node == null) { return false } + return node.kind == "number" + } + + // Helper: check if an AST node is a known-bool literal + var is_known_bool = function(node) { + if (node == null) { return false } + return node.kind == "true" || node.kind == "false" + } + + // Helper: check if an AST node is a known-null literal + var is_known_null = function(node) { + if (node == null) { return false } + return node.kind == "null" + } + + // emit_add_decomposed: int path -> text path -> float path -> disrupt + var emit_add_decomposed = function(dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var left_is_int = is_known_int(left_node) + var left_is_text = is_known_text(left_node) + var left_is_num = is_known_number(left_node) + var right_is_int = is_known_int(right_node) + var right_is_text = is_known_text(right_node) + var right_is_num = is_known_number(right_node) + var not_int = null + var not_text = null + var done = null + var err = null + + // Both sides known int + if (left_is_int && right_is_int) { + emit_3("add_int", dest, left, right) + return null + } + // Both sides known text + if (left_is_text && right_is_text) { + emit_3("concat", dest, left, right) + return null + } + // Both sides known number (but not both int) + if (left_is_num && right_is_num) { + if (left_is_int && right_is_int) { + emit_3("add_int", dest, left, right) + } else { + emit_3("add_float", dest, left, right) + } + return null + } + + not_int = gen_label("add_ni") + not_text = gen_label("add_nt") + done = gen_label("add_done") + err = gen_label("add_err") + + // Int path + t0 = alloc_slot() + if (!left_is_int) { + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + } + t1 = alloc_slot() + if (!right_is_int) { + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + } + emit_3("add_int", dest, left, right) + emit_jump(done) + + // Text path + emit_label(not_int) + if (!left_is_text) { + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, not_text) + } + if (!right_is_text) { + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, not_text) + } + emit_3("concat", dest, left, right) + emit_jump(done) + + // Float path + emit_label(not_text) + if (!left_is_num) { + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, err) + } + if (!right_is_num) { + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, err) + } + emit_3("add_float", dest, left, right) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // emit_numeric_binop: int path -> float path -> disrupt + var emit_numeric_binop = function(int_op, float_op, dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var left_is_int = is_known_int(left_node) + var left_is_num = is_known_number(left_node) + var right_is_int = is_known_int(right_node) + var right_is_num = is_known_number(right_node) + var not_int = null + var done = null + var err = null + + // Both sides known int + if (left_is_int && right_is_int) { + emit_3(int_op, dest, left, right) + return null + } + // Both sides known number (but not both int) + if (left_is_num && right_is_num) { + emit_3(float_op, dest, left, right) + return null + } + + not_int = gen_label("num_ni") + done = gen_label("num_done") + err = gen_label("num_err") + + t0 = alloc_slot() + if (!left_is_int) { + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + } + t1 = alloc_slot() + if (!right_is_int) { + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + } + emit_3(int_op, dest, left, right) + emit_jump(done) + + emit_label(not_int) + if (!left_is_num) { + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, err) + } + if (!right_is_num) { + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, err) + } + emit_3(float_op, dest, left, right) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false) + var emit_eq_decomposed = function(dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var done = gen_label("eq_done") + var not_int = gen_label("eq_ni") + var not_num = gen_label("eq_nn") + var not_text = gen_label("eq_nt") + var not_null = gen_label("eq_nnl") + var not_bool = gen_label("eq_nb") + + // Identical check + emit_3("is_identical", dest, left, right) + emit_jump_cond("jump_true", dest, done) + + // Int path + t0 = alloc_slot() + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + t1 = alloc_slot() + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + emit_3("eq_int", dest, left, right) + emit_jump(done) + + // Float path + emit_label(not_int) + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, not_num) + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, not_num) + emit_3("eq_float", dest, left, right) + emit_jump(done) + + // Text path + emit_label(not_num) + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, not_text) + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, not_text) + emit_3("eq_text", dest, left, right) + emit_jump(done) + + // Null path + emit_label(not_text) + emit_2("is_null", t0, left) + emit_jump_cond("jump_false", t0, not_null) + emit_2("is_null", t1, right) + emit_jump_cond("jump_false", t1, not_null) + emit_1("true", dest) + emit_jump(done) + + // Bool path + emit_label(not_null) + emit_2("is_bool", t0, left) + emit_jump_cond("jump_false", t0, not_bool) + emit_2("is_bool", t1, right) + emit_jump_cond("jump_false", t1, not_bool) + emit_3("eq_bool", dest, left, right) + emit_jump(done) + + // Mismatch -> false + emit_label(not_bool) + emit_1("false", dest) + emit_label(done) + return null + } + + // emit_ne_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(true) + var emit_ne_decomposed = function(dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var done = gen_label("ne_done") + var not_ident = gen_label("ne_nid") + var not_int = gen_label("ne_ni") + var not_num = gen_label("ne_nn") + var not_text = gen_label("ne_nt") + var not_null = gen_label("ne_nnl") + var not_bool = gen_label("ne_nb") + + // Identical -> false + emit_3("is_identical", dest, left, right) + emit_jump_cond("jump_true", dest, not_ident) + // If jump_true doesn't fire, dest already holds false, continue to checks + emit_jump(not_int) + + emit_label(not_ident) + emit_1("false", dest) + emit_jump(done) + + // Int path + emit_label(not_int) + t0 = alloc_slot() + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_num) + t1 = alloc_slot() + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_num) + emit_3("ne_int", dest, left, right) + emit_jump(done) + + // Float path + emit_label(not_num) + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, not_text) + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, not_text) + emit_3("ne_float", dest, left, right) + emit_jump(done) + + // Text path + emit_label(not_text) + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, not_null) + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, not_null) + emit_3("ne_text", dest, left, right) + emit_jump(done) + + // Null path + emit_label(not_null) + emit_2("is_null", t0, left) + emit_jump_cond("jump_false", t0, not_bool) + emit_2("is_null", t1, right) + emit_jump_cond("jump_false", t1, not_bool) + emit_1("false", dest) + emit_jump(done) + + // Bool path + var mismatch = gen_label("ne_mis") + emit_label(not_bool) + emit_2("is_bool", t0, left) + emit_jump_cond("jump_false", t0, mismatch) + emit_2("is_bool", t1, right) + emit_jump_cond("jump_false", t1, mismatch) + emit_3("ne_bool", dest, left, right) + emit_jump(done) + + // Mismatch -> true (ne of different types is true) + emit_label(mismatch) + emit_1("true", dest) + emit_label(done) + return null + } + + // emit_relational: int -> float -> text -> disrupt + var emit_relational = function(int_op, float_op, text_op, dest, left, right, left_node, right_node) { + var t0 = 0 + var t1 = 0 + var left_is_int = is_known_int(left_node) + var left_is_num = is_known_number(left_node) + var left_is_text = is_known_text(left_node) + var right_is_int = is_known_int(right_node) + var right_is_num = is_known_number(right_node) + var right_is_text = is_known_text(right_node) + var not_int = null + var not_num = null + var done = null + var err = null + + // Both known int + if (left_is_int && right_is_int) { + emit_3(int_op, dest, left, right) + return null + } + // Both known number + if (left_is_num && right_is_num) { + emit_3(float_op, dest, left, right) + return null + } + // Both known text + if (left_is_text && right_is_text) { + emit_3(text_op, dest, left, right) + return null + } + + not_int = gen_label("rel_ni") + not_num = gen_label("rel_nn") + done = gen_label("rel_done") + err = gen_label("rel_err") + + t0 = alloc_slot() + emit_2("is_int", t0, left) + emit_jump_cond("jump_false", t0, not_int) + t1 = alloc_slot() + emit_2("is_int", t1, right) + emit_jump_cond("jump_false", t1, not_int) + emit_3(int_op, dest, left, right) + emit_jump(done) + + emit_label(not_int) + emit_2("is_num", t0, left) + emit_jump_cond("jump_false", t0, not_num) + emit_2("is_num", t1, right) + emit_jump_cond("jump_false", t1, not_num) + emit_3(float_op, dest, left, right) + emit_jump(done) + + emit_label(not_num) + emit_2("is_text", t0, left) + emit_jump_cond("jump_false", t0, err) + emit_2("is_text", t1, right) + emit_jump_cond("jump_false", t1, err) + emit_3(text_op, dest, left, right) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // emit_neg_decomposed: int path -> float path -> disrupt + var emit_neg_decomposed = function(dest, src, src_node) { + var t0 = 0 + var not_int = null + var done = null + var err = null + + if (is_known_int(src_node)) { + emit_2("neg_int", dest, src) + return null + } + if (is_known_number(src_node)) { + emit_2("neg_float", dest, src) + return null + } + + not_int = gen_label("neg_ni") + done = gen_label("neg_done") + err = gen_label("neg_err") + + t0 = alloc_slot() + emit_2("is_int", t0, src) + emit_jump_cond("jump_false", t0, not_int) + emit_2("neg_int", dest, src) + emit_jump(done) + + emit_label(not_int) + emit_2("is_num", t0, src) + emit_jump_cond("jump_false", t0, err) + emit_2("neg_float", dest, src) + emit_jump(done) + + emit_label(err) + emit_0("disrupt") + emit_label(done) + return null + } + + // Central router: maps op string to decomposition helper + var emit_binop = function(op_str, dest, left, right, left_node, right_node) { + if (op_str == "add") { + emit_add_decomposed(dest, left, right, left_node, right_node) + } else if (op_str == "subtract") { + emit_numeric_binop("sub_int", "sub_float", dest, left, right, left_node, right_node) + } else if (op_str == "multiply") { + emit_numeric_binop("mul_int", "mul_float", dest, left, right, left_node, right_node) + } else if (op_str == "divide") { + emit_numeric_binop("div_int", "div_float", dest, left, right, left_node, right_node) + } else if (op_str == "modulo") { + emit_numeric_binop("mod_int", "mod_float", dest, left, right, left_node, right_node) + } else if (op_str == "eq") { + emit_eq_decomposed(dest, left, right, left_node, right_node) + } else if (op_str == "ne") { + emit_ne_decomposed(dest, left, right, left_node, right_node) + } else if (op_str == "lt") { + emit_relational("lt_int", "lt_float", "lt_text", dest, left, right, left_node, right_node) + } else if (op_str == "le") { + emit_relational("le_int", "le_float", "le_text", dest, left, right, left_node, right_node) + } else if (op_str == "gt") { + emit_relational("gt_int", "gt_float", "gt_text", dest, left, right, left_node, right_node) + } else if (op_str == "ge") { + emit_relational("ge_int", "ge_float", "ge_text", dest, left, right, left_node, right_node) + } else { + // Passthrough for bitwise, pow, in, etc. + emit_3(op_str, dest, left, right) + } + return null + } + var emit_get_prop = function(dest, obj, prop) { add_instr(["load", dest, obj, prop]) } @@ -463,7 +920,7 @@ var mcode = function(ast) { if (op == null) { op = "add" } - emit_3(op, dest, left_slot, right_slot) + emit_binop(op, dest, left_slot, right_slot, left, right) return dest } @@ -515,7 +972,7 @@ var mcode = function(ast) { } right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_3(op, dest, left_slot, right_slot) + emit_binop(op, dest, left_slot, right_slot, null, right) if (level == 0) { local = find_var(name) if (local >= 0) { @@ -538,7 +995,7 @@ var mcode = function(ast) { emit_get_prop(old_val, obj_slot, prop) right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_3(op, dest, old_val, right_slot) + emit_binop(op, dest, old_val, right_slot, null, right) emit_set_prop(obj_slot, prop, dest) return dest } else if (left_kind == "[") { @@ -550,7 +1007,7 @@ var mcode = function(ast) { emit_get_elem(old_val, obj_slot, idx_slot) right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_3(op, dest, old_val, right_slot) + emit_binop(op, dest, old_val, right_slot, null, right) emit_set_elem(obj_slot, idx_slot, dest) return dest } @@ -679,6 +1136,7 @@ var mcode = function(ast) { var arith_op = null var operand_kind = null var one_slot = 0 + var one_node = null var old_slot = 0 var local = 0 var new_slot = 0 @@ -899,7 +1357,7 @@ var mcode = function(ast) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() - emit_3(mop, d, a0, a1) + emit_binop(mop, d, a0, a1, args_list[0], args_list[1]) return d } @@ -946,7 +1404,7 @@ var mcode = function(ast) { if (kind == "-unary") { operand_slot = gen_expr(expr.expression, -1) slot = alloc_slot() - emit_2("neg", slot, operand_slot) + emit_neg_decomposed(slot, operand_slot, expr.expression) return slot } if (kind == "+unary") { @@ -961,6 +1419,7 @@ var mcode = function(ast) { operand_kind = operand.kind one_slot = alloc_slot() emit_2("int", one_slot, 1) + one_node = {kind: "number", number: 1} if (operand_kind == "name") { name = operand.name @@ -983,7 +1442,7 @@ var mcode = function(ast) { emit_access_intrinsic(old_slot, name) } new_slot = alloc_slot() - emit_3(arith_op, new_slot, old_slot, one_slot) + emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) if (level == 0) { local = find_var(name) if (local >= 0) { @@ -1003,7 +1462,7 @@ var mcode = function(ast) { old_slot = alloc_slot() emit_get_prop(old_slot, obj_slot, prop) new_slot = alloc_slot() - emit_3(arith_op, new_slot, old_slot, one_slot) + emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) emit_set_prop(obj_slot, prop, new_slot) return postfix ? old_slot : new_slot } else if (operand_kind == "[") { @@ -1014,7 +1473,7 @@ var mcode = function(ast) { old_slot = alloc_slot() emit_get_elem(old_slot, obj_slot, idx_slot) new_slot = alloc_slot() - emit_3(arith_op, new_slot, old_slot, one_slot) + emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) emit_set_elem(obj_slot, idx_slot, new_slot) return postfix ? old_slot : new_slot } @@ -1452,7 +1911,7 @@ var mcode = function(ast) { case_expr = case_node.expression case_val = gen_expr(case_expr, -1) cmp_slot = alloc_slot() - emit_3("eq", cmp_slot, switch_val, case_val) + emit_binop("eq", cmp_slot, switch_val, case_val, null, case_expr) emit_jump_cond("jump_true", cmp_slot, case_label) push(case_labels, case_label) } diff --git a/qbe.cm b/qbe.cm index 25fe173c..6dbc6947 100644 --- a/qbe.cm +++ b/qbe.cm @@ -705,6 +705,257 @@ var ushr = function(p, ctx, a, b) { return shift_op(p, ctx, a, b, "shr") } +// ============================================================ +// Decomposed per-type-path operations +// These map directly to the new IR ops emitted by mcode.cm. +// ============================================================ + +// --- Arithmetic (int path) --- +// add_int: assume both operands are tagged ints. Overflow -> float. +var add_int = function(p, ctx, a, b) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.sum =l add %${p}.ia, %${p}.ib + %${p}.lo =w csltl %${p}.sum, ${int32_min} + %${p}.hi =w csgtl %${p}.sum, ${int32_max} + %${p}.ov =w or %${p}.lo, %${p}.hi + jnz %${p}.ov, @${p}.ov, @${p}.ok +@${p}.ok + %${p}.rw =w copy %${p}.sum + %${p}.rext =l extuw %${p}.rw + %${p} =l shl %${p}.rext, 1 + jmp @${p}.done +@${p}.ov + %${p}.fd =d sltof %${p}.sum + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd) +@${p}.done +` +} + +var sub_int = function(p, ctx, a, b) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.diff =l sub %${p}.ia, %${p}.ib + %${p}.lo =w csltl %${p}.diff, ${int32_min} + %${p}.hi =w csgtl %${p}.diff, ${int32_max} + %${p}.ov =w or %${p}.lo, %${p}.hi + jnz %${p}.ov, @${p}.ov, @${p}.ok +@${p}.ok + %${p}.rw =w copy %${p}.diff + %${p}.rext =l extuw %${p}.rw + %${p} =l shl %${p}.rext, 1 + jmp @${p}.done +@${p}.ov + %${p}.fd =d sltof %${p}.diff + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd) +@${p}.done +` +} + +var mul_int = function(p, ctx, a, b) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.prod =l mul %${p}.ia, %${p}.ib + %${p}.lo =w csltl %${p}.prod, ${int32_min} + %${p}.hi =w csgtl %${p}.prod, ${int32_max} + %${p}.ov =w or %${p}.lo, %${p}.hi + jnz %${p}.ov, @${p}.ov, @${p}.ok +@${p}.ok + %${p}.rw =w copy %${p}.prod + %${p}.rext =l extuw %${p}.rw + %${p} =l shl %${p}.rext, 1 + jmp @${p}.done +@${p}.ov + %${p}.fd =d sltof %${p}.prod + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd) +@${p}.done +` +} + +var div_int = function(p, ctx, a, b) { + return ` %${p}.ia =w copy 0 + %${p}.tmp =l sar ${a}, 1 + %${p}.ia =w copy %${p}.tmp + %${p}.ib =w copy 0 + %${p}.tmp2 =l sar ${b}, 1 + %${p}.ib =w copy %${p}.tmp2 + %${p}.div0 =w ceqw %${p}.ib, 0 + jnz %${p}.div0, @${p}.null, @${p}.chk +@${p}.null + %${p} =l copy ${js_null} + jmp @${p}.done +@${p}.chk + %${p}.rem =w rem %${p}.ia, %${p}.ib + %${p}.exact =w ceqw %${p}.rem, 0 + jnz %${p}.exact, @${p}.idiv, @${p}.fdiv +@${p}.idiv + %${p}.q =w div %${p}.ia, %${p}.ib + %${p}.qext =l extuw %${p}.q + %${p} =l shl %${p}.qext, 1 + jmp @${p}.done +@${p}.fdiv + %${p}.da =d swtof %${p}.ia + %${p}.db =d swtof %${p}.ib + %${p}.dr =d div %${p}.da, %${p}.db + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.dr) +@${p}.done +` +} + +var mod_int = function(p, ctx, a, b) { + return ` %${p}.ia =w copy 0 + %${p}.tmp =l sar ${a}, 1 + %${p}.ia =w copy %${p}.tmp + %${p}.ib =w copy 0 + %${p}.tmp2 =l sar ${b}, 1 + %${p}.ib =w copy %${p}.tmp2 + %${p}.div0 =w ceqw %${p}.ib, 0 + jnz %${p}.div0, @${p}.null, @${p}.do_mod +@${p}.null + %${p} =l copy ${js_null} + jmp @${p}.done +@${p}.do_mod + %${p}.r =w rem %${p}.ia, %${p}.ib + %${p}.rext =l extuw %${p}.r + %${p} =l shl %${p}.rext, 1 +@${p}.done +` +} + +var neg_int = function(p, ctx, v) { + return ` %${p}.sl =l sar ${v}, 1 + %${p}.iw =w copy %${p}.sl + %${p}.is_min =w ceqw %${p}.iw, ${int32_min} + jnz %${p}.is_min, @${p}.ov, @${p}.ok +@${p}.ov + %${p}.fd =d swtof %${p}.iw + %${p}.fdn =d neg %${p}.fd + %${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fdn) + jmp @${p}.done +@${p}.ok + %${p}.ni =w sub 0, %${p}.iw + %${p}.niext =l extuw %${p}.ni + %${p} =l shl %${p}.niext, 1 +@${p}.done +` +} + +// --- Arithmetic (float path) --- +var add_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b}) +` +} + +var sub_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b}) +` +} + +var mul_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b}) +` +} + +var div_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b}) +` +} + +var mod_float = function(p, ctx, a, b) { + return ` %${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b}) +` +} + +var neg_float = function(p, ctx, v) { + return ` %${p} =l call $qbe_float_neg(l ${ctx}, l ${v}) +` +} + +// --- Text concat --- +var concat = function(p, ctx, a, b) { + return ` %${p} =l call $JS_ConcatString(l ${ctx}, l ${a}, l ${b}) +` +} + +// --- Comparisons (int path) --- +var cmp_int = function(p, a, b, qbe_op) { + return ` %${p}.ia =l sar ${a}, 1 + %${p}.ib =l sar ${b}, 1 + %${p}.iaw =w copy %${p}.ia + %${p}.ibw =w copy %${p}.ib + %${p}.cr =w ${qbe_op} %${p}.iaw, %${p}.ibw + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + +var eq_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "ceqw") } +var ne_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "cnew") } +var lt_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csltw") } +var le_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "cslew") } +var gt_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csgtw") } +var ge_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csgew") } + +// --- Comparisons (float path) --- +var cmp_float = function(p, ctx, a, b, op_id) { + return ` %${p}.fcr =w call $qbe_float_cmp(l ${ctx}, w ${op_id}, l ${a}, l ${b}) + %${p}.fcrext =l extuw %${p}.fcr + %${p}.fsh =l shl %${p}.fcrext, 5 + %${p} =l or %${p}.fsh, 3 +` +} + +var eq_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 0) } +var ne_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 1) } +var lt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 2) } +var le_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 3) } +var gt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 4) } +var ge_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 5) } + +// --- Comparisons (text path) --- +var cmp_text = function(p, ctx, a, b, qbe_op, eq_only) { + return ` %${p}.scmp =w call $js_string_compare_value(l ${ctx}, l ${a}, l ${b}, w ${eq_only}) + %${p}.tcr =w ${qbe_op} %${p}.scmp, 0 + %${p}.tcrext =l extuw %${p}.tcr + %${p}.tsh =l shl %${p}.tcrext, 5 + %${p} =l or %${p}.tsh, 3 +` +} + +var eq_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "ceqw", 1) } +var ne_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cnew", 1) } +var lt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csltw", 0) } +var le_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cslew", 0) } +var gt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgtw", 0) } +var ge_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgew", 0) } + +// --- Comparisons (bool path) --- +var eq_bool = function(p, a, b) { + return ` %${p}.cr =w ceql ${a}, ${b} + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + +var ne_bool = function(p, a, b) { + return ` %${p}.cr =w cnel ${a}, ${b} + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + +// --- Type guard: is_identical --- +var is_identical = function(p, a, b) { + return ` %${p}.cr =w ceql ${a}, ${b} + %${p}.crext =l extuw %${p}.cr + %${p}.sh =l shl %${p}.crext, 5 + %${p} =l or %${p}.sh, 3 +` +} + // ============================================================ // Module export // ============================================================ @@ -760,5 +1011,47 @@ return { bxor: bxor, shl: shl, shr: shr, - ushr: ushr + ushr: ushr, + // decomposed arithmetic (int path) + add_int: add_int, + sub_int: sub_int, + mul_int: mul_int, + div_int: div_int, + mod_int: mod_int, + neg_int: neg_int, + // decomposed arithmetic (float path) + add_float: add_float, + sub_float: sub_float, + mul_float: mul_float, + div_float: div_float, + mod_float: mod_float, + neg_float: neg_float, + // text concat + concat: concat, + // decomposed comparisons (int) + eq_int: eq_int, + ne_int: ne_int, + lt_int: lt_int, + le_int: le_int, + gt_int: gt_int, + ge_int: ge_int, + // decomposed comparisons (float) + eq_float: eq_float, + ne_float: ne_float, + lt_float: lt_float, + le_float: le_float, + gt_float: gt_float, + ge_float: ge_float, + // decomposed comparisons (text) + eq_text: eq_text, + ne_text: ne_text, + lt_text: lt_text, + le_text: le_text, + gt_text: gt_text, + ge_text: ge_text, + // decomposed comparisons (bool) + eq_bool: eq_bool, + ne_bool: ne_bool, + // type guard + is_identical: is_identical } diff --git a/source/mcode.c b/source/mcode.c index 26edb270..96aabbb5 100644 --- a/source/mcode.c +++ b/source/mcode.c @@ -486,6 +486,220 @@ JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, frame->slots[dest] = JS_NewFloat64(ctx, -d); } } + /* ---- Compiler-internal type guards ---- */ + else if (strcmp(op, "is_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsInt(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_num") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsNumber(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsText(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_null") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsNull(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_bool") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (JS_VALUE_GET_TAG(frame->slots[(int)a2->valuedouble]) == JS_TAG_BOOL) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "is_identical") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; + } + + /* ---- Specialized arithmetic (int) ---- */ + else if (strcmp(op, "add_int") == 0) { + int dest = (int)a1->valuedouble; + int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) + (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } + else if (strcmp(op, "sub_int") == 0) { + int dest = (int)a1->valuedouble; + int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) - (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } + else if (strcmp(op, "mul_int") == 0) { + int dest = (int)a1->valuedouble; + int64_t r = (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) * (int64_t)JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } + else if (strcmp(op, "div_int") == 0) { + int dest = (int)a1->valuedouble; + int32_t ia = JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]); + int32_t ib = JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + if (ib == 0) frame->slots[dest] = JS_NULL; + else if (ia % ib == 0) frame->slots[dest] = JS_NewInt32(ctx, ia / ib); + else frame->slots[dest] = JS_NewFloat64(ctx, (double)ia / (double)ib); + } + else if (strcmp(op, "mod_int") == 0) { + int dest = (int)a1->valuedouble; + int32_t ib = JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble]); + if (ib == 0) frame->slots[dest] = JS_NULL; + else frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) % ib); + } + else if (strcmp(op, "neg_int") == 0) { + int dest = (int)a1->valuedouble; + int32_t i = JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]); + if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, -(double)i); + else frame->slots[dest] = JS_NewInt32(ctx, -i); + } + + /* ---- Specialized arithmetic (float) ---- */ + else if (strcmp(op, "add_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, a + b); + } + else if (strcmp(op, "sub_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, a - b); + } + else if (strcmp(op, "mul_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, a * b); + } + else if (strcmp(op, "div_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + if (b == 0.0) frame->slots[dest] = JS_NULL; + else frame->slots[dest] = JS_NewFloat64(ctx, a / b); + } + else if (strcmp(op, "mod_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + if (b == 0.0) frame->slots[dest] = JS_NULL; + else frame->slots[dest] = JS_NewFloat64(ctx, fmod(a, b)); + } + else if (strcmp(op, "neg_float") == 0) { + int dest = (int)a1->valuedouble; + double d; + JS_ToFloat64(ctx, &d, frame->slots[(int)a2->valuedouble]); + frame->slots[dest] = JS_NewFloat64(ctx, -d); + } + + /* ---- Specialized comparisons (int) ---- */ + else if (strcmp(op, "eq_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) == JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "ne_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) != JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "lt_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) < JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "le_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) <= JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "gt_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) > JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + else if (strcmp(op, "ge_int") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[(int)a2->valuedouble]) >= JS_VALUE_GET_INT(frame->slots[(int)a3->valuedouble])); + } + + /* ---- Specialized comparisons (float) ---- */ + else if (strcmp(op, "eq_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a == b); + } + else if (strcmp(op, "ne_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a != b); + } + else if (strcmp(op, "lt_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a < b); + } + else if (strcmp(op, "le_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a <= b); + } + else if (strcmp(op, "gt_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a > b); + } + else if (strcmp(op, "ge_float") == 0) { + int dest = (int)a1->valuedouble; + double a, b; + JS_ToFloat64(ctx, &a, frame->slots[(int)a2->valuedouble]); + JS_ToFloat64(ctx, &b, frame->slots[(int)a3->valuedouble]); + frame->slots[dest] = JS_NewBool(ctx, a >= b); + } + + /* ---- Specialized comparisons (text) ---- */ + else if (strcmp(op, "eq_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], TRUE) == 0); + } + else if (strcmp(op, "ne_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], TRUE) != 0); + } + else if (strcmp(op, "lt_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) < 0); + } + else if (strcmp(op, "le_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) <= 0); + } + else if (strcmp(op, "gt_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) > 0); + } + else if (strcmp(op, "ge_text") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble], FALSE) >= 0); + } + + /* ---- Specialized comparisons (bool) ---- */ + else if (strcmp(op, "eq_bool") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "ne_bool") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = (frame->slots[(int)a2->valuedouble] != frame->slots[(int)a3->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "abs") == 0) { int dest = (int)a1->valuedouble; JSValue v = frame->slots[(int)a2->valuedouble];