OLD | NEW |
(Empty) | |
| 1 //===- ExpandSmallArguments.cpp - Expand out arguments smaller than i32----===// |
| 2 // |
| 3 // The LLVM Compiler Infrastructure |
| 4 // |
| 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. |
| 7 // |
| 8 //===----------------------------------------------------------------------===// |
| 9 // |
| 10 // LLVM IR allows function return types and argument types such as |
| 11 // "zeroext i8" and "signext i8". The Language Reference says that |
| 12 // zeroext "indicates to the code generator that the parameter or |
| 13 // return value should be zero-extended to the extent required by the |
| 14 // target's ABI (which is usually 32-bits, but is 8-bits for a i1 on |
| 15 // x86-64) by the caller (for a parameter) or the callee (for a return |
| 16 // value)". |
| 17 // |
| 18 // This can lead to non-portable behaviour when calling functions |
| 19 // without C prototypes or with wrong C prototypes. |
| 20 // |
| 21 // In order to remove this non-portability from PNaCl, and to simplify |
| 22 // the language that the PNaCl translator accepts, the |
| 23 // ExpandSmallArguments pass widens integer arguments and return types |
| 24 // to be at least 32 bits. The pass inserts explicit cast |
| 25 // instructions (ZExtInst/SExtInst/TruncInst) as needed. |
| 26 // |
| 27 // The pass chooses between ZExtInst and SExtInst widening based on |
| 28 // whether a "signext" attribute is present. However, in principle |
| 29 // the pass could always use zero-extension, because the extent to |
| 30 // which either zero-extension or sign-extension is done is up to the |
| 31 // target ABI, which is up to PNaCl to specify. |
| 32 // |
| 33 //===----------------------------------------------------------------------===// |
| 34 |
| 35 #include "llvm/IR/Function.h" |
| 36 #include "llvm/IR/Instructions.h" |
| 37 #include "llvm/IR/IntrinsicInst.h" |
| 38 #include "llvm/IR/Module.h" |
| 39 #include "llvm/Pass.h" |
| 40 #include "llvm/Transforms/NaCl.h" |
| 41 #include "llvm/Transforms/Utils/BasicBlockUtils.h" |
| 42 |
| 43 using namespace llvm; |
| 44 |
| 45 namespace { |
| 46 // This is a ModulePass because the pass recreates functions in |
| 47 // order to change their arguments' types. |
| 48 class ExpandSmallArguments : public ModulePass { |
| 49 public: |
| 50 static char ID; // Pass identification, replacement for typeid |
| 51 ExpandSmallArguments() : ModulePass(ID) { |
| 52 initializeExpandSmallArgumentsPass(*PassRegistry::getPassRegistry()); |
| 53 } |
| 54 |
| 55 virtual bool runOnModule(Module &M); |
| 56 }; |
| 57 } |
| 58 |
| 59 char ExpandSmallArguments::ID = 0; |
| 60 INITIALIZE_PASS(ExpandSmallArguments, "expand-small-arguments", |
| 61 "Expand function arguments to be at least 32 bits in size", |
| 62 false, false) |
| 63 |
| 64 // Returns the normalized version of the given argument/return type. |
| 65 static Type *NormalizeType(Type *Ty) { |
| 66 if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) { |
| 67 if (IntTy->getBitWidth() < 32) { |
| 68 return IntegerType::get(Ty->getContext(), 32); |
| 69 } |
| 70 } |
| 71 return Ty; |
| 72 } |
| 73 |
| 74 // Returns the normalized version of the given function type. |
| 75 static FunctionType *NormalizeFunctionType(FunctionType *FTy) { |
| 76 if (FTy->isVarArg()) { |
| 77 report_fatal_error( |
| 78 "ExpandSmallArguments does not handle varargs functions"); |
| 79 } |
| 80 SmallVector<Type *, 8> ArgTypes; |
| 81 for (unsigned I = 0; I < FTy->getNumParams(); ++I) { |
| 82 ArgTypes.push_back(NormalizeType(FTy->getParamType(I))); |
| 83 } |
| 84 return FunctionType::get(NormalizeType(FTy->getReturnType()), |
| 85 ArgTypes, false); |
| 86 } |
| 87 |
| 88 // Convert the given function to use normalized argument/return types. |
| 89 static bool ConvertFunction(Function *Func) { |
| 90 FunctionType *FTy = Func->getFunctionType(); |
| 91 FunctionType *NFTy = NormalizeFunctionType(FTy); |
| 92 if (NFTy == FTy) |
| 93 return false; // No change needed. |
| 94 Function *NewFunc = RecreateFunction(Func, NFTy); |
| 95 |
| 96 // Move the arguments across to the new function. |
| 97 for (Function::arg_iterator Arg = Func->arg_begin(), E = Func->arg_end(), |
| 98 NewArg = NewFunc->arg_begin(); |
| 99 Arg != E; ++Arg, ++NewArg) { |
| 100 NewArg->takeName(Arg); |
| 101 if (Arg->getType() == NewArg->getType()) { |
| 102 Arg->replaceAllUsesWith(NewArg); |
| 103 } else { |
| 104 Instruction *Trunc = new TruncInst( |
| 105 NewArg, Arg->getType(), NewArg->getName() + ".arg_trunc", |
| 106 NewFunc->getEntryBlock().getFirstInsertionPt()); |
| 107 Arg->replaceAllUsesWith(Trunc); |
| 108 } |
| 109 } |
| 110 |
| 111 if (FTy->getReturnType() != NFTy->getReturnType()) { |
| 112 // Fix up return instructions. |
| 113 Instruction::CastOps CastType = |
| 114 Func->getAttributes().hasAttribute(0, Attribute::SExt) ? |
| 115 Instruction::SExt : Instruction::ZExt; |
| 116 for (Function::iterator BB = NewFunc->begin(), E = NewFunc->end(); |
| 117 BB != E; |
| 118 ++BB) { |
| 119 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); |
| 120 Iter != E; ) { |
| 121 Instruction *Inst = Iter++; |
| 122 if (ReturnInst *Ret = dyn_cast<ReturnInst>(Inst)) { |
| 123 Value *Ext = CopyDebug( |
| 124 CastInst::Create(CastType, Ret->getReturnValue(), |
| 125 NFTy->getReturnType(), |
| 126 Ret->getReturnValue()->getName() + ".ret_ext", |
| 127 Ret), |
| 128 Ret); |
| 129 CopyDebug(ReturnInst::Create(Ret->getContext(), Ext, Ret), Ret); |
| 130 Ret->eraseFromParent(); |
| 131 } |
| 132 } |
| 133 } |
| 134 } |
| 135 |
| 136 Func->eraseFromParent(); |
| 137 return true; |
| 138 } |
| 139 |
| 140 // Convert the given call to use normalized argument/return types. |
| 141 template <class T> static bool ConvertCall(T *Call, Pass *P) { |
| 142 // Don't try to change calls to intrinsics. |
| 143 if (isa<IntrinsicInst>(Call)) |
| 144 return false; |
| 145 FunctionType *FTy = cast<FunctionType>( |
| 146 Call->getCalledValue()->getType()->getPointerElementType()); |
| 147 FunctionType *NFTy = NormalizeFunctionType(FTy); |
| 148 if (NFTy == FTy) |
| 149 return false; // No change needed. |
| 150 |
| 151 // Convert arguments. |
| 152 SmallVector<Value *, 8> Args; |
| 153 for (unsigned I = 0; I < Call->getNumArgOperands(); ++I) { |
| 154 Value *Arg = Call->getArgOperand(I); |
| 155 if (NFTy->getParamType(I) != FTy->getParamType(I)) { |
| 156 Instruction::CastOps CastType = |
| 157 Call->getAttributes().hasAttribute(I + 1, Attribute::SExt) ? |
| 158 Instruction::SExt : Instruction::ZExt; |
| 159 Arg = CopyDebug(CastInst::Create(CastType, Arg, NFTy->getParamType(I), |
| 160 "arg_ext", Call), Call); |
| 161 } |
| 162 Args.push_back(Arg); |
| 163 } |
| 164 Value *CastFunc = |
| 165 CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), |
| 166 Call->getName() + ".arg_cast", Call), Call); |
| 167 Value *Result = NULL; |
| 168 if (CallInst *OldCall = dyn_cast<CallInst>(Call)) { |
| 169 CallInst *NewCall = CopyDebug(CallInst::Create(CastFunc, Args, "", OldCall), |
| 170 OldCall); |
| 171 NewCall->takeName(OldCall); |
| 172 NewCall->setAttributes(OldCall->getAttributes()); |
| 173 NewCall->setCallingConv(OldCall->getCallingConv()); |
| 174 NewCall->setTailCall(OldCall->isTailCall()); |
| 175 Result = NewCall; |
| 176 |
| 177 if (FTy->getReturnType() != NFTy->getReturnType()) { |
| 178 Result = CopyDebug(new TruncInst(NewCall, FTy->getReturnType(), |
| 179 NewCall->getName() + ".ret_trunc", Call), |
| 180 Call); |
| 181 } |
| 182 } else if (InvokeInst *OldInvoke = dyn_cast<InvokeInst>(Call)) { |
| 183 BasicBlock *Parent = OldInvoke->getParent(); |
| 184 BasicBlock *NormalDest = OldInvoke->getNormalDest(); |
| 185 BasicBlock *UnwindDest = OldInvoke->getUnwindDest(); |
| 186 |
| 187 if (FTy->getReturnType() != NFTy->getReturnType()) { |
| 188 if (BasicBlock *SplitDest = SplitCriticalEdge(Parent, NormalDest)) { |
| 189 NormalDest = SplitDest; |
| 190 } |
| 191 } |
| 192 |
| 193 InvokeInst *New = CopyDebug(InvokeInst::Create(CastFunc, NormalDest, |
| 194 UnwindDest, Args, |
| 195 "", OldInvoke), |
| 196 OldInvoke); |
| 197 New->takeName(OldInvoke); |
| 198 |
| 199 if (FTy->getReturnType() != NFTy->getReturnType()) { |
| 200 Result = CopyDebug(new TruncInst(New, FTy->getReturnType(), |
| 201 New->getName() + ".ret_trunc", |
| 202 NormalDest->getTerminator()), |
| 203 OldInvoke); |
| 204 } else { |
| 205 Result = New; |
| 206 } |
| 207 |
| 208 New->setAttributes(OldInvoke->getAttributes()); |
| 209 New->setCallingConv(OldInvoke->getCallingConv()); |
| 210 } |
| 211 Call->replaceAllUsesWith(Result); |
| 212 Call->eraseFromParent(); |
| 213 return true; |
| 214 } |
| 215 |
| 216 bool ExpandSmallArguments::runOnModule(Module &M) { |
| 217 bool Changed = false; |
| 218 for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) { |
| 219 Function *Func = Iter++; |
| 220 // Don't try to change intrinsic declarations because intrinsics |
| 221 // will continue to have non-normalized argument types. For |
| 222 // example, memset() takes an i8 argument. It shouldn't matter |
| 223 // whether we modify the types of other function declarations, but |
| 224 // we don't expect to see non-intrinsic function declarations in a |
| 225 // PNaCl pexe. |
| 226 if (Func->empty()) |
| 227 continue; |
| 228 |
| 229 for (Function::iterator BB = Func->begin(), E = Func->end(); BB != E; |
| 230 ++BB) { |
| 231 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E;) { |
| 232 Instruction *Inst = Iter++; |
| 233 if (CallInst *Call = dyn_cast<CallInst>(Inst)) { |
| 234 Changed |= ConvertCall(Call, this); |
| 235 } else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) { |
| 236 Changed |= ConvertCall(Invoke, this); |
| 237 } |
| 238 } |
| 239 } |
| 240 |
| 241 Changed |= ConvertFunction(Func); |
| 242 } |
| 243 return Changed; |
| 244 } |
| 245 |
| 246 ModulePass *llvm::createExpandSmallArgumentsPass() { |
| 247 return new ExpandSmallArguments(); |
| 248 } |
OLD | NEW |