3 ms·
Just tangentially: I have high hopes for an experiment with a 96-bit word size in RaptorJIT (a fork of LuaJIT, a compiler for a Lisp-like language.) That's a 32
by lukego 9y ago
Just tangentially: I have high hopes for an experiment with a 96-bit word size in RaptorJIT (a fork of LuaJIT, a compiler for a Lisp-like language.) That's a 32-bit tag and a 64-bit value.
This seems practical for a few reasons:
Full 64-bit pointers and numbers can fit into tagged words. (No boxing and so no heap allocation for u64 and pointer arithmetic.)
Naked 64-bit values can still be stored in registers and used directly. The JIT knows the type of each register and does not need to load the tag bits (they are checked on load and added on store.)
The x86-64 target CPU is efficient for unaligned access.
We will see how this works out in practice. Link: https://github.com/raptorjit/raptorjit/pull/93 https://github.com/raptorjit/raptorjit/pull/93
- gsg 9y agoHow do you pass arguments? In two registers? On the stack?
- lukego 9y agoThis turns out to be much simpler than expected, on paper at least. First, this is a tracing JIT and so it basically inlines everything. There is no argument passing convention at all within a block of JIT code because subroutines bodies are completely inline. So - passing tagged values as arguments is actually a fairly infrequent operation. Then in cases where tagged values really do need to be passed around they will always be passed by pointer reference. The JIT will write them to memory - the Lua stack or the heap - and pass a pointer reference in a register. The really happy circumstance is that the whole C runtime system is already written to reference tagged values using pointers, and there is already a port that supports 64-bit Lua values using 32-bit machine words.
- gsg 9y agoI see. It seems like that would still make calling supporting routines annoyingly expensive, but perhaps that (and the extra tag memory) doesn't matter as much as I think it does. Good luck with the project.
- lukego 9y agoThanks! There are really two kinds of supporting routines here. Runtime system routines operate on tagged values and do have to swallow the stack convention. Thankfully the code is already written in this style and the JIT seldom invokes any of these routines in optimized programs (it generates inline versions.) Second is C routines called via FFI. These use native C data types and the standard platform calling convention. They never see the tagged values (get an untagged uint64_t in a register instead of a TValue struct.) These are easy, cheap, and frequently used.