
This PR adds functionality for `__atomic_store` libcall in AtomicInfo. This allows for supporting complex types in `atomic write`. Fixes https://github.com/llvm/llvm-project/issues/113479 Fixes https://github.com/llvm/llvm-project/issues/115652
195 lines
7.4 KiB
C++
195 lines
7.4 KiB
C++
//===--- Atomic.cpp - Codegen of atomic operations ------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Frontend/Atomic/Atomic.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include <utility>
|
|
|
|
using namespace llvm;
|
|
|
|
bool AtomicInfo::shouldCastToInt(Type *ValTy, bool CmpXchg) {
|
|
if (ValTy->isFloatingPointTy())
|
|
return ValTy->isX86_FP80Ty() || CmpXchg;
|
|
return !ValTy->isIntegerTy() && !ValTy->isPointerTy();
|
|
}
|
|
|
|
Value *AtomicInfo::EmitAtomicLoadOp(AtomicOrdering AO, bool IsVolatile,
|
|
bool CmpXchg) {
|
|
Value *Ptr = getAtomicPointer();
|
|
Type *AtomicTy = Ty;
|
|
if (shouldCastToInt(Ty, CmpXchg))
|
|
AtomicTy = IntegerType::get(getLLVMContext(), AtomicSizeInBits);
|
|
LoadInst *Load =
|
|
Builder->CreateAlignedLoad(AtomicTy, Ptr, AtomicAlign, "atomic-load");
|
|
Load->setAtomic(AO);
|
|
if (IsVolatile)
|
|
Load->setVolatile(true);
|
|
decorateWithTBAA(Load);
|
|
return Load;
|
|
}
|
|
|
|
CallInst *AtomicInfo::EmitAtomicLibcall(StringRef fnName, Type *ResultType,
|
|
ArrayRef<Value *> Args) {
|
|
LLVMContext &ctx = Builder->getContext();
|
|
SmallVector<Type *, 6> ArgTys;
|
|
for (Value *Arg : Args)
|
|
ArgTys.push_back(Arg->getType());
|
|
FunctionType *FnType = FunctionType::get(ResultType, ArgTys, false);
|
|
Module *M = Builder->GetInsertBlock()->getModule();
|
|
|
|
// TODO: Use llvm::TargetLowering for Libcall ABI
|
|
AttrBuilder fnAttrBuilder(ctx);
|
|
fnAttrBuilder.addAttribute(Attribute::NoUnwind);
|
|
fnAttrBuilder.addAttribute(Attribute::WillReturn);
|
|
AttributeList fnAttrs =
|
|
AttributeList::get(ctx, AttributeList::FunctionIndex, fnAttrBuilder);
|
|
FunctionCallee LibcallFn = M->getOrInsertFunction(fnName, FnType, fnAttrs);
|
|
CallInst *Call = Builder->CreateCall(LibcallFn, Args);
|
|
return Call;
|
|
}
|
|
|
|
std::pair<Value *, Value *> AtomicInfo::EmitAtomicCompareExchangeLibcall(
|
|
Value *ExpectedVal, Value *DesiredVal, AtomicOrdering Success,
|
|
AtomicOrdering Failure) {
|
|
LLVMContext &ctx = getLLVMContext();
|
|
|
|
// __atomic_compare_exchange's expected and desired are passed by pointers
|
|
// FIXME: types
|
|
|
|
// TODO: Get from llvm::TargetMachine / clang::TargetInfo
|
|
// if clang shares this codegen in future
|
|
constexpr uint64_t IntBits = 32;
|
|
|
|
// bool __atomic_compare_exchange(size_t size, void *obj, void *expected,
|
|
// void *desired, int success, int failure);
|
|
|
|
Value *Args[6] = {
|
|
getAtomicSizeValue(),
|
|
getAtomicPointer(),
|
|
ExpectedVal,
|
|
DesiredVal,
|
|
Constant::getIntegerValue(IntegerType::get(ctx, IntBits),
|
|
APInt(IntBits, static_cast<uint64_t>(Success),
|
|
/*signed=*/true)),
|
|
Constant::getIntegerValue(IntegerType::get(ctx, IntBits),
|
|
APInt(IntBits, static_cast<uint64_t>(Failure),
|
|
/*signed=*/true)),
|
|
};
|
|
auto Result = EmitAtomicLibcall("__atomic_compare_exchange",
|
|
IntegerType::getInt1Ty(ctx), Args);
|
|
return std::make_pair(ExpectedVal, Result);
|
|
}
|
|
|
|
std::pair<Value *, Value *> AtomicInfo::EmitAtomicCompareExchangeOp(
|
|
Value *ExpectedVal, Value *DesiredVal, AtomicOrdering Success,
|
|
AtomicOrdering Failure, bool IsVolatile, bool IsWeak) {
|
|
// Do the atomic store.
|
|
Value *Addr = getAtomicAddressAsAtomicIntPointer();
|
|
auto *Inst = Builder->CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal,
|
|
getAtomicAlignment(), Success,
|
|
Failure, SyncScope::System);
|
|
|
|
// Other decoration.
|
|
Inst->setVolatile(IsVolatile);
|
|
Inst->setWeak(IsWeak);
|
|
auto *PreviousVal = Builder->CreateExtractValue(Inst, /*Idxs=*/0);
|
|
auto *SuccessFailureVal = Builder->CreateExtractValue(Inst, /*Idxs=*/1);
|
|
return std::make_pair(PreviousVal, SuccessFailureVal);
|
|
}
|
|
|
|
std::pair<LoadInst *, AllocaInst *>
|
|
AtomicInfo::EmitAtomicLoadLibcall(AtomicOrdering AO) {
|
|
LLVMContext &Ctx = getLLVMContext();
|
|
Type *SizedIntTy = Type::getIntNTy(Ctx, getAtomicSizeInBits());
|
|
Type *ResultTy;
|
|
SmallVector<Value *, 6> Args;
|
|
AttributeList Attr;
|
|
Module *M = Builder->GetInsertBlock()->getModule();
|
|
const DataLayout &DL = M->getDataLayout();
|
|
Args.push_back(
|
|
ConstantInt::get(DL.getIntPtrType(Ctx), this->getAtomicSizeInBits() / 8));
|
|
|
|
Value *PtrVal = getAtomicPointer();
|
|
PtrVal = Builder->CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
|
|
Args.push_back(PtrVal);
|
|
|
|
auto CurrentIP = Builder->saveIP();
|
|
Builder->restoreIP(AllocaIP);
|
|
AllocaInst *AllocaResult =
|
|
CreateAlloca(Ty, getAtomicPointer()->getName() + "atomic.temp.load");
|
|
Builder->restoreIP(CurrentIP);
|
|
const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
|
|
AllocaResult->setAlignment(AllocaAlignment);
|
|
Args.push_back(AllocaResult);
|
|
Constant *OrderingVal =
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(AO));
|
|
Args.push_back(OrderingVal);
|
|
|
|
ResultTy = Type::getVoidTy(Ctx);
|
|
SmallVector<Type *, 6> ArgTys;
|
|
for (Value *Arg : Args)
|
|
ArgTys.push_back(Arg->getType());
|
|
FunctionType *FnType = FunctionType::get(ResultTy, ArgTys, false);
|
|
FunctionCallee LibcallFn =
|
|
M->getOrInsertFunction("__atomic_load", FnType, Attr);
|
|
CallInst *Call = Builder->CreateCall(LibcallFn, Args);
|
|
Call->setAttributes(Attr);
|
|
return std::make_pair(
|
|
Builder->CreateAlignedLoad(Ty, AllocaResult, AllocaAlignment),
|
|
AllocaResult);
|
|
}
|
|
|
|
void AtomicInfo::EmitAtomicStoreLibcall(AtomicOrdering AO, Value *Source) {
|
|
LLVMContext &Ctx = getLLVMContext();
|
|
SmallVector<Value *, 6> Args;
|
|
AttributeList Attr;
|
|
Module *M = Builder->GetInsertBlock()->getModule();
|
|
const DataLayout &DL = M->getDataLayout();
|
|
Args.push_back(
|
|
ConstantInt::get(DL.getIntPtrType(Ctx), this->getAtomicSizeInBits() / 8));
|
|
|
|
Value *PtrVal = getAtomicPointer();
|
|
PtrVal = Builder->CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
|
|
Args.push_back(PtrVal);
|
|
|
|
auto CurrentIP = Builder->saveIP();
|
|
Builder->restoreIP(AllocaIP);
|
|
Value *SourceAlloca = Builder->CreateAlloca(Source->getType());
|
|
Builder->restoreIP(CurrentIP);
|
|
Builder->CreateStore(Source, SourceAlloca);
|
|
SourceAlloca = Builder->CreatePointerBitCastOrAddrSpaceCast(
|
|
SourceAlloca, Builder->getPtrTy());
|
|
Args.push_back(SourceAlloca);
|
|
|
|
Constant *OrderingVal =
|
|
ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(AO));
|
|
Args.push_back(OrderingVal);
|
|
|
|
SmallVector<Type *, 6> ArgTys;
|
|
for (Value *Arg : Args)
|
|
ArgTys.push_back(Arg->getType());
|
|
FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), ArgTys, false);
|
|
FunctionCallee LibcallFn =
|
|
M->getOrInsertFunction("__atomic_store", FnType, Attr);
|
|
CallInst *Call = Builder->CreateCall(LibcallFn, Args);
|
|
Call->setAttributes(Attr);
|
|
}
|
|
|
|
std::pair<Value *, Value *> AtomicInfo::EmitAtomicCompareExchange(
|
|
Value *ExpectedVal, Value *DesiredVal, AtomicOrdering Success,
|
|
AtomicOrdering Failure, bool IsVolatile, bool IsWeak) {
|
|
if (shouldUseLibcall())
|
|
return EmitAtomicCompareExchangeLibcall(ExpectedVal, DesiredVal, Success,
|
|
Failure);
|
|
|
|
auto Res = EmitAtomicCompareExchangeOp(ExpectedVal, DesiredVal, Success,
|
|
Failure, IsVolatile, IsWeak);
|
|
return Res;
|
|
}
|