Index: lib/Transforms/NaCl/ExpandVarArgs.cpp |
diff --git a/lib/Transforms/NaCl/ExpandVarArgs.cpp b/lib/Transforms/NaCl/ExpandVarArgs.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e1bd8f93a8873c1a2c542d4c9ecaf1cfd8de8854 |
--- /dev/null |
+++ b/lib/Transforms/NaCl/ExpandVarArgs.cpp |
@@ -0,0 +1,327 @@ |
+//===- ExpandVarArgs.cpp - Expand out variable argument function calls-----===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// This pass expands out all use of variable argument functions. |
+// |
+// This pass replaces a varargs function call with a function call in |
+// which a pointer to the variable arguments is passed explicitly. |
+// The callee explicitly allocates space for the variable arguments on |
+// the stack using "alloca". |
+// |
+// Alignment: |
+// |
+// This pass does not add any alignment padding between the arguments |
+// that are copied onto the stack. We assume that the only argument |
+// types that need to be handled are 32-bit and 64-bit -- i32, i64, |
+// pointers and double: |
+// |
+// * We won't see i1, i8, i16 and float as varargs arguments because |
+// the C standard requires the compiler to promote these to the |
+// types "int" and "double". |
+// |
+// * We won't see va_arg instructions of struct type because Clang |
+// does not yet support them in PNaCl mode. See |
+// https://code.google.com/p/nativeclient/issues/detail?id=2381 |
+// |
+// If such arguments do appear in the input, this pass will generate |
+// correct, working code, but this code might be inefficient due to |
+// using unaligned memory accesses. |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#include "llvm/ADT/SmallVector.h" |
+#include "llvm/IR/DataLayout.h" |
+#include "llvm/IR/Function.h" |
+#include "llvm/IR/Instructions.h" |
+#include "llvm/IR/IntrinsicInst.h" |
+#include "llvm/IR/Module.h" |
+#include "llvm/Pass.h" |
+#include "llvm/Transforms/NaCl.h" |
+ |
+using namespace llvm; |
+ |
+namespace { |
+ // This is a ModulePass because the pass recreates functions in |
+ // order to change their argument lists. |
+ class ExpandVarArgs : public ModulePass { |
+ public: |
+ static char ID; // Pass identification, replacement for typeid |
+ ExpandVarArgs() : ModulePass(ID) { |
+ initializeExpandVarArgsPass(*PassRegistry::getPassRegistry()); |
+ } |
+ |
+ virtual bool runOnModule(Module &M); |
+ }; |
+} |
+ |
+char ExpandVarArgs::ID = 0; |
+INITIALIZE_PASS(ExpandVarArgs, "expand-varargs", |
+ "Expand out variable argument function definitions and calls", |
+ false, false) |
+ |
+static Instruction *CopyDebug(Instruction *NewInst, Instruction *Original) { |
+ NewInst->setDebugLoc(Original->getDebugLoc()); |
+ return NewInst; |
+} |
+ |
+static void ExpandVarArgFunc(Function *Func) { |
+ Type *PtrType = Type::getInt8PtrTy(Func->getContext()); |
+ |
+ FunctionType *FTy = Func->getFunctionType(); |
+ SmallVector<Type *, 8> Params(FTy->param_begin(), FTy->param_end()); |
+ Params.push_back(PtrType); |
+ FunctionType *NFTy = FunctionType::get(FTy->getReturnType(), Params, false); |
+ |
+ // In order to change the function's arguments, we have to recreate |
+ // the function. |
+ Function *NewFunc = Function::Create(NFTy, Func->getLinkage()); |
+ NewFunc->copyAttributesFrom(Func); |
+ Func->getParent()->getFunctionList().insert(Func, NewFunc); |
+ NewFunc->takeName(Func); |
+ NewFunc->getBasicBlockList().splice(NewFunc->begin(), |
+ Func->getBasicBlockList()); |
+ |
+ // Move the arguments across to the new function. |
+ for (Function::arg_iterator Arg = Func->arg_begin(), E = Func->arg_end(), |
+ NewArg = NewFunc->arg_begin(); |
+ Arg != E; ++Arg, ++NewArg) { |
+ Arg->replaceAllUsesWith(NewArg); |
+ NewArg->takeName(Arg); |
+ } |
+ |
+ Func->replaceAllUsesWith( |
+ ConstantExpr::getBitCast(NewFunc, FTy->getPointerTo())); |
+ Func->eraseFromParent(); |
+ |
+ Value *VarArgsArg = --NewFunc->arg_end(); |
+ VarArgsArg->setName("varargs"); |
+ |
+ // Expand out uses of llvm.va_start in this function. |
+ for (Function::iterator BB = NewFunc->begin(), E = NewFunc->end(); |
+ BB != E; |
+ ++BB) { |
+ for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); |
+ Iter != E; ) { |
+ Instruction *Inst = Iter++; |
+ if (VAStartInst *VAS = dyn_cast<VAStartInst>(Inst)) { |
+ Value *Cast = CopyDebug(new BitCastInst(VAS->getArgList(), |
+ PtrType->getPointerTo(), |
+ "arglist", VAS), VAS); |
+ CopyDebug(new StoreInst(VarArgsArg, Cast, VAS), VAS); |
+ VAS->eraseFromParent(); |
+ } |
+ } |
+ } |
+} |
+ |
+static void ExpandVAArgInst(VAArgInst *Inst) { |
+ // Read the argument. We assume that no realignment of the pointer |
+ // is required. |
+ Value *ArgList = CopyDebug(new BitCastInst( |
+ Inst->getPointerOperand(), |
+ Inst->getType()->getPointerTo()->getPointerTo(), "arglist", Inst), Inst); |
+ Value *CurrentPtr = CopyDebug(new LoadInst(ArgList, "arglist_current", Inst), |
+ Inst); |
+ Value *Result = CopyDebug(new LoadInst(CurrentPtr, "va_arg", Inst), Inst); |
+ Result->takeName(Inst); |
+ |
+ // Update the va_list to point to the next argument. |
+ SmallVector<Value *, 1> Indexes; |
+ Indexes.push_back(ConstantInt::get(Inst->getContext(), APInt(32, 1))); |
+ Value *Next = CopyDebug(GetElementPtrInst::Create( |
+ CurrentPtr, Indexes, "arglist_next", Inst), Inst); |
+ CopyDebug(new StoreInst(Next, ArgList, Inst), Inst); |
+ |
+ Inst->replaceAllUsesWith(Result); |
+ Inst->eraseFromParent(); |
+} |
+ |
+static void ExpandVACopyInst(VACopyInst *Inst) { |
+ // va_list may have more space reserved, but we only need to |
+ // copy a single pointer. |
+ Type *PtrTy = Type::getInt8PtrTy(Inst->getContext())->getPointerTo(); |
+ Value *Src = CopyDebug(new BitCastInst(Inst->getSrc(), PtrTy, "vacopy_src", |
+ Inst), Inst); |
+ Value *Dest = CopyDebug(new BitCastInst(Inst->getDest(), PtrTy, "vacopy_dest", |
+ Inst), Inst); |
+ Value *CurrentPtr = CopyDebug(new LoadInst(Src, "vacopy_currentptr", Inst), |
+ Inst); |
+ CopyDebug(new StoreInst(CurrentPtr, Dest, Inst), Inst); |
+ Inst->eraseFromParent(); |
+} |
+ |
+static void LifetimeDecl(Intrinsic::ID id, Value *Ptr, Value *Size, |
+ Instruction *InsertPt) { |
+ Module *M = InsertPt->getParent()->getParent()->getParent(); |
+ Value *Func = Intrinsic::getDeclaration(M, id); |
+ SmallVector<Value *, 2> Args; |
+ Args.push_back(Size); |
+ Args.push_back(Ptr); |
+ CallInst::Create(Func, Args, "", InsertPt); |
+} |
+ |
+// CopyCall() uses argument overloading so that it can be used by the |
+// template ExpandVarArgCall(). |
+static Instruction *CopyCall(CallInst *Original, Value *Callee, |
+ ArrayRef<Value*> Args) { |
+ return CallInst::Create(Callee, Args, "", Original); |
+} |
+ |
+static Instruction *CopyCall(InvokeInst *Original, Value *Callee, |
+ ArrayRef<Value*> Args) { |
+ return InvokeInst::Create(Callee, Original->getNormalDest(), |
+ Original->getUnwindDest(), Args, "", Original); |
+} |
+ |
+// ExpandVarArgCall() converts a CallInst or InvokeInst to expand out |
+// of varargs. It returns whether the module was modified. |
+template <class InstType> |
+static bool ExpandVarArgCall(InstType *Call, DataLayout *DL) { |
+ FunctionType *FuncType = cast<FunctionType>( |
+ Call->getCalledValue()->getType()->getPointerElementType()); |
+ if (!FuncType->isFunctionVarArg()) |
+ return false; |
+ |
+ LLVMContext *Context = &Call->getContext(); |
+ |
+ // Split argument list into fixed and variable arguments. |
+ SmallVector<Value *, 8> FixedArgs; |
+ SmallVector<Value *, 8> VarArgs; |
+ SmallVector<Type *, 8> VarArgsTypes; |
+ for (unsigned I = 0; I < FuncType->getNumParams(); ++I) |
+ FixedArgs.push_back(Call->getArgOperand(I)); |
+ for (unsigned I = FuncType->getNumParams(); |
+ I < Call->getNumArgOperands(); ++I) { |
+ VarArgs.push_back(Call->getArgOperand(I)); |
+ VarArgsTypes.push_back(Call->getArgOperand(I)->getType()); |
+ } |
+ |
+ StructType *VarArgsTy; |
+ Value *ArgToAdd; |
+ Instruction *BufPtr = NULL; |
+ Value *BufSize = NULL; |
+ if (VarArgs.size() == 0) { |
+ // If there are no variable arguments being passed, we still want |
+ // to add an extra argument to the function call so that the |
+ // number of arguments matches the callee's type. |
+ VarArgsTy = StructType::get(*Context); |
+ ArgToAdd = UndefValue::get(VarArgsTy->getPointerTo()); |
+ } else { |
+ // Create struct type for packing variable arguments into. We |
+ // create this as packed for now and assume that no alignment |
+ // padding is desired. |
+ VarArgsTy = StructType::create(VarArgsTypes, "vararg_call", true); |
+ |
+ // Allocate space for the variable argument buffer. Do this at the |
+ // start of the function so that we don't leak space if the function |
+ // is called in a loop. |
+ Function *Func = Call->getParent()->getParent(); |
+ Instruction *Buf = new AllocaInst(VarArgsTy, "vararg_buffer"); |
+ Func->getEntryBlock().getInstList().push_front(Buf); |
+ ArgToAdd = Buf; |
+ |
+ // Call llvm.lifetime.start/end intrinsics to indicate that Buf is |
+ // only used for the duration of the function call, so that the |
+ // stack space can be reused elsewhere. |
+ Type *I8Ptr = Type::getInt8Ty(*Context)->getPointerTo(); |
+ BufPtr = new BitCastInst(Buf, I8Ptr, "vararg_lifetime_bitcast"); |
+ BufPtr->insertAfter(Buf); |
+ BufSize = ConstantInt::get(*Context, |
+ APInt(64, DL->getTypeAllocSize(VarArgsTy))); |
+ LifetimeDecl(Intrinsic::lifetime_start, BufPtr, BufSize, Call); |
+ |
+ // Copy variable arguments into buffer. |
+ int Index = 0; |
+ for (SmallVector<Value *, 8>::iterator Iter = VarArgs.begin(); |
+ Iter != VarArgs.end(); |
+ ++Iter, ++Index) { |
+ SmallVector<Value *, 2> Indexes; |
+ Indexes.push_back(ConstantInt::get(*Context, APInt(32, 0))); |
+ Indexes.push_back(ConstantInt::get(*Context, APInt(32, Index))); |
+ Value *Ptr = CopyDebug(GetElementPtrInst::Create( |
+ Buf, Indexes, "vararg_ptr", Call), Call); |
+ CopyDebug(new StoreInst(*Iter, Ptr, Call), Call); |
+ } |
+ } |
+ |
+ // Cast function to new type to add our extra pointer argument. |
+ SmallVector<Type *, 8> ArgTypes(FuncType->param_begin(), |
+ FuncType->param_end()); |
+ ArgTypes.push_back(VarArgsTy->getPointerTo()); |
+ FunctionType *NFTy = FunctionType::get(FuncType->getReturnType(), |
+ ArgTypes, false); |
+ Value *CastFunc = |
+ CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), |
+ "vararg_func", Call), Call); |
+ |
+ // Create the converted function call. |
+ FixedArgs.push_back(ArgToAdd); |
+ Value *NewCall = CopyDebug(CopyCall(Call, CastFunc, FixedArgs), Call); |
+ NewCall->takeName(Call); |
+ |
+ if (BufPtr) { |
+ if (isa<CallInst>(Call)) { |
+ LifetimeDecl(Intrinsic::lifetime_end, BufPtr, BufSize, Call); |
+ } else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Call)) { |
+ LifetimeDecl(Intrinsic::lifetime_end, BufPtr, BufSize, |
+ Invoke->getNormalDest()->getFirstInsertionPt()); |
+ LifetimeDecl(Intrinsic::lifetime_end, BufPtr, BufSize, |
+ Invoke->getUnwindDest()->getFirstInsertionPt()); |
+ } |
+ } |
+ |
+ Call->replaceAllUsesWith(NewCall); |
+ Call->eraseFromParent(); |
+ |
+ return true; |
+} |
+ |
+bool ExpandVarArgs::runOnModule(Module &M) { |
+ bool Changed = false; |
+ DataLayout DL(&M); |
+ |
+ for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) { |
+ Function *Func = Iter++; |
+ |
+ for (Function::iterator BB = Func->begin(), E = Func->end(); |
+ BB != E; |
+ ++BB) { |
+ for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); |
+ Iter != E; ) { |
+ Instruction *Inst = Iter++; |
+ if (VAArgInst *VI = dyn_cast<VAArgInst>(Inst)) { |
+ Changed = true; |
+ ExpandVAArgInst(VI); |
+ } else if (isa<VAEndInst>(Inst)) { |
+ // va_end() is a no-op in this implementation. |
+ Changed = true; |
+ Inst->eraseFromParent(); |
+ } else if (VACopyInst *VAC = dyn_cast<VACopyInst>(Inst)) { |
+ Changed = true; |
+ ExpandVACopyInst(VAC); |
+ } else if (CallInst *Call = dyn_cast<CallInst>(Inst)) { |
+ Changed |= ExpandVarArgCall(Call, &DL); |
+ } else if (InvokeInst *Call = dyn_cast<InvokeInst>(Inst)) { |
+ Changed |= ExpandVarArgCall(Call, &DL); |
+ } |
+ } |
+ } |
+ |
+ if (Func->isVarArg()) { |
+ Changed = true; |
+ ExpandVarArgFunc(Func); |
+ } |
+ } |
+ |
+ return Changed; |
+} |
+ |
+ModulePass *llvm::createExpandVarArgsPass() { |
+ return new ExpandVarArgs(); |
+} |