lua/testes/code.lua
Roberto Ierusalimschy 4cd1f4aac0 Towards "to closed" local variables
Start of the implementation of "scoped variables" or "to be closed"
variables, local variables whose '__close' (or themselves) are called
when they go out of scope. This commit implements the syntax, the
opcode, and the creation of the corresponding upvalue, but it still
does not call the finalizations when the variable goes out of scope
(the most important part).

Currently, the syntax is 'local scoped name = exp', but that will
probably change.
2018-10-08 10:42:07 -03:00

352 lines
9.7 KiB
Lua

-- $Id: testes/code.lua $
-- See Copyright Notice in file all.lua
if T==nil then
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
return
end
print "testing code generation and optimizations"
-- this code gave an error for the code checker
do
local function f (a)
for k,v,w in a do end
end
end
-- testing reuse in constant table
local function checkKlist (func, list)
local k = T.listk(func)
assert(#k == #list)
for i = 1, #k do
assert(k[i] == list[i] and math.type(k[i]) == math.type(list[i]))
end
end
local function foo ()
local a
a = 3;
a = 0; a = 0.0; a = -7 + 7
a = 3.78/4; a = 3.78/4
a = -3.78/4; a = 3.78/4; a = -3.78/4
a = -3.79/4; a = 0.0; a = -0;
a = 3; a = 3.0; a = 3; a = 3.0
end
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
-- testing opcodes
function check (f, ...)
local arg = {...}
local c = T.listcode(f)
for i=1, #arg do
local opcode = string.match(c[i], "%u%w+")
-- print(arg[i], opcode)
assert(arg[i] == opcode)
end
assert(c[#arg+2] == undef)
end
function checkequal (a, b)
a = T.listcode(a)
b = T.listcode(b)
for i = 1, #a do
a[i] = string.gsub(a[i], '%b()', '') -- remove line number
b[i] = string.gsub(b[i], '%b()', '') -- remove line number
assert(a[i] == b[i])
end
end
-- some basic instructions
check(function () -- function does not create upvalues
(function () end){f()}
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0')
check(function (x) -- function creates upvalues
(function () return x end){f()}
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
-- sequence of LOADNILs
check(function ()
local a,b,c
local d; local e;
local f,g,h;
d = nil; d=nil; b=nil; a=nil; c=nil;
end, 'LOADNIL', 'RETURN0')
check(function ()
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
end, 'LOADI', 'LOADI', 'LOADI', 'LOADI', 'LOADNIL', 'RETURN0')
do
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
assert(a == nil and b == nil and c == nil and d == nil)
end
-- single return
check (function (a,b,c) return a end, 'RETURN1')
-- infinite loops
check(function () while true do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () while 1 do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () repeat local x = 1 until true end,
'LOADI', 'RETURN0')
-- concat optimization
check(function (a,b,c,d) return a..b..c..d end,
'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1')
-- not
check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
check(function () return not not false end, 'LOADBOOL', 'RETURN1')
check(function () return not not true end, 'LOADBOOL', 'RETURN1')
check(function () return not not 1 end, 'LOADBOOL', 'RETURN1')
-- direct access to locals
check(function ()
local a,b,c,d
a = b*a
c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
end,
'LOADNIL',
'MUL',
'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETFIELD', 'POW',
'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
-- direct access to constants
check(function ()
local a,b
a.x = 3.2
a.x = b
a[b] = 'x'
end,
'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0')
-- "get/set table" with numeric indices
check(function (a)
a[1] = a[100]
a[255] = a[256]
a[256] = 5
end,
'GETI', 'SETI',
'LOADI', 'GETTABLE', 'SETI',
'LOADI', 'SETTABLE', 'RETURN0')
check(function ()
local a,b
a = a - a
b = a/a
b = 5-4
end,
'LOADNIL', 'SUB', 'DIV', 'LOADI', 'RETURN0')
check(function ()
local a,b
a[true] = false
end,
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
-- equalities
check(function (a) if a == 1 then return 2 end end,
'EQI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if -4.0 == a then return 2 end end,
'EQI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a == "hi" then return 2 end end,
'EQK', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a == 10000 then return 2 end end,
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
check(function (a) if -10000 == a then return 2 end end,
'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
-- comparisons
check(function (a) if -10 <= a then return 2 end end,
'GEI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if 128.0 > a then return 2 end end,
'LTI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if -127.0 < a then return 2 end end,
'GTI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if 10 < a then return 2 end end,
'GTI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if 129 < a then return 2 end end,
'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a >= 23.0 then return 2 end end,
'GEI', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a >= 23.1 then return 2 end end,
'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
check(function (a) if a > 2300.0 then return 2 end end,
'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
-- constant folding
local function checkK (func, val)
check(func, 'LOADK', 'RETURN1')
local k = T.listk(func)
assert(#k == 1 and k[1] == val and math.type(k[1]) == math.type(val))
assert(func() == val)
end
local function checkI (func, val)
check(func, 'LOADI', 'RETURN1')
assert(#T.listk(func) == 0)
assert(func() == val)
end
local function checkF (func, val)
check(func, 'LOADF', 'RETURN1')
assert(#T.listk(func) == 0)
assert(func() == val)
end
checkF(function () return 0.0 end, 0.0)
checkI(function () return 0 end, 0)
checkI(function () return -0//1 end, 0)
checkK(function () return 3^-1 end, 1/3)
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
checkF(function () return (-3^0 + 5) // 3.0 end, 1.0)
checkI(function () return -3 % 5 end, 2)
checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
checkI(function () return ~(~0xFF0 | 0xFF0) end, 0)
checkI(function () return ~~-1024.0 end, -1024)
checkI(function () return ((100 << 6) << -4) >> 2 end, 100)
-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
checkI(function () return 65535 end, sbx)
checkI(function () return -65535 end, -sbx)
checkI(function () return 65536 end, sbx + 1)
checkK(function () return 65537 end, sbx + 2)
checkK(function () return -65536 end, -(sbx + 1))
checkF(function () return 65535.0 end, sbx + 0.0)
checkF(function () return -65535.0 end, -sbx + 0.0)
checkF(function () return 65536.0 end, (sbx + 1.0))
checkK(function () return 65537.0 end, (sbx + 2.0))
checkK(function () return -65536.0 end, -(sbx + 1.0))
-- immediate operands
check(function (x) return x + 1 end, 'ADDI', 'RETURN1')
check(function (x) return 128 + x end, 'ADDI', 'RETURN1')
check(function (x) return x * -127 end, 'MULI', 'RETURN1')
check(function (x) return 20 * x end, 'MULI', 'RETURN1')
check(function (x) return x ^ -2 end, 'POWI', 'RETURN1')
check(function (x) return x / 40 end, 'DIVI', 'RETURN1')
check(function (x) return x // 1 end, 'IDIVI', 'RETURN1')
check(function (x) return x % (100 - 10) end, 'MODI', 'RETURN1')
check(function (x) return 1 << x end, 'SHLI', 'RETURN1')
check(function (x) return x << 2 end, 'SHRI', 'RETURN1')
check(function (x) return x >> 2 end, 'SHRI', 'RETURN1')
check(function (x) return x & 1 end, 'BANDK', 'RETURN1')
check(function (x) return 10 | x end, 'BORK', 'RETURN1')
check(function (x) return -10 ~ x end, 'BXORK', 'RETURN1')
-- no foldings (and immediate operands)
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1')
check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'RETURN1')
-- basic 'for' loops
check(function () for i = -10, 10.5 do end end,
'LOADI', 'LOADK', 'LOADI', 'FORPREP1', 'FORLOOP1', 'RETURN0')
check(function () for i = 0xfffffff, 10.0, 1 do end end,
'LOADK', 'LOADF', 'LOADI', 'FORPREP1', 'FORLOOP1', 'RETURN0')
-- bug in constant folding for 5.1
check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1')
check(function ()
local a,b,c
b[c], a = c, b
b[a], a = c, b
a, b = c, a
a = a
end,
'LOADNIL',
'MOVE', 'MOVE', 'SETTABLE',
'MOVE', 'MOVE', 'MOVE', 'SETTABLE',
'MOVE', 'MOVE', 'MOVE',
-- no code for a = a
'RETURN0')
-- x == nil , x ~= nil
-- checkequal(function (b) if (a==nil) then a=1 end; if a~=nil then a=1 end end,
-- function () if (a==9) then a=1 end; if a~=9 then a=1 end end)
-- check(function () if a==nil then a='a' end end,
-- 'GETTABUP', 'EQ', 'JMP', 'SETTABUP', 'RETURN')
do -- tests for table access in upvalues
local t
check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP')
check(function (a) t[a()] = t[a()] end,
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
'GETUPVAL', 'GETTABLE', 'SETTABLE')
end
-- de morgan
checkequal(function () local a; if not (a or b) then b=a end end,
function () local a; if (not a and not b) then b=a end end)
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-goto optimizations
check(function (a, b, c, d, e)
if a == b then goto l1;
elseif a == c then goto l2;
elseif a == d then goto l2;
else if a == e then goto l3;
else goto l3
end
end
::l1:: ::l2:: ::l3:: ::l4::
end, 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'JMP',
'CLOSE', 'CLOSE', 'CLOSE', 'CLOSE', 'RETURN0')
checkequal(
function (a) while a < 10 do a = a + 1 end end,
function (a) while true do if not(a < 10) then break end; a = a + 1; end end
)
print 'OK'