Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,14 @@ def CIR_GlobalCtorAttr : CIR_GlobalCtorDtor<"Ctor", "ctor"> {
}];
}

def CIR_GlobalDtorAttr : CIR_GlobalCtorDtor<"Dtor", "dtor"> {
let summary = "Marks a function as a global destructor";
let description = [{
Marks a function as a global destructor in the module dtors list.
The function will be executed before the module unloading.
}];
}

//===----------------------------------------------------------------------===//
// BitfieldInfoAttr
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRDialect.td
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def CIR_Dialect : Dialect {
static llvm::StringRef getSideEffectAttrName() { return "side_effect"; }
static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; }
static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; }
static llvm::StringRef getGlobalDtorsAttrName() { return "cir.global_dtors"; }

void registerAttributes();
void registerTypes();
Expand Down
31 changes: 30 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ class CIR_Op<string mnemonic, list<Trait> traits = []> :
code extraLLVMLoweringPatternDecl = "";
}

//===----------------------------------------------------------------------===//
// CIR Operation Traits
//===----------------------------------------------------------------------===//

class HasAtMostOneOfAttrsPred<list<string> names> :
CPred<!foldl("0", names, acc, name, acc # " + (" # name # " ? 1 : 0)")
# " <= 1">;

class HasAtMostOneOfAttrs<list<string> names> : PredOpTrait<
"has only one of the optional attributes: " # !interleave(names, ", "),
HasAtMostOneOfAttrsPred<!foreach(name, names, "$" # name)>
>;

//===----------------------------------------------------------------------===//
// CastOp
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -2422,9 +2435,17 @@ def CIR_GetMemberOp : CIR_Op<"get_member"> {
// TODO(CIR): FuncOp is still a tiny shell of what it will become. Many more
// properties and attributes will be added as upstreaming continues.

def CIR_OptionalPriorityAttr : OptionalAttr<
DefaultValuedAttr<
ConfinedAttr<I32Attr, [IntMinValue<101>, IntMaxValue<65535>]>,
"65535"
>
>;

def CIR_FuncOp : CIR_Op<"func", [
AutomaticAllocationScope, CallableOpInterface, FunctionOpInterface,
DeclareOpInterfaceMethods<CIRGlobalValueInterface>,
HasAtMostOneOfAttrs<["global_ctor_priority", "global_dtor_priority"]>,
IsolatedFromAbove
]> {
let summary = "Declare or define a function";
Expand All @@ -2449,6 +2470,12 @@ def CIR_FuncOp : CIR_Op<"func", [
without a prototype and, consequently, may contain calls with invalid
arguments and undefined behavior.

The `global_ctor` keyword indicates whether a function should execute before
`main()` function, as specified by `__attribute__((constructor))`. An
execution priority can also be specified `global_ctor(<priority>)`.
Similarly, for global destructors both `global_dtor` and
`global_dtor(<priority>)` are available.

Example:

```mlir
Expand Down Expand Up @@ -2487,7 +2514,9 @@ def CIR_FuncOp : CIR_Op<"func", [
UnitAttr:$comdat,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs,
OptionalAttr<FlatSymbolRefAttr>:$aliasee);
OptionalAttr<FlatSymbolRefAttr>:$aliasee,
CIR_OptionalPriorityAttr:$global_ctor_priority,
CIR_OptionalPriorityAttr:$global_dtor_priority);

let regions = (region AnyRegion:$body);

Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ struct MissingFeatures {
static bool opGlobalUsedOrCompilerUsed() { return false; }
static bool opGlobalAnnotations() { return false; }
static bool opGlobalCtorPriority() { return false; }
static bool opGlobalDtorList() { return false; }
static bool setDSOLocal() { return false; }
static bool setComdat() { return false; }

Expand Down Expand Up @@ -175,6 +174,10 @@ struct MissingFeatures {
static bool atomicScope() { return false; }
static bool atomicSyncScopeID() { return false; }

// Global ctor handling
static bool globalCtorLexOrder() { return false; }
static bool globalCtorAssociatedData() { return false; }

// Misc
static bool abiArgInfo() { return false; }
static bool addHeapAllocSiteMetadata() { return false; }
Expand Down
40 changes: 36 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,15 +451,47 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,
setNonAliasAttributes(gd, funcOp);
assert(!cir::MissingFeatures::opFuncAttributesForDefinition());

if (funcDecl->getAttr<ConstructorAttr>())
errorNYI(funcDecl->getSourceRange(), "constructor attribute");
if (funcDecl->getAttr<DestructorAttr>())
errorNYI(funcDecl->getSourceRange(), "destructor attribute");
auto getPriority = [this](const auto *attr) -> int {
Expr *e = attr->getPriority();
if (e)
return e->EvaluateKnownConstInt(this->getASTContext()).getExtValue();
return attr->DefaultPriority;
};

if (const ConstructorAttr *ca = funcDecl->getAttr<ConstructorAttr>())
addGlobalCtor(funcOp, getPriority(ca));
if (const DestructorAttr *da = funcDecl->getAttr<DestructorAttr>())
addGlobalDtor(funcOp, getPriority(da));

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

/// Track functions to be called before main() runs.
void CIRGenModule::addGlobalCtor(cir::FuncOp ctor,
std::optional<int> priority) {
assert(!cir::MissingFeatures::globalCtorLexOrder());
assert(!cir::MissingFeatures::globalCtorAssociatedData());

// Traditional LLVM codegen directly adds the function to the list of global
// ctors. In CIR we just add a global_ctor attribute to the function. The
// global list is created in LoweringPrepare.
//
// FIXME(from traditional LLVM): Type coercion of void()* types.
ctor.setGlobalCtorPriority(priority);
}

/// Add a function to the list that will be called when the module is unloaded.
void CIRGenModule::addGlobalDtor(cir::FuncOp dtor,
std::optional<int> priority) {
if (codeGenOpts.RegisterGlobalDtorsWithAtExit &&
(!getASTContext().getTargetInfo().getTriple().isOSAIX()))
errorNYI(dtor.getLoc(), "registerGlobalDtorsWithAtExit");

// FIXME(from traditional LLVM): Type coercion of void()* types.
dtor.setGlobalDtorPriority(priority);
}

void CIRGenModule::handleCXXStaticMemberVarInstantiation(VarDecl *vd) {
VarDecl::DefinitionKind dk = vd->isThisDeclarationADefinition();
if (dk == VarDecl::Definition && vd->hasAttr<DLLImportAttr>())
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ class CIRGenModule : public CIRGenTypeCache {
bool isConstant = false,
mlir::Operation *insertPoint = nullptr);

/// Add a global constructor or destructor to the module.
/// The priority is optional, if not specified, the default priority is used.
void addGlobalCtor(cir::FuncOp ctor,
std::optional<int> priority = std::nullopt);
void addGlobalDtor(cir::FuncOp dtor,
std::optional<int> priority = std::nullopt);

bool shouldZeroInitPadding() const {
// In C23 (N3096) $6.7.10:
// """
Expand Down
50 changes: 50 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"

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

auto parseGlobalDtorCtor =
[&](StringRef keyword,
llvm::function_ref<void(std::optional<int> prio)> createAttr)
-> mlir::LogicalResult {
if (mlir::succeeded(parser.parseOptionalKeyword(keyword))) {
std::optional<int> priority;
if (mlir::succeeded(parser.parseOptionalLParen())) {
auto parsedPriority = mlir::FieldParser<int>::parse(parser);
if (mlir::failed(parsedPriority))
return parser.emitError(parser.getCurrentLocation(),
"failed to parse 'priority', of type 'int'");
priority = parsedPriority.value_or(int());
// Parse literal ')'
if (parser.parseRParen())
return failure();
}
createAttr(priority);
}
return success();
};

if (parseGlobalDtorCtor("global_ctor", [&](std::optional<int> priority) {
mlir::IntegerAttr globalCtorPriorityAttr =
builder.getI32IntegerAttr(priority.value_or(65535));
state.addAttribute(getGlobalCtorPriorityAttrName(state.name),
globalCtorPriorityAttr);
}).failed())
return failure();

if (parseGlobalDtorCtor("global_dtor", [&](std::optional<int> priority) {
mlir::IntegerAttr globalDtorPriorityAttr =
builder.getI32IntegerAttr(priority.value_or(65535));
state.addAttribute(getGlobalDtorPriorityAttrName(state.name),
globalDtorPriorityAttr);
}).failed())
return failure();

// Parse the optional function body.
auto *body = state.addRegion();
OptionalParseResult parseResult = parser.parseOptionalRegion(
Expand Down Expand Up @@ -1801,6 +1839,18 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
p << ")";
}

if (auto globalCtorPriority = getGlobalCtorPriority()) {
p << " global_ctor";
if (globalCtorPriority.value() != 65535)
p << "(" << globalCtorPriority.value() << ")";
}

if (auto globalDtorPriority = getGlobalDtorPriority()) {
p << " global_dtor";
if (globalDtorPriority.value() != 65535)
p << "(" << globalDtorPriority.value() << ")";
}

// Print the body if this is not an external function.
Region &body = getOperation()->getRegion(0);
if (!body.empty()) {
Expand Down
37 changes: 24 additions & 13 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {

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

void setASTContext(clang::ASTContext *c) {
astCtx = c;
Expand Down Expand Up @@ -823,10 +825,13 @@ void LoweringPreparePass::buildGlobalCtorDtorList() {
mlir::ArrayAttr::get(&getContext(), globalCtors));
}

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

void LoweringPreparePass::buildCXXGlobalInitFunc() {
Expand Down Expand Up @@ -975,22 +980,28 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) {
}

void LoweringPreparePass::runOnOp(mlir::Operation *op) {
if (auto arrayCtor = dyn_cast<ArrayCtor>(op))
if (auto arrayCtor = dyn_cast<cir::ArrayCtor>(op)) {
lowerArrayCtor(arrayCtor);
else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op))
} else if (auto arrayDtor = dyn_cast<cir::ArrayDtor>(op)) {
lowerArrayDtor(arrayDtor);
else if (auto cast = mlir::dyn_cast<cir::CastOp>(op))
} else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) {
lowerCastOp(cast);
else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op))
} else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op)) {
lowerComplexDivOp(complexDiv);
else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op))
} else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op)) {
lowerComplexMulOp(complexMul);
else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op))
} else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op)) {
lowerGlobalOp(glob);
else if (auto dynamicCast = mlir::dyn_cast<cir::DynamicCastOp>(op))
} else if (auto dynamicCast = mlir::dyn_cast<cir::DynamicCastOp>(op)) {
lowerDynamicCastOp(dynamicCast);
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
} else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op)) {
lowerUnaryOp(unary);
} else if (auto fnOp = dyn_cast<cir::FuncOp>(op)) {
if (auto globalCtor = fnOp.getGlobalCtorPriority())
globalCtorList.emplace_back(fnOp.getName(), globalCtor.value());
else if (auto globalDtor = fnOp.getGlobalDtorPriority())
globalDtorList.emplace_back(fnOp.getName(), globalDtor.value());
}
}

void LoweringPreparePass::runOnOperation() {
Expand All @@ -1003,7 +1014,7 @@ void LoweringPreparePass::runOnOperation() {
op->walk([&](mlir::Operation *op) {
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
cir::ComplexMulOp, cir::ComplexDivOp, cir::DynamicCastOp,
cir::GlobalOp, cir::UnaryOp>(op))
cir::FuncOp, cir::GlobalOp, cir::UnaryOp>(op))
opsToTransform.push_back(op);
});

Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2598,7 +2598,13 @@ void ConvertCIRToLLVMPass::runOnOperation() {
return std::make_pair(ctorAttr.getName(),
ctorAttr.getPriority());
});
assert(!cir::MissingFeatures::opGlobalDtorList());
// Emit the llvm.global_dtors array.
buildCtorDtorList(module, cir::CIRDialect::getGlobalDtorsAttrName(),
"llvm.global_dtors", [](mlir::Attribute attr) {
auto dtorAttr = mlir::cast<cir::GlobalDtorAttr>(attr);
return std::make_pair(dtorAttr.getName(),
dtorAttr.getPriority());
});
}

mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
Expand Down
45 changes: 45 additions & 0 deletions clang/test/CIR/CodeGen/global-ctor-dtor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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
// RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR-AFTER
// RUN: FileCheck --check-prefix=CIR-AFTER --input-file=%t.cir %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s

extern int bar();
void foo(void) __attribute__((constructor));
void foo(void) {
bar();
}

// CIR-BEFORE-LPP: cir.func dso_local @_Z3foov() global_ctor

void foo2(void) __attribute__((constructor(777)));
void foo2(void) {
bar();
}

// CIR-BEFORE-LPP: cir.func dso_local @_Z4foo2v() global_ctor(777)

void foo3(void) __attribute__((destructor));
void foo3(void) {
bar();
}

// CIR-BEFORE-LPP: cir.func dso_local @_Z4foo3v() global_dtor

void foo4(void) __attribute__((destructor(789)));
void foo4(void) {
bar();
}

// CIR-BEFORE-LPP: cir.func dso_local @_Z4foo4v() global_dtor(789)

// 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>]

// 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 }]
// 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 }]

// 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 }]
// 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 }]
Loading