lua-users home
lua-l archive

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

Mark Hamburg wrote:
> ... [runtime type assertions] ...
> Maybe a tracing JIT makes their cost drop low enough in practice so as not
> to matter.

Yes, they are usually subsumed or eliminated.

Here's how this works with LuaJIT 2.x for a simple example:

  assert(type(x) == "number")
  y = x + 1.5

The first line is transformed into a single ISNUM ucode. This is
a type-checking guard which exits the trace if its precondition
later turns out not to be true.

The bytecode generated for the first line is not trivial (it has
two calls and a conditional expression). The transformation to a
single ucode is quite involved, but most of it can be done while

- type() specializes on its argument type by emitting an ISNUM
  (because this is the runtime type of x, NOT because its result
  is later compared to a string). This allows it to return a
  constant value ("number" in this case).

- The comparison "number" == "number" can be constant folded to a
  true value. No code is generated.

  [Some details left out: the bytecode only has branching
  conditionals. Conditional expressions used in a non-conditional
  context need to be transformed to a two-way branch by the
  parser. The two arms hold a true and a false assignment. The
  interpreter already evaluates this, so the recorder can just
  collect the "right" constant assignment.]

- The assert() is passed the constant true value. It's eliminated
  with constant folding, too. The return value (true) is unused
  by the function call statement.

This leaves us with a single ISNUM guard for x. The FP addition
causes the same guard and a FADD. The 2nd instance of the guard
is subsumed with CSE (yes, this works on guards, too). The
remaining unused constants are ignored during code generation
which employs on-the-fly dead-code elimination.

End result: a single type check and the FP addition (two machine
code instructions). The complex type assertion has been
completely optimized away.

The remaining type check can most probably be eliminated, too.
But this depends on the surrounding context: e.g. with x = a*3.5
the multiplication result already has a constant type, or with
x = 1.5 constant folding happens again.

If you call the above code snippet in a loop, the type check can
usually be hoisted out of the loop. It doesn't matter whether
parts of the code are inside of yet another function or not. All
runtime control-flow is inlined, function boundaries are ignored
(so go ahead and define checknum(x)).

Summary: you can leave those assert()'s in with LuaJIT 2.x. But
write them in plain Lua and not in specialized C functions. These
are black boxes for the compiler and cannot be optimized away.