-- $Id: code.lua,v 1.42 2016/11/07 13:04:32 roberto Exp $ -- 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, 0, 0.0, 3.78 / 4, -3.78 / 4, -3.79 / 4, 3.0 }) -- testing opcodes function check(f, ...) local arg = { ... } local c = T.listcode(f) for i = 1, #arg do -- print(arg[i], c[i]) assert(string.find(c[i], '- ' .. arg[i] .. ' *%d')) end assert(c[#arg + 2] == nil) 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() 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', 'RETURN') check(function() local a, b, c, d = 1, 1, 1, 1 d = nil; c = nil; b = nil; a = nil end, 'LOADK', 'LOADK', 'LOADK', 'LOADK', 'LOADNIL', 'RETURN') 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, 'RETURN') -- infinite loops check(function() while true do local a = -1 end end, 'LOADK', 'JMP', 'RETURN') check(function() while 1 do local a = -1 end end, 'LOADK', 'JMP', 'RETURN') check(function() repeat local x = 1 until true end, 'LOADK', 'RETURN') -- concat optimization check(function(a, b, c, d) return a .. b .. c .. d end, 'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN') -- not check(function() return not not nil end, 'LOADBOOL', 'RETURN') check(function() return not not false end, 'LOADBOOL', 'RETURN') check(function() return not not true end, 'LOADBOOL', 'RETURN') check(function() return not not 1 end, 'LOADBOOL', 'RETURN') -- direct access to locals check(function() local a, b, c, d a = b * 2 c[2], a[b] = -((a + d / 2 - a[b]) ^ a.x), b end, 'LOADNIL', 'MUL', 'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETTABLE', 'POW', 'UNM', 'SETTABLE', 'SETTABLE', 'RETURN') -- direct access to constants check(function() local a, b a.x = 3.2 a.x = b a[b] = 'x' end, 'LOADNIL', 'SETTABLE', 'SETTABLE', 'SETTABLE', 'RETURN') check(function() local a, b a = 1 - a b = 1 / a b = 5 - 4 end, 'LOADNIL', 'SUB', 'DIV', 'LOADK', 'RETURN') check(function() local a, b a[true] = false end, 'LOADNIL', 'SETTABLE', 'RETURN') -- constant folding local function checkK(func, val) check(func, 'LOADK', 'RETURN') local k = T.listk(func) assert(#k == 1 and k[1] == val and math.type(k[1]) == math.type(val)) assert(func() == val) end checkK(function() return 0.0 end, 0.0) checkK(function() return 0 end, 0) checkK(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) checkK(function() return (-3 ^ 0 + 5) // 3.0 end, 1.0) checkK(function() return -3 % 5 end, 2) checkK(function() return -((2.0 ^ 8 + -(-1)) % 8) / 2 * 4 - 3 end, -5.0) checkK(function() return -((2 ^ 8 + -(-1)) % 8) // 2 * 4 - 3 end, -7.0) checkK(function() return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4) checkK(function() return ~(~0xFF0 | 0xFF0) end, 0) checkK(function() return ~ ~ -100024.0 end, -100024) checkK(function() return ((100 << 6) << -4) >> 2 end, 100) -- no foldings check(function() return -0.0 end, 'LOADK', 'UNM', 'RETURN') check(function() return 3 / 0 end, 'DIV', 'RETURN') check(function() return 0 % 0 end, 'MOD', 'RETURN') check(function() return -4 // 0 end, 'IDIV', 'RETURN') -- bug in constant folding for 5.1 check(function() return -nil end, 'LOADNIL', 'UNM', 'RETURN') 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 'RETURN') -- x == nil , x ~= nil checkequal(function() 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') -- 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 elseif a == c then elseif a == d then else if a == e then else end end end, 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'JMP', 'RETURN') checkequal( function(a) while a < 10 do a = a + 1 end end, function(a) if not (a < 10) then end ; a = a + 1; end ) 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'