| Index: lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| diff --git a/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp b/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| index e4efeb67c3c3c24c5ca1103f960338dea0c134f6..6b278a2ebba663a753f9f16dede19c0cf2a83215 100644
|
| --- a/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| +++ b/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| @@ -17,10 +17,14 @@
|
| //===----------------------------------------------------------------------===//
|
|
|
| #include "llvm/ADT/SmallVector.h"
|
| +#include "llvm/IR/Constant.h"
|
| #include "llvm/IR/Instructions.h"
|
| #include "llvm/IR/Intrinsics.h"
|
| #include "llvm/IR/Module.h"
|
| +#include "llvm/IR/NaCl.h"
|
| +#include "llvm/IR/Value.h"
|
| #include "llvm/Pass.h"
|
| +#include "llvm/Support/MathExtras.h"
|
| #include "llvm/Transforms/NaCl.h"
|
|
|
| using namespace llvm;
|
| @@ -37,14 +41,16 @@ namespace {
|
| private:
|
| // Some intrinsic calls are resolved simply by replacing the call with a
|
| // call to an alternative function with exactly the same type.
|
| - bool resolveSimpleCall(Function &F, Intrinsic::ID IntrinsicID,
|
| - const char *TargetFunctionName);
|
| + bool resolveSimpleCall(Function &F, Intrinsic::ID IntrinsicID);
|
| + // All atomic calls are rewritten to a single instruction, and the
|
| + // call is removed.
|
| + void rewriteAtomicCall(Module *M, Function &F, Intrinsic::ID IntrinsicID,
|
| + CallInst *Call);
|
| };
|
| }
|
|
|
| bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F,
|
| - Intrinsic::ID IntrinsicID,
|
| - const char *TargetFunctionName) {
|
| + Intrinsic::ID IntrinsicID) {
|
| Module *M = F.getParent();
|
| bool Changed = false;
|
| Function *IntrinsicFunction = Intrinsic::getDeclaration(M, IntrinsicID);
|
| @@ -53,12 +59,18 @@ bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F,
|
| return false;
|
| }
|
|
|
| - // Expect to find the target function for this intrinsic already declared
|
| - Function *TargetFunction = M->getFunction(TargetFunctionName);
|
| - if (!TargetFunction) {
|
| - report_fatal_error(
|
| - std::string("Expected to find external declaration of ") +
|
| - TargetFunctionName);
|
| + Function *TargetFunction = NULL;
|
| + if (IntrinsicID == Intrinsic::nacl_setjmp ||
|
| + IntrinsicID == Intrinsic::nacl_longjmp) {
|
| + // Expect to find the target function for this intrinsic already declared.
|
| + const char *TargetFunctionName = IntrinsicID == Intrinsic::nacl_setjmp
|
| + ? "setjmp" : "longjmp";
|
| + TargetFunction = M->getFunction(TargetFunctionName);
|
| + if (!TargetFunction) {
|
| + report_fatal_error(
|
| + std::string("Expected to find external declaration of ") +
|
| + TargetFunctionName);
|
| + }
|
| }
|
|
|
| for (Value::use_iterator UI = IntrinsicFunction->use_begin(),
|
| @@ -77,7 +89,11 @@ bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F,
|
| // functions. These will be handled when the pass manager gets to those
|
| // functions.
|
| if (Call->getParent()->getParent() == &F) {
|
| - Call->setCalledFunction(TargetFunction);
|
| + if (IntrinsicID == Intrinsic::nacl_setjmp ||
|
| + IntrinsicID == Intrinsic::nacl_longjmp)
|
| + Call->setCalledFunction(TargetFunction);
|
| + else
|
| + rewriteAtomicCall(M, F, IntrinsicID, Call);
|
| Changed = true;
|
| }
|
| }
|
| @@ -85,9 +101,97 @@ bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F,
|
| return Changed;
|
| }
|
|
|
| +void ResolvePNaClIntrinsics::rewriteAtomicCall(Module *M, Function &F,
|
| + Intrinsic::ID IntrinsicID,
|
| + CallInst *Call) {
|
| + // Assume the @nacl.atomic.<size> intrinsics follow the PNaCl
|
| + // ABI: this should have been checked by the verifier.
|
| + uint64_t Op(cast<Constant>(
|
| + Call->getArgOperand(0))->getUniqueInteger().getLimitedValue());
|
| + Value *Ptr = Call->getArgOperand(1);
|
| + Value *Val = Call->getArgOperand(2);
|
| + Value *Old = Call->getArgOperand(3);
|
| + uint64_t MemoryOrder(cast<Constant>(
|
| + Call->getArgOperand(4))->getUniqueInteger().getLimitedValue());
|
| +
|
| + AtomicRMWInst::BinOp RMWOp;
|
| + switch (Op) {
|
| + default: RMWOp = AtomicRMWInst::BAD_BINOP; break;
|
| + case NaCl::AtomicAdd: RMWOp = AtomicRMWInst::Add; break;
|
| + case NaCl::AtomicSub: RMWOp = AtomicRMWInst::Sub; break;
|
| + case NaCl::AtomicOr: RMWOp = AtomicRMWInst::Or; break;
|
| + case NaCl::AtomicAnd: RMWOp = AtomicRMWInst::And; break;
|
| + case NaCl::AtomicXor: RMWOp = AtomicRMWInst::Xor; break;
|
| + case NaCl::AtomicXchg: RMWOp = AtomicRMWInst::Xchg; break;
|
| + }
|
| + AtomicOrdering Order;
|
| + switch (MemoryOrder) {
|
| + default:
|
| + // Conservatively use the strongest ordering.
|
| + Order = SequentiallyConsistent; break;
|
| + case NaCl::MemoryOrderRelaxed: Order = Unordered; break;
|
| + case NaCl::MemoryOrderConsume:
|
| + // TODO Unspecified by LLVM's internal IR.
|
| + Order = SequentiallyConsistent; break;
|
| + case NaCl::MemoryOrderAcquire: Order = Acquire; break;
|
| + case NaCl::MemoryOrderRelease: Order = Release; break;
|
| + case NaCl::MemoryOrderAcquireRelease: Order = AcquireRelease; break;
|
| + case NaCl::MemoryOrderSequentiallyConsistent:
|
| + Order = SequentiallyConsistent; break;
|
| + }
|
| + const Twine Name("");
|
| + bool isVolatile = false;
|
| + SynchronizationScope SS = CrossThread;
|
| + bool hasUses;
|
| + Instruction *I;
|
| +
|
| + for (size_t II = 0; II != NaCl::NumAtomicIntrinsics; ++II) {
|
| + if (IntrinsicID != NaCl::AtomicIntrinsics[II].ID)
|
| + continue;
|
| +
|
| + unsigned BitSize = NaCl::AtomicIntrinsics[II].BitSize;
|
| + unsigned Align = 1 << (CountTrailingZeros_32(BitSize) - 3);
|
| + switch (Op) {
|
| + default: llvm_unreachable("invalid atomic operation");
|
| + case NaCl::AtomicLoad:
|
| + hasUses = true;
|
| + I = new LoadInst(Ptr, Name, isVolatile, Align, Order, SS, Call);
|
| + break;
|
| + case NaCl::AtomicStore:
|
| + hasUses = false;
|
| + I = new StoreInst(Val, Ptr, isVolatile, Align, Order, SS, Call);
|
| + break;
|
| + case NaCl::AtomicAdd:
|
| + case NaCl::AtomicSub:
|
| + case NaCl::AtomicOr:
|
| + case NaCl::AtomicAnd:
|
| + case NaCl::AtomicXor:
|
| + case NaCl::AtomicXchg:
|
| + hasUses = true;
|
| + I = new AtomicRMWInst(RMWOp, Ptr, Val, Order, SS, Call);
|
| + break;
|
| + case NaCl::AtomicCmpXchg:
|
| + hasUses = true;
|
| + I = new AtomicCmpXchgInst(Ptr, Old, Val, Order, SS, Call);
|
| + break;
|
| + case NaCl::AtomicFence:
|
| + hasUses = false;
|
| + I = new FenceInst(M->getContext(), Order, SS, Call);
|
| + break;
|
| + }
|
| + I->setDebugLoc(Call->getDebugLoc());
|
| + if (hasUses)
|
| + Call->replaceAllUsesWith(I);
|
| + Call->eraseFromParent();
|
| + }
|
| +}
|
| +
|
| bool ResolvePNaClIntrinsics::runOnFunction(Function &F) {
|
| - bool Changed = resolveSimpleCall(F, Intrinsic::nacl_setjmp, "setjmp");
|
| - Changed |= resolveSimpleCall(F, Intrinsic::nacl_longjmp, "longjmp");
|
| + bool Changed = resolveSimpleCall(F, Intrinsic::nacl_setjmp);
|
| + Changed |= resolveSimpleCall(F, Intrinsic::nacl_longjmp);
|
| + for (size_t II = 0; II != NaCl::NumAtomicIntrinsics; ++II) {
|
| + Changed |= resolveSimpleCall(F, NaCl::AtomicIntrinsics[II].ID);
|
| + }
|
| return Changed;
|
| }
|
|
|
|
|