Passes

For this assignment you will need to extend the FunctionPass. In particular, methods you might need to override include

Iterators

Use a Function::iterator to iterate over the basic blocks of a function:

for (Function::iterator B = F.begin(), BE = F.end(); B != BE; ++B) {
// Here B is a pointer to a basic block
  ...
}

Use a BasicBlock::iterator to iterate over the instructions in a basic block:

for (BasicBlock::iterator I = B->begin(), IE = B->end(); I != IE; ++I) {
  // Here I is a pointer to an instruction
  ...
}

Global Variables

Creation

Global variables define regions of memory allocated at compilation time instead of run-time. So this is a useful option if you think you'll need to allocate data that won't be changing throughout the execution of your program (e.g. a static array).

You can create a global variable with the following constructor:

GlobalVariable::GlobalVariable(Module &M,
                               Type * Ty,
                               bool isConstant,
                               LinkageTypes Linkage,
                               Constant * Initializer,
                               const Twine & Name = "")

For example, let's say you want to allocate the static array [0,1]. You would do that by calling

GlobalVariable* v = new GlobalVariable(
    *mod,
    ArrayTy,
    true,
    GlobalValue::InternalLinkage,
    ConstantDataArray::get(*ctx, *(new ArrayRef<uint32_t>({0,1}))),
    "my_array");

where ArrayTy is the LLVM representation of the type of an array with two elements

ArrayType* arrayTy = ArrayType::get(IntegerType::get(F.getContext(), 32), 2);

The above allocates a constant array that can be seen in the instrumented file as a line of the form

@my_array.NNN = internal constant [2 x i32] [i32 0, i32 1]

Reference

To obtain a reference to the above array you will find the getElementPtr instruction particularly useful.

To understand the particulars of this instruction

Alternatively, you might want to look into the bitcast instruction, that converts its argument to a specified type without changing any bits.

For example, to convert a pointer v to the type int*, we can call IBuilder::CreatePointerCast

Value* castV = Builder.CreatePointerCast(v, Type::getInt32PtrTy(context));

The Builder class is introduced in the last section of this tutorial.

Looking up Functions

Use Module::getOrInsertFunction to look up the specified function in the module's symbol table. Note that getOrInsertFunction will return a cast of the existing function if the function already existed with a different prototype.

For example, to look up a function foo that accepts two i32 arguments and returns void, we would issue the call:

Constant *fooFunc = Mod.getOrInsertFunction(
    "foo",                           // name of function
    Type::getVoidTy(context),        // return type
    Type::getInt32Ty(context),       // first parameter type
    Type::getInt32Ty(context),       // second parameter type
    NULL                             // sentinel
  );

Creating a Call

Use the IRBuilder to create instructions and insert them into a basic block.

For example we can define a builder as

IRBuilder<> Builder(&*blk->getFirstInsertionPt());

Suppose we want to call function foo from above with arguments 0 and 1. First we create a vector args to hold the arguments to the call

std::vector<Value*> args;
args.push_back(zero);
args.push_back(one);

where zero is the representation for the number 0

Constant * zero = ConstantInt::get(IntegerType::get(F.getContext(),32), 0);

Finally, we create the actual call

Builder.CreateCall(fooFunc, args);

This will place a call to foo before the basic block that blk points to.