For this assignment you will need to extend the
FunctionPass
.
In particular, methods you might need to override include
virtual bool doInitialization(Module &)
which is inherited from
Pass
. Override this method
to access the current
module
and extract the
context
with:
LLVMContext &context = mod.getContext();
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 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]
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.
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
);
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.