Skip to content

Commit 6e84a91

Browse files
committed
[CIR] Emit allocas into the proper lexical scope
Alloca operations were being emitted into the entry block of the current function unconditionally, even if the variable they represented was declared in a different scope. This change upstreams the code for handling insertion of the alloca into the proper lexcial scope. It also adds a CIR-to-CIR transformation to hoist allocas to the function entry block, which is necessary to produce the expected LLVM IR during lowering.
1 parent f51e5f3 commit 6e84a91

File tree

13 files changed

+353
-14
lines changed

13 files changed

+353
-14
lines changed

clang/include/clang/CIR/Dialect/Passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace mlir {
2222

2323
std::unique_ptr<Pass> createCIRCanonicalizePass();
2424
std::unique_ptr<Pass> createCIRFlattenCFGPass();
25+
std::unique_ptr<Pass> createHoistAllocasPass();
2526

2627
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
2728

clang/include/clang/CIR/Dialect/Passes.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ def CIRCanonicalize : Pass<"cir-canonicalize"> {
2929
let dependentDialects = ["cir::CIRDialect"];
3030
}
3131

32+
def HoistAllocas : Pass<"cir-hoist-allocas"> {
33+
let summary = "Hoist allocas to the entry of the function";
34+
let description = [{
35+
This pass hoist all non-dynamic allocas to the entry of the function.
36+
This is helpful for later code generation.
37+
}];
38+
let constructor = "mlir::createHoistAllocasPass()";
39+
let dependentDialects = ["cir::CIRDialect"];
40+
}
41+
3242
def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
3343
let summary = "Produces flatten CFG";
3444
let description = [{

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ struct MissingFeatures {
127127
static bool ternaryOp() { return false; }
128128
static bool tryOp() { return false; }
129129
static bool zextOp() { return false; }
130+
131+
// Future CIR attributes
132+
static bool optInfoAttr() { return false; }
130133
};
131134

132135
} // namespace cir

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
4949
// A normal fixed sized variable becomes an alloca in the entry block,
5050
mlir::Type allocaTy = convertTypeForMem(ty);
5151
// Create the temp alloca and declare variable using it.
52-
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
52+
address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
53+
/*insertIntoFnEntryBlock=*/false);
5354
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
5455

5556
emission.Addr = address;

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,27 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
261261
}
262262

263263
mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
264-
mlir::Location loc,
265-
CharUnits alignment) {
266-
mlir::Block *entryBlock = getCurFunctionEntryBlock();
264+
mlir::Location loc, CharUnits alignment,
265+
bool insertIntoFnEntryBlock,
266+
mlir::Value arraySize) {
267+
mlir::Block *entryBlock = insertIntoFnEntryBlock
268+
? getCurFunctionEntryBlock()
269+
: curLexScope->getEntryBlock();
270+
271+
// If this is an alloca in the entry basic block of a cir.try and there's
272+
// a surrounding cir.scope, make sure the alloca ends up in the surrounding
273+
// scope instead. This is necessary in order to guarantee all SSA values are
274+
// reachable during cleanups.
275+
assert(!cir::MissingFeatures::tryOp());
276+
277+
return emitAlloca(name, ty, loc, alignment,
278+
builder.getBestAllocaInsertPoint(entryBlock), arraySize);
279+
}
267280

281+
mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
282+
mlir::Location loc, CharUnits alignment,
283+
mlir::OpBuilder::InsertPoint ip,
284+
mlir::Value arraySize) {
268285
// CIR uses its own alloca address space rather than follow the target data
269286
// layout like original CodeGen. The data layout awareness should be done in
270287
// the lowering pass instead.
@@ -275,7 +292,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
275292
mlir::Value addr;
276293
{
277294
mlir::OpBuilder::InsertionGuard guard(builder);
278-
builder.restoreInsertionPoint(builder.getBestAllocaInsertPoint(entryBlock));
295+
builder.restoreInsertionPoint(ip);
279296
addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy,
280297
/*var type*/ ty, name, alignIntAttr);
281298
assert(!cir::MissingFeatures::astVarDeclInterface());
@@ -290,11 +307,13 @@ mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
290307
return builder.createDummyValue(loc, t, alignment);
291308
}
292309

293-
/// This creates an alloca and inserts it at the current insertion point of the
294-
/// builder.
310+
/// This creates an alloca and inserts it into the entry block if
311+
/// \p insertIntoFnEntryBlock is true, otherwise it inserts it at the current
312+
/// insertion point of the builder.
295313
Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
296-
mlir::Location loc,
297-
const Twine &name) {
298-
mlir::Value alloca = emitAlloca(name.str(), ty, loc, align);
314+
mlir::Location loc, const Twine &name,
315+
bool insertIntoFnEntryBlock) {
316+
mlir::Value alloca =
317+
emitAlloca(name.str(), ty, loc, align, insertIntoFnEntryBlock);
299318
return Address(alloca, ty, align);
300319
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
138138
void CIRGenFunction::emitAndUpdateRetAlloca(QualType type, mlir::Location loc,
139139
CharUnits alignment) {
140140
if (!type->isVoidType()) {
141-
fnRetAlloca = emitAlloca("__retval", convertType(type), loc, alignment);
141+
fnRetAlloca = emitAlloca("__retval", convertType(type), loc, alignment,
142+
/*insertIntoFnEntryBlock=*/false);
142143
}
143144
}
144145

@@ -293,7 +294,8 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
293294

294295
mlir::Value addrVal =
295296
emitAlloca(cast<NamedDecl>(paramVar)->getName(),
296-
convertType(paramVar->getType()), paramLoc, alignment);
297+
convertType(paramVar->getType()), paramLoc, alignment,
298+
/*insertIntoFnEntryBlock=*/false);
297299

298300
declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment,
299301
/*isParam=*/true);

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,13 @@ class CIRGenFunction : public CIRGenTypeCache {
111111

112112
public:
113113
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
114-
mlir::Location loc, clang::CharUnits alignment);
114+
mlir::Location loc, clang::CharUnits alignment,
115+
bool insertIntoFnEntryBlock,
116+
mlir::Value arraySize = nullptr);
117+
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
118+
mlir::Location loc, clang::CharUnits alignment,
119+
mlir::OpBuilder::InsertPoint ip,
120+
mlir::Value arraySize = nullptr);
115121

116122
mlir::Value createDummyValue(mlir::Location loc, clang::QualType qt);
117123

@@ -483,7 +489,7 @@ class CIRGenFunction : public CIRGenTypeCache {
483489
LexicalScope *curLexScope = nullptr;
484490

485491
Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc,
486-
const Twine &name = "tmp");
492+
const Twine &name, bool insertIntoFnEntryBlock);
487493
};
488494

489495
} // namespace clang::CIRGen

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_clang_library(MLIRCIRTransforms
22
CIRCanonicalize.cpp
33
FlattenCFG.cpp
4+
HoistAllocas.cpp
45

56
DEPENDS
67
MLIRCIRPassIncGen
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "PassDetail.h"
10+
#include "mlir/Dialect/Func/IR/FuncOps.h"
11+
#include "mlir/IR/PatternMatch.h"
12+
#include "mlir/Support/LogicalResult.h"
13+
#include "mlir/Transforms/DialectConversion.h"
14+
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
15+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
16+
#include "clang/CIR/Dialect/Passes.h"
17+
#include "clang/CIR/MissingFeatures.h"
18+
#include "llvm/Support/TimeProfiler.h"
19+
20+
using namespace mlir;
21+
using namespace cir;
22+
23+
namespace {
24+
25+
struct HoistAllocasPass : public HoistAllocasBase<HoistAllocasPass> {
26+
27+
HoistAllocasPass() = default;
28+
void runOnOperation() override;
29+
};
30+
31+
static void process(mlir::ModuleOp mod, cir::FuncOp func) {
32+
if (func.getRegion().empty())
33+
return;
34+
35+
// Hoist all static allocas to the entry block.
36+
mlir::Block &entryBlock = func.getRegion().front();
37+
llvm::SmallVector<cir::AllocaOp> allocas;
38+
func.getBody().walk([&](cir::AllocaOp alloca) {
39+
if (alloca->getBlock() == &entryBlock)
40+
return;
41+
// Don't hoist allocas with dynamic alloca size.
42+
assert(!cir::MissingFeatures::opAllocaDynAllocSize());
43+
allocas.push_back(alloca);
44+
});
45+
if (allocas.empty())
46+
return;
47+
48+
mlir::Operation *insertPoint = &*entryBlock.begin();
49+
50+
for (auto alloca : allocas) {
51+
// Preserving the `const` attribute on hoisted allocas can cause LLVM to
52+
// incorrectly introduce invariant group metadata in some circumstances.
53+
// The incubator performs some analysis to determine whether the attribute
54+
// can be preserved, but it only runs this analysis when optimizations are
55+
// enabled. Until we start tracking the optimization level, we can just
56+
// always remove the `const` attribute.
57+
assert(!cir::MissingFeatures::optInfoAttr());
58+
if (alloca.getConstant())
59+
alloca.setConstant(false);
60+
61+
alloca->moveBefore(insertPoint);
62+
}
63+
}
64+
65+
void HoistAllocasPass::runOnOperation() {
66+
llvm::TimeTraceScope scope("Hoist Allocas");
67+
llvm::SmallVector<Operation *, 16> ops;
68+
69+
Operation *op = getOperation();
70+
auto mod = mlir::dyn_cast<mlir::ModuleOp>(op);
71+
if (!mod)
72+
mod = op->getParentOfType<mlir::ModuleOp>();
73+
74+
getOperation()->walk([&](cir::FuncOp op) { process(mod, op); });
75+
}
76+
77+
} // namespace
78+
79+
std::unique_ptr<Pass> mlir::createHoistAllocasPass() {
80+
return std::make_unique<HoistAllocasPass>();
81+
}

clang/lib/CIR/Lowering/CIRPasses.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule,
3737
namespace mlir {
3838

3939
void populateCIRPreLoweringPasses(OpPassManager &pm) {
40+
pm.addPass(createHoistAllocasPass());
4041
pm.addPass(createCIRFlattenCFGPass());
4142
}
4243

0 commit comments

Comments
 (0)