This happens because hash part of the res is no longer empty.
Which means luajit trace recorder can't guard on trace __newindex
invocation with a simple res.hmask == 0 comparison.
Instead it guards it with a real load checking that res[k] == nil:
0039 > tab TDUP {0x00050ed8}
0040 p32 FLOAD 0039 tab.array
0041 p32 AREF 0040 +2
0042 num CONV 0036 num.int
0043 num ASTORE 0041 0042
0044 p32 HREF 0007 0039 ;; this is where it escapes!
0045 > nil HLOAD 0044
You can move table_index to mt to workaround this.
In reality load forwarding could just eliminate HLOAD because it's
easy to see that there's no corresponding aliasing store.
However as far as I can see fwd_ahload doesn't take into account
aliasing between keys so it thinks there might be some aliasing.
// Vyacheslav Egorov
On Sat, Aug 1, 2015 at 9:25 AM, Denis Golovan <denis.golovan@xxxxxxxxx> wrote:
Hi all
I'd like to know if the behaviour I found is a bug.
If it is, consider the mail as bugreport :)
I've managed to create a small example, but I'd like to show several
iterations.
Case 1 works perfectly. I see {sink} tag for temporary table {1, i}.
--case 1
function table_index(t, ix)
return t[ix]
end
function tt()
local res = {}
for i=1,10000 do
res[ table_index({1, i}, 2) ] = 1
end
return res
end
jit.flush()
aa=tt()
--=================================================
Case 2 works fine again. The same idea, but via metatable.
I see {sink} tag for temporary table {1, ix}.
--case 2
function table_index(t, ix)
return t[ix]
end
mt = {
__newindex = function (a, ix, v)
rawset(a, table_index({1, ix}, 2), v)
end
}
function tt()
local res = {}
setmetatable(res, mt)
for i=1,10000 do
res[ i ] = 1
end
return res
end
jit.flush()
aa=tt()
--=================================================
Case 3 is almost production example and works fine again. The same
idea, but via metatable + indexing via table.
I see {sink} tag for temporary table {1, i}.
--case 3
function table_index(t, ix)
return t[ix]
end
mt = {
__newindex = function (a, ix, v)
rawset(a, table_index(ix, 2), v)
end
}
function tt()
local res = {}
setmetatable(res, mt)
for i=1,10000 do
res[ {1, i} ] = 1
end
return res
end
jit.flush()
aa=tt()
--=================================================
Case 4 is actually a production example. The same idea, but via
metatable + indexing via table + extra indirection.
Alas, allocation sinking stops working and each iteration TDUPs small
temporary table {1, i}.
--case 4
function table_index(t, ix)
return t[ix]
end
mt = {
__newindex = function (a, ix, v)
rawset(a, a.table_index(ix, 2), v) -- just additional indirection added
end
}
function tt()
local res = {}
res.table_index = table_index
setmetatable(res, mt)
for i=1,10000 do
res[ {1, i} ] = 1
end
return res
end
jit.flush()
aa=tt()
As you probably guessed, but I'm trying to emulate multidimensional
views for arrays on top of FFI 1-dimensional arrays, but allocation
sinking is crucial for decent speed here.
BR,
Denis