Skip to content

Commit 645745f

Browse files
authored
[CIR] Add support for global ctor/dtor attributes (#163247)
This adds support for adding the `global_ctor` or `global_dtor` attribute to the CIR representation of functions defined with `__attribute__((constructor))` or `__attribute__((destructor))` and adding them to the `@llvm.global_ctors` or `@llvm.global_dtors` list during lowering to LLVM IR.
1 parent 472ee43 commit 645745f

File tree

11 files changed

+244
-20
lines changed

11 files changed

+244
-20
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,14 @@ def CIR_GlobalCtorAttr : CIR_GlobalCtorDtor<"Ctor", "ctor"> {
814814
}];
815815
}
816816

817+
def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
818+
let summary = "Marks a function as a global destructor";
819+
let description = [{
820+
Marks a function as a global destructor in the module dtors list.
821+
The function will be executed before the module unloading.
822+
}];
823+
}
824+
817825
//===----------------------------------------------------------------------===//
818826
// BitfieldInfoAttr
819827
//===----------------------------------------------------------------------===//

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def CIR_Dialect : Dialect {
4343
static llvm::StringRef getSideEffectAttrName() { return "side_effect"; }
4444
static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
4545
static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; }
46+
static llvm::StringRef getGlobalDtorsAttrName() { return "cir.global_dtors"; }
4647

4748
void registerAttributes();
4849
void registerTypes();

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ class CIR_Op<string mnemonic, list<Trait> traits = []> :
8888
code extraLLVMLoweringPatternDecl = "";
8989
}
9090

91+
//===----------------------------------------------------------------------===//
92+
// CIR Operation Traits
93+
//===----------------------------------------------------------------------===//
94+
95+
class HasAtMostOneOfAttrsPred<list<string> names> :
96+
CPred<!foldl("0", names, acc, name, acc # " + (" # name # " ? 1 : 0)")
97+
# " <= 1">;
98+
99+
class HasAtMostOneOfAttrs<list<string> names> : PredOpTrait<
100+
"has only one of the optional attributes: " # !interleave(names, ", "),
101+
HasAtMostOneOfAttrsPred<!foreach(name, names, "$" # name)>
102+
>;
103+
91104
//===----------------------------------------------------------------------===//
92105
// CastOp
93106
//===----------------------------------------------------------------------===//
@@ -2422,9 +2435,17 @@ def CIR_GetMemberOp : CIR_Op<"get_member"> {
24222435
// TODO(CIR): FuncOp is still a tiny shell of what it will become. Many more
24232436
// properties and attributes will be added as upstreaming continues.
24242437

2438+
def CIR_OptionalPriorityAttr : OptionalAttr<
2439+
DefaultValuedAttr<
2440+
ConfinedAttr<I32Attr, [IntMinValue<101>, IntMaxValue<65535>]>,
2441+
"65535"
2442+
>
2443+
>;
2444+
24252445
def CIR_FuncOp : CIR_Op<"func", [
24262446
AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
24272447
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
2448+
HasAtMostOneOfAttrs<["global_ctor_priority", "global_dtor_priority"]>,
24282449
IsolatedFromAbove
24292450
]> {
24302451
let summary = "Declare or define a function";
@@ -2449,6 +2470,12 @@ def CIR_FuncOp : CIR_Op<"func", [
24492470
without a prototype and, consequently, may contain calls with invalid
24502471
arguments and undefined behavior.
24512472

2473+
The `global_ctor` keyword indicates whether a function should execute before
2474+
`main()` function, as specified by `__attribute__((constructor))`. An
2475+
execution priority can also be specified `global_ctor(<priority>)`.
2476+
Similarly, for global destructors both `global_dtor` and
2477+
`global_dtor(<priority>)` are available.
2478+
24522479
Example:
24532480

24542481
```mlir
@@ -2487,7 +2514,9 @@ def CIR_FuncOp : CIR_Op<"func", [
24872514
UnitAttr:$comdat,
24882515
OptionalAttr<DictArrayAttr>:$arg_attrs,
24892516
OptionalAttr<DictArrayAttr>:$res_attrs,
2490-
OptionalAttr<FlatSymbolRefAttr>:$aliasee);
2517+
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
2518+
CIR_OptionalPriorityAttr:$global_ctor_priority,
2519+
CIR_OptionalPriorityAttr:$global_dtor_priority);
24912520

24922521
let regions = (region AnyRegion:$body);
24932522

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 4 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 opGlobalCtorPriority() { return false; }
42-
static bool opGlobalDtorList() { return false; }
4342
static bool setDSOLocal() { return false; }
4443
static bool setComdat() { return false; }
4544

@@ -175,6 +174,10 @@ struct MissingFeatures {
175174
static bool atomicScope() { return false; }
176175
static bool atomicSyncScopeID() { return false; }
177176

177+
// Global ctor handling
178+
static bool globalCtorLexOrder() { return false; }
179+
static bool globalCtorAssociatedData() { return false; }
180+
178181
// Misc
179182
static bool abiArgInfo() { return false; }
180183
static bool addHeapAllocSiteMetadata() { return false; }

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -451,15 +451,47 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
451451
setNonAliasAttributes(gd, funcOp);
452452
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());
453453

454-
if (funcDecl->getAttr<ConstructorAttr>())
455-
errorNYI(funcDecl->getSourceRange(), "constructor attribute");
456-
if (funcDecl->getAttr<DestructorAttr>())
457-
errorNYI(funcDecl->getSourceRange(), "destructor attribute");
454+
auto getPriority = [this](const auto *attr) -> int {
455+
Expr *e = attr->getPriority();
456+
if (e)
457+
return e->EvaluateKnownConstInt(this->getASTContext()).getExtValue();
458+
return attr->DefaultPriority;
459+
};
460+
461+
if (const ConstructorAttr *ca = funcDecl->getAttr<ConstructorAttr>())
462+
addGlobalCtor(funcOp, getPriority(ca));
463+
if (const DestructorAttr *da = funcDecl->getAttr<DestructorAttr>())
464+
addGlobalDtor(funcOp, getPriority(da));
458465

459466
if (funcDecl->getAttr<AnnotateAttr>())
460467
errorNYI(funcDecl->getSourceRange(), "deferredAnnotations");
461468
}
462469

470+
/// Track functions to be called before main() runs.
471+
void CIRGenModule::addGlobalCtor(cir::FuncOp ctor,
472+
std::optional<int> priority) {
473+
assert(!cir::MissingFeatures::globalCtorLexOrder());
474+
assert(!cir::MissingFeatures::globalCtorAssociatedData());
475+
476+
// Traditional LLVM codegen directly adds the function to the list of global
477+
// ctors. In CIR we just add a global_ctor attribute to the function. The
478+
// global list is created in LoweringPrepare.
479+
//
480+
// FIXME(from traditional LLVM): Type coercion of void()* types.
481+
ctor.setGlobalCtorPriority(priority);
482+
}
483+
484+
/// Add a function to the list that will be called when the module is unloaded.
485+
void CIRGenModule::addGlobalDtor(cir::FuncOp dtor,
486+
std::optional<int> priority) {
487+
if (codeGenOpts.RegisterGlobalDtorsWithAtExit &&
488+
(!getASTContext().getTargetInfo().getTriple().isOSAIX()))
489+
errorNYI(dtor.getLoc(), "registerGlobalDtorsWithAtExit");
490+
491+
// FIXME(from traditional LLVM): Type coercion of void()* types.
492+
dtor.setGlobalDtorPriority(priority);
493+
}
494+
463495
void CIRGenModule::handleCXXStaticMemberVarInstantiation(VarDecl *vd) {
464496
VarDecl::DefinitionKind dk = vd->isThisDeclarationADefinition();
465497
if (dk == VarDecl::Definition && vd->hasAttr<DLLImportAttr>())

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,13 @@ class CIRGenModule : public CIRGenTypeCache {
159159
bool isConstant = false,
160160
mlir::Operation *insertPoint = nullptr);
161161

162+
/// Add a global constructor or destructor to the module.
163+
/// The priority is optional, if not specified, the default priority is used.
164+
void addGlobalCtor(cir::FuncOp ctor,
165+
std::optional<int> priority = std::nullopt);
166+
void addGlobalDtor(cir::FuncOp dtor,
167+
std::optional<int> priority = std::nullopt);
168+
162169
bool shouldZeroInitPadding() const {
163170
// In C23 (N3096) $6.7.10:
164171
// """

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
1616
#include "clang/CIR/Dialect/IR/CIRTypes.h"
1717

18+
#include "mlir/IR/DialectImplementation.h"
1819
#include "mlir/Interfaces/ControlFlowInterfaces.h"
1920
#include "mlir/Interfaces/FunctionImplementation.h"
2021
#include "mlir/Support/LLVM.h"
@@ -1720,6 +1721,43 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
17201721
hasAlias = true;
17211722
}
17221723

1724+
auto parseGlobalDtorCtor =
1725+
[&](StringRef keyword,
1726+
llvm::function_ref<void(std::optional<int> prio)> createAttr)
1727+
-> mlir::LogicalResult {
1728+
if (mlir::succeeded(parser.parseOptionalKeyword(keyword))) {
1729+
std::optional<int> priority;
1730+
if (mlir::succeeded(parser.parseOptionalLParen())) {
1731+
auto parsedPriority = mlir::FieldParser<int>::parse(parser);
1732+
if (mlir::failed(parsedPriority))
1733+
return parser.emitError(parser.getCurrentLocation(),
1734+
"failed to parse 'priority', of type 'int'");
1735+
priority = parsedPriority.value_or(int());
1736+
// Parse literal ')'
1737+
if (parser.parseRParen())
1738+
return failure();
1739+
}
1740+
createAttr(priority);
1741+
}
1742+
return success();
1743+
};
1744+
1745+
if (parseGlobalDtorCtor("global_ctor", [&](std::optional<int> priority) {
1746+
mlir::IntegerAttr globalCtorPriorityAttr =
1747+
builder.getI32IntegerAttr(priority.value_or(65535));
1748+
state.addAttribute(getGlobalCtorPriorityAttrName(state.name),
1749+
globalCtorPriorityAttr);
1750+
}).failed())
1751+
return failure();
1752+
1753+
if (parseGlobalDtorCtor("global_dtor", [&](std::optional<int> priority) {
1754+
mlir::IntegerAttr globalDtorPriorityAttr =
1755+
builder.getI32IntegerAttr(priority.value_or(65535));
1756+
state.addAttribute(getGlobalDtorPriorityAttrName(state.name),
1757+
globalDtorPriorityAttr);
1758+
}).failed())
1759+
return failure();
1760+
17231761
// Parse the optional function body.
17241762
auto *body = state.addRegion();
17251763
OptionalParseResult parseResult = parser.parseOptionalRegion(
@@ -1801,6 +1839,18 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
18011839
p << ")";
18021840
}
18031841

1842+
if (auto globalCtorPriority = getGlobalCtorPriority()) {
1843+
p << " global_ctor";
1844+
if (globalCtorPriority.value() != 65535)
1845+
p << "(" << globalCtorPriority.value() << ")";
1846+
}
1847+
1848+
if (auto globalDtorPriority = getGlobalDtorPriority()) {
1849+
p << " global_dtor";
1850+
if (globalDtorPriority.value() != 65535)
1851+
p << "(" << globalDtorPriority.value() << ")";
1852+
}
1853+
18041854
// Print the body if this is not an external function.
18051855
Region &body = getOperation()->getRegion(0);
18061856
if (!body.empty()) {

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

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
105105

106106
/// List of ctors and their priorities to be called before main()
107107
llvm::SmallVector<std::pair<std::string, uint32_t>, 4> globalCtorList;
108+
/// List of dtors and their priorities to be called when unloading module.
109+
llvm::SmallVector<std::pair<std::string, uint32_t>, 4> globalDtorList;
108110

109111
void setASTContext(clang::ASTContext *c) {
110112
astCtx = c;
@@ -823,10 +825,13 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
823825
mlir::ArrayAttr::get(&getContext(), globalCtors));
824826
}
825827

826-
// We will eventual need to populate a global_dtor list, but that's not
827-
// needed for globals with destructors. It will only be needed for functions
828-
// that are marked as global destructors with an attribute.
829-
assert(!cir::MissingFeatures::opGlobalDtorList());
828+
if (!globalDtorList.empty()) {
829+
llvm::SmallVector<mlir::Attribute> globalDtors =
830+
prepareCtorDtorAttrList<cir::GlobalDtorAttr>(&getContext(),
831+
globalDtorList);
832+
mlirModule->setAttr(cir::CIRDialect::getGlobalDtorsAttrName(),
833+
mlir::ArrayAttr::get(&getContext(), globalDtors));
834+
}
830835
}
831836

832837
void LoweringPreparePass::buildCXXGlobalInitFunc() {
@@ -975,22 +980,28 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
975980
}
976981

977982
void LoweringPreparePass::runOnOp(mlir::Operation *op) {
978-
if (auto arrayCtor = dyn_cast<ArrayCtor>(op))
983+
if (auto arrayCtor = dyn_cast<cir::ArrayCtor>(op)) {
979984
lowerArrayCtor(arrayCtor);
980-
else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op))
985+
} else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) {
981986
lowerArrayDtor(arrayDtor);
982-
else if (auto cast = mlir::dyn_cast<cir::CastOp>(op))
987+
} else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) {
983988
lowerCastOp(cast);
984-
else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op))
989+
} else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op)) {
985990
lowerComplexDivOp(complexDiv);
986-
else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op))
991+
} else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op)) {
987992
lowerComplexMulOp(complexMul);
988-
else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op))
993+
} else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op)) {
989994
lowerGlobalOp(glob);
990-
else if (auto dynamicCast = mlir::dyn_cast<cir::DynamicCastOp>(op))
995+
} else if (auto dynamicCast = mlir::dyn_cast<cir::DynamicCastOp>(op)) {
991996
lowerDynamicCastOp(dynamicCast);
992-
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
997+
} else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op)) {
993998
lowerUnaryOp(unary);
999+
} else if (auto fnOp = dyn_cast<cir::FuncOp>(op)) {
1000+
if (auto globalCtor = fnOp.getGlobalCtorPriority())
1001+
globalCtorList.emplace_back(fnOp.getName(), globalCtor.value());
1002+
else if (auto globalDtor = fnOp.getGlobalDtorPriority())
1003+
globalDtorList.emplace_back(fnOp.getName(), globalDtor.value());
1004+
}
9941005
}
9951006

9961007
void LoweringPreparePass::runOnOperation() {
@@ -1003,7 +1014,7 @@ void LoweringPreparePass::runOnOperation() {
10031014
op->walk([&](mlir::Operation *op) {
10041015
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
10051016
cir::ComplexMulOp, cir::ComplexDivOp, cir::DynamicCastOp,
1006-
cir::GlobalOp, cir::UnaryOp>(op))
1017+
cir::FuncOp, cir::GlobalOp, cir::UnaryOp>(op))
10071018
opsToTransform.push_back(op);
10081019
});
10091020

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2598,7 +2598,13 @@ void ConvertCIRToLLVMPass::runOnOperation() {
25982598
return std::make_pair(ctorAttr.getName(),
25992599
ctorAttr.getPriority());
26002600
});
2601-
assert(!cir::MissingFeatures::opGlobalDtorList());
2601+
// Emit the llvm.global_dtors array.
2602+
buildCtorDtorList(module, cir::CIRDialect::getGlobalDtorsAttrName(),
2603+
"llvm.global_dtors", [](mlir::Attribute attr) {
2604+
auto dtorAttr = mlir::cast<cir::GlobalDtorAttr>(attr);
2605+
return std::make_pair(dtorAttr.getName(),
2606+
dtorAttr.getPriority());
2607+
});
26022608
}
26032609

26042610
mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: %clang_cc1 -std=c++20 -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
3+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR-AFTER
4+
// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t.cir %s
5+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
6+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
7+
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
8+
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
9+
10+
extern int bar();
11+
void foo(void) __attribute__((constructor));
12+
void foo(void) {
13+
bar();
14+
}
15+
16+
// CIR-BEFORE-LPP: cir.func dso_local @_Z3foov() global_ctor
17+
18+
void foo2(void) __attribute__((constructor(777)));
19+
void foo2(void) {
20+
bar();
21+
}
22+
23+
// CIR-BEFORE-LPP: cir.func dso_local @_Z4foo2v() global_ctor(777)
24+
25+
void foo3(void) __attribute__((destructor));
26+
void foo3(void) {
27+
bar();
28+
}
29+
30+
// CIR-BEFORE-LPP: cir.func dso_local @_Z4foo3v() global_dtor
31+
32+
void foo4(void) __attribute__((destructor(789)));
33+
void foo4(void) {
34+
bar();
35+
}
36+
37+
// CIR-BEFORE-LPP: cir.func dso_local @_Z4foo4v() global_dtor(789)
38+
39+
// CIR-AFTER: module @{{.*}} attributes {cir.global_ctors = [#cir.global_ctor<"_Z3foov", 65535>, #cir.global_ctor<"_Z4foo2v", 777>], cir.global_dtors = [#cir.global_dtor<"_Z4foo3v", 65535>, #cir.global_dtor<"_Z4foo4v", 789>]
40+
41+
// LLVM: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_Z3foov, ptr null }, { i32, ptr, ptr } { i32 777, ptr @_Z4foo2v, ptr null }]
42+
// LLVM: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_Z4foo3v, ptr null }, { i32, ptr, ptr } { i32 789, ptr @_Z4foo4v, ptr null }]
43+
44+
// OGCG: @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_Z3foov, ptr null }, { i32, ptr, ptr } { i32 777, ptr @_Z4foo2v, ptr null }]
45+
// OGCG: @llvm.global_dtors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_Z4foo3v, ptr null }, { i32, ptr, ptr } { i32 789, ptr @_Z4foo4v, ptr null }]

0 commit comments

Comments
 (0)