llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/162130.diff 6 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+45) - (modified) clang/include/clang/CIR/Dialect/IR/CIRDialect.td (+1) - (modified) clang/include/clang/CIR/MissingFeatures.h (-1) - (modified) clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp (+37-1) - (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+75) - (modified) clang/test/CIR/CodeGen/global-init.cpp (+27-3) ``````````diff 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<CIR_VisibilityKind, "visibility"> { }]; } +//===----------------------------------------------------------------------===// +// GloblCtorAttr +//===----------------------------------------------------------------------===// + +class CIR_GlobalCtorDtor<string name, string attrMnemonic> + : 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<LoweringPreparePass> { /// 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<LoweringPreparePass> { llvm::StringMap<uint32_t> dynamicInitializerNames; llvm::SmallVector<cir::FuncOp> dynamicInitializers; + /// List of ctors and their priorities to be called before main() + llvm::SmallVector<std::pair<std::string, uint32_t>, 4> globalCtorList; + void setASTContext(clang::ASTContext *c) { astCtx = c; } }; @@ -689,11 +695,36 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { assert(!cir::MissingFeatures::opGlobalAnnotations()); } +template <typename AttributeTy> +static llvm::SmallVector<mlir::Attribute> +prepareCtorDtorAttrList(mlir::MLIRContext *context, + llvm::ArrayRef<std::pair<std::string, uint32_t>> list) { + llvm::SmallVector<mlir::Attribute> 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<mlir::Attribute> globalCtors = + prepareCtorDtorAttrList<cir::GlobalCtorAttr>(&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<Pass> mlir::createLoweringPreparePass() { diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 1ff8cc55b57fa..702c2bf1a02a2 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<std::pair<StringRef, int>(mlir::Attribute)> createXtor) { + llvm::SmallVector<std::pair<StringRef, int>> globalXtors; + for (const mlir::NamedAttribute namedAttr : module->getAttrs()) { + if (namedAttr.getName() == globalXtorName) { + for (auto attr : mlir::cast<mlir::ArrayAttr>(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<mlir::Type> 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 = + builder.create<mlir::LLVM::UndefOp>(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<int64_t> 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<mlir::LLVM::ReturnOp>(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<cir::GlobalCtorAttr>(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..d6bf83a5c36df 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<!rec_NeedsCtor> // CIR-BEFORE-LPP: cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> () +// 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<!rec_NeedsCtor> @@ -24,3 +28,23 @@ 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() + \ No newline at end of file `````````` </details> https://github.com/llvm/llvm-project/pull/162130 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
