OLD | NEW |
---|---|
(Empty) | |
1 //===- RewriteAtomics.cpp - Stabilize instructions used for concurrency ---===// | |
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 encodes atomics, volatiles and fences using NaCl intrinsics | |
11 // instead of LLVM's regular IR instructions. | |
12 // | |
13 // All of the above are transformed into one of the | |
14 // @llvm.nacl.atomic.* intrinsics. | |
15 // | |
16 //===----------------------------------------------------------------------===// | |
17 | |
18 #include "llvm/ADT/Twine.h" | |
19 #include "llvm/IR/Function.h" | |
20 #include "llvm/IR/Instructions.h" | |
21 #include "llvm/IR/Intrinsics.h" | |
22 #include "llvm/IR/Module.h" | |
23 #include "llvm/IR/NaClAtomicIntrinsics.h" | |
24 #include "llvm/InstVisitor.h" | |
25 #include "llvm/Pass.h" | |
26 #include "llvm/Support/Compiler.h" | |
27 #include "llvm/Support/raw_ostream.h" | |
28 #include "llvm/Support/raw_ostream.h" | |
29 #include "llvm/Transforms/NaCl.h" | |
30 #include <climits> | |
31 #include <string> | |
32 | |
33 using namespace llvm; | |
34 | |
35 namespace { | |
36 class RewriteAtomics : public ModulePass { | |
37 public: | |
38 static char ID; // Pass identification, replacement for typeid | |
39 RewriteAtomics() : ModulePass(ID) { | |
40 // This is a module pass because it may have to introduce | |
41 // intrinsic declarations into the module and modify a global function. | |
42 initializeRewriteAtomicsPass(*PassRegistry::getPassRegistry()); | |
43 } | |
44 | |
45 virtual bool runOnModule(Module &M); | |
46 }; | |
47 | |
48 template <class T> std::string ToStr(const T &V) { | |
49 std::string S; | |
50 raw_string_ostream OS(S); | |
51 OS << const_cast<T &>(V); | |
52 return OS.str(); | |
53 } | |
54 | |
55 class AtomicVisitor : public InstVisitor<AtomicVisitor> { | |
56 public: | |
57 AtomicVisitor(Module &M) | |
58 : M(M), C(M.getContext()), AI(C), ModifiedModule(false) {} | |
59 ~AtomicVisitor() {} | |
60 bool modifiedModule() const { return ModifiedModule; } | |
61 | |
62 void visitLoadInst(LoadInst &I); | |
63 void visitStoreInst(StoreInst &I); | |
64 void visitAtomicCmpXchgInst(AtomicCmpXchgInst &I); | |
65 void visitAtomicRMWInst(AtomicRMWInst &I); | |
66 void visitFenceInst(FenceInst &I); | |
67 | |
68 private: | |
69 Module &M; | |
70 LLVMContext &C; | |
71 NaCl::AtomicIntrinsics AI; | |
72 bool ModifiedModule; | |
73 | |
74 AtomicVisitor() LLVM_DELETED_FUNCTION; | |
75 AtomicVisitor(const AtomicVisitor &) LLVM_DELETED_FUNCTION; | |
76 AtomicVisitor &operator=(const AtomicVisitor &) LLVM_DELETED_FUNCTION; | |
77 | |
78 /// Create an integer constant holding a NaCl::MemoryOrder that can be | |
79 /// passed as an argument to one of the @llvm.nacl.atomic.* | |
80 /// intrinsics. This function may strengthen the ordering initially | |
81 /// specified by the instruction \p I for stability purpose. | |
82 template <class Instruction> | |
83 ConstantInt *freezeMemoryOrder(const Instruction &I) const; | |
84 | |
85 /// Sanity-check that instruction \p I which has pointer and value | |
86 /// parameters have matching sizes \p BitSize for the type-pointed-to | |
87 /// and the value's type \p T. | |
88 void checkSizeMatchesType(const Instruction &I, unsigned BitSize, | |
89 const Type *T) const; | |
90 | |
91 /// Verify that loads and stores are at least naturally aligned. Use | |
92 /// byte alignment because converting to bits could truncate the | |
93 /// value. | |
94 void checkAlignment(const Instruction &I, unsigned ByteAlignment, | |
95 unsigned ByteSize) const; | |
96 | |
97 /// Helper function which rewrites a single instruction \p I to a | |
98 /// particular intrinsic \p ID with overloaded type \p OverloadedType, | |
99 /// and argument list \p Args. Will perform a bitcast to the proper \p | |
100 /// DstType, if different from \p OverloadedType. | |
101 void replaceInstructionWithIntrinsicCall(Instruction &I, Intrinsic::ID ID, | |
102 Type *DstType, Type *OverloadedType, | |
103 ArrayRef<Value *> Args); | |
104 | |
105 /// Most atomics instructions deal with at least one pointer, this | |
106 /// struct automates some of this and has generic sanity checks. | |
107 template <class Instruction> struct PointerHelper { | |
108 Value *P; | |
109 Type *OriginalPET; | |
110 Type *PET; | |
111 unsigned BitSize; | |
112 PointerHelper(const AtomicVisitor &AV, Instruction &I) | |
113 : P(I.getPointerOperand()) { | |
114 if (I.getPointerAddressSpace() != 0) | |
115 report_fatal_error("unhandled pointer address space " + | |
116 Twine(I.getPointerAddressSpace()) + " for atomic: " + | |
117 ToStr(I)); | |
118 assert(P->getType()->isPointerTy() && "expected a pointer"); | |
119 PET = OriginalPET = P->getType()->getPointerElementType(); | |
120 BitSize = OriginalPET->getPrimitiveSizeInBits(); | |
121 if (!OriginalPET->isIntegerTy()) { | |
122 // The pointer wasn't to an integer type. We define atomics in | |
123 // terms of integers, so bitcast the pointer to an integer of | |
124 // the proper width. | |
125 P = CastInst::Create(Instruction::BitCast, P, | |
126 Type::getIntNPtrTy(AV.C, BitSize), | |
127 P->getName() + ".cast", &I); | |
128 PET = P->getType()->getPointerElementType(); | |
129 } | |
130 AV.checkSizeMatchesType(I, BitSize, PET); | |
131 } | |
132 }; | |
133 }; | |
134 } | |
135 | |
136 char RewriteAtomics::ID = 0; | |
137 INITIALIZE_PASS(RewriteAtomics, "nacl-rewrite-atomics", | |
138 "rewrite atomics, volatiles and fences into stable " | |
139 "@llvm.nacl.atomics.* intrinsics", | |
140 false, false) | |
141 | |
142 bool RewriteAtomics::runOnModule(Module &M) { | |
143 AtomicVisitor AV(M); | |
144 AV.visit(M); | |
145 return AV.modifiedModule(); | |
146 } | |
147 | |
148 template <class Instruction> | |
149 ConstantInt *AtomicVisitor::freezeMemoryOrder(const Instruction &I) const { | |
150 NaCl::MemoryOrder AO = NaCl::MemoryOrderInvalid; | |
151 | |
152 // TODO Volatile load/store are promoted to sequentially consistent | |
153 // for now. We could do something weaker. | |
154 if (const LoadInst *L = dyn_cast<LoadInst>(&I)) { | |
155 if (L->isVolatile()) | |
156 AO = NaCl::MemoryOrderSequentiallyConsistent; | |
157 } else if (const StoreInst *S = dyn_cast<StoreInst>(&I)) { | |
158 if (S->isVolatile()) | |
159 AO = NaCl::MemoryOrderSequentiallyConsistent; | |
160 } | |
161 | |
162 if (AO == NaCl::MemoryOrderInvalid) { | |
163 switch (I.getOrdering()) { | |
164 default: | |
165 case NotAtomic: llvm_unreachable("unexpected memory order"); | |
166 // Monotonic is a strict superset of Unordered. Both can therefore | |
167 // map to Relaxed ordering, which is in the C11/C++11 standard. | |
168 case Unordered: AO = NaCl::MemoryOrderRelaxed; break; | |
169 case Monotonic: AO = NaCl::MemoryOrderRelaxed; break; | |
170 // TODO Consume is currently unspecified by LLVM's internal IR. | |
171 case Acquire: AO = NaCl::MemoryOrderAcquire; break; | |
172 case Release: AO = NaCl::MemoryOrderRelease; break; | |
173 case AcquireRelease: AO = NaCl::MemoryOrderAcquireRelease; break; | |
174 case SequentiallyConsistent: | |
175 AO = NaCl::MemoryOrderSequentiallyConsistent; break; | |
176 } | |
177 } | |
178 | |
179 // TODO For now only sequential consistency is allowed. | |
180 AO = NaCl::MemoryOrderSequentiallyConsistent; | |
181 | |
182 return ConstantInt::get(Type::getInt32Ty(C), AO); | |
183 } | |
184 | |
185 void AtomicVisitor::checkSizeMatchesType(const Instruction &I, unsigned BitSize, | |
186 const Type *T) const { | |
187 Type *IntType = Type::getIntNTy(C, BitSize); | |
188 if (IntType && T == IntType) | |
189 return; | |
190 report_fatal_error("unsupported atomic type " + ToStr(*T) + " of size " + | |
191 Twine(BitSize) + " bits in: " + ToStr(I)); | |
192 } | |
193 | |
194 void AtomicVisitor::checkAlignment(const Instruction &I, unsigned ByteAlignment, | |
195 unsigned ByteSize) const { | |
196 if (ByteAlignment < ByteSize) | |
197 report_fatal_error("atomic load/store must be at least naturally aligned, " | |
198 "got " + | |
199 Twine(ByteAlignment) + ", bytes expected at least " + | |
200 Twine(ByteSize) + " bytes, in: " + ToStr(I)); | |
201 } | |
202 | |
203 void AtomicVisitor::replaceInstructionWithIntrinsicCall( | |
204 Instruction &I, Intrinsic::ID ID, Type *DstType, Type *OverloadedType, | |
205 ArrayRef<Value *> Args) { | |
206 std::string Name(I.getName()); | |
207 Function *F = AI.find(ID, OverloadedType)->getDeclaration(&M); | |
208 CallInst *Call = CallInst::Create(F, Args, "", &I); | |
209 Instruction *Res = Call; | |
210 if (!Call->getType()->isVoidTy() && DstType != OverloadedType) { | |
211 // The call returns a value which needs to be cast to a non-integer. | |
212 Res = CastInst::Create(Instruction::BitCast, Call, DstType, Name + ".cast", | |
213 &I); | |
214 Res->setDebugLoc(I.getDebugLoc()); | |
215 } | |
216 Call->setDebugLoc(I.getDebugLoc()); | |
217 I.replaceAllUsesWith(Res); | |
218 I.eraseFromParent(); | |
219 Call->setName(Name); | |
220 ModifiedModule = true; | |
221 } | |
222 | |
223 /// %res = load {atomic|volatile} T* %ptr memory_order, align sizeof(T) | |
224 /// becomes: | |
225 /// %res = call T @llvm.nacl.atomic.load.i<size>(%ptr, memory_order) | |
226 void AtomicVisitor::visitLoadInst(LoadInst &I) { | |
227 if (I.isSimple()) | |
228 return; | |
229 PointerHelper<LoadInst> PH(*this, I); | |
230 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT); | |
231 Value *Args[] = { PH.P, freezeMemoryOrder(I) }; | |
232 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_load, | |
233 PH.OriginalPET, PH.PET, Args); | |
234 } | |
235 | |
236 /// store {atomic|volatile} T %val, T* %ptr memory_order, align sizeof(T) | |
237 /// becomes: | |
238 /// call void @llvm.nacl.atomic.store.i<size>(%val, %ptr, memory_order) | |
239 void AtomicVisitor::visitStoreInst(StoreInst &I) { | |
240 if (I.isSimple()) | |
241 return; | |
242 PointerHelper<StoreInst> PH(*this, I); | |
243 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT); | |
244 Value *V = I.getValueOperand(); | |
245 if (!V->getType()->isIntegerTy()) { | |
246 // The store isn't of an integer type. We define atomics in terms of | |
247 // integers, so bitcast the value to store to an integer of the | |
248 // proper width. | |
249 CastInst *Cast = CastInst::Create(Instruction::BitCast, V, | |
250 Type::getIntNTy(C, PH.BitSize), | |
251 V->getName() + ".cast", &I); | |
252 Cast->setDebugLoc(I.getDebugLoc()); | |
253 V = Cast; | |
254 } | |
255 checkSizeMatchesType(I, PH.BitSize, V->getType()); | |
256 Value *Args[] = { V, PH.P, freezeMemoryOrder(I) }; | |
257 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_store, | |
258 PH.OriginalPET, PH.PET, Args); | |
259 } | |
260 | |
261 /// %res = atomicrmw OP T* %ptr, T %val memory_order | |
262 /// becomes: | |
263 /// %res = call T @llvm.nacl.atomic.rmw.i<size>(OP, %ptr, %val, memory_order) | |
264 void AtomicVisitor::visitAtomicRMWInst(AtomicRMWInst &I) { | |
265 NaCl::AtomicRMWOperation Op; | |
266 switch (I.getOperation()) { | |
267 default: report_fatal_error("unsupported atomicrmw operation: " + ToStr(I)); | |
268 case AtomicRMWInst::Add: Op = NaCl::AtomicAdd; break; | |
269 case AtomicRMWInst::Sub: Op = NaCl::AtomicSub; break; | |
270 case AtomicRMWInst::And: Op = NaCl::AtomicAnd; break; | |
271 case AtomicRMWInst::Or: Op = NaCl::AtomicOr; break; | |
272 case AtomicRMWInst::Xor: Op = NaCl::AtomicXor; break; | |
273 case AtomicRMWInst::Xchg: Op = NaCl::AtomicExchange; break; | |
274 } | |
275 PointerHelper<AtomicRMWInst> PH(*this, I); | |
276 checkSizeMatchesType(I, PH.BitSize, I.getValOperand()->getType()); | |
277 Value *Args[] = { ConstantInt::get(Type::getInt32Ty(C), Op), PH.P, | |
278 I.getValOperand(), freezeMemoryOrder(I) }; | |
279 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_rmw, | |
280 PH.OriginalPET, PH.PET, Args); | |
jvoung (off chromium)
2013/07/09 17:01:53
Does atomicrmw and cmpxchg need to handle float an
| |
281 } | |
282 | |
283 /// %res = cmpxchg T* %ptr, T %old, T %new memory_order | |
284 /// becomes: | |
285 /// %res = call T @llvm.nacl.atomic.cmpxchg.i<size>( | |
286 /// %object, %expected, %desired, memory_order_success, | |
287 /// memory_order_failure) | |
288 void AtomicVisitor::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) { | |
289 PointerHelper<AtomicCmpXchgInst> PH(*this, I); | |
290 checkSizeMatchesType(I, PH.BitSize, I.getCompareOperand()->getType()); | |
291 checkSizeMatchesType(I, PH.BitSize, I.getNewValOperand()->getType()); | |
292 // TODO LLVM currently doesn't support specifying separate memory | |
293 // orders for compare exchange's success and failure cases: LLVM | |
294 // IR implicitly drops the Release part of the specified memory | |
295 // order on failure. | |
296 Value *Args[] = { PH.P, I.getCompareOperand(), I.getNewValOperand(), | |
297 freezeMemoryOrder(I), freezeMemoryOrder(I) }; | |
298 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_cmpxchg, | |
299 PH.OriginalPET, PH.PET, Args); | |
300 } | |
301 | |
302 /// fence memory_order | |
303 /// becomes: | |
304 /// call void @llvm.nacl.atomic.fence(memory_order) | |
305 void AtomicVisitor::visitFenceInst(FenceInst &I) { | |
306 Type *T = Type::getInt32Ty(C); // Fences aren't overloaded on type. | |
307 Value *Args[] = { freezeMemoryOrder(I) }; | |
308 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_fence, T, T, | |
309 Args); | |
310 } | |
311 | |
312 ModulePass *llvm::createRewriteAtomicsPass() { return new RewriteAtomics(); } | |
OLD | NEW |