Skip to content

Commit 3718d2d

Browse files
authored
[CIR][CodeGen] Support static references to temporaries (#872)
1 parent 3ec8a75 commit 3718d2d

File tree

9 files changed

+178
-100
lines changed

9 files changed

+178
-100
lines changed

clang/lib/CIR/CodeGen/CIRGenCXX.cpp

+106-37
Original file line numberDiff line numberDiff line change
@@ -306,51 +306,120 @@ void CIRGenFunction::buildInvariantStart([[maybe_unused]] CharUnits Size) {
306306
assert(!MissingFeatures::createInvariantIntrinsic());
307307
}
308308

309-
void CIRGenModule::codegenGlobalInitCxxStructor(const VarDecl *D,
310-
mlir::cir::GlobalOp Addr,
311-
bool NeedsCtor, bool NeedsDtor,
312-
bool isCstStorage) {
313-
assert(D && " Expected a global declaration!");
314-
CIRGenFunction CGF{*this, builder, true};
315-
CurCGF = &CGF;
316-
CurCGF->CurFn = Addr;
317-
Addr.setAstAttr(mlir::cir::ASTVarDeclAttr::get(builder.getContext(), D));
309+
void CIRGenModule::buildCXXGlobalVarDeclInit(const VarDecl *varDecl,
310+
mlir::cir::GlobalOp addr,
311+
bool performInit) {
312+
const Expr *init = varDecl->getInit();
313+
QualType ty = varDecl->getType();
314+
315+
// TODO: handle address space
316+
// The address space of a static local variable (DeclPtr) may be different
317+
// from the address space of the "this" argument of the constructor. In that
318+
// case, we need an addrspacecast before calling the constructor.
319+
//
320+
// struct StructWithCtor {
321+
// __device__ StructWithCtor() {...}
322+
// };
323+
// __device__ void foo() {
324+
// __shared__ StructWithCtor s;
325+
// ...
326+
// }
327+
//
328+
// For example, in the above CUDA code, the static local variable s has a
329+
// "shared" address space qualifier, but the constructor of StructWithCtor
330+
// expects "this" in the "generic" address space.
331+
assert(!MissingFeatures::addressSpace());
332+
333+
if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd &&
334+
varDecl->hasAttr<OMPThreadPrivateDeclAttr>()) {
335+
llvm_unreachable("NYI");
336+
}
318337

319-
if (NeedsCtor) {
320-
mlir::OpBuilder::InsertionGuard guard(builder);
321-
auto block = builder.createBlock(&Addr.getCtorRegion());
322-
CIRGenFunction::LexicalScope lexScope{*CurCGF, Addr.getLoc(),
323-
builder.getInsertionBlock()};
324-
lexScope.setAsGlobalInit();
338+
assert(varDecl && " Expected a global declaration!");
339+
CIRGenFunction cgf{*this, builder, true};
340+
CurCGF = &cgf;
341+
CurCGF->CurFn = addr;
325342

326-
builder.setInsertionPointToStart(block);
327-
Address DeclAddr(getAddrOfGlobalVar(D), getASTContext().getDeclAlign(D));
328-
buildDeclInit(CGF, D, DeclAddr);
329-
builder.setInsertionPointToEnd(block);
330-
builder.create<mlir::cir::YieldOp>(Addr->getLoc());
331-
}
343+
CIRGenFunction::SourceLocRAIIObject fnLoc{cgf,
344+
getLoc(varDecl->getLocation())};
332345

333-
if (isCstStorage) {
334-
// TODO: this leads to a missing feature in the moment, probably also need a
335-
// LexicalScope to be inserted here.
336-
buildDeclInvariant(CGF, D);
337-
} else {
338-
// If not constant storage we'll emit this regardless of NeedsDtor value.
346+
addr.setAstAttr(
347+
mlir::cir::ASTVarDeclAttr::get(builder.getContext(), varDecl));
348+
349+
if (ty->isReferenceType()) {
339350
mlir::OpBuilder::InsertionGuard guard(builder);
340-
auto block = builder.createBlock(&Addr.getDtorRegion());
341-
CIRGenFunction::LexicalScope lexScope{*CurCGF, Addr.getLoc(),
351+
auto *block = builder.createBlock(&addr.getCtorRegion());
352+
CIRGenFunction::LexicalScope lexScope{*CurCGF, addr.getLoc(),
342353
builder.getInsertionBlock()};
343354
lexScope.setAsGlobalInit();
344-
345355
builder.setInsertionPointToStart(block);
346-
buildDeclDestroy(CGF, D);
356+
auto getGlobal = builder.createGetGlobal(addr);
357+
358+
Address declAddr(getGlobal, getGlobal.getType(),
359+
getASTContext().getDeclAlign(varDecl));
360+
assert(performInit && "cannot have constant initializer which needs "
361+
"destruction for reference");
362+
RValue rv = cgf.buildReferenceBindingToExpr(init);
363+
{
364+
mlir::OpBuilder::InsertionGuard guard(builder);
365+
mlir::Operation *rvalueDefOp = rv.getScalarVal().getDefiningOp();
366+
if (rvalueDefOp && rvalueDefOp->getBlock()) {
367+
mlir::Block *rvalSrcBlock = rvalueDefOp->getBlock();
368+
if (!rvalSrcBlock->empty() &&
369+
isa<mlir::cir::YieldOp>(rvalSrcBlock->back())) {
370+
auto &front = rvalSrcBlock->front();
371+
getGlobal.getDefiningOp()->moveBefore(&front);
372+
auto yield = cast<mlir::cir::YieldOp>(rvalSrcBlock->back());
373+
builder.setInsertionPoint(yield);
374+
}
375+
}
376+
cgf.buildStoreOfScalar(rv.getScalarVal(), declAddr, false, ty);
377+
}
347378
builder.setInsertionPointToEnd(block);
348-
if (block->empty()) {
349-
block->erase();
350-
// Don't confuse lexical cleanup.
351-
builder.clearInsertionPoint();
352-
} else
353-
builder.create<mlir::cir::YieldOp>(Addr->getLoc());
379+
builder.create<mlir::cir::YieldOp>(addr->getLoc());
380+
} else {
381+
bool needsDtor = varDecl->needsDestruction(getASTContext()) ==
382+
QualType::DK_cxx_destructor;
383+
// PerformInit, constant store invariant / destroy handled below.
384+
bool isConstantStorage =
385+
varDecl->getType().isConstantStorage(getASTContext(), true, !needsDtor);
386+
if (performInit) {
387+
mlir::OpBuilder::InsertionGuard guard(builder);
388+
auto *block = builder.createBlock(&addr.getCtorRegion());
389+
CIRGenFunction::LexicalScope lexScope{*CurCGF, addr.getLoc(),
390+
builder.getInsertionBlock()};
391+
lexScope.setAsGlobalInit();
392+
393+
builder.setInsertionPointToStart(block);
394+
Address declAddr(getAddrOfGlobalVar(varDecl),
395+
getASTContext().getDeclAlign(varDecl));
396+
buildDeclInit(cgf, varDecl, declAddr);
397+
builder.setInsertionPointToEnd(block);
398+
builder.create<mlir::cir::YieldOp>(addr->getLoc());
399+
}
400+
401+
if (isConstantStorage) {
402+
// TODO: this leads to a missing feature in the moment, probably also need
403+
// a LexicalScope to be inserted here.
404+
buildDeclInvariant(cgf, varDecl);
405+
} else {
406+
// If not constant storage we'll emit this regardless of NeedsDtor value.
407+
mlir::OpBuilder::InsertionGuard guard(builder);
408+
auto *block = builder.createBlock(&addr.getDtorRegion());
409+
CIRGenFunction::LexicalScope lexScope{*CurCGF, addr.getLoc(),
410+
builder.getInsertionBlock()};
411+
lexScope.setAsGlobalInit();
412+
413+
builder.setInsertionPointToStart(block);
414+
buildDeclDestroy(cgf, varDecl);
415+
builder.setInsertionPointToEnd(block);
416+
if (block->empty()) {
417+
block->erase();
418+
// Don't confuse lexical cleanup.
419+
builder.clearInsertionPoint();
420+
} else
421+
builder.create<mlir::cir::YieldOp>(addr->getLoc());
422+
}
354423
}
355424

356425
CurCGF = nullptr;

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,7 @@ class CIRGenCXXABI {
170170
/// \param Dtor - a function taking a single pointer argument
171171
/// \param Addr - a pointer to pass to the destructor function.
172172
virtual void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D,
173-
mlir::cir::FuncOp dtor,
174-
mlir::Attribute Addr) = 0;
173+
mlir::cir::FuncOp dtor, mlir::Value Addr) = 0;
175174

176175
virtual size_t getSrcArgforCopyCtor(const CXXConstructorDecl *,
177176
FunctionArgList &Args) const = 0;

clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp

-45
Original file line numberDiff line numberDiff line change
@@ -44,55 +44,10 @@ void CIRGenModule::buildCXXGlobalVarDeclInitFunc(const VarDecl *D,
4444
D->hasAttr<CUDASharedAttr>()))
4545
return;
4646

47-
assert(!getLangOpts().OpenMP && "OpenMP global var init not implemented");
48-
4947
// Check if we've already initialized this decl.
5048
auto I = DelayedCXXInitPosition.find(D);
5149
if (I != DelayedCXXInitPosition.end() && I->second == ~0U)
5250
return;
5351

5452
buildCXXGlobalVarDeclInit(D, Addr, PerformInit);
5553
}
56-
57-
void CIRGenModule::buildCXXGlobalVarDeclInit(const VarDecl *D,
58-
mlir::cir::GlobalOp Addr,
59-
bool PerformInit) {
60-
QualType T = D->getType();
61-
62-
// TODO: handle address space
63-
// The address space of a static local variable (DeclPtr) may be different
64-
// from the address space of the "this" argument of the constructor. In that
65-
// case, we need an addrspacecast before calling the constructor.
66-
//
67-
// struct StructWithCtor {
68-
// __device__ StructWithCtor() {...}
69-
// };
70-
// __device__ void foo() {
71-
// __shared__ StructWithCtor s;
72-
// ...
73-
// }
74-
//
75-
// For example, in the above CUDA code, the static local variable s has a
76-
// "shared" address space qualifier, but the constructor of StructWithCtor
77-
// expects "this" in the "generic" address space.
78-
assert(!MissingFeatures::addressSpace());
79-
80-
if (!T->isReferenceType()) {
81-
if (getLangOpts().OpenMP && !getLangOpts().OpenMPSimd &&
82-
D->hasAttr<OMPThreadPrivateDeclAttr>()) {
83-
llvm_unreachable("NYI");
84-
}
85-
bool NeedsDtor =
86-
D->needsDestruction(getASTContext()) == QualType::DK_cxx_destructor;
87-
// PerformInit, constant store invariant / destroy handled below.
88-
bool isCstStorage =
89-
D->getType().isConstantStorage(getASTContext(), true, !NeedsDtor);
90-
codegenGlobalInitCxxStructor(D, Addr, PerformInit, NeedsDtor, isCstStorage);
91-
return;
92-
}
93-
94-
assert(PerformInit && "cannot have constant initializer which needs "
95-
"destruction for reference");
96-
// TODO(cir): buildReferenceBindingToExpr
97-
llvm_unreachable("NYI");
98-
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

+22-4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "EHScopeStack.h"
2121
#include "TargetInfo.h"
2222

23+
#include "clang/AST/Decl.h"
2324
#include "clang/AST/ExprCXX.h"
2425
#include "clang/AST/GlobalDecl.h"
2526
#include "clang/Basic/Builtins.h"
@@ -1952,6 +1953,9 @@ LValue CIRGenFunction::buildCastLValue(const CastExpr *E) {
19521953
// CK_NoOp can model a qualification conversion, which can remove an array
19531954
// bound and change the IR type.
19541955
LValue LV = buildLValue(E->getSubExpr());
1956+
// Propagate the volatile qualifier to LValue, if exists in E.
1957+
if (E->changesVolatileQualification())
1958+
llvm_unreachable("NYI");
19551959
if (LV.isSimple()) {
19561960
Address V = LV.getAddress();
19571961
if (V.isValid()) {
@@ -2195,8 +2199,14 @@ static Address createReferenceTemporary(CIRGenFunction &CGF,
21952199
CGF.getCounterRefTmpAsString(), Alloca, ip);
21962200
}
21972201
case SD_Thread:
2198-
case SD_Static:
2199-
assert(0 && "NYI");
2202+
case SD_Static: {
2203+
auto a = mlir::cast<mlir::cir::GlobalOp>(
2204+
CGF.CGM.getAddrOfGlobalTemporary(M, Inner));
2205+
auto f = CGF.CGM.getBuilder().createGetGlobal(a);
2206+
assert(a.getAlignment().has_value() &&
2207+
"This should always have an alignment");
2208+
return Address(f, clang::CharUnits::fromQuantity(a.getAlignment().value()));
2209+
}
22002210

22012211
case SD_Dynamic:
22022212
llvm_unreachable("temporary can't have dynamic storage duration");
@@ -2232,12 +2242,20 @@ static void pushTemporaryCleanup(CIRGenFunction &CGF,
22322242
switch (M->getStorageDuration()) {
22332243
case SD_Static:
22342244
case SD_Thread: {
2245+
mlir::cir::FuncOp cleanupFn;
2246+
mlir::Value cleanupArg;
22352247
if (E->getType()->isArrayType()) {
22362248
llvm_unreachable("SD_Static|SD_Thread + array types not implemented");
22372249
} else {
2238-
llvm_unreachable("SD_Static|SD_Thread for general types not implemented");
2250+
cleanupFn = CGF.CGM
2251+
.getAddrAndTypeOfCXXStructor(
2252+
GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete))
2253+
.second;
2254+
cleanupArg = ReferenceTemporary.emitRawPointer();
22392255
}
2240-
llvm_unreachable("SD_Static|SD_Thread not implemented");
2256+
CGF.CGM.getCXXABI().registerGlobalDtor(
2257+
CGF, cast<VarDecl>(M->getExtendingDecl()), cleanupFn, cleanupArg);
2258+
break;
22412259
}
22422260

22432261
case SD_FullExpression:

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@ class CIRGenItaniumCXXABI : public cir::CIRGenCXXABI {
173173
bool Delegating, Address This,
174174
QualType ThisTy) override;
175175
void registerGlobalDtor(CIRGenFunction &CGF, const VarDecl *D,
176-
mlir::cir::FuncOp dtor,
177-
mlir::Attribute Addr) override;
176+
mlir::cir::FuncOp dtor, mlir::Value Addr) override;
178177
virtual void buildRethrow(CIRGenFunction &CGF, bool isNoReturn) override;
179178
virtual void buildThrow(CIRGenFunction &CGF, const CXXThrowExpr *E) override;
180179
CatchTypeInfo
@@ -2144,7 +2143,7 @@ void CIRGenItaniumCXXABI::buildDestructorCall(
21442143
void CIRGenItaniumCXXABI::registerGlobalDtor(CIRGenFunction &CGF,
21452144
const VarDecl *D,
21462145
mlir::cir::FuncOp dtor,
2147-
mlir::Attribute Addr) {
2146+
mlir::Value Addr) {
21482147
if (D->isNoDestroy(CGM.getASTContext()))
21492148
return;
21502149

clang/lib/CIR/CodeGen/CIRGenModule.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,7 @@ CIRGenModule::getAddrOfGlobalTemporary(const MaterializeTemporaryExpr *expr,
16451645
} else {
16461646
// No initializer, the initialization will be provided when we initialize
16471647
// the declaration which performed lifetime extension.
1648-
llvm_unreachable("else value");
1648+
type = getTypes().convertTypeForMem(materializedType);
16491649
}
16501650

16511651
// Create a global variable for this lifetime-extended temporary.

clang/lib/CIR/CodeGen/CIRGenModule.h

+2-7
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,8 @@ class CIRGenModule : public CIRGenTypeCache {
630630
bool IsTentative = false);
631631

632632
/// Emit the function that initializes the specified global
633-
void buildCXXGlobalVarDeclInit(const VarDecl *D, mlir::cir::GlobalOp Addr,
634-
bool PerformInit);
633+
void buildCXXGlobalVarDeclInit(const VarDecl *varDecl,
634+
mlir::cir::GlobalOp addr, bool performInit);
635635

636636
void buildCXXGlobalVarDeclInitFunc(const VarDecl *D, mlir::cir::GlobalOp Addr,
637637
bool PerformInit);
@@ -673,11 +673,6 @@ class CIRGenModule : public CIRGenTypeCache {
673673
// or if they are alias to each other.
674674
mlir::cir::FuncOp codegenCXXStructor(clang::GlobalDecl GD);
675675

676-
// Produce code for this constructor/destructor for global initialzation.
677-
void codegenGlobalInitCxxStructor(const clang::VarDecl *D,
678-
mlir::cir::GlobalOp Addr, bool NeedsCtor,
679-
bool NeedsDtor, bool isCstStorage);
680-
681676
bool lookupRepresentativeDecl(llvm::StringRef MangledName,
682677
clang::GlobalDecl &Result) const;
683678

clang/test/CIR/CodeGen/temporaries.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ void f() {
1919
// CHECK-NEXT: cir.scope {
2020
// CHECK-NEXT: %[[ONE:[0-9]+]] = cir.alloca !ty_E, !cir.ptr<!ty_E>, ["agg.tmp.ensured"] {alignment = 1 : i64}
2121
// CHECK-NEXT: %[[TWO:[0-9]+]] = cir.alloca !ty_E, !cir.ptr<!ty_E>, ["ref.tmp0"] {alignment = 1 : i64}
22-
// CHECK-NEXT: cir.call @_ZN1EC1Ev(%1) : (!cir.ptr<!ty_E>) -> () extra(#fn_attr)
22+
// CHECK-NEXT: cir.call @_ZN1EC1Ev(%[[TWO]]) : (!cir.ptr<!ty_E>) -> () extra(#fn_attr)
2323
// CHECK-NEXT: %[[THREE:[0-9]+]] = cir.call @_ZN1EntEv(%[[TWO]]) : (!cir.ptr<!ty_E>) -> !ty_E
2424
// CHECK-NEXT: cir.store %[[THREE]], %[[ONE]] : !ty_E, !cir.ptr<!ty_E>
2525
// CHECK-NEXT: cir.call @_ZN1ED1Ev(%[[ONE]]) : (!cir.ptr<!ty_E>) -> () extra(#fn_attr)
@@ -37,3 +37,4 @@ const int &r = (const int&)n;
3737
// LLVM: @_ZGR1r_ = internal constant i32 1234, align 4
3838
// LLVM-NEXT: @r = constant ptr @_ZGR1r_, align 8
3939

40+

clang/test/CIR/CodeGen/tempref.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s
3+
// RUN: cir-translate %t.cir -cir-to-llvmir -o %t.ll
4+
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM
5+
6+
struct A { ~A(); };
7+
A &&a = dynamic_cast<A&&>(A{});
8+
9+
// CHECK: cir.func private @_ZN1AD1Ev(!cir.ptr<!ty_A>) extra(#fn_attr)
10+
// CHECK-NEXT: cir.global external @a = #cir.ptr<null> : !cir.ptr<!ty_A> {alignment = 8 : i64, ast = #cir.var.decl.ast}
11+
// CHECK-NEXT: cir.func internal private @__cxx_global_var_init() {
12+
// CHECK-NEXT: cir.scope {
13+
// CHECK-NEXT: %[[SEVEN:[0-9]+]] = cir.get_global @a : !cir.ptr<!cir.ptr<!ty_A>>
14+
// CHECK-NEXT: %[[EIGHT:[0-9]+]] = cir.get_global @_ZGR1a_ : !cir.ptr<!ty_A>
15+
// CHECK-NEXT: cir.store %[[EIGHT]], %[[SEVEN]] : !cir.ptr<!ty_A>, !cir.ptr<!cir.ptr<!ty_A>>
16+
// CHECK-NEXT: }
17+
// CHECK-NEXT: cir.return
18+
// CHECK-NEXT: }
19+
// CHECK-NEXT: cir.func private @_GLOBAL__sub_I_tempref.cpp() {
20+
// CHECK-NEXT: cir.call @__cxx_global_var_init() : () -> ()
21+
// CHECK-NEXT: cir.return
22+
// CHECK-NEXT: }
23+
24+
// LLVM: @_ZGR1a_ = internal global %struct.A undef
25+
// LLVM-DAG: @a = global ptr null, align 8
26+
// LLVM-DAG: @llvm.global_ctors = appending constant [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65536, ptr @__cxx_global_var_init, ptr null }]
27+
28+
// LLVM-DAG: declare {{.*}} void @_ZN1AD1Ev(ptr)
29+
30+
// LLVM-DAG: define internal void @__cxx_global_var_init()
31+
// LLVM-DAG: br label %[[L1:[0-9]+]]
32+
// LLVM-DAG: [[L1]]:
33+
// LLVM-DAG: store ptr @_ZGR1a_, ptr @a, align 8
34+
// LLVM-DAG: br label %[[L2:[0-9]+]]
35+
// LLVM-DAG: [[L2]]:
36+
// LLVM-DAG: ret void
37+
// LLVM-DAG: }
38+
39+
// LLVM-DAG: define void @_GLOBAL__sub_I_tempref.cpp()
40+
// LLVM-DAG: call void @__cxx_global_var_init()
41+
// LLVM-DAG: ret void
42+
// LLVM-DAG: }

0 commit comments

Comments
 (0)