How to change the type of operands in the Callinst in llvm?

Hi all,

I am Sue. I am writing a LLVM pass to change the type of float-point variables in the original program to long double. My framework is like this:

for (auto B : F) {
//for every basic block B in the funtion F
for (auto I : B){
//for every instruction I in the basic block B
    if (I is a AllocaInst){
       If the type of I is float or double,
       then change the type to long double, construct new I';
         for (auto U: I->users()){
              //propogate the new type to I's users
              transform(U, I, I');
            }
       }
   }
}

transform(U, I, I'){
    if (U is StoreInst)
        Construct new storeinst;
    if(U is LoadInst){
        Construct new_load;
        for(auto W: U->users())
           transform(W, U, new_load);
     }
    ... ....   
}

I have finished changing the type of AllocaInst, StoreInst, LoadInst, BinaryOperator.

However, I have some trouble when implementing the transformation of CallInst:

  1. Change the type of the arguments of function calls

  2. Construct the body of called function with new type

  3. Change the type of the return value

For example, I want to change the following IR:

    %call = call double @add(double %0, double %1)

    define double @add(double %x, double %y) #0 {
    entry:
      %x.addr = alloca double, align 8
      %y.addr = alloca double, align 8
      store double %x, double* %x.addr, align 8
      store double %y, double* %y.addr, align 8
      %0 = load double, double* %x.addr, align 8
      %1 = load double, double* %x.addr, align 8
      %add = fadd double %0, %1
      ret double %add
    }

To IR_New:

    %call = call x86_fp80 @new_add(x86_fp80 %0, x86_fp80 %1)

    define x86_fp80 @new_add(x86_fp80 %x, x86_fp80 %y) #0 {
    entry:
      %x.addr = alloca x86_fp80, align 16
      %y.addr = alloca x86_fp80, align 16
      store x86_fp80 %x, x86_fp80* %x.addr, align 16
      store x86_fp80 %y, x86_fp80* %y.addr, align 16
      %0 = load x86_fp80, x86_fp80* %x.addr, align 16
      %1 = load x86_fp80, x86_fp80* %x.addr, align 16
      %add = fadd x86_fp80 %0, %1
      ret x86_fp80 %add
    }

My original idea is when I find a CallInst,

   if (CallInst *call = dyn_cast<CallInst>(it)){

do the following three steps:

  1. Construct the new FunctionType

    x86_fp80(x86_fp80, x86_fp80)
    
    

    using

    std::vector<Type*> ParamTys;
    ParamTys.push_back(Type::getX86_FP80Ty(context));
    ParamTys.push_back(Type::getX86_FP80Ty(context));
    FunctionType *new_fun_type = FunctionType::get(Type::getX86_FP80Ty(context), ParamTys, true);
    
    
  2. Construct function with new type in Step 1, i.e. construct new_add in the example

    Function *fun = call->getCalledFunction();
    Function *new_fun = Function::Create(new_fun_type,fun->getLinkage(), "new_add", fun->getParent());
    
    
  3. Construct a new CallInst with the new function obtained from step 2.

    CallInst *new_call = CallInst::Create(new_fun, *arrayRefOperands, "newCall", call);
    new_call->takeName(call);
    }
    
    

However, in this way, I got the following:

   %call = call x86_fp80 (x86_fp80, x86_fp80, ...) @0(x86_fp80 %5, x86_fp80 %7)

   declare x86_fp80 @new_add(x86_fp80, x86_fp80, ...)

A new definition of called function is constructed(declare x86_fp80 @new_add(x86_fp80, x86_fp80, …)), but the body of this new function is empty. I am very confused how to add the body and get the IR_New I want. My naive idea is:

for (Instruction i : called function(add in the example)){
create new_i with type x86_fp80;
insert new_i in the new function constructed(new_add in the example);
}

Is this a good way to achieve my goal please?

Any advice will be greatly appreciated :slight_smile:

Sincerely,

Suhua


Hi Sue,

``

A new definition of called function is constructed(declare x86_fp80 @new_add(x86_fp80, x86_fp80, …)), but the body of this new function is empty. I am very confused how to add the body and get the IR_New I want. My naive idea is:

for (Instruction i : called function(add in the example)){
create new_i with type x86_fp80;
insert new_i in the new function constructed(new_add in the example);
}

Is this a good way to achieve my goal please?

I think this will work but you would have to care about replacing operands coming from function’s argument with new arguments. Maybe take a look at CloneFunctionInto. Once you have created both functions (like it is your case obviously), you need to map arguments together like:

ValueToValueMapTy VMap;
for(Function::arg_iterator b_a = func->arg_begin(), e_a = func->arg_end() ; b_a != e_a ; ++b_a) {
Argument *Arg = &*b_a;
VMap[Arg] = … (argument from the new function);
}

CloneFunctionInto will replace them in the new function body (as they say in the source code, that’s the worst part to implement). Though I am not sure it will work with different types? Maybe consider doing this at beginning of you instrumentation and then do you instrumentation to mutate the type.

Any advice will be greatly appreciated :slight_smile:

Sincerely,

Suhua

Note that if you don’t need to keep your old “add” function there are better ways to do. But for compatibility reasons it can be nice to have it.

Hope that helps,
Pierre