Add call printf instructions problems

Hello, everyone!
I want to write a pass which can insert a call “printf” instructions before every instruction in the LLVM IR. here is what I wrote:

namespace {
class call_print : public FunctionPass{

private:
DenseMap<const Value*, int> inst_map;

public:
static char ID;
call_print() : FunctionPass(ID){}

//define a extern function “printf”
static llvm::Function*
printf_prototype(llvm::LLVMContext& ctx, llvm::Module *mod)
{
std::vectorllvm::Type* printf_arg_types;
printf_arg_types.push_back(llvm::Type::getInt32Ty(ctx));

llvm::FunctionType* printf_type =
llvm::FunctionType::get(llvm::Type::getInt32Ty(ctx), printf_arg_types, true);

llvm::Function *func = llvm::Function::Create(printf_type, llvm::Function::ExternalLinkage,
llvm::Twine(“printf”),mod);
func->setCallingConv(llvm::CallingConv::C);
return func;
}

//get a printf function
Function* Get_print()
{
llvm::LLVMContext& ctx = llvm::getGlobalContext();
Module* mod = new Module(“test”,ctx);
// Constant* c = mod->getOrInsertFunction(“printf”);
Function *printf_func = printf_prototype(ctx, mod);
printf_func->setCallingConv(CallingConv::C);
return printf_func;
}

virtual bool runOnFunction(Function &F)
{
int id = 0;
// get a Function
Function *call_print = Get_print();
for(inst_iterator i = inst_begin(F),e = inst_end(F);i != e; i++,id++)
{
errs()<<"@"<<id<<": “<<*i<<”\n";
std::vectorllvm::Value* paramArrayRef;
Value a = ConstantInt::get(Type::getInt32Ty(getGlobalContext()),1);
paramArrayRef.push_back(a);
// get an instruction pointer
Instruction
ins_temp = &*i;
//create a call instruction and insert it before every instruction
CallInst *call_print = CallInst::Create(call_print,paramArrayRef,"",ins_temp);

DEBUG(errs()<<“insert an instruction:”<<*call_print<<"\n");

}

return true;
}
};

char call_print::ID = 1;
// register the printCode class:
// - give it a command-line argument (printCode)
// - a name (“print code”)
// - a flag saying that we don’t modify the CFG
// - a flag saying this is not an analysis pass
RegisterPass<call_print> X(“call_print”, “call print func”,
false, false);

}

I successfully compile it .but when I use the call_print.so to transform the test .bc file like this:

opt -load /home/king/llvm/Release+Asserts/lib/call_print.so -call_print <test_sum.bc> test_sum.call.bc -debug

It failed with such information:

/home/king/llvm/include/llvm/Support/Casting.h:237: typename enable_if<is_same<Y, typename simplify_type::SimpleType>, typename cast_retty<X, Y *>::ret_type>::type llvm::cast(Y *) [X = llvm::PointerType, Y = llvm::Type]: Assertion `isa(Val) && “cast() argument of incompatible type!”’ failed.

Can anyone tell me what the problem is ? And whether I wrote the pass right ?
Thank you!

Hi Jin,

It's difficult to say just from looking at a pass, but one thing looked odd:

CallInst *call_print = CallInst::Create(call_print,paramArrayRef,"",ins_temp);

This looks very dodgy. The "call_print" being used as an argument is
the (uninitialised) one that's just been declared. This could be the
source of the assertion failure (though a segfault is just as likely).

Other than that I'd suggest hooking up a debugger and going up the
call frames when that assertion hits. That should tell you exactly
which line of your pass is causing trouble.

Cheers.

Tim.

As Tim said, call_print is one of the root causes.

Another problem is that for a pass, you’d better use llvm::Module::getOrInsertFunction API.

Best Regards,
Hongxu Chen

Thanks,Cheers!

I found the problem is that the “Function *call_print” using the same name as the “class call_print”, which made the compiler wrongly resolved the call_print type!

But I got another problems. I successfully compile the pass and I can insert the call printf (C Lib function) instructions in the LLVM IR(eg: call.bc). If the call.bc didn’t contain call printf instruction ,I can call printf successfully ,but if not ,there exists a problem. the llvm will rename my inserted call function , and the transformed code can not run correctly!

the original .bc file:

; ModuleID = ‘call.bc’
target datalayout = “e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128”
target triple = “x86_64-unknown-linux-gnu”

@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%a = alloca i32, align 4
store i32 0, i32* %retval
store i32 3, i32* %a, align 4
%0 = load i32* %a, align 4
%call = call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %0)
%1 = load i32* %a, align 4
ret i32 %1
}

//contains call printf
declare i32 @printf(i8*, …) #1

attributes #0 = { nounwind uwtable “less-precise-fpmad”=“false” “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf”=“true” “no-infs-fp-math”=“false” “no-nans-fp-math”=“false” “unsafe-fp-math”=“false” “use-soft-float”=“false” }
attributes #1 = { “less-precise-fpmad”=“false” “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf”=“true” “no-infs-fp-math”=“false” “no-nans-fp-math”=“false” “unsafe-fp-math”=“false” “use-soft-float”=“false” }

here is the .bc file that I get after the transform:

; ModuleID = ‘call.opt.bc’
target datalayout = “e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128”
target triple = “x86_64-unknown-linux-gnu”

@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@.str2 = private constant [4 x i8] c"%d\0A\00"

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%a = alloca i32, align 4
store i32 0, i32* %retval
// this is the instruction that I inserted ,which calls a C Lib “printf”
%0 = call i32 (i8*, …)* @printf1(i8* getelementptr inbounds ([4 x i8]* @.str2, i32 0, i32 0), i32 2)
store i32 3, i32* %a, align 4
%1 = load i32* %a, align 4
%call = call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i32 %1)
%2 = load i32* %a, align 4
ret i32 %2
}

declare i32 @printf(i8*, …) #1

// if the program didn’t contain call printf instruction ,this declaration should be as the one above,but now it was renamed and can not be resolved
declare i32 @printf1(i8*, …)

attributes #0 = { nounwind uwtable “less-precise-fpmad”=“false” “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf”=“true” “no-infs-fp-math”=“false” “no-nans-fp-math”=“false” “unsafe-fp-math”=“false” “use-soft-float”=“false” }
attributes #1 = { “less-precise-fpmad”=“false” “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf”=“true” “no-infs-fp-math”=“false” “no-nans-fp-math”=“false” “unsafe-fp-math”=“false” “use-soft-float”=“false” }

Is there any method to avoid the rename manipulation ?
Thanks!

But I got another problems. I successfully compile the pass and I can insert
the call printf (C Lib function) instructions in the LLVM IR(eg: call.bc).
If the call.bc didn't contain call printf instruction ,I can call printf
successfully ,but if not ,there exists a problem. the llvm will rename my
inserted call function , and the transformed code can not run correctly!

That looks like the issue Hongxu mentioned: you're always creating a
new Function (at which point LLVM renames it if one already exists)
when you should really be calling GetOrInsertFunction to lookup an
existing "printf" if it's available.

Cheers.

Tim.

Thank you ,Cheers.
I also found a good example of inserting call “printf” instruction in https://chromium.googlesource.com/chromiumos/third_party/llvm/+/release_16/lib/Transforms/Instrumentation/TraceValues.cpp

The code in this link also use GetOrInsertFunction().