lua-users home
lua-l archive

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

Alex Davies wrote:
> Impressive that jit 2.x performs loop hoisting, but how is it that neither 
> assert nor type need to be checked? The only thing I can think of is that 
> it is assumed that they are both stored in upvalues, and that jit 2.x 
> checks that nothing modifies those upvalues. If so, wouldn't that cause 
> problems with debug.setupvalue =/? Intrigued. Sounds good though.

I've left out some details, but you're right -- this can get
tricky. Function identity checks can be hoisted out just like
type checks (conceptually this is the same).

I use optimistic hoisting for type checks (with a very high
success rate). If this leads to a PHI conflict at the end of the
loop (type has changed), the slots are blacklisted and the
recorder retries automatically.

Hoisting is quite easy for locals and upvalues. With one caveat:
open upvalues can alias locals in an outer frame -- which could
be part of the same trace.

Right now I don't care about interaction of the debug.* functions
with JIT-compiled code. They work fine with the interpreter and
you can just selectively disable compilation if necessary. But I
could use a trick similar to what I'm doing in lua_sethook(): if
you call it, it just aborts trace recording and drops back to the
interpreter. :-)

Hoisting of checks for functions stored in tables is a lot more
involved. This requires alias analysis, which is still on my TODO

Lua's semantics permit use of t[k]-disambiguation (no pointers!).
And it doesn't have type aliasing problems. This is a lot better
than the low-level memory disambiguation one needs to do for
C/C++. And since the trace compiler has knowledge about runtime
values, it can insert appropriate runtime disambiguation checks
if static disambiguation fails.

Some examples for hoisting of the check for math.sqrt (these are
all inside a 'for i=1,n do' loop):

  -- No ambiguity: load with string key vs. load/store with numeric key.
  b[i] = math.sqrt(a[i])

  -- No ambiguity: "foo" ~= "math" and "foo" ~= "sqrt" = math.sqrt([i])

  -- Needs a (hoisted) runtime check for t ~= math
  t.sqrt = math.sqrt(t.sqrt+a[i])

CSE for loads needs alias analysis, too:

  -- either a ~= b, then the 2nd is a CS for
  -- or     a == b, then the 2nd is a CS for = =

As you can see this can get arbitrarily complex. But I realize
I'll have to add this optimization sooner or later. It's the only
way to get good performance for methods written in OO-style.