Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(456)

Side by Side Diff: lib/Transforms/NaCl/RewriteAtomics.cpp

Issue 17777004: Concurrency support for PNaCl ABI (Closed) Base URL: http://git.chromium.org/native_client/pnacl-llvm.git@master
Patch Set: Clarify documentation. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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(); }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698