Skip to content

Commit ee7ed16

Browse files
andykaylorgithub-actions[bot]
authored andcommitted
Automerge: [CIR] Finish global ctor init lowering (#162130)
Implement support for creating a global ctor list during the cir::LoweringPrepare pass, and add tests for lowering global constructor-based initialization to LLVM IR.
2 parents d94ff59 + eb1ce38 commit ee7ed16

File tree

6 files changed

+184
-5
lines changed

6 files changed

+184
-5
lines changed

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,51 @@ def CIR_VisibilityAttr : CIR_EnumAttr<CIR_VisibilityKind, "visibility"> {
769769
}];
770770
}
771771

772+
//===----------------------------------------------------------------------===//
773+
// GloblCtorAttr
774+
//===----------------------------------------------------------------------===//
775+
776+
class CIR_GlobalCtorDtor<string name, string attrMnemonic>
777+
: CIR_Attr<"Global" # name, "global_" # attrMnemonic> {
778+
let parameters = (ins "mlir::StringAttr":$name, "int":$priority);
779+
780+
let skipDefaultBuilders = 1;
781+
let builders = [
782+
AttrBuilder<(ins
783+
"llvm::StringRef":$name,
784+
CArg<"int", "65535">:$priority), [{
785+
return $_get($_ctxt, mlir::StringAttr::get($_ctxt, name), priority);
786+
}]>,
787+
AttrBuilderWithInferredContext<(ins
788+
"mlir::StringAttr":$name,
789+
CArg<"int", "65535">:$priority), [{
790+
return $_get(name.getContext(), name, priority);
791+
}]>
792+
];
793+
794+
let assemblyFormat = [{
795+
`<` $name `,` $priority `>`
796+
}];
797+
798+
let extraClassDeclaration = [{
799+
bool isDefaultPriority() const {
800+
return getPriority() == getDefaultPriority();
801+
};
802+
803+
static int getDefaultPriority() {
804+
return 65535;
805+
}
806+
}];
807+
}
808+
809+
def CIR_GlobalCtorAttr : CIR_GlobalCtorDtor<"Ctor", "ctor"> {
810+
let summary = "Marks a function as a global constructor";
811+
let description = [{
812+
Marks the function as a global constructor in the module's constructor list.
813+
It will be executed before main() is called.
814+
}];
815+
}
816+
772817
//===----------------------------------------------------------------------===//
773818
// BitfieldInfoAttr
774819
//===----------------------------------------------------------------------===//

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def CIR_Dialect : Dialect {
4242
static llvm::StringRef getNoThrowAttrName() { return "nothrow"; }
4343
static llvm::StringRef getSideEffectAttrName() { return "side_effect"; }
4444
static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
45+
static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; }
4546

4647
void registerAttributes();
4748
void registerTypes();

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ struct MissingFeatures {
3939
static bool opGlobalUsedOrCompilerUsed() { return false; }
4040
static bool opGlobalAnnotations() { return false; }
4141
static bool opGlobalDtorLowering() { return false; }
42-
static bool opGlobalCtorAttr() { return false; }
4342
static bool opGlobalCtorPriority() { return false; }
4443
static bool opGlobalCtorList() { return false; }
4544
static bool setDSOLocal() { return false; }

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
6161
/// Build a module init function that calls all the dynamic initializers.
6262
void buildCXXGlobalInitFunc();
6363

64+
/// Materialize global ctor/dtor list
65+
void buildGlobalCtorDtorList();
66+
6467
cir::FuncOp buildRuntimeFunction(
6568
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
6669
cir::FuncType type,
@@ -79,6 +82,9 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
7982
llvm::StringMap<uint32_t> dynamicInitializerNames;
8083
llvm::SmallVector<cir::FuncOp> dynamicInitializers;
8184

85+
/// List of ctors and their priorities to be called before main()
86+
llvm::SmallVector<std::pair<std::string, uint32_t>, 4> globalCtorList;
87+
8288
void setASTContext(clang::ASTContext *c) { astCtx = c; }
8389
};
8490

@@ -689,11 +695,36 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
689695
assert(!cir::MissingFeatures::opGlobalAnnotations());
690696
}
691697

698+
template <typename AttributeTy>
699+
static llvm::SmallVector<mlir::Attribute>
700+
prepareCtorDtorAttrList(mlir::MLIRContext *context,
701+
llvm::ArrayRef<std::pair<std::string, uint32_t>> list) {
702+
llvm::SmallVector<mlir::Attribute> attrs;
703+
for (const auto &[name, priority] : list)
704+
attrs.push_back(AttributeTy::get(context, name, priority));
705+
return attrs;
706+
}
707+
708+
void LoweringPreparePass::buildGlobalCtorDtorList() {
709+
if (!globalCtorList.empty()) {
710+
llvm::SmallVector<mlir::Attribute> globalCtors =
711+
prepareCtorDtorAttrList<cir::GlobalCtorAttr>(&getContext(),
712+
globalCtorList);
713+
714+
mlirModule->setAttr(cir::CIRDialect::getGlobalCtorsAttrName(),
715+
mlir::ArrayAttr::get(&getContext(), globalCtors));
716+
}
717+
718+
assert(!cir::MissingFeatures::opGlobalDtorLowering());
719+
}
720+
692721
void LoweringPreparePass::buildCXXGlobalInitFunc() {
693722
if (dynamicInitializers.empty())
694723
return;
695724

696-
assert(!cir::MissingFeatures::opGlobalCtorList());
725+
// TODO: handle globals with a user-specified initialzation priority.
726+
// TODO: handle default priority more nicely.
727+
assert(!cir::MissingFeatures::opGlobalCtorPriority());
697728

698729
SmallString<256> fnName;
699730
// Include the filename in the symbol name. Including "sub_" matches gcc
@@ -722,6 +753,10 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() {
722753
builder.setInsertionPointToStart(f.addEntryBlock());
723754
for (cir::FuncOp &f : dynamicInitializers)
724755
builder.createCallOp(f.getLoc(), f, {});
756+
// Add the global init function (not the individual ctor functions) to the
757+
// global ctor list.
758+
globalCtorList.emplace_back(fnName,
759+
cir::GlobalCtorAttr::getDefaultPriority());
725760

726761
cir::ReturnOp::create(builder, f.getLoc());
727762
}
@@ -852,6 +887,7 @@ void LoweringPreparePass::runOnOperation() {
852887
runOnOp(o);
853888

854889
buildCXXGlobalInitFunc();
890+
buildGlobalCtorDtorList();
855891
}
856892

857893
std::unique_ptr<Pass> mlir::createLoweringPreparePass() {

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,73 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
24132413
});
24142414
}
24152415

2416+
static void buildCtorDtorList(
2417+
mlir::ModuleOp module, StringRef globalXtorName, StringRef llvmXtorName,
2418+
llvm::function_ref<std::pair<StringRef, int>(mlir::Attribute)> createXtor) {
2419+
llvm::SmallVector<std::pair<StringRef, int>> globalXtors;
2420+
for (const mlir::NamedAttribute namedAttr : module->getAttrs()) {
2421+
if (namedAttr.getName() == globalXtorName) {
2422+
for (auto attr : mlir::cast<mlir::ArrayAttr>(namedAttr.getValue()))
2423+
globalXtors.emplace_back(createXtor(attr));
2424+
break;
2425+
}
2426+
}
2427+
2428+
if (globalXtors.empty())
2429+
return;
2430+
2431+
mlir::OpBuilder builder(module.getContext());
2432+
builder.setInsertionPointToEnd(&module.getBodyRegion().back());
2433+
2434+
// Create a global array llvm.global_ctors with element type of
2435+
// struct { i32, ptr, ptr }
2436+
auto ctorPFTy = mlir::LLVM::LLVMPointerType::get(builder.getContext());
2437+
llvm::SmallVector<mlir::Type> ctorStructFields;
2438+
ctorStructFields.push_back(builder.getI32Type());
2439+
ctorStructFields.push_back(ctorPFTy);
2440+
ctorStructFields.push_back(ctorPFTy);
2441+
2442+
auto ctorStructTy = mlir::LLVM::LLVMStructType::getLiteral(
2443+
builder.getContext(), ctorStructFields);
2444+
auto ctorStructArrayTy =
2445+
mlir::LLVM::LLVMArrayType::get(ctorStructTy, globalXtors.size());
2446+
2447+
mlir::Location loc = module.getLoc();
2448+
auto newGlobalOp = mlir::LLVM::GlobalOp::create(
2449+
builder, loc, ctorStructArrayTy, /*constant=*/false,
2450+
mlir::LLVM::Linkage::Appending, llvmXtorName, mlir::Attribute());
2451+
2452+
builder.createBlock(&newGlobalOp.getRegion());
2453+
builder.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
2454+
2455+
mlir::Value result =
2456+
mlir::LLVM::UndefOp::create(builder, loc, ctorStructArrayTy);
2457+
2458+
for (auto [index, fn] : llvm::enumerate(globalXtors)) {
2459+
mlir::Value structInit =
2460+
mlir::LLVM::UndefOp::create(builder, loc, ctorStructTy);
2461+
mlir::Value initPriority = mlir::LLVM::ConstantOp::create(
2462+
builder, loc, ctorStructFields[0], fn.second);
2463+
mlir::Value initFuncAddr = mlir::LLVM::AddressOfOp::create(
2464+
builder, loc, ctorStructFields[1], fn.first);
2465+
mlir::Value initAssociate =
2466+
mlir::LLVM::ZeroOp::create(builder, loc, ctorStructFields[2]);
2467+
// Literal zero makes the InsertValueOp::create ambiguous.
2468+
llvm::SmallVector<int64_t> zero{0};
2469+
structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
2470+
initPriority, zero);
2471+
structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
2472+
initFuncAddr, 1);
2473+
// TODO: handle associated data for initializers.
2474+
structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
2475+
initAssociate, 2);
2476+
result = mlir::LLVM::InsertValueOp::create(builder, loc, result, structInit,
2477+
index);
2478+
}
2479+
2480+
builder.create<mlir::LLVM::ReturnOp>(loc, result);
2481+
}
2482+
24162483
// The applyPartialConversion function traverses blocks in the dominance order,
24172484
// so it does not lower and operations that are not reachachable from the
24182485
// operations passed in as arguments. Since we do need to lower such code in
@@ -2519,6 +2586,14 @@ void ConvertCIRToLLVMPass::runOnOperation() {
25192586

25202587
if (failed(applyPartialConversion(ops, target, std::move(patterns))))
25212588
signalPassFailure();
2589+
2590+
// Emit the llvm.global_ctors array.
2591+
buildCtorDtorList(module, cir::CIRDialect::getGlobalCtorsAttrName(),
2592+
"llvm.global_ctors", [](mlir::Attribute attr) {
2593+
auto ctorAttr = mlir::cast<cir::GlobalCtorAttr>(attr);
2594+
return std::make_pair(ctorAttr.getName(),
2595+
ctorAttr.getPriority());
2596+
});
25222597
}
25232598

25242599
mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(

clang/test/CIR/CodeGen/global-init.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// 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
22
// RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP
33
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
4-
5-
// Note: The LoweringPrepare work isn't yet complete. We still need to create
6-
// the global ctor list attribute.
4+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
5+
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
6+
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
7+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
78

89
struct NeedsCtor {
910
NeedsCtor();
@@ -15,6 +16,9 @@ NeedsCtor needsCtor;
1516
// CIR-BEFORE-LPP: %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
1617
// CIR-BEFORE-LPP: cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> ()
1718

19+
// CIR: module @{{.*}} attributes {
20+
// CIR-SAME: cir.global_ctors = [#cir.global_ctor<"_GLOBAL__sub_I_[[FILENAME:.*]]", 65535>]
21+
1822
// CIR: cir.global external @needsCtor = #cir.zero : !rec_NeedsCtor
1923
// CIR: cir.func internal private @__cxx_global_var_init() {
2024
// CIR: %0 = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
@@ -24,3 +28,22 @@ NeedsCtor needsCtor;
2428
// CIR: cir.call @__cxx_global_var_init() : () -> ()
2529
// CIR: cir.return
2630
// CIR: }
31+
32+
// LLVM: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1
33+
// LLVM: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
34+
// LLVM: declare void @_ZN9NeedsCtorC1Ev(ptr)
35+
36+
// LLVM: define internal void @__cxx_global_var_init()
37+
// LLVM: call void @_ZN9NeedsCtorC1Ev(ptr @needsCtor)
38+
39+
// LLVM: define void @_GLOBAL__sub_I_[[FILENAME]]()
40+
// LLVM: call void @__cxx_global_var_init()
41+
42+
// OGCG: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1
43+
// OGCG: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
44+
45+
// OGCG: define internal void @__cxx_global_var_init() {{.*}} section ".text.startup" {
46+
// OGCG: call void @_ZN9NeedsCtorC1Ev(ptr noundef nonnull align 1 dereferenceable(1) @needsCtor)
47+
48+
// OGCG: define internal void @_GLOBAL__sub_I_[[FILENAME]]() {{.*}} section ".text.startup" {
49+
// OGCG: call void @__cxx_global_var_init()

0 commit comments

Comments
 (0)