123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- -- $Id: testes/code.lua $
- -- See Copyright Notice in file lua.h
- global <const> *
- if T==nil then
- (Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
- return
- end
- print "testing code generation and optimizations"
- -- to test constant propagation
- local k0aux <const> = 0
- local k0 <const> = k0aux
- local k1 <const> = 1
- local k3 <const> = 3
- local k6 <const> = k3 + (k3 << k0)
- local kFF0 <const> = 0xFF0
- local k3_78 <const> = 3.78
- local x, k3_78_4 <const> = 10, k3_78 / 4
- assert(x == 10)
- local kx <const> = "x"
- local kTrue <const> = true
- local kFalse <const> = false
- local kNil <const> = nil
- -- 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 = k3;
- a = 0; a = 0.0; a = -7 + 7
- a = k3_78/4; a = k3_78_4
- a = -k3_78/4; a = k3_78/4; a = -3.78/4
- a = -3.79/4; a = 0.0; a = -0;
- a = k3; a = 3.0; a = 3; a = 3.0
- end
- checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
- foo = function (f, a)
- f(100 * 1000)
- f(100.0 * 1000)
- f(-100 * 1000)
- f(-100 * 1000.0)
- f(100000)
- f(100000.0)
- f(-100000)
- f(-100000.0)
- end
- checkKlist(foo, {100000, 100000.0, -100000, -100000.0})
- -- floats x integers
- foo = function (t, a)
- t[a] = 1; t[a] = 1.0
- t[a] = 1; t[a] = 1.0
- t[a] = 2; t[a] = 2.0
- t[a] = 0; t[a] = 0.0
- t[a] = 1; t[a] = 1.0
- t[a] = 2; t[a] = 2.0
- t[a] = 0; t[a] = 0.0
- end
- checkKlist(foo, {1, 1.0, 2, 2.0, 0, 0.0})
- -- testing opcodes
- -- check that 'f' opcodes match '...'
- local 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
- -- check that 'f' opcodes match '...' and that 'f(p) == r'.
- local function checkR (f, p, r, ...)
- local r1 = f(p)
- assert(r == r1 and math.type(r) == math.type(r1))
- check(f, ...)
- end
- -- check that 'a' and 'b' has the same opcodes
- local function checkequal (a, b)
- a = T.listcode(a)
- b = T.listcode(b)
- assert(#a == #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', 'EXTRAARG', 'GETTABUP', 'CALL',
- 'SETLIST', 'CALL', 'RETURN0')
- check(function (x) -- function creates upvalues
- (function () return x end){f()}
- end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
- 'SETLIST', 'CALL', 'RETURN')
- -- sequence of LOADNILs
- check(function ()
- local kNil <const> = nil
- local a,b,c
- local d; local e;
- local f,g,h;
- d = nil; d=nil; b=nil; a=kNil; 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 kTrue 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, 'LOADFALSE', 'RETURN1')
- check(function () return not not kFalse end, 'LOADFALSE', 'RETURN1')
- check(function () return not not true end, 'LOADTRUE', 'RETURN1')
- check(function () return not not k3 end, 'LOADTRUE', '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', 'MMBIN',
- 'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN',
- 'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
- -- direct access to constants
- check(function ()
- local a,b
- local c = kNil
- a[kx] = 3.2
- a.x = b
- a[b] = 'x'
- end,
- 'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0')
- -- "get/set table" with numeric indices
- check(function (a)
- local k255 <const> = 255
- a[1] = a[100]
- a[k255] = 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', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
- check(function ()
- local a,b
- a[kTrue] = false
- end,
- 'LOADNIL', 'LOADTRUE', 'SETTABLE', 'RETURN0')
- -- equalities
- checkR(function (a) if a == 1 then return 2 end end, 1, 2,
- 'EQI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if -4.0 == a then return 2 end end, -4, 2,
- 'EQI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if a == "hi" then return 2 end end, 10, nil,
- 'EQK', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if a == 10000 then return 2 end end, 1, nil,
- 'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
- checkR(function (a) if -10000 == a then return 2 end end, -10000, 2,
- 'EQK', 'JMP', 'LOADI', 'RETURN1') -- number too large
- -- comparisons
- checkR(function (a) if -10 <= a then return 2 end end, -10, 2,
- 'GEI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if 128.0 > a then return 2 end end, 129, nil,
- 'LTI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if -127.0 < a then return 2 end end, -127, nil,
- 'GTI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if 10 < a then return 2 end end, 11, 2,
- 'GTI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if 129 < a then return 2 end end, 130, 2,
- 'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if a >= 23.0 then return 2 end end, 25, 2,
- 'GEI', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if a >= 23.1 then return 2 end end, 0, nil,
- 'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
- checkR(function (a) if a > 2300.0 then return 2 end end, 0, nil,
- 'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
- -- constant folding
- local function checkK (func, val)
- check(func, 'LOADK', 'RETURN1')
- checkKlist(func, {val})
- assert(func() == val)
- end
- local function checkI (func, val)
- check(func, 'LOADI', 'RETURN1')
- checkKlist(func, {})
- assert(func() == val)
- end
- local function checkF (func, val)
- check(func, 'LOADF', 'RETURN1')
- checkKlist(func, {})
- assert(func() == val)
- end
- checkF(function () return 0.0 end, 0.0)
- checkI(function () return k0 end, 0)
- checkI(function () return -k0//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 (-k3^0 + 5) // 3.0 end, 1.0)
- checkI(function () return -k3 % 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 ~(~kFF0 | kFF0) end, 0)
- checkI(function () return ~~-1024.0 end, -1024)
- checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
- -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
- local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
- local border <const> = 65535
- checkI(function () return border end, sbx)
- checkI(function () return -border end, -sbx)
- checkI(function () return border + 1 end, sbx + 1)
- checkK(function () return border + 2 end, sbx + 2)
- checkK(function () return -(border + 1) end, -(sbx + 1))
- local border <const> = 65535.0
- checkF(function () return border end, sbx + 0.0)
- checkF(function () return -border end, -sbx + 0.0)
- checkF(function () return border + 1 end, (sbx + 1.0))
- checkK(function () return border + 2 end, (sbx + 2.0))
- checkK(function () return -(border + 1) end, -(sbx + 1.0))
- -- immediate operands
- checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x - 127 end, 10, -117, 'ADDI', 'MMBINI', 'RETURN1')
- checkR(function (x) return 128 + x end, 0.0, 128.0,
- 'ADDI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x * -127 end, -1.0, 127.0,
- 'MULK', 'MMBINK', 'RETURN1')
- checkR(function (x) return 20 * x end, 2, 40, 'MULK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x / 40 end, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x // 1 end, 10.0, 10.0,
- 'IDIVK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x % (100 - 10) end, 91, 1,
- 'MODK', 'MMBINK', 'RETURN1')
- checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x << 127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x << -127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x >> 128 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x >> -127 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
- checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'MMBINK', 'RETURN1')
- checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'MMBINK', 'RETURN1')
- checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'MMBINK', 'RETURN1')
- -- K operands in arithmetic operations
- checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1')
- -- check(function (x) return 128 + x end, 'ADDK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x * -10000 end, 2, -20000,
- 'MULK', 'MMBINK', 'RETURN1')
- -- check(function (x) return 20 * x end, 'MULK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x // 10000 end, 10000, 1,
- 'IDIVK', 'MMBINK', 'RETURN1')
- checkR(function (x) return x % (100.0 - 10) end, 91, 1.0,
- 'MODK', 'MMBINK', 'RETURN1')
- -- no foldings (and immediate operands)
- check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
- check(function () return k3/0 end, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1')
- check(function () return 0%0 end, 'LOADI', 'MODK', 'MMBINK', 'RETURN1')
- check(function () return -4//0 end, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1')
- check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'MMBIN', 'RETURN1')
- check(function (x) return x << 128 end, 'LOADI', 'SHL', 'MMBIN', 'RETURN1')
- check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'MMBIN', 'RETURN1')
- -- basic 'for' loops
- check(function () for i = -10, 10.5 do end end,
- 'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
- check(function () for i = 0xfffffff, 10.0, 1 do end end,
- 'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', '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[kx] = 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, b; if not (a or b) then b=a end end,
- function () local a, b; 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)
- check(function (a, b)
- while a do
- if b then break else a = a + 1 end
- end
- end,
- '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)
- checkequal(function () return 6 and true or nil end,
- function () return k6 and kTrue or kNil end)
- do -- string constants
- local k0 <const> = "00000000000000000000000000000000000000000000000000"
- local function f1 ()
- local k <const> = k0
- return function ()
- return function () return k end
- end
- end
- local f2 = f1()
- local f3 = f2()
- assert(f3() == k0)
- checkK(f3, k0)
- -- string is not needed by other functions
- assert(T.listk(f1)[1] == nil)
- assert(T.listk(f2)[1] == nil)
- end
- do -- check number of available registers
- -- 1 register for local + 1 for function + 252 arguments
- local source = "local a; return a(" .. string.rep("a, ", 252) .. "a)"
- local prog = T.listcode(assert(load(source)))
- -- maximum valid register is 254
- for i = 1, 254 do
- assert(string.find(prog[2 + i], "MOVE%s*" .. i))
- end
- -- one more argument would need register #255 (but that is reserved)
- source = "local a; return a(" .. string.rep("a, ", 253) .. "a)"
- local _, msg = load(source)
- assert(string.find(msg, "too many registers"))
- end
- do -- basic check for SETLIST
- -- create a list constructor with 50 elements
- local source = "local a; return {" .. string.rep("a, ", 50) .. "}"
- local func = assert(load(source))
- local code = table.concat(T.listcode(func), "\n")
- local _, count = string.gsub(code, "SETLIST", "")
- -- code uses only 1 SETLIST for the constructor
- assert(count == 1)
- end
- do print("testing code for integer limits")
- local function checkints (n)
- local source = string.format(
- "local a = {[true] = 0X%x}; return a[true]", n)
- local f = assert(load(source))
- checkKlist(f, {n})
- assert(f() == n)
- f = load(string.dump(f))
- assert(f() == n)
- end
- checkints(math.maxinteger)
- checkints(math.mininteger)
- checkints(-1)
- end
- print 'OK'
|