From 6cac591dc9f2389793aac60618647cc9c8958854 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 20 Feb 2026 20:32:28 -0600 Subject: [PATCH] move fold --- mcode.cm | 1 - streamline.cm | 297 +++++++++++++++++++++++++------------------------- 2 files changed, 149 insertions(+), 149 deletions(-) diff --git a/mcode.cm b/mcode.cm index 92eb9344..8e914d14 100644 --- a/mcode.cm +++ b/mcode.cm @@ -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 diff --git a/streamline.cm b/streamline.cm index 4357d46f..ae1e0048 100644 --- a/streamline.cm +++ b/streamline.cm @@ -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)