lua-users home
lua-l archive

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

Roberto Ierusalimschy wrote:
> Unfortunately, with a one-pass compiler, by the time a return is compiled
> we cannot be sure that there won't be a sub-proto later. For instance,
> consider a code like this:
> function foo (x)
>   while true do
>     if something then return end   --<<< 1
>     b = function () return x end
>   end
> end
> When the return at (1) is compiled, Lua still thinks that the function
> does not have a sub-proto. But that return may need to close the upvalues
> used by 'b' (if 'something' is true only after some iterations).

I've done the following for the LJ2 bytecode:
- The RET* and CALLT* opcodes do not close upvalues.
- The UCLO opcode has a base register plus a jump target. It
  closes all upvalues higher or equal than the base register and
  then jumps to the target. By default this is the next opcode.
- UCLO are often followed by jumps, so these are merged in.
- When a return or tail call is emitted, the HAS_RETURN flag is set.
- When a sub-function is emitted, the HAS_CLOSURE flag is set. If
  the HAS_RETURN flag was set before, the FIXUP_RETURN flag is set.
- Returns and tail calls get an additional UCLO if the HAS_CLOSURE
  flag is set.
- In the rare case that the FIXUP_RETURN flag is set at the end of
  a function definition, the bytecode is scanned for all RET* and
  CALLT*. They are then moved to the end of the bytecode and get
  replaced by an UCLO which jumps to the end. The first UCLO found
  means the scan can be terminated early.

This way one gets all the benefits and only pays the cost for
fixing up the bytecode for the pathological cases.