LuaJIT tries to keep the spirit of Lua — it's light-weight, efficient and extensible.
Features
All functions are by default compiled Just-In-Time (JIT) to machine code:
- Functions that are unused are not compiled at all.
- Compilation can be enabled/disabled selectively for individual functions and subfunctions or even whole modules.
- Interpreted and compiled functions can be freely mixed.
Ahead-Of-Time (AOT) compilation (at runtime) is supported, too:
- A number of API functions and command line options allows full user control over the compilation process.
The JIT compiler is extensible:
- The optimizer is an extra module that attaches to the compiler pipeline.
- Various modules provide trace and debug information about the compilation process.
- All of these features can be activated with command line options.
Performance
The compiled machine code is very efficient:
- Have a look at some Performance Measurements.
- Aggressive optimizations (specialization, inlining) are enabled wherever possible. Inlined contracts catch wrong optimizer predictions at runtime (undetected polymorphism).
- Adaptive deoptimization is used to recompile individual bytecode instructions with broken contracts. This avoids generating code for the generic fallback cases most of the time (faster compilation, reduced I-cache contention).
- Special CPU features (such as conditional moves or SSE2) are automatically used when detected.
The JIT compiler is very fast:
- Compilation times vary a great deal (depending on the nature of the function to be compiled) but are generally in the microsecond range.
- Even compiling large functions (hundreds of lines) with the maximum optimization level takes only a few milliseconds in the worst case.
LuaJIT is very small:
- The whole JIT compiler engine adds only around 32K of code to the Lua core (if compiled with -Os).
- The optimizer is split into several optional modules that can be loaded at runtime if requested.
- LuaJIT adds around 6.000 lines of C and assembler code and 2.000 lines of Lua code to the Lua 5.1 core (17.000 lines of C).
- Required build tools (DynASM) take another 2.500 lines of Lua code.
Compatibility
LuaJIT is designed to be fully compatible with Lua 5.1. It accepts the same source code and/or precompiled bytecode. It supports all standard language semantics. In particular:
- All standard types, operators and metamethods are supported.
- Implicit type coercions (number/string) work as expected.
- Full IEEE-754 semantics for floating point arithmetics (NaN, +-Inf, +-0, ...).
- Full support for lexical closures. Proper tail calls do not consume a call frame.
- Exceptions are precise. Backtraces work fine.
- Coroutines are supported with the help of Coco.
- No changes to the Lua 5.1 incremental garbage collector.
- No changes to the standard Lua/C API.
- Dynamically loaded C modules are link compatible with Lua 5.1 (same ABI).
- LuaJIT can be embedded into an application just like Lua.
Some minor differences are related to debugging:
- Debug hooks are only called if debug code generation is enabled.
- There is no support for tailcall counting in JIT compiled code. HOOKTAILRET is not called, too. Note: this won't affect you unless you are writing a Lua debugger. *
* There is not much I can do to improve this situation without undue complications. A suggestion to modify the behaviour of standard Lua has been made on the mailing list (it would be beneficial there, too).
Restrictions
- Only x86 (i386+) CPUs are supported right now (but see below).
- Only the default type for lua_Number is supported (double).
- The interrupt signal (Ctrl-C) is ignored unless you enable debug hooks (with -j debug). But this will seriously slow down your application. I'm looking for better ways to handle this. In the meantime you have to press Ctrl-C twice to interrupt a currently running JIT compiled function (just like C functions).
- GDB, Valgrind and other debugging tools can't report symbols or stack frames for JIT compiled code. This is rather difficult to solve. Have a look at Debugging LuaJIT, too.
Caveats
- LuaJIT allocates executable memory for the generated machine code
if your OS has support for it: either HeapCreate() for Windows or
mmap() on POSIX systems.
The fallback is the standard Lua allocator (i.e. malloc()). But this usually means the allocated memory is not marked executable. Running compiled code will trap on CPUs/OS with the NX (No eXecute) extension if you can only use the fallback. - DynASM is needed to regenerate the
ljit_x86.h file. But only in case you want to modify
the *.dasc/*.dash files. A pre-processed *.h
file is supplied with LuaJIT.
DynASM is written in Lua and needs a plain copy of Lua 5.1 (installed as lua). Or you can run it with LuaJIT built from the *.h file supplied with the distribution (modify DASM= in src/Makefile). It's a good idea to install a known good copy of LuaJIT under a different name for this. - LuaJIT ships with LUA_COMPAT_VARARG turned off. I.e. the implicit arg parameter is not created anymore. Please have a look at the comments in luaconf.h for this configuration option. You can turn it on, if you really need it. Or better yet, convert your code to the new Lua 5.1 vararg syntax.