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 pass. |
14 // They are separate because one is a ModulePass and the other is a | 14 // They are separate because one is a ModulePass and the other is a |
15 // FunctionPass. | 15 // 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" |
21 #include "llvm/IR/Intrinsics.h" | 22 #include "llvm/IR/Intrinsics.h" |
22 #include "llvm/IR/Module.h" | 23 #include "llvm/IR/Module.h" |
24 #include "llvm/IR/NaCl.h" | |
25 #include "llvm/IR/Value.h" | |
23 #include "llvm/Pass.h" | 26 #include "llvm/Pass.h" |
27 #include "llvm/Support/MathExtras.h" | |
24 #include "llvm/Transforms/NaCl.h" | 28 #include "llvm/Transforms/NaCl.h" |
25 | 29 |
26 using namespace llvm; | 30 using namespace llvm; |
27 | 31 |
28 namespace { | 32 namespace { |
29 class ResolvePNaClIntrinsics : public FunctionPass { | 33 class ResolvePNaClIntrinsics : public FunctionPass { |
30 public: | 34 public: |
31 ResolvePNaClIntrinsics() : FunctionPass(ID) { | 35 ResolvePNaClIntrinsics() : FunctionPass(ID) { |
32 initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry()); | 36 initializeResolvePNaClIntrinsicsPass(*PassRegistry::getPassRegistry()); |
33 } | 37 } |
34 | 38 |
35 static char ID; | 39 static char ID; |
36 virtual bool runOnFunction(Function &F); | 40 virtual bool runOnFunction(Function &F); |
37 private: | 41 private: |
38 // Some intrinsic calls are resolved simply by replacing the call with a | 42 // Some intrinsic calls are resolved simply by replacing the call with a |
39 // call to an alternative function with exactly the same type. | 43 // call to an alternative function with exactly the same type. |
40 bool resolveSimpleCall(Function &F, Intrinsic::ID IntrinsicID, | 44 bool resolveSimpleCall(Function &F, Intrinsic::ID IntrinsicID); |
41 const char *TargetFunctionName); | 45 // All atomic calls are rewritten to a single instruction, and the |
46 // call is removed. | |
47 void rewriteAtomicCall(Module *M, Function &F, Intrinsic::ID IntrinsicID, | |
48 CallInst *Call); | |
42 }; | 49 }; |
43 } | 50 } |
44 | 51 |
45 bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F, | 52 bool ResolvePNaClIntrinsics::resolveSimpleCall(Function &F, |
46 Intrinsic::ID IntrinsicID, | 53 Intrinsic::ID IntrinsicID) { |
47 const char *TargetFunctionName) { | |
48 Module *M = F.getParent(); | 54 Module *M = F.getParent(); |
49 bool Changed = false; | 55 bool Changed = false; |
50 Function *IntrinsicFunction = Intrinsic::getDeclaration(M, IntrinsicID); | 56 Function *IntrinsicFunction = Intrinsic::getDeclaration(M, IntrinsicID); |
51 | 57 |
52 if (!IntrinsicFunction) { | 58 if (!IntrinsicFunction) { |
53 return false; | 59 return false; |
54 } | 60 } |
55 | 61 |
56 // Expect to find the target function for this intrinsic already declared | 62 Function *TargetFunction = NULL; |
57 Function *TargetFunction = M->getFunction(TargetFunctionName); | 63 if (IntrinsicID == Intrinsic::nacl_setjmp || |
eliben
2013/06/26 16:20:57
No no no no no :-)
resolveSimpleCall's comment is
JF
2013/06/26 22:23:12
Done: rewrote to use one class per type of rewrite
| |
58 if (!TargetFunction) { | 64 IntrinsicID == Intrinsic::nacl_longjmp) { |
59 report_fatal_error( | 65 // Expect to find the target function for this intrinsic already declared. |
60 std::string("Expected to find external declaration of ") + | 66 const char *TargetFunctionName = IntrinsicID == Intrinsic::nacl_setjmp |
61 TargetFunctionName); | 67 ? "setjmp" : "longjmp"; |
68 TargetFunction = M->getFunction(TargetFunctionName); | |
69 if (!TargetFunction) { | |
70 report_fatal_error( | |
71 std::string("Expected to find external declaration of ") + | |
72 TargetFunctionName); | |
73 } | |
62 } | 74 } |
63 | 75 |
64 for (Value::use_iterator UI = IntrinsicFunction->use_begin(), | 76 for (Value::use_iterator UI = IntrinsicFunction->use_begin(), |
65 UE = IntrinsicFunction->use_end(); UI != UE;) { | 77 UE = IntrinsicFunction->use_end(); UI != UE;) { |
66 // At this point, the only uses of the intrinsic can be calls, since | 78 // 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. | 79 // we assume this pass runs on bitcode that passed ABI verification. |
68 CallInst *Call = dyn_cast<CallInst>(*UI++); | 80 CallInst *Call = dyn_cast<CallInst>(*UI++); |
69 | 81 |
70 if (!Call) { | 82 if (!Call) { |
71 report_fatal_error( | 83 report_fatal_error( |
72 std::string("Expected use of intrinsic to be a call: ") + | 84 std::string("Expected use of intrinsic to be a call: ") + |
73 Intrinsic::getName(IntrinsicID)); | 85 Intrinsic::getName(IntrinsicID)); |
74 } | 86 } |
75 | 87 |
76 // To be a well-behaving FunctionPass, don't touch uses in other | 88 // 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 | 89 // functions. These will be handled when the pass manager gets to those |
78 // functions. | 90 // functions. |
79 if (Call->getParent()->getParent() == &F) { | 91 if (Call->getParent()->getParent() == &F) { |
80 Call->setCalledFunction(TargetFunction); | 92 if (IntrinsicID == Intrinsic::nacl_setjmp || |
93 IntrinsicID == Intrinsic::nacl_longjmp) | |
94 Call->setCalledFunction(TargetFunction); | |
95 else | |
96 rewriteAtomicCall(M, F, IntrinsicID, Call); | |
81 Changed = true; | 97 Changed = true; |
82 } | 98 } |
83 } | 99 } |
84 | 100 |
85 return Changed; | 101 return Changed; |
86 } | 102 } |
87 | 103 |
104 void ResolvePNaClIntrinsics::rewriteAtomicCall(Module *M, Function &F, | |
105 Intrinsic::ID IntrinsicID, | |
106 CallInst *Call) { | |
107 // Assume the @nacl.atomic.<size> intrinsics follow the PNaCl | |
108 // ABI: this should have been checked by the verifier. | |
109 uint64_t Op(cast<Constant>( | |
110 Call->getArgOperand(0))->getUniqueInteger().getLimitedValue()); | |
111 Value *Ptr(Call->getArgOperand(1)); | |
Mark Seaborn
2013/06/26 14:33:41
Just write "Ptr = Call->getArgOperand(1)"? Using
JF
2013/06/26 15:52:29
Done.
| |
112 Value *Val(Call->getArgOperand(2)); | |
113 Value *Old(Call->getArgOperand(3)); | |
114 uint64_t MemoryOrder(cast<Constant>( | |
115 Call->getArgOperand(4))->getUniqueInteger().getLimitedValue()); | |
116 | |
117 AtomicRMWInst::BinOp RMWOp; | |
118 switch (Op) { | |
119 default: RMWOp = AtomicRMWInst::BAD_BINOP; break; | |
120 case NaCl::AtomicAdd: RMWOp = AtomicRMWInst::Add; break; | |
121 case NaCl::AtomicSub: RMWOp = AtomicRMWInst::Sub; break; | |
122 case NaCl::AtomicOr: RMWOp = AtomicRMWInst::Or; break; | |
123 case NaCl::AtomicAnd: RMWOp = AtomicRMWInst::And; break; | |
124 case NaCl::AtomicXor: RMWOp = AtomicRMWInst::Xor; break; | |
125 case NaCl::AtomicXchg: RMWOp = AtomicRMWInst::Xchg; break; | |
126 } | |
127 AtomicOrdering Order; | |
128 switch (MemoryOrder) { | |
129 default: | |
130 // Conservatively use the strongest ordering. | |
131 Order = SequentiallyConsistent; break; | |
132 case NaCl::MemoryOrderRelaxed: Order = Unordered; break; | |
133 case NaCl::MemoryOrderConsume: | |
134 // TODO Unspecified by LLVM's internal IR. | |
135 Order = SequentiallyConsistent; break; | |
136 case NaCl::MemoryOrderAcquire: Order = Acquire; break; | |
137 case NaCl::MemoryOrderRelease: Order = Release; break; | |
138 case NaCl::MemoryOrderAcquireRelease: Order = AcquireRelease; break; | |
139 case NaCl::MemoryOrderSequentiallyConsistent: | |
140 Order = SequentiallyConsistent; break; | |
141 } | |
142 const Twine Name(""); | |
143 bool isVolatile = false; | |
144 SynchronizationScope SS = CrossThread; | |
145 bool hasUses; | |
146 Instruction *I; | |
147 | |
148 for (size_t II = 0; II != NaCl::NumAtomicIntrinsics; ++II) { | |
149 if (IntrinsicID != NaCl::AtomicIntrinsics[II].ID) | |
150 continue; | |
151 | |
152 unsigned BitSize = NaCl::AtomicIntrinsics[II].BitSize; | |
153 unsigned Align = 1 << (CountTrailingZeros_32(BitSize) - 3); | |
154 switch (Op) { | |
155 default: llvm_unreachable("invalid atomic operation"); | |
156 case NaCl::AtomicLoad: | |
157 hasUses = true; | |
158 I = new LoadInst(Ptr, Name, isVolatile, Align, Order, SS, Call); | |
159 break; | |
160 case NaCl::AtomicStore: | |
161 hasUses = false; | |
162 I = new StoreInst(Val, Ptr, isVolatile, Align, Order, SS, Call); | |
163 break; | |
164 case NaCl::AtomicAdd: | |
165 case NaCl::AtomicSub: | |
166 case NaCl::AtomicOr: | |
167 case NaCl::AtomicAnd: | |
168 case NaCl::AtomicXor: | |
169 case NaCl::AtomicXchg: | |
170 hasUses = true; | |
171 I = new AtomicRMWInst(RMWOp, Ptr, Val, Order, SS, Call); | |
172 break; | |
173 case NaCl::AtomicCmpXchg: | |
174 hasUses = true; | |
175 I = new AtomicCmpXchgInst(Ptr, Old, Val, Order, SS, Call); | |
176 break; | |
177 case NaCl::AtomicFence: | |
178 hasUses = false; | |
179 I = new FenceInst(M->getContext(), Order, SS, Call); | |
180 break; | |
181 } | |
182 I->setDebugLoc(Call->getDebugLoc()); | |
183 if (hasUses) | |
Mark Seaborn
2013/06/26 14:33:41
I think you can do the RAUW unconditionally
JF
2013/06/26 15:52:29
No:
assert(New->getType() == getType() &&
| |
184 Call->replaceAllUsesWith(I); | |
185 Call->eraseFromParent(); | |
186 } | |
187 } | |
188 | |
88 bool ResolvePNaClIntrinsics::runOnFunction(Function &F) { | 189 bool ResolvePNaClIntrinsics::runOnFunction(Function &F) { |
89 bool Changed = resolveSimpleCall(F, Intrinsic::nacl_setjmp, "setjmp"); | 190 bool Changed = resolveSimpleCall(F, Intrinsic::nacl_setjmp); |
90 Changed |= resolveSimpleCall(F, Intrinsic::nacl_longjmp, "longjmp"); | 191 Changed |= resolveSimpleCall(F, Intrinsic::nacl_longjmp); |
192 for (size_t II = 0; II != NaCl::NumAtomicIntrinsics; ++II) { | |
193 Changed |= resolveSimpleCall(F, NaCl::AtomicIntrinsics[II].ID); | |
194 } | |
91 return Changed; | 195 return Changed; |
92 } | 196 } |
93 | 197 |
94 char ResolvePNaClIntrinsics::ID = 0; | 198 char ResolvePNaClIntrinsics::ID = 0; |
95 INITIALIZE_PASS(ResolvePNaClIntrinsics, "resolve-pnacl-intrinsics", | 199 INITIALIZE_PASS(ResolvePNaClIntrinsics, "resolve-pnacl-intrinsics", |
96 "Resolve PNaCl intrinsic calls", false, false) | 200 "Resolve PNaCl intrinsic calls", false, false) |
97 | 201 |
98 FunctionPass *llvm::createResolvePNaClIntrinsicsPass() { | 202 FunctionPass *llvm::createResolvePNaClIntrinsicsPass() { |
99 return new ResolvePNaClIntrinsics(); | 203 return new ResolvePNaClIntrinsics(); |
100 } | 204 } |
OLD | NEW |