fix gc closure shortening
This commit is contained in:
@@ -1598,6 +1598,8 @@ var streamline = function(ir, log) {
|
||||
var found = false
|
||||
var anc_remap = null
|
||||
var old_slot = 0
|
||||
var max_close = null
|
||||
var needed = 0
|
||||
var fi = 0
|
||||
var i = 0
|
||||
var j = 0
|
||||
@@ -1703,6 +1705,8 @@ var streamline = function(ir, log) {
|
||||
}
|
||||
|
||||
// Fix get/put parent_slot references using ancestor remap tables
|
||||
// and track the max close slot per ancestor for nr_close_slots update
|
||||
max_close = array(func_count + 1, -1)
|
||||
fi = 0
|
||||
while (fi < func_count) {
|
||||
instrs = functions[fi].instructions
|
||||
@@ -1725,6 +1729,9 @@ var streamline = function(ir, log) {
|
||||
instr[2] = anc_remap[old_slot]
|
||||
}
|
||||
}
|
||||
if (ancestor >= 0 && instr[2] > max_close[ancestor]) {
|
||||
max_close[ancestor] = instr[2]
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
@@ -1732,6 +1739,26 @@ var streamline = function(ir, log) {
|
||||
fi = fi + 1
|
||||
}
|
||||
|
||||
// Update nr_close_slots for functions whose close slots were remapped.
|
||||
// Frame shortening keeps 1 + nr_args + nr_close_slots slots, so
|
||||
// nr_close_slots must cover the highest-numbered close slot.
|
||||
fi = 0
|
||||
while (fi < func_count) {
|
||||
if (max_close[fi] >= 0) {
|
||||
needed = max_close[fi] - (functions[fi].nr_args != null ? functions[fi].nr_args : 0)
|
||||
if (needed > functions[fi].nr_close_slots) {
|
||||
functions[fi].nr_close_slots = needed
|
||||
}
|
||||
}
|
||||
fi = fi + 1
|
||||
}
|
||||
if (max_close[func_count] >= 0 && ir.main != null) {
|
||||
needed = max_close[func_count] - (ir.main.nr_args != null ? ir.main.nr_args : 0)
|
||||
if (needed > ir.main.nr_close_slots) {
|
||||
ir.main.nr_close_slots = needed
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
151
vm_suite.ce
151
vm_suite.ce
@@ -5539,6 +5539,157 @@ run("gc blob forward pointer chase", function() {
|
||||
}
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// GC CLOSURE FRAME SHORTENING
|
||||
// Verify that closure-captured variables survive GC collection, particularly
|
||||
// when the streamline optimizer remaps close slots to different positions.
|
||||
// ============================================================================
|
||||
|
||||
var force_gc = function() {
|
||||
var _g = 0
|
||||
var _gx = null
|
||||
for (_g = 0; _g < 200; _g = _g + 1) {
|
||||
_gx = {a: _g, b: [1, 2, 3], c: "garbage"}
|
||||
}
|
||||
}
|
||||
|
||||
run("gc closure basic - captured function survives gc", function() {
|
||||
var make = function() {
|
||||
function helper() { return 42 }
|
||||
var obj = { call() { return helper() } }
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
assert_eq(obj.call(), 42, "captured function should survive GC")
|
||||
})
|
||||
|
||||
run("gc closure - captured variable survives gc", function() {
|
||||
var make = function() {
|
||||
var val = 99
|
||||
var obj = { get() { return val } }
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
assert_eq(obj.get(), 99, "captured variable should survive GC")
|
||||
})
|
||||
|
||||
run("gc closure - multiple captured variables survive gc", function() {
|
||||
var make = function() {
|
||||
var a = 10
|
||||
var b = 20
|
||||
var c = 30
|
||||
var obj = {
|
||||
sum() { return a + b + c }
|
||||
}
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
assert_eq(obj.sum(), 60, "all captured vars should survive GC")
|
||||
})
|
||||
|
||||
run("gc closure - captured function and var survive gc", function() {
|
||||
var make = function() {
|
||||
function double(x) { return x * 2 }
|
||||
var base = 5
|
||||
var obj = { compute() { return double(base) } }
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
assert_eq(obj.compute(), 10, "captured fn and var should survive GC")
|
||||
})
|
||||
|
||||
run("gc closure - nested closure chain survives gc", function() {
|
||||
var outer = function() {
|
||||
var x = 7
|
||||
var mid = function() {
|
||||
var y = 3
|
||||
var inner = function() { return x + y }
|
||||
return inner
|
||||
}
|
||||
return mid()
|
||||
}
|
||||
var fn = outer()
|
||||
force_gc()
|
||||
assert_eq(fn(), 10, "nested closure chain should survive GC")
|
||||
})
|
||||
|
||||
run("gc closure - multiple methods share captured frame", function() {
|
||||
var make = function() {
|
||||
var count = 0
|
||||
function inc() { count = count + 1 }
|
||||
function get() { return count }
|
||||
var obj = {
|
||||
increment() { inc() },
|
||||
value() { return get() }
|
||||
}
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
obj.increment()
|
||||
obj.increment()
|
||||
force_gc()
|
||||
obj.increment()
|
||||
assert_eq(obj.value(), 3, "shared closure frame should survive GC")
|
||||
})
|
||||
|
||||
run("gc closure - closure survives repeated gc cycles", function() {
|
||||
var make = function() {
|
||||
var val = 123
|
||||
var obj = { get() { return val } }
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
force_gc()
|
||||
force_gc()
|
||||
assert_eq(obj.get(), 123, "closure should survive repeated GC cycles")
|
||||
})
|
||||
|
||||
run("gc closure - object literal method with temp slot reuse", function() {
|
||||
var make = function() {
|
||||
function helper() { return "ok" }
|
||||
var temp = [1, 2, 3]
|
||||
var unused = {x: temp}
|
||||
var obj = { call() { return helper() } }
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
assert_eq(obj.call(), "ok", "closure should work after temp slots discarded")
|
||||
})
|
||||
|
||||
run("gc closure - closure array survives gc", function() {
|
||||
var make = function() {
|
||||
var items = [10, 20, 30]
|
||||
var obj = {
|
||||
first() { return items[0] },
|
||||
last() { return items[2] }
|
||||
}
|
||||
return obj
|
||||
}
|
||||
var obj = make()
|
||||
force_gc()
|
||||
assert_eq(obj.first(), 10, "captured array first element")
|
||||
assert_eq(obj.last(), 30, "captured array last element")
|
||||
})
|
||||
|
||||
run("gc closure - factory pattern survives gc", function() {
|
||||
var factory = function(name) {
|
||||
function greet() { return "hello " + name }
|
||||
var obj = { say() { return greet() } }
|
||||
return obj
|
||||
}
|
||||
var a = factory("alice")
|
||||
var b = factory("bob")
|
||||
force_gc()
|
||||
assert_eq(a.say(), "hello alice", "first factory closure")
|
||||
assert_eq(b.say(), "hello bob", "second factory closure")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// SUMMARY
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user