Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 //===- RewriteAtomics.cpp - Stabilize instructions used for concurrency ---===// | 1 //===- RewriteAtomics.cpp - Stabilize instructions used for concurrency ---===// |
| 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 encodes atomics, volatiles and fences using NaCl intrinsics | 10 // This pass encodes atomics, volatiles and fences using NaCl intrinsics |
| 11 // instead of LLVM's regular IR instructions. | 11 // instead of LLVM's regular IR instructions. |
| 12 // | 12 // |
| 13 // All of the above are transformed into one of the | 13 // All of the above are transformed into one of the |
| 14 // @llvm.nacl.atomic.* intrinsics. | 14 // @llvm.nacl.atomic.* intrinsics. |
| 15 // | 15 // |
| 16 //===----------------------------------------------------------------------===// | 16 //===----------------------------------------------------------------------===// |
| 17 | 17 |
| 18 #include "llvm/ADT/Twine.h" | 18 #include "llvm/ADT/Twine.h" |
| 19 #include "llvm/IR/DataLayout.h" | 19 #include "llvm/IR/DataLayout.h" |
| 20 #include "llvm/IR/Function.h" | 20 #include "llvm/IR/Function.h" |
| 21 #include "llvm/IR/Instructions.h" | 21 #include "llvm/IR/Instructions.h" |
| 22 #include "llvm/IR/Intrinsics.h" | 22 #include "llvm/IR/Intrinsics.h" |
| 23 #include "llvm/IR/Module.h" | 23 #include "llvm/IR/Module.h" |
| 24 #include "llvm/IR/NaClAsm.h" | |
| 24 #include "llvm/IR/NaClAtomicIntrinsics.h" | 25 #include "llvm/IR/NaClAtomicIntrinsics.h" |
| 25 #include "llvm/InstVisitor.h" | 26 #include "llvm/InstVisitor.h" |
| 26 #include "llvm/Pass.h" | 27 #include "llvm/Pass.h" |
| 27 #include "llvm/Support/Compiler.h" | 28 #include "llvm/Support/Compiler.h" |
| 28 #include "llvm/Support/raw_ostream.h" | 29 #include "llvm/Support/raw_ostream.h" |
| 29 #include "llvm/Transforms/NaCl.h" | 30 #include "llvm/Transforms/NaCl.h" |
| 30 #include <climits> | 31 #include <climits> |
| 31 #include <string> | 32 #include <string> |
| 32 | 33 |
| 33 using namespace llvm; | 34 using namespace llvm; |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 234 Res = createCast(I, Call, DstType, Name + ".cast"); | 235 Res = createCast(I, Call, DstType, Name + ".cast"); |
| 235 Res->setDebugLoc(I.getDebugLoc()); | 236 Res->setDebugLoc(I.getDebugLoc()); |
| 236 } | 237 } |
| 237 Call->setDebugLoc(I.getDebugLoc()); | 238 Call->setDebugLoc(I.getDebugLoc()); |
| 238 I.replaceAllUsesWith(Res); | 239 I.replaceAllUsesWith(Res); |
| 239 I.eraseFromParent(); | 240 I.eraseFromParent(); |
| 240 Call->setName(Name); | 241 Call->setName(Name); |
| 241 ModifiedModule = true; | 242 ModifiedModule = true; |
| 242 } | 243 } |
| 243 | 244 |
| 244 /// %res = load {atomic|volatile} T* %ptr memory_order, align sizeof(T) | 245 /// %res = load {atomic|volatile} T* %ptr memory_order, align sizeof(T) |
| 245 /// becomes: | 246 /// becomes: |
| 246 /// %res = call T @llvm.nacl.atomic.load.i<size>(%ptr, memory_order) | 247 /// %res = call T @llvm.nacl.atomic.load.i<size>(%ptr, memory_order) |
| 247 void AtomicVisitor::visitLoadInst(LoadInst &I) { | 248 void AtomicVisitor::visitLoadInst(LoadInst &I) { |
| 248 if (I.isSimple()) | 249 if (I.isSimple()) |
| 249 return; | 250 return; |
| 250 PointerHelper<LoadInst> PH(*this, I); | 251 PointerHelper<LoadInst> PH(*this, I); |
| 251 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT); | 252 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT); |
| 252 Value *Args[] = { PH.P, freezeMemoryOrder(I) }; | 253 Value *Args[] = { PH.P, freezeMemoryOrder(I) }; |
| 253 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_load, | 254 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_load, |
| 254 PH.OriginalPET, PH.PET, Args); | 255 PH.OriginalPET, PH.PET, Args); |
| 255 } | 256 } |
| 256 | 257 |
| 257 /// store {atomic|volatile} T %val, T* %ptr memory_order, align sizeof(T) | 258 /// store {atomic|volatile} T %val, T* %ptr memory_order, align sizeof(T) |
| 258 /// becomes: | 259 /// becomes: |
| 259 /// call void @llvm.nacl.atomic.store.i<size>(%val, %ptr, memory_order) | 260 /// call void @llvm.nacl.atomic.store.i<size>(%val, %ptr, memory_order) |
| 260 void AtomicVisitor::visitStoreInst(StoreInst &I) { | 261 void AtomicVisitor::visitStoreInst(StoreInst &I) { |
| 261 if (I.isSimple()) | 262 if (I.isSimple()) |
| 262 return; | 263 return; |
| 263 PointerHelper<StoreInst> PH(*this, I); | 264 PointerHelper<StoreInst> PH(*this, I); |
| 264 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT); | 265 checkAlignment(I, I.getAlignment(), PH.BitSize / CHAR_BIT); |
| 265 Value *V = I.getValueOperand(); | 266 Value *V = I.getValueOperand(); |
| 266 if (!V->getType()->isIntegerTy()) { | 267 if (!V->getType()->isIntegerTy()) { |
| 267 // The store isn't of an integer type. We define atomics in terms of | 268 // The store isn't of an integer type. We define atomics in terms of |
| 268 // integers, so bitcast the value to store to an integer of the | 269 // integers, so bitcast the value to store to an integer of the |
| 269 // proper width. | 270 // proper width. |
| 270 CastInst *Cast = createCast(I, V, Type::getIntNTy(C, PH.BitSize), | 271 CastInst *Cast = createCast(I, V, Type::getIntNTy(C, PH.BitSize), |
| 271 V->getName() + ".cast"); | 272 V->getName() + ".cast"); |
| 272 Cast->setDebugLoc(I.getDebugLoc()); | 273 Cast->setDebugLoc(I.getDebugLoc()); |
| 273 V = Cast; | 274 V = Cast; |
| 274 } | 275 } |
| 275 checkSizeMatchesType(I, PH.BitSize, V->getType()); | 276 checkSizeMatchesType(I, PH.BitSize, V->getType()); |
| 276 Value *Args[] = { V, PH.P, freezeMemoryOrder(I) }; | 277 Value *Args[] = { V, PH.P, freezeMemoryOrder(I) }; |
| 277 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_store, | 278 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_store, |
| 278 PH.OriginalPET, PH.PET, Args); | 279 PH.OriginalPET, PH.PET, Args); |
| 279 } | 280 } |
| 280 | 281 |
| 281 /// %res = atomicrmw OP T* %ptr, T %val memory_order | 282 /// %res = atomicrmw OP T* %ptr, T %val memory_order |
| 282 /// becomes: | 283 /// becomes: |
| 283 /// %res = call T @llvm.nacl.atomic.rmw.i<size>(OP, %ptr, %val, memory_order) | 284 /// %res = call T @llvm.nacl.atomic.rmw.i<size>(OP, %ptr, %val, memory_order) |
| 284 void AtomicVisitor::visitAtomicRMWInst(AtomicRMWInst &I) { | 285 void AtomicVisitor::visitAtomicRMWInst(AtomicRMWInst &I) { |
| 285 NaCl::AtomicRMWOperation Op; | 286 NaCl::AtomicRMWOperation Op; |
| 286 switch (I.getOperation()) { | 287 switch (I.getOperation()) { |
| 287 default: report_fatal_error("unsupported atomicrmw operation: " + ToStr(I)); | 288 default: report_fatal_error("unsupported atomicrmw operation: " + ToStr(I)); |
| 288 case AtomicRMWInst::Add: Op = NaCl::AtomicAdd; break; | 289 case AtomicRMWInst::Add: Op = NaCl::AtomicAdd; break; |
| 289 case AtomicRMWInst::Sub: Op = NaCl::AtomicSub; break; | 290 case AtomicRMWInst::Sub: Op = NaCl::AtomicSub; break; |
| 290 case AtomicRMWInst::And: Op = NaCl::AtomicAnd; break; | 291 case AtomicRMWInst::And: Op = NaCl::AtomicAnd; break; |
| 291 case AtomicRMWInst::Or: Op = NaCl::AtomicOr; break; | 292 case AtomicRMWInst::Or: Op = NaCl::AtomicOr; break; |
| 292 case AtomicRMWInst::Xor: Op = NaCl::AtomicXor; break; | 293 case AtomicRMWInst::Xor: Op = NaCl::AtomicXor; break; |
| 293 case AtomicRMWInst::Xchg: Op = NaCl::AtomicExchange; break; | 294 case AtomicRMWInst::Xchg: Op = NaCl::AtomicExchange; break; |
| 294 } | 295 } |
| 295 PointerHelper<AtomicRMWInst> PH(*this, I); | 296 PointerHelper<AtomicRMWInst> PH(*this, I); |
| 296 checkSizeMatchesType(I, PH.BitSize, I.getValOperand()->getType()); | 297 checkSizeMatchesType(I, PH.BitSize, I.getValOperand()->getType()); |
| 297 Value *Args[] = { ConstantInt::get(Type::getInt32Ty(C), Op), PH.P, | 298 Value *Args[] = { ConstantInt::get(Type::getInt32Ty(C), Op), PH.P, |
| 298 I.getValOperand(), freezeMemoryOrder(I) }; | 299 I.getValOperand(), freezeMemoryOrder(I) }; |
| 299 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_rmw, | 300 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_rmw, |
| 300 PH.OriginalPET, PH.PET, Args); | 301 PH.OriginalPET, PH.PET, Args); |
| 301 } | 302 } |
| 302 | 303 |
| 303 /// %res = cmpxchg T* %ptr, T %old, T %new memory_order | 304 /// %res = cmpxchg T* %ptr, T %old, T %new memory_order |
| 304 /// becomes: | 305 /// becomes: |
| 305 /// %res = call T @llvm.nacl.atomic.cmpxchg.i<size>( | 306 /// %res = call T @llvm.nacl.atomic.cmpxchg.i<size>( |
| 306 /// %object, %expected, %desired, memory_order_success, | 307 /// %object, %expected, %desired, memory_order_success, |
| 307 /// memory_order_failure) | 308 /// memory_order_failure) |
| 308 void AtomicVisitor::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) { | 309 void AtomicVisitor::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) { |
| 309 PointerHelper<AtomicCmpXchgInst> PH(*this, I); | 310 PointerHelper<AtomicCmpXchgInst> PH(*this, I); |
| 310 checkSizeMatchesType(I, PH.BitSize, I.getCompareOperand()->getType()); | 311 checkSizeMatchesType(I, PH.BitSize, I.getCompareOperand()->getType()); |
| 311 checkSizeMatchesType(I, PH.BitSize, I.getNewValOperand()->getType()); | 312 checkSizeMatchesType(I, PH.BitSize, I.getNewValOperand()->getType()); |
| 312 // TODO LLVM currently doesn't support specifying separate memory | 313 // TODO LLVM currently doesn't support specifying separate memory |
| 313 // orders for compare exchange's success and failure cases: LLVM | 314 // orders for compare exchange's success and failure cases: LLVM |
| 314 // IR implicitly drops the Release part of the specified memory | 315 // IR implicitly drops the Release part of the specified memory |
| 315 // order on failure. | 316 // order on failure. |
| 316 Value *Args[] = { PH.P, I.getCompareOperand(), I.getNewValOperand(), | 317 Value *Args[] = { PH.P, I.getCompareOperand(), I.getNewValOperand(), |
| 317 freezeMemoryOrder(I), freezeMemoryOrder(I) }; | 318 freezeMemoryOrder(I), freezeMemoryOrder(I) }; |
| 318 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_cmpxchg, | 319 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_cmpxchg, |
| 319 PH.OriginalPET, PH.PET, Args); | 320 PH.OriginalPET, PH.PET, Args); |
| 320 } | 321 } |
| 321 | 322 |
| 322 /// fence memory_order | 323 /// fence memory_order |
| 323 /// becomes: | 324 /// becomes: |
| 324 /// call void @llvm.nacl.atomic.fence(memory_order) | 325 /// call void @llvm.nacl.atomic.fence(memory_order) |
| 326 /// and | |
| 327 /// call void asm sideeffect "", "~{memory}"() | |
| 328 /// fence seq_cst | |
| 329 /// call void asm sideeffect "", "~{memory}"() | |
| 330 /// becomes: | |
| 331 /// call void asm sideeffect "", "~{memory}"() | |
| 332 /// call void @llvm.nacl.atomic.fence.all() | |
|
jvoung (off chromium)
2013/08/07 19:38:40
Is it possible to just have this pass remove the a
JF
2013/08/07 19:55:21
Could be, but I'd rather be able to keep both inde
jvoung (off chromium)
2013/08/07 22:32:11
I guess we could also make clang generate the fenc
JF
2013/08/07 22:47:41
Done.
| |
| 333 /// call void asm sideeffect "", "~{memory}"() | |
| 325 void AtomicVisitor::visitFenceInst(FenceInst &I) { | 334 void AtomicVisitor::visitFenceInst(FenceInst &I) { |
| 326 Type *T = Type::getInt32Ty(C); // Fences aren't overloaded on type. | 335 Type *T = Type::getInt32Ty(C); // Fences aren't overloaded on type. |
| 327 Value *Args[] = { freezeMemoryOrder(I) }; | 336 BasicBlock::InstListType &IL(I.getParent()->getInstList()); |
| 328 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_fence, T, T, | 337 bool isFirst = IL.empty() || &*I.getParent()->getInstList().begin() == &I; |
| 329 Args); | 338 bool isLast = IL.empty() || &*I.getParent()->getInstList().rbegin() == &I; |
| 339 Instruction *PrevI = isFirst ? 0 : I.getPrevNode(); | |
| 340 Instruction *NextI = isLast ? 0 : I.getNextNode(); | |
| 341 | |
| 342 if (NaCl::isAsmMemory(PrevI) && NaCl::isAsmMemory(NextI) && | |
| 343 I.getOrdering() == SequentiallyConsistent) { | |
| 344 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_fence_all, T, | |
| 345 T, ArrayRef<Value *>()); | |
| 346 } else { | |
| 347 Value *Args[] = { freezeMemoryOrder(I) }; | |
| 348 replaceInstructionWithIntrinsicCall(I, Intrinsic::nacl_atomic_fence, T, T, | |
| 349 Args); | |
| 350 } | |
| 330 } | 351 } |
| 331 | 352 |
| 332 ModulePass *llvm::createRewriteAtomicsPass() { return new RewriteAtomics(); } | 353 ModulePass *llvm::createRewriteAtomicsPass() { return new RewriteAtomics(); } |
| OLD | NEW |