blob: 973ffe7e6a40f4a03dc15ac2cbadda2f93b614f0 [file] [log] [blame]
//===- LowerAtomic.cpp - Lower atomic intrinsics --------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass lowers atomic intrinsics to non-atomic form for use in a known
// non-preemptible environment.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "loweratomic"
#include "llvm/Transforms/Scalar.h"
#include "llvm/BasicBlock.h"
#include "llvm/Function.h"
#include "llvm/Instruction.h"
#include "llvm/Instructions.h"
#include "llvm/Intrinsics.h"
#include "llvm/Pass.h"
#include "llvm/Support/IRBuilder.h"
using namespace llvm;
namespace {
bool LowerAtomicIntrinsic(CallInst *CI) {
IRBuilder<> Builder(CI->getParent(), CI);
Function *Callee = CI->getCalledFunction();
if (!Callee)
return false;
unsigned IID = Callee->getIntrinsicID();
switch (IID) {
case Intrinsic::memory_barrier:
break;
case Intrinsic::atomic_load_add:
case Intrinsic::atomic_load_sub:
case Intrinsic::atomic_load_and:
case Intrinsic::atomic_load_nand:
case Intrinsic::atomic_load_or:
case Intrinsic::atomic_load_xor:
case Intrinsic::atomic_load_max:
case Intrinsic::atomic_load_min:
case Intrinsic::atomic_load_umax:
case Intrinsic::atomic_load_umin: {
Value *Ptr = CI->getArgOperand(0);
Value *Delta = CI->getArgOperand(1);
LoadInst *Orig = Builder.CreateLoad(Ptr);
Value *Res = NULL;
switch (IID) {
default: assert(0 && "Unrecognized atomic modify operation");
case Intrinsic::atomic_load_add:
Res = Builder.CreateAdd(Orig, Delta);
break;
case Intrinsic::atomic_load_sub:
Res = Builder.CreateSub(Orig, Delta);
break;
case Intrinsic::atomic_load_and:
Res = Builder.CreateAnd(Orig, Delta);
break;
case Intrinsic::atomic_load_nand:
Res = Builder.CreateNot(Builder.CreateAnd(Orig, Delta));
break;
case Intrinsic::atomic_load_or:
Res = Builder.CreateOr(Orig, Delta);
break;
case Intrinsic::atomic_load_xor:
Res = Builder.CreateXor(Orig, Delta);
break;
case Intrinsic::atomic_load_max:
Res = Builder.CreateSelect(Builder.CreateICmpSLT(Orig, Delta),
Delta,
Orig);
break;
case Intrinsic::atomic_load_min:
Res = Builder.CreateSelect(Builder.CreateICmpSLT(Orig, Delta),
Orig,
Delta);
break;
case Intrinsic::atomic_load_umax:
Res = Builder.CreateSelect(Builder.CreateICmpULT(Orig, Delta),
Delta,
Orig);
break;
case Intrinsic::atomic_load_umin:
Res = Builder.CreateSelect(Builder.CreateICmpULT(Orig, Delta),
Orig,
Delta);
break;
}
Builder.CreateStore(Res, Ptr);
CI->replaceAllUsesWith(Orig);
break;
}
case Intrinsic::atomic_swap: {
Value *Ptr = CI->getArgOperand(0);
Value *Val = CI->getArgOperand(1);
LoadInst *Orig = Builder.CreateLoad(Ptr);
Builder.CreateStore(Val, Ptr);
CI->replaceAllUsesWith(Orig);
break;
}
case Intrinsic::atomic_cmp_swap: {
Value *Ptr = CI->getArgOperand(0);
Value *Cmp = CI->getArgOperand(1);
Value *Val = CI->getArgOperand(2);
LoadInst *Orig = Builder.CreateLoad(Ptr);
Value *Equal = Builder.CreateICmpEQ(Orig, Cmp);
Value *Res = Builder.CreateSelect(Equal, Val, Orig);
Builder.CreateStore(Res, Ptr);
CI->replaceAllUsesWith(Orig);
break;
}
default:
return false;
}
assert(CI->use_empty() &&
"Lowering should have eliminated any uses of the intrinsic call!");
CI->eraseFromParent();
return true;
}
struct LowerAtomic : public BasicBlockPass {
static char ID;
LowerAtomic() : BasicBlockPass(ID) {}
bool runOnBasicBlock(BasicBlock &BB) {
bool Changed = false;
for (BasicBlock::iterator DI = BB.begin(), DE = BB.end(); DI != DE; ) {
Instruction *Inst = DI++;
if (CallInst *CI = dyn_cast<CallInst>(Inst))
Changed |= LowerAtomicIntrinsic(CI);
}
return Changed;
}
};
}
char LowerAtomic::ID = 0;
INITIALIZE_PASS(LowerAtomic, "loweratomic",
"Lower atomic intrinsics to non-atomic form",
false, false);
Pass *llvm::createLowerAtomicPass() { return new LowerAtomic(); }