mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-05 19:09:11 +00:00
3b0e93bec9
# What's changed? Add program argument passing to scripts run using the Luau REPL! You can now pass `--program-args` (or shorthand `-a`) to the REPL which will treat all remaining arguments as arguments to pass to executed scripts. These values can be accessed through variadic argument expansion. You can read these values like so: ``` local args = {...} -- gets you an array of all the arguments ``` For example if we run the following script like `luau test.lua -a test1 test2 test3`: ``` -- test.lua print(...) ``` you should get the output: ``` test1 test2 test3 ``` ### Native Code Generation * Improve A64 lowering for vector operations by using vector instructions * Fix lowering issue in IR value location tracking! - A developer reported a divergence between code run in the VM and Native Code Generation which we have now fixed ### New Type Solver * Apply substitution to type families, and emit new constraints to reduce those further * More progress on reducing comparison (`lt/le`)type families * Resolve two major sources of cyclic types in the new solver ### Miscellaneous * Turned internal compiler errors (ICE's) into warnings and errors ------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
410 lines
10 KiB
Lua
410 lines
10 KiB
Lua
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
print("testing native code generation")
|
|
|
|
assert((function(x, y)
|
|
-- trigger a linear sequence
|
|
local t1 = x + 2
|
|
local t2 = x - 7
|
|
|
|
local a = x * 10
|
|
local b = a + 1
|
|
a = y -- update 'a' version
|
|
local t = {} -- call to allocate table forces a spill
|
|
local c = x * 10
|
|
return c, b, t, t1, t2
|
|
end)(5, 10) == 50)
|
|
|
|
assert((function(x)
|
|
local oops -- split to prevent inlining
|
|
function oops()
|
|
end
|
|
|
|
-- x is checked to be a number here; we can not execute a reentry from oops() because optimizer assumes this holds until return
|
|
local y = math.abs(x)
|
|
oops()
|
|
return y * x
|
|
end)("42") == 1764)
|
|
|
|
local function fuzzfail1(...)
|
|
repeat
|
|
_ = nil
|
|
until not {}
|
|
for _ in ... do
|
|
for l0=_,_ do
|
|
end
|
|
return
|
|
end
|
|
end
|
|
|
|
local function fuzzfail2()
|
|
local _
|
|
do
|
|
repeat
|
|
_ = typeof(_),{_=_,}
|
|
_ = _(_._)
|
|
until _
|
|
end
|
|
end
|
|
|
|
assert(pcall(fuzzfail2) == false)
|
|
|
|
local function fuzzfail3()
|
|
function _(...)
|
|
_({_,_,true,},{...,},_,not _)
|
|
end
|
|
_()
|
|
end
|
|
|
|
assert(pcall(fuzzfail3) == false)
|
|
|
|
local function fuzzfail4()
|
|
local _ = setmetatable({},setmetatable({_=_,},_))
|
|
return _(_:_())
|
|
end
|
|
|
|
assert(pcall(fuzzfail4) == false)
|
|
|
|
local function fuzzfail5()
|
|
local _ = bit32.band
|
|
_(_(_,0),_)
|
|
_(_,_)
|
|
end
|
|
|
|
assert(pcall(fuzzfail5) == false)
|
|
|
|
local function fuzzfail6(_)
|
|
return bit32.extract(_,671088640,_)
|
|
end
|
|
|
|
assert(pcall(fuzzfail6, 1) == false)
|
|
|
|
local function fuzzfail7(_)
|
|
return bit32.extract(_,_,671088640)
|
|
end
|
|
|
|
assert(pcall(fuzzfail7, 1) == false)
|
|
|
|
local function fuzzfail8(...)
|
|
local _ = _,_
|
|
_.n0,_,_,_,_,_,_,_,_._,_,_,_[...],_,_,_ = nil
|
|
_,n0,_,_,_,_,_,_,_,_,l0,_,_,_,_ = nil
|
|
function _()
|
|
end
|
|
_._,_,_,_,_,_,_,_,_,_,_[...],_,n0[l0],_ = nil
|
|
_[...],_,_,_,_,_,_,_,_()[_],_,_,_,_,_ = _(),...
|
|
end
|
|
|
|
assert(pcall(fuzzfail8) == false)
|
|
|
|
local function fuzzfail9()
|
|
local _ = bit32.bor
|
|
local x = _(_(_,_),_(_,_),_(-16834560,_),_(_(- _,-2130706432)),- _),_(_(_,_),_(-16834560,-2130706432))
|
|
end
|
|
|
|
assert(pcall(fuzzfail9) == false)
|
|
|
|
local function fuzzfail10()
|
|
local _
|
|
_ = false,if _ then _ else _
|
|
_ = not _
|
|
l0,_[l0] = not _
|
|
end
|
|
|
|
assert(pcall(fuzzfail10) == false)
|
|
|
|
local function fuzzfail11(x, ...)
|
|
return bit32.arshift(bit32.bnot(x),(...))
|
|
end
|
|
|
|
assert(fuzzfail11(0xffff0000, 8) == 0xff)
|
|
|
|
local function fuzzfail12()
|
|
_,_,_,_,_,_,_,_ = not _, not _, not _, not _, not _, not _, not _, not _
|
|
end
|
|
|
|
assert(pcall(fuzzfail12) == true)
|
|
|
|
local function fuzzfail13()
|
|
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ = not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _, not _
|
|
end
|
|
|
|
assert(pcall(fuzzfail13) == true)
|
|
|
|
local function fuzzfail14()
|
|
for l0=771751936,_ do
|
|
for l0=771751936,0 do
|
|
while 538970624 do
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
assert(pcall(fuzzfail14) == false)
|
|
|
|
local function fuzzfail15()
|
|
local a
|
|
if a then
|
|
repeat until a
|
|
else
|
|
local b = `{a}`
|
|
a = nil
|
|
end
|
|
end
|
|
|
|
assert(pcall(fuzzfail15) == true)
|
|
|
|
local function fuzzfail16()
|
|
_ = {[{[2]=77,_=_,[2]=_,}]=not _,}
|
|
_ = {77,[2]=11008,[2]=_,[0]=_,}
|
|
end
|
|
|
|
assert(pcall(fuzzfail16) == true)
|
|
|
|
local function fuzzfail17()
|
|
return bit32.extract(1293942816,1293942816)
|
|
end
|
|
|
|
assert(pcall(fuzzfail17) == false)
|
|
|
|
local function fuzzfail18()
|
|
return bit32.extract(7890276,0)
|
|
end
|
|
|
|
assert(pcall(fuzzfail18) == true)
|
|
assert(fuzzfail18() == 0)
|
|
|
|
local function arraySizeInv1()
|
|
local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}
|
|
|
|
table.insert(t, 3)
|
|
|
|
return t[10]
|
|
end
|
|
|
|
assert(arraySizeInv1() == true)
|
|
|
|
local function arraySizeInv2()
|
|
local t = {1, 2, nil, nil, nil, nil, nil, nil, nil, true}
|
|
|
|
local u = {a = t}
|
|
table.insert(u.a, 3) -- aliased modifiction of 't' register through other value
|
|
|
|
return t[10]
|
|
end
|
|
|
|
assert(arraySizeInv2() == true)
|
|
|
|
local function nilInvalidatesSlot()
|
|
local function tabs()
|
|
local t = { x=1, y=2, z=3 }
|
|
setmetatable(t, { __index = function(t, k) return 42 end })
|
|
return t, t
|
|
end
|
|
|
|
local t1, t2 = tabs()
|
|
|
|
for i=1,2 do
|
|
local a = t1.x
|
|
t2.x = nil
|
|
local b = t1.x
|
|
t2.x = 1
|
|
assert(a == 1 and b == 42)
|
|
end
|
|
end
|
|
|
|
nilInvalidatesSlot()
|
|
|
|
local function arraySizeOpt1(a)
|
|
a[1] += 2
|
|
a[1] *= 3
|
|
|
|
table.insert(a, 3)
|
|
table.insert(a, 4)
|
|
table.insert(a, 5)
|
|
table.insert(a, 6)
|
|
|
|
a[1] += 4
|
|
a[1] *= 5
|
|
|
|
return a[1] + a[5]
|
|
end
|
|
|
|
assert(arraySizeOpt1({1}) == 71)
|
|
|
|
local function arraySizeOpt2(a, i)
|
|
a[i] += 2
|
|
a[i] *= 3
|
|
|
|
table.insert(a, 3)
|
|
table.insert(a, 4)
|
|
table.insert(a, 5)
|
|
table.insert(a, 6)
|
|
|
|
a[i] += 4
|
|
a[i] *= 5
|
|
|
|
return a[i] + a[5]
|
|
end
|
|
|
|
assert(arraySizeOpt2({1}, 1) == 71)
|
|
|
|
function deadLoopBody(n)
|
|
local r = 0
|
|
if n and false then
|
|
for i = 1, n do
|
|
r += 1
|
|
end
|
|
end
|
|
return r
|
|
end
|
|
|
|
assert(deadLoopBody(5) == 0)
|
|
|
|
function arrayIndexingSpecialNumbers1(a, b, c)
|
|
local arr = table.create(100000)
|
|
arr[a] = 9
|
|
arr[b-1] = 80
|
|
arr[b] = 700
|
|
arr[b+1] = 6000
|
|
arr[c-1] = 50000
|
|
arr[c] = 400000
|
|
arr[c+1] = 3000000
|
|
|
|
return arr[1] + arr[255] + arr[256] + arr[257] + arr[65535] + arr[65536] + arr[65537]
|
|
end
|
|
|
|
assert(arrayIndexingSpecialNumbers1(1, 256, 65536) == 3456789)
|
|
|
|
function loopIteratorProtocol(a, t)
|
|
local sum = 0
|
|
|
|
do
|
|
local a, b, c, d, e, f, g = {}, {}, {}, {}, {}, {}, {}
|
|
end
|
|
|
|
for k, v in ipairs(t) do
|
|
if k == 10 then sum += math.abs('-8') end
|
|
|
|
sum += k
|
|
end
|
|
|
|
return sum
|
|
end
|
|
|
|
assert(loopIteratorProtocol(0, table.create(100, 5)) == 5058)
|
|
|
|
function valueTrackingIssue1()
|
|
local b = buffer.create(1)
|
|
buffer.writeu8(b, 0, 0)
|
|
local v1
|
|
|
|
local function closure()
|
|
assert(type(b) == "buffer") -- b is the first upvalue
|
|
v1 = nil -- v1 is the second upvalue
|
|
|
|
-- prevent inlining
|
|
for i = 1, 100 do print(`{b} is {b}`) end
|
|
end
|
|
|
|
closure()
|
|
end
|
|
|
|
valueTrackingIssue1()
|
|
|
|
local function vec3compsum(a: vector)
|
|
return a.X + a.Y + a.Z
|
|
end
|
|
|
|
assert(vec3compsum(vector(1, 2, 4)) == 7.0)
|
|
|
|
local function vec3add(a: vector, b: vector) return a + b end
|
|
local function vec3sub(a: vector, b: vector) return a - b end
|
|
local function vec3mul(a: vector, b: vector) return a * b end
|
|
local function vec3div(a: vector, b: vector) return a / b end
|
|
local function vec3neg(a: vector) return -a end
|
|
|
|
assert(vec3add(vector(10, 20, 40), vector(1, 0, 2)) == vector(11, 20, 42))
|
|
assert(vec3sub(vector(10, 20, 40), vector(1, 0, 2)) == vector(9, 20, 38))
|
|
assert(vec3mul(vector(10, 20, 40), vector(1, 0, 2)) == vector(10, 0, 80))
|
|
assert(vec3div(vector(10, 20, 40), vector(1, 0, 2)) == vector(10, math.huge, 20))
|
|
assert(vec3neg(vector(10, 20, 40)) == vector(-10, -20, -40))
|
|
|
|
local function vec3mulnum(a: vector, b: number) return a * b end
|
|
local function vec3mulconst(a: vector) return a * 4 end
|
|
|
|
assert(vec3mulnum(vector(10, 20, 40), 4) == vector(40, 80, 160))
|
|
assert(vec3mulconst(vector(10, 20, 40), 4) == vector(40, 80, 160))
|
|
|
|
local function bufferbounds(zero)
|
|
local b1 = buffer.create(1)
|
|
local b2 = buffer.create(2)
|
|
local b4 = buffer.create(4)
|
|
local b8 = buffer.create(8)
|
|
local b10 = buffer.create(10)
|
|
|
|
-- only one valid position and size for a 1 byte buffer
|
|
buffer.writei8(b1, zero + 0, buffer.readi8(b1, zero + 0))
|
|
buffer.writeu8(b1, zero + 0, buffer.readu8(b1, zero + 0))
|
|
|
|
-- 2 byte buffer
|
|
buffer.writei8(b2, zero + 0, buffer.readi8(b2, zero + 0))
|
|
buffer.writeu8(b2, zero + 0, buffer.readu8(b2, zero + 0))
|
|
buffer.writei8(b2, zero + 1, buffer.readi8(b2, zero + 1))
|
|
buffer.writeu8(b2, zero + 1, buffer.readu8(b2, zero + 1))
|
|
buffer.writei16(b2, zero + 0, buffer.readi16(b2, zero + 0))
|
|
buffer.writeu16(b2, zero + 0, buffer.readu16(b2, zero + 0))
|
|
|
|
-- 4 byte buffer
|
|
buffer.writei8(b4, zero + 0, buffer.readi8(b4, zero + 0))
|
|
buffer.writeu8(b4, zero + 0, buffer.readu8(b4, zero + 0))
|
|
buffer.writei8(b4, zero + 3, buffer.readi8(b4, zero + 3))
|
|
buffer.writeu8(b4, zero + 3, buffer.readu8(b4, zero + 3))
|
|
buffer.writei16(b4, zero + 0, buffer.readi16(b4, zero + 0))
|
|
buffer.writeu16(b4, zero + 0, buffer.readu16(b4, zero + 0))
|
|
buffer.writei16(b4, zero + 2, buffer.readi16(b4, zero + 2))
|
|
buffer.writeu16(b4, zero + 2, buffer.readu16(b4, zero + 2))
|
|
buffer.writei32(b4, zero + 0, buffer.readi32(b4, zero + 0))
|
|
buffer.writeu32(b4, zero + 0, buffer.readu32(b4, zero + 0))
|
|
buffer.writef32(b4, zero + 0, buffer.readf32(b4, zero + 0))
|
|
|
|
-- 8 byte buffer
|
|
buffer.writei8(b8, zero + 0, buffer.readi8(b8, zero + 0))
|
|
buffer.writeu8(b8, zero + 0, buffer.readu8(b8, zero + 0))
|
|
buffer.writei8(b8, zero + 7, buffer.readi8(b8, zero + 7))
|
|
buffer.writeu8(b8, zero + 7, buffer.readu8(b8, zero + 7))
|
|
buffer.writei16(b8, zero + 0, buffer.readi16(b8, zero + 0))
|
|
buffer.writeu16(b8, zero + 0, buffer.readu16(b8, zero + 0))
|
|
buffer.writei16(b8, zero + 6, buffer.readi16(b8, zero + 6))
|
|
buffer.writeu16(b8, zero + 6, buffer.readu16(b8, zero + 6))
|
|
buffer.writei32(b8, zero + 0, buffer.readi32(b8, zero + 0))
|
|
buffer.writeu32(b8, zero + 0, buffer.readu32(b8, zero + 0))
|
|
buffer.writef32(b8, zero + 0, buffer.readf32(b8, zero + 0))
|
|
buffer.writei32(b8, zero + 4, buffer.readi32(b8, zero + 4))
|
|
buffer.writeu32(b8, zero + 4, buffer.readu32(b8, zero + 4))
|
|
buffer.writef32(b8, zero + 4, buffer.readf32(b8, zero + 4))
|
|
buffer.writef64(b8, zero + 0, buffer.readf64(b8, zero + 0))
|
|
|
|
-- 'any' size buffer
|
|
buffer.writei8(b10, zero + 0, buffer.readi8(b10, zero + 0))
|
|
buffer.writeu8(b10, zero + 0, buffer.readu8(b10, zero + 0))
|
|
buffer.writei8(b10, zero + 9, buffer.readi8(b10, zero + 9))
|
|
buffer.writeu8(b10, zero + 9, buffer.readu8(b10, zero + 9))
|
|
buffer.writei16(b10, zero + 0, buffer.readi16(b10, zero + 0))
|
|
buffer.writeu16(b10, zero + 0, buffer.readu16(b10, zero + 0))
|
|
buffer.writei16(b10, zero + 8, buffer.readi16(b10, zero + 8))
|
|
buffer.writeu16(b10, zero + 8, buffer.readu16(b10, zero + 8))
|
|
buffer.writei32(b10, zero + 0, buffer.readi32(b10, zero + 0))
|
|
buffer.writeu32(b10, zero + 0, buffer.readu32(b10, zero + 0))
|
|
buffer.writef32(b10, zero + 0, buffer.readf32(b10, zero + 0))
|
|
buffer.writei32(b10, zero + 6, buffer.readi32(b10, zero + 6))
|
|
buffer.writeu32(b10, zero + 6, buffer.readu32(b10, zero + 6))
|
|
buffer.writef32(b10, zero + 6, buffer.readf32(b10, zero + 6))
|
|
buffer.writef64(b10, zero + 0, buffer.readf64(b10, zero + 0))
|
|
buffer.writef64(b10, zero + 2, buffer.readf64(b10, zero + 2))
|
|
|
|
assert(is_native())
|
|
end
|
|
|
|
bufferbounds(0)
|
|
|
|
return('OK')
|