str stone; concat

This commit is contained in:
2026-02-20 21:54:19 -06:00
parent a82c13170f
commit fca1041e52
9 changed files with 389 additions and 15 deletions

View File

@@ -78,7 +78,8 @@ var streamline = function(ir, log) {
jump: true, jump_true: true, jump_false: true, jump_not_null: true,
return: true, disrupt: true,
store_field: true, store_index: true, store_dynamic: true,
push: true, setarg: true, invoke: true, tail_invoke: true
push: true, setarg: true, invoke: true, tail_invoke: true,
stone_text: true
}
// --- Logging support ---
@@ -1097,6 +1098,163 @@ var streamline = function(ir, log) {
return null
}
// =========================================================
// Pass: insert_stone_text — freeze mutable text at escape points
// Only inserts stone_text when the slot is provably T_TEXT.
// Escape points: setfield, setindex, store_field, store_index,
// store_dynamic, push, setarg, put (value leaving its slot).
// move: stone source only if source is still live after the move.
// =========================================================
// Map: escape opcode → index of the escaping slot in the instruction
var escape_slot_index = {
setfield: 3, setindex: 3,
store_field: 3, store_index: 3, store_dynamic: 3,
push: 2, setarg: 3, put: 1
}
var insert_stone_text = function(func, log) {
var instructions = func.instructions
var nr_slots = func.nr_slots
var events = null
var slot_types = null
var result = null
var i = 0
var j = 0
var s = 0
var n = 0
var instr = null
var op = null
var esc = null
var slot = 0
var nc = 0
var limit = 0
var first_ref = null
var last_ref = null
var label_map = null
var changed = false
var target = null
var tpos = 0
if (instructions == null || length(instructions) == 0) {
return null
}
if (log != null && log.events != null) {
events = log.events
}
// Build first_ref / last_ref for liveness (needed for move)
first_ref = array(nr_slots, -1)
last_ref = array(nr_slots, -1)
n = length(instructions)
i = 0
while (i < n) {
instr = instructions[i]
if (is_array(instr)) {
j = 1
limit = length(instr) - 2
while (j < limit) {
if (is_number(instr[j]) && instr[j] >= 0 && instr[j] < nr_slots) {
if (first_ref[instr[j]] < 0) first_ref[instr[j]] = i
last_ref[instr[j]] = i
}
j = j + 1
}
}
i = i + 1
}
// Extend for backward jumps (loops)
label_map = {}
i = 0
while (i < n) {
instr = instructions[i]
if (is_text(instr) && !starts_with(instr, "_nop_")) {
label_map[instr] = i
}
i = i + 1
}
changed = true
while (changed) {
changed = false
i = 0
while (i < n) {
instr = instructions[i]
if (is_array(instr)) {
target = null
op = instr[0]
if (op == "jump") {
target = instr[1]
} else if (op == "jump_true" || op == "jump_false" || op == "jump_not_null") {
target = instr[2]
}
if (target != null && is_text(target)) {
tpos = label_map[target]
if (tpos != null && tpos < i) {
s = 0
while (s < nr_slots) {
if (first_ref[s] >= 0 && first_ref[s] < tpos && last_ref[s] >= tpos && last_ref[s] < i) {
last_ref[s] = i
changed = true
}
s = s + 1
}
}
}
}
i = i + 1
}
}
// Walk instructions, tracking types, inserting stone_text
slot_types = array(nr_slots, T_UNKNOWN)
result = []
i = 0
while (i < n) {
instr = instructions[i]
if (is_array(instr)) {
op = instr[0]
esc = escape_slot_index[op]
if (esc != null) {
slot = instr[esc]
if (is_number(slot) && slot_is(slot_types, slot, T_TEXT)) {
result[] = ["stone_text", slot]
nc = nc + 1
if (events != null) {
events[] = {
event: "insert", pass: "insert_stone_text",
rule: "escape_stone", at: i, slot: slot, op: op
}
}
}
} else if (op == "move") {
// Stone source before move only if source is provably text
// AND source slot is still live after this instruction
slot = instr[2]
if (is_number(slot) && slot_is(slot_types, slot, T_TEXT) && last_ref[slot] > i) {
result[] = ["stone_text", slot]
nc = nc + 1
if (events != null) {
events[] = {
event: "insert", pass: "insert_stone_text",
rule: "move_alias_stone", at: i, slot: slot
}
}
}
}
track_types(slot_types, instr)
}
result[] = instr
i = i + 1
}
if (nc > 0) {
func.instructions = result
}
return null
}
// =========================================================
// Pass: eliminate_unreachable — nop code after return/disrupt
// =========================================================
@@ -1299,7 +1457,8 @@ var streamline = function(ir, log) {
frame: [1, 2], goframe: [1, 2],
jump: [], disrupt: [],
jump_true: [1], jump_false: [1], jump_not_null: [1],
return: [1]
return: [1],
stone_text: [1]
}
var get_slot_refs = function(instr) {
@@ -2116,6 +2275,12 @@ var streamline = function(ir, log) {
})
if (verify_fn) verify_fn(func, "after " + name)
name = "insert_stone_text" + suffix
run_pass(func, name, function() {
return insert_stone_text(func, log)
})
if (verify_fn) verify_fn(func, "after " + name)
name = "eliminate_unreachable" + suffix
run_pass(func, name, function() {
return eliminate_unreachable(func)