move fold
This commit is contained in:
1
mcode.cm
1
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
|
||||
|
||||
297
streamline.cm
297
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)
|
||||
|
||||
Reference in New Issue
Block a user