Skip to content

Commit 6423811

Browse files
committed
[clang][flang][OpenMPIRBuilder] Emit __atomic_load and __atomic_compare_exchange libcalls for complex types in atomic update
1 parent f84056c commit 6423811

File tree

8 files changed

+701
-354
lines changed

8 files changed

+701
-354
lines changed

clang/lib/CodeGen/CGAtomic.cpp

Lines changed: 319 additions & 354 deletions
Large diffs are not rendered by default.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
!===----------------------------------------------------------------------===!
2+
! This directory can be used to add Integration tests involving multiple
3+
! stages of the compiler (for eg. from Fortran to LLVM IR). It should not
4+
! contain executable tests. We should only add tests here sparingly and only
5+
! if there is no other way to test. Repeat this message in each test that is
6+
! added to this directory and sub-directories.
7+
!===----------------------------------------------------------------------===!
8+
9+
!RUN: %flang_fc1 -emit-llvm -fopenmp %s -o - | FileCheck %s
10+
11+
!CHECK: define void @_QQmain() {
12+
!CHECK: %[[X_NEW_VAL:.*]] = alloca { float, float }, align 8
13+
!CHECK: {{.*}} = alloca { float, float }, i64 1, align 8
14+
!CHECK: %[[ORIG_VAL:.*]] = alloca { float, float }, i64 1, align 8
15+
!CHECK: store { float, float } { float 2.000000e+00, float 2.000000e+00 }, ptr %[[ORIG_VAL]], align 4
16+
!CHECK: br label %entry
17+
18+
!CHECK: entry:
19+
!CHECK: %[[ATOMIC_TEMP_LOAD:.*]] = alloca { float, float }, align 16
20+
!CHECK: call void @__atomic_load(i64 8, ptr %[[ORIG_VAL]], ptr %[[ATOMIC_TEMP_LOAD]], i32 0)
21+
!CHECK: %[[PHI_NODE_ENTRY_1:.*]] = load { float, float }, ptr %[[ATOMIC_TEMP_LOAD]], align 16
22+
!CHECK: br label %.atomic.cont
23+
24+
!CHECK: .atomic.cont
25+
!CHECK: %[[VAL_4:.*]] = phi { float, float } [ %[[PHI_NODE_ENTRY_1]], %entry ], [ %{{.*}}, %.atomic.cont ]
26+
!CHECK: %[[VAL_5:.*]] = extractvalue { float, float } %[[VAL_4]], 0
27+
!CHECK: %[[VAL_6:.*]] = extractvalue { float, float } %[[VAL_4]], 1
28+
!CHECK: %[[VAL_7:.*]] = fadd contract float %[[VAL_5]], 1.000000e+00
29+
!CHECK: %[[VAL_8:.*]] = fadd contract float %[[VAL_6]], 1.000000e+00
30+
!CHECK: %[[VAL_9:.*]] = insertvalue { float, float } undef, float %[[VAL_7]], 0
31+
!CHECK: %[[VAL_10:.*]] = insertvalue { float, float } %[[VAL_9]], float %[[VAL_8]], 1
32+
!CHECK: store { float, float } %[[VAL_10]], ptr %[[X_NEW_VAL]], align 4
33+
!CHECK: %[[VAL_11:.*]] = call i1 @__atomic_compare_exchange(i64 8, ptr %[[ORIG_VAL]], ptr %[[ATOMIC_TEMP_LOAD]], ptr %[[X_NEW_VAL]], i32 2, i32 2)
34+
!CHECK: %[[VAL_12:.*]] = load { float, float }, ptr %[[ATOMIC_TEMP_LOAD]], align 4
35+
!CHECK: br i1 %[[VAL_11]], label %.atomic.exit, label %.atomic.cont
36+
program main
37+
complex*8 ia, ib
38+
ia = (2, 2)
39+
!$omp atomic update
40+
ia = ia + (1, 1)
41+
!$omp end atomic
42+
end program
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
//===--- Atomic.h - Shared 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+
#ifndef LLVM_FRONTEND_ATOMIC_ATOMIC_H
10+
#define LLVM_FRONTEND_ATOMIC_ATOMIC_H
11+
12+
#include "llvm/ADT/DenseMap.h"
13+
#include "llvm/CodeGen/RuntimeLibcalls.h"
14+
#include "llvm/IR/DataLayout.h"
15+
#include "llvm/IR/Instructions.h"
16+
#include "llvm/IR/Intrinsics.h"
17+
#include "llvm/IR/Module.h"
18+
#include "llvm/IR/Operator.h"
19+
20+
namespace llvm {
21+
22+
template <typename IRBuilderTy> struct AtomicInfo {
23+
IRBuilderTy *Builder;
24+
25+
Type *Ty;
26+
uint64_t AtomicSizeInBits;
27+
uint64_t ValueSizeInBits;
28+
llvm::Align AtomicAlign;
29+
llvm::Align ValueAlign;
30+
bool UseLibcall;
31+
32+
public:
33+
AtomicInfo(IRBuilderTy *Builder, Type *Ty, uint64_t AtomicSizeInBits,
34+
uint64_t ValueSizeInBits, llvm::Align AtomicAlign,
35+
llvm::Align ValueAlign, bool UseLibcall)
36+
: Builder(Builder), Ty(Ty), AtomicSizeInBits(AtomicSizeInBits),
37+
ValueSizeInBits(ValueSizeInBits), AtomicAlign(AtomicAlign),
38+
ValueAlign(ValueAlign), UseLibcall(UseLibcall) {}
39+
40+
virtual ~AtomicInfo() = default;
41+
42+
llvm::Align getAtomicAlignment() const { return AtomicAlign; }
43+
uint64_t getAtomicSizeInBits() const { return AtomicSizeInBits; }
44+
uint64_t getValueSizeInBits() const { return ValueSizeInBits; }
45+
bool shouldUseLibcall() const { return UseLibcall; }
46+
llvm::Type *getAtomicTy() const { return Ty; }
47+
48+
virtual llvm::Value *getAtomicPointer() const = 0;
49+
virtual void decorateWithTBAA(Instruction *I) {}
50+
virtual llvm::AllocaInst *CreateAlloca(llvm::Type *Ty,
51+
const llvm::Twine &Name) = 0;
52+
53+
/// Is the atomic size larger than the underlying value type?
54+
///
55+
/// Note that the absence of padding does not mean that atomic
56+
/// objects are completely interchangeable with non-atomic
57+
/// objects: we might have promoted the alignment of a type
58+
/// without making it bigger.
59+
bool hasPadding() const { return (ValueSizeInBits != AtomicSizeInBits); }
60+
61+
LLVMContext &getLLVMContext() const { return Builder->getContext(); }
62+
63+
static bool shouldCastToInt(llvm::Type *ValTy, bool CmpXchg) {
64+
if (ValTy->isFloatingPointTy())
65+
return ValTy->isX86_FP80Ty() || CmpXchg;
66+
return !ValTy->isIntegerTy() && !ValTy->isPointerTy();
67+
}
68+
69+
llvm::Value *EmitAtomicLoadOp(llvm::AtomicOrdering AO, bool IsVolatile,
70+
bool CmpXchg = false) {
71+
Value *Ptr = getAtomicPointer();
72+
Type *AtomicTy = Ty;
73+
if (shouldCastToInt(Ty, CmpXchg))
74+
AtomicTy = llvm::IntegerType::get(getLLVMContext(), AtomicSizeInBits);
75+
LoadInst *Load =
76+
Builder->CreateAlignedLoad(AtomicTy, Ptr, AtomicAlign, "atomic-load");
77+
Load->setAtomic(AO);
78+
79+
// Other decoration.
80+
if (IsVolatile)
81+
Load->setVolatile(true);
82+
decorateWithTBAA(Load);
83+
return Load;
84+
}
85+
86+
static CallInst *emitAtomicLibcalls2(IRBuilderTy *Builder, StringRef fnName,
87+
Type *ResultType,
88+
ArrayRef<Value *> Args) {
89+
auto &C = Builder->getContext();
90+
91+
SmallVector<Type *, 6> ArgTys;
92+
for (Value *Arg : Args)
93+
ArgTys.push_back(Arg->getType());
94+
FunctionType *FnType = FunctionType::get(ResultType, ArgTys, false);
95+
Module *M = Builder->GetInsertBlock()->getModule();
96+
97+
// TODO: Use llvm::TargetLowering for Libcall ABI
98+
llvm::AttrBuilder fnAttrB(C);
99+
fnAttrB.addAttribute(llvm::Attribute::NoUnwind);
100+
fnAttrB.addAttribute(llvm::Attribute::WillReturn);
101+
llvm::AttributeList fnAttrs = llvm::AttributeList::get(
102+
C, llvm::AttributeList::FunctionIndex, fnAttrB);
103+
FunctionCallee LibcallFn = M->getOrInsertFunction(fnName, FnType, fnAttrs);
104+
CallInst *Call = Builder->CreateCall(LibcallFn, Args);
105+
106+
return Call;
107+
}
108+
109+
llvm::Value *getAtomicSizeValue() const {
110+
auto &C = getLLVMContext();
111+
112+
// TODO: Get from llvm::TargetMachine / clang::TargetInfo
113+
uint16_t SizeTBits = 64;
114+
uint16_t BitsPerByte = 8;
115+
return llvm::ConstantInt::get(llvm::IntegerType::get(C, SizeTBits),
116+
AtomicSizeInBits / BitsPerByte);
117+
}
118+
119+
std::pair<llvm::Value *, llvm::Value *> EmitAtomicCompareExchangeLibcall(
120+
llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
121+
llvm::AtomicOrdering Success, llvm::AtomicOrdering Failure) {
122+
auto &C = getLLVMContext();
123+
124+
// __atomic_compare_exchange's expected and desired are passed by pointers
125+
// FIXME: types
126+
127+
// TODO: Get from llvm::TargetMachine / clang::TargetInfo
128+
uint64_t IntBits = 32;
129+
130+
// bool __atomic_compare_exchange(size_t size, void *obj, void *expected,
131+
// void *desired, int success, int failure);
132+
llvm::Value *Args[6] = {
133+
getAtomicSizeValue(),
134+
getAtomicPointer(),
135+
ExpectedVal,
136+
DesiredVal,
137+
llvm::Constant::getIntegerValue(
138+
llvm::IntegerType::get(C, IntBits),
139+
llvm::APInt(IntBits, (uint64_t)Success, /*signed=*/true)),
140+
llvm::Constant::getIntegerValue(
141+
llvm::IntegerType::get(C, IntBits),
142+
llvm::APInt(IntBits, (uint64_t)Failure, /*signed=*/true)),
143+
};
144+
auto Res = emitAtomicLibcalls2(Builder, "__atomic_compare_exchange",
145+
llvm::IntegerType::getInt1Ty(C), Args);
146+
return std::make_pair(ExpectedVal, Res);
147+
}
148+
149+
Value *castToAtomicIntPointer(Value *addr) const {
150+
return addr; // opaque pointer
151+
}
152+
153+
Value *getAtomicAddressAsAtomicIntPointer() const {
154+
return castToAtomicIntPointer(getAtomicPointer());
155+
}
156+
157+
std::pair<llvm::Value *, llvm::Value *>
158+
EmitAtomicCompareExchangeOp(llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
159+
llvm::AtomicOrdering Success,
160+
llvm::AtomicOrdering Failure,
161+
bool IsVolatile = false, bool IsWeak = false) {
162+
// Do the atomic store.
163+
Value *Addr = getAtomicAddressAsAtomicIntPointer();
164+
auto *Inst = Builder->CreateAtomicCmpXchg(Addr, ExpectedVal, DesiredVal,
165+
getAtomicAlignment(), Success,
166+
Failure, llvm::SyncScope::System);
167+
// Other decoration.
168+
Inst->setVolatile(IsVolatile);
169+
Inst->setWeak(IsWeak);
170+
171+
// Okay, turn that back into the original value type.
172+
auto *PreviousVal = Builder->CreateExtractValue(Inst, /*Idxs=*/0);
173+
auto *SuccessFailureVal = Builder->CreateExtractValue(Inst, /*Idxs=*/1);
174+
return std::make_pair(PreviousVal, SuccessFailureVal);
175+
}
176+
177+
std::pair<llvm::Value *, llvm::Value *>
178+
EmitAtomicCompareExchange(llvm::Value *ExpectedVal, llvm::Value *DesiredVal,
179+
llvm::AtomicOrdering Success,
180+
llvm::AtomicOrdering Failure, bool IsVolatile,
181+
bool IsWeak) {
182+
183+
// Check whether we should use a library call.
184+
if (shouldUseLibcall()) {
185+
return EmitAtomicCompareExchangeLibcall(ExpectedVal, DesiredVal, Success,
186+
Failure);
187+
}
188+
189+
// If we've got a scalar value of the right size, try to avoid going
190+
// through memory.
191+
auto Res = EmitAtomicCompareExchangeOp(ExpectedVal, DesiredVal, Success,
192+
Failure, IsVolatile, IsWeak);
193+
return Res;
194+
}
195+
196+
// void __atomic_load(size_t size, void *mem, void *return, int order);
197+
std::pair<llvm::LoadInst *, llvm::AllocaInst *>
198+
EmitAtomicLoadLibcall(llvm::AtomicOrdering AO) {
199+
LLVMContext &Ctx = getLLVMContext();
200+
Type *SizedIntTy = Type::getIntNTy(Ctx, getAtomicSizeInBits() * 8);
201+
Type *ResultTy;
202+
SmallVector<Value *, 6> Args;
203+
AttributeList Attr;
204+
Module *M = Builder->GetInsertBlock()->getModule();
205+
const DataLayout &DL = M->getDataLayout();
206+
Args.push_back(ConstantInt::get(DL.getIntPtrType(Ctx),
207+
this->getAtomicSizeInBits() / 8));
208+
Value *PtrVal = getAtomicPointer();
209+
PtrVal = Builder->CreateAddrSpaceCast(PtrVal, PointerType::getUnqual(Ctx));
210+
Args.push_back(PtrVal);
211+
AllocaInst *AllocaResult =
212+
CreateAlloca(Ty, getAtomicPointer()->getName() + "atomic.temp.load");
213+
const Align AllocaAlignment = DL.getPrefTypeAlign(SizedIntTy);
214+
AllocaResult->setAlignment(AllocaAlignment);
215+
Args.push_back(AllocaResult);
216+
Constant *OrderingVal =
217+
ConstantInt::get(Type::getInt32Ty(Ctx), (int)toCABI(AO));
218+
Args.push_back(OrderingVal);
219+
ResultTy = Type::getVoidTy(Ctx);
220+
SmallVector<Type *, 6> ArgTys;
221+
for (Value *Arg : Args)
222+
ArgTys.push_back(Arg->getType());
223+
FunctionType *FnType = FunctionType::get(ResultTy, ArgTys, false);
224+
FunctionCallee LibcallFn =
225+
M->getOrInsertFunction("__atomic_load", FnType, Attr);
226+
CallInst *Call = Builder->CreateCall(LibcallFn, Args);
227+
Call->setAttributes(Attr);
228+
return std::make_pair(
229+
Builder->CreateAlignedLoad(Ty, AllocaResult, AllocaAlignment),
230+
AllocaResult);
231+
}
232+
};
233+
} // end namespace llvm
234+
235+
#endif /* LLVM_FRONTEND_ATOMIC_ATOMIC_H */

llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_FRONTEND_OPENMP_OMPIRBUILDER_H
1616

1717
#include "llvm/Analysis/MemorySSAUpdater.h"
18+
#include "llvm/Frontend/Atomic/Atomic.h"
1819
#include "llvm/Frontend/OpenMP/OMPConstants.h"
1920
#include "llvm/IR/DebugLoc.h"
2021
#include "llvm/IR/IRBuilder.h"
@@ -457,6 +458,29 @@ class OpenMPIRBuilder {
457458
T(Triple(M.getTargetTriple())) {}
458459
~OpenMPIRBuilder();
459460

461+
class AtomicInfo : public llvm::AtomicInfo<IRBuilder<>> {
462+
463+
llvm::Value *AtomicVar;
464+
465+
public:
466+
AtomicInfo(IRBuilder<> *Builder, llvm::Type *Ty, uint64_t AtomicSizeInBits,
467+
uint64_t ValueSizeInBits, llvm::Align AtomicAlign,
468+
llvm::Align ValueAlign, bool UseLibcall, llvm::Value *AtomicVar)
469+
: llvm::AtomicInfo<IRBuilder<>>(Builder, Ty, AtomicSizeInBits,
470+
ValueSizeInBits, AtomicAlign,
471+
ValueAlign, UseLibcall),
472+
AtomicVar(AtomicVar) {}
473+
474+
llvm::Value *getAtomicPointer() const override { return AtomicVar; }
475+
void decorateWithTBAA(llvm::Instruction *I) override {}
476+
llvm::AllocaInst *CreateAlloca(llvm::Type *Ty,
477+
const llvm::Twine &Name) override {
478+
llvm::AllocaInst *allocaInst = Builder->CreateAlloca(Ty);
479+
allocaInst->setName(Name);
480+
return allocaInst;
481+
}
482+
};
483+
460484
/// Initialize the internal state, this will put structures types and
461485
/// potentially other helpers into the underlying module. Must be called
462486
/// before any other method and only once! This internal state includes types

llvm/lib/Frontend/Atomic/Atomic.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===--- Atomic.cpp - Shared 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/ADT/DenseMap.h"
11+
#include "llvm/Frontend/Atomic/Atomic.h"
12+
#include "llvm/IR/DataLayout.h"
13+
#include "llvm/IR/Intrinsics.h"
14+
#include "llvm/IR/Operator.h"
15+
16+
namespace {} // namespace
17+
18+
namespace llvm {} // end namespace llvm
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
add_llvm_component_library(LLVMFrontendAtomic
2+
Atomic.cpp
3+
4+
ADDITIONAL_HEADER_DIRS
5+
${LLVM_MAIN_INCLUDE_DIR}/llvm/Frontend/Atomic
6+
7+
DEPENDS
8+
LLVMAnalysis
9+
LLVMTargetParser
10+
11+
LINK_COMPONENTS
12+
Core
13+
Support
14+
Analysis
15+
)

llvm/lib/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
add_subdirectory(Atomic)
12
add_subdirectory(Driver)
23
add_subdirectory(HLSL)
34
add_subdirectory(OpenACC)

0 commit comments

Comments
 (0)