reduce mcode

This commit is contained in:
2026-02-20 19:53:29 -06:00
parent 611d538e9f
commit cf3c2c9c5f
3 changed files with 174 additions and 25 deletions

View File

@@ -557,7 +557,7 @@ var mcode = function(ast) {
// emit_relational: int -> float -> text -> disrupt
// reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure
var emit_relational = function(int_op, float_op, text_op) {
var emit_relational = function(poly_op, int_op, float_op, text_op) {
var dest = _bp_dest
var left = _bp_left
var right = _bp_right
@@ -590,26 +590,17 @@ var mcode = function(ast) {
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)
t1 = alloc_slot()
emit_2("is_num", t1, right)
emit_jump_cond("jump_false", t1, not_num)
emit_3(float_op, dest, left, right)
emit_3(poly_op, dest, left, right)
emit_jump(done)
emit_label(not_num)
@@ -651,10 +642,10 @@ 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_int", "lt_float", "lt_text"],
le: ["le_int", "le_float", "le_text"],
gt: ["gt_int", "gt_float", "gt_text"],
ge: ["ge_int", "ge_float", "ge_text"]
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
@@ -671,7 +662,7 @@ var mcode = function(ast) {
} else {
rel = relational_ops[op_str]
if (rel != null) {
emit_relational(rel[0], rel[1], rel[2])
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") {

View File

@@ -1112,7 +1112,10 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
default: break;
}
}
/* Different types: EQ→false, NEQ→true, others→false */
/* Different types for ordering comparisons: disrupt */
if (op >= MACH_LT && op <= MACH_GE)
return JS_RaiseDisrupt(ctx, "cannot compare: operands must be same type");
/* EQ/NEQ with different types: false/true */
if (op == MACH_NEQ) return JS_NewBool(ctx, 1);
return JS_NewBool(ctx, 0);
}
@@ -3045,6 +3048,13 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "ceiling") == 0) { ABC3(MACH_CEILING); }
else if (strcmp(op, "round") == 0) { ABC3(MACH_ROUND); }
else if (strcmp(op, "trunc") == 0) { ABC3(MACH_TRUNC); }
/* Generic comparisons */
else if (strcmp(op, "eq") == 0) { ABC3(MACH_EQ); }
else if (strcmp(op, "ne") == 0) { ABC3(MACH_NEQ); }
else if (strcmp(op, "lt") == 0) { ABC3(MACH_LT); }
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); }

View File

@@ -1212,7 +1212,7 @@ var streamline = function(ir, log) {
idx = 0
while (idx < num_instr) {
if (!reachable[idx] && is_array(instructions[idx])) {
if (!reachable[idx] && is_array(instructions[idx]) && (disruption_pc < 0 || idx >= disruption_pc)) {
nc = nc + 1
instructions[idx] = "_nop_ucfg_" + text(nc)
}
@@ -1222,6 +1222,145 @@ var streamline = function(ir, log) {
return null
}
// =========================================================
// Pass: hoist_loop_invariant — move loop-invariant `get`
// instructions before the loop header. A `get` is invariant
// if the closure slot it reads is not `put` inside the loop.
// Uses in-place nop swapping to preserve instruction indices.
// =========================================================
var hoist_loop_invariant = function(func) {
var instructions = func.instructions
var num_instr = 0
var label_map = null
var i = 0
var j = 0
var k = 0
var instr = null
var candidate = null
var tgt_label = null
var tgt_idx = 0
var loop_start = 0
var loop_end = 0
var put_slots = null
var write_regs = null
var wr = null
var src_slot = 0
var safe_nop = -1
var nc = 0
if (instructions == null || length(instructions) == 0) {
return null
}
num_instr = length(instructions)
// Build label → index map
label_map = {}
i = 0
while (i < num_instr) {
instr = instructions[i]
if (is_text(instr) && !starts_with(instr, "_nop_")) {
label_map[instr] = i
}
i = i + 1
}
// Find backward jumps (loop back-edges) and hoist invariant gets
i = 0
while (i < num_instr) {
instr = instructions[i]
if (is_array(instr) && instr[0] == "jump") {
tgt_label = instr[1]
tgt_idx = label_map[tgt_label]
if (is_number(tgt_idx) && tgt_idx <= i) {
loop_start = tgt_idx
loop_end = i
// Collect closure slots written by put and registers
// written by any instruction inside the loop
put_slots = {}
write_regs = {}
j = loop_start
while (j <= loop_end) {
instr = instructions[j]
if (is_array(instr)) {
if (instr[0] == "put") {
put_slots[text(instr[2])] = true
}
// Track destination registers written by instructions
wr = write_rules[instr[0]]
if (wr != null && wr[0] == 1 && instr[0] != "get") {
write_regs[text(instr[1])] = true
}
if (instr[0] == "move") {
write_regs[text(instr[1])] = true
}
if (instr[0] == "invoke" || instr[0] == "tail_invoke") {
write_regs[text(instr[1])] = true
}
}
j = j + 1
}
// Find safe nop position: scan backward from loop_start
// for a nop in the linear fall-through path (no jumps between)
safe_nop = -1
k = loop_start - 1
while (k >= 0) {
candidate = instructions[k]
if (is_text(candidate) && starts_with(candidate, "_nop_")) {
safe_nop = k
k = -1
}
if (is_array(candidate)) {
// Hit an actual instruction — stop if it's a jump or branch
if (candidate[0] == "jump" || candidate[0] == "jump_true" || candidate[0] == "jump_false" || candidate[0] == "jump_not_null" || candidate[0] == "return" || candidate[0] == "disrupt") {
k = -1
}
}
k = k - 1
}
// Find and hoist invariant get instructions
if (safe_nop >= 0) {
j = loop_start
while (j <= loop_end) {
instr = instructions[j]
if (is_array(instr) && instr[0] == "get") {
src_slot = instr[2]
if (put_slots[text(src_slot)] != true && write_regs[text(instr[1])] != true && safe_nop >= 0) {
instructions[safe_nop] = instr
nc = nc + 1
instructions[j] = "_nop_hli_" + text(nc)
// Find next safe nop for additional gets
k = safe_nop - 1
safe_nop = -1
while (k >= 0) {
candidate = instructions[k]
if (is_text(candidate) && starts_with(candidate, "_nop_")) {
safe_nop = k
k = -1
}
if (is_array(candidate)) {
if (candidate[0] == "jump" || candidate[0] == "jump_true" || candidate[0] == "jump_false" || candidate[0] == "jump_not_null" || candidate[0] == "return" || candidate[0] == "disrupt") {
k = -1
}
}
k = k - 1
}
}
}
j = j + 1
}
}
}
}
i = i + 1
}
return null
}
// =========================================================
// Pass: eliminate_dead_jumps — jump to next label → nop
// =========================================================
@@ -2068,12 +2207,14 @@ var streamline = function(ir, log) {
var slot_types = null
var run_cycle = function(suffix) {
var name = null
name = "infer_param_types" + suffix
run_pass(func, name, function() {
param_types = infer_param_types(func)
return param_types
})
if (verify_fn) verify_fn(func, "after " + name)
if (param_types == null) {
name = "infer_param_types" + suffix
run_pass(func, name, function() {
param_types = infer_param_types(func)
return param_types
})
if (verify_fn) verify_fn(func, "after " + name)
}
name = "infer_slot_write_types" + suffix
run_pass(func, name, function() {
@@ -2127,6 +2268,12 @@ var streamline = function(ir, log) {
return eliminate_dead_jumps(func, log)
})
if (verify_fn) verify_fn(func, "after " + name)
name = "eliminate_unreachable_cfg" + suffix
run_pass(func, name, function() {
return eliminate_unreachable_cfg(func)
})
if (verify_fn) verify_fn(func, "after " + name)
return null
}
@@ -2135,6 +2282,7 @@ var streamline = function(ir, log) {
}
run_cycle("")
run_cycle("_2")
if (ir._warn) {
diagnose_function(func, {param_types: param_types, write_types: write_types}, ir)
}