1212793Sdim//===- LowerAtomic.cpp - Lower atomic intrinsics --------------------------===//
2212793Sdim//
3212793Sdim//                     The LLVM Compiler Infrastructure
4212793Sdim//
5212793Sdim// This file is distributed under the University of Illinois Open Source
6212793Sdim// License. See LICENSE.TXT for details.
7212793Sdim//
8212793Sdim//===----------------------------------------------------------------------===//
9212793Sdim//
10212793Sdim// This pass lowers atomic intrinsics to non-atomic form for use in a known
11212793Sdim// non-preemptible environment.
12212793Sdim//
13212793Sdim//===----------------------------------------------------------------------===//
14212793Sdim
15212793Sdim#define DEBUG_TYPE "loweratomic"
16212793Sdim#include "llvm/Transforms/Scalar.h"
17252723Sdim#include "llvm/IR/Function.h"
18252723Sdim#include "llvm/IR/IRBuilder.h"
19252723Sdim#include "llvm/IR/IntrinsicInst.h"
20212793Sdim#include "llvm/Pass.h"
21212793Sdimusing namespace llvm;
22212793Sdim
23226890Sdimstatic bool LowerAtomicCmpXchgInst(AtomicCmpXchgInst *CXI) {
24226890Sdim  IRBuilder<> Builder(CXI->getParent(), CXI);
25226890Sdim  Value *Ptr = CXI->getPointerOperand();
26226890Sdim  Value *Cmp = CXI->getCompareOperand();
27226890Sdim  Value *Val = CXI->getNewValOperand();
28245431Sdim
29226890Sdim  LoadInst *Orig = Builder.CreateLoad(Ptr);
30226890Sdim  Value *Equal = Builder.CreateICmpEQ(Orig, Cmp);
31226890Sdim  Value *Res = Builder.CreateSelect(Equal, Val, Orig);
32226890Sdim  Builder.CreateStore(Res, Ptr);
33245431Sdim
34226890Sdim  CXI->replaceAllUsesWith(Orig);
35226890Sdim  CXI->eraseFromParent();
36226890Sdim  return true;
37226890Sdim}
38212793Sdim
39226890Sdimstatic bool LowerAtomicRMWInst(AtomicRMWInst *RMWI) {
40226890Sdim  IRBuilder<> Builder(RMWI->getParent(), RMWI);
41226890Sdim  Value *Ptr = RMWI->getPointerOperand();
42226890Sdim  Value *Val = RMWI->getValOperand();
43212793Sdim
44226890Sdim  LoadInst *Orig = Builder.CreateLoad(Ptr);
45226890Sdim  Value *Res = NULL;
46212793Sdim
47226890Sdim  switch (RMWI->getOperation()) {
48226890Sdim  default: llvm_unreachable("Unexpected RMW operation");
49226890Sdim  case AtomicRMWInst::Xchg:
50226890Sdim    Res = Val;
51212793Sdim    break;
52226890Sdim  case AtomicRMWInst::Add:
53226890Sdim    Res = Builder.CreateAdd(Orig, Val);
54212793Sdim    break;
55226890Sdim  case AtomicRMWInst::Sub:
56226890Sdim    Res = Builder.CreateSub(Orig, Val);
57212793Sdim    break;
58226890Sdim  case AtomicRMWInst::And:
59226890Sdim    Res = Builder.CreateAnd(Orig, Val);
60226890Sdim    break;
61226890Sdim  case AtomicRMWInst::Nand:
62226890Sdim    Res = Builder.CreateNot(Builder.CreateAnd(Orig, Val));
63226890Sdim    break;
64226890Sdim  case AtomicRMWInst::Or:
65226890Sdim    Res = Builder.CreateOr(Orig, Val);
66226890Sdim    break;
67226890Sdim  case AtomicRMWInst::Xor:
68226890Sdim    Res = Builder.CreateXor(Orig, Val);
69226890Sdim    break;
70226890Sdim  case AtomicRMWInst::Max:
71226890Sdim    Res = Builder.CreateSelect(Builder.CreateICmpSLT(Orig, Val),
72226890Sdim                               Val, Orig);
73226890Sdim    break;
74226890Sdim  case AtomicRMWInst::Min:
75226890Sdim    Res = Builder.CreateSelect(Builder.CreateICmpSLT(Orig, Val),
76226890Sdim                               Orig, Val);
77226890Sdim    break;
78226890Sdim  case AtomicRMWInst::UMax:
79226890Sdim    Res = Builder.CreateSelect(Builder.CreateICmpULT(Orig, Val),
80226890Sdim                               Val, Orig);
81226890Sdim    break;
82226890Sdim  case AtomicRMWInst::UMin:
83226890Sdim    Res = Builder.CreateSelect(Builder.CreateICmpULT(Orig, Val),
84226890Sdim                               Orig, Val);
85226890Sdim    break;
86212793Sdim  }
87226890Sdim  Builder.CreateStore(Res, Ptr);
88226890Sdim  RMWI->replaceAllUsesWith(Orig);
89226890Sdim  RMWI->eraseFromParent();
90226890Sdim  return true;
91226890Sdim}
92212793Sdim
93226890Sdimstatic bool LowerFenceInst(FenceInst *FI) {
94226890Sdim  FI->eraseFromParent();
95226890Sdim  return true;
96226890Sdim}
97212793Sdim
98226890Sdimstatic bool LowerLoadInst(LoadInst *LI) {
99226890Sdim  LI->setAtomic(NotAtomic);
100226890Sdim  return true;
101226890Sdim}
102212793Sdim
103226890Sdimstatic bool LowerStoreInst(StoreInst *SI) {
104226890Sdim  SI->setAtomic(NotAtomic);
105212793Sdim  return true;
106212793Sdim}
107212793Sdim
108218893Sdimnamespace {
109218893Sdim  struct LowerAtomic : public BasicBlockPass {
110218893Sdim    static char ID;
111218893Sdim    LowerAtomic() : BasicBlockPass(ID) {
112218893Sdim      initializeLowerAtomicPass(*PassRegistry::getPassRegistry());
113212793Sdim    }
114218893Sdim    bool runOnBasicBlock(BasicBlock &BB) {
115218893Sdim      bool Changed = false;
116226890Sdim      for (BasicBlock::iterator DI = BB.begin(), DE = BB.end(); DI != DE; ) {
117226890Sdim        Instruction *Inst = DI++;
118226890Sdim        if (FenceInst *FI = dyn_cast<FenceInst>(Inst))
119226890Sdim          Changed |= LowerFenceInst(FI);
120226890Sdim        else if (AtomicCmpXchgInst *CXI = dyn_cast<AtomicCmpXchgInst>(Inst))
121226890Sdim          Changed |= LowerAtomicCmpXchgInst(CXI);
122226890Sdim        else if (AtomicRMWInst *RMWI = dyn_cast<AtomicRMWInst>(Inst))
123226890Sdim          Changed |= LowerAtomicRMWInst(RMWI);
124226890Sdim        else if (LoadInst *LI = dyn_cast<LoadInst>(Inst)) {
125226890Sdim          if (LI->isAtomic())
126226890Sdim            LowerLoadInst(LI);
127226890Sdim        } else if (StoreInst *SI = dyn_cast<StoreInst>(Inst)) {
128226890Sdim          if (SI->isAtomic())
129226890Sdim            LowerStoreInst(SI);
130226890Sdim        }
131226890Sdim      }
132218893Sdim      return Changed;
133218893Sdim    }
134218893Sdim  };
135212793Sdim}
136212793Sdim
137212793Sdimchar LowerAtomic::ID = 0;
138212793SdimINITIALIZE_PASS(LowerAtomic, "loweratomic",
139212793Sdim                "Lower atomic intrinsics to non-atomic form",
140218893Sdim                false, false)
141212793Sdim
142212793SdimPass *llvm::createLowerAtomicPass() { return new LowerAtomic(); }
143