Adding conditionals with LLVM

I am trying to create a pass that will add conditional statements like

if(v1<v2){
printf(“a\n”);
}else{
printf(“b”);
}

For test purposes, wrote the following minimal code

void ThisWillBeRunOnFunctions(Function &F) {
LLVMContext &ctx = F.getContext();
IRBuilder<> builder(ctx);

BasicBlock &entryBlock = F.getEntryBlock();
Instruction *firstInstruction = &*(entryBlock.getFirstInsertionPt());

builder.SetInsertPoint(firstInstruction);
Value *condition = builder.getInt1(1 < 2);
BasicBlock *trueBlock = BasicBlock::Create(ctx, "trueBlock", &F);
BasicBlock *falseBlock = BasicBlock::Create(ctx, "falseBlock", &F);
BasicBlock *continueBlock = BasicBlock::Create(ctx, "continueBlock", &F);
builder.CreateCondBr(condition, trueBlock, falseBlock);

Function *printfFunc = F.getParent()->getFunction("printf");
if (!printfFunc) {
    Type *printfTypes[] = {PointerType::get(IntegerType::get(ctx, 8), 0)};
    FunctionType *printfType = FunctionType::get(IntegerType::get(ctx, 32), printfTypes, true);
    printfFunc = Function::Create(printfType, GlobalValue::ExternalLinkage, "printf", F.getParent());
}

builder.SetInsertPoint(trueBlock);
Value *formatStr1 = builder.CreateGlobalStringPtr("aaaaaa\n");
Value *printfArgs1[] = {formatStr1};
builder.CreateCall(printfFunc, printfArgs1, "ebannyrot1");
builder.CreateBr(continueBlock); 

builder.SetInsertPoint(falseBlock);
Value *formatStr2 = builder.CreateGlobalStringPtr("bbbbbb\n");
Value *printfArgs2[] = {formatStr2};
builder.CreateCall(printfFunc, printfArgs2, "ebannyrot2");
builder.CreateBr(continueBlock); 


builder.SetInsertPoint(continueBlock);

}

This does not work. It gives “Basic Block in function ‘main’ does not have terminator!”

I guess there is no terminator :). How do I fix this?

If this is the only code you put into main then you need to make sure that continueBlock has a terminator like ret i32 0. You already set the IRBuilder to the right block at the end so you should just be able to call CreateRet.

You probably already know this and it’s just to get something working, but you’re also inserting your code right at the beginning of the function. If the function is not empty then that will almost certainly break things in the future.

For more complicated cases you’ll probably have to find where you want to insert the code and split the basic block there. Fortunately it’s a common enough situation that there’s llvm::SplitBlock to help.

Thanks for your reply!

  1. I modified the above code by adding

    builder.SetInsertPoint(continueBlock);
    Type *intType = Type::getInt32Ty(ctx);
    Value *constantInt = ConstantInt::get(intType, 0);
    builder.CreateRet(constantInt);

at the end. This gives “Terminator found in the middle of a basic block!” It seems, am adding return statement to the newly inserted block, which is not expected. How to fix this?

  1. As you said, I need to use SplitBlock in order to insert an if statement. As a test, I decided to insert the above if statement after each call to malloc. Below is the code that I wrote.

    void runOnFunctions(Function &F) {
    LLVMContext &ctx = F.getContext();
    Function *printfFunc = F.getParent()->getFunction(“printf”);
    if (!printfFunc) {
    Type *printfTypes = {PointerType::get(IntegerType::get(ctx, 8), 0)};
    FunctionType *printfType = FunctionType::get(IntegerType::get(ctx, 32), printfTypes, true);
    printfFunc = Function::Create(printfType, GlobalValue::ExternalLinkage, “printf”, F.getParent());
    }

    for (auto &BB : F) {
    for (auto &Inst : BB) {
    if (auto *Call = dyn_cast(&Inst)) {
    Function *Callee = Call->getCalledFunction();
    errs()<<“Callee”<<*Callee<<“\n”;

         if (Callee && Callee->getName() == "malloc") {
    
             BasicBlock* X = SplitBlock(
                 &BB,
                 &Inst,
                 (DomTreeUpdater *)nullptr,
                 (LoopInfo *)nullptr,
                 (MemorySSAUpdater *)nullptr,
                 "before",
                 false
                 );
             
             errs()<<" X="<<X<<"\n";
    
             IRBuilder<> builder(ctx);
    
             BasicBlock *entryBlock = X;
             Instruction *firstInstruction = &*(entryBlock->getFirstInsertionPt());
    
             builder.SetInsertPoint(firstInstruction);
             Value *condition = builder.getInt1(1 < 2);
             BasicBlock *trueBlock = BasicBlock::Create(ctx, "trueBlock", &F);
             BasicBlock *falseBlock = BasicBlock::Create(ctx, "falseBlock", &F);
             BasicBlock *continueBlock = BasicBlock::Create(ctx, "continueBlock", &F);
             builder.CreateCondBr(condition, trueBlock, falseBlock);
    
             errs()<<"  builder\n";
    
             builder.SetInsertPoint(trueBlock);
             Value *formatStr1 = builder.CreateGlobalStringPtr("aaaaaa\n");
             Value *printfArgs1[] = {formatStr1};
             builder.CreateCall(printfFunc, printfArgs1, "ebannyrot1");
             builder.CreateBr(continueBlock); 
    
             builder.SetInsertPoint(falseBlock);
             Value *formatStr2 = builder.CreateGlobalStringPtr("bbbbbb\n");
             Value *printfArgs2[] = {formatStr2};
             builder.CreateCall(printfFunc, printfArgs2, "ebannyrot2");
             builder.CreateBr(continueBlock); 
    
    
             builder.SetInsertPoint(continueBlock);
             /*
             Type *intType = Type::getInt32Ty(ctx);
             Value *constantInt = ConstantInt::get(intType, 0);
             builder.CreateRet(constantInt);
             */
         }
     }
    

    }
    }
    }

    There is something wrong with this code. It does not terminate. How to fix it?

Could you run F.dump() after all the code is generated and show us the output? It sounds like there actually is other code going into the function and that affects how best to fix it.

It looks like you’re modifying the blocks while iterating through them. This can be done in LLVM you need to really think about which block you want to look at next after your modification.

Usually you have to manually manage iterators (old-school!) rather than use the simpler for (auto &BB : F) style loop.

I tried F.dump(). It gives opt: symbol lookup error: /path/to/lib.so: undefined symbol: _ZNK4llvm5Value4dumpEv

I would like to check every call to malloc. I see only one way to check all mallocs: iterate over blocks and then over instructions. How would you do that?