diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 7714750a53d44..8f1095f513de0 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -712,6 +712,51 @@ def CIR_VisibilityAttr : CIR_EnumAttr { }]; } +//===----------------------------------------------------------------------===// +// GloblCtorAttr +//===----------------------------------------------------------------------===// + +class CIR_GlobalCtorDtor + : CIR_Attr<"Global" # name, "global_" # attrMnemonic> { + let parameters = (ins "mlir::StringAttr":$name, "int":$priority); + + let skipDefaultBuilders = 1; + let builders = [ + AttrBuilder<(ins + "llvm::StringRef":$name, + CArg<"int", "65535">:$priority), [{ + return $_get($_ctxt, mlir::StringAttr::get($_ctxt, name), priority); + }]>, + AttrBuilderWithInferredContext<(ins + "mlir::StringAttr":$name, + CArg<"int", "65535">:$priority), [{ + return $_get(name.getContext(), name, priority); + }]> + ]; + + let assemblyFormat = [{ + `<` $name `,` $priority `>` + }]; + + let extraClassDeclaration = [{ + bool isDefaultPriority() const { + return getPriority() == getDefaultPriority(); + }; + + static int getDefaultPriority() { + return 65535; + } + }]; +} + +def CIR_GlobalCtorAttr : CIR_GlobalCtorDtor<"Ctor", "ctor"> { + let summary = "Marks a function as a global constructor"; + let description = [{ + Marks the function as a global constructor in the module's constructor list. + It will be executed before main() is called. + }]; +} + //===----------------------------------------------------------------------===// // BitfieldInfoAttr //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index 15d5fa0b4753e..feb08d6088125 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -42,6 +42,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getNoThrowAttrName() { return "nothrow"; } static llvm::StringRef getSideEffectAttrName() { return "side_effect"; } static llvm::StringRef getModuleLevelAsmAttrName() { return "cir.module_asm"; } + static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; } void registerAttributes(); void registerTypes(); diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index f79580083cf9f..3b7b130ebc973 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -39,7 +39,6 @@ struct MissingFeatures { static bool opGlobalUsedOrCompilerUsed() { return false; } static bool opGlobalAnnotations() { return false; } static bool opGlobalDtorLowering() { return false; } - static bool opGlobalCtorAttr() { return false; } static bool opGlobalCtorPriority() { return false; } static bool opGlobalCtorList() { return false; } static bool setDSOLocal() { return false; } diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 2eeef819dc3fc..bc917d0604668 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -61,6 +61,9 @@ struct LoweringPreparePass : public LoweringPrepareBase { /// Build a module init function that calls all the dynamic initializers. void buildCXXGlobalInitFunc(); + /// Materialize global ctor/dtor list + void buildGlobalCtorDtorList(); + cir::FuncOp buildRuntimeFunction( mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc, cir::FuncType type, @@ -79,6 +82,9 @@ struct LoweringPreparePass : public LoweringPrepareBase { llvm::StringMap dynamicInitializerNames; llvm::SmallVector dynamicInitializers; + /// List of ctors and their priorities to be called before main() + llvm::SmallVector, 4> globalCtorList; + void setASTContext(clang::ASTContext *c) { astCtx = c; } }; @@ -689,11 +695,36 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { assert(!cir::MissingFeatures::opGlobalAnnotations()); } +template +static llvm::SmallVector +prepareCtorDtorAttrList(mlir::MLIRContext *context, + llvm::ArrayRef> list) { + llvm::SmallVector attrs; + for (const auto &[name, priority] : list) + attrs.push_back(AttributeTy::get(context, name, priority)); + return attrs; +} + +void LoweringPreparePass::buildGlobalCtorDtorList() { + if (!globalCtorList.empty()) { + llvm::SmallVector globalCtors = + prepareCtorDtorAttrList(&getContext(), + globalCtorList); + + mlirModule->setAttr(cir::CIRDialect::getGlobalCtorsAttrName(), + mlir::ArrayAttr::get(&getContext(), globalCtors)); + } + + assert(!cir::MissingFeatures::opGlobalDtorLowering()); +} + void LoweringPreparePass::buildCXXGlobalInitFunc() { if (dynamicInitializers.empty()) return; - assert(!cir::MissingFeatures::opGlobalCtorList()); + // TODO: handle globals with a user-specified initialzation priority. + // TODO: handle default priority more nicely. + assert(!cir::MissingFeatures::opGlobalCtorPriority()); SmallString<256> fnName; // Include the filename in the symbol name. Including "sub_" matches gcc @@ -722,6 +753,10 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() { builder.setInsertionPointToStart(f.addEntryBlock()); for (cir::FuncOp &f : dynamicInitializers) builder.createCallOp(f.getLoc(), f, {}); + // Add the global init function (not the individual ctor functions) to the + // global ctor list. + globalCtorList.emplace_back(fnName, + cir::GlobalCtorAttr::getDefaultPriority()); cir::ReturnOp::create(builder, f.getLoc()); } @@ -852,6 +887,7 @@ void LoweringPreparePass::runOnOperation() { runOnOp(o); buildCXXGlobalInitFunc(); + buildGlobalCtorDtorList(); } std::unique_ptr mlir::createLoweringPreparePass() { diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 1ff8cc55b57fa..36854946cfeee 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2413,6 +2413,73 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, }); } +static void buildCtorDtorList( + mlir::ModuleOp module, StringRef globalXtorName, StringRef llvmXtorName, + llvm::function_ref(mlir::Attribute)> createXtor) { + llvm::SmallVector> globalXtors; + for (const mlir::NamedAttribute namedAttr : module->getAttrs()) { + if (namedAttr.getName() == globalXtorName) { + for (auto attr : mlir::cast(namedAttr.getValue())) + globalXtors.emplace_back(createXtor(attr)); + break; + } + } + + if (globalXtors.empty()) + return; + + mlir::OpBuilder builder(module.getContext()); + builder.setInsertionPointToEnd(&module.getBodyRegion().back()); + + // Create a global array llvm.global_ctors with element type of + // struct { i32, ptr, ptr } + auto ctorPFTy = mlir::LLVM::LLVMPointerType::get(builder.getContext()); + llvm::SmallVector ctorStructFields; + ctorStructFields.push_back(builder.getI32Type()); + ctorStructFields.push_back(ctorPFTy); + ctorStructFields.push_back(ctorPFTy); + + auto ctorStructTy = mlir::LLVM::LLVMStructType::getLiteral( + builder.getContext(), ctorStructFields); + auto ctorStructArrayTy = + mlir::LLVM::LLVMArrayType::get(ctorStructTy, globalXtors.size()); + + mlir::Location loc = module.getLoc(); + auto newGlobalOp = mlir::LLVM::GlobalOp::create( + builder, loc, ctorStructArrayTy, /*constant=*/false, + mlir::LLVM::Linkage::Appending, llvmXtorName, mlir::Attribute()); + + builder.createBlock(&newGlobalOp.getRegion()); + builder.setInsertionPointToEnd(newGlobalOp.getInitializerBlock()); + + mlir::Value result = + mlir::LLVM::UndefOp::create(builder, loc, ctorStructArrayTy); + + for (auto [index, fn] : llvm::enumerate(globalXtors)) { + mlir::Value structInit = + mlir::LLVM::UndefOp::create(builder, loc, ctorStructTy); + mlir::Value initPriority = mlir::LLVM::ConstantOp::create( + builder, loc, ctorStructFields[0], fn.second); + mlir::Value initFuncAddr = mlir::LLVM::AddressOfOp::create( + builder, loc, ctorStructFields[1], fn.first); + mlir::Value initAssociate = + mlir::LLVM::ZeroOp::create(builder, loc, ctorStructFields[2]); + // Literal zero makes the InsertValueOp::create ambiguous. + llvm::SmallVector zero{0}; + structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit, + initPriority, zero); + structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit, + initFuncAddr, 1); + // TODO: handle associated data for initializers. + structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit, + initAssociate, 2); + result = mlir::LLVM::InsertValueOp::create(builder, loc, result, structInit, + index); + } + + builder.create(loc, result); +} + // The applyPartialConversion function traverses blocks in the dominance order, // so it does not lower and operations that are not reachachable from the // operations passed in as arguments. Since we do need to lower such code in @@ -2519,6 +2586,14 @@ void ConvertCIRToLLVMPass::runOnOperation() { if (failed(applyPartialConversion(ops, target, std::move(patterns)))) signalPassFailure(); + + // Emit the llvm.global_ctors array. + buildCtorDtorList(module, cir::CIRDialect::getGlobalCtorsAttrName(), + "llvm.global_ctors", [](mlir::Attribute attr) { + auto ctorAttr = mlir::cast(attr); + return std::make_pair(ctorAttr.getName(), + ctorAttr.getPriority()); + }); } mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite( diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp index 0c19e686b2493..2afb5a5af01a0 100644 --- a/clang/test/CIR/CodeGen/global-init.cpp +++ b/clang/test/CIR/CodeGen/global-init.cpp @@ -1,9 +1,10 @@ // 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 // RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR - -// Note: The LoweringPrepare work isn't yet complete. We still need to create -// the global ctor list attribute. +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG struct NeedsCtor { NeedsCtor(); @@ -15,6 +16,9 @@ NeedsCtor needsCtor; // CIR-BEFORE-LPP: %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr // CIR-BEFORE-LPP: cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr) -> () +// CIR: module @{{.*}} attributes { +// CIR-SAME: cir.global_ctors = [#cir.global_ctor<"_GLOBAL__sub_I_[[FILENAME:.*]]", 65535>] + // CIR: cir.global external @needsCtor = #cir.zero : !rec_NeedsCtor // CIR: cir.func internal private @__cxx_global_var_init() { // CIR: %0 = cir.get_global @needsCtor : !cir.ptr @@ -24,3 +28,22 @@ NeedsCtor needsCtor; // CIR: cir.call @__cxx_global_var_init() : () -> () // CIR: cir.return // CIR: } + +// LLVM: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1 +// LLVM: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }] +// LLVM: declare void @_ZN9NeedsCtorC1Ev(ptr) + +// LLVM: define internal void @__cxx_global_var_init() +// LLVM: call void @_ZN9NeedsCtorC1Ev(ptr @needsCtor) + +// LLVM: define void @_GLOBAL__sub_I_[[FILENAME]]() +// LLVM: call void @__cxx_global_var_init() + +// OGCG: @needsCtor = global %struct.NeedsCtor zeroinitializer, align 1 +// OGCG: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }] + +// OGCG: define internal void @__cxx_global_var_init() {{.*}} section ".text.startup" { +// OGCG: call void @_ZN9NeedsCtorC1Ev(ptr noundef nonnull align 1 dereferenceable(1) @needsCtor) + +// OGCG: define internal void @_GLOBAL__sub_I_[[FILENAME]]() {{.*}} section ".text.startup" { +// OGCG: call void @__cxx_global_var_init()