| Index: lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| diff --git a/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp b/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| index e4efeb67c3c3c24c5ca1103f960338dea0c134f6..8504a81f7e1a17fdf4afbdbab7b9952fa0b69149 100644
|
| --- a/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| +++ b/lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp
|
| @@ -17,77 +17,233 @@
|
| //===----------------------------------------------------------------------===//
|
|
|
| #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/NaClIntrinsics.h"
|
| +#include "llvm/IR/Value.h"
|
| #include "llvm/Pass.h"
|
| +#include "llvm/Support/MathExtras.h"
|
| #include "llvm/Transforms/NaCl.h"
|
|
|
| using namespace llvm;
|
|
|
| namespace {
|
| - class ResolvePNaClIntrinsics : public FunctionPass {
|
| +class ResolvePNaClIntrinsics : public FunctionPass {
|
| +public:
|
| + ResolvePNaClIntrinsics() : FunctionPass(ID) {
|
| + initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry());
|
| + }
|
| +
|
| + static char ID;
|
| + virtual bool runOnFunction(Function &F);
|
| +
|
| + // Interface specifying how calls should be resolved.
|
| + class CallResolver {
|
| public:
|
| - ResolvePNaClIntrinsics() : FunctionPass(ID) {
|
| - initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry());
|
| + // Called once the intrinsic's declaration is found in the module.
|
| + void Init() { DoInit(); }
|
| + // Called once per call to the intrinsic in the module.
|
| + bool Resolve(CallInst *Call) {
|
| + // To be a well-behaving FunctionPass, don't touch uses in other
|
| + // functions. These will be handled when the pass manager gets
|
| + // to those functions.
|
| + if (Call->getParent()->getParent() == &F)
|
| + return DoResolve(Call);
|
| + return false;
|
| }
|
| + Function *getDeclaration() const {
|
| + return Intrinsic::getDeclaration(M, IntrinsicID);
|
| + }
|
| + std::string getName() { return Intrinsic::getName(IntrinsicID); }
|
| +
|
| + protected:
|
| + Function &F;
|
| + Module *M;
|
| + Intrinsic::ID IntrinsicID;
|
| +
|
| + CallResolver(Function &F, Intrinsic::ID IntrinsicID)
|
| + : F(F), M(F.getParent()), IntrinsicID(IntrinsicID) {}
|
| + virtual ~CallResolver() {}
|
| + virtual void DoInit() = 0;
|
| + virtual bool DoResolve(CallInst *Call) = 0;
|
|
|
| - static char ID;
|
| - virtual bool runOnFunction(Function &F);
|
| 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);
|
| + CallResolver(const CallResolver &);
|
| + CallResolver &operator=(const CallResolver &);
|
| };
|
| -}
|
|
|
| -bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F,
|
| - Intrinsic::ID IntrinsicID,
|
| - const char *TargetFunctionName) {
|
| - Module *M = F.getParent();
|
| - bool Changed = false;
|
| - Function *IntrinsicFunction = Intrinsic::getDeclaration(M, IntrinsicID);
|
| +private:
|
| + // Walk calls matching the resolver's declaration, and invoke the
|
| + // CallResolver methods on each of them.
|
| + bool walkCalls(CallResolver &Resolver);
|
| +};
|
|
|
| - if (!IntrinsicFunction) {
|
| - return false;
|
| +// Rewrite intrinsic calls to another function.
|
| +class SimpleCallResolver : public ResolvePNaClIntrinsics::CallResolver {
|
| +public:
|
| + SimpleCallResolver(Function &F, Intrinsic::ID IntrinsicID,
|
| + const char *TargetFunctionName)
|
| + : CallResolver(F, IntrinsicID), TargetFunctionName(TargetFunctionName),
|
| + TargetFunction(NULL) {}
|
| + virtual ~SimpleCallResolver() {}
|
| +
|
| +private:
|
| + const char *TargetFunctionName;
|
| + Function *TargetFunction;
|
| +
|
| + virtual void DoInit() {
|
| + // Expect to find the target function for this intrinsic already declared.
|
| + TargetFunction = M->getFunction(TargetFunctionName);
|
| + if (!TargetFunction)
|
| + report_fatal_error(std::string(
|
| + "Expected to find external declaration of ") + TargetFunctionName);
|
| }
|
| + virtual bool DoResolve(CallInst *Call) {
|
| + Call->setCalledFunction(TargetFunction);
|
| + return true;
|
| + }
|
| +
|
| + SimpleCallResolver(const SimpleCallResolver &);
|
| + SimpleCallResolver &operator=(const SimpleCallResolver &);
|
| +};
|
| +
|
| +// Rewrite atomic intrinsics to LLVM IR instructions.
|
| +class AtomicCallResolver : public ResolvePNaClIntrinsics::CallResolver {
|
| +public:
|
| + AtomicCallResolver(Function &F, Intrinsic::ID IntrinsicID)
|
| + : CallResolver(F, IntrinsicID) {}
|
| + virtual ~AtomicCallResolver() {}
|
| +
|
| +private:
|
| + virtual void DoInit() {}
|
| + virtual bool DoResolve(CallInst *Call) {
|
| + // Assume the @llvm.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) {
|
| + // Conservatively use the strongest ordering.
|
| + default: Order = SequentiallyConsistent; break;
|
| + case NaCl::MemoryOrderRelaxed: Order = Unordered; break;
|
| + // TODO Consume is unspecified by LLVM's internal IR.
|
| + case NaCl::MemoryOrderConsume: 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;
|
|
|
| - // 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);
|
| + 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();
|
| + }
|
| + return true;
|
| }
|
|
|
| + AtomicCallResolver(const AtomicCallResolver &);
|
| + AtomicCallResolver &operator=(const AtomicCallResolver &);
|
| +};
|
| +}
|
| +
|
| +bool ResolvePNaClIntrinsics::walkCalls(
|
| + ResolvePNaClIntrinsics::CallResolver &Resolver) {
|
| + bool Changed = false;
|
| + Function *IntrinsicFunction = Resolver.getDeclaration();
|
| + if (!IntrinsicFunction)
|
| + return false;
|
| +
|
| + Resolver.Init();
|
| +
|
| for (Value::use_iterator UI = IntrinsicFunction->use_begin(),
|
| - UE = IntrinsicFunction->use_end(); UI != UE;) {
|
| + UE = IntrinsicFunction->use_end();
|
| + UI != UE;) {
|
| // At this point, the only uses of the intrinsic can be calls, since
|
| // we assume this pass runs on bitcode that passed ABI verification.
|
| CallInst *Call = dyn_cast<CallInst>(*UI++);
|
| + if (!Call)
|
| + report_fatal_error("Expected use of intrinsic to be a call: " +
|
| + Resolver.getName());
|
|
|
| - if (!Call) {
|
| - report_fatal_error(
|
| - std::string("Expected use of intrinsic to be a call: ") +
|
| - Intrinsic::getName(IntrinsicID));
|
| - }
|
| -
|
| - // To be a well-behaving FunctionPass, don't touch uses in other
|
| - // functions. These will be handled when the pass manager gets to those
|
| - // functions.
|
| - if (Call->getParent()->getParent() == &F) {
|
| - Call->setCalledFunction(TargetFunction);
|
| - Changed = true;
|
| - }
|
| + Changed |= Resolver.Resolve(Call);
|
| }
|
|
|
| return Changed;
|
| }
|
|
|
| bool ResolvePNaClIntrinsics::runOnFunction(Function &F) {
|
| - bool Changed = resolveSimpleCall(F, Intrinsic::nacl_setjmp, "setjmp");
|
| - Changed |= resolveSimpleCall(F, Intrinsic::nacl_longjmp, "longjmp");
|
| + bool Changed = false;
|
| +
|
| + SimpleCallResolver SetJmpResolver(F, Intrinsic::nacl_setjmp, "setjmp");
|
| + SimpleCallResolver LongJmpResolver(F, Intrinsic::nacl_longjmp, "longjmp");
|
| + Changed |= walkCalls(SetJmpResolver);
|
| + Changed |= walkCalls(LongJmpResolver);
|
| +
|
| + for (size_t II = 0; II != NaCl::NumAtomicIntrinsics; ++II) {
|
| + AtomicCallResolver AtomicResolver(F, NaCl::AtomicIntrinsics[II].ID);
|
| + Changed |= walkCalls(AtomicResolver);
|
| + }
|
| +
|
| return Changed;
|
| }
|
|
|
|
|