Skip to content

Commit 44b2673

Browse files
authored
[CIR] Implement initial LoweringPrepare support for global ctors (#161452)
This adds the initial support for lowering the 'ctor' region of cir.global operations to an init function which is called from a TU-specific static initialization function. This does not yet add an attribute to hold a list of global initializers. That will be added in a future change.
1 parent 9e8dda1 commit 44b2673

File tree

4 files changed

+165
-8
lines changed

4 files changed

+165
-8
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
125125
cir::ConstantOp getTrue(mlir::Location loc) { return getBool(true, loc); }
126126

127127
cir::BoolType getBoolTy() { return cir::BoolType::get(getContext()); }
128+
cir::VoidType getVoidTy() { return cir::VoidType::get(getContext()); }
128129

129130
cir::PointerType getPointerTo(mlir::Type ty) {
130131
return cir::PointerType::get(ty);

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ struct MissingFeatures {
3737
static bool opGlobalDLLImportExport() { return false; }
3838
static bool opGlobalPartition() { return false; }
3939
static bool opGlobalUsedOrCompilerUsed() { return false; }
40+
static bool opGlobalAnnotations() { return false; }
41+
static bool opGlobalDtorLowering() { return false; }
42+
static bool opGlobalCtorAttr() { return false; }
43+
static bool opGlobalCtorPriority() { return false; }
44+
static bool opGlobalCtorList() { return false; }
4045
static bool setDSOLocal() { return false; }
4146
static bool setComdat() { return false; }
4247

clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,39 @@
88

99
#include "PassDetail.h"
1010
#include "clang/AST/ASTContext.h"
11+
#include "clang/Basic/Module.h"
1112
#include "clang/Basic/TargetInfo.h"
1213
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
1314
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1415
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
1516
#include "clang/CIR/Dialect/Passes.h"
1617
#include "clang/CIR/MissingFeatures.h"
18+
#include "llvm/Support/Path.h"
1719

1820
#include <memory>
1921

2022
using namespace mlir;
2123
using namespace cir;
2224

25+
static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
26+
SmallString<128> fileName;
27+
28+
if (mlirModule.getSymName())
29+
fileName = llvm::sys::path::filename(mlirModule.getSymName()->str());
30+
31+
if (fileName.empty())
32+
fileName = "<null>";
33+
34+
for (size_t i = 0; i < fileName.size(); ++i) {
35+
// Replace everything that's not [a-zA-Z0-9._] with a _. This set happens
36+
// to be the set of C preprocessing numbers.
37+
if (!clang::isPreprocessingNumberBody(fileName[i]))
38+
fileName[i] = '_';
39+
}
40+
41+
return fileName;
42+
}
43+
2344
namespace {
2445
struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
2546
LoweringPreparePass() = default;
@@ -30,9 +51,16 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
3051
void lowerComplexDivOp(cir::ComplexDivOp op);
3152
void lowerComplexMulOp(cir::ComplexMulOp op);
3253
void lowerUnaryOp(cir::UnaryOp op);
54+
void lowerGlobalOp(cir::GlobalOp op);
3355
void lowerArrayDtor(cir::ArrayDtor op);
3456
void lowerArrayCtor(cir::ArrayCtor op);
3557

58+
/// Build the function that initializes the specified global
59+
cir::FuncOp buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op);
60+
61+
/// Build a module init function that calls all the dynamic initializers.
62+
void buildCXXGlobalInitFunc();
63+
3664
cir::FuncOp buildRuntimeFunction(
3765
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
3866
cir::FuncType type,
@@ -47,6 +75,10 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
4775
/// Tracks current module.
4876
mlir::ModuleOp mlirModule;
4977

78+
/// Tracks existing dynamic initializers.
79+
llvm::StringMap<uint32_t> dynamicInitializerNames;
80+
llvm::SmallVector<cir::FuncOp> dynamicInitializers;
81+
5082
void setASTContext(clang::ASTContext *c) { astCtx = c; }
5183
};
5284

@@ -589,6 +621,111 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
589621
op.erase();
590622
}
591623

624+
cir::FuncOp
625+
LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
626+
// TODO(cir): Store this in the GlobalOp.
627+
// This should come from the MangleContext, but for now I'm hardcoding it.
628+
SmallString<256> fnName("__cxx_global_var_init");
629+
// Get a unique name
630+
uint32_t cnt = dynamicInitializerNames[fnName]++;
631+
if (cnt)
632+
fnName += "." + llvm::Twine(cnt).str();
633+
634+
// Create a variable initialization function.
635+
CIRBaseBuilderTy builder(getContext());
636+
builder.setInsertionPointAfter(op);
637+
auto fnType = cir::FuncType::get({}, builder.getVoidTy());
638+
FuncOp f = buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
639+
cir::GlobalLinkageKind::InternalLinkage);
640+
641+
// Move over the initialzation code of the ctor region.
642+
mlir::Block *entryBB = f.addEntryBlock();
643+
if (!op.getCtorRegion().empty()) {
644+
mlir::Block &block = op.getCtorRegion().front();
645+
entryBB->getOperations().splice(entryBB->begin(), block.getOperations(),
646+
block.begin(), std::prev(block.end()));
647+
}
648+
649+
// Register the destructor call with __cxa_atexit
650+
mlir::Region &dtorRegion = op.getDtorRegion();
651+
if (!dtorRegion.empty()) {
652+
assert(!cir::MissingFeatures::opGlobalDtorLowering());
653+
llvm_unreachable("dtor region lowering is NYI");
654+
}
655+
656+
// Replace cir.yield with cir.return
657+
builder.setInsertionPointToEnd(entryBB);
658+
mlir::Operation *yieldOp = nullptr;
659+
if (!op.getCtorRegion().empty()) {
660+
mlir::Block &block = op.getCtorRegion().front();
661+
yieldOp = &block.getOperations().back();
662+
} else {
663+
assert(!cir::MissingFeatures::opGlobalDtorLowering());
664+
llvm_unreachable("dtor region lowering is NYI");
665+
}
666+
667+
assert(isa<YieldOp>(*yieldOp));
668+
cir::ReturnOp::create(builder, yieldOp->getLoc());
669+
return f;
670+
}
671+
672+
void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
673+
mlir::Region &ctorRegion = op.getCtorRegion();
674+
mlir::Region &dtorRegion = op.getDtorRegion();
675+
676+
if (!ctorRegion.empty() || !dtorRegion.empty()) {
677+
// Build a variable initialization function and move the initialzation code
678+
// in the ctor region over.
679+
cir::FuncOp f = buildCXXGlobalVarDeclInitFunc(op);
680+
681+
// Clear the ctor and dtor region
682+
ctorRegion.getBlocks().clear();
683+
dtorRegion.getBlocks().clear();
684+
685+
assert(!cir::MissingFeatures::astVarDeclInterface());
686+
dynamicInitializers.push_back(f);
687+
}
688+
689+
assert(!cir::MissingFeatures::opGlobalAnnotations());
690+
}
691+
692+
void LoweringPreparePass::buildCXXGlobalInitFunc() {
693+
if (dynamicInitializers.empty())
694+
return;
695+
696+
assert(!cir::MissingFeatures::opGlobalCtorList());
697+
698+
SmallString<256> fnName;
699+
// Include the filename in the symbol name. Including "sub_" matches gcc
700+
// and makes sure these symbols appear lexicographically behind the symbols
701+
// with priority (TBD). Module implementation units behave the same
702+
// way as a non-modular TU with imports.
703+
// TODO: check CXX20ModuleInits
704+
if (astCtx->getCurrentNamedModule() &&
705+
!astCtx->getCurrentNamedModule()->isModuleImplementation()) {
706+
llvm::raw_svector_ostream out(fnName);
707+
std::unique_ptr<clang::MangleContext> mangleCtx(
708+
astCtx->createMangleContext());
709+
cast<clang::ItaniumMangleContext>(*mangleCtx)
710+
.mangleModuleInitializer(astCtx->getCurrentNamedModule(), out);
711+
} else {
712+
fnName += "_GLOBAL__sub_I_";
713+
fnName += getTransformedFileName(mlirModule);
714+
}
715+
716+
CIRBaseBuilderTy builder(getContext());
717+
builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
718+
auto fnType = cir::FuncType::get({}, builder.getVoidTy());
719+
cir::FuncOp f =
720+
buildRuntimeFunction(builder, fnName, mlirModule.getLoc(), fnType,
721+
cir::GlobalLinkageKind::ExternalLinkage);
722+
builder.setInsertionPointToStart(f.addEntryBlock());
723+
for (cir::FuncOp &f : dynamicInitializers)
724+
builder.createCallOp(f.getLoc(), f, {});
725+
726+
cir::ReturnOp::create(builder, f.getLoc());
727+
}
728+
592729
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
593730
clang::ASTContext *astCtx,
594731
mlir::Operation *op, mlir::Type eltTy,
@@ -691,6 +828,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
691828
lowerComplexDivOp(complexDiv);
692829
else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op))
693830
lowerComplexMulOp(complexMul);
831+
else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op))
832+
lowerGlobalOp(glob);
694833
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
695834
lowerUnaryOp(unary);
696835
}
@@ -704,12 +843,15 @@ void LoweringPreparePass::runOnOperation() {
704843

705844
op->walk([&](mlir::Operation *op) {
706845
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
707-
cir::ComplexMulOp, cir::ComplexDivOp, cir::UnaryOp>(op))
846+
cir::ComplexMulOp, cir::ComplexDivOp, cir::GlobalOp,
847+
cir::UnaryOp>(op))
708848
opsToTransform.push_back(op);
709849
});
710850

711851
for (mlir::Operation *o : opsToTransform)
712852
runOnOp(o);
853+
854+
buildCXXGlobalInitFunc();
713855
}
714856

715857
std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1-
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
1+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
2+
// RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP
23
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
34

4-
// Note: The CIR generated from this test isn't ready for lowering to LLVM yet.
5-
// That will require changes to LoweringPrepare.
5+
// Note: The LoweringPrepare work isn't yet complete. We still need to create
6+
// the global ctor list attribute.
67

78
struct NeedsCtor {
89
NeedsCtor();
910
};
1011

1112
NeedsCtor needsCtor;
1213

13-
// CIR: cir.func private @_ZN9NeedsCtorC1Ev(!cir.ptr<!rec_NeedsCtor>)
14-
// CIR: cir.global external @needsCtor = ctor : !rec_NeedsCtor {
15-
// CIR: %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
16-
// CIR: cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> ()
14+
// CIR-BEFORE-LPP: cir.global external @needsCtor = ctor : !rec_NeedsCtor {
15+
// CIR-BEFORE-LPP: %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
16+
// CIR-BEFORE-LPP: cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> ()
17+
18+
// CIR: cir.global external @needsCtor = #cir.zero : !rec_NeedsCtor
19+
// CIR: cir.func internal private @__cxx_global_var_init() {
20+
// CIR: %0 = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
21+
// CIR: cir.call @_ZN9NeedsCtorC1Ev(%0) : (!cir.ptr<!rec_NeedsCtor>) -> ()
22+
23+
// CIR: cir.func private @_GLOBAL__sub_I_[[FILENAME:.*]]() {
24+
// CIR: cir.call @__cxx_global_var_init() : () -> ()
25+
// CIR: cir.return
1726
// CIR: }

0 commit comments

Comments
 (0)