Skip to content

Commit 701148f

Browse files
authored
[CIR] Upstream initial support for CIR flattening (#130648)
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 6dbb531 commit 701148f

File tree

18 files changed

+551
-4
lines changed

18 files changed

+551
-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 by inlining all the nested regions. Thus,
18+
the following conditions are true after the pass applied:
19+
- there are no nested regions in any 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

clang/lib/CIR/Dialect/CMakeLists.txt

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: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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. MLIR canonicalizer is too aggressive and we
48+
// need to either (a) make sure all our ops model all side-effects and/or
49+
// (b) have more options in the canonicalizer in MLIR to temper
50+
// aggressiveness level.
51+
if (scopeOp.isEmpty()) {
52+
rewriter.eraseOp(scopeOp);
53+
return mlir::success();
54+
}
55+
56+
// Split the current block before the ScopeOp to create the inlining
57+
// point.
58+
mlir::Block *currentBlock = rewriter.getInsertionBlock();
59+
mlir::Block *continueBlock =
60+
rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
61+
if (scopeOp.getNumResults() > 0)
62+
continueBlock->addArguments(scopeOp.getResultTypes(), loc);
63+
64+
// Inline body region.
65+
mlir::Block *beforeBody = &scopeOp.getScopeRegion().front();
66+
mlir::Block *afterBody = &scopeOp.getScopeRegion().back();
67+
rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);
68+
69+
// Save stack and then branch into the body of the region.
70+
rewriter.setInsertionPointToEnd(currentBlock);
71+
assert(!cir::MissingFeatures::stackSaveOp());
72+
rewriter.create<cir::BrOp>(loc, mlir::ValueRange(), beforeBody);
73+
74+
// Replace the scopeop return with a branch that jumps out of the body.
75+
// Stack restore before leaving the body region.
76+
rewriter.setInsertionPointToEnd(afterBody);
77+
if (auto yieldOp = dyn_cast<cir::YieldOp>(afterBody->getTerminator())) {
78+
rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getArgs(),
79+
continueBlock);
80+
}
81+
82+
// Replace the op with values return from the body region.
83+
rewriter.replaceOp(scopeOp, continueBlock->getArguments());
84+
85+
return mlir::success();
86+
}
87+
};
88+
89+
void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
90+
patterns.add<CIRScopeOpFlattening>(patterns.getContext());
91+
}
92+
93+
void CIRFlattenCFGPass::runOnOperation() {
94+
RewritePatternSet patterns(&getContext());
95+
populateFlattenCFGPatterns(patterns);
96+
97+
// Collect operations to apply patterns.
98+
llvm::SmallVector<Operation *, 16> ops;
99+
getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
100+
if (isa<ScopeOp>(op))
101+
ops.push_back(op);
102+
});
103+
104+
// Apply patterns.
105+
if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
106+
signalPassFailure();
107+
}
108+
109+
} // namespace
110+
111+
namespace mlir {
112+
113+
std::unique_ptr<Pass> createCIRFlattenCFGPass() {
114+
return std::make_unique<CIRFlattenCFGPass>();
115+
}
116+
117+
} // namespace mlir
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 CIR_DIALECT_TRANSFORMS_PASSDETAIL_H
10+
#define CIR_DIALECT_TRANSFORMS_PASSDETAIL_H
11+
12+
#include "mlir/IR/Dialect.h"
13+
#include "mlir/Pass/Pass.h"
14+
15+
namespace cir {
16+
class CIRDialect;
17+
} // namespace cir
18+
19+
namespace mlir {
20+
// Forward declaration from Dialect.h
21+
template <typename ConcreteDialect>
22+
void registerDialect(DialectRegistry &registry);
23+
24+
#define GEN_PASS_CLASSES
25+
#include "clang/CIR/Dialect/Passes.h.inc"
26+
27+
} // namespace mlir
28+
29+
#endif // CIR_DIALECT_TRANSFORMS_PASSDETAIL_H

clang/lib/CIR/Lowering/CIRPasses.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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 machinery for any CIR <-> CIR passes used by clang.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// #include "clang/AST/ASTContext.h"
14+
#include "clang/CIR/Dialect/Passes.h"
15+
16+
#include "mlir/Pass/PassManager.h"
17+
18+
namespace mlir {
19+
20+
void populateCIRPreLoweringPasses(OpPassManager &pm) {
21+
pm.addPass(createCIRFlattenCFGPass());
22+
}
23+
24+
} // namespace mlir

clang/lib/CIR/Lowering/CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,20 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Core
3+
Support
4+
)
5+
6+
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
7+
8+
add_clang_library(clangCIRLoweringCommon
9+
CIRPasses.cpp
10+
11+
LINK_LIBS
12+
clangCIR
13+
${dialect_libs}
14+
MLIRCIR
15+
MLIRCIRTransforms
16+
MLIRTransforms
17+
MLIRSupport
18+
)
19+
120
add_subdirectory(DirectToLLVM)

clang/lib/CIR/Lowering/DirectToLLVM/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
77

88
add_clang_library(clangCIRLoweringDirectToLLVM
99
LowerToLLVM.cpp
10+
LowerToLLVMIR.cpp
1011

1112
DEPENDS
1213
MLIRCIREnumsGen
1314
MLIRCIROpsIncGen
1415
MLIRCIROpInterfacesIncGen
1516

1617
LINK_LIBS
17-
MLIRIR
18+
clangCIRLoweringCommon
1819
${dialect_libs}
1920
MLIRCIR
2021
MLIRBuiltinToLLVMIRTranslation
2122
MLIRLLVMToLLVMIRTranslation
23+
MLIRIR
2224
)

0 commit comments

Comments
 (0)