lua-users home
lua-l archive

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


Robert G. Jakabosky wrote:
> > Recompiling everyhing for every taken exit is too expensive. But
> > there's a gradual approach:
> > [...]
> 
> Does LJ1 do this, or is this new in LJ2?  I have seen the de-optimize exits 
> that are generated in LJ1 and though that they just ran a re-compile of the 
> full function with some optimizations turned off.

Upon de-optimization, LJ1 compiles a generic variant. But only for
the failing bytecode (i.e. just a couple of bytes), not for the
whole function. It merges this code snippet by patching a jump into
the original code. LJ2 uses the above mentioned gradual approach.

You can see this in action with LJ1: modify your example to x=x*2,
so it predicts a number type for x and omits the generic part. Then
run 'luajit -O -jdump test.lua' and you'll see it generates a tiny
DEOPT block with both the fast and the generic part which jumps back
to the original code.

> [...] The slow path function would need to 
> be inlined at the end of the fast path function.

Why not save all modified state to the Lua stack and refer the hard
part to the interpreter? Unlike a JIT compiler, if the static type
prediction fails, you're pretty much hosed, anyway. You'll not be
able to considerably improve performance by emitting calls to
generic operators.

> > A static compiler probably needs to match the builtins by name.
> > OTOH in LJ2 all builtin functions are already marked with an
> > internal ID. Doing the lookup at runtime during parsing is trivial.
> > This would also allow a limited form of monkey patching -- if you
> > do it *before* parsing/loading the main part of your Lua code.
> 
> No, the static compiler could use compile-time IDs for all the standard 
> library functions.  Those IDs would need to be added to the library 
> registration process.
> 
> Does LJ2 only use the internal IDs to test if the inlined function was 
> changed?

The internal ID is used by the recorder to know which function to
record. Since the builtin functions are written in assembler and/or
C, they can't be traced (easily). The recorder has to emulate their
internal decision steps (type checks, coercions et al) and translate
this into IR instructions. This is easy for most of the library
functions (like math.*) and hard only for a couple of them (like
string.sub *sigh*).

The runtime dispatch check compares the closure identities (i.e.
their address), because this is cheaper than looking up the ID in
the closure object.

> > [This idea would pay off even for the plain Lua interpreter. Simply
> > set a flag in luaL_openlibs before registering the standard library
> > functions and turn it off at the end. Copy this flag into every
> > newly created CClosure in lua_pushcclosure(). Check this flag while
> > parsing and do the above transformation. That's all.]
> 
> If the Lua core reserved a range of IDs for standard functions it could use 
> those, in a new bytecode file format, as constant function references.

Since dumping bytecode doesn't provide much compression, I'd just
disable bytecode saving if the feature is turned on.

> One way to still allow some monkey patching would be to put a proxy-object 
> into the global tables for each of the functions. [...]

I don't think you need to go to these lengths. A dynamic lookup
during parsing covers 99% of all monkey patching cases (like Mark
Hamburg's example). A static compiler could just take a list of
exempt library functions.

--Mike