6/24/2026 at 2:23:46 PM
Interesting, it's similar to what I've done recently in Koffi 3, which is an FFI package for Node.js. I made my own C FFI layer, I did not use libffi. This works great, and got me close to statically-implemented NAPI modules.Benchmarks are here: https://koffi.dev/benchmarks
It's still a little different because in my case, the instructions tend to do two things: decode JS value and prepare register/stack. For typical functions, only a few instructions have to run, with minimal overhead. So, for example, I have a PushBool instruction which calls napi_get_value_bool() and then puts the bool at the correct offset (pre-computed) so that it ends up in a register or on the stack.
A function like int atoi(const char *) ends up with only two bytecode instructions:
- PushString
- RunInt32 (combined macro-operation that defers to assembly to set up registers, call the function, and then directly decodes the value)
Or another exemple, void *memset(void *ptr, int value, size_t size) only needs four instructions: - PushPointer
- PushInt32
- PushUInt64
- RunPointer
I've coupled that with a tail-call direct-threaded interpreter, with Clang's __attribute__((preserve_none)) ABI, just like Python did recently: https://github.com/python/cpython/issues/128563
by Koromix