The intermediate code is similar to the article, a mix of macros and basic C expressions as high level assembler.
I cannot remember all the details, the last time I used the book was around 1996.
You can still get the source code, http://www.holub.com/software/compiler.design.in.c.html
touch *.c *.hWhen compiling dynamic languages of course, that often doesn't work as the optimiser doesn't have anything to go on if you do everything through void* pointers, or a similar dynamic construct.
You might find some anti-C bias from my post history, but I am also very pragmatic and would certainly use C if required to do so.
I used the compilation to bytecode disguised as Assembler macros as part of a few toy compilers back in late 90's.
If it was today I would probably use LLVM or C(++) as being discussed in the article.
mov reg1, reg2 ==> reg1 = reg2
mov reg1, [reg2] ==> reg1 = *reg2
add reg1, reg2 ==> reg1 += reg2
And so on.
Compiler Design in C (1990)
http://www.amazon.com/Compiler-Design-C-Prentice-Hall-softwa...
EDIT: Adding some extra remarks I think might also be interesting to share.
Another approach, that I really like, is to output bytecodes that are mapped directly to macros in typical macro assemblers like NASM/MASM/TASM. Those macro assemblers provide very powerful macro systems.
Then map those macros to the corresponding assembly code.
Sure it gives a bit more work, but I find it more fun.