Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@

#include "clang/CIR/Interfaces/CIRTypeInterfaces.h"

namespace cir {
inline constexpr uint32_t DefaultGlobalCtorDtorPriority = 65535;
Copy link
Member

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.

} // namespace cir

//===----------------------------------------------------------------------===//
// CIR Dialect Attrs
//===----------------------------------------------------------------------===//
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
146 changes: 145 additions & 1 deletion clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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; }
};

Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto voidTy = cir::VoidType::get(builder.getContext());
auto fnType = cir::FuncType::get({}, voidTy);
auto fnType = cir::FuncType::get({}, builder.getVoidTy());

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();
Copy link
Member

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 🤔 ?

Copy link
Contributor Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
builder.create<ReturnOp>(yieldOp->getLoc());
cir::ReturnOp::create(builder, 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()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cir::FuncType::get({}, cir::VoidType::get(builder.getContext()));
cir::FuncType::get({}, builder.getVoidTy());

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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
builder.create<ReturnOp>(f.getLoc());
cir::ReturnOp::create(builder, f.getLoc());

}

static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder,
clang::ASTContext *astCtx,
mlir::Operation *op, mlir::Type eltTy,
Expand Down Expand Up @@ -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);
}
Expand All @@ -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() {
Expand Down
23 changes: 16 additions & 7 deletions clang/test/CIR/CodeGen/global-init.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
// 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();
};

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.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: 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.func private @_GLOBAL__sub_I_[[FILENAME:.*]]() {
// CIR: cir.call @__cxx_global_var_init() : () -> ()
// CIR: cir.return
// CIR: }