Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 //===- ResolvePNaClIntrinsics.cpp - Resolve calls to PNaCl intrinsics ----====// | 1 //===- ResolvePNaClIntrinsics.cpp - Resolve calls to PNaCl intrinsics ----====// |
| 2 // | 2 // |
| 3 // The LLVM Compiler Infrastructure | 3 // The LLVM Compiler Infrastructure |
| 4 // | 4 // |
| 5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
| 7 // | 7 // |
| 8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
| 9 // | 9 // |
| 10 // This pass resolves calls to PNaCl stable bitcode intrinsics. It is | 10 // This pass resolves calls to PNaCl stable bitcode intrinsics. It is |
| 11 // normally run in the PNaCl translator. | 11 // normally run in the PNaCl translator. |
| 12 // | 12 // |
| 13 // Running AddPNaClExternalDeclsPass is a precondition for running this pass. | 13 // Running AddPNaClExternalDeclsPass is a precondition for running this |
| 14 // They are separate because one is a ModulePass and the other is a | 14 // pass. They are separate because one is a ModulePass and the other is |
| 15 // FunctionPass. | 15 // a FunctionPass. |
| 16 // | 16 // |
| 17 //===----------------------------------------------------------------------===// | 17 //===----------------------------------------------------------------------===// |
| 18 | 18 |
| 19 #include "llvm/ADT/SmallVector.h" | 19 #include "llvm/ADT/SmallVector.h" |
| 20 #include "llvm/IR/Constant.h" | |
| 20 #include "llvm/IR/Instructions.h" | 21 #include "llvm/IR/Instructions.h" |
| 22 #include "llvm/IR/IntrinsicInst.h" | |
| 21 #include "llvm/IR/Intrinsics.h" | 23 #include "llvm/IR/Intrinsics.h" |
| 22 #include "llvm/IR/Module.h" | 24 #include "llvm/IR/Module.h" |
| 25 #include "llvm/IR/NaClIntrinsics.h" | |
| 26 #include "llvm/IR/Value.h" | |
| 23 #include "llvm/Pass.h" | 27 #include "llvm/Pass.h" |
| 28 #include "llvm/Support/MathExtras.h" | |
| 24 #include "llvm/Transforms/NaCl.h" | 29 #include "llvm/Transforms/NaCl.h" |
| 25 | 30 |
| 26 using namespace llvm; | 31 using namespace llvm; |
| 27 | 32 |
| 28 namespace { | 33 namespace { |
| 29 class ResolvePNaClIntrinsics : public FunctionPass { | 34 class ResolvePNaClIntrinsics : public FunctionPass { |
| 35 public: | |
| 36 ResolvePNaClIntrinsics() : FunctionPass(ID) { | |
| 37 initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry()); | |
| 38 } | |
| 39 | |
| 40 static char ID; | |
| 41 virtual bool runOnFunction(Function &F); | |
| 42 | |
| 43 // Interface specifying how calls should be resolved. | |
| 44 class CallResolver { | |
| 30 public: | 45 public: |
| 31 ResolvePNaClIntrinsics() : FunctionPass(ID) { | 46 // Called once per call to the intrinsic in the module. |
| 32 initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry()); | 47 bool Resolve(IntrinsicInst *Call) { |
| 33 } | 48 // To be a well-behaving FunctionPass, don't touch uses in other |
| 34 | 49 // functions. These will be handled when the pass manager gets to |
| 35 static char ID; | 50 // those functions. |
| 36 virtual bool runOnFunction(Function &F); | 51 if (Call->getParent()->getParent() == &F) |
| 52 return DoResolve(Call); | |
| 53 return false; | |
| 54 } | |
| 55 Function *getDeclaration() const { return DoGetDeclaration(); } | |
| 56 std::string getName() { return Intrinsic::getName(IntrinsicID); } | |
| 57 | |
| 58 protected: | |
| 59 Function &F; | |
| 60 Module *M; | |
| 61 Intrinsic::ID IntrinsicID; | |
| 62 | |
| 63 CallResolver(Function &F, Intrinsic::ID IntrinsicID) | |
| 64 : F(F), M(F.getParent()), IntrinsicID(IntrinsicID) {} | |
| 65 virtual ~CallResolver() {} | |
| 66 virtual Function *DoGetDeclaration() const = 0; | |
| 67 virtual bool DoResolve(IntrinsicInst *Call) = 0; | |
| 68 | |
| 37 private: | 69 private: |
| 38 // Some intrinsic calls are resolved simply by replacing the call with a | 70 CallResolver(const CallResolver &); |
|
Derek Schuff
2013/07/02 22:13:17
use LLVM_DELETED_FUNCTION to mark these as uncalla
JF
2013/07/02 23:44:32
Done, in a few other places.
| |
| 39 // call to an alternative function with exactly the same type. | 71 CallResolver &operator=(const CallResolver &); |
| 40 bool resolveSimpleCall(Function &F, Intrinsic::ID IntrinsicID, | |
| 41 const char *TargetFunctionName); | |
| 42 }; | 72 }; |
| 73 | |
| 74 private: | |
| 75 // Walk calls matching the resolver's declaration, and invoke the | |
| 76 // CallResolver methods on each of them. | |
| 77 bool walkCalls(CallResolver &Resolver); | |
| 78 }; | |
| 79 | |
| 80 // Rewrite intrinsic calls to another function. | |
| 81 class SimpleCallResolver : public ResolvePNaClIntrinsics::CallResolver { | |
| 82 public: | |
| 83 SimpleCallResolver(Function &F, Intrinsic::ID IntrinsicID, | |
| 84 const char *TargetFunctionName) | |
| 85 : CallResolver(F, IntrinsicID), | |
| 86 TargetFunction(M->getFunction(TargetFunctionName)) { | |
| 87 // Expect to find the target function for this intrinsic already | |
| 88 // declared, even if it is never used. | |
| 89 if (!TargetFunction) | |
| 90 report_fatal_error(std::string( | |
| 91 "Expected to find external declaration of ") + TargetFunctionName); | |
| 92 } | |
| 93 virtual ~SimpleCallResolver() {} | |
| 94 | |
| 95 private: | |
| 96 Function *TargetFunction; | |
| 97 | |
| 98 virtual Function *DoGetDeclaration() const { | |
| 99 return Intrinsic::getDeclaration(M, IntrinsicID); | |
| 100 } | |
| 101 | |
| 102 virtual bool DoResolve(IntrinsicInst *Call) { | |
| 103 Call->setCalledFunction(TargetFunction); | |
| 104 return true; | |
| 105 } | |
| 106 | |
| 107 SimpleCallResolver(const SimpleCallResolver &); | |
| 108 SimpleCallResolver &operator=(const SimpleCallResolver &); | |
| 109 }; | |
| 110 | |
| 111 // Rewrite atomic intrinsics to LLVM IR instructions. | |
| 112 class AtomicCallResolver : public ResolvePNaClIntrinsics::CallResolver { | |
| 113 public: | |
| 114 AtomicCallResolver(Function &F, NaCl::AtomicIntrinsics::const_iterator I) | |
| 115 : CallResolver(F, I->ID), AI(I) {} | |
| 116 virtual ~AtomicCallResolver() {} | |
| 117 | |
| 118 private: | |
| 119 NaCl::AtomicIntrinsics::const_iterator AI; | |
| 120 | |
| 121 virtual Function *DoGetDeclaration() const { return AI->getDeclaration(M); } | |
| 122 | |
| 123 virtual bool DoResolve(IntrinsicInst *Call) { | |
| 124 // Assume the @llvm.nacl.atomic.* intrinsics follow the PNaCl ABI: | |
| 125 // this should have been checked by the verifier. | |
| 126 const Twine Name(""); | |
| 127 bool isVolatile = false; | |
| 128 SynchronizationScope SS = CrossThread; | |
| 129 Instruction *I; | |
| 130 | |
| 131 switch (Call->getIntrinsicID()) { | |
| 132 default: | |
| 133 llvm_unreachable("unknown atomic intrinsic"); | |
| 134 case Intrinsic::nacl_atomic_load: | |
| 135 I = new LoadInst(Call->getArgOperand(0), Name, isVolatile, | |
| 136 alignmentFromPointer(Call->getArgOperand(0)), | |
| 137 thawMemoryOrder(Call->getArgOperand(1)), SS, Call); | |
| 138 break; | |
| 139 case Intrinsic::nacl_atomic_store: | |
| 140 I = new StoreInst(Call->getArgOperand(0), Call->getArgOperand(1), | |
| 141 isVolatile, | |
| 142 alignmentFromPointer(Call->getArgOperand(1)), | |
| 143 thawMemoryOrder(Call->getArgOperand(2)), SS, Call); | |
| 144 break; | |
| 145 case Intrinsic::nacl_atomic_rmw: | |
| 146 I = new AtomicRMWInst(thawRMWOperation(Call->getArgOperand(0)), | |
| 147 Call->getArgOperand(1), Call->getArgOperand(2), | |
| 148 thawMemoryOrder(Call->getArgOperand(3)), SS, Call); | |
| 149 break; | |
| 150 case Intrinsic::nacl_atomic_cmpxchg: | |
| 151 // TODO LLVM currently doesn't support specifying separate memory | |
| 152 // orders for compare exchange's success and failure cases: | |
| 153 // LLVM IR implicitly drops the Release part of the specified | |
| 154 // memory order on failure. It is therefore correct to map | |
| 155 // the success memory order onto the LLVM IR and ignore the | |
| 156 // failure one. | |
| 157 I = new AtomicCmpXchgInst(Call->getArgOperand(0), Call->getArgOperand(1), | |
| 158 Call->getArgOperand(2), | |
| 159 thawMemoryOrder(Call->getArgOperand(3)), SS, | |
| 160 Call); | |
| 161 break; | |
| 162 case Intrinsic::nacl_atomic_fence: | |
| 163 I = new FenceInst(M->getContext(), | |
| 164 thawMemoryOrder(Call->getArgOperand(0)), SS, Call); | |
| 165 break; | |
| 166 } | |
| 167 I->setDebugLoc(Call->getDebugLoc()); | |
| 168 Call->replaceAllUsesWith(I); | |
| 169 Call->eraseFromParent(); | |
| 170 | |
| 171 return true; | |
| 172 } | |
| 173 | |
| 174 unsigned alignmentFromPointer(const Value *Ptr) const { | |
| 175 unsigned BitWidth = cast<IntegerType>( | |
| 176 cast<PointerType>(Ptr->getType())->getElementType())->getBitWidth(); | |
| 177 return 1 << (CountTrailingZeros_32(BitWidth) - 3); | |
| 178 } | |
| 179 | |
| 180 AtomicOrdering thawMemoryOrder(const Value *MemoryOrder) const { | |
| 181 NaCl::MemoryOrder MO = (NaCl::MemoryOrder) | |
| 182 cast<Constant>(MemoryOrder)->getUniqueInteger().getLimitedValue(); | |
| 183 switch (MO) { | |
| 184 // Only valid values should pass validation. | |
| 185 default: llvm_unreachable("unknown memory order"); | |
| 186 case NaCl::MemoryOrderRelaxed: return Monotonic; | |
| 187 // TODO Consume is unspecified by LLVM's internal IR. | |
| 188 case NaCl::MemoryOrderConsume: return SequentiallyConsistent; | |
| 189 case NaCl::MemoryOrderAcquire: return Acquire; | |
| 190 case NaCl::MemoryOrderRelease: return Release; | |
| 191 case NaCl::MemoryOrderAcquireRelease: return AcquireRelease; | |
| 192 case NaCl::MemoryOrderSequentiallyConsistent: return SequentiallyConsistent; | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 AtomicRMWInst::BinOp thawRMWOperation(const Value *Operation) const { | |
| 197 NaCl::AtomicRMWOperation Op = (NaCl::AtomicRMWOperation) | |
| 198 cast<Constant>(Operation)->getUniqueInteger().getLimitedValue(); | |
| 199 switch (Op) { | |
| 200 // Only valid values should pass validation. | |
| 201 default: llvm_unreachable("unknown atomic RMW operation"); | |
| 202 case NaCl::AtomicAdd: return AtomicRMWInst::Add; | |
| 203 case NaCl::AtomicSub: return AtomicRMWInst::Sub; | |
| 204 case NaCl::AtomicOr: return AtomicRMWInst::Or; | |
| 205 case NaCl::AtomicAnd: return AtomicRMWInst::And; | |
| 206 case NaCl::AtomicXor: return AtomicRMWInst::Xor; | |
| 207 case NaCl::AtomicExchange: return AtomicRMWInst::Xchg; | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 AtomicCallResolver(const AtomicCallResolver &); | |
| 212 AtomicCallResolver &operator=(const AtomicCallResolver &); | |
| 213 }; | |
| 43 } | 214 } |
| 44 | 215 |
| 45 bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F, | 216 bool ResolvePNaClIntrinsics::walkCalls( |
| 46 Intrinsic::ID IntrinsicID, | 217 ResolvePNaClIntrinsics::CallResolver &Resolver) { |
| 47 const char *TargetFunctionName) { | |
| 48 Module *M = F.getParent(); | |
| 49 bool Changed = false; | 218 bool Changed = false; |
| 50 Function *IntrinsicFunction = Intrinsic::getDeclaration(M, IntrinsicID); | 219 Function *IntrinsicFunction = Resolver.getDeclaration(); |
| 51 | 220 if (!IntrinsicFunction) |
| 52 if (!IntrinsicFunction) { | |
| 53 return false; | 221 return false; |
| 54 } | |
| 55 | |
| 56 // Expect to find the target function for this intrinsic already declared | |
| 57 Function *TargetFunction = M->getFunction(TargetFunctionName); | |
| 58 if (!TargetFunction) { | |
| 59 report_fatal_error( | |
| 60 std::string("Expected to find external declaration of ") + | |
| 61 TargetFunctionName); | |
| 62 } | |
| 63 | 222 |
| 64 for (Value::use_iterator UI = IntrinsicFunction->use_begin(), | 223 for (Value::use_iterator UI = IntrinsicFunction->use_begin(), |
| 65 UE = IntrinsicFunction->use_end(); UI != UE;) { | 224 UE = IntrinsicFunction->use_end(); |
| 225 UI != UE;) { | |
| 66 // At this point, the only uses of the intrinsic can be calls, since | 226 // At this point, the only uses of the intrinsic can be calls, since |
| 67 // we assume this pass runs on bitcode that passed ABI verification. | 227 // we assume this pass runs on bitcode that passed ABI verification. |
| 68 CallInst *Call = dyn_cast<CallInst>(*UI++); | 228 IntrinsicInst *Call = dyn_cast<IntrinsicInst>(*UI++); |
| 69 | 229 if (!Call) |
| 70 if (!Call) { | 230 report_fatal_error("Expected use of intrinsic to be a call: " + |
| 71 report_fatal_error( | 231 Resolver.getName()); |
| 72 std::string("Expected use of intrinsic to be a call: ") + | 232 |
| 73 Intrinsic::getName(IntrinsicID)); | 233 Changed |= Resolver.Resolve(Call); |
| 74 } | |
| 75 | |
| 76 // To be a well-behaving FunctionPass, don't touch uses in other | |
| 77 // functions. These will be handled when the pass manager gets to those | |
| 78 // functions. | |
| 79 if (Call->getParent()->getParent() == &F) { | |
| 80 Call->setCalledFunction(TargetFunction); | |
| 81 Changed = true; | |
| 82 } | |
| 83 } | 234 } |
| 84 | 235 |
| 85 return Changed; | 236 return Changed; |
| 86 } | 237 } |
| 87 | 238 |
| 88 bool ResolvePNaClIntrinsics::runOnFunction(Function &F) { | 239 bool ResolvePNaClIntrinsics::runOnFunction(Function &F) { |
| 89 bool Changed = resolveSimpleCall(F, Intrinsic::nacl_setjmp, "setjmp"); | 240 bool Changed = false; |
| 90 Changed |= resolveSimpleCall(F, Intrinsic::nacl_longjmp, "longjmp"); | 241 |
| 242 SimpleCallResolver SetJmpResolver(F, Intrinsic::nacl_setjmp, "setjmp"); | |
| 243 SimpleCallResolver LongJmpResolver(F, Intrinsic::nacl_longjmp, "longjmp"); | |
| 244 Changed |= walkCalls(SetJmpResolver); | |
| 245 Changed |= walkCalls(LongJmpResolver); | |
| 246 | |
| 247 NaCl::AtomicIntrinsics AI(F.getParent()->getContext()); | |
| 248 for (NaCl::AtomicIntrinsics::const_iterator I = AI.begin(), E = AI.end(); | |
| 249 I != E; ++I) { | |
| 250 AtomicCallResolver AtomicResolver(F, I); | |
| 251 Changed |= walkCalls(AtomicResolver); | |
| 252 } | |
| 253 | |
| 91 return Changed; | 254 return Changed; |
| 92 } | 255 } |
| 93 | 256 |
| 94 char ResolvePNaClIntrinsics::ID = 0; | 257 char ResolvePNaClIntrinsics::ID = 0; |
| 95 INITIALIZE_PASS(ResolvePNaClIntrinsics, "resolve-pnacl-intrinsics", | 258 INITIALIZE_PASS(ResolvePNaClIntrinsics, "resolve-pnacl-intrinsics", |
| 96 "Resolve PNaCl intrinsic calls", false, false) | 259 "Resolve PNaCl intrinsic calls", false, false) |
| 97 | 260 |
| 98 FunctionPass *llvm::createResolvePNaClIntrinsicsPass() { | 261 FunctionPass *llvm::createResolvePNaClIntrinsicsPass() { |
| 99 return new ResolvePNaClIntrinsics(); | 262 return new ResolvePNaClIntrinsics(); |
| 100 } | 263 } |
| OLD | NEW |