Skip to content

Commit 7fcc3b1

Browse files
committed
[CIR] Add initial support for atomic types
This patch adds the initial support for C11 atomic types, including: - Convert QualType that represents atomic types to CIR types; - Start emitting code for atomic value initializers.
1 parent b2cdd80 commit 7fcc3b1

File tree

8 files changed

+286
-1
lines changed

8 files changed

+286
-1
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ struct MissingFeatures {
162162
static bool addressIsKnownNonNull() { return false; }
163163
static bool addressPointerAuthInfo() { return false; }
164164

165+
// Atomic
166+
static bool convertAtomicType() { return false; }
167+
static bool atomicExpr() { return false; }
168+
static bool atomicInitComplex() { return false; }
169+
static bool atomicInitAggregate() { return false; }
170+
static bool atomicCopyAggregateIntoAtomic() { return false; }
171+
static bool atomicCopyComplexIntoAtomic() { return false; }
172+
static bool atomicInfo() { return false; }
173+
static bool atomicInfoGetAtomicPointer() { return false; }
174+
static bool atomicInfoGetAtomicAddress() { return false; }
175+
165176
// Misc
166177
static bool abiArgInfo() { return false; }
167178
static bool addHeapAllocSiteMetadata() { return false; }
@@ -197,6 +208,7 @@ struct MissingFeatures {
197208
static bool cudaSupport() { return false; }
198209
static bool cxxRecordStaticMembers() { return false; }
199210
static bool dataLayoutTypeAllocSize() { return false; }
211+
static bool dataLayoutTypeStoreSize() { return false; }
200212
static bool deferredCXXGlobalInit() { return false; }
201213
static bool ehCleanupFlags() { return false; }
202214
static bool ehCleanupScope() { return false; }
@@ -232,6 +244,7 @@ struct MissingFeatures {
232244
static bool objCBlocks() { return false; }
233245
static bool objCGC() { return false; }
234246
static bool objCLifetime() { return false; }
247+
static bool openCL() { return false; }
235248
static bool openMP() { return false; }
236249
static bool opGlobalViewAttr() { return false; }
237250
static bool opTBAA() { return false; }
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
//===--- CIRGenAtomic.cpp - Emit CIR for 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+
// This file contains the code for emitting atomic operations.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CIRGenFunction.h"
14+
#include "clang/CIR/MissingFeatures.h"
15+
16+
using namespace clang;
17+
using namespace clang::CIRGen;
18+
using namespace cir;
19+
20+
namespace {
21+
class AtomicInfo {
22+
CIRGenFunction &cgf;
23+
QualType atomicTy;
24+
QualType valueTy;
25+
uint64_t atomicSizeInBits = 0;
26+
uint64_t valueSizeInBits = 0;
27+
CharUnits atomicAlign;
28+
CharUnits valueAlign;
29+
TypeEvaluationKind evaluationKind = cir::TEK_Scalar;
30+
LValue lvalue;
31+
mlir::Location loc;
32+
33+
public:
34+
AtomicInfo(CIRGenFunction &cgf, LValue &lvalue, mlir::Location loc)
35+
: cgf(cgf), loc(loc) {
36+
assert(!lvalue.isGlobalReg());
37+
ASTContext &ctx = cgf.getContext();
38+
if (lvalue.isSimple()) {
39+
atomicTy = lvalue.getType();
40+
if (auto *ty = atomicTy->getAs<AtomicType>())
41+
valueTy = ty->getValueType();
42+
else
43+
valueTy = atomicTy;
44+
evaluationKind = cgf.getEvaluationKind(valueTy);
45+
46+
TypeInfo valueTypeInfo = ctx.getTypeInfo(valueTy);
47+
TypeInfo atomicTypeInfo = ctx.getTypeInfo(atomicTy);
48+
uint64_t valueAlignInBits = valueTypeInfo.Align;
49+
uint64_t atomicAlignInBits = atomicTypeInfo.Align;
50+
valueSizeInBits = valueTypeInfo.Width;
51+
atomicSizeInBits = atomicTypeInfo.Width;
52+
assert(valueSizeInBits <= atomicSizeInBits);
53+
assert(valueAlignInBits <= atomicAlignInBits);
54+
55+
atomicAlign = ctx.toCharUnitsFromBits(atomicAlignInBits);
56+
valueAlign = ctx.toCharUnitsFromBits(valueAlignInBits);
57+
if (lvalue.getAlignment().isZero())
58+
lvalue.setAlignment(atomicAlign);
59+
60+
this->lvalue = lvalue;
61+
} else {
62+
assert(!cir::MissingFeatures::atomicInfo());
63+
}
64+
65+
assert(!cir::MissingFeatures::atomicInfo());
66+
}
67+
68+
QualType getValueType() const { return valueTy; }
69+
CharUnits getAtomicAlignment() const { return atomicAlign; }
70+
TypeEvaluationKind getEvaluationKind() const { return evaluationKind; }
71+
mlir::Value getAtomicPointer() const {
72+
if (lvalue.isSimple())
73+
return lvalue.getPointer();
74+
assert(!cir::MissingFeatures::atomicInfoGetAtomicPointer());
75+
return nullptr;
76+
}
77+
Address getAtomicAddress() const {
78+
mlir::Type elemTy;
79+
if (lvalue.isSimple()) {
80+
elemTy = lvalue.getAddress().getElementType();
81+
} else {
82+
assert(!cir::MissingFeatures::atomicInfoGetAtomicAddress());
83+
}
84+
return Address(getAtomicPointer(), elemTy, getAtomicAlignment());
85+
}
86+
87+
/// Is the atomic size larger than the underlying value type?
88+
///
89+
/// Note that the absence of padding does not mean that atomic
90+
/// objects are completely interchangeable with non-atomic
91+
/// objects: we might have promoted the alignment of a type
92+
/// without making it bigger.
93+
bool hasPadding() const { return (valueSizeInBits != atomicSizeInBits); }
94+
95+
bool emitMemSetZeroIfNecessary() const;
96+
97+
/// Copy an atomic r-value into atomic-layout memory.
98+
void emitCopyIntoMemory(RValue rvalue) const;
99+
100+
/// Project an l-value down to the value field.
101+
LValue projectValue() const {
102+
assert(lvalue.isSimple());
103+
Address addr = getAtomicAddress();
104+
if (hasPadding())
105+
llvm_unreachable("NYI");
106+
107+
assert(!cir::MissingFeatures::opTBAA());
108+
return LValue::makeAddr(addr, getValueType(), lvalue.getBaseInfo());
109+
}
110+
111+
private:
112+
bool requiresMemSetZero(mlir::Type ty) const;
113+
};
114+
} // namespace
115+
116+
/// Does a store of the given IR type modify the full expected width?
117+
static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty,
118+
uint64_t expectedSize) {
119+
assert(!cir::MissingFeatures::dataLayoutTypeStoreSize());
120+
return true;
121+
}
122+
123+
/// Does the atomic type require memsetting to zero before initialization?
124+
///
125+
/// The IR type is provided as a way of making certain queries faster.
126+
bool AtomicInfo::requiresMemSetZero(mlir::Type ty) const {
127+
// If the atomic type has size padding, we definitely need a memset.
128+
if (hasPadding())
129+
return true;
130+
131+
// Otherwise, do some simple heuristics to try to avoid it:
132+
switch (getEvaluationKind()) {
133+
// For scalars and complexes, check whether the store size of the
134+
// type uses the full size.
135+
case cir::TEK_Scalar:
136+
return !isFullSizeType(cgf.cgm, ty, atomicSizeInBits);
137+
case cir::TEK_Complex:
138+
llvm_unreachable("NYI");
139+
140+
// Padding in structs has an undefined bit pattern. User beware.
141+
case cir::TEK_Aggregate:
142+
return false;
143+
}
144+
llvm_unreachable("bad evaluation kind");
145+
}
146+
147+
bool AtomicInfo::emitMemSetZeroIfNecessary() const {
148+
assert(lvalue.isSimple());
149+
Address addr = lvalue.getAddress();
150+
if (!requiresMemSetZero(addr.getElementType()))
151+
return false;
152+
153+
llvm_unreachable("NYI");
154+
}
155+
156+
/// Copy an r-value into memory as part of storing to an atomic type.
157+
/// This needs to create a bit-pattern suitable for atomic operations.
158+
void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const {
159+
assert(lvalue.isSimple());
160+
161+
// If we have an r-value, the rvalue should be of the atomic type,
162+
// which means that the caller is responsible for having zeroed
163+
// any padding. Just do an aggregate copy of that type.
164+
if (rvalue.isAggregate()) {
165+
assert(!cir::MissingFeatures::atomicCopyAggregateIntoAtomic());
166+
cgf.cgm.errorNYI("copying aggregate into atomic lvalue is NYI");
167+
return;
168+
}
169+
170+
// Okay, otherwise we're copying stuff.
171+
172+
// Zero out the buffer if necessary.
173+
emitMemSetZeroIfNecessary();
174+
175+
// Drill past the padding if present.
176+
LValue tempLValue = projectValue();
177+
178+
// Okay, store the rvalue in.
179+
if (rvalue.isScalar()) {
180+
cgf.emitStoreOfScalar(rvalue.getValue(), tempLValue, /*isInit=*/true);
181+
} else {
182+
assert(!cir::MissingFeatures::atomicCopyComplexIntoAtomic());
183+
cgf.cgm.errorNYI("copying complex into atomic lvalue is NYI");
184+
}
185+
}
186+
187+
RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
188+
QualType atomicTy = e->getPtr()->getType()->getPointeeType();
189+
QualType memTy = atomicTy;
190+
if (const auto *ty = atomicTy->getAs<AtomicType>())
191+
memTy = ty->getValueType();
192+
193+
Address ptr = emitPointerWithAlignment(e->getPtr());
194+
195+
assert(!cir::MissingFeatures::openCL());
196+
if (e->getOp() == AtomicExpr::AO__c11_atomic_init) {
197+
LValue lvalue = makeAddrLValue(ptr, atomicTy);
198+
emitAtomicInit(e->getVal1(), lvalue);
199+
return RValue::get(nullptr);
200+
}
201+
202+
assert(!cir::MissingFeatures::atomicExpr());
203+
cgm.errorNYI(e->getSourceRange(), "atomic expr is NYI");
204+
return RValue::get(nullptr);
205+
}
206+
207+
void CIRGenFunction::emitAtomicInit(Expr *init, LValue dest) {
208+
AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
209+
210+
switch (atomics.getEvaluationKind()) {
211+
case cir::TEK_Scalar: {
212+
mlir::Value value = emitScalarExpr(init);
213+
atomics.emitCopyIntoMemory(RValue::get(value));
214+
return;
215+
}
216+
217+
case cir::TEK_Complex:
218+
assert(!cir::MissingFeatures::atomicInitComplex());
219+
return;
220+
221+
case cir::TEK_Aggregate:
222+
assert(!cir::MissingFeatures::atomicInitAggregate());
223+
return;
224+
}
225+
226+
llvm_unreachable("bad evaluation kind");
227+
}

clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,10 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
10601060

10611061
return maybePromoteBoolResult(resOp.getResult(), resTy);
10621062
}
1063+
1064+
mlir::Value VisitAtomicExpr(AtomicExpr *e) {
1065+
return cgf.emitAtomicExpr(e).getValue();
1066+
}
10631067
};
10641068

10651069
LValue ScalarExprEmitter::emitCompoundAssignLValue(

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,9 @@ class CIRGenFunction : public CIRGenTypeCache {
893893

894894
Address emitArrayToPointerDecay(const Expr *array);
895895

896+
RValue emitAtomicExpr(AtomicExpr *e);
897+
void emitAtomicInit(Expr *init, LValue dest);
898+
896899
AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d,
897900
mlir::OpBuilder::InsertPoint ip = {});
898901

@@ -1197,7 +1200,7 @@ class CIRGenFunction : public CIRGenTypeCache {
11971200
/// reasonable to just ignore the returned alignment when it isn't from an
11981201
/// explicit source.
11991202
Address emitPointerWithAlignment(const clang::Expr *expr,
1200-
LValueBaseInfo *baseInfo);
1203+
LValueBaseInfo *baseInfo = nullptr);
12011204

12021205
/// Emits a reference binding to the passed in expression.
12031206
RValue emitReferenceBindingToExpr(const Expr *e);

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,16 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
491491
break;
492492
}
493493

494+
case Type::Atomic: {
495+
QualType valueType = cast<AtomicType>(ty)->getValueType();
496+
resultType = convertTypeForMem(valueType);
497+
498+
// Pad out to the inflated size if necessary.
499+
assert(!cir::MissingFeatures::convertAtomicType());
500+
501+
break;
502+
}
503+
494504
default:
495505
cgm.errorNYI(SourceLocation(), "processing of type",
496506
type->getTypeClassName());

clang/lib/CIR/CodeGen/CIRGenValue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ class LValue {
190190
bool isSimple() const { return lvType == Simple; }
191191
bool isVectorElt() const { return lvType == VectorElt; }
192192
bool isBitField() const { return lvType == BitField; }
193+
bool isGlobalReg() const { return lvType == GlobalReg; }
193194
bool isVolatile() const { return quals.hasVolatile(); }
194195

195196
bool isVolatileQualified() const { return quals.hasVolatile(); }

clang/lib/CIR/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
88

99
add_clang_library(clangCIR
1010
CIRGenerator.cpp
11+
CIRGenAtomic.cpp
1112
CIRGenBuilder.cpp
1213
CIRGenCall.cpp
1314
CIRGenClass.cpp

clang/test/CIR/CodeGen/atomic.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
7+
8+
void f1(void) {
9+
_Atomic(int) x = 42;
10+
}
11+
12+
// CIR-LABEL: @f1
13+
// CIR: %[[SLOT:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
14+
// CIR-NEXT: %[[INIT:.+]] = cir.const #cir.int<42> : !s32i
15+
// CIR-NEXT: cir.store align(4) %[[INIT]], %[[SLOT]] : !s32i, !cir.ptr<!s32i>
16+
// CIR: }
17+
18+
// LLVM-LABEL: @f1
19+
// LLVM: %[[SLOT:.+]] = alloca i32, i64 1, align 4
20+
// LLVM-NEXT: store i32 42, ptr %[[SLOT]], align 4
21+
// LLVM: }
22+
23+
// OGCG-LABEL: @f1
24+
// OGCG: %[[SLOT:.+]] = alloca i32, align 4
25+
// OGCG-NEXT: store i32 42, ptr %[[SLOT]], align 4
26+
// OGCG: }

0 commit comments

Comments
 (0)