lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Tue, Jul 6, 2021, 9:03 AM Roberto Ierusalimschy <roberto@inf.puc-rio.br> wrote:
> I have been tracing through the Lua VM source code and see how it tracks
> the top. I see a number of places where L->top is set and adjusted, and it
> looks like lgc.c uses that same value. However, it seems that L->top is
> invalidated at the top of the VM switch when assertions are turned on, so
> I'm not entirely sure that is the correct thing to follow.

Currently, every opcode where L->top (top for short) may be needed
sets it. The assertion invalidates the top exactly to check that this
set is happening as it should be.

Most of this sets happen through macros Protect and halfProtect.
However, these macros restore the top to the end of the call frame
(ci->top). If a GC happens during these opcodes, Lua will have the
problem you described of being too convervative and keeping alive
too many registers. As an example, see the code below:

--------------------------------------------------------
collectgarbage("stop")

local x = setmetatable({}, {__add = function () collectgarbage() end})
do
  local b,c,d,e,f
  local a = {}
  for i = 1, 1e5 do a[i] = {} end
  print(collectgarbage("count") * 1024)
end

-- getmetatable(x).__add()
x = x + 10
print(collectgarbage("count") * 1024)
--------------------------------------------------------
Although 'a' is out of scope when the __add metamethod is called,
its value is not collected.

However, in most common cases the VM is able to set a more precise top.
This happens, for instance, if you remove the comment on the example
and calls the __add metamethod directly.

The Lua VM assumes a specific algorithm for "register allocation"
(which is not very smart), so by the registers being used in some
opcodes it knows the limit of the live registers.

When a function calls another one, the new call frame is built on the
limit of the live registers of the calling function, so all call
frames not on the top naturally have only live registers. (This is
what happens in the second case with our example. This is not what
happens when a metamethod is called, because arithmetic opcodes do
not know the correct register limit.)

At the opcodes when the GC can be called directly, top is set
according to the specific opcode being executed.  Look for the macro
'checkGC'. Everty time that macro is called, it sets the top to an
appropriate limit.

-- Roberto

Excellent, thank you very much. Next time I sit down to work on this project, I will start tracing through the code again with this info in mind. 

I had noticed Protect(), etc. setting L->top to ci->top and wasn't sure what that was about, but forgot to mention it. Interesting to know that Lua also has the problem of keeping too many registers alive at once.