
Hello JIT World: The Joy of Simple JITs (2012) - jeffreyrogers
http://blog.reverberate.org/2012/12/hello-jit-world-joy-of-simple-jits.html
======
haberman
Author here -- I'm happy to see the continued interest in this article. I had
a lot of fun writing it. DynASM is some great work and doesn't have a lot of
documentation, so I was hoping to bridge this gap with an introductory
tutoral. And Brainfuck provided just the right problem for writing a short and
sweet program that does something interesting.

------
Scaevolus
I made a very fast BF JIT based on this post:
[https://github.com/rmmh/beefit](https://github.com/rmmh/beefit)

I implemented a fair number of optimizations, but there aren't many large
benchmarks to really crunch down on. An experimental register allocation pass
was awkward (DynASM doesn't really support run-time register selection) and
didn't improve performance much.

------
Udo
Thanks for linking to this!

I've been building a toy language by mutating the excellent Lua 5.2 runtime,
it's one of my silly side projects aimed at taking me out of my coding comfort
zone.

Looking into the LuaJIT code to figure out how they did it has been on my list
for a few days now, and this introduction to DynASM provides an nice starting
point (LuaJIT itself is based on Lua 5.1 and unlikely to be updated).

------
rurban
I would have appreciated if you would also had described the downsides of
using DynASM compared to other jitters.

E.g. In lightning you don't care too much about the target architecture, as
you jit to an abstract RISC machine. So the jit library is portable (not in
DynASM), does handle the calling conventions for you (not in DynASM), and does
register optimizations automatically (max 6 with lightning), again not in
DynASM).

The real trick you seem to like is the syntax abstraction, using | as prefix.
But normal C can do the same with __asm(string) and it's easier to use C
macros for portable ASM, and LISP jit libraries have long done the same, with
the advantage in LISP that the JIT macros in lisp are part of the normal
language. No need to deal with restricted C macros.

~~~
haberman
> I would have appreciated if you would also had described the downsides of
> using DynASM compared to other jitters.

The article wasn't meant to be a comparative evaluation of DynASM vs other
dynamic code generation engines. If I was comparing it against engines like
LLVM and Lightning that have their own IR and optimizers I would have also
mentioned DynASM's advantages compared to them, such as:

\- DynASM is _much_ smaller: DynASM's runtime is something like 2k of object
code. Lightning is more like 150k and LLVM is many times bigger than that.

\- DynASM is faster: I haven't benchmarked against lightning but LLVM is known
not to be very fast at JITting, and just the fact that Lightning is performing
its own optimizations means that DynASM is probably faster (note that I'm
talking specifically about the speed of doing the code generation itself).

\- DynASM gives greater control: you have full control over what instructions
are emitted. If you want to play a little fast and loose with calling
conventions for speed, you can (I do this in my JIT). If you want to use
SSE/AVX/etc. or other very specialized instructions like x86's parsing
instructions, you can.

Basically, if you already have your own IR and optimizer and you just want a
code generator, and want control over what specific processor instructions you
are emitting, then DynASM is a great choice. If you don't have your own
optimized IR, or you don't want to write a separate code generator per
platform, and you don't mind the size/speed overhead of other engines, then
other projects might be a better choice.

> The real trick you seem to like is the syntax abstraction, using | as
> prefix. But normal C can do the same with __asm(string)

If you think __asm(string) is the same as DynASM, then you missed the point of
the article.

__asm(string) is great if you just want to write some static ASM that is fully
known at compile time. But how are you going to use __asm(string) to do this?

    
    
        void emit_mov(int immediate) {
          // This won't work, you can't substitute a variable
          // into an __asm() expression.
          __asm("mov eax, immediate");
        }
    

Sure you could pass the immediate to the ASM via a register, but that's not
what we're trying to do, we're trying to build code at runtime. __asm() won't
help us with that.

> and it's easier to use C macros for portable ASM

Not sure what you mean by this. If you're talking about a BPF-like approach
like I discussed in my article, that's not portable and not very readable IMO.

~~~
Scaevolus
DynASM isn't quite a _full_ code generator, since you can't easily select
registers dynamically.

~~~
haberman
x86 supports this, though none of the other architectures do:
[http://www.freelists.org/post/luajit/runtime-variable-
regist...](http://www.freelists.org/post/luajit/runtime-variable-register-in-
dynasm,2)

------
jedismith
Very well-written bite-sized intro. I've always wondered how well JITs would
do if they had access to a scratchpad RAM (kind of like many GPUs). It's a
very different problem than with a register allocator but I feel it'd be more
useful for a JIT since it probably knows more about the program structure than
whatever LRU variant the cache algorithm is using.

------
thedigitalengel
Related: [https://github.com/sanjoy/bfjit](https://github.com/sanjoy/bfjit) (a
brainfuck interpreter with a tracing JIT for hot loops).

