Skip to content

Commit f23a121

Browse files
htyulanza
authored andcommitted
[CIR][Lowering] Add LoweringPrepare pass to pre-lower global initializers (llvm#235)
As a follow up to llvm/clangir#197, this change pre-lowers high-level CIR for global initializers. High-level CIR: ``` cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 { %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22> loc(#loc8) %1 = cir.const(#true) : !cir.bool loc(#loc5) cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> () loc(#loc6) } ``` After pre-lowering: ``` cir.global "private" internal @_ZL8__ioinit = #cir.zero : !ty_22class2EInit22 {ast = #cir.vardecl.ast} cir.func internal private @__cxx_global_var_init() { %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22> %1 = cir.const(#true) : !cir.bool cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> () cir.return } cir.func private @_GLOBAL__sub_I_static.cpp() { cir.call @__cxx_global_var_init() : () -> () cir.return } ``` There is still work to be done to fully lower to LLVM. E.g, add `llvm.global_ctors` global to list all module initializers like `_GLOBAL__sub_I_static`. This will be handled in a separate change.
1 parent dd5ce51 commit f23a121

File tree

6 files changed

+231
-12
lines changed

6 files changed

+231
-12
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,9 @@ def GlobalOp : CIR_Op<"global", [Symbol, DeclareOpInterfaceMethods<RegionBranchO
12801280
// Note this can also be a FlatSymbolRefAttr
12811281
OptionalAttr<AnyAttr>:$initial_value,
12821282
UnitAttr:$constant,
1283-
OptionalAttr<I64Attr>:$alignment);
1283+
OptionalAttr<I64Attr>:$alignment,
1284+
OptionalAttr<ASTVarDeclAttr>:$ast
1285+
);
12841286
let regions = (region AnyRegion:$ctorRegion);
12851287
let assemblyFormat = [{
12861288
($sym_visibility^)?

clang/lib/CIR/CodeGen/CIRGenCXX.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ void CIRGenModule::codegenGlobalInitCxxStructor(const VarDecl *D,
8080
buildDeclInit(CGF, D, DeclAddr);
8181
builder.setInsertionPointToEnd(block);
8282
builder.create<mlir::cir::YieldOp>(Addr->getLoc());
83+
Addr.setAstAttr(mlir::cir::ASTVarDeclAttr::get(builder.getContext(), D));
8384
}
8485
CurCGF = nullptr;
8586
}

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,20 @@ LogicalResult GlobalOp::verify() {
12321232
return failure();
12331233
}
12341234

1235+
// Verify that the constructor region, if present, has only one block which is
1236+
// not empty.
1237+
auto &ctorRegion = getCtorRegion();
1238+
if (!ctorRegion.empty()) {
1239+
if (!ctorRegion.hasOneBlock()) {
1240+
return emitError() << "ctor region must have exactly one block.";
1241+
}
1242+
1243+
auto &block = ctorRegion.front();
1244+
if (block.empty()) {
1245+
return emitError() << "ctor region shall not be empty.";
1246+
}
1247+
}
1248+
12351249
if (std::optional<uint64_t> alignAttr = getAlignment()) {
12361250
uint64_t alignment = alignAttr.value();
12371251
if (!llvm::isPowerOf2_64(alignment))

clang/lib/CIR/Dialect/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_clang_library(MLIRCIRTransforms
99

1010
LINK_LIBS PUBLIC
1111
clangAST
12+
clangBasic
1213

1314
MLIRAnalysis
1415
MLIRIR

clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,55 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "PassDetail.h"
10+
#include "mlir/Dialect/Func/IR/FuncOps.h"
11+
#include "mlir/IR/Region.h"
1012
#include "clang/AST/ASTContext.h"
13+
#include "clang/AST/Mangle.h"
14+
#include "clang/Basic/Module.h"
1115
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1216
#include "clang/CIR/Dialect/Passes.h"
17+
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/ADT/StringMap.h"
19+
#include "llvm/ADT/Twine.h"
20+
#include "llvm/Support/Path.h"
1321

1422
using namespace mlir;
1523
using namespace cir;
1624

25+
static SmallString<128> getTransformedFileName(ModuleOp theModule) {
26+
SmallString<128> FileName;
27+
28+
if (theModule.getSymName()) {
29+
FileName = llvm::sys::path::filename(theModule.getSymName()->str());
30+
}
31+
32+
if (FileName.empty())
33+
FileName = "<null>";
34+
35+
for (size_t i = 0; i < FileName.size(); ++i) {
36+
// Replace everything that's not [a-zA-Z0-9._] with a _. This set happens
37+
// to be the set of C preprocessing numbers.
38+
if (!clang::isPreprocessingNumberBody(FileName[i]))
39+
FileName[i] = '_';
40+
}
41+
42+
return FileName;
43+
}
44+
1745
namespace {
1846
struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
1947
LoweringPreparePass() = default;
2048
void runOnOperation() override;
2149

50+
void runOnOp(Operation *op);
51+
void lowerGlobalOp(GlobalOp op);
52+
53+
/// Build the function that initializes the specified global
54+
cir::FuncOp buildCXXGlobalVarDeclInitFunc(GlobalOp op);
55+
56+
/// Build a module init function that calls all the dynamic initializers.
57+
void buildCXXGlobalInitFunc();
58+
2259
///
2360
/// AST related
2461
/// -----------
@@ -28,16 +65,139 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> {
2865

2966
/// Tracks current module.
3067
ModuleOp theModule;
68+
69+
/// Tracks existing dynamic initializers.
70+
llvm::StringMap<uint32_t> dynamicInitializerNames;
71+
llvm::SmallVector<FuncOp, 4> dynamicInitializers;
3172
};
3273
} // namespace
3374

75+
cir::FuncOp LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(GlobalOp op) {
76+
SmallString<256> fnName;
77+
{
78+
std::unique_ptr<clang::MangleContext> MangleCtx(
79+
astCtx->createMangleContext());
80+
llvm::raw_svector_ostream Out(fnName);
81+
auto varDecl = op.getAst()->getAstDecl();
82+
MangleCtx->mangleDynamicInitializer(varDecl, Out);
83+
// Name numbering
84+
uint32_t cnt = dynamicInitializerNames[fnName]++;
85+
if (cnt)
86+
fnName += "." + llvm::Twine(cnt).str();
87+
}
88+
89+
// Create a variable initialization function.
90+
mlir::OpBuilder builder(&getContext());
91+
builder.setInsertionPointAfter(op);
92+
auto fnType = mlir::cir::FuncType::get(
93+
{}, mlir::cir::VoidType::get(builder.getContext()));
94+
FuncOp f = builder.create<mlir::cir::FuncOp>(op.getLoc(), fnName, fnType);
95+
f.setLinkageAttr(mlir::cir::GlobalLinkageKindAttr::get(
96+
builder.getContext(), mlir::cir::GlobalLinkageKind::InternalLinkage));
97+
mlir::SymbolTable::setSymbolVisibility(
98+
f, mlir::SymbolTable::Visibility::Private);
99+
mlir::NamedAttrList attrs;
100+
f.setExtraAttrsAttr(mlir::cir::ExtraFuncAttributesAttr::get(
101+
builder.getContext(), attrs.getDictionary(builder.getContext())));
102+
103+
// move over the initialzation code of the ctor region.
104+
auto &block = op.getCtorRegion().front();
105+
mlir::Block *EntryBB = f.addEntryBlock();
106+
EntryBB->getOperations().splice(EntryBB->begin(), block.getOperations(),
107+
block.begin(), std::prev(block.end()));
108+
109+
// Replace cir.yield with cir.return
110+
builder.setInsertionPointToEnd(EntryBB);
111+
auto &yieldOp = block.getOperations().back();
112+
assert(isa<YieldOp>(yieldOp));
113+
builder.create<ReturnOp>(yieldOp.getLoc());
114+
return f;
115+
}
116+
117+
void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
118+
auto &ctorRegion = op.getCtorRegion();
119+
if (!ctorRegion.empty()) {
120+
// Build a variable initialization function and move the initialzation code
121+
// in the ctor region over.
122+
auto f = buildCXXGlobalVarDeclInitFunc(op);
123+
124+
// Clear the ctor region
125+
ctorRegion.getBlocks().clear();
126+
127+
// Add a function call to the variable initialization function.
128+
dynamicInitializers.push_back(f);
129+
}
130+
}
131+
132+
void LoweringPreparePass::buildCXXGlobalInitFunc() {
133+
if (dynamicInitializers.empty())
134+
return;
135+
136+
SmallString<256> fnName;
137+
// Include the filename in the symbol name. Including "sub_" matches gcc
138+
// and makes sure these symbols appear lexicographically behind the symbols
139+
// with priority emitted above. Module implementation units behave the same
140+
// way as a non-modular TU with imports.
141+
// TODO: check CXX20ModuleInits
142+
if (astCtx->getCurrentNamedModule() &&
143+
!astCtx->getCurrentNamedModule()->isModuleImplementation()) {
144+
llvm::raw_svector_ostream Out(fnName);
145+
std::unique_ptr<clang::MangleContext> MangleCtx(
146+
astCtx->createMangleContext());
147+
cast<clang::ItaniumMangleContext>(*MangleCtx)
148+
.mangleModuleInitializer(astCtx->getCurrentNamedModule(), Out);
149+
} else {
150+
fnName += "_GLOBAL__sub_I_";
151+
fnName += getTransformedFileName(theModule);
152+
}
153+
154+
mlir::OpBuilder builder(&getContext());
155+
builder.setInsertionPointToEnd(&theModule.getBodyRegion().back());
156+
auto fnType = mlir::cir::FuncType::get(
157+
{}, mlir::cir::VoidType::get(builder.getContext()));
158+
FuncOp f =
159+
builder.create<mlir::cir::FuncOp>(theModule.getLoc(), fnName, fnType);
160+
f.setLinkageAttr(mlir::cir::GlobalLinkageKindAttr::get(
161+
builder.getContext(), mlir::cir::GlobalLinkageKind::ExternalLinkage));
162+
mlir::SymbolTable::setSymbolVisibility(
163+
f, mlir::SymbolTable::Visibility::Private);
164+
mlir::NamedAttrList attrs;
165+
f.setExtraAttrsAttr(mlir::cir::ExtraFuncAttributesAttr::get(
166+
builder.getContext(), attrs.getDictionary(builder.getContext())));
167+
168+
builder.setInsertionPointToStart(f.addEntryBlock());
169+
for (auto &f : dynamicInitializers) {
170+
builder.create<mlir::cir::CallOp>(f.getLoc(), f);
171+
}
172+
173+
builder.create<ReturnOp>(f.getLoc());
174+
}
175+
176+
void LoweringPreparePass::runOnOp(Operation *op) {
177+
if (GlobalOp globalOp = cast<GlobalOp>(op)) {
178+
lowerGlobalOp(globalOp);
179+
return;
180+
}
181+
}
34182

35183
void LoweringPreparePass::runOnOperation() {
36184
assert(astCtx && "Missing ASTContext, please construct with the right ctor");
37185
auto* op = getOperation();
38186
if (isa<::mlir::ModuleOp>(op)) {
39187
theModule = cast<::mlir::ModuleOp>(op);
40188
}
189+
190+
SmallVector<Operation *> opsToTransform;
191+
op->walk([&](Operation *op) {
192+
if (isa<GlobalOp>(op))
193+
opsToTransform.push_back(op);
194+
});
195+
196+
for (auto *o : opsToTransform) {
197+
runOnOp(o);
198+
}
199+
200+
buildCXXGlobalInitFunc();
41201
}
42202

43203
std::unique_ptr<Pass> mlir::createLoweringPreparePass() {

clang/test/CIR/CodeGen/static.cpp

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2-
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
3-
// RUN: cir-opt %t.cir -o - | FileCheck %s -check-prefix=CIR
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=BEFORE
2+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-after=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s -check-prefix=AFTER
3+
// RUN: cir-opt %t.cir -o - | FileCheck %s -check-prefix=AFTER
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s -check-prefix=LLVM
45

56
class Init {
67

@@ -13,12 +14,52 @@ class Init {
1314

1415

1516
static Init __ioinit(true);
17+
static Init __ioinit2(false);
1618

17-
// CIR: module {{.*}} {
18-
// CIR-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
19-
// CIR-NEXT: cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 {
20-
// CIR-NEXT: %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22>
21-
// CIR-NEXT: %1 = cir.const(#true) : !cir.bool
22-
// CIR-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
23-
// CIR-NEXT: }
24-
// CIR-NEXT: }
19+
// BEFORE: module {{.*}} {
20+
// BEFORE-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
21+
// BEFORE-NEXT: cir.global "private" internal @_ZL8__ioinit = ctor : !ty_22class2EInit22 {
22+
// BEFORE-NEXT: %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22>
23+
// BEFORE-NEXT: %1 = cir.const(#true) : !cir.bool
24+
// BEFORE-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
25+
// BEFORE-NEXT: } {ast = #cir.vardecl.ast}
26+
// BEFORE: cir.global "private" internal @_ZL9__ioinit2 = ctor : !ty_22class2EInit22 {
27+
// BEFORE-NEXT: %0 = cir.get_global @_ZL9__ioinit2 : cir.ptr <!ty_22class2EInit22>
28+
// BEFORE-NEXT: %1 = cir.const(#false) : !cir.bool
29+
// BEFORE-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
30+
// BEFORE-NEXT: } {ast = #cir.vardecl.ast}
31+
// BEFORE-NEXT: }
32+
33+
34+
// AFTER: module {{.*}} {
35+
// AFTER-NEXT: cir.func private @_ZN4InitC1Eb(!cir.ptr<!ty_22class2EInit22>, !cir.bool)
36+
// AFTER-NEXT: cir.global "private" internal @_ZL8__ioinit = #cir.zero : !ty_22class2EInit22 {ast = #cir.vardecl.ast}
37+
// AFTER-NEXT: cir.func internal private @__cxx_global_var_init()
38+
// AFTER-NEXT: %0 = cir.get_global @_ZL8__ioinit : cir.ptr <!ty_22class2EInit22>
39+
// AFTER-NEXT: %1 = cir.const(#true) : !cir.bool
40+
// AFTER-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
41+
// AFTER-NEXT: cir.return
42+
// AFTER: cir.global "private" internal @_ZL9__ioinit2 = #cir.zero : !ty_22class2EInit22 {ast = #cir.vardecl.ast}
43+
// AFTER-NEXT: cir.func internal private @__cxx_global_var_init.1()
44+
// AFTER-NEXT: %0 = cir.get_global @_ZL9__ioinit2 : cir.ptr <!ty_22class2EInit22>
45+
// AFTER-NEXT: %1 = cir.const(#false) : !cir.bool
46+
// AFTER-NEXT: cir.call @_ZN4InitC1Eb(%0, %1) : (!cir.ptr<!ty_22class2EInit22>, !cir.bool) -> ()
47+
// AFTER-NEXT: cir.return
48+
// AFTER: cir.func private @_GLOBAL__sub_I_static.cpp()
49+
// AFTER-NEXT: cir.call @__cxx_global_var_init() : () -> ()
50+
// AFTER-NEXT: cir.call @__cxx_global_var_init.1() : () -> ()
51+
// AFTER-NEXT: cir.return
52+
53+
54+
// LLVM: @_ZL8__ioinit = internal global %class.Init zeroinitializer
55+
// LLVM: @_ZL9__ioinit2 = internal global %class.Init zeroinitializer
56+
// LLVM: define internal void @__cxx_global_var_init()
57+
// LLVM-NEXT: call void @_ZN4InitC1Eb(ptr @_ZL8__ioinit, i8 1)
58+
// LLVM-NEXT: ret void
59+
// LLVM: define internal void @__cxx_global_var_init.1()
60+
// LLVM-NEXT: call void @_ZN4InitC1Eb(ptr @_ZL9__ioinit2, i8 0)
61+
// LLVM-NEXT: ret void
62+
// LLVM: define void @_GLOBAL__sub_I_static.cpp()
63+
// LLVM-NEXT: call void @__cxx_global_var_init()
64+
// LLVM-NEXT: call void @__cxx_global_var_init.1()
65+
// LLVM-NEXT: ret void

0 commit comments

Comments
 (0)