1

I've made a virtual processor with a simple instruction set and memory model as a learning exercise (and mostly just for fun). I can write programs in its assembly language, assemble them with my assembler, and successfully run them on a virtual machine.

I've been wanting to write a compiler for a higher-level language that targets this machine so I don't have to write everything in assembly. I've heard of LLVM, a compiler project that supports multiple front-ends for different languages and outputs IR that is somewhat assembly-like. This IR is given to a platform-specific backend that turns it into assembly/machine code for a platform.

I've looked into writing my own backend; I've seen LLVM's backend tutorial and the backend for Cpu0 tutorial, and, frankly, it looks a bit complicated.

Is it feasible to write a compiler, external to LLVM, that reads LLVM's *.ll files and outputs assembler for my platform, as opposed to writing a whole new LLVM backend?

I don't care much about my code running fast and being highly optimized right now, I just want to get real code compiling to it. I'm okay with figuring out the register allocation, et. al. on my own, I don't need it to be efficient.

  • 1
    I'm confused. I thought a program that reads .ll files and outputs files for your platform is an LLVM back end. – Karl Bielefeldt Sep 4 '18 at 2:04
  • You're right - I'd like to write one external to LLVM, one that simply parses the IR, turns it into a simple AST, and generates code based on it. I've edited the question. – Celarix Sep 4 '18 at 2:13
4

That depends on what you mean by feasible. It's certainly possible.

However, LLVM IR in its initial form is not particularly suitable for direct translation. Inefficient generated IR aside, it's still meant for a rather abstract machine. It has lots of complicated instructions that may not map directly to equivalents on your side, e.g. vector instructions, elementptr, call-with-arguments. It has an infinite number of registers.

So to write an LLVM backend you need to do the things outlined in the basic steps section of that tutorial. Describe the machine, describe its registers, describe its instructions, write some additional code for the emitters, and if you want, add some peephole optimizations. LLVM has lots of code generators to make these tasks more compact and less boilerplate-y than writing the code for it by hand.

To write your own IR compiler, you need to:

  • Read the IR, ideally in both text and bitcode formats (you could use the LLVM libraries for that).
  • Write code to select native instructions to match the IR instructions.
    • In some cases, this may mean longer instruction sequences, i.e. a single LLVM call may resulting in a sequence of stack pushes followed by a call instruction followed by cleanup code.
  • Write code to select native registers to put the values in.
    • You don't have infinite registers. You may have to generate additional instructions to spill some values to the stack, but without interfering with other stack operations.
  • Write code to generate the resulting instruction sequences. This means lots of boilerplate. You have to write a bit of code for every machine instruction.
  • Write code to put the resulting object code into a format that your CPU can process.

This is a lot of work that LLVM no longer does for you. I'd say it's almost certainly a lot faster to just learn how to write the LLVM backend. Yes, the concepts may be a bit daunting up front, but the amount of work you save is significant.

  • Yes, I think you're right. I'm going to have to write a ton of code either way (instruction selection, et. al.), might as well learn LLVM while I do it. – Celarix Sep 5 '18 at 0:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.