Skip to content

Commit 249ee3e

Browse files
htyulanza
authored andcommitted
[CIR][CodeGen] Emit globals with constructor initializer (#197)
This change does the CIR generation for globals initialized by a constructor call. It currently only covers C++ to CIR generation. The corresponding LLVM lowering will be in a follow-up commit. A motivating example is ``` class Init { friend class ios_base; public: Init(bool); ~Init(); private: static bool _S_synced_with_stdio; }; static Init ioinit(true); ``` Unlike what the default Clang codegen generates LLVM that detaches the initialization code from the global var definition (like below), we are taking a different approach that keeps them together, which we think will make the later dataflow analysis/transform easier. ``` @_ZL8ioinit = internal global %class.Init zeroinitializer, align 1, !dbg !0 define internal void @cxx_global_var_init() #0 section ".text.startup" !dbg !23 { entry: call void @_ZN4InitC2Ev(ptr noundef nonnull align 1 dereferenceable(1) @_ZL8ioinit), !dbg !27 %0 = call i32 @cxa_atexit(ptr @_ZN4InitD1Ev, ptr @_ZL8ioinit, ptr @dso_handle) #3, !dbg !29 ret void, !dbg !27 } ``` So on CIR, we have something like: ``` cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 { %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22> loc(#loc8) %1 = cir.const(#true) : !cir.bool loc(#loc5) cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> () loc(#loc6) } ``` The destructor support will also be in a separate change.
1 parent 30195ff commit 249ee3e

File tree

12 files changed

+291
-55
lines changed

12 files changed

+291
-55
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -571,11 +571,12 @@ def YieldOpKind : I32EnumAttr<
571571

572572
def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
573573
ParentOneOf<["IfOp", "ScopeOp", "SwitchOp", "LoopOp", "AwaitOp",
574-
"TernaryOp"]>]> {
574+
"TernaryOp", "GlobalOp"]>]> {
575575
let summary = "Terminate CIR regions";
576576
let description = [{
577577
The `cir.yield` operation terminates regions on different CIR operations:
578-
`cir.if`, `cir.scope`, `cir.switch`, `cir.loop`, `cir.await` and `cir.ternary`.
578+
`cir.if`, `cir.scope`, `cir.switch`, `cir.loop`, `cir.await`, `cir.ternary`
579+
and `cir.global`.
579580

580581
Might yield an SSA value and the semantics of how the values are yielded is
581582
defined by the parent operation.
@@ -1242,7 +1243,7 @@ def SignedOverflowBehaviorEnum : I32EnumAttr<
12421243
}
12431244

12441245

1245-
def GlobalOp : CIR_Op<"global", [Symbol]> {
1246+
def GlobalOp : CIR_Op<"global", [Symbol, DeclareOpInterfaceMethods<RegionBranchOpInterface>, NoRegionArguments]> {
12461247
let summary = "Declares or defines a global variable";
12471248
let description = [{
12481249
The `cir.global` operation declares or defines a named global variable.
@@ -1280,19 +1281,19 @@ def GlobalOp : CIR_Op<"global", [Symbol]> {
12801281
OptionalAttr<AnyAttr>:$initial_value,
12811282
UnitAttr:$constant,
12821283
OptionalAttr<I64Attr>:$alignment);
1283-
1284+
let regions = (region AnyRegion:$ctorRegion);
12841285
let assemblyFormat = [{
12851286
($sym_visibility^)?
12861287
(`constant` $constant^)?
12871288
$linkage
12881289
$sym_name
1289-
custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value)
1290+
custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value, $ctorRegion)
12901291
attr-dict
12911292
}];
12921293

12931294
let extraClassDeclaration = [{
12941295
bool isDeclaration() {
1295-
return !getInitialValue();
1296+
return !getInitialValue() && getCtorRegion().empty();
12961297
}
12971298
bool hasInitializer() { return !isDeclaration(); }
12981299
bool hasAvailableExternallyLinkage() {
@@ -1318,8 +1319,9 @@ def GlobalOp : CIR_Op<"global", [Symbol]> {
13181319
CArg<"bool", "false">:$isConstant,
13191320
// CIR defaults to external linkage.
13201321
CArg<"cir::GlobalLinkageKind",
1321-
"cir::GlobalLinkageKind::ExternalLinkage">:$linkage
1322-
)>
1322+
"cir::GlobalLinkageKind::ExternalLinkage">:$linkage,
1323+
CArg<"function_ref<void(OpBuilder &, Location)>",
1324+
"nullptr">:$ctorBuilder)>
13231325
];
13241326

13251327
let hasVerifier = 1;

clang/lib/CIR/CodeGen/CIRGenCXX.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,34 @@
2020
using namespace clang;
2121
using namespace cir;
2222

23+
static void buildDeclInit(CIRGenFunction &CGF, const VarDecl *D,
24+
Address DeclPtr) {
25+
assert((D->hasGlobalStorage() ||
26+
(D->hasLocalStorage() &&
27+
CGF.getContext().getLangOpts().OpenCLCPlusPlus)) &&
28+
"VarDecl must have global or local (in the case of OpenCL) storage!");
29+
assert(!D->getType()->isReferenceType() &&
30+
"Should not call buildDeclInit on a reference!");
31+
32+
QualType type = D->getType();
33+
LValue lv = CGF.makeAddrLValue(DeclPtr, type);
34+
35+
const Expr *Init = D->getInit();
36+
switch (CIRGenFunction::getEvaluationKind(type)) {
37+
case TEK_Aggregate:
38+
CGF.buildAggExpr(
39+
Init, AggValueSlot::forLValue(lv, AggValueSlot::IsDestructed,
40+
AggValueSlot::DoesNotNeedGCBarriers,
41+
AggValueSlot::IsNotAliased,
42+
AggValueSlot::DoesNotOverlap));
43+
return;
44+
case TEK_Scalar:
45+
llvm_unreachable("scalar evaluation NYI");
46+
case TEK_Complex:
47+
llvm_unreachable("complext evaluation NYI");
48+
}
49+
}
50+
2351
mlir::cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl GD) {
2452
const auto &FnInfo = getTypes().arrangeCXXStructorDeclaration(GD);
2553
auto Fn = getAddrOfCXXStructor(GD, &FnInfo, /*FnType=*/nullptr,
@@ -38,3 +66,20 @@ mlir::cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl GD) {
3866
// TODO: SetLLVMFunctionAttributesForDefinition
3967
return Fn;
4068
}
69+
70+
void CIRGenModule::codegenGlobalInitCxxStructor(const VarDecl *D,
71+
mlir::cir::GlobalOp Addr) {
72+
CIRGenFunction CGF{*this, builder, true};
73+
CurCGF = &CGF;
74+
CurCGF->CurFn = Addr;
75+
{
76+
mlir::OpBuilder::InsertionGuard guard(builder);
77+
auto block = builder.createBlock(&Addr.getCtorRegion());
78+
builder.setInsertionPointToStart(block);
79+
Address DeclAddr(getAddrOfGlobalVar(D), getASTContext().getDeclAlign(D));
80+
buildDeclInit(CGF, D, DeclAddr);
81+
builder.setInsertionPointToEnd(block);
82+
builder.create<mlir::cir::YieldOp>(Addr->getLoc());
83+
}
84+
CurCGF = nullptr;
85+
}

clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@ CIRGenFunction::buildCoroutineBody(const CoroutineBodyStmt &S) {
254254
auto openCurlyLoc = getLoc(S.getBeginLoc());
255255
auto nullPtrCst = builder.getNullPtr(VoidPtrTy, openCurlyLoc);
256256

257-
CurFn.setCoroutineAttr(mlir::UnitAttr::get(builder.getContext()));
257+
auto Fn = dyn_cast<mlir::cir::FuncOp>(CurFn);
258+
assert(Fn && "other callables NYI");
259+
Fn.setCoroutineAttr(mlir::UnitAttr::get(builder.getContext()));
258260
auto coroId = buildCoroIDBuiltinCall(openCurlyLoc, nullPtrCst);
259261
createCoroData(*this, CurCoro, coroId);
260262

clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "CIRGenFunction.h"
1314
#include "CIRGenModule.h"
1415
#include "TargetInfo.h"
1516
#include "clang/AST/Attr.h"
@@ -28,4 +29,52 @@ void CIRGenModule::buildCXXGlobalInitFunc() {
2829
return;
2930

3031
assert(0 && "NYE");
31-
}
32+
}
33+
34+
void CIRGenModule::buildGlobalVarDeclInit(const VarDecl *D,
35+
mlir::cir::GlobalOp Addr,
36+
bool PerformInit) {
37+
// According to E.2.3.1 in CUDA-7.5 Programming guide: __device__,
38+
// __constant__ and __shared__ variables defined in namespace scope,
39+
// that are of class type, cannot have a non-empty constructor. All
40+
// the checks have been done in Sema by now. Whatever initializers
41+
// are allowed are empty and we just need to ignore them here.
42+
if (getLangOpts().CUDAIsDevice && !getLangOpts().GPUAllowDeviceInit &&
43+
(D->hasAttr<CUDADeviceAttr>() || D->hasAttr<CUDAConstantAttr>() ||
44+
D->hasAttr<CUDASharedAttr>()))
45+
return;
46+
47+
assert(!getLangOpts().OpenMP && "OpenMP global var init not implemented");
48+
49+
// Check if we've already initialized this decl.
50+
auto I = DelayedCXXInitPosition.find(D);
51+
if (I != DelayedCXXInitPosition.end() && I->second == ~0U)
52+
return;
53+
54+
if (PerformInit) {
55+
QualType T = D->getType();
56+
57+
// TODO: handle address space
58+
// The address space of a static local variable (DeclPtr) may be different
59+
// from the address space of the "this" argument of the constructor. In that
60+
// case, we need an addrspacecast before calling the constructor.
61+
//
62+
// struct StructWithCtor {
63+
// __device__ StructWithCtor() {...}
64+
// };
65+
// __device__ void foo() {
66+
// __shared__ StructWithCtor s;
67+
// ...
68+
// }
69+
//
70+
// For example, in the above CUDA code, the static local variable s has a
71+
// "shared" address space qualifier, but the constructor of StructWithCtor
72+
// expects "this" in the "generic" address space.
73+
assert(!UnimplementedFeature::addressSpace());
74+
75+
if (!T->isReferenceType()) {
76+
codegenGlobalInitCxxStructor(D, Addr);
77+
return;
78+
}
79+
}
80+
}

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ static CIRGenCallee buildDirectCallee(CIRGenModule &CGM, GlobalDecl GD) {
355355

356356
// When directing calling an inline builtin, call it through it's mangled
357357
// name to make it clear it's not the actual builtin.
358-
if (CGF.CurFn.getName() != FDInlineName &&
358+
auto Fn = cast<mlir::cir::FuncOp>(CGF.CurFn);
359+
if (Fn.getName() != FDInlineName &&
359360
onlyHasInlineBuiltinDeclaration(FD)) {
360361
assert(0 && "NYI");
361362
}
@@ -2132,7 +2133,7 @@ mlir::Value CIRGenFunction::buildAlloca(StringRef name, mlir::Type ty,
21322133
mlir::Location loc, CharUnits alignment,
21332134
bool insertIntoFnEntryBlock) {
21342135
mlir::Block *entryBlock = insertIntoFnEntryBlock
2135-
? &CurFn.getRegion().front()
2136+
? getCurFunctionEntryBlock()
21362137
: currLexScope->getEntryBlock();
21372138
return buildAlloca(name, ty, loc, alignment,
21382139
builder.getBestAllocaInsertPoint(entryBlock));
@@ -2506,9 +2507,11 @@ mlir::Value CIRGenFunction::buildScalarConstant(
25062507
}
25072508

25082509
LValue CIRGenFunction::buildPredefinedLValue(const PredefinedExpr *E) {
2509-
auto SL = E->getFunctionName();
2510+
const auto *SL = E->getFunctionName();
25102511
assert(SL != nullptr && "No StringLiteral name in PredefinedExpr");
2511-
StringRef FnName = CurFn.getName();
2512+
auto Fn = dyn_cast<mlir::cir::FuncOp>(CurFn);
2513+
assert(Fn && "other callables NYI");
2514+
StringRef FnName = Fn.getName();
25122515
if (FnName.starts_with("\01"))
25132516
FnName = FnName.substr(1);
25142517
StringRef NameItems[] = {PredefinedExpr::getIdentKindName(E->getIdentKind()),

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,9 @@ void CIRGenFunction::LexicalScopeGuard::cleanup() {
322322

323323
auto buildReturn = [&](mlir::Location loc) {
324324
// If we are on a coroutine, add the coro_end builtin call.
325-
if (CGF.CurFn.getCoroutine())
325+
auto Fn = dyn_cast<mlir::cir::FuncOp>(CGF.CurFn);
326+
assert(Fn && "other callables NYI");
327+
if (Fn.getCoroutine())
326328
CGF.buildCoroEndBuiltinCall(
327329
loc, builder.getNullPtr(builder.getVoidPtrTy(), loc));
328330

@@ -1012,7 +1014,9 @@ void CIRGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
10121014
const auto *MD = cast<CXXMethodDecl>(D);
10131015
if (MD->getParent()->isLambda() && MD->getOverloadedOperator() == OO_Call) {
10141016
// We're in a lambda.
1015-
CurFn.setLambdaAttr(mlir::UnitAttr::get(builder.getContext()));
1017+
auto Fn = dyn_cast<mlir::cir::FuncOp>(CurFn);
1018+
assert(Fn && "other callables NYI");
1019+
Fn.setLambdaAttr(mlir::UnitAttr::get(builder.getContext()));
10161020

10171021
// Figure out the captures.
10181022
MD->getParent()->getCaptureFields(LambdaCaptureFields,

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,9 @@ class CIRGenFunction : public CIRGenTypeCache {
576576
const clang::Decl *CurCodeDecl;
577577
const CIRGenFunctionInfo *CurFnInfo;
578578
clang::QualType FnRetTy;
579-
mlir::cir::FuncOp CurFn = nullptr;
579+
580+
/// This is the current function or global initializer that is generated code for.
581+
mlir::Operation *CurFn = nullptr;
580582

581583
/// Save Parameter Decl for coroutine.
582584
llvm::SmallVector<const ParmVarDecl *, 4> FnArgs;
@@ -591,6 +593,12 @@ class CIRGenFunction : public CIRGenTypeCache {
591593

592594
CIRGenModule &getCIRGenModule() { return CGM; }
593595

596+
mlir::Block* getCurFunctionEntryBlock() {
597+
auto Fn = dyn_cast<mlir::cir::FuncOp>(CurFn);
598+
assert(Fn && "other callables NYI");
599+
return &Fn.getRegion().front();
600+
}
601+
594602
/// Sanitizers enabled for this function.
595603
clang::SanitizerSet SanOpts;
596604

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ mlir::cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &CGM,
479479
// Be sure to insert global before the current function
480480
auto *curCGF = CGM.getCurrCIRGenFun();
481481
if (curCGF)
482-
builder.setInsertionPoint(curCGF->CurFn.getOperation());
482+
builder.setInsertionPoint(curCGF->CurFn);
483483

484484
g = builder.create<mlir::cir::GlobalOp>(loc, name, t, isCst);
485485
if (!curCGF)
@@ -784,8 +784,14 @@ void CIRGenModule::buildGlobalVarDefinition(const clang::VarDecl *D,
784784
// TODO(cir): LLVM's codegen uses a llvm::TrackingVH here. Is that
785785
// necessary here for CIR gen?
786786
mlir::Attribute Init;
787-
// TODO(cir): bool NeedsGlobalCtor = false;
787+
bool NeedsGlobalCtor = false;
788+
// Whether the definition of the variable is available externally.
789+
// If yes, we shouldn't emit the GloablCtor and GlobalDtor for the variable
790+
// since this is the job for its original source.
791+
bool IsDefinitionAvailableExternally =
792+
astCtx.GetGVALinkageForVariable(D) == GVA_AvailableExternally;
788793
bool NeedsGlobalDtor =
794+
!IsDefinitionAvailableExternally &&
789795
D->needsDestruction(astCtx) == QualType::DK_cxx_destructor;
790796

791797
const VarDecl *InitDecl;
@@ -831,7 +837,19 @@ void CIRGenModule::buildGlobalVarDefinition(const clang::VarDecl *D,
831837
emitter.emplace(*this);
832838
auto Initializer = emitter->tryEmitForInitializer(*InitDecl);
833839
if (!Initializer) {
834-
assert(0 && "not implemented");
840+
QualType T = InitExpr->getType();
841+
if (D->getType()->isReferenceType())
842+
T = D->getType();
843+
844+
if (getLangOpts().CPlusPlus) {
845+
if (InitDecl->hasFlexibleArrayInit(astCtx))
846+
ErrorUnsupported(D, "flexible array initializer");
847+
Init = builder.getZeroInitAttr(getCIRType(T));
848+
if (!IsDefinitionAvailableExternally)
849+
NeedsGlobalCtor = true;
850+
} else {
851+
ErrorUnsupported(D, "static initializer");
852+
}
835853
} else {
836854
Init = Initializer;
837855
// We don't need an initializer, so remove the entry for the delayed
@@ -973,8 +991,8 @@ void CIRGenModule::buildGlobalVarDefinition(const clang::VarDecl *D,
973991

974992
// TODO(cir):
975993
// Emit the initializer function if necessary.
976-
// if (NeedsGlobalCtor || NeedsGlobalDtor)
977-
// EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor);
994+
if (NeedsGlobalCtor || NeedsGlobalDtor)
995+
buildGlobalVarDeclInit(D, GV, NeedsGlobalCtor);
978996

979997
// TODO(cir): sanitizers (reportGlobalToASan) and global variable debug
980998
// information.
@@ -1790,7 +1808,7 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name,
17901808
// Be sure to insert a new function before a current one.
17911809
auto *curCGF = getCurrCIRGenFun();
17921810
if (curCGF)
1793-
builder.setInsertionPoint(curCGF->CurFn.getOperation());
1811+
builder.setInsertionPoint(curCGF->CurFn);
17941812

17951813
f = builder.create<mlir::cir::FuncOp>(loc, name, Ty);
17961814

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,10 @@ class CIRGenModule : public CIRGenTypeCache {
477477
void buildGlobalVarDefinition(const clang::VarDecl *D,
478478
bool IsTentative = false);
479479

480+
/// Emit the function that initializes the specified global
481+
void buildGlobalVarDeclInit(const VarDecl *D, mlir::cir::GlobalOp Addr,
482+
bool PerformInit);
483+
480484
void addDeferredVTable(const CXXRecordDecl *RD) {
481485
DeferredVTables.push_back(RD);
482486
}
@@ -508,6 +512,10 @@ class CIRGenModule : public CIRGenTypeCache {
508512
// or if they are alias to each other.
509513
mlir::cir::FuncOp codegenCXXStructor(clang::GlobalDecl GD);
510514

515+
// Produce code for this constructor/destructor for global initialzation.
516+
void codegenGlobalInitCxxStructor(const clang::VarDecl *D,
517+
mlir::cir::GlobalOp Addr);
518+
511519
bool lookupRepresentativeDecl(llvm::StringRef MangledName,
512520
clang::GlobalDecl &Result) const;
513521

0 commit comments

Comments
 (0)