[LLVMdev] Duplicate Function with duplicated Arguments

Matthijs Kooijman matthijs at stdin.nl
Mon Sep 15 07:34:41 PDT 2008


Hi James,

> I'm now writing a pass and I wanna ask a question about how to
> duplicate the function and add duplicated arguments in llvm, for
> example:

Here is some example code. This code creates a new function NF that has one
extra argument as the original F, named "globals". The variable Globals points
to this extra argument. Also, this function adds an extra return value,
containing the final value of the struct the globals argument points to. I've
flagged the lines which are specific to this pass and probably not useful for
you with #.

Also, note that this code destroys the old function. In particular, it uses 
    NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());
to move the instruction list from F to NF, effectively crippling F.

For doing this without destroying F, you can look at
lib/Transforms/Utils/CloneFunction.cpp. In particular the function
CloneFunctionInto looks interesting, though there might be functions that can
even do more stuff for you.

Hope this helps,

Matthijs

    // Start by computing a new prototype for the function, which is the same as
    // the old function, but has an extra argument.
    const llvm::FunctionType *FTy = F->getFunctionType();

    /* Copy the argument types and add an extra struct */
    std::vector<const llvm::Type*> Params(FTy->param_begin(), FTy->param_end());
    Params.push_back(ArgType);

    // Make a new parameter attribute list (they are immutable) that has the new
    // argument marked as byval. Since the parameter attributes include the
    // return type parameters at index 0, we don't use size() - 1, but just
    // size() as index.
    llvm::PAListPtr PAL = F->getParamAttrs().addAttr(Params.size(), llvm::ParamAttr::ByVal);

    // New return type is simply a struct of the old type and the globals type,
    // or just the globals type if the original was void
    const llvm::Type *RetTy = F->getReturnType();
    const llvm::Type *NRetTy;
    if (RetTy == llvm::Type::VoidTy) {
      NRetTy = ArgType->getElementType();
    } else {
      NRetTy = llvm::StructType::get(RetTy, ArgType->getElementType(), NULL);
    }

    // Create the new function type based on the recomputed parameters.
    llvm::FunctionType *NFTy = llvm::FunctionType::get(NRetTy, Params, FTy->isVarArg());

    // Create the new function body and insert it into the module...
    llvm::Function *NF = llvm::Function::Create(NFTy, F->getLinkage());
    NF->copyAttributesFrom(F);
    NF->setParamAttrs(PAL);
    F->getParent()->getFunctionList().insert(F, NF);
    NF->takeName(F);
    for (llvm::Function::arg_iterator AI = F->arg_begin(), AE = F->arg_end(),
          NAI = NF->arg_begin(); AI != AE; ++AI, ++NAI)
      NAI->takeName(AI);

    llvm::Value *Globals = --NF->arg_end();
    Globals->setName("globals");

#   // Prepare our result
#   ArgAndGEPs &Result = GlobalStructs[NF];
#   Result.first = Globals;
   // Since we have now created the new function, splice the body of the old    
    // function right into the new function, leaving the old rotting hulk of the 
    // function empty.
    NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());

    // Now, pimp all return instructions
    for(llvm::Function::iterator BI = NF->begin(), BE = NF->end();
          BI != BE; ++BI)
        if (llvm::ReturnInst *RI = llvm::dyn_cast<llvm::ReturnInst>(BI->getTerminator())) {
          // Don't support functions that had multiple return values
          assert(RI->getNumOperands() < 2);
          // Insert a new load instruction to return
          llvm::Value *Load = new llvm::LoadInst(Globals, "globalsret", RI);
          // Return type was void
          if (RetTy == llvm::Type::VoidTy) {
            llvm::ReturnInst::Create(Load, RI);
            RI->getParent()->getInstList().erase(RI);
          } else {
            // Start out with an empty struct
            llvm::Value *Return = llvm::ConstantAggregateZero::get(NRetTy);
            DOUT << "Return: " << *Return->getType();
            // Insert the original return value in field 0
            Return = llvm::InsertValueInst::Create(Return, RI->getOperand(0), 0, "ret", RI);
            DOUT << "Return: " << *Return->getType();
            // Insert the globals return value in field 1
            Return = llvm::InsertValueInst::Create(Return, Load, 1, "ret", RI);
            DOUT << "Return: " << *Return->getType();

            // And update the return instruction
            RI->setOperand(0, Return);
          }
        }

#   // Create GEPs in this function
#   CreateGEPs(Globals, NF->getEntryBlock().begin(), Result.second);

    DOUT << *NF;

    // Replace all uses of the old arguments with the new arguments
    for (llvm::Function::arg_iterator I = F->arg_begin(), E = F->arg_end(),
           NI = NF->arg_begin(); I != E; ++I, ++NI)
      I->replaceAllUsesWith(NI);

    // Replace all callers
    while (!F->use_empty()) {
      llvm::CallSite CS = llvm::CallSite::get(F->use_back());
      llvm::Instruction *Call = CS.getInstruction();
      llvm::Function *CallingF = Call->getParent()->getParent();

#     /* Get the global struct in our caller */
#     llvm::Value* CallerGlobals = ModifyFunctionRecursive(CallingF).first;

      // Copy the existing arguments
      std::vector<llvm::Value*> Args;
      Args.reserve(CS.arg_size());
      llvm::CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end();
      // First, copy regular arguments
      for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i, ++AI)
        Args.push_back(*AI);
      // Then, insert the new argument
      Args.push_back(CallerGlobals);
      // Lastly, copy any remaining varargs
      for (; AI != AE; ++AI)
        Args.push_back(*AI);

      llvm::Instruction *New;
      llvm::Instruction *Before = Call;
      if (llvm::InvokeInst *II = llvm::dyn_cast<llvm::InvokeInst>(Call)) {
        New = llvm::InvokeInst::Create(NF, II->getNormalDest(), II->getUnwindDest(),
                                 Args.begin(), Args.end(), "", Before);
        llvm::cast<llvm::InvokeInst>(New)->setCallingConv(CS.getCallingConv());
        llvm::cast<llvm::InvokeInst>(New)->setParamAttrs(CS.getParamAttrs());
      } else {
        New = llvm::CallInst::Create(NF, Args.begin(), Args.end(), "", Before);
        llvm::cast<llvm::CallInst>(New)->setCallingConv(CS.getCallingConv());
        llvm::cast<llvm::CallInst>(New)->setParamAttrs(CS.getParamAttrs());
        if (llvm::cast<llvm::CallInst>(Call)->isTailCall())
          llvm::cast<llvm::CallInst>(New)->setTailCall();
      }

      if (Call->hasName())
        New->takeName(Call);
      else
        New->setName(NF->getName() + ".ret");

      llvm::Value *GlobalsRet;
      if (Call->getType() == llvm::Type::VoidTy) {
        // The original function returned nothing, so the new function returns
        // only the globals
        GlobalsRet = New;
      } else {
        // Split the values
        llvm::Value *OrigRet = llvm::ExtractValueInst::Create(New, 0, "origret", Before);
        GlobalsRet     = llvm::ExtractValueInst::Create(New, 1, "globalsret", Before);
        // Replace all the uses of the original result
        Call->replaceAllUsesWith(OrigRet);
      }

      // Now, store the globals back
      new llvm::StoreInst(GlobalsRet, CallerGlobals, Before);

      DOUT << " Call " << *Call << " replaced, function is now " << *Call->getParent()->getParent() << "\n";

      // Finally, remove the old call from the program, reducing the use-count of
      // F.
      Call->eraseFromParent();

    }
    // Delete the old function
    F->eraseFromParent();
    return Result;
  }
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20080915/1f10081b/attachment.sig>


More information about the llvm-dev mailing list