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_int = is_known_int(_bp_rn)
|
||||||
var right_is_num = is_known_number(_bp_rn)
|
var right_is_num = is_known_number(_bp_rn)
|
||||||
var right_is_text = is_known_text(_bp_rn)
|
var right_is_text = is_known_text(_bp_rn)
|
||||||
var not_int = null
|
|
||||||
var not_num = null
|
var not_num = null
|
||||||
var done = null
|
var done = null
|
||||||
var err = 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 eliminate_moves = function(func, log) {
|
||||||
var instructions = func.instructions
|
var instructions = func.instructions
|
||||||
@@ -1067,6 +1069,19 @@ var streamline = function(ir, log) {
|
|||||||
var i = 0
|
var i = 0
|
||||||
var instr = null
|
var instr = null
|
||||||
var events = 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) {
|
if (instructions == null || length(instructions) == 0) {
|
||||||
return null
|
return null
|
||||||
@@ -1076,21 +1091,142 @@ var streamline = function(ir, log) {
|
|||||||
events = log.events
|
events = log.events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copies = {}
|
||||||
num_instr = length(instructions)
|
num_instr = length(instructions)
|
||||||
i = 0
|
i = 0
|
||||||
while (i < num_instr) {
|
while (i < num_instr) {
|
||||||
instr = instructions[i]
|
instr = instructions[i]
|
||||||
if (is_array(instr) && instr[0] == "move" && instr[1] == instr[2]) {
|
|
||||||
nc = nc + 1
|
// Labels: clear copies at join points
|
||||||
instructions[i] = "_nop_mv_" + text(nc)
|
if (is_text(instr)) {
|
||||||
if (events != null) {
|
if (!starts_with(instr, "_nop_")) {
|
||||||
events[] = {
|
copies = {}
|
||||||
event: "rewrite", pass: "eliminate_moves",
|
}
|
||||||
rule: "self_move", at: i,
|
i = i + 1
|
||||||
before: instr, after: instructions[i]
|
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
|
i = i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1222,145 +1358,6 @@ var streamline = function(ir, log) {
|
|||||||
return null
|
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
|
// Pass: eliminate_dead_jumps — jump to next label → nop
|
||||||
// =========================================================
|
// =========================================================
|
||||||
@@ -1393,6 +1390,10 @@ var streamline = function(ir, log) {
|
|||||||
while (j < num_instr) {
|
while (j < num_instr) {
|
||||||
peek = instructions[j]
|
peek = instructions[j]
|
||||||
if (is_text(peek)) {
|
if (is_text(peek)) {
|
||||||
|
if (starts_with(peek, "_nop_")) {
|
||||||
|
j = j + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (peek == target_label) {
|
if (peek == target_label) {
|
||||||
nc = nc + 1
|
nc = nc + 1
|
||||||
instructions[i] = "_nop_dj_" + text(nc)
|
instructions[i] = "_nop_dj_" + text(nc)
|
||||||
|
|||||||
Reference in New Issue
Block a user