For starters, let's consider a relatively straightforward function that takes three integer parameters and returns an arithmetic combination of them. This is nice and simple, especially since it involves no control flow:
int mul_add(int x, int y, int z) { return x * y + z; }
As a preview, the LLVM IR we’re going to end up generating for this function will look like:
define i32 @mul_add(i32 %x, i32 %y, i32 %z) { entry: %tmp = mul i32 %x, %y %tmp2 = add i32 %tmp, %z ret i32 %tmp2 }
If you're unsure what the above code says, skim through the LLVM Language Reference Manual and convince yourself that the above LLVM IR is actually equivalent to the original function. Once you’re satisfied with that, let's move on to actually generating it programmatically!
Of course, before we can start, we need to #include
the appropriate LLVM header files:
#include "llvm/Module.h" #include "llvm/Function.h" #include "llvm/PassManager.h" #include "llvm/CallingConv.h" #include "llvm/Analysis/Verifier.h" #include "llvm/Assembly/PrintModulePass.h" #include "llvm/Support/IRBuilder.h"
Now, let's get started on our real program. Here's what our basic main()
will look like:
using namespace llvm; Module* makeLLVMModule(); int main(int argc, char**argv) { Module* Mod = makeLLVMModule(); verifyModule(*Mod, PrintMessageAction); PassManager PM; PM.add(new PrintModulePass(&llvm::cout)); PM.run(*Mod); delete Mod; return 0; }
The first segment is pretty simple: it creates an LLVM “module.” In LLVM, a module represents a single unit of code that is to be processed together. A module contains things like global variables, function declarations, and implementations. Here we’ve declared a makeLLVMModule()
function to do the real work of creating the module. Don’t worry, we’ll be looking at that one next!
The second segment runs the LLVM module verifier on our newly created module. While this probably isn’t really necessary for a simple module like this one, it's always a good idea, especially if you’re generating LLVM IR based on some input. The verifier will print an error message if your LLVM module is malformed in any way.
Finally, we instantiate an LLVM PassManager
and run
the PrintModulePass
on our module. LLVM uses an explicit pass
infrastructure to manage optimizations and various other things.
A PassManager
, as should be obvious from its name, manages passes:
it is responsible for scheduling them, invoking them, and ensuring the proper
disposal after we’re done with them. For this example, we’re just using a
trivial pass that prints out our module in textual form.
Now onto the interesting part: creating and populating a module. Here's the
first chunk of our makeLLVMModule()
:
Module* makeLLVMModule() { // Module Construction Module* mod = new Module("test");
Exciting, isn’t it!? All we’re doing here is instantiating a module and giving it a name. The name isn’t particularly important unless you’re going to be dealing with multiple modules at once.
Constant* c = mod->getOrInsertFunction("mul_add", /*ret type*/ IntegerType::get(32), /*args*/ IntegerType::get(32), IntegerType::get(32), IntegerType::get(32), /*varargs terminated with null*/ NULL); Function* mul_add = cast<Function>(c); mul_add->setCallingConv(CallingConv::C);
We construct our Function
by calling getOrInsertFunction()
on our module, passing in the name, return type, and argument types of the function. In the case of our mul_add
function, that means one 32-bit integer for the return value and three 32-bit integers for the arguments.
You'll notice that getOrInsertFunction()
doesn't actually return a Function*
. This is because getOrInsertFunction()
will return a cast of the existing function if the function already existed with a different prototype. Since we know that there's not already a mul_add
function, we can safely just cast c
to a Function*
.
In addition, we set the calling convention for our new function to be the C calling convention. This isn’t strictly necessary, but it ensures that our new function will interoperate properly with C code, which is a good thing.
Function::arg_iterator args = mul_add->arg_begin(); Value* x = args++; x->setName("x"); Value* y = args++; y->setName("y"); Value* z = args++; z->setName("z");
While we’re setting up our function, let's also give names to the parameters. This also isn’t strictly necessary (LLVM will generate names for them if you don’t specify them), but it’ll make looking at our output somewhat more pleasant. To name the parameters, we iterate over the arguments of our function and call setName()
on them. We’ll also keep the pointer to x
, y
, and z
around, since we’ll need them when we get around to creating instructions.
Great! We have a function now. But what good is a function if it has no body? Before we start working on a body for our new function, we need to recall some details of the LLVM IR. The IR, being an abstract assembly language, represents control flow using jumps (we call them branches), both conditional and unconditional. The straight-line sequences of code between branches are called basic blocks, or just blocks. To create a body for our function, we fill it with blocks:
BasicBlock* block = BasicBlock::Create("entry", mul_add); IRBuilder<> builder(block);
We create a new basic block, as you might expect, by calling its constructor. All we need to tell it is its name and the function to which it belongs. In addition, we’re creating an IRBuilder
object, which is a convenience interface for creating instructions and appending them to the end of a block. Instructions can be created through their constructors as well, but some of their interfaces are quite complicated. Unless you need a lot of control, using IRBuilder
will make your life simpler.
Value* tmp = builder.CreateBinOp(Instruction::Mul, x, y, "tmp"); Value* tmp2 = builder.CreateBinOp(Instruction::Add, tmp, z, "tmp2"); builder.CreateRet(tmp2); return mod; }
The final step in creating our function is to create the instructions that make it up. Our mul_add
function is composed of just three instructions: a multiply, an add, and a return. IRBuilder
gives us a simple interface for constructing these instructions and appending them to the “entry” block. Each of the calls to IRBuilder
returns a Value*
that represents the value yielded by the instruction. You’ll also notice that, above, x
, y
, and z
are also Value*
's, so it's clear that instructions operate on Value*
's.
And that's it! Now you can compile and run your code, and get a wonderful textual print out of the LLVM IR we saw at the beginning. To compile, use the following command line as a guide:
# c++ -g tut1.cpp `llvm-config --cxxflags --ldflags --libs core` -o tut1 # ./tut1
The llvm-config
utility is used to obtain the necessary GCC-compatible compiler flags for linking with LLVM. For this example, we only need the 'core' library. We'll use others once we start adding optimizers and the JIT engine.