remove typed ops

This commit is contained in:
2026-02-21 02:52:17 -06:00
parent ac707bc399
commit fea76ecac5
12 changed files with 48748 additions and 121796 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -257,7 +257,7 @@ var run = function() {
annotation = ""
if (kind == "rewritten") {
if (search(o_instr[0], "_int") != null || search(o_instr[0], "_float") != null || search(o_instr[0], "_text") != null) {
if (o_instr[0] == "concat" && m_instr[0] != "concat") {
annotation = "(specialized)"
} else {
annotation = "(rewritten)"

317
mcode.cm
View File

@@ -354,28 +354,29 @@ var mcode = function(ast) {
// emit_add_decomposed: emit type-dispatched add (text → concat, num → add)
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
var emit_add_decomposed = function() {
if (is_known_text(_bp_ln) && is_known_text(_bp_rn)) {
var left_is_num = is_known_number(_bp_ln) || slot_is_num(_bp_left)
var left_is_text = is_known_text(_bp_ln) || slot_is_text(_bp_left)
var right_is_num = is_known_number(_bp_rn) || slot_is_num(_bp_right)
var right_is_text = is_known_text(_bp_rn) || slot_is_text(_bp_right)
// Both known text → concat
if (left_is_text && right_is_text) {
emit_3("concat", _bp_dest, _bp_left, _bp_right)
mark_slot(_bp_dest, "text")
return null
}
if (is_known_number(_bp_ln) && is_known_number(_bp_rn)) {
// Both known number → add
if (left_is_num && right_is_num) {
emit_3("add", _bp_dest, _bp_left, _bp_right)
mark_slot(_bp_dest, "num")
return null
}
// If either operand is a known number (AST or slot), concat is impossible
if (is_known_number(_bp_ln) || is_known_number(_bp_rn)
|| slot_is_num(_bp_left) || slot_is_num(_bp_right)) {
// One known number, other unknown → emit_numeric_binop (guard on unknown side)
if (left_is_num || right_is_num) {
emit_numeric_binop("add")
mark_slot(_bp_dest, "num")
return null
}
if (slot_is_text(_bp_left) && slot_is_text(_bp_right)) {
emit_3("concat", _bp_dest, _bp_left, _bp_right)
mark_slot(_bp_dest, "text")
return null
}
// Unknown types: emit full dispatch
var t0 = alloc_slot()
var t1 = alloc_slot()
@@ -410,8 +411,8 @@ var mcode = function(ast) {
// emit_numeric_binop: emit type-guarded numeric binary op
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
var emit_numeric_binop = function(op_str) {
if ((is_known_number(_bp_ln) && is_known_number(_bp_rn))
|| (slot_is_num(_bp_left) && slot_is_num(_bp_right))) {
if ((is_known_number(_bp_ln) || slot_is_num(_bp_left))
&& (is_known_number(_bp_rn) || slot_is_num(_bp_right))) {
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
mark_slot(_bp_dest, "num")
return null
@@ -435,239 +436,21 @@ var mcode = function(ast) {
return null
}
// emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false)
// reads _bp_dest, _bp_left, _bp_right from closure
// emit_eq_decomposed: VM eq handles all types (int fast path, text memcmp, identity, mixed→false)
var emit_eq_decomposed = function() {
var dest = _bp_dest
var left = _bp_left
var right = _bp_right
var t0 = 0
var t1 = 0
// Known-num fast path
if ((is_known_number(_bp_ln) || slot_is_num(left))
&& (is_known_number(_bp_rn) || slot_is_num(right))) {
emit_3("eq_float", dest, left, right)
return null
}
// Known-text fast path
if ((is_known_text(_bp_ln) || slot_is_text(left))
&& (is_known_text(_bp_rn) || slot_is_text(right))) {
emit_3("eq_text", dest, left, right)
return null
}
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)
emit_3("eq", _bp_dest, _bp_left, _bp_right)
return null
}
// emit_ne_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(true)
// reads _bp_dest, _bp_left, _bp_right from closure
// emit_ne_decomposed: VM ne handles all types (int fast path, text memcmp, identity, mixed→true)
var emit_ne_decomposed = function() {
var dest = _bp_dest
var left = _bp_left
var right = _bp_right
var t0 = 0
var t1 = 0
// Known-num fast path
if ((is_known_number(_bp_ln) || slot_is_num(left))
&& (is_known_number(_bp_rn) || slot_is_num(right))) {
emit_3("ne_float", dest, left, right)
return null
}
// Known-text fast path
if ((is_known_text(_bp_ln) || slot_is_text(left))
&& (is_known_text(_bp_rn) || slot_is_text(right))) {
emit_3("ne_text", dest, left, right)
return null
}
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)
emit_3("ne", _bp_dest, _bp_left, _bp_right)
return null
}
// emit_relational: int -> float -> text -> disrupt
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
var emit_relational = function(poly_op, int_op, float_op, text_op) {
var dest = _bp_dest
var left = _bp_left
var right = _bp_right
var t0 = 0
var t1 = 0
var left_is_num = is_known_number(_bp_ln) || slot_is_num(left)
var left_is_text = is_known_text(_bp_ln) || slot_is_text(left)
var right_is_num = is_known_number(_bp_rn) || slot_is_num(right)
var right_is_text = is_known_text(_bp_rn) || slot_is_text(right)
var not_num = null
var done = null
var err = null
// Both known number
if (left_is_num && right_is_num) {
emit_3(poly_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_num = gen_label("rel_nn")
done = gen_label("rel_done")
err = gen_label("rel_err")
t0 = alloc_slot()
emit_2("is_num", t0, left)
emit_jump_cond("jump_false", t0, not_num)
t1 = alloc_slot()
emit_2("is_num", t1, right)
emit_jump_cond("jump_false", t1, not_num)
emit_3(poly_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_log_error("cannot compare with '" + _bp_op_sym + "': operands must be same type")
emit_0("disrupt")
emit_label(done)
// emit_relational: VM lt/le/gt/ge handle numbers and text, disrupt on mismatch
var emit_relational = function(op_str) {
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
return null
}
@@ -696,14 +479,7 @@ var mcode = function(ast) {
// Central router: maps op string to decomposition helper
// Sets _bp_* closure vars then calls helper with reduced args
var relational_ops = {
lt: ["lt", "lt_int", "lt_float", "lt_text"],
le: ["le", "le_int", "le_float", "le_text"],
gt: ["gt", "gt_int", "gt_float", "gt_text"],
ge: ["ge", "ge_int", "ge_float", "ge_text"]
}
var emit_binop = function(op_str, dest, left, right) {
var rel = null
_bp_dest = dest
_bp_left = left
_bp_right = right
@@ -714,18 +490,15 @@ var mcode = function(ast) {
emit_eq_decomposed()
} else if (op_str == "ne") {
emit_ne_decomposed()
} else if (op_str == "lt" || op_str == "le" || op_str == "gt" || op_str == "ge") {
emit_relational(op_str)
} else if (op_str == "subtract" || op_str == "multiply" ||
op_str == "divide" || op_str == "modulo" || op_str == "remainder" ||
op_str == "pow") {
emit_numeric_binop(op_str)
} else {
rel = relational_ops[op_str]
if (rel != null) {
emit_relational(rel[0], rel[1], rel[2], rel[3])
} else if (op_str == "subtract" || op_str == "multiply" ||
op_str == "divide" || op_str == "modulo" || op_str == "remainder" ||
op_str == "pow") {
emit_numeric_binop(op_str)
} else {
// Passthrough for bitwise, in, etc.
emit_3(op_str, dest, left, right)
}
// Passthrough for bitwise, in, etc.
emit_3(op_str, dest, left, right)
}
return null
}
@@ -1103,20 +876,20 @@ var mcode = function(ast) {
emit_1("null", null_s)
emit_label(loop_label)
if (forward) {
emit_3("lt_int", check, i, len)
emit_3("lt", check, i, len)
} else {
emit_3("ge_int", check, i, zero)
emit_3("ge", check, i, zero)
}
emit_jump_cond("jump_false", check, done_label)
emit_3("load_index", item, arr_slot, i)
emit_3("eq_int", arity_is_zero, fn_arity, zero)
emit_3("eq", arity_is_zero, fn_arity, zero)
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
emit_3("frame", f, fn_slot, 0)
emit_3("setarg", f, 0, null_s)
emit_2("invoke", f, acc)
emit_jump(call_done_label)
emit_label(call_one_label)
emit_3("eq_int", arity_is_one, fn_arity, one)
emit_3("eq", arity_is_one, fn_arity, one)
emit_jump_cond("jump_false", arity_is_one, call_two_label)
emit_3("frame", f, fn_slot, 1)
emit_3("setarg", f, 0, null_s)
@@ -1164,17 +937,17 @@ var mcode = function(ast) {
emit_1("null", null_s)
emit_2("length", fn_arity, fn_slot)
emit_label(loop_label)
emit_3("lt_int", check, i, len)
emit_3("lt", check, i, len)
emit_jump_cond("jump_false", check, done_label)
emit_3("load_index", item, arr_slot, i)
emit_3("eq_int", arity_is_zero, fn_arity, zero)
emit_3("eq", arity_is_zero, fn_arity, zero)
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
emit_3("frame", f, fn_slot, 0)
emit_3("setarg", f, 0, null_s)
emit_2("invoke", f, discard)
emit_jump(call_done_label)
emit_label(call_one_label)
emit_3("eq_int", arity_is_one, fn_arity, one)
emit_3("eq", arity_is_one, fn_arity, one)
emit_jump_cond("jump_false", arity_is_one, call_two_label)
emit_3("frame", f, fn_slot, 1)
emit_3("setarg", f, 0, null_s)
@@ -1221,10 +994,10 @@ var mcode = function(ast) {
emit_1("null", null_s)
emit_2("length", fn_arity, fn_slot)
emit_label(loop_label)
emit_3("lt_int", check, i, len)
emit_3("lt", check, i, len)
emit_jump_cond("jump_false", check, ret_true)
emit_3("load_index", item, arr_slot, i)
emit_3("eq_int", arity_is_zero, fn_arity, zero)
emit_3("eq", arity_is_zero, fn_arity, zero)
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
emit_3("frame", f, fn_slot, 0)
emit_3("setarg", f, 0, null_s)
@@ -1274,10 +1047,10 @@ var mcode = function(ast) {
emit_1("null", null_s)
emit_2("length", fn_arity, fn_slot)
emit_label(loop_label)
emit_3("lt_int", check, i, len)
emit_3("lt", check, i, len)
emit_jump_cond("jump_false", check, ret_false)
emit_3("load_index", item, arr_slot, i)
emit_3("eq_int", arity_is_zero, fn_arity, zero)
emit_3("eq", arity_is_zero, fn_arity, zero)
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
emit_3("frame", f, fn_slot, 0)
emit_3("setarg", f, 0, null_s)
@@ -1330,17 +1103,17 @@ var mcode = function(ast) {
emit_1("null", null_s)
emit_2("length", fn_arity, fn_slot)
emit_label(loop_label)
emit_3("lt_int", check, i, len)
emit_3("lt", check, i, len)
emit_jump_cond("jump_false", check, done_label)
emit_3("load_index", item, arr_slot, i)
emit_3("eq_int", arity_is_zero, fn_arity, zero)
emit_3("eq", arity_is_zero, fn_arity, zero)
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
emit_3("frame", f, fn_slot, 0)
emit_3("setarg", f, 0, null_s)
emit_2("invoke", f, val)
emit_jump(call_done_label)
emit_label(call_one_label)
emit_3("eq_int", arity_is_one, fn_arity, one)
emit_3("eq", arity_is_one, fn_arity, one)
emit_jump_cond("jump_false", arity_is_one, call_two_label)
emit_3("frame", f, fn_slot, 1)
emit_3("setarg", f, 0, null_s)
@@ -1395,7 +1168,7 @@ var mcode = function(ast) {
if (nargs == 2) {
null_label = gen_label("reduce_null")
d1 = gen_label("reduce_d1")
emit_3("lt_int", check, zero, len)
emit_3("lt", check, zero, len)
emit_jump_cond("jump_false", check, null_label)
emit_3("load_index", acc, arr_slot, zero)
emit_2("move", i, one)
@@ -1414,7 +1187,7 @@ var mcode = function(ast) {
emit_2("is_null", check, init_slot)
emit_jump_cond("jump_false", check, has_init)
// No initial, forward
emit_3("lt_int", check, zero, len)
emit_3("lt", check, zero, len)
emit_jump_cond("jump_false", check, null_label)
emit_3("load_index", acc, arr_slot, zero)
emit_2("move", i, one)
@@ -1446,7 +1219,7 @@ var mcode = function(ast) {
emit_2("is_null", check, init_slot)
emit_jump_cond("jump_false", check, has_init)
// No initial
emit_3("lt_int", check, zero, len)
emit_3("lt", check, zero, len)
emit_jump_cond("jump_false", check, null_label)
emit_jump_cond("jump_true", rev_slot, no_init_rev)
// No initial, forward

View File

@@ -24,70 +24,9 @@ var fmt_val = function(v) {
return text(v)
}
// Classify instruction operands as DEF or USE
// Returns {defs: [operand_positions], uses: [operand_positions]}
// Positions are 1-based indices into the instruction array
var classify_operands = function(op) {
// Binary ops: DEF=[1], USE=[2,3]
if (op == "add" || op == "subtract" || op == "multiply" || op == "divide" ||
op == "modulo" || op == "pow" || op == "remainder" ||
op == "add_int" || op == "sub_int" || op == "mul_int" || op == "div_int" ||
op == "mod_int" || op == "pow_int" || op == "rem_int" ||
op == "add_float" || op == "sub_float" || op == "mul_float" || op == "div_float" ||
op == "mod_float" || op == "pow_float" ||
op == "eq" || op == "ne" || op == "lt" || op == "gt" || op == "le" || op == "ge" ||
op == "eq_int" || op == "ne_int" || op == "lt_int" || op == "gt_int" ||
op == "le_int" || op == "ge_int" ||
op == "eq_float" || op == "ne_float" || op == "lt_float" || op == "gt_float" ||
op == "le_float" || op == "ge_float" ||
op == "eq_text" || op == "ne_text" || op == "lt_text" || op == "gt_text" ||
op == "le_text" || op == "ge_text" ||
op == "eq_bool" || op == "ne_bool" ||
op == "concat" ||
op == "bitand" || op == "bitor" || op == "bitxor" ||
op == "shl" || op == "shr" || op == "ushr" ||
op == "and" || op == "or" ||
op == "is_identical") {
return {defs: [1], uses: [2, 3]}
}
// Unary ops: DEF=[1], USE=[2]
if (op == "not" || op == "negate" || op == "neg_int" || op == "neg_float" ||
op == "bitnot" || op == "typeof" || op == "length" ||
op == "is_int" || op == "is_num" || op == "is_text" || op == "is_bool" ||
op == "is_null" || op == "is_array" || op == "is_func" || op == "is_record" ||
op == "is_stone" || op == "is_integer") {
return {defs: [1], uses: [2]}
}
// Constants: DEF=[1], USE=[]
if (op == "int" || op == "true" || op == "false" || op == "null" || op == "access") {
return {defs: [1], uses: []}
}
if (op == "move") return {defs: [1], uses: [2]}
if (op == "function") return {defs: [1], uses: []}
if (op == "array") return {defs: [1], uses: []}
if (op == "record") return {defs: [1], uses: []}
if (op == "frame") return {defs: [1], uses: [2]}
if (op == "setarg") return {defs: [], uses: [1, 3]}
if (op == "invoke") return {defs: [2], uses: [1]}
if (op == "tail_invoke" || op == "goinvoke") return {defs: [], uses: [1]}
if (op == "load_field") return {defs: [1], uses: [2]}
if (op == "store_field") return {defs: [], uses: [1, 3]}
if (op == "load_index" || op == "load_dynamic") return {defs: [1], uses: [2, 3]}
if (op == "store_index" || op == "store_dynamic") return {defs: [], uses: [1, 2, 3]}
if (op == "push") return {defs: [], uses: [1, 2]}
if (op == "pop") return {defs: [1], uses: [2]}
if (op == "jump_true" || op == "jump_false" || op == "jump_null" || op == "jump_not_null") return {defs: [], uses: [1]}
if (op == "jump") return {defs: [], uses: []}
if (op == "return") return {defs: [], uses: [1]}
if (op == "disrupt") return {defs: [], uses: []}
if (op == "get") return {defs: [1], uses: []}
if (op == "set_var") return {defs: [], uses: [1]}
return {defs: [], uses: []}
}
// DEF/USE functions — populated from streamline's log hooks
var sl_get_defs = null
var sl_get_uses = null
var run = function() {
var filename = null
@@ -137,9 +76,14 @@ var run = function() {
sl_log = {
passes: [],
events: null,
type_deltas: []
type_deltas: [],
request_def_use: true
}
streamline(mcode_copy, sl_log)
if (sl_log.get_slot_defs != null) {
sl_get_defs = sl_log.get_slot_defs
sl_get_uses = sl_log.get_slot_uses
}
if (sl_log.type_deltas != null) {
ti = 0
while (ti < length(sl_log.type_deltas)) {
@@ -181,7 +125,8 @@ var run = function() {
var instr = null
var op = null
var n = 0
var cls = null
var def_positions = null
var use_positions = null
var di = 0
var ui = 0
var slot_num = null
@@ -220,11 +165,12 @@ var run = function() {
op = instr[0]
n = length(instr)
cls = classify_operands(op)
def_positions = sl_get_defs(instr)
use_positions = sl_get_uses(instr)
di = 0
while (di < length(cls.defs)) {
operand_val = instr[cls.defs[di]]
while (di < length(def_positions)) {
operand_val = instr[def_positions[di]]
if (is_number(operand_val)) {
slot_num = text(operand_val)
if (!defs[slot_num]) defs[slot_num] = 0
@@ -239,8 +185,8 @@ var run = function() {
}
ui = 0
while (ui < length(cls.uses)) {
operand_val = instr[cls.uses[ui]]
while (ui < length(use_positions)) {
operand_val = instr[use_positions[ui]]
if (is_number(operand_val)) {
slot_num = text(operand_val)
if (!uses[slot_num]) uses[slot_num] = 0

View File

@@ -214,34 +214,6 @@ typedef enum MachOpcode {
MACH_CONCAT, /* R(A) = R(B) ++ R(C) — string concatenation */
MACH_STONE_TEXT, /* stone(R(A)) — freeze mutable text before escape */
/* Typed integer comparisons (ABC) */
MACH_EQ_INT, /* R(A) = (R(B) == R(C)) — int */
MACH_NE_INT, /* R(A) = (R(B) != R(C)) — int */
MACH_LT_INT, /* R(A) = (R(B) < R(C)) — int */
MACH_LE_INT, /* R(A) = (R(B) <= R(C)) — int */
MACH_GT_INT, /* R(A) = (R(B) > R(C)) — int */
MACH_GE_INT, /* R(A) = (R(B) >= R(C)) — int */
/* Typed float comparisons (ABC) */
MACH_EQ_FLOAT, /* R(A) = (R(B) == R(C)) — float */
MACH_NE_FLOAT, /* R(A) = (R(B) != R(C)) — float */
MACH_LT_FLOAT, /* R(A) = (R(B) < R(C)) — float */
MACH_LE_FLOAT, /* R(A) = (R(B) <= R(C)) — float */
MACH_GT_FLOAT, /* R(A) = (R(B) > R(C)) — float */
MACH_GE_FLOAT, /* R(A) = (R(B) >= R(C)) — float */
/* Typed text comparisons (ABC) */
MACH_EQ_TEXT, /* R(A) = (R(B) == R(C)) — text */
MACH_NE_TEXT, /* R(A) = (R(B) != R(C)) — text */
MACH_LT_TEXT, /* R(A) = (R(B) < R(C)) — text */
MACH_LE_TEXT, /* R(A) = (R(B) <= R(C)) — text */
MACH_GT_TEXT, /* R(A) = (R(B) > R(C)) — text */
MACH_GE_TEXT, /* R(A) = (R(B) >= R(C)) — text */
/* Typed bool comparisons (ABC) */
MACH_EQ_BOOL, /* R(A) = (R(B) == R(C)) — bool */
MACH_NE_BOOL, /* R(A) = (R(B) != R(C)) — bool */
/* Special comparisons */
MACH_IS_IDENTICAL, /* R(A) = (R(B) === R(C)) — identity check (ABC) */
@@ -374,26 +346,6 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
/* Mcode-derived */
[MACH_CONCAT] = "concat",
[MACH_STONE_TEXT] = "stone_text",
[MACH_EQ_INT] = "eq_int",
[MACH_NE_INT] = "ne_int",
[MACH_LT_INT] = "lt_int",
[MACH_LE_INT] = "le_int",
[MACH_GT_INT] = "gt_int",
[MACH_GE_INT] = "ge_int",
[MACH_EQ_FLOAT] = "eq_float",
[MACH_NE_FLOAT] = "ne_float",
[MACH_LT_FLOAT] = "lt_float",
[MACH_LE_FLOAT] = "le_float",
[MACH_GT_FLOAT] = "gt_float",
[MACH_GE_FLOAT] = "ge_float",
[MACH_EQ_TEXT] = "eq_text",
[MACH_NE_TEXT] = "ne_text",
[MACH_LT_TEXT] = "lt_text",
[MACH_LE_TEXT] = "le_text",
[MACH_GT_TEXT] = "gt_text",
[MACH_GE_TEXT] = "ge_text",
[MACH_EQ_BOOL] = "eq_bool",
[MACH_NE_BOOL] = "ne_bool",
[MACH_IS_IDENTICAL] = "is_identical",
[MACH_IS_INT] = "is_int",
[MACH_IS_NUM] = "is_num",
@@ -1083,10 +1035,6 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
}
}
/* String concat for ADD */
if (op == MACH_ADD && mist_is_text(a) && mist_is_text(b))
return JS_ConcatString(ctx, a, b);
/* Comparison ops allow mixed types — return false for mismatches */
if (op >= MACH_EQ && op <= MACH_GE) {
/* Fast path: identical values (chase pointers for forwarded objects) */
@@ -1429,16 +1377,6 @@ vm_dispatch:
DT(MACH_EQ_TOL), DT(MACH_NEQ_TOL),
DT(MACH_NOP),
DT(MACH_CONCAT), DT(MACH_STONE_TEXT),
DT(MACH_EQ_INT), DT(MACH_NE_INT),
DT(MACH_LT_INT), DT(MACH_LE_INT),
DT(MACH_GT_INT), DT(MACH_GE_INT),
DT(MACH_EQ_FLOAT), DT(MACH_NE_FLOAT),
DT(MACH_LT_FLOAT), DT(MACH_LE_FLOAT),
DT(MACH_GT_FLOAT), DT(MACH_GE_FLOAT),
DT(MACH_EQ_TEXT), DT(MACH_NE_TEXT),
DT(MACH_LT_TEXT), DT(MACH_LE_TEXT),
DT(MACH_GT_TEXT), DT(MACH_GE_TEXT),
DT(MACH_EQ_BOOL), DT(MACH_NE_BOOL),
DT(MACH_IS_IDENTICAL),
DT(MACH_IS_INT), DT(MACH_IS_NUM),
DT(MACH_IS_TEXT), DT(MACH_IS_BOOL),
@@ -2365,74 +2303,6 @@ vm_dispatch:
stone_mutable_text(frame->slots[a]);
VM_BREAK();
/* Typed integer comparisons */
VM_CASE(MACH_EQ_INT):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) == JS_VALUE_GET_INT(frame->slots[c]));
VM_BREAK();
VM_CASE(MACH_NE_INT):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) != JS_VALUE_GET_INT(frame->slots[c]));
VM_BREAK();
VM_CASE(MACH_LT_INT):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) < JS_VALUE_GET_INT(frame->slots[c]));
VM_BREAK();
VM_CASE(MACH_LE_INT):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) <= JS_VALUE_GET_INT(frame->slots[c]));
VM_BREAK();
VM_CASE(MACH_GT_INT):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) > JS_VALUE_GET_INT(frame->slots[c]));
VM_BREAK();
VM_CASE(MACH_GE_INT):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) >= JS_VALUE_GET_INT(frame->slots[c]));
VM_BREAK();
/* Typed float comparisons */
VM_CASE(MACH_EQ_FLOAT): VM_CASE(MACH_NE_FLOAT):
VM_CASE(MACH_LT_FLOAT): VM_CASE(MACH_LE_FLOAT):
VM_CASE(MACH_GT_FLOAT): VM_CASE(MACH_GE_FLOAT): {
double da, db;
JS_ToFloat64(ctx, &da, frame->slots[b]);
JS_ToFloat64(ctx, &db, frame->slots[c]);
int r;
switch (op) {
case MACH_EQ_FLOAT: r = (da == db); break;
case MACH_NE_FLOAT: r = (da != db); break;
case MACH_LT_FLOAT: r = (da < db); break;
case MACH_LE_FLOAT: r = (da <= db); break;
case MACH_GT_FLOAT: r = (da > db); break;
case MACH_GE_FLOAT: r = (da >= db); break;
default: r = 0; break;
}
frame->slots[a] = JS_NewBool(ctx, r);
VM_BREAK();
}
/* Typed text comparisons */
VM_CASE(MACH_EQ_TEXT): VM_CASE(MACH_NE_TEXT):
VM_CASE(MACH_LT_TEXT): VM_CASE(MACH_LE_TEXT):
VM_CASE(MACH_GT_TEXT): VM_CASE(MACH_GE_TEXT): {
int cmp = js_string_compare_value(ctx, frame->slots[b], frame->slots[c], FALSE);
int r;
switch (op) {
case MACH_EQ_TEXT: r = (cmp == 0); break;
case MACH_NE_TEXT: r = (cmp != 0); break;
case MACH_LT_TEXT: r = (cmp < 0); break;
case MACH_LE_TEXT: r = (cmp <= 0); break;
case MACH_GT_TEXT: r = (cmp > 0); break;
case MACH_GE_TEXT: r = (cmp >= 0); break;
default: r = 0; break;
}
frame->slots[a] = JS_NewBool(ctx, r);
VM_BREAK();
}
/* Typed bool comparisons */
VM_CASE(MACH_EQ_BOOL):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_BOOL(frame->slots[b]) == JS_VALUE_GET_BOOL(frame->slots[c]));
VM_BREAK();
VM_CASE(MACH_NE_BOOL):
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_BOOL(frame->slots[b]) != JS_VALUE_GET_BOOL(frame->slots[c]));
VM_BREAK();
/* Identity check */
VM_CASE(MACH_IS_IDENTICAL): {
JSValue va = JS_IsPtr(frame->slots[b]) ? JS_MKPTR(chase(frame->slots[b])) : frame->slots[b];
@@ -3105,30 +2975,6 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "le") == 0) { ABC3(MACH_LE); }
else if (strcmp(op, "gt") == 0) { ABC3(MACH_GT); }
else if (strcmp(op, "ge") == 0) { ABC3(MACH_GE); }
/* 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); }
else if (strcmp(op, "lt_int") == 0) { ABC3(MACH_LT_INT); }
else if (strcmp(op, "le_int") == 0) { ABC3(MACH_LE_INT); }
else if (strcmp(op, "gt_int") == 0) { ABC3(MACH_GT_INT); }
else if (strcmp(op, "ge_int") == 0) { ABC3(MACH_GE_INT); }
/* Typed float comparisons */
else if (strcmp(op, "eq_float") == 0) { ABC3(MACH_EQ_FLOAT); }
else if (strcmp(op, "ne_float") == 0) { ABC3(MACH_NE_FLOAT); }
else if (strcmp(op, "lt_float") == 0) { ABC3(MACH_LT_FLOAT); }
else if (strcmp(op, "le_float") == 0) { ABC3(MACH_LE_FLOAT); }
else if (strcmp(op, "gt_float") == 0) { ABC3(MACH_GT_FLOAT); }
else if (strcmp(op, "ge_float") == 0) { ABC3(MACH_GE_FLOAT); }
/* Typed text comparisons */
else if (strcmp(op, "eq_text") == 0) { ABC3(MACH_EQ_TEXT); }
else if (strcmp(op, "ne_text") == 0) { ABC3(MACH_NE_TEXT); }
else if (strcmp(op, "lt_text") == 0) { ABC3(MACH_LT_TEXT); }
else if (strcmp(op, "le_text") == 0) { ABC3(MACH_LE_TEXT); }
else if (strcmp(op, "gt_text") == 0) { ABC3(MACH_GT_TEXT); }
else if (strcmp(op, "ge_text") == 0) { ABC3(MACH_GE_TEXT); }
/* Typed bool comparisons */
else if (strcmp(op, "eq_bool") == 0) { ABC3(MACH_EQ_BOOL); }
else if (strcmp(op, "ne_bool") == 0) { ABC3(MACH_NE_BOOL); }
/* Special comparisons */
else if (strcmp(op, "is_identical") == 0) { ABC3(MACH_IS_IDENTICAL); }
else if (strcmp(op, "eq_tol") == 0) {

View File

@@ -41,14 +41,8 @@ var streamline = function(ir, log) {
max: true, min: true, pow: true
}
var bool_result_ops = {
eq_int: true, ne_int: true, lt_int: true, gt_int: true,
le_int: true, ge_int: true,
eq_float: true, ne_float: true, lt_float: true, gt_float: true,
le_float: true, ge_float: true,
eq_text: true, ne_text: true, lt_text: true, gt_text: true,
le_text: true, ge_text: true,
eq_bool: true, ne_bool: true,
eq_tol: true, ne_tol: true,
eq: true, ne: true, lt: true, gt: true, le: true, ge: true,
eq_tol: true, ne_tol: true, in: true,
not: true, and: true, or: true,
is_int: true, is_text: true, is_num: true,
is_bool: true, is_null: true, is_identical: true,
@@ -63,15 +57,10 @@ var streamline = function(ir, log) {
// simplify_algebra dispatch tables
var self_true_ops = {
eq_int: true, eq_float: true, eq_text: true, eq_bool: true,
is_identical: true,
le_int: true, le_float: true, le_text: true,
ge_int: true, ge_float: true, ge_text: true
eq: true, is_identical: true, le: true, ge: true
}
var self_false_ops = {
ne_int: true, ne_float: true, ne_text: true, ne_bool: true,
lt_int: true, lt_float: true, lt_text: true,
gt_int: true, gt_float: true, gt_text: true
ne: true, lt: true, gt: true
}
var no_clear_ops = {
int: true, access: true, true: true, false: true, move: true, null: true,
@@ -365,13 +354,6 @@ var streamline = function(ir, log) {
load_index: [1, T_UNKNOWN], load_dynamic: [1, T_UNKNOWN],
pop: [1, T_UNKNOWN], get: [1, T_UNKNOWN],
invoke: [2, T_UNKNOWN], tail_invoke: [2, T_UNKNOWN],
eq_int: [1, T_BOOL], ne_int: [1, T_BOOL], lt_int: [1, T_BOOL],
gt_int: [1, T_BOOL], le_int: [1, T_BOOL], ge_int: [1, T_BOOL],
eq_float: [1, T_BOOL], ne_float: [1, T_BOOL], lt_float: [1, T_BOOL],
gt_float: [1, T_BOOL], le_float: [1, T_BOOL], ge_float: [1, T_BOOL],
eq_text: [1, T_BOOL], ne_text: [1, T_BOOL], lt_text: [1, T_BOOL],
gt_text: [1, T_BOOL], le_text: [1, T_BOOL], ge_text: [1, T_BOOL],
eq_bool: [1, T_BOOL], ne_bool: [1, T_BOOL],
eq_tol: [1, T_BOOL], ne_tol: [1, T_BOOL],
not: [1, T_BOOL], and: [1, T_BOOL], or: [1, T_BOOL],
is_int: [1, T_BOOL], is_text: [1, T_BOOL], is_num: [1, T_BOOL],
@@ -1692,6 +1674,54 @@ var streamline = function(ir, log) {
return result
}
// DEF/USE classification: which instruction positions are definitions vs uses
var slot_def_special = {
get: [1], put: [], access: [1], int: [1], function: [1], regexp: [1],
true: [1], false: [1], null: [1], record: [1], array: [1],
invoke: [2], tail_invoke: [2], goinvoke: [],
move: [1], load_field: [1], load_index: [1], load_dynamic: [1],
pop: [1], frame: [1], goframe: [1],
setarg: [], store_field: [], store_index: [], store_dynamic: [],
push: [], set_var: [], stone_text: [],
jump: [], jump_true: [], jump_false: [], jump_not_null: [],
return: [], disrupt: []
}
var slot_use_special = {
get: [], put: [1], access: [], int: [], function: [], regexp: [],
true: [], false: [], null: [], record: [], array: [],
invoke: [1], tail_invoke: [1], goinvoke: [1],
move: [2], load_field: [2], load_index: [2, 3], load_dynamic: [2, 3],
pop: [2], frame: [2], goframe: [2],
setarg: [1, 3], store_field: [1, 3], store_index: [1, 2, 3],
store_dynamic: [1, 2, 3],
push: [1, 2], set_var: [1], stone_text: [1],
jump: [], jump_true: [1], jump_false: [1], jump_not_null: [1],
return: [1], disrupt: []
}
var get_slot_defs = function(instr) {
var special = slot_def_special[instr[0]]
if (special != null) return special
return [1]
}
var get_slot_uses = function(instr) {
var special = slot_use_special[instr[0]]
var result = null
var j = 0
var limit = 0
if (special != null) return special
result = []
limit = length(instr) - 2
j = 2
while (j < limit) {
if (is_number(instr[j])) result[] = j
j = j + 1
}
return result
}
var compress_one_fn = function(func, captured_slots) {
var instructions = func.instructions
var nr_slots = func.nr_slots
@@ -2648,6 +2678,14 @@ var streamline = function(ir, log) {
// Compress slots across all functions (must run after per-function passes)
compress_slots(ir)
// Expose DEF/USE functions via log if requested
if (log != null) {
if (log.request_def_use) {
log.get_slot_defs = get_slot_defs
log.get_slot_uses = get_slot_uses
}
}
return ir
}

View File

@@ -2761,6 +2761,22 @@ run("modulo floats", function() {
if (result < 1.4 || result > 1.6) fail("modulo floats failed")
})
run("remainder float basic", function() {
if (remainder(5.5, 2.5) != 0.5) fail("remainder 5.5 % 2.5 failed")
})
run("modulo float basic", function() {
if (modulo(5.5, 2.5) != 0.5) fail("modulo 5.5 % 2.5 failed")
})
run("remainder float negative", function() {
if (remainder(-5.5, 2.5) != -0.5) fail("remainder -5.5 % 2.5 failed")
})
run("modulo float negative", function() {
if (modulo(-5.5, 2.5) != 2.0) fail("modulo -5.5 % 2.5 failed")
})
// ============================================================================
// MIN AND MAX FUNCTIONS
// ============================================================================