move fold

This commit is contained in:
2026-02-20 20:32:28 -06:00
parent cf3c2c9c5f
commit 6cac591dc9
2 changed files with 149 additions and 149 deletions

View File

@@ -569,7 +569,6 @@ var mcode = function(ast) {
var right_is_int = is_known_int(_bp_rn)
var right_is_num = is_known_number(_bp_rn)
var right_is_text = is_known_text(_bp_rn)
var not_int = null
var not_num = null
var done = null
var err = null

View File

@@ -1058,7 +1058,9 @@ var streamline = function(ir, log) {
}
// =========================================================
// Pass: eliminate_moves — move a, a → nop
// Pass: eliminate_moves — copy propagation + self-move nop
// Tracks move chains within basic blocks, substitutes read
// operands to use the original source, and nops self-moves.
// =========================================================
var eliminate_moves = function(func, log) {
var instructions = func.instructions
@@ -1067,6 +1069,19 @@ var streamline = function(ir, log) {
var i = 0
var instr = null
var events = null
var copies = null
var key = null
var actual = null
var dest = 0
var src = 0
var wr = null
var write_pos = null
var op = null
var j = 0
var k = 0
var keys = null
var special = null
var limit = 0
if (instructions == null || length(instructions) == 0) {
return null
@@ -1076,21 +1091,142 @@ var streamline = function(ir, log) {
events = log.events
}
copies = {}
num_instr = length(instructions)
i = 0
while (i < num_instr) {
instr = instructions[i]
if (is_array(instr) && instr[0] == "move" && instr[1] == instr[2]) {
nc = nc + 1
instructions[i] = "_nop_mv_" + text(nc)
if (events != null) {
events[] = {
event: "rewrite", pass: "eliminate_moves",
rule: "self_move", at: i,
before: instr, after: instructions[i]
// Labels: clear copies at join points
if (is_text(instr)) {
if (!starts_with(instr, "_nop_")) {
copies = {}
}
i = i + 1
continue
}
if (!is_array(instr)) {
i = i + 1
continue
}
op = instr[0]
// Control flow without reads: clear copies
if (op == "jump" || op == "disrupt") {
copies = {}
i = i + 1
continue
}
// Control flow with a read at position 1: substitute then clear
if (op == "return" || op == "jump_true" || op == "jump_false" || op == "jump_not_null") {
actual = copies[text(instr[1])]
if (actual != null) {
instr[1] = actual
}
copies = {}
i = i + 1
continue
}
// Move: copy propagation
if (op == "move") {
dest = instr[1]
src = instr[2]
// Follow transitive chain for src
actual = copies[text(src)]
if (actual == null) {
actual = src
}
// Rewrite the move's src operand
instr[2] = actual
// Kill stale entries for dest
key = text(dest)
copies[key] = null
keys = array(copies)
k = 0
while (k < length(keys)) {
if (copies[keys[k]] == dest) {
copies[keys[k]] = null
}
k = k + 1
}
// Record the new copy
copies[text(dest)] = actual
// Self-move after substitution → nop
if (dest == actual) {
nc = nc + 1
instructions[i] = "_nop_mv_" + text(nc)
if (events != null) {
events[] = {
event: "rewrite", pass: "eliminate_moves",
rule: "self_move", at: i,
before: ["move", dest, src], after: instructions[i]
}
}
}
i = i + 1
continue
}
// General instruction: substitute reads, then kill write
wr = write_rules[op]
write_pos = null
if (wr != null) {
write_pos = wr[0]
}
// Substitute read operands
special = slot_idx_special[op]
if (special != null) {
j = 0
while (j < length(special)) {
k = special[j]
if (k != write_pos && is_number(instr[k])) {
actual = copies[text(instr[k])]
if (actual != null) {
instr[k] = actual
}
}
j = j + 1
}
} else {
limit = length(instr) - 2
j = 1
while (j < limit) {
if (j != write_pos && is_number(instr[j])) {
actual = copies[text(instr[j])]
if (actual != null) {
instr[j] = actual
}
}
j = j + 1
}
}
// Kill write destination
if (write_pos != null && is_number(instr[write_pos])) {
dest = instr[write_pos]
key = text(dest)
copies[key] = null
keys = array(copies)
k = 0
while (k < length(keys)) {
if (copies[keys[k]] == dest) {
copies[keys[k]] = null
}
k = k + 1
}
}
i = i + 1
}
@@ -1222,145 +1358,6 @@ 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
// =========================================================
@@ -1393,6 +1390,10 @@ var streamline = function(ir, log) {
while (j < num_instr) {
peek = instructions[j]
if (is_text(peek)) {
if (starts_with(peek, "_nop_")) {
j = j + 1
continue
}
if (peek == target_label) {
nc = nc + 1
instructions[i] = "_nop_dj_" + text(nc)