-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Implement initial LoweringPrepare support for global ctors #161452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This adds the initial support for lowering the 'ctor' region of cir.global operations to an init function which is called from a TU-specific static initialization function. This does not yet add an attribute to hold a list of global initializers. That will be added in a future change.
@llvm/pr-subscribers-clangir @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis adds the initial support for lowering the 'ctor' region of cir.global operations to an init function which is called from a TU-specific static initialization function. This does not yet add an attribute to hold a list of global initializers. That will be added in a future change. Full diff: https://github.com/llvm/llvm-project/pull/161452.diff 4 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
index 925a9a87e267f..d447354a1ad47 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
@@ -20,6 +20,10 @@
#include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
+namespace cir {
+inline constexpr uint32_t DefaultGlobalCtorDtorPriority = 65535;
+} // namespace cir
+
//===----------------------------------------------------------------------===//
// CIR Dialect Attrs
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 7a6c084f51cd7..a4dfc051e9953 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -37,6 +37,11 @@ struct MissingFeatures {
static bool opGlobalDLLImportExport() { return false; }
static bool opGlobalPartition() { return false; }
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; }
static bool setComdat() { return false; }
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index c15637d297cd1..d7645bebc25ed 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -8,18 +8,39 @@
#include "PassDetail.h"
#include "clang/AST/ASTContext.h"
+#include "clang/Basic/Module.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIROpsEnums.h"
#include "clang/CIR/Dialect/Passes.h"
#include "clang/CIR/MissingFeatures.h"
+#include "llvm/Support/Path.h"
#include <memory>
using namespace mlir;
using namespace cir;
+static SmallString<128> getTransformedFileName(mlir::ModuleOp mlirModule) {
+ SmallString<128> fileName;
+
+ if (mlirModule.getSymName())
+ fileName = llvm::sys::path::filename(mlirModule.getSymName()->str());
+
+ if (fileName.empty())
+ fileName = "<null>";
+
+ for (size_t i = 0; i < fileName.size(); ++i) {
+ // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens
+ // to be the set of C preprocessing numbers.
+ if (!clang::isPreprocessingNumberBody(fileName[i]))
+ fileName[i] = '_';
+ }
+
+ return fileName;
+}
+
namespace {
struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
LoweringPreparePass() = default;
@@ -30,9 +51,16 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
void lowerComplexDivOp(cir::ComplexDivOp op);
void lowerComplexMulOp(cir::ComplexMulOp op);
void lowerUnaryOp(cir::UnaryOp op);
+ void lowerGlobalOp(cir::GlobalOp op);
void lowerArrayDtor(cir::ArrayDtor op);
void lowerArrayCtor(cir::ArrayCtor op);
+ /// Build the function that initializes the specified global
+ cir::FuncOp buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op);
+
+ /// Build a module init function that calls all the dynamic initializers.
+ void buildCXXGlobalInitFunc();
+
cir::FuncOp buildRuntimeFunction(
mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
cir::FuncType type,
@@ -47,6 +75,10 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
/// Tracks current module.
mlir::ModuleOp mlirModule;
+ /// Tracks existing dynamic initializers.
+ llvm::StringMap<uint32_t> dynamicInitializerNames;
+ llvm::SmallVector<cir::FuncOp> dynamicInitializers;
+
void setASTContext(clang::ASTContext *c) { astCtx = c; }
};
@@ -589,6 +621,113 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) {
op.erase();
}
+cir::FuncOp
+LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) {
+ // TODO(cir): Store this in the GlobalOp.
+ // This should come from the MangleContext, but for now I'm hardcoding it.
+ SmallString<256> fnName("__cxx_global_var_init");
+ // Get a unique name
+ uint32_t cnt = dynamicInitializerNames[fnName]++;
+ if (cnt)
+ fnName += "." + llvm::Twine(cnt).str();
+
+ // Create a variable initialization function.
+ CIRBaseBuilderTy builder(getContext());
+ builder.setInsertionPointAfter(op);
+ auto voidTy = cir::VoidType::get(builder.getContext());
+ auto fnType = cir::FuncType::get({}, voidTy);
+ FuncOp f = buildRuntimeFunction(builder, fnName, op.getLoc(), fnType,
+ cir::GlobalLinkageKind::InternalLinkage);
+
+ // Move over the initialzation code of the ctor region.
+ mlir::Block *entryBB = f.addEntryBlock();
+ if (!op.getCtorRegion().empty()) {
+ mlir::Block &block = op.getCtorRegion().front();
+ entryBB->getOperations().splice(entryBB->begin(), block.getOperations(),
+ block.begin(), std::prev(block.end()));
+ }
+
+ // Register the destructor call with __cxa_atexit
+ auto &dtorRegion = op.getDtorRegion();
+ if (!dtorRegion.empty()) {
+ assert(!cir::MissingFeatures::opGlobalDtorLowering());
+ llvm_unreachable("dtor region lowering is NYI");
+ }
+
+ // Replace cir.yield with cir.return
+ builder.setInsertionPointToEnd(entryBB);
+ mlir::Operation *yieldOp = nullptr;
+ if (!op.getCtorRegion().empty()) {
+ mlir::Block &block = op.getCtorRegion().front();
+ yieldOp = &block.getOperations().back();
+ } else {
+ assert(!cir::MissingFeatures::opGlobalDtorLowering());
+ llvm_unreachable("dtor region lowering is NYI");
+ }
+
+ assert(isa<YieldOp>(*yieldOp));
+ builder.create<ReturnOp>(yieldOp->getLoc());
+ return f;
+}
+
+void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
+ mlir::Region &ctorRegion = op.getCtorRegion();
+ mlir::Region &dtorRegion = op.getDtorRegion();
+
+ if (!ctorRegion.empty() || !dtorRegion.empty()) {
+ // Build a variable initialization function and move the initialzation code
+ // in the ctor region over.
+ cir::FuncOp f = buildCXXGlobalVarDeclInitFunc(op);
+
+ // Clear the ctor and dtor region
+ ctorRegion.getBlocks().clear();
+ dtorRegion.getBlocks().clear();
+
+ assert(!cir::MissingFeatures::astVarDeclInterface());
+ dynamicInitializers.push_back(f);
+ }
+
+ assert(!cir::MissingFeatures::opGlobalAnnotations());
+}
+
+void LoweringPreparePass::buildCXXGlobalInitFunc() {
+ if (dynamicInitializers.empty())
+ return;
+
+ assert(!cir::MissingFeatures::opGlobalCtorList());
+
+ SmallString<256> fnName;
+ // Include the filename in the symbol name. Including "sub_" matches gcc
+ // and makes sure these symbols appear lexicographically behind the symbols
+ // with priority (TBD). Module implementation units behave the same
+ // way as a non-modular TU with imports.
+ // TODO: check CXX20ModuleInits
+ if (astCtx->getCurrentNamedModule() &&
+ !astCtx->getCurrentNamedModule()->isModuleImplementation()) {
+ llvm::raw_svector_ostream out(fnName);
+ std::unique_ptr<clang::MangleContext> mangleCtx(
+ astCtx->createMangleContext());
+ cast<clang::ItaniumMangleContext>(*mangleCtx)
+ .mangleModuleInitializer(astCtx->getCurrentNamedModule(), out);
+ } else {
+ fnName += "_GLOBAL__sub_I_";
+ fnName += getTransformedFileName(mlirModule);
+ }
+
+ CIRBaseBuilderTy builder(getContext());
+ builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
+ auto fnType =
+ cir::FuncType::get({}, cir::VoidType::get(builder.getContext()));
+ cir::FuncOp f =
+ buildRuntimeFunction(builder, fnName, mlirModule.getLoc(), fnType,
+ cir::GlobalLinkageKind::ExternalLinkage);
+ builder.setInsertionPointToStart(f.addEntryBlock());
+ for (cir::FuncOp &f : dynamicInitializers)
+ builder.createCallOp(f.getLoc(), f, {});
+
+ builder.create<ReturnOp>(f.getLoc());
+}
+
static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
clang::ASTContext *astCtx,
mlir::Operation *op, mlir::Type eltTy,
@@ -691,6 +830,8 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
lowerComplexDivOp(complexDiv);
else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op))
lowerComplexMulOp(complexMul);
+ else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op))
+ lowerGlobalOp(glob);
else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op))
lowerUnaryOp(unary);
}
@@ -704,12 +845,15 @@ void LoweringPreparePass::runOnOperation() {
op->walk([&](mlir::Operation *op) {
if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
- cir::ComplexMulOp, cir::ComplexDivOp, cir::UnaryOp>(op))
+ cir::ComplexMulOp, cir::ComplexDivOp, cir::GlobalOp,
+ cir::UnaryOp>(op))
opsToTransform.push_back(op);
});
for (mlir::Operation *o : opsToTransform)
runOnOp(o);
+
+ buildCXXGlobalInitFunc();
}
std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp
index 102affc5563ac..d70bad37f22b9 100644
--- a/clang/test/CIR/CodeGen/global-init.cpp
+++ b/clang/test/CIR/CodeGen/global-init.cpp
@@ -1,8 +1,9 @@
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// 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 CIR generated from this test isn't ready for lowering to LLVM yet.
-// That will require changes to LoweringPrepare.
+// Note: The LoweringPrepare work isn't yet complete. We still need to create
+// the global ctor list attribute.
struct NeedsCtor {
NeedsCtor();
@@ -10,8 +11,19 @@ struct NeedsCtor {
NeedsCtor needsCtor;
-// CIR: cir.func private @_ZN9NeedsCtorC1Ev(!cir.ptr<!rec_NeedsCtor>)
-// CIR: cir.global external @needsCtor = ctor : !rec_NeedsCtor {
-// CIR: %[[THIS:.*]] = cir.get_global @needsCtor : !cir.ptr<!rec_NeedsCtor>
-// CIR: cir.call @_ZN9NeedsCtorC1Ev(%[[THIS]]) : (!cir.ptr<!rec_NeedsCtor>) -> ()
+// CIR-BEFORE-LPP: cir.func private @_ZN9NeedsCtorC1Ev(!cir.ptr<!rec_NeedsCtor>)
+// CIR-BEFORE-LPP: cir.global external @needsCtor = ctor : !rec_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-BEFORE-LPP: }
+
+// 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>
+// CIR: cir.call @_ZN9NeedsCtorC1Ev(%0) : (!cir.ptr<!rec_NeedsCtor>) -> ()
+// CIR: cir.return
+// CIR: }
+// CIR: cir.func private @_GLOBAL__sub_I_[[FILENAME:.*]]() {
+// CIR: cir.call @__cxx_global_var_init() : () -> ()
+// CIR: cir.return
// CIR: }
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, with some nits
for (cir::FuncOp &f : dynamicInitializers) | ||
builder.createCallOp(f.getLoc(), f, {}); | ||
|
||
builder.create<ReturnOp>(f.getLoc()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
builder.create<ReturnOp>(f.getLoc()); | |
cir::ReturnOp::create(builder, f.getLoc()); |
} | ||
|
||
assert(isa<YieldOp>(*yieldOp)); | ||
builder.create<ReturnOp>(yieldOp->getLoc()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
builder.create<ReturnOp>(yieldOp->getLoc()); | |
cir::ReturnOp::create(builder, yieldOp->getLoc()); |
CIRBaseBuilderTy builder(getContext()); | ||
builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back()); | ||
auto fnType = | ||
cir::FuncType::get({}, cir::VoidType::get(builder.getContext())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cir::FuncType::get({}, cir::VoidType::get(builder.getContext())); | |
cir::FuncType::get({}, builder.getVoidTy()); |
auto voidTy = cir::VoidType::get(builder.getContext()); | ||
auto fnType = cir::FuncType::get({}, voidTy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto voidTy = cir::VoidType::get(builder.getContext()); | |
auto fnType = cir::FuncType::get({}, voidTy); | |
auto fnType = cir::FuncType::get({}, builder.getVoidTy()); |
} | ||
|
||
// Register the destructor call with __cxa_atexit | ||
auto &dtorRegion = op.getDtorRegion(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Should we use auto
here as Region is a common MLIR term, or use the type similar to L675 🤔 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, one minor nit
#include "clang/CIR/Interfaces/CIRTypeInterfaces.h" | ||
|
||
namespace cir { | ||
inline constexpr uint32_t DefaultGlobalCtorDtorPriority = 65535; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this: (a) it's not used in this PR and (b) can be done in tablegen level by assigning a default value for priorities.
This adds the initial support for lowering the 'ctor' region of cir.global operations to an init function which is called from a TU-specific static initialization function.
This does not yet add an attribute to hold a list of global initializers. That will be added in a future change.