Index: lib/Transforms/NaCl/ExpandSmallArguments.cpp |
diff --git a/lib/Transforms/NaCl/ExpandSmallArguments.cpp b/lib/Transforms/NaCl/ExpandSmallArguments.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b252169d4b75dd237f8f4a02879684e8d8d8a3a9 |
--- /dev/null |
+++ b/lib/Transforms/NaCl/ExpandSmallArguments.cpp |
@@ -0,0 +1,248 @@ |
+//===- ExpandSmallArguments.cpp - Expand out arguments smaller than i32----===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// LLVM IR allows function return types and argument types such as |
+// "zeroext i8" and "signext i8". The Language Reference says that |
+// zeroext "indicates to the code generator that the parameter or |
+// return value should be zero-extended to the extent required by the |
+// target's ABI (which is usually 32-bits, but is 8-bits for a i1 on |
+// x86-64) by the caller (for a parameter) or the callee (for a return |
+// value)". |
+// |
+// This can lead to non-portable behaviour when calling functions |
+// without C prototypes or with wrong C prototypes. |
+// |
+// In order to remove this non-portability from PNaCl, and to simplify |
+// the language that the PNaCl translator accepts, the |
+// ExpandSmallArguments pass widens integer arguments and return types |
+// to be at least 32 bits. The pass inserts explicit cast |
+// instructions (ZExtInst/SExtInst/TruncInst) as needed. |
+// |
+// The pass chooses between ZExtInst and SExtInst widening based on |
+// whether a "signext" attribute is present. However, in principle |
+// the pass could always use zero-extension, because the extent to |
+// which either zero-extension or sign-extension is done is up to the |
+// target ABI, which is up to PNaCl to specify. |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#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" |
+#include "llvm/Transforms/Utils/BasicBlockUtils.h" |
+ |
+using namespace llvm; |
+ |
+namespace { |
+ // This is a ModulePass because the pass recreates functions in |
+ // order to change their arguments' types. |
+ class ExpandSmallArguments : public ModulePass { |
+ public: |
+ static char ID; // Pass identification, replacement for typeid |
+ ExpandSmallArguments() : ModulePass(ID) { |
+ initializeExpandSmallArgumentsPass(*PassRegistry::getPassRegistry()); |
+ } |
+ |
+ virtual bool runOnModule(Module &M); |
+ }; |
+} |
+ |
+char ExpandSmallArguments::ID = 0; |
+INITIALIZE_PASS(ExpandSmallArguments, "expand-small-arguments", |
+ "Expand function arguments to be at least 32 bits in size", |
+ false, false) |
+ |
+// Returns the normalized version of the given argument/return type. |
+static Type *NormalizeType(Type *Ty) { |
+ if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) { |
+ if (IntTy->getBitWidth() < 32) { |
+ return IntegerType::get(Ty->getContext(), 32); |
+ } |
+ } |
+ return Ty; |
+} |
+ |
+// Returns the normalized version of the given function type. |
+static FunctionType *NormalizeFunctionType(FunctionType *FTy) { |
+ if (FTy->isVarArg()) { |
+ report_fatal_error( |
+ "ExpandSmallArguments does not handle varargs functions"); |
+ } |
+ SmallVector<Type *, 8> ArgTypes; |
+ for (unsigned I = 0; I < FTy->getNumParams(); ++I) { |
+ ArgTypes.push_back(NormalizeType(FTy->getParamType(I))); |
+ } |
+ return FunctionType::get(NormalizeType(FTy->getReturnType()), |
+ ArgTypes, false); |
+} |
+ |
+// Convert the given function to use normalized argument/return types. |
+static bool ConvertFunction(Function *Func) { |
+ FunctionType *FTy = Func->getFunctionType(); |
+ FunctionType *NFTy = NormalizeFunctionType(FTy); |
+ if (NFTy == FTy) |
+ return false; // No change needed. |
+ Function *NewFunc = RecreateFunction(Func, NFTy); |
+ |
+ // 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) { |
+ NewArg->takeName(Arg); |
+ if (Arg->getType() == NewArg->getType()) { |
+ Arg->replaceAllUsesWith(NewArg); |
+ } else { |
+ Instruction *Trunc = new TruncInst( |
+ NewArg, Arg->getType(), NewArg->getName() + ".arg_trunc", |
+ NewFunc->getEntryBlock().getFirstInsertionPt()); |
+ Arg->replaceAllUsesWith(Trunc); |
+ } |
+ } |
+ |
+ if (FTy->getReturnType() != NFTy->getReturnType()) { |
+ // Fix up return instructions. |
+ Instruction::CastOps CastType = |
+ Func->getAttributes().hasAttribute(0, Attribute::SExt) ? |
+ Instruction::SExt : Instruction::ZExt; |
+ 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 (ReturnInst *Ret = dyn_cast<ReturnInst>(Inst)) { |
+ Value *Ext = CopyDebug( |
+ CastInst::Create(CastType, Ret->getReturnValue(), |
+ NFTy->getReturnType(), |
+ Ret->getReturnValue()->getName() + ".ret_ext", |
+ Ret), |
+ Ret); |
+ CopyDebug(ReturnInst::Create(Ret->getContext(), Ext, Ret), Ret); |
+ Ret->eraseFromParent(); |
+ } |
+ } |
+ } |
+ } |
+ |
+ Func->eraseFromParent(); |
+ return true; |
+} |
+ |
+// Convert the given call to use normalized argument/return types. |
+template <class T> static bool ConvertCall(T *Call, Pass *P) { |
+ // Don't try to change calls to intrinsics. |
+ if (isa<IntrinsicInst>(Call)) |
+ return false; |
+ FunctionType *FTy = cast<FunctionType>( |
+ Call->getCalledValue()->getType()->getPointerElementType()); |
+ FunctionType *NFTy = NormalizeFunctionType(FTy); |
+ if (NFTy == FTy) |
+ return false; // No change needed. |
+ |
+ // Convert arguments. |
+ SmallVector<Value *, 8> Args; |
+ for (unsigned I = 0; I < Call->getNumArgOperands(); ++I) { |
+ Value *Arg = Call->getArgOperand(I); |
+ if (NFTy->getParamType(I) != FTy->getParamType(I)) { |
+ Instruction::CastOps CastType = |
+ Call->getAttributes().hasAttribute(I + 1, Attribute::SExt) ? |
+ Instruction::SExt : Instruction::ZExt; |
+ Arg = CopyDebug(CastInst::Create(CastType, Arg, NFTy->getParamType(I), |
+ "arg_ext", Call), Call); |
+ } |
+ Args.push_back(Arg); |
+ } |
+ Value *CastFunc = |
+ CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), |
+ Call->getName() + ".arg_cast", Call), Call); |
+ Value *Result = NULL; |
+ if (CallInst *OldCall = dyn_cast<CallInst>(Call)) { |
+ CallInst *NewCall = CopyDebug(CallInst::Create(CastFunc, Args, "", OldCall), |
+ OldCall); |
+ NewCall->takeName(OldCall); |
+ NewCall->setAttributes(OldCall->getAttributes()); |
+ NewCall->setCallingConv(OldCall->getCallingConv()); |
+ NewCall->setTailCall(OldCall->isTailCall()); |
+ Result = NewCall; |
+ |
+ if (FTy->getReturnType() != NFTy->getReturnType()) { |
+ Result = CopyDebug(new TruncInst(NewCall, FTy->getReturnType(), |
+ NewCall->getName() + ".ret_trunc", Call), |
+ Call); |
+ } |
+ } else if (InvokeInst *OldInvoke = dyn_cast<InvokeInst>(Call)) { |
+ BasicBlock *Parent = OldInvoke->getParent(); |
+ BasicBlock *NormalDest = OldInvoke->getNormalDest(); |
+ BasicBlock *UnwindDest = OldInvoke->getUnwindDest(); |
+ |
+ if (FTy->getReturnType() != NFTy->getReturnType()) { |
+ if (BasicBlock *SplitDest = SplitCriticalEdge(Parent, NormalDest)) { |
+ NormalDest = SplitDest; |
+ } |
+ } |
+ |
+ InvokeInst *New = CopyDebug(InvokeInst::Create(CastFunc, NormalDest, |
+ UnwindDest, Args, |
+ "", OldInvoke), |
+ OldInvoke); |
+ New->takeName(OldInvoke); |
+ |
+ if (FTy->getReturnType() != NFTy->getReturnType()) { |
+ Result = CopyDebug(new TruncInst(New, FTy->getReturnType(), |
+ New->getName() + ".ret_trunc", |
+ NormalDest->getTerminator()), |
+ OldInvoke); |
+ } else { |
+ Result = New; |
+ } |
+ |
+ New->setAttributes(OldInvoke->getAttributes()); |
+ New->setCallingConv(OldInvoke->getCallingConv()); |
+ } |
+ Call->replaceAllUsesWith(Result); |
+ Call->eraseFromParent(); |
+ return true; |
+} |
+ |
+bool ExpandSmallArguments::runOnModule(Module &M) { |
+ bool Changed = false; |
+ for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) { |
+ Function *Func = Iter++; |
+ // Don't try to change intrinsic declarations because intrinsics |
+ // will continue to have non-normalized argument types. For |
+ // example, memset() takes an i8 argument. It shouldn't matter |
+ // whether we modify the types of other function declarations, but |
+ // we don't expect to see non-intrinsic function declarations in a |
+ // PNaCl pexe. |
+ if (Func->empty()) |
+ continue; |
+ |
+ 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 (CallInst *Call = dyn_cast<CallInst>(Inst)) { |
+ Changed |= ConvertCall(Call, this); |
+ } else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) { |
+ Changed |= ConvertCall(Invoke, this); |
+ } |
+ } |
+ } |
+ |
+ Changed |= ConvertFunction(Func); |
+ } |
+ return Changed; |
+} |
+ |
+ModulePass *llvm::createExpandSmallArgumentsPass() { |
+ return new ExpandSmallArguments(); |
+} |