The author is lucky brainfuck is so simple, no need for the hairy generic MOD/RM [0] addressing mode encoding.
Of course instruction encoding is far from the hairiest part of the process of JITting. Data flow simplification (= don't generate unnecessary intermediates, like target architecture flag register bits that never affect execution), register allocation and instruction selection are pretty complicated optimization problems.
Oh, and indirect calls and jumps. How you'll hate those when writing a CPU JIT... Especially those that very inconveniently jump in the middle of a previously generated JIT trace.
[0]: MOD/RM is has four modes (MOD).
One for simply accessing a register. Three for addressing memory through pointers in various ways. The most complicated case is SIB (scale + index + base) addressing mode. In C, it'd be something like:
addr = (reg1<<scale) + reg2 + <immediate>;
Where scale is 1, 2, 4 or 8, <immediate> value is a signed 8 or 32-bit value. Reg1 can't be SP/ESP/RSP due to encoding limitations.Some x86(-64) register combinations can't be encoded directly, so the encoder will need to play around and possibly "upgrade" the addressing mode to a more complicated SIB version.
For more curious, this page was very informative for writing my addressing mode encoder:
https://wiki.osdev.org/X86-64_Instruction_Encoding#ModR.2FM_...