OLD | NEW |
(Empty) | |
| 1 //===- ResolvePNaClIntrinsics.cpp - Resolve calls to PNaCl intrinsics ----====// |
| 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 // This pass resolves calls to PNaCl stable bitcode intrinsics. It is |
| 11 // normally run in the PNaCl translator. |
| 12 // |
| 13 // Running AddPNaClExternalDeclsPass is a precondition for running this |
| 14 // pass. They are separate because one is a ModulePass and the other is |
| 15 // a FunctionPass. |
| 16 // |
| 17 //===----------------------------------------------------------------------===// |
| 18 |
| 19 #include "llvm/ADT/SmallVector.h" |
| 20 #include "llvm/ADT/Triple.h" |
| 21 #include "llvm/IR/Constant.h" |
| 22 #include "llvm/IR/DerivedTypes.h" |
| 23 #include "llvm/IR/IRBuilder.h" |
| 24 #include "llvm/IR/InlineAsm.h" |
| 25 #include "llvm/IR/Instructions.h" |
| 26 #include "llvm/IR/IntrinsicInst.h" |
| 27 #include "llvm/IR/Intrinsics.h" |
| 28 #include "llvm/IR/Module.h" |
| 29 #include "llvm/IR/NaClAtomicIntrinsics.h" |
| 30 #include "llvm/IR/Value.h" |
| 31 #include "llvm/Pass.h" |
| 32 #include "llvm/Support/Compiler.h" |
| 33 #include "llvm/Transforms/NaCl.h" |
| 34 #include "llvm/Transforms/Utils/Local.h" |
| 35 #if defined(PNACL_BROWSER_TRANSLATOR) |
| 36 #include "native_client/src/untrusted/nacl/pnacl.h" |
| 37 #endif |
| 38 |
| 39 using namespace llvm; |
| 40 |
| 41 namespace { |
| 42 class ResolvePNaClIntrinsics : public FunctionPass { |
| 43 public: |
| 44 ResolvePNaClIntrinsics() : FunctionPass(ID) { |
| 45 initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry()); |
| 46 } |
| 47 |
| 48 static char ID; |
| 49 virtual bool runOnFunction(Function &F); |
| 50 |
| 51 /// Interface specifying how intrinsic calls should be resolved. Each |
| 52 /// intrinsic call handled by the implementor will be visited by the |
| 53 /// doResolve method. |
| 54 class CallResolver { |
| 55 public: |
| 56 /// Called once per \p Call to the intrinsic in the module. |
| 57 /// Returns true if the Function was changed. |
| 58 bool resolve(IntrinsicInst *Call) { |
| 59 // To be a well-behaving FunctionPass, don't touch uses in other |
| 60 // functions. These will be handled when the pass manager gets to |
| 61 // those functions. |
| 62 if (Call->getParent()->getParent() == &F) |
| 63 return doResolve(Call); |
| 64 return false; |
| 65 } |
| 66 Function *getDeclaration() const { return doGetDeclaration(); } |
| 67 std::string getName() { return Intrinsic::getName(IntrinsicID); } |
| 68 |
| 69 protected: |
| 70 Function &F; |
| 71 Module *M; |
| 72 Intrinsic::ID IntrinsicID; |
| 73 |
| 74 CallResolver(Function &F, Intrinsic::ID IntrinsicID) |
| 75 : F(F), M(F.getParent()), IntrinsicID(IntrinsicID) {} |
| 76 virtual ~CallResolver() {} |
| 77 |
| 78 /// The following pure virtual methods must be defined by |
| 79 /// implementors, and will be called once per intrinsic call. |
| 80 /// NOTE: doGetDeclaration() should only "get" the intrinsic declaration |
| 81 /// and not *add* decls to the module. Declarations should be added |
| 82 /// up front by the AddPNaClExternalDecls module pass. |
| 83 virtual Function *doGetDeclaration() const = 0; |
| 84 /// Returns true if the Function was changed. |
| 85 virtual bool doResolve(IntrinsicInst *Call) = 0; |
| 86 |
| 87 private: |
| 88 CallResolver(const CallResolver &) LLVM_DELETED_FUNCTION; |
| 89 CallResolver &operator=(const CallResolver &) LLVM_DELETED_FUNCTION; |
| 90 }; |
| 91 |
| 92 private: |
| 93 /// Visit all calls matching the \p Resolver's declaration, and invoke |
| 94 /// the CallResolver methods on each of them. |
| 95 bool visitCalls(CallResolver &Resolver); |
| 96 }; |
| 97 |
| 98 /// Rewrite intrinsic calls to another function. |
| 99 class IntrinsicCallToFunctionCall : |
| 100 public ResolvePNaClIntrinsics::CallResolver { |
| 101 public: |
| 102 IntrinsicCallToFunctionCall(Function &F, Intrinsic::ID IntrinsicID, |
| 103 const char *TargetFunctionName) |
| 104 : CallResolver(F, IntrinsicID), |
| 105 TargetFunction(M->getFunction(TargetFunctionName)) { |
| 106 // Expect to find the target function for this intrinsic already |
| 107 // declared, even if it is never used. |
| 108 if (!TargetFunction) |
| 109 report_fatal_error(std::string( |
| 110 "Expected to find external declaration of ") + TargetFunctionName); |
| 111 } |
| 112 virtual ~IntrinsicCallToFunctionCall() {} |
| 113 |
| 114 private: |
| 115 Function *TargetFunction; |
| 116 |
| 117 virtual Function *doGetDeclaration() const { |
| 118 return Intrinsic::getDeclaration(M, IntrinsicID); |
| 119 } |
| 120 |
| 121 virtual bool doResolve(IntrinsicInst *Call) { |
| 122 Call->setCalledFunction(TargetFunction); |
| 123 if (IntrinsicID == Intrinsic::nacl_setjmp) { |
| 124 // The "returns_twice" attribute is required for correctness, |
| 125 // otherwise the backend will reuse stack slots in a way that is |
| 126 // incorrect for setjmp(). See: |
| 127 // https://code.google.com/p/nativeclient/issues/detail?id=3733 |
| 128 Call->setCanReturnTwice(); |
| 129 } |
| 130 return true; |
| 131 } |
| 132 |
| 133 IntrinsicCallToFunctionCall(const IntrinsicCallToFunctionCall &) |
| 134 LLVM_DELETED_FUNCTION; |
| 135 IntrinsicCallToFunctionCall &operator=(const IntrinsicCallToFunctionCall &) |
| 136 LLVM_DELETED_FUNCTION; |
| 137 }; |
| 138 |
| 139 /// Rewrite intrinsic calls to a constant whose value is determined by a |
| 140 /// functor. This functor is called once per Call, and returns a |
| 141 /// Constant that should replace the Call. |
| 142 template <class Callable> |
| 143 class ConstantCallResolver : public ResolvePNaClIntrinsics::CallResolver { |
| 144 public: |
| 145 ConstantCallResolver(Function &F, Intrinsic::ID IntrinsicID, |
| 146 Callable Functor) |
| 147 : CallResolver(F, IntrinsicID), Functor(Functor) {} |
| 148 virtual ~ConstantCallResolver() {} |
| 149 |
| 150 private: |
| 151 Callable Functor; |
| 152 |
| 153 virtual Function *doGetDeclaration() const { |
| 154 return Intrinsic::getDeclaration(M, IntrinsicID); |
| 155 } |
| 156 |
| 157 virtual bool doResolve(IntrinsicInst *Call) { |
| 158 Constant *C = Functor(Call); |
| 159 Call->replaceAllUsesWith(C); |
| 160 Call->eraseFromParent(); |
| 161 return true; |
| 162 } |
| 163 |
| 164 ConstantCallResolver(const ConstantCallResolver &) LLVM_DELETED_FUNCTION; |
| 165 ConstantCallResolver &operator=(const ConstantCallResolver &) |
| 166 LLVM_DELETED_FUNCTION; |
| 167 }; |
| 168 |
| 169 /// Resolve __nacl_atomic_is_lock_free to true/false at translation |
| 170 /// time. PNaCl's currently supported platforms all support lock-free |
| 171 /// atomics at byte sizes {1,2,4,8} except for MIPS arch that supports |
| 172 /// lock-free atomics at byte sizes {1,2,4}, and the alignment of the |
| 173 /// pointer is always expected to be natural (as guaranteed by C11 and |
| 174 /// C++11). PNaCl's Module-level ABI verification checks that the byte |
| 175 /// size is constant and in {1,2,4,8}. |
| 176 struct IsLockFreeToConstant { |
| 177 Constant *operator()(CallInst *Call) { |
| 178 uint64_t MaxLockFreeByteSize = 8; |
| 179 const APInt &ByteSize = |
| 180 cast<Constant>(Call->getOperand(0))->getUniqueInteger(); |
| 181 |
| 182 # if defined(PNACL_BROWSER_TRANSLATOR) |
| 183 switch (__builtin_nacl_target_arch()) { |
| 184 case PnaclTargetArchitectureX86_32: |
| 185 case PnaclTargetArchitectureX86_64: |
| 186 case PnaclTargetArchitectureARM_32: |
| 187 break; |
| 188 case PnaclTargetArchitectureMips_32: |
| 189 MaxLockFreeByteSize = 4; |
| 190 break; |
| 191 default: |
| 192 report_fatal_error("Unhandled arch from __builtin_nacl_target_arch()"); |
| 193 } |
| 194 # elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ |
| 195 defined(__pnacl__) |
| 196 // TODO(jfb): MaxLockFreeByteSize should actually be selected at runtime. |
| 197 // Continue. |
| 198 # elif defined(__mips__) |
| 199 MaxLockFreeByteSize = 4; |
| 200 # else |
| 201 # error "Unknown architecture" |
| 202 # endif |
| 203 |
| 204 bool IsLockFree = ByteSize.ule(MaxLockFreeByteSize); |
| 205 Constant *C = ConstantInt::get(Call->getType(), IsLockFree); |
| 206 return C; |
| 207 } |
| 208 }; |
| 209 |
| 210 /// Rewrite atomic intrinsics to LLVM IR instructions. |
| 211 class AtomicCallResolver : public ResolvePNaClIntrinsics::CallResolver { |
| 212 public: |
| 213 AtomicCallResolver(Function &F, |
| 214 const NaCl::AtomicIntrinsics::AtomicIntrinsic *I) |
| 215 : CallResolver(F, I->ID), I(I) {} |
| 216 virtual ~AtomicCallResolver() {} |
| 217 |
| 218 private: |
| 219 const NaCl::AtomicIntrinsics::AtomicIntrinsic *I; |
| 220 |
| 221 virtual Function *doGetDeclaration() const { return I->getDeclaration(M); } |
| 222 |
| 223 virtual bool doResolve(IntrinsicInst *Call) { |
| 224 // Assume the @llvm.nacl.atomic.* intrinsics follow the PNaCl ABI: |
| 225 // this should have been checked by the verifier. |
| 226 bool isVolatile = false; |
| 227 SynchronizationScope SS = CrossThread; |
| 228 Instruction *I; |
| 229 SmallVector<Instruction *, 16> MaybeDead; |
| 230 |
| 231 switch (Call->getIntrinsicID()) { |
| 232 default: |
| 233 llvm_unreachable("unknown atomic intrinsic"); |
| 234 case Intrinsic::nacl_atomic_load: |
| 235 I = new LoadInst(Call->getArgOperand(0), "", isVolatile, |
| 236 alignmentFromPointer(Call->getArgOperand(0)), |
| 237 thawMemoryOrder(Call->getArgOperand(1)), SS, Call); |
| 238 break; |
| 239 case Intrinsic::nacl_atomic_store: |
| 240 I = new StoreInst(Call->getArgOperand(0), Call->getArgOperand(1), |
| 241 isVolatile, |
| 242 alignmentFromPointer(Call->getArgOperand(1)), |
| 243 thawMemoryOrder(Call->getArgOperand(2)), SS, Call); |
| 244 break; |
| 245 case Intrinsic::nacl_atomic_rmw: |
| 246 I = new AtomicRMWInst(thawRMWOperation(Call->getArgOperand(0)), |
| 247 Call->getArgOperand(1), Call->getArgOperand(2), |
| 248 thawMemoryOrder(Call->getArgOperand(3)), SS, Call); |
| 249 break; |
| 250 case Intrinsic::nacl_atomic_cmpxchg: |
| 251 I = new AtomicCmpXchgInst( |
| 252 Call->getArgOperand(0), Call->getArgOperand(1), |
| 253 Call->getArgOperand(2), thawMemoryOrder(Call->getArgOperand(3)), |
| 254 thawMemoryOrder(Call->getArgOperand(4)), SS, Call); |
| 255 |
| 256 // cmpxchg returns struct { T loaded, i1 success } whereas the PNaCl |
| 257 // intrinsic only returns the loaded value. The Call can't simply be |
| 258 // replaced. Identify loaded+success structs that can be replaced by the |
| 259 // cmxpchg's returned struct. |
| 260 { |
| 261 Instruction *Loaded = nullptr; |
| 262 Instruction *Success = nullptr; |
| 263 for (User *CallUser : Call->users()) { |
| 264 if (auto ICmp = dyn_cast<ICmpInst>(CallUser)) { |
| 265 // Identify comparisons for cmpxchg's success. |
| 266 if (ICmp->getPredicate() != CmpInst::ICMP_EQ) |
| 267 continue; |
| 268 Value *LHS = ICmp->getOperand(0); |
| 269 Value *RHS = ICmp->getOperand(1); |
| 270 Value *Old = I->getOperand(1); |
| 271 if (RHS != Old && LHS != Old) // Call is either RHS or LHS. |
| 272 continue; // The comparison isn't checking for cmpxchg's success. |
| 273 |
| 274 // Recognize the pattern creating struct { T loaded, i1 success }: |
| 275 // it can be replaced by cmpxchg's result. |
| 276 for (User *InsUser : ICmp->users()) { |
| 277 if (!isa<Instruction>(InsUser) || |
| 278 cast<Instruction>(InsUser)->getParent() != Call->getParent()) |
| 279 continue; // Different basic blocks, don't be clever. |
| 280 auto Ins = dyn_cast<InsertValueInst>(InsUser); |
| 281 if (!Ins) |
| 282 continue; |
| 283 auto InsTy = dyn_cast<StructType>(Ins->getType()); |
| 284 if (!InsTy) |
| 285 continue; |
| 286 if (!InsTy->isLayoutIdentical(cast<StructType>(I->getType()))) |
| 287 continue; // Not a struct { T loaded, i1 success }. |
| 288 if (Ins->getNumIndices() != 1 || Ins->getIndices()[0] != 1) |
| 289 continue; // Not an insert { T, i1 } %something, %success, 1. |
| 290 auto TIns = dyn_cast<InsertValueInst>(Ins->getAggregateOperand()); |
| 291 if (!TIns) |
| 292 continue; // T wasn't inserted into the struct, don't be clever. |
| 293 if (!isa<UndefValue>(TIns->getAggregateOperand())) |
| 294 continue; // Not an insert into an undef value, don't be clever. |
| 295 if (TIns->getInsertedValueOperand() != Call) |
| 296 continue; // Not inserting the loaded value. |
| 297 if (TIns->getNumIndices() != 1 || TIns->getIndices()[0] != 0) |
| 298 continue; // Not an insert { T, i1 } undef, %loaded, 0. |
| 299 // Hooray! This is the struct you're looking for. |
| 300 |
| 301 // Keep track of values extracted from the struct, instead of |
| 302 // recreating them. |
| 303 for (User *StructUser : Ins->users()) { |
| 304 if (auto Extract = dyn_cast<ExtractValueInst>(StructUser)) { |
| 305 MaybeDead.push_back(Extract); |
| 306 if (!Loaded && Extract->getIndices()[0] == 0) { |
| 307 Loaded = cast<Instruction>(StructUser); |
| 308 Loaded->moveBefore(Call); |
| 309 } else if (!Success && Extract->getIndices()[0] == 1) { |
| 310 Success = cast<Instruction>(StructUser); |
| 311 Success->moveBefore(Call); |
| 312 } |
| 313 } |
| 314 } |
| 315 |
| 316 MaybeDead.push_back(Ins); |
| 317 MaybeDead.push_back(TIns); |
| 318 Ins->replaceAllUsesWith(I); |
| 319 } |
| 320 |
| 321 MaybeDead.push_back(ICmp); |
| 322 if (!Success) |
| 323 Success = ExtractValueInst::Create(I, 1, "success", Call); |
| 324 ICmp->replaceAllUsesWith(Success); |
| 325 } |
| 326 } |
| 327 |
| 328 // Clean up remaining uses of the loaded value, if any. Later code will |
| 329 // try to replace Call with I, make sure the types match. |
| 330 if (Call->hasNUsesOrMore(1)) { |
| 331 if (!Loaded) |
| 332 Loaded = ExtractValueInst::Create(I, 0, "loaded", Call); |
| 333 I = Loaded; |
| 334 } else { |
| 335 I = nullptr; |
| 336 } |
| 337 |
| 338 if (Loaded) |
| 339 MaybeDead.push_back(Loaded); |
| 340 if (Success) |
| 341 MaybeDead.push_back(Success); |
| 342 } |
| 343 break; |
| 344 case Intrinsic::nacl_atomic_fence: |
| 345 I = new FenceInst(M->getContext(), |
| 346 thawMemoryOrder(Call->getArgOperand(0)), SS, Call); |
| 347 break; |
| 348 case Intrinsic::nacl_atomic_fence_all: { |
| 349 FunctionType *FTy = |
| 350 FunctionType::get(Type::getVoidTy(M->getContext()), false); |
| 351 std::string AsmString; // Empty. |
| 352 std::string Constraints("~{memory}"); |
| 353 bool HasSideEffect = true; |
| 354 CallInst *Asm = CallInst::Create( |
| 355 InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect), "", Call); |
| 356 Asm->setDebugLoc(Call->getDebugLoc()); |
| 357 I = new FenceInst(M->getContext(), SequentiallyConsistent, SS, Asm); |
| 358 Asm = CallInst::Create( |
| 359 InlineAsm::get(FTy, AsmString, Constraints, HasSideEffect), "", I); |
| 360 Asm->setDebugLoc(Call->getDebugLoc()); |
| 361 } break; |
| 362 } |
| 363 |
| 364 if (I) { |
| 365 I->setName(Call->getName()); |
| 366 I->setDebugLoc(Call->getDebugLoc()); |
| 367 Call->replaceAllUsesWith(I); |
| 368 } |
| 369 Call->eraseFromParent(); |
| 370 |
| 371 // Remove dead code. |
| 372 for (auto Kill : MaybeDead) |
| 373 if (isInstructionTriviallyDead(Kill)) |
| 374 Kill->eraseFromParent(); |
| 375 |
| 376 return true; |
| 377 } |
| 378 |
| 379 unsigned alignmentFromPointer(const Value *Ptr) const { |
| 380 const PointerType *PtrType = cast<PointerType>(Ptr->getType()); |
| 381 unsigned BitWidth = PtrType->getElementType()->getIntegerBitWidth(); |
| 382 return BitWidth / 8; |
| 383 } |
| 384 |
| 385 AtomicOrdering thawMemoryOrder(const Value *MemoryOrder) const { |
| 386 NaCl::MemoryOrder MO = (NaCl::MemoryOrder) |
| 387 cast<Constant>(MemoryOrder)->getUniqueInteger().getLimitedValue(); |
| 388 switch (MO) { |
| 389 // Only valid values should pass validation. |
| 390 default: llvm_unreachable("unknown memory order"); |
| 391 case NaCl::MemoryOrderRelaxed: return Monotonic; |
| 392 // TODO Consume is unspecified by LLVM's internal IR. |
| 393 case NaCl::MemoryOrderConsume: return SequentiallyConsistent; |
| 394 case NaCl::MemoryOrderAcquire: return Acquire; |
| 395 case NaCl::MemoryOrderRelease: return Release; |
| 396 case NaCl::MemoryOrderAcquireRelease: return AcquireRelease; |
| 397 case NaCl::MemoryOrderSequentiallyConsistent: return SequentiallyConsistent; |
| 398 } |
| 399 } |
| 400 |
| 401 AtomicRMWInst::BinOp thawRMWOperation(const Value *Operation) const { |
| 402 NaCl::AtomicRMWOperation Op = (NaCl::AtomicRMWOperation) |
| 403 cast<Constant>(Operation)->getUniqueInteger().getLimitedValue(); |
| 404 switch (Op) { |
| 405 // Only valid values should pass validation. |
| 406 default: llvm_unreachable("unknown atomic RMW operation"); |
| 407 case NaCl::AtomicAdd: return AtomicRMWInst::Add; |
| 408 case NaCl::AtomicSub: return AtomicRMWInst::Sub; |
| 409 case NaCl::AtomicOr: return AtomicRMWInst::Or; |
| 410 case NaCl::AtomicAnd: return AtomicRMWInst::And; |
| 411 case NaCl::AtomicXor: return AtomicRMWInst::Xor; |
| 412 case NaCl::AtomicExchange: return AtomicRMWInst::Xchg; |
| 413 } |
| 414 } |
| 415 |
| 416 AtomicCallResolver(const AtomicCallResolver &); |
| 417 AtomicCallResolver &operator=(const AtomicCallResolver &); |
| 418 }; |
| 419 } |
| 420 |
| 421 bool ResolvePNaClIntrinsics::visitCalls( |
| 422 ResolvePNaClIntrinsics::CallResolver &Resolver) { |
| 423 bool Changed = false; |
| 424 Function *IntrinsicFunction = Resolver.getDeclaration(); |
| 425 if (!IntrinsicFunction) |
| 426 return false; |
| 427 |
| 428 SmallVector<IntrinsicInst *, 64> Calls; |
| 429 for (User *U : IntrinsicFunction->users()) { |
| 430 // At this point, the only uses of the intrinsic can be calls, since we |
| 431 // assume this pass runs on bitcode that passed ABI verification. |
| 432 IntrinsicInst *Call = dyn_cast<IntrinsicInst>(U); |
| 433 if (!Call) |
| 434 report_fatal_error("Expected use of intrinsic to be a call: " + |
| 435 Resolver.getName()); |
| 436 Calls.push_back(Call); |
| 437 } |
| 438 |
| 439 for (auto Call : Calls) |
| 440 Changed |= Resolver.resolve(Call); |
| 441 |
| 442 return Changed; |
| 443 } |
| 444 |
| 445 bool ResolvePNaClIntrinsics::runOnFunction(Function &F) { |
| 446 LLVMContext &C = F.getParent()->getContext(); |
| 447 bool Changed = false; |
| 448 |
| 449 IntrinsicCallToFunctionCall SetJmpResolver(F, Intrinsic::nacl_setjmp, |
| 450 "setjmp"); |
| 451 IntrinsicCallToFunctionCall LongJmpResolver(F, Intrinsic::nacl_longjmp, |
| 452 "longjmp"); |
| 453 Changed |= visitCalls(SetJmpResolver); |
| 454 Changed |= visitCalls(LongJmpResolver); |
| 455 |
| 456 NaCl::AtomicIntrinsics AI(C); |
| 457 NaCl::AtomicIntrinsics::View V = AI.allIntrinsicsAndOverloads(); |
| 458 for (NaCl::AtomicIntrinsics::View::iterator I = V.begin(), E = V.end(); |
| 459 I != E; ++I) { |
| 460 AtomicCallResolver AtomicResolver(F, I); |
| 461 Changed |= visitCalls(AtomicResolver); |
| 462 } |
| 463 |
| 464 ConstantCallResolver<IsLockFreeToConstant> IsLockFreeResolver( |
| 465 F, Intrinsic::nacl_atomic_is_lock_free, IsLockFreeToConstant()); |
| 466 Changed |= visitCalls(IsLockFreeResolver); |
| 467 |
| 468 return Changed; |
| 469 } |
| 470 |
| 471 char ResolvePNaClIntrinsics::ID = 0; |
| 472 INITIALIZE_PASS(ResolvePNaClIntrinsics, "resolve-pnacl-intrinsics", |
| 473 "Resolve PNaCl intrinsic calls", false, false) |
| 474 |
| 475 FunctionPass *llvm::createResolvePNaClIntrinsicsPass() { |
| 476 return new ResolvePNaClIntrinsics(); |
| 477 } |
OLD | NEW |