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