Skip to content

Commit f75c9fd

Browse files
committed
[CIR] Upstream initial support for CIR flattening
The ClangIR CFG has to be flat before it can be lowered to LLVM IR. That is, there can be no nested regions and all blocks in a region must belong to the parent region. Currently only cir.scope operations violate these rules, so the initial implementation of the cir-flatten-cfg pass only has to transform scope operations.
1 parent e1bd39c commit f75c9fd

File tree

18 files changed

+550
-4
lines changed

18 files changed

+550
-4
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
add_subdirectory(IR)
2+
3+
set(LLVM_TARGET_DEFINITIONS Passes.td)
4+
mlir_tablegen(Passes.h.inc -gen-pass-decls -name CIR)
5+
mlir_tablegen(Passes.capi.h.inc -gen-pass-capi-header --prefix CIR)
6+
mlir_tablegen(Passes.capi.cpp.inc -gen-pass-capi-impl --prefix CIR)
7+
add_public_tablegen_target(MLIRCIRPassIncGen)

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,46 @@ def ScopeOp : CIR_Op<"scope", [
428428
];
429429
}
430430

431+
//===----------------------------------------------------------------------===//
432+
// BrOp
433+
//===----------------------------------------------------------------------===//
434+
435+
def BrOp : CIR_Op<"br",
436+
[DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
437+
Pure, Terminator]> {
438+
let summary = "Unconditional branch";
439+
let description = [{
440+
The `cir.br` branches unconditionally to a block. Used to represent C/C++
441+
goto's and general block branching.
442+
443+
Note that for source level `goto`'s crossing scope boundaries, those are
444+
usually represented with the "symbolic" `cir.goto` operation.
445+
446+
Example:
447+
448+
```mlir
449+
...
450+
cir.br ^bb3
451+
^bb3:
452+
cir.return
453+
```
454+
}];
455+
456+
let builders = [
457+
OpBuilder<(ins "mlir::Block *":$dest,
458+
CArg<"mlir::ValueRange", "{}">:$destOperands), [{
459+
$_state.addSuccessors(dest);
460+
$_state.addOperands(destOperands);
461+
}]>
462+
];
463+
464+
let arguments = (ins Variadic<CIR_AnyType>:$destOperands);
465+
let successors = (successor AnySuccessor:$dest);
466+
let assemblyFormat = [{
467+
$dest (`(` $destOperands^ `:` type($destOperands) `)`)? attr-dict
468+
}];
469+
}
470+
431471
//===----------------------------------------------------------------------===//
432472
// GlobalOp
433473
//===----------------------------------------------------------------------===//
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===- Passes.h - CIR pass entry points -------------------------*- C++ -*-===//
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+
// This header file defines prototypes that expose pass constructors.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef CLANG_CIR_DIALECT_PASSES_H
14+
#define CLANG_CIR_DIALECT_PASSES_H
15+
16+
#include "mlir/Pass/Pass.h"
17+
18+
namespace clang {
19+
class ASTContext;
20+
}
21+
namespace mlir {
22+
23+
std::unique_ptr<Pass> createCIRFlattenCFGPass();
24+
25+
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm);
26+
27+
//===----------------------------------------------------------------------===//
28+
// Registration
29+
//===----------------------------------------------------------------------===//
30+
31+
void registerCIRDialectTranslation(mlir::MLIRContext &context);
32+
33+
/// Generate the code for registering passes.
34+
#define GEN_PASS_REGISTRATION
35+
#include "clang/CIR/Dialect/Passes.h.inc"
36+
37+
} // namespace mlir
38+
39+
#endif // CLANG_CIR_DIALECT_PASSES_H
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
#ifndef CLANG_CIR_DIALECT_PASSES_TD
10+
#define CLANG_CIR_DIALECT_PASSES_TD
11+
12+
include "mlir/Pass/PassBase.td"
13+
14+
def CIRFlattenCFG : Pass<"cir-flatten-cfg"> {
15+
let summary = "Produces flatten CFG";
16+
let description = [{
17+
This pass transforms CIR and inline all the nested regions. Thus,
18+
the next post condtions are met after the pass applied:
19+
- there is not any nested region in a function body
20+
- all the blocks in a function belong to the parent region
21+
In other words, this pass removes such CIR operations like IfOp, LoopOp,
22+
ScopeOp and etc. and produces a flat CIR.
23+
}];
24+
let constructor = "mlir::createCIRFlattenCFGPass()";
25+
let dependentDialects = ["cir::CIRDialect"];
26+
}
27+
28+
#endif // CLANG_CIR_DIALECT_PASSES_TD

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct MissingFeatures {
8282
static bool objCLifetime() { return false; }
8383
static bool emitNullabilityCheck() { return false; }
8484
static bool astVarDeclInterface() { return false; }
85+
static bool stackSaveOp() { return false; }
8586
};
8687

8788
} // namespace cir
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
add_subdirectory(IR)
2+
add_subdirectory(Transforms)

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,19 @@ LogicalResult cir::ScopeOp::verify() {
268268
return success();
269269
}
270270

271+
//===----------------------------------------------------------------------===//
272+
// BrOp
273+
//===----------------------------------------------------------------------===//
274+
275+
mlir::SuccessorOperands cir::BrOp::getSuccessorOperands(unsigned index) {
276+
assert(index == 0 && "invalid successor index");
277+
return mlir::SuccessorOperands(getDestOperandsMutable());
278+
}
279+
280+
Block *cir::BrOp::getSuccessorForOperands(ArrayRef<Attribute>) {
281+
return getDest();
282+
}
283+
271284
//===----------------------------------------------------------------------===//
272285
// GlobalOp
273286
//===----------------------------------------------------------------------===//

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_clang_library(MLIRCIR
1111

1212
LINK_LIBS PUBLIC
1313
MLIRIR
14+
MLIRCIRInterfaces
1415
MLIRDLTIDialect
1516
MLIRDataLayoutInterfaces
1617
MLIRFuncDialect
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
add_clang_library(MLIRCIRTransforms
2+
FlattenCFG.cpp
3+
4+
DEPENDS
5+
MLIRCIRPassIncGen
6+
7+
LINK_LIBS PUBLIC
8+
clangAST
9+
clangBasic
10+
11+
MLIRAnalysis
12+
MLIRIR
13+
MLIRPass
14+
MLIRTransformUtils
15+
16+
MLIRCIR
17+
MLIRCIRInterfaces
18+
)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
// This file implements pass that inlines CIR operations regions into the parent
10+
// function region.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "PassDetail.h"
15+
#include "mlir/Dialect/Func/IR/FuncOps.h"
16+
#include "mlir/IR/PatternMatch.h"
17+
#include "mlir/Support/LogicalResult.h"
18+
#include "mlir/Transforms/DialectConversion.h"
19+
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
20+
#include "clang/CIR/Dialect/IR/CIRDialect.h"
21+
#include "clang/CIR/Dialect/Passes.h"
22+
#include "clang/CIR/MissingFeatures.h"
23+
24+
using namespace mlir;
25+
using namespace cir;
26+
27+
namespace {
28+
29+
struct CIRFlattenCFGPass : public CIRFlattenCFGBase<CIRFlattenCFGPass> {
30+
31+
CIRFlattenCFGPass() = default;
32+
void runOnOperation() override;
33+
};
34+
35+
class CIRScopeOpFlattening : public mlir::OpRewritePattern<cir::ScopeOp> {
36+
public:
37+
using OpRewritePattern<cir::ScopeOp>::OpRewritePattern;
38+
39+
mlir::LogicalResult
40+
matchAndRewrite(cir::ScopeOp scopeOp,
41+
mlir::PatternRewriter &rewriter) const override {
42+
mlir::OpBuilder::InsertionGuard guard(rewriter);
43+
mlir::Location loc = scopeOp.getLoc();
44+
45+
// Empty scope: just remove it.
46+
// TODO: Remove this logic once CIR uses MLIR infrastructure to remove
47+
// trivially dead operations
48+
if (scopeOp.isEmpty()) {
49+
rewriter.eraseOp(scopeOp);
50+
return mlir::success();
51+
}
52+
53+
// Split the current block before the ScopeOp to create the inlining
54+
// point.
55+
mlir::Block *currentBlock = rewriter.getInsertionBlock();
56+
mlir::Block *continueBlock =
57+
rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
58+
if (scopeOp.getNumResults() > 0)
59+
continueBlock->addArguments(scopeOp.getResultTypes(), loc);
60+
61+
// Inline body region.
62+
mlir::Block *beforeBody = &scopeOp.getScopeRegion().front();
63+
mlir::Block *afterBody = &scopeOp.getScopeRegion().back();
64+
rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);
65+
66+
// Save stack and then branch into the body of the region.
67+
rewriter.setInsertionPointToEnd(currentBlock);
68+
assert(!cir::MissingFeatures::stackSaveOp());
69+
rewriter.create<cir::BrOp>(loc, mlir::ValueRange(), beforeBody);
70+
71+
// Replace the scopeop return with a branch that jumps out of the body.
72+
// Stack restore before leaving the body region.
73+
rewriter.setInsertionPointToEnd(afterBody);
74+
if (auto yieldOp = dyn_cast<cir::YieldOp>(afterBody->getTerminator())) {
75+
rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getArgs(),
76+
continueBlock);
77+
}
78+
79+
// Replace the op with values return from the body region.
80+
rewriter.replaceOp(scopeOp, continueBlock->getArguments());
81+
82+
return mlir::success();
83+
}
84+
};
85+
86+
void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
87+
patterns.add<CIRScopeOpFlattening>(patterns.getContext());
88+
}
89+
90+
void CIRFlattenCFGPass::runOnOperation() {
91+
RewritePatternSet patterns(&getContext());
92+
populateFlattenCFGPatterns(patterns);
93+
94+
// Collect operations to apply patterns.
95+
llvm::SmallVector<Operation *, 16> ops;
96+
getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
97+
if (isa<ScopeOp>(op))
98+
ops.push_back(op);
99+
});
100+
101+
// Apply patterns.
102+
if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
103+
signalPassFailure();
104+
}
105+
106+
} // namespace
107+
108+
namespace mlir {
109+
110+
std::unique_ptr<Pass> createCIRFlattenCFGPass() {
111+
return std::make_unique<CIRFlattenCFGPass>();
112+
}
113+
114+
} // namespace mlir

0 commit comments

Comments
 (0)