Merge branch 'mach' into mcode2

This commit is contained in:
2026-02-10 07:38:35 -06:00
3 changed files with 977 additions and 11 deletions

479
mcode.cm
View File

@@ -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)
}

295
qbe.cm
View File

@@ -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
}

View File

@@ -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];