--- source: src/main.rs expression: compiled input_file: test-data/lua5.4-tests/code.lua --- if T == nil { (Message || print)('\n >>> testC not active: skipping opcode tests <<<\n'); return } print("testing code generation and optimizations"); local k0aux = 0 local k0 = k0aux local k1 = 1 local k3 = 3 local k6 = k3 + (k3 << k0) local kFF0 = 0xFF0 local k3_78 = 3.78 local x, k3_78_4 = 10, k3_78 / 4 assert(x == 10); local kx = "x" local kTrue = true local kFalse = false local kNil = nil { local fn f(a) { for k, v, w with a { } } } local fn checkKlist(func, list) { local k = T.listk(func) assert(#k == #list); for i = 1, #k { assert(k[(i)] == list[(i)] && math.type(k[(i)]) == math.type(list[(i)])); } } local fn 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 } checkKlist(foo, { 3.78 / 4, -3.78 / 4, -3.79 / 4 }); foo = fn (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); } checkKlist(foo, { 100000, 100000.0, -100000, -100000.0 }); foo = fn (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 } checkKlist(foo, { 1, 1.0, 2, 2.0, 0, 0.0 }); global fn check(f, ...) { local arg = { ... } local c = T.listcode(f) for i = 1, #arg { local opcode = string.match(c[(i)], "%u%w+") assert(arg[(i)] == opcode); } assert(c[(#arg + 2)] == undef); } global fn checkR(f, p, r, ...) { local r1 = f(p) assert(r == r1 && math.type(r) == math.type(r1)); check(f, ...); } global fn checkequal(a, b) { a = T.listcode(a) b = T.listcode(b) assert(#a == #b); for i = 1, #a { a[(i)] = string.gsub(a[(i)], '%b()', '') b[(i)] = string.gsub(b[(i)], '%b()', '') assert(a[(i)] == b[(i)]); } } check(fn () { (fn () { })({ f() }); }, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0'); check(fn (x) { (fn () { return x })({ f() }); }, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN'); check(fn () { local kNil = nil local a, b, c local d local e local f, g, h d = nil d = nil b = nil a = kNil c = nil }, 'LOADNIL', 'RETURN0'); check(fn () { local a, b, c, d = 1, 1, 1, 1 d = nil c = nil b = nil a = nil }, 'LOADI', 'LOADI', 'LOADI', 'LOADI', 'LOADNIL', 'RETURN0'); { local a, b, c, d = 1, 1, 1, 1 d = nil c = nil b = nil a = nil assert(a == nil && b == nil && c == nil && d == nil); } check(fn (a, b, c) { return a }, 'RETURN1'); check(fn () { while kTrue { local a = -1 } }, 'LOADI', 'JMP', 'RETURN0'); check(fn () { while 1 { local a = -1 } }, 'LOADI', 'JMP', 'RETURN0'); check(fn () { loop { local x = 1 } until true }, 'LOADI', 'RETURN0'); check(fn (a, b, c, d) { return a .. b .. c .. d }, 'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1'); check(fn () { return !!nil }, 'LOADFALSE', 'RETURN1'); check(fn () { return !!kFalse }, 'LOADFALSE', 'RETURN1'); check(fn () { return !!true }, 'LOADTRUE', 'RETURN1'); check(fn () { return !!k3 }, 'LOADTRUE', 'RETURN1'); check(fn () { local a, b, c, d a = b * a c.x, a[(b)] = -((a + d / b - a[(b)]) ^ a.x), b }, 'LOADNIL', 'MUL', 'MMBIN', 'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN', 'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0'); check(fn () { local a, b local c = kNil a[(kx)] = 3.2 a.x = b a[(b)] = 'x' }, 'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0'); check(fn (a) { local k255 = 255 a[(1)] = a[(100)] a[(k255)] = a[(256)] a[(256)] = 5 }, 'GETI', 'SETI', 'LOADI', 'GETTABLE', 'SETI', 'LOADI', 'SETTABLE', 'RETURN0'); check(fn () { local a, b a = a - a b = a / a b = 5 - 4 }, 'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0'); check(fn () { local a, b a[(kTrue)] = false }, 'LOADNIL', 'LOADTRUE', 'SETTABLE', 'RETURN0'); checkR(fn (a) { if a == 1 { return 2 } }, 1, 2, 'EQI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if -4.0 == a { return 2 } }, -4, 2, 'EQI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if a == "hi" { return 2 } }, 10, nil, 'EQK', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if a == 10000 { return 2 } }, 1, nil, 'EQK', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if -10000 == a { return 2 } }, -10000, 2, 'EQK', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if -10 <= a { return 2 } }, -10, 2, 'GEI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if 128.0 > a { return 2 } }, 129, nil, 'LTI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if -127.0 < a { return 2 } }, -127, nil, 'GTI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if 10 < a { return 2 } }, 11, 2, 'GTI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if 129 < a { return 2 } }, 130, 2, 'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if a >= 23.0 { return 2 } }, 25, 2, 'GEI', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if a >= 23.1 { return 2 } }, 0, nil, 'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1'); checkR(fn (a) { if a > 2300.0 { return 2 } }, 0, nil, 'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1'); local fn checkK(func, val) { check(func, 'LOADK', 'RETURN1'); checkKlist(func, { val }); assert(func() == val); } local fn checkI(func, val) { check(func, 'LOADI', 'RETURN1'); checkKlist(func, {}); assert(func() == val); } local fn checkF(func, val) { check(func, 'LOADF', 'RETURN1'); checkKlist(func, {}); assert(func() == val); } checkF(fn () { return 0.0 }, 0.0); checkI(fn () { return k0 }, 0); checkI(fn () { return -k0 /_ 1 }, 0); checkK(fn () { return 3 ^ -1 }, 1 / 3); checkK(fn () { return (1 + 1) ^ (50 + 50) }, 2 ^ 100); checkK(fn () { return (-2) ^ (31 - 2) }, -0x20000000 + 0.0); checkF(fn () { return (-k3 ^ 0 + 5) /_ 3.0 }, 1.0); checkI(fn () { return -k3 % 5 }, 2); checkF(fn () { return -((2.0 ^ 8 + -(-1)) % 8) / 2 * 4 - 3 }, -5.0); checkF(fn () { return -((2 ^ 8 + -(-1)) % 8) /_ 2 * 4 - 3 }, -7.0); checkI(fn () { return 240 | 204 ~ 0xAA & 0xFD }, 0xF4); checkI(fn () { return ^^(^^kFF0 | kFF0) }, 0); checkI(fn () { return ^^ ~ -1024.0 }, -1024); checkI(fn () { return ((100 << k6) << -4) >> 2 }, 100); local a = 17 local sbx = ((1 << a) - 1) >> 1 local border = 65535 checkI(fn () { return border }, sbx); checkI(fn () { return -border }, -sbx); checkI(fn () { return border + 1 }, sbx + 1); checkK(fn () { return border + 2 }, sbx + 2); checkK(fn () { return -(border + 1) }, -(sbx + 1)); local border = 65535.0 checkF(fn () { return border }, sbx + 0.0); checkF(fn () { return -border }, -sbx + 0.0); checkF(fn () { return border + 1 }, (sbx + 1.0)); checkK(fn () { return border + 2 }, (sbx + 2.0)); checkK(fn () { return -(border + 1) }, -(sbx + 1.0)); checkR(fn (x) { return x + k1 }, 10, 11, 'ADDI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x - 127 }, 10, -117, 'ADDI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return 128 + x }, 0.0, 128.0, 'ADDI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x * -127 }, -1.0, 127.0, 'MULK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return 20 * x }, 2, 40, 'MULK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x ^ -2 }, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x / 40 }, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x /_ 1 }, 10.0, 10.0, 'IDIVK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x % (100 - 10) }, 91, 1, 'MODK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return k1 << x }, 3, 8, 'SHLI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x << 127 }, 10, 0, 'SHRI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x << -127 }, 10, 0, 'SHRI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x >> 128 }, 8, 0, 'SHRI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x >> -127 }, 8, 0, 'SHRI', 'MMBINI', 'RETURN1'); checkR(fn (x) { return x & 1 }, 9, 1, 'BANDK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return 10 | x }, 1, 11, 'BORK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return -10 ~ x }, -1, 9, 'BXORK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x + 0.0 }, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x * -10000 }, 2, -20000, 'MULK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x ^ 0.5 }, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x / 2.0 }, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x /_ 10000 }, 10000, 1, 'IDIVK', 'MMBINK', 'RETURN1'); checkR(fn (x) { return x % (100.0 - 10) }, 91, 1.0, 'MODK', 'MMBINK', 'RETURN1'); check(fn () { return -0.0 }, 'LOADF', 'UNM', 'RETURN1'); check(fn () { return k3 / 0 }, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1'); check(fn () { return 0 % 0 }, 'LOADI', 'MODK', 'MMBINK', 'RETURN1'); check(fn () { return -4 /_ 0 }, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1'); check(fn (x) { return x >> 2.0 }, 'LOADF', 'SHR', 'MMBIN', 'RETURN1'); check(fn (x) { return x << 128 }, 'LOADI', 'SHL', 'MMBIN', 'RETURN1'); check(fn (x) { return x & 2.0 }, 'LOADF', 'BAND', 'MMBIN', 'RETURN1'); check(fn () { for i = -10, 10.5 { } }, 'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0'); check(fn () { for i = 0xfffffff, 10.0, 1 { } }, 'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0'); check(fn () { return -nil }, 'LOADNIL', 'UNM', 'RETURN1'); check(fn () { local a, b, c b[(c)], a = c, b b[(a)], a = c, b a, b = c, a a = a }, 'LOADNIL', 'MOVE', 'MOVE', 'SETTABLE', 'MOVE', 'MOVE', 'MOVE', 'SETTABLE', 'MOVE', 'MOVE', 'MOVE', 'RETURN0'); { local t check(fn () { t[(kx)] = t.y }, 'GETTABUP', 'SETTABUP'); check(fn (a) { t[(a())] = t[(a())] }, 'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL', 'GETUPVAL', 'GETTABLE', 'SETTABLE'); } checkequal(fn () { local a if !(a || b) { b = a } }, fn () { local a if (!a && !b) { b = a } }); checkequal(fn (l) { local a return 0 <= a && a <= l }, fn (l) { local a return !(!(a >= 0) || !(a <= l)) }); check(fn (a, b) { while a { if b { break } else { a = a + 1 } } }, 'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0'); checkequal(fn () { return 6 || true || nil }, fn () { return k6 || kTrue || kNil }); checkequal(fn () { return 6 && true || nil }, fn () { return k6 && kTrue || kNil }); { local k0 = "00000000000000000000000000000000000000000000000000" local fn f1() { local k = k0 return fn () { return fn () { return k } } } local f2 = f1() local f3 = f2() assert(f3() == k0); checkK(f3, k0); assert(T.listk(f1)[(1)] == nil); assert(T.listk(f2)[(1)] == nil); } print('OK');