Another way to compile goto's

The compilation of a goto or a label just create an entry and generate
boilerplate code for the gotos. As we don't know yet whether it needs a
CLOSE, we code a jump followed by a CLOSE, which is then dead code.

When a block ends (and then we know for sure whether there are variables
that need to be closed), we check the goto's against the labels of that
block. When closing a goto against a label, if it needs a CLOSE, the
compiler swaps the order of the jump and the CLOSE, making the CLOSE
active.
This commit is contained in:
Roberto Ierusalimschy
2025-01-10 13:54:51 -03:00
parent 8a3a49250c
commit 7ca3c40b50
6 changed files with 120 additions and 122 deletions

View File

@@ -412,13 +412,22 @@ checkequal(function (l) local a; return 0 <= a and a <= l end,
function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
-- if-break optimizations
check(function (a, b)
while a do
if b then break else a = a + 1 end
end
end,
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
'TEST', 'JMP', 'TEST', 'JMP', 'JMP', 'CLOSE', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
check(function ()
do
goto exit -- don't need to close
local x <close> = nil
goto exit -- must close
end
::exit::
end, 'JMP', 'CLOSE', 'LOADNIL', 'TBC',
'CLOSE', 'JMP', 'CLOSE', 'RETURN')
checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end)

View File

@@ -128,7 +128,7 @@ then
else
a=2
end
]], {2,3,4,7})
]], {2,4,7})
test([[

View File

@@ -250,21 +250,36 @@ assert(testG(3) == "3")
assert(testG(4) == 5)
assert(testG(5) == 10)
do
-- if x back goto out of scope of upvalue
local X
goto L1
do -- test goto's around to-be-closed variable
::L2:: goto L3
::L1:: do
local a <close> = setmetatable({}, {__close = function () X = true end})
assert(X == nil)
if a then goto L2 end -- jumping back out of scope of 'a'
-- set 'var' and return an object that will reset 'var' when
-- it goes out of scope
local function newobj (var)
_ENV[var] = true
return setmetatable({}, {__close = function ()
_ENV[var] = nil
end})
end
::L3:: assert(X == true) -- checks that 'a' was correctly closed
goto L1
::L4:: assert(not X); goto L5 -- varX dead here
::L1::
local varX <close> = newobj("X")
assert(X); goto L2 -- varX alive here
::L3::
assert(X); goto L4 -- varX alive here
::L2:: assert(X); goto L3 -- varX alive here
::L5:: -- return
end
foo()
--------------------------------------------------------------------------------