xref: /freebsd/contrib/llvm-project/llvm/lib/Frontend/Atomic/Atomic.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1*700637cbSDimitry Andric //===--- Atomic.cpp - Codegen of atomic operations ------------------------===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric 
9*700637cbSDimitry Andric #include "llvm/Frontend/Atomic/Atomic.h"
10*700637cbSDimitry Andric #include "llvm/IR/DerivedTypes.h"
11*700637cbSDimitry Andric #include "llvm/IR/IRBuilder.h"
12*700637cbSDimitry Andric #include <utility>
13*700637cbSDimitry Andric 
14*700637cbSDimitry Andric using namespace llvm;
15*700637cbSDimitry Andric 
shouldCastToInt(Type * ValTy,bool CmpXchg)16*700637cbSDimitry Andric bool AtomicInfo::shouldCastToInt(Type *ValTy, bool CmpXchg) {
17*700637cbSDimitry Andric   if (ValTy->isFloatingPointTy())
18*700637cbSDimitry Andric     return ValTy->isX86_FP80Ty() || CmpXchg;
19*700637cbSDimitry Andric   return !ValTy->isIntegerTy() && !ValTy->isPointerTy();
20*700637cbSDimitry Andric }
21*700637cbSDimitry Andric 
EmitAtomicLoadOp(AtomicOrdering AO,bool IsVolatile,bool CmpXchg)22*700637cbSDimitry Andric Value *AtomicInfo::EmitAtomicLoadOp(AtomicOrdering AO, bool IsVolatile,
23*700637cbSDimitry Andric                                     bool CmpXchg) {
24*700637cbSDimitry Andric   Value *Ptr = getAtomicPointer();
25*700637cbSDimitry Andric   Type *AtomicTy = Ty;
26*700637cbSDimitry Andric   if (shouldCastToInt(Ty, CmpXchg))
27*700637cbSDimitry Andric     AtomicTy = IntegerType::get(getLLVMContext(), AtomicSizeInBits);
28*700637cbSDimitry Andric   LoadInst *Load =
29*700637cbSDimitry Andric       Builder->CreateAlignedLoad(AtomicTy, Ptr, AtomicAlign, "atomic-load");
30*700637cbSDimitry Andric   Load->setAtomic(AO);
31*700637cbSDimitry Andric   if (IsVolatile)
32*700637cbSDimitry Andric     Load->setVolatile(true);
33*700637cbSDimitry Andric   decorateWithTBAA(Load);
34*700637cbSDimitry Andric   return Load;
35*700637cbSDimitry Andric }
36*700637cbSDimitry Andric 
EmitAtomicLibcall(StringRef fnName,Type * ResultType,ArrayRef<Value * > Args)37*700637cbSDimitry Andric CallInst *AtomicInfo::EmitAtomicLibcall(StringRef fnName, Type *ResultType,
38*700637cbSDimitry Andric                                         ArrayRef<Value *> Args) {
39*700637cbSDimitry Andric   LLVMContext &ctx = Builder->getContext();
40*700637cbSDimitry Andric   SmallVector<Type *, 6> ArgTys;
41*700637cbSDimitry Andric   for (Value *Arg : Args)
42*700637cbSDimitry Andric     ArgTys.push_back(Arg->getType());
43*700637cbSDimitry Andric   FunctionType *FnType = FunctionType::get(ResultType, ArgTys, false);
44*700637cbSDimitry Andric   Module *M = Builder->GetInsertBlock()->getModule();
45*700637cbSDimitry Andric 
46*700637cbSDimitry Andric   // TODO: Use llvm::TargetLowering for Libcall ABI
47*700637cbSDimitry Andric   AttrBuilder fnAttrBuilder(ctx);
48*700637cbSDimitry Andric   fnAttrBuilder.addAttribute(Attribute::NoUnwind);
49*700637cbSDimitry Andric   fnAttrBuilder.addAttribute(Attribute::WillReturn);
50*700637cbSDimitry Andric   AttributeList fnAttrs =
51*700637cbSDimitry Andric       AttributeList::get(ctx, AttributeList::FunctionIndex, fnAttrBuilder);
52*700637cbSDimitry Andric   FunctionCallee LibcallFn = M->getOrInsertFunction(fnName, FnType, fnAttrs);
53*700637cbSDimitry Andric   CallInst *Call = Builder->CreateCall(LibcallFn, Args);
54*700637cbSDimitry Andric   return Call;
55*700637cbSDimitry Andric }
56*700637cbSDimitry Andric 
EmitAtomicCompareExchangeLibcall(Value * ExpectedVal,Value * DesiredVal,AtomicOrdering Success,AtomicOrdering Failure)57*700637cbSDimitry Andric std::pair<Value *, Value *> AtomicInfo::EmitAtomicCompareExchangeLibcall(
58*700637cbSDimitry Andric     Value *ExpectedVal, Value *DesiredVal, AtomicOrdering Success,
59*700637cbSDimitry Andric     AtomicOrdering Failure) {
60*700637cbSDimitry Andric   LLVMContext &ctx = getLLVMContext();
61*700637cbSDimitry Andric 
62*700637cbSDimitry Andric   // __atomic_compare_exchange's expected and desired are passed by pointers
63*700637cbSDimitry Andric   // FIXME: types
64*700637cbSDimitry Andric 
65*700637cbSDimitry Andric   // TODO: Get from llvm::TargetMachine / clang::TargetInfo
66*700637cbSDimitry Andric   // if clang shares this codegen in future
67*700637cbSDimitry Andric   constexpr uint64_t IntBits = 32;
68*700637cbSDimitry Andric 
69*700637cbSDimitry Andric   // bool __atomic_compare_exchange(size_t size, void *obj, void *expected,
70*700637cbSDimitry Andric   //  void *desired, int success, int failure);
71*700637cbSDimitry Andric 
72*700637cbSDimitry Andric   Value *Args[6] = {
73*700637cbSDimitry Andric       getAtomicSizeValue(),
74*700637cbSDimitry Andric       getAtomicPointer(),
75*700637cbSDimitry Andric       ExpectedVal,
76*700637cbSDimitry Andric       DesiredVal,
77*700637cbSDimitry Andric       Constant::getIntegerValue(IntegerType::get(ctx, IntBits),
78*700637cbSDimitry Andric                                 APInt(IntBits, static_cast<uint64_t>(Success),
79*700637cbSDimitry Andric                                       /*signed=*/true)),
80*700637cbSDimitry Andric       Constant::getIntegerValue(IntegerType::get(ctx, IntBits),
81*700637cbSDimitry Andric                                 APInt(IntBits, static_cast<uint64_t>(Failure),
82*700637cbSDimitry Andric                                       /*signed=*/true)),
83*700637cbSDimitry Andric   };
84*700637cbSDimitry Andric   auto Result = EmitAtomicLibcall("__atomic_compare_exchange",
85*700637cbSDimitry Andric                                   IntegerType::getInt1Ty(ctx), Args);
86*700637cbSDimitry Andric   return std::make_pair(ExpectedVal, Result);
87*700637cbSDimitry Andric }
88*700637cbSDimitry Andric 
EmitAtomicCompareExchangeOp(Value * ExpectedVal,Value * DesiredVal,AtomicOrdering Success,AtomicOrdering Failure,bool IsVolatile,bool IsWeak)89*700637cbSDimitry Andric std::pair<Value *, Value *> AtomicInfo::EmitAtomicCompareExchangeOp(
90*700637cbSDimitry Andric     Value *ExpectedVal, Value *DesiredVal, AtomicOrdering Success,
91*700637cbSDimitry Andric     AtomicOrdering Failure, bool IsVolatile, bool IsWeak) {
92*700637cbSDimitry Andric   // Do the atomic store.
93*700637cbSDimitry Andric   Value *Addr = getAtomicAddressAsAtomicIntPointer();
94*700637cbSDimitry Andric   auto *Inst = Builder->CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal,
95*700637cbSDimitry Andric                                             getAtomicAlignment(), Success,
96*700637cbSDimitry Andric                                             Failure, SyncScope::System);
97*700637cbSDimitry Andric 
98*700637cbSDimitry Andric   // Other decoration.
99*700637cbSDimitry Andric   Inst->setVolatile(IsVolatile);
100*700637cbSDimitry Andric   Inst->setWeak(IsWeak);
101*700637cbSDimitry Andric   auto *PreviousVal = Builder->CreateExtractValue(Inst, /*Idxs=*/0);
102*700637cbSDimitry Andric   auto *SuccessFailureVal = Builder->CreateExtractValue(Inst, /*Idxs=*/1);
103*700637cbSDimitry Andric   return std::make_pair(PreviousVal, SuccessFailureVal);
104*700637cbSDimitry Andric }
105*700637cbSDimitry Andric 
106*700637cbSDimitry Andric std::pair<LoadInst *, AllocaInst *>
EmitAtomicLoadLibcall(AtomicOrdering AO)107*700637cbSDimitry Andric AtomicInfo::EmitAtomicLoadLibcall(AtomicOrdering AO) {
108*700637cbSDimitry Andric   LLVMContext &Ctx = getLLVMContext();
109*700637cbSDimitry Andric   Type *SizedIntTy = Type::getIntNTy(Ctx, getAtomicSizeInBits());
110*700637cbSDimitry Andric   Type *ResultTy;
111*700637cbSDimitry Andric   SmallVector<Value *, 6> Args;
112*700637cbSDimitry Andric   AttributeList Attr;
113*700637cbSDimitry Andric   Module *M = Builder->GetInsertBlock()->getModule();
114*700637cbSDimitry Andric   const DataLayout &DL = M->getDataLayout();
115*700637cbSDimitry Andric   Args.push_back(
116*700637cbSDimitry Andric       ConstantInt::get(DL.getIntPtrType(Ctx), this->getAtomicSizeInBits() / 8));
117*700637cbSDimitry Andric 
118*700637cbSDimitry Andric   Value *PtrVal = getAtomicPointer();
119*700637cbSDimitry Andric   PtrVal = Builder->CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
120*700637cbSDimitry Andric   Args.push_back(PtrVal);
121*700637cbSDimitry Andric 
122*700637cbSDimitry Andric   auto CurrentIP = Builder->saveIP();
123*700637cbSDimitry Andric   Builder->restoreIP(AllocaIP);
124*700637cbSDimitry Andric   AllocaInst *AllocaResult =
125*700637cbSDimitry Andric       CreateAlloca(Ty, getAtomicPointer()->getName() + "atomic.temp.load");
126*700637cbSDimitry Andric   Builder->restoreIP(CurrentIP);
127*700637cbSDimitry Andric   const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
128*700637cbSDimitry Andric   AllocaResult->setAlignment(AllocaAlignment);
129*700637cbSDimitry Andric   Args.push_back(AllocaResult);
130*700637cbSDimitry Andric   Constant *OrderingVal =
131*700637cbSDimitry Andric       ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(AO));
132*700637cbSDimitry Andric   Args.push_back(OrderingVal);
133*700637cbSDimitry Andric 
134*700637cbSDimitry Andric   ResultTy = Type::getVoidTy(Ctx);
135*700637cbSDimitry Andric   SmallVector<Type *, 6> ArgTys;
136*700637cbSDimitry Andric   for (Value *Arg : Args)
137*700637cbSDimitry Andric     ArgTys.push_back(Arg->getType());
138*700637cbSDimitry Andric   FunctionType *FnType = FunctionType::get(ResultTy, ArgTys, false);
139*700637cbSDimitry Andric   FunctionCallee LibcallFn =
140*700637cbSDimitry Andric       M->getOrInsertFunction("__atomic_load", FnType, Attr);
141*700637cbSDimitry Andric   CallInst *Call = Builder->CreateCall(LibcallFn, Args);
142*700637cbSDimitry Andric   Call->setAttributes(Attr);
143*700637cbSDimitry Andric   return std::make_pair(
144*700637cbSDimitry Andric       Builder->CreateAlignedLoad(Ty, AllocaResult, AllocaAlignment),
145*700637cbSDimitry Andric       AllocaResult);
146*700637cbSDimitry Andric }
147*700637cbSDimitry Andric 
EmitAtomicStoreLibcall(AtomicOrdering AO,Value * Source)148*700637cbSDimitry Andric void AtomicInfo::EmitAtomicStoreLibcall(AtomicOrdering AO, Value *Source) {
149*700637cbSDimitry Andric   LLVMContext &Ctx = getLLVMContext();
150*700637cbSDimitry Andric   SmallVector<Value *, 6> Args;
151*700637cbSDimitry Andric   AttributeList Attr;
152*700637cbSDimitry Andric   Module *M = Builder->GetInsertBlock()->getModule();
153*700637cbSDimitry Andric   const DataLayout &DL = M->getDataLayout();
154*700637cbSDimitry Andric   Args.push_back(
155*700637cbSDimitry Andric       ConstantInt::get(DL.getIntPtrType(Ctx), this->getAtomicSizeInBits() / 8));
156*700637cbSDimitry Andric 
157*700637cbSDimitry Andric   Value *PtrVal = getAtomicPointer();
158*700637cbSDimitry Andric   PtrVal = Builder->CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
159*700637cbSDimitry Andric   Args.push_back(PtrVal);
160*700637cbSDimitry Andric 
161*700637cbSDimitry Andric   auto CurrentIP = Builder->saveIP();
162*700637cbSDimitry Andric   Builder->restoreIP(AllocaIP);
163*700637cbSDimitry Andric   Value *SourceAlloca = Builder->CreateAlloca(Source->getType());
164*700637cbSDimitry Andric   Builder->restoreIP(CurrentIP);
165*700637cbSDimitry Andric   Builder->CreateStore(Source, SourceAlloca);
166*700637cbSDimitry Andric   SourceAlloca = Builder->CreatePointerBitCastOrAddrSpaceCast(
167*700637cbSDimitry Andric       SourceAlloca, Builder->getPtrTy());
168*700637cbSDimitry Andric   Args.push_back(SourceAlloca);
169*700637cbSDimitry Andric 
170*700637cbSDimitry Andric   Constant *OrderingVal =
171*700637cbSDimitry Andric       ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(AO));
172*700637cbSDimitry Andric   Args.push_back(OrderingVal);
173*700637cbSDimitry Andric 
174*700637cbSDimitry Andric   SmallVector<Type *, 6> ArgTys;
175*700637cbSDimitry Andric   for (Value *Arg : Args)
176*700637cbSDimitry Andric     ArgTys.push_back(Arg->getType());
177*700637cbSDimitry Andric   FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), ArgTys, false);
178*700637cbSDimitry Andric   FunctionCallee LibcallFn =
179*700637cbSDimitry Andric       M->getOrInsertFunction("__atomic_store", FnType, Attr);
180*700637cbSDimitry Andric   CallInst *Call = Builder->CreateCall(LibcallFn, Args);
181*700637cbSDimitry Andric   Call->setAttributes(Attr);
182*700637cbSDimitry Andric }
183*700637cbSDimitry Andric 
EmitAtomicCompareExchange(Value * ExpectedVal,Value * DesiredVal,AtomicOrdering Success,AtomicOrdering Failure,bool IsVolatile,bool IsWeak)184*700637cbSDimitry Andric std::pair<Value *, Value *> AtomicInfo::EmitAtomicCompareExchange(
185*700637cbSDimitry Andric     Value *ExpectedVal, Value *DesiredVal, AtomicOrdering Success,
186*700637cbSDimitry Andric     AtomicOrdering Failure, bool IsVolatile, bool IsWeak) {
187*700637cbSDimitry Andric   if (shouldUseLibcall())
188*700637cbSDimitry Andric     return EmitAtomicCompareExchangeLibcall(ExpectedVal, DesiredVal, Success,
189*700637cbSDimitry Andric                                             Failure);
190*700637cbSDimitry Andric 
191*700637cbSDimitry Andric   auto Res = EmitAtomicCompareExchangeOp(ExpectedVal, DesiredVal, Success,
192*700637cbSDimitry Andric                                          Failure, IsVolatile, IsWeak);
193*700637cbSDimitry Andric   return Res;
194*700637cbSDimitry Andric }
195