I'm not clear on what you are proposing. Are you saying that the IR will contain X86 instructions and "normal" IR instructions at the same time?
No. The point would be to translate the IR into these instructions by replacing the IR instructions in the CFG by x86 instructions, with the benefit of reusing facilities of the CFG/IR framework in this process. This will make it easier to work on the generated x86 code once it's been allocated... We want to perform instruction selection, peephole optimizations and register allocation *before* passing the x86 instructions to the assembler. The CFG/IR framework seems like it could support these things.
Mixing these levels is not "clean". It mixes two semantic levels where concepts from one level don't exist or are awkward at the other level. I fear the IR code will become tainted with X86-specific concerns, and that it will be extremely tedious to port the system to a different processor.
There will be no mixing. Just an HIR -> x86 translation for now, and eventually, hopefully, HIR -> MIR/LIR -> x86 for further added portability. The CFG containing x86 instructions will also be a separate entity from the original CFG.
For example, consider the 2 IR instructions
(1) t1 := x + y (2) t2 := t1 + 1
The best x86 code to generate for the second IR instruction will depend on which CPU resources t1 and t2 map to. If t1 and t2 are mapped to the same register (because t1 becomes dead after IR instruction 2), say %eax, then the best x86 code will be
inc %eax
If on the other hand t1 and t2 map to 2 different registers, say %eax and %ebx, then the best x86 code will be
lea 1(%ebx),%eax
If on the other hand t1 is in memory, say stack slot 8(%ebp), and t2 is in a registers, say %eax, then the best x86 code will be
mov 8(%ebp),%eax inc %eax
Erick and I discussed implementing a naive HIR -> x86 translator, and then having a linear scan register allocator and a peephole optimization pass to try and optimize the instruction choice through pattern matching and replacement. If the second approach works as well or better than the first, it may be advantageous, as it separates instruction selection from register allocation a little more.
I think it is better to maintain a high-level of abstraction for the IR instructions, and to have an independent pass which translates the IR instructions to x86 code. The x86 specific knowledge can be in this "x86 back-end". As we discussed in the last meeting, to keep things simple, we should use an on-the-fly register allocator to attribute registers/memory locations to the IR temporaries. On-the-fly register allocation requires that the IR instructions are translated to x86 in sequence.
That's pretty much exactly what Erick and I have in mind, although we aren't clear about whether it's best to translate to x86 while doing register allocation or translating first, doing register allocation (and inserting load/stores for spills), and then doing peephole optimizations.
The idea so far is:
HIR -> pseudo x86 (SSA/CFG) -> register allocation -> "true" x86 (x86 in CFG annotated with register/mem uses) -> peephole optimizer (working on CFG) -> assembler -> machine code bytes
You didn't give me feedback on whether you think it's a good idea to refactor the instructions to have branch and side effect flags, what do you think about that?
- Maxime