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

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

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 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/DataLayout.h"
20 #include "llvm/IR/Function.h"
21 #include "llvm/IR/InlineAsm.h"
22 #include "llvm/IR/InstVisitor.h"
23 #include "llvm/IR/Instructions.h"
24 #include "llvm/IR/Intrinsics.h"
25 #include "llvm/IR/Module.h"
26 #include "llvm/IR/NaClAtomicIntrinsics.h"
27 #include "llvm/Pass.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Support/Compiler.h"
30 #include "llvm/Support/raw_ostream.h"
31 #include "llvm/Transforms/NaCl.h"
32 #include <climits>
33 #include <string>
34
35 using namespace llvm;
36
37 // TODO(jfb) Keep the default of this option to true for Chrome 42, and change
38 // it to false for Chrome 43. This allows the PNaCl translator to be
39 // updated before the SDK starts emitting atomic memory orders that
40 // the old translator rejected.
41 static cl::opt<bool> PNaClMemoryOrderSeqCstOnly(
42 "pnacl-memory-order-seq-cst-only",
43 cl::desc("PNaCl should upgrade all atomic memory orders to seq_cst"),
44 cl::init(true));
45
46 namespace {
47
48 class RewriteAtomics : public ModulePass {
49 public:
50 static char ID; // Pass identification, replacement for typeid
51 RewriteAtomics() : ModulePass(ID) {
52 // This is a module pass because it may have to introduce
53 // intrinsic declarations into the module and modify a global function.
54 initializeRewriteAtomicsPass(*PassRegistry::getPassRegistry());
55 }
56
57 virtual bool runOnModule(Module &M);
58 virtual void getAnalysisUsage(AnalysisUsage &Info) const {
59 Info.addRequired<DataLayoutPass>();
60 }
61 };
62
63 template <class T> std::string ToStr(const T &V) {
64 std::string S;
65 raw_string_ostream OS(S);
66 OS << const_cast<T &>(V);
67 return OS.str();
68 }
69
70 class AtomicVisitor : public InstVisitor<AtomicVisitor> {
71 public:
72 AtomicVisitor(Module &M, Pass &P)
73 : M(M), C(M.getContext()),
74 TD(P.getAnalysis<DataLayoutPass>().getDataLayout()), AI(C),
75 ModifiedModule(false) {}
76 ~AtomicVisitor() {}
77 bool modifiedModule() const { return ModifiedModule; }
78
79 void visitLoadInst(LoadInst &I);
80 void visitStoreInst(StoreInst &I);
81 void visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
82 void visitAtomicRMWInst(AtomicRMWInst &I);
83 void visitFenceInst(FenceInst &I);
84
85 private:
86 Module &M;
87 LLVMContext &C;
88 const DataLayout TD;
89 NaCl::AtomicIntrinsics AI;
90 bool ModifiedModule;
91
92 AtomicVisitor() LLVM_DELETED_FUNCTION;
93 AtomicVisitor(const AtomicVisitor &) LLVM_DELETED_FUNCTION;
94 AtomicVisitor &operator=(const AtomicVisitor &) LLVM_DELETED_FUNCTION;
95
96 /// Create an integer constant holding a NaCl::MemoryOrder that can be
97 /// passed as an argument to one of the @llvm.nacl.atomic.*
98 /// intrinsics. This function may strengthen the ordering initially
99 /// specified by the instruction \p I for stability purpose.
100 template <class Instruction>
101 ConstantInt *freezeMemoryOrder(const Instruction &I, AtomicOrdering O) const;
102 std::pair<ConstantInt *, ConstantInt *>
103 freezeMemoryOrder(const AtomicCmpXchgInst &I, AtomicOrdering S,
104 AtomicOrdering F) const;
105
106 /// Sanity-check that instruction \p I which has pointer and value
107 /// parameters have matching sizes \p BitSize for the type-pointed-to
108 /// and the value's type \p T.
109 void checkSizeMatchesType(const Instruction &I, unsigned BitSize,
110 const Type *T) const;
111
112 /// Verify that loads and stores are at least naturally aligned. Use
113 /// byte alignment because converting to bits could truncate the
114 /// value.
115 void checkAlignment(const Instruction &I, unsigned ByteAlignment,
116 unsigned ByteSize) const;
117
118 /// Create a cast before Instruction \p I from \p Src to \p Dst with \p Name.
119 CastInst *createCast(Instruction &I, Value *Src, Type *Dst, Twine Name) const;
120
121 /// Try to find the atomic intrinsic of with its \p ID and \OverloadedType.
122 /// Report fatal error on failure.
123 const NaCl::AtomicIntrinsics::AtomicIntrinsic *
124 findAtomicIntrinsic(const Instruction &I, Intrinsic::ID ID,
125 Type *OverloadedType) const;
126
127 /// Helper function which rewrites a single instruction \p I to a
128 /// particular \p intrinsic with overloaded type \p OverloadedType,
129 /// and argument list \p Args. Will perform a bitcast to the proper \p
130 /// DstType, if different from \p OverloadedType.
131 void replaceInstructionWithIntrinsicCall(
132 Instruction &I, const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic,
133 Type *DstType, Type *OverloadedType, ArrayRef<Value *> Args);
134
135 /// Most atomics instructions deal with at least one pointer, this
136 /// struct automates some of this and has generic sanity checks.
137 template <class Instruction> struct PointerHelper {
138 Value *P;
139 Type *OriginalPET;
140 Type *PET;
141 unsigned BitSize;
142 PointerHelper(const AtomicVisitor &AV, Instruction &I)
143 : P(I.getPointerOperand()) {
144 if (I.getPointerAddressSpace() != 0)
145 report_fatal_error("unhandled pointer address space " +
146 Twine(I.getPointerAddressSpace()) + " for atomic: " +
147 ToStr(I));
148 assert(P->getType()->isPointerTy() && "expected a pointer");
149 PET = OriginalPET = P->getType()->getPointerElementType();
150 BitSize = AV.TD.getTypeSizeInBits(OriginalPET);
151 if (!OriginalPET->isIntegerTy()) {
152 // The pointer wasn't to an integer type. We define atomics in
153 // terms of integers, so bitcast the pointer to an integer of
154 // the proper width.
155 Type *IntNPtr = Type::getIntNPtrTy(AV.C, BitSize);
156 P = AV.createCast(I, P, IntNPtr, P->getName() + ".cast");
157 PET = P->getType()->getPointerElementType();
158 }
159 AV.checkSizeMatchesType(I, BitSize, PET);
160 }
161 };
162 };
163 }
164
165 char RewriteAtomics::ID = 0;
166 INITIALIZE_PASS(RewriteAtomics, "nacl-rewrite-atomics",
167 "rewrite atomics, volatiles and fences into stable "
168 "@llvm.nacl.atomics.* intrinsics",
169 false, false)
170
171 bool RewriteAtomics::runOnModule(Module &M) {
172 AtomicVisitor AV(M, *this);
173 AV.visit(M);
174 return AV.modifiedModule();
175 }
176
177 template <class Instruction>
178 ConstantInt *AtomicVisitor::freezeMemoryOrder(const Instruction &I,
179 AtomicOrdering O) const {
180 NaCl::MemoryOrder AO = NaCl::MemoryOrderInvalid;
181
182 // TODO Volatile load/store are promoted to sequentially consistent
183 // for now. We could do something weaker.
184 if (const LoadInst *L = dyn_cast<LoadInst>(&I)) {
185 if (L->isVolatile())
186 AO = NaCl::MemoryOrderSequentiallyConsistent;
187 } else if (const StoreInst *S = dyn_cast<StoreInst>(&I)) {
188 if (S->isVolatile())
189 AO = NaCl::MemoryOrderSequentiallyConsistent;
190 }
191
192 if (AO == NaCl::MemoryOrderInvalid) {
193 switch (O) {
194 case NotAtomic: llvm_unreachable("unexpected memory order");
195 // Monotonic is a strict superset of Unordered. Both can therefore
196 // map to Relaxed ordering, which is in the C11/C++11 standard.
197 case Unordered: AO = NaCl::MemoryOrderRelaxed; break;
198 case Monotonic: AO = NaCl::MemoryOrderRelaxed; break;
199 // TODO Consume is currently unspecified by LLVM's internal IR.
200 case Acquire: AO = NaCl::MemoryOrderAcquire; break;
201 case Release: AO = NaCl::MemoryOrderRelease; break;
202 case AcquireRelease: AO = NaCl::MemoryOrderAcquireRelease; break;
203 case SequentiallyConsistent:
204 AO = NaCl::MemoryOrderSequentiallyConsistent; break;
205 }
206 }
207
208 // TODO For now only acquire/release/acq_rel/seq_cst are allowed.
209 if (PNaClMemoryOrderSeqCstOnly || AO == NaCl::MemoryOrderRelaxed)
210 AO = NaCl::MemoryOrderSequentiallyConsistent;
211
212 return ConstantInt::get(Type::getInt32Ty(C), AO);
213 }
214
215 std::pair<ConstantInt *, ConstantInt *>
216 AtomicVisitor::freezeMemoryOrder(const AtomicCmpXchgInst &I, AtomicOrdering S,
217 AtomicOrdering F) const {
218 if (S == Release || (S == AcquireRelease && F != Acquire))
219 // According to C++11's [atomics.types.operations.req], cmpxchg with release
220 // success memory ordering must have relaxed failure memory ordering, which
221 // PNaCl currently disallows. The next-strongest ordering is acq_rel which
222 // is also an invalid failure ordering, we therefore have to change the
223 // success ordering to seq_cst, which can then fail as seq_cst.
224 S = F = SequentiallyConsistent;
225 if (F == Unordered || F == Monotonic) // Both are treated as relaxed.
226 F = AtomicCmpXchgInst::getStrongestFailureOrdering(S);
227 return std::make_pair(freezeMemoryOrder(I, S), freezeMemoryOrder(I, F));
228 }
229
230 void AtomicVisitor::checkSizeMatchesType(const Instruction &I, unsigned BitSize,
231 const Type *T) const {
232 Type *IntType = Type::getIntNTy(C, BitSize);
233 if (IntType && T == IntType)
234 return;
235 report_fatal_error("unsupported atomic type " + ToStr(*T) + " of size " +
236 Twine(BitSize) + " bits in: " + ToStr(I));
237 }
238
239 void AtomicVisitor::checkAlignment(const Instruction &I, unsigned ByteAlignment,
240 unsigned ByteSize) const {
241 if (ByteAlignment < ByteSize)
242 report_fatal_error("atomic load/store must be at least naturally aligned, "
243 "got " +
244 Twine(ByteAlignment) + ", bytes expected at least " +
245 Twine(ByteSize) + " bytes, in: " + ToStr(I));
246 }
247
248 CastInst *AtomicVisitor::createCast(Instruction &I, Value *Src, Type *Dst,
249 Twine Name) const {
250 Type *SrcT = Src->getType();
251 Instruction::CastOps Op = SrcT->isIntegerTy() && Dst->isPointerTy()
252 ? Instruction::IntToPtr
253 : SrcT->isPointerTy() && Dst->isIntegerTy()
254 ? Instruction::PtrToInt
255 : Instruction::BitCast;
256 if (!CastInst::castIsValid(Op, Src, Dst))
257 report_fatal_error("cannot emit atomic instruction while converting type " +
258 ToStr(*SrcT) + " to " + ToStr(*Dst) + " for " + Name +
259 " in " + ToStr(I));
260 return CastInst::Create(Op, Src, Dst, Name, &I);
261 }
262
263 const NaCl::AtomicIntrinsics::AtomicIntrinsic *
264 AtomicVisitor::findAtomicIntrinsic(const Instruction &I, Intrinsic::ID ID,
265 Type *OverloadedType) const {
266 if (const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
267 AI.find(ID, OverloadedType))
268 return Intrinsic;
269 report_fatal_error("unsupported atomic instruction: " + ToStr(I));
270 }
271
272 void AtomicVisitor::replaceInstructionWithIntrinsicCall(
273 Instruction &I, const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic,
274 Type *DstType, Type *OverloadedType, ArrayRef<Value *> Args) {
275 std::string Name(I.getName());
276 Function *F = Intrinsic->getDeclaration(&M);
277 CallInst *Call = CallInst::Create(F, Args, "", &I);
278 Call->setDebugLoc(I.getDebugLoc());
279 Instruction *Res = Call;
280
281 assert((I.getType()->isStructTy() == isa<AtomicCmpXchgInst>(&I)) &&
282 "cmpxchg returns a struct, and other instructions don't");
283 if (auto S = dyn_cast<StructType>(I.getType())) {
284 assert(S->getNumElements() == 2 &&
285 "cmpxchg returns a struct with two elements");
286 assert(S->getElementType(0) == DstType &&
287 "cmpxchg struct's first member should be the value type");
288 assert(S->getElementType(1) == Type::getInt1Ty(C) &&
289 "cmpxchg struct's second member should be the success flag");
290 // Recreate struct { T value, i1 success } after the call.
291 auto Success = CmpInst::Create(
292 Instruction::ICmp, CmpInst::ICMP_EQ, Res,
293 cast<AtomicCmpXchgInst>(&I)->getCompareOperand(), "success", &I);
294 Res = InsertValueInst::Create(
295 InsertValueInst::Create(UndefValue::get(S), Res, 0,
296 Name + ".insert.value", &I),
297 Success, 1, Name + ".insert.success", &I);
298 } else if (!Call->getType()->isVoidTy() && DstType != OverloadedType) {
299 // The call returns a value which needs to be cast to a non-integer.
300 Res = createCast(I, Call, DstType, Name + ".cast");
301 Res->setDebugLoc(I.getDebugLoc());
302 }
303
304 I.replaceAllUsesWith(Res);
305 I.eraseFromParent();
306 Call->setName(Name);
307 ModifiedModule = true;
308 }
309
310 /// %res = load {atomic|volatile} T* %ptr memory_order, align sizeof(T)
311 /// becomes:
312 /// %res = call T @llvm.nacl.atomic.load.i<size>(%ptr, memory_order)
313 void AtomicVisitor::visitLoadInst(LoadInst &I) {
314 if (I.isSimple())
315 return;
316 PointerHelper<LoadInst> PH(*this, I);
317 const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
318 findAtomicIntrinsic(I, Intrinsic::nacl_atomic_load, PH.PET);
319 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT);
320 Value *Args[] = {PH.P, freezeMemoryOrder(I, I.getOrdering())};
321 replaceInstructionWithIntrinsicCall(I, Intrinsic, PH.OriginalPET, PH.PET,
322 Args);
323 }
324
325 /// store {atomic|volatile} T %val, T* %ptr memory_order, align sizeof(T)
326 /// becomes:
327 /// call void @llvm.nacl.atomic.store.i<size>(%val, %ptr, memory_order)
328 void AtomicVisitor::visitStoreInst(StoreInst &I) {
329 if (I.isSimple())
330 return;
331 PointerHelper<StoreInst> PH(*this, I);
332 const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
333 findAtomicIntrinsic(I, Intrinsic::nacl_atomic_store, PH.PET);
334 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT);
335 Value *V = I.getValueOperand();
336 if (!V->getType()->isIntegerTy()) {
337 // The store isn't of an integer type. We define atomics in terms of
338 // integers, so bitcast the value to store to an integer of the
339 // proper width.
340 CastInst *Cast = createCast(I, V, Type::getIntNTy(C, PH.BitSize),
341 V->getName() + ".cast");
342 Cast->setDebugLoc(I.getDebugLoc());
343 V = Cast;
344 }
345 checkSizeMatchesType(I, PH.BitSize, V->getType());
346 Value *Args[] = {V, PH.P, freezeMemoryOrder(I, I.getOrdering())};
347 replaceInstructionWithIntrinsicCall(I, Intrinsic, PH.OriginalPET, PH.PET,
348 Args);
349 }
350
351 /// %res = atomicrmw OP T* %ptr, T %val memory_order
352 /// becomes:
353 /// %res = call T @llvm.nacl.atomic.rmw.i<size>(OP, %ptr, %val, memory_order)
354 void AtomicVisitor::visitAtomicRMWInst(AtomicRMWInst &I) {
355 NaCl::AtomicRMWOperation Op;
356 switch (I.getOperation()) {
357 default: report_fatal_error("unsupported atomicrmw operation: " + ToStr(I));
358 case AtomicRMWInst::Add: Op = NaCl::AtomicAdd; break;
359 case AtomicRMWInst::Sub: Op = NaCl::AtomicSub; break;
360 case AtomicRMWInst::And: Op = NaCl::AtomicAnd; break;
361 case AtomicRMWInst::Or: Op = NaCl::AtomicOr; break;
362 case AtomicRMWInst::Xor: Op = NaCl::AtomicXor; break;
363 case AtomicRMWInst::Xchg: Op = NaCl::AtomicExchange; break;
364 }
365 PointerHelper<AtomicRMWInst> PH(*this, I);
366 const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
367 findAtomicIntrinsic(I, Intrinsic::nacl_atomic_rmw, PH.PET);
368 checkSizeMatchesType(I, PH.BitSize, I.getValOperand()->getType());
369 Value *Args[] = {ConstantInt::get(Type::getInt32Ty(C), Op), PH.P,
370 I.getValOperand(), freezeMemoryOrder(I, I.getOrdering())};
371 replaceInstructionWithIntrinsicCall(I, Intrinsic, PH.OriginalPET, PH.PET,
372 Args);
373 }
374
375 /// %res = cmpxchg [weak] T* %ptr, T %old, T %new, memory_order_success
376 /// memory_order_failure
377 /// %val = extractvalue { T, i1 } %res, 0
378 /// %success = extractvalue { T, i1 } %res, 1
379 /// becomes:
380 /// %val = call T @llvm.nacl.atomic.cmpxchg.i<size>(
381 /// %object, %expected, %desired, memory_order_success,
382 /// memory_order_failure)
383 /// %success = icmp eq %old, %val
384 /// Note: weak is currently dropped if present, the cmpxchg is always strong.
385 void AtomicVisitor::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
386 PointerHelper<AtomicCmpXchgInst> PH(*this, I);
387 const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
388 findAtomicIntrinsic(I, Intrinsic::nacl_atomic_cmpxchg, PH.PET);
389 checkSizeMatchesType(I, PH.BitSize, I.getCompareOperand()->getType());
390 checkSizeMatchesType(I, PH.BitSize, I.getNewValOperand()->getType());
391 auto Order =
392 freezeMemoryOrder(I, I.getSuccessOrdering(), I.getFailureOrdering());
393 Value *Args[] = {PH.P, I.getCompareOperand(), I.getNewValOperand(),
394 Order.first, Order.second};
395 replaceInstructionWithIntrinsicCall(I, Intrinsic, PH.OriginalPET, PH.PET,
396 Args);
397 }
398
399 /// fence memory_order
400 /// becomes:
401 /// call void @llvm.nacl.atomic.fence(memory_order)
402 /// and
403 /// call void asm sideeffect "", "~{memory}"()
404 /// fence seq_cst
405 /// call void asm sideeffect "", "~{memory}"()
406 /// becomes:
407 /// call void asm sideeffect "", "~{memory}"()
408 /// call void @llvm.nacl.atomic.fence.all()
409 /// call void asm sideeffect "", "~{memory}"()
410 /// Note that the assembly gets eliminated by the -remove-asm-memory pass.
411 void AtomicVisitor::visitFenceInst(FenceInst &I) {
412 Type *T = Type::getInt32Ty(C); // Fences aren't overloaded on type.
413 BasicBlock::InstListType &IL(I.getParent()->getInstList());
414 bool isFirst = IL.empty() || &*I.getParent()->getInstList().begin() == &I;
415 bool isLast = IL.empty() || &*I.getParent()->getInstList().rbegin() == &I;
416 CallInst *PrevC = isFirst ? 0 : dyn_cast<CallInst>(I.getPrevNode());
417 CallInst *NextC = isLast ? 0 : dyn_cast<CallInst>(I.getNextNode());
418
419 if ((PrevC && PrevC->isInlineAsm() &&
420 cast<InlineAsm>(PrevC->getCalledValue())->isAsmMemory()) &&
421 (NextC && NextC->isInlineAsm() &&
422 cast<InlineAsm>(NextC->getCalledValue())->isAsmMemory()) &&
423 I.getOrdering() == SequentiallyConsistent) {
424 const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
425 findAtomicIntrinsic(I, Intrinsic::nacl_atomic_fence_all, T);
426 replaceInstructionWithIntrinsicCall(I, Intrinsic, T, T,
427 ArrayRef<Value *>());
428 } else {
429 const NaCl::AtomicIntrinsics::AtomicIntrinsic *Intrinsic =
430 findAtomicIntrinsic(I, Intrinsic::nacl_atomic_fence, T);
431 Value *Args[] = {freezeMemoryOrder(I, I.getOrdering())};
432 replaceInstructionWithIntrinsicCall(I, Intrinsic, T, T, Args);
433 }
434 }
435
436 ModulePass *llvm::createRewriteAtomicsPass() { return new RewriteAtomics(); }
OLDNEW
« no previous file with comments | « lib/Transforms/NaCl/ResolvePNaClIntrinsics.cpp ('k') | lib/Transforms/NaCl/RewriteLLVMIntrinsics.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698