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
4 changes: 3 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1274,7 +1274,9 @@ def GlobalOp : CIR_Op<"global", [Symbol, DeclareOpInterfaceMethods<RegionBranchO
// Note this can also be a FlatSymbolRefAttr
OptionalAttr<AnyAttr>:$initial_value,
UnitAttr:$constant,
OptionalAttr<I64Attr>:$alignment);
OptionalAttr<I64Attr>:$alignment,
OptionalAttr<ASTVarDeclAttr>:$ast
);
let regions = (region AnyRegion:$ctorRegion);
let assemblyFormat = [{
($sym_visibility^)?
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ void CIRGenModule::codegenGlobalInitCxxStructor(const VarDecl *D,
buildDeclInit(CGF, D, DeclAddr);
builder.setInsertionPointToEnd(block);
builder.create<mlir::cir::YieldOp>(Addr->getLoc());
Addr.setAstAttr(mlir::cir::ASTVarDeclAttr::get(builder.getContext(), D));
}
CurCGF = nullptr;
}
14 changes: 14 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,20 @@ LogicalResult GlobalOp::verify() {
return failure();
}

// Verify that the constructor region, if present, has only one block which is
// not empty.
auto &ctorRegion = getCtorRegion();
if (!ctorRegion.empty()) {
if (!ctorRegion.hasOneBlock()) {
return emitError() << "ctor region must have exactly one block.";
}

auto &block = ctorRegion.front();
if (block.empty()) {
return emitError() << "ctor region shall not be empty.";
}
}

if (std::optional<uint64_t> alignAttr = getAlignment()) {
uint64_t alignment = alignAttr.value();
if (!llvm::isPowerOf2_64(alignment))
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ add_clang_library(MLIRCIRTransforms

LINK_LIBS PUBLIC
clangAST
clangBasic

MLIRAnalysis
MLIRIR
Expand Down
160 changes: 160 additions & 0 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,55 @@
//===----------------------------------------------------------------------===//

#include "PassDetail.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/IR/Region.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/Module.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/Passes.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Path.h"

using namespace mlir;
using namespace cir;

static SmallString<128> getTransformedFileName(ModuleOp theModule) {
SmallString<128> FileName;

if (theModule.getSymName()) {
FileName = llvm::sys::path::filename(theModule.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;
void runOnOperation() override;

void runOnOp(Operation *op);
void lowerGlobalOp(GlobalOp op);

/// Build the function that initializes the specified global
cir::FuncOp buildCXXGlobalVarDeclInitFunc(GlobalOp op);

/// Build a module init function that calls all the dynamic initializers.
void buildCXXGlobalInitFunc();

///
/// AST related
/// -----------
Expand All @@ -28,16 +65,139 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {

/// Tracks current module.
ModuleOp theModule;

/// Tracks existing dynamic initializers.
llvm::StringMap<uint32_t> dynamicInitializerNames;
llvm::SmallVector<FuncOp, 4> dynamicInitializers;
};
} // namespace

cir::FuncOp LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(GlobalOp op) {
SmallString<256> fnName;
{
std::unique_ptr<clang::MangleContext> MangleCtx(
astCtx->createMangleContext());
llvm::raw_svector_ostream Out(fnName);
auto varDecl = op.getAst()->getAstDecl();
MangleCtx->mangleDynamicInitializer(varDecl, Out);
// Name numbering
uint32_t cnt = dynamicInitializerNames[fnName]++;
if (cnt)
fnName += "." + llvm::Twine(cnt).str();
}

// Create a variable initialization function.
mlir::OpBuilder builder(&getContext());
builder.setInsertionPointAfter(op);
auto fnType = mlir::cir::FuncType::get(
{}, mlir::cir::VoidType::get(builder.getContext()));
FuncOp f = builder.create<mlir::cir::FuncOp>(op.getLoc(), fnName, fnType);
f.setLinkageAttr(mlir::cir::GlobalLinkageKindAttr::get(
builder.getContext(), mlir::cir::GlobalLinkageKind::InternalLinkage));
mlir::SymbolTable::setSymbolVisibility(
f, mlir::SymbolTable::Visibility::Private);
mlir::NamedAttrList attrs;
f.setExtraAttrsAttr(mlir::cir::ExtraFuncAttributesAttr::get(
builder.getContext(), attrs.getDictionary(builder.getContext())));

// move over the initialzation code of the ctor region.
auto &block = op.getCtorRegion().front();
mlir::Block *EntryBB = f.addEntryBlock();
EntryBB->getOperations().splice(EntryBB->begin(), block.getOperations(),
block.begin(), std::prev(block.end()));

// Replace cir.yield with cir.return
builder.setInsertionPointToEnd(EntryBB);
auto &yieldOp = block.getOperations().back();
assert(isa<YieldOp>(yieldOp));
builder.create<ReturnOp>(yieldOp.getLoc());
return f;
}

void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
auto &ctorRegion = op.getCtorRegion();
if (!ctorRegion.empty()) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you instead early return if ctorRegion.empty() is true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add a parallel dtorRegion check later on, so the check would probably look as is. So make sense to leave it as is?

Copy link
Member

Choose a reason for hiding this comment

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

Ok, sounds good!

// Build a variable initialization function and move the initialzation code
// in the ctor region over.
auto f = buildCXXGlobalVarDeclInitFunc(op);

// Clear the ctor region
ctorRegion.getBlocks().clear();

// Add a function call to the variable initialization function.
dynamicInitializers.push_back(f);
}
}

void LoweringPreparePass::buildCXXGlobalInitFunc() {
if (dynamicInitializers.empty())
return;

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 emitted above. 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(theModule);
}

mlir::OpBuilder builder(&getContext());
builder.setInsertionPointToEnd(&theModule.getBodyRegion().back());
auto fnType = mlir::cir::FuncType::get(
{}, mlir::cir::VoidType::get(builder.getContext()));
FuncOp f =
builder.create<mlir::cir::FuncOp>(theModule.getLoc(), fnName, fnType);
f.setLinkageAttr(mlir::cir::GlobalLinkageKindAttr::get(
builder.getContext(), mlir::cir::GlobalLinkageKind::ExternalLinkage));
mlir::SymbolTable::setSymbolVisibility(
f, mlir::SymbolTable::Visibility::Private);
mlir::NamedAttrList attrs;
f.setExtraAttrsAttr(mlir::cir::ExtraFuncAttributesAttr::get(
builder.getContext(), attrs.getDictionary(builder.getContext())));

builder.setInsertionPointToStart(f.addEntryBlock());
for (auto &f : dynamicInitializers) {
builder.create<mlir::cir::CallOp>(f.getLoc(), f);
}

builder.create<ReturnOp>(f.getLoc());
}

void LoweringPreparePass::runOnOp(Operation *op) {
if (GlobalOp globalOp = cast<GlobalOp>(op)) {
lowerGlobalOp(globalOp);
return;
}
}

void LoweringPreparePass::runOnOperation() {
assert(astCtx && "Missing ASTContext, please construct with the right ctor");
auto* op = getOperation();
if (isa<::mlir::ModuleOp>(op)) {
theModule = cast<::mlir::ModuleOp>(op);
}

SmallVector<Operation *> opsToTransform;
op->walk([&](Operation *op) {
if (isa<GlobalOp>(op))
opsToTransform.push_back(op);
});

for (auto *o : opsToTransform) {
runOnOp(o);
}

buildCXXGlobalInitFunc();
}

std::unique_ptr<Pass> mlir::createLoweringPreparePass() {
Expand Down
63 changes: 52 additions & 11 deletions clang/test/CIR/CodeGen/static.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: cir-opt %t.cir -o - | FileCheck %s -check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER
// RUN: cir-opt %t.cir -o - | FileCheck %s -check-prefix=AFTER
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-llvm %s -o - | FileCheck %s -check-prefix=LLVM

class Init {

Expand All @@ -13,12 +14,52 @@ class Init {


static Init __ioinit(true);
static Init __ioinit2(false);

// CIR: module {{.*}} {
// CIR-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
// CIR-NEXT: cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 {
// CIR-NEXT: %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22>
// CIR-NEXT: %1 = cir.const(#true) : !cir.bool
// CIR-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
// CIR-NEXT: }
// CIR-NEXT: }
// BEFORE: module {{.*}} {
// BEFORE-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
// BEFORE-NEXT: cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 {
// BEFORE-NEXT: %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22>
// BEFORE-NEXT: %1 = cir.const(#true) : !cir.bool
// BEFORE-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
// BEFORE-NEXT: } {ast = #cir.vardecl.ast}
// BEFORE: cir.global "private" internal @_ZL9__ioinit2 = ctor : !ty_22class2EInit22 {
// BEFORE-NEXT: %0 = cir.get_global @_ZL9__ioinit2 : cir.ptr <!ty_22class2EInit22>
// BEFORE-NEXT: %1 = cir.const(#false) : !cir.bool
// BEFORE-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
// BEFORE-NEXT: } {ast = #cir.vardecl.ast}
// BEFORE-NEXT: }


// AFTER: module {{.*}} {
// AFTER-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
// AFTER-NEXT: cir.global "private" internal @_ZL8__ioinit = #cir.zero : !ty_22class2EInit22 {ast = #cir.vardecl.ast}
// AFTER-NEXT: cir.func internal private @__cxx_global_var_init()
// AFTER-NEXT: %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22>
// AFTER-NEXT: %1 = cir.const(#true) : !cir.bool
// AFTER-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
// AFTER-NEXT: cir.return
// AFTER: cir.global "private" internal @_ZL9__ioinit2 = #cir.zero : !ty_22class2EInit22 {ast = #cir.vardecl.ast}
// AFTER-NEXT: cir.func internal private @__cxx_global_var_init.1()
// AFTER-NEXT: %0 = cir.get_global @_ZL9__ioinit2 : cir.ptr <!ty_22class2EInit22>
// AFTER-NEXT: %1 = cir.const(#false) : !cir.bool
// AFTER-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
// AFTER-NEXT: cir.return
// AFTER: cir.func private @_GLOBAL__sub_I_static.cpp()
// AFTER-NEXT: cir.call @__cxx_global_var_init() : () -> ()
// AFTER-NEXT: cir.call @__cxx_global_var_init.1() : () -> ()
// AFTER-NEXT: cir.return


// LLVM: @_ZL8__ioinit = internal global %class.Init zeroinitializer
// LLVM: @_ZL9__ioinit2 = internal global %class.Init zeroinitializer
// LLVM: define internal void @__cxx_global_var_init()
// LLVM-NEXT: call void @_ZN4InitC1Eb(ptr @_ZL8__ioinit, i8 1)
// LLVM-NEXT: ret void
// LLVM: define internal void @__cxx_global_var_init.1()
// LLVM-NEXT: call void @_ZN4InitC1Eb(ptr @_ZL9__ioinit2, i8 0)
// LLVM-NEXT: ret void
// LLVM: define void @_GLOBAL__sub_I_static.cpp()
// LLVM-NEXT: call void @__cxx_global_var_init()
// LLVM-NEXT: call void @__cxx_global_var_init.1()
// LLVM-NEXT: ret void