diff --git a/source/runtime.c b/source/runtime.c index 36fc55df..ceea3f99 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -1369,7 +1369,7 @@ JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *f /* Frame shortening: returned frames (caller == JS_NULL) only need [this][args][closure_locals] — shrink during copy. */ - if (type == OBJ_FRAME) { + if (0 && type == OBJ_FRAME) { JSFrame *f = (JSFrame *)hdr_ptr; if (JS_IsNull (f->caller) && JS_IsPtr (f->function)) { /* fn may be forwarded, but kind (offset 18) and u.cell.code (offset 24) diff --git a/streamline.cm b/streamline.cm index cf2f327b..ccb50cbe 100644 --- a/streamline.cm +++ b/streamline.cm @@ -1308,25 +1308,18 @@ var streamline = function(ir, log) { var insert_stone_text = function(func, log) { var instructions = func.instructions var nr_slots = func.nr_slots + var dpc = func.disruption_pc 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 + var shift = 0 if (instructions == null || length(instructions) == 0) { return null @@ -1336,72 +1329,10 @@ var streamline = function(ir, log) { 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 = [] + n = length(instructions) i = 0 while (i < n) { instr = instructions[i] @@ -1413,6 +1344,7 @@ var streamline = function(ir, log) { if (is_number(slot) && slot_is(slot_types, slot, T_TEXT)) { result[] = ["stone_text", slot] nc = nc + 1 + if (is_number(dpc) && i < dpc) shift = shift + 1 if (events != null) { events[] = { event: "insert", pass: "insert_stone_text", @@ -1421,12 +1353,12 @@ var streamline = function(ir, log) { } } } else if (op == "move") { - // Stone source before move only if source is provably text - // AND source slot is still live after this instruction + // Conservatively stone source before move if provably text slot = instr[2] - if (is_number(slot) && slot_is(slot_types, slot, T_TEXT) && last_ref[slot] > i) { + if (is_number(slot) && slot_is(slot_types, slot, T_TEXT)) { result[] = ["stone_text", slot] nc = nc + 1 + if (is_number(dpc) && i < dpc) shift = shift + 1 if (events != null) { events[] = { event: "insert", pass: "insert_stone_text", @@ -1443,6 +1375,9 @@ var streamline = function(ir, log) { if (nc > 0) { func.instructions = result + if (is_number(dpc) && shift > 0) { + func.disruption_pc = dpc + shift + } } return null } @@ -2538,12 +2473,6 @@ 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) @@ -2590,6 +2519,7 @@ var streamline = function(ir, log) { // Process main function if (ir.main != null) { optimize_function(ir.main, log) + insert_stone_text(ir.main, log) } // Process all sub-functions (resolve closure types from parent first) @@ -2599,6 +2529,7 @@ var streamline = function(ir, log) { while (fi < length(ir.functions)) { resolve_closure_types(ir.functions[fi], fi, ir) optimize_function(ir.functions[fi], log) + insert_stone_text(ir.functions[fi], log) fi = fi + 1 } } diff --git a/vm_suite.ce b/vm_suite.ce index 0a391df1..b8518ace 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -103,6 +103,29 @@ run("string concatenation empty", function() { if ("" + "world" != "world") fail("empty + string failed") }) +run("string concat does not mutate alias", function() { + var a = "hello world" + var b = a + a = a + " appended" + if (a != "hello world appended") fail("a wrong, got " + a) + if (b != "hello world") fail("b should still be hello world, got " + b) +}) + +run("string concat in loop preserves aliases", function() { + var a = "starting value" + var copies = [a] + var i = 0 + while (i < 5) { + a = a + " more" + copies[] = a + i = i + 1 + } + if (copies[0] != "starting value") fail("copies[0] wrong, got " + copies[0]) + if (copies[1] != "starting value more") fail("copies[1] wrong, got " + copies[1]) + if (copies[5] != "starting value more more more more more") fail("copies[5] wrong, got " + copies[5]) + if (a != "starting value more more more more more") fail("a wrong, got " + a) +}) + // ============================================================================ // TYPE MIXING SHOULD DISRUPT // ============================================================================