Skip to content

Commit 3b45fe2

Browse files
committed
[mlir][cf] Add ControlFlow to SCF lifting pass
Structured control flow ops have proven very useful for many transformations doing analysis on conditional flow and loops. Doing these transformations on CFGs requires repeated analysis of the IR possibly leading to more complicated or less capable implementations. With structured control flow, a lot of the information is already present in the structure. This patch therefore adds a transformation making it possible to lift arbitrary control flow graphs to structured control flow operations. The algorithm used is outlined in https://dl.acm.org/doi/10.1145/2693261. The complexity in implementing the algorithm was mostly spent correctly handling block arguments in MLIR (the paper only addresses the control flow graph part of it). Note that the transformation has been implemented fully generically and does not depend on any dialect. An interface implemented by the caller is used to construct any operation necessary for the transformation, making it possible to create an interface implementation purpose fit for ones IR. For the purpose of testing and due to likely being a very common scenario, this patch adds an interface implementation lifting the control flow dialect to the SCF dialect. Note the use of the word "lifting". Unlike other conversion passes, this pass is not 100% guaranteed to convert all ControlFlow ops. Only if the input region being transformed contains a single kind of return-like operations is it guaranteed to replace all control flow ops. If that is not the case, exactly one control flow op will remain branching to regions terminating with a given return-like operation (e.g. one region terminates with `llvm.return` the other with `llvm.unreachable`). Differential Revision: https://reviews.llvm.org/D156889
1 parent f3b1361 commit 3b45fe2

File tree

15 files changed

+2452
-1
lines changed

15 files changed

+2452
-1
lines changed

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1792,7 +1792,7 @@ OutputIt copy_if(R &&Range, OutputIt Out, UnaryPredicate P) {
17921792
template <typename T, typename R, typename Predicate>
17931793
T *find_singleton(R &&Range, Predicate P, bool AllowRepeats = false) {
17941794
T *RC = nullptr;
1795-
for (auto *A : Range) {
1795+
for (auto &&A : Range) {
17961796
if (T *PRC = P(A, AllowRepeats)) {
17971797
if (RC) {
17981798
if (!AllowRepeats || PRC != RC)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===- ControlFlowToSCF.h - ControlFlow to SCF -------------*- 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+
// Define conversions from the ControlFlow dialect to the SCF dialect.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef MLIR_CONVERSION_CONTROLFLOWTOSCF_CONTROLFLOWTOSCF_H
14+
#define MLIR_CONVERSION_CONTROLFLOWTOSCF_CONTROLFLOWTOSCF_H
15+
16+
#include <memory>
17+
18+
namespace mlir {
19+
class Pass;
20+
21+
#define GEN_PASS_DECL_LIFTCONTROLFLOWTOSCFPASS
22+
#include "mlir/Conversion/Passes.h.inc"
23+
24+
} // namespace mlir
25+
26+
#endif // MLIR_CONVERSION_CONTROLFLOWTOSCF_CONTROLFLOWTOSCF_H

mlir/include/mlir/Conversion/Passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "mlir/Conversion/ComplexToSPIRV/ComplexToSPIRVPass.h"
2323
#include "mlir/Conversion/ComplexToStandard/ComplexToStandard.h"
2424
#include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
25+
#include "mlir/Conversion/ControlFlowToSCF/ControlFlowToSCF.h"
2526
#include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRV.h"
2627
#include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRVPass.h"
2728
#include "mlir/Conversion/ConvertToLLVM/ToLLVMPass.h"

mlir/include/mlir/Conversion/Passes.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,31 @@ def ConvertControlFlowToLLVMPass : Pass<"convert-cf-to-llvm", "ModuleOp"> {
283283
];
284284
}
285285

286+
//===----------------------------------------------------------------------===//
287+
// ControlFlowToSCF
288+
//===----------------------------------------------------------------------===//
289+
290+
def LiftControlFlowToSCFPass : Pass<"lift-cf-to-scf"> {
291+
let summary = "Lift ControlFlow dialect to SCF dialect";
292+
let description = [{
293+
Lifts ControlFlow operations to SCF dialect operations.
294+
295+
This pass is prefixed with "lift" instead of "convert" as it is not always
296+
guaranteed to replace all ControlFlow ops.
297+
If a region contains only a single kind of return-like operation, all
298+
ControlFlow operations will be replaced successfully.
299+
Otherwise a single ControlFlow switch branching to one block per return-like
300+
operation kind remains.
301+
}];
302+
303+
let dependentDialects = ["scf::SCFDialect",
304+
"arith::ArithDialect",
305+
"ub::UBDialect",
306+
// TODO: This is only necessary until we have a
307+
// ub.unreachable op.
308+
"func::FuncDialect"];
309+
}
310+
286311
//===----------------------------------------------------------------------===//
287312
// ControlFlowToSPIRV
288313
//===----------------------------------------------------------------------===//

mlir/include/mlir/IR/Block.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ class Block : public IRObjectWithUseList<BlockOperand>,
5959
/// the specified block.
6060
void insertBefore(Block *block);
6161

62+
/// Insert this block (which must not already be in a region) right after
63+
/// the specified block.
64+
void insertAfter(Block *block);
65+
6266
/// Unlink this block from its current region and insert it right before the
6367
/// specific block.
6468
void moveBefore(Block *block);

mlir/include/mlir/IR/ValueRange.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,14 @@ class MutableOperandRange {
167167
return operator OperandRange()[index];
168168
}
169169

170+
OperandRange::iterator begin() const {
171+
return static_cast<OperandRange>(*this).begin();
172+
}
173+
174+
OperandRange::iterator end() const {
175+
return static_cast<OperandRange>(*this).end();
176+
}
177+
170178
private:
171179
/// Update the length of this range to the one provided.
172180
void updateLength(unsigned newLength);

mlir/include/mlir/Interfaces/ControlFlowInterfaces.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ class SuccessorOperands {
8282
/// Get the range of operands that are simply forwarded to the successor.
8383
OperandRange getForwardedOperands() const { return forwardedOperands; }
8484

85+
/// Get the range of operands that are simply forwarded to the successor.
86+
MutableOperandRange getMutableForwardedOperands() const {
87+
return forwardedOperands;
88+
}
89+
8590
/// Get a slice of the operands forwarded to the successor. The given range
8691
/// must not contain any operands produced by the operation.
8792
MutableOperandRange slice(unsigned subStart, unsigned subLen) const {
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//===- CFGToSCF.h - Control Flow Graph to Structured Control Flow *- 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 a generic `transformCFGToSCF` function that can be
10+
// used to lift any dialect operations implementing control flow graph
11+
// operations to any dialect implementing structured control flow operations.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef MLIR_TRANSFORMS_CFGTOSCF_H
16+
#define MLIR_TRANSFORMS_CFGTOSCF_H
17+
18+
#include "mlir/IR/Builders.h"
19+
#include "mlir/IR/Dominance.h"
20+
#include "mlir/IR/Operation.h"
21+
22+
namespace mlir {
23+
24+
/// Interface that should be implemented by any caller of `transformCFGToSCF`.
25+
/// The transformation requires the caller to 1) create switch-like control
26+
/// flow operations for intermediate transformations and 2) to create
27+
/// the desired structured control flow ops.
28+
class CFGToSCFInterface {
29+
public:
30+
virtual ~CFGToSCFInterface() = default;
31+
32+
/// Creates a structured control flow operation branching to one of `regions`.
33+
/// It replaces `controlFlowCondOp` and must have `resultTypes` as results.
34+
/// `regions` contains the list of branch regions corresponding to each
35+
/// successor of `controlFlowCondOp`. Their bodies must simply be taken and
36+
/// left as is.
37+
/// Returns failure if incapable of converting the control flow graph
38+
/// operation.
39+
virtual FailureOr<Operation *> createStructuredBranchRegionOp(
40+
OpBuilder &builder, Operation *controlFlowCondOp, TypeRange resultTypes,
41+
MutableArrayRef<Region> regions) = 0;
42+
43+
/// Creates a return-like terminator for a branch region of the op returned
44+
/// by `createStructuredBranchRegionOp`. `branchRegionOp` is the operation
45+
/// returned by `createStructuredBranchRegionOp` while `results` are the
46+
/// values that should be returned by the branch region.
47+
virtual LogicalResult
48+
createStructuredBranchRegionTerminatorOp(Location loc, OpBuilder &builder,
49+
Operation *branchRegionOp,
50+
ValueRange results) = 0;
51+
52+
/// Creates a structured control flow operation representing a do-while loop.
53+
/// The do-while loop is expected to have the exact same result types as the
54+
/// types of the iteration values.
55+
/// `loopBody` is the body of the loop. The implementation of this
56+
/// function must create a suitable terminator op at the end of the last block
57+
/// in `loopBody` which continues the loop if `condition` is 1 and exits the
58+
/// loop if 0. `loopValuesNextIter` are the values that have to be passed as
59+
/// the iteration values for the next iteration if continuing, or the result
60+
/// of the loop if exiting.
61+
/// `condition` is guaranteed to be of the same type as values returned by
62+
/// `getCFGSwitchValue` with either 0 or 1 as value.
63+
///
64+
/// `loopValuesInit` are the values used to initialize the iteration
65+
/// values of the loop.
66+
/// Returns failure if incapable of creating a loop op.
67+
virtual FailureOr<Operation *> createStructuredDoWhileLoopOp(
68+
OpBuilder &builder, Operation *replacedOp, ValueRange loopValuesInit,
69+
Value condition, ValueRange loopValuesNextIter, Region &&loopBody) = 0;
70+
71+
/// Creates a constant operation with a result representing `value` that is
72+
/// suitable as flag for `createCFGSwitchOp`.
73+
virtual Value getCFGSwitchValue(Location loc, OpBuilder &builder,
74+
unsigned value) = 0;
75+
76+
/// Creates a switch CFG branch operation branching to one of
77+
/// `caseDestinations` or `defaultDest`. This is used by the transformation
78+
/// for intermediate transformations before lifting to structured control
79+
/// flow. The switch op branches based on `flag` which is guaranteed to be of
80+
/// the same type as values returned by `getCFGSwitchValue`. Note:
81+
/// `caseValues` and other related ranges may be empty to represent an
82+
/// unconditional branch.
83+
virtual void createCFGSwitchOp(Location loc, OpBuilder &builder, Value flag,
84+
ArrayRef<unsigned> caseValues,
85+
BlockRange caseDestinations,
86+
ArrayRef<ValueRange> caseArguments,
87+
Block *defaultDest,
88+
ValueRange defaultArgs) = 0;
89+
90+
/// Creates a constant operation returning an undefined instance of `type`.
91+
/// This is required by the transformation as the lifting process might create
92+
/// control-flow paths where an SSA-value is undefined.
93+
virtual Value getUndefValue(Location loc, OpBuilder &builder, Type type) = 0;
94+
95+
/// Creates a return-like terminator indicating unreachable.
96+
/// This is required when the transformation encounters a statically known
97+
/// infinite loop. Since structured control flow ops are not terminators,
98+
/// after lifting an infinite loop, a terminator has to be placed after to
99+
/// possibly satisfy the terminator requirement of the region originally
100+
/// passed to `transformCFGToSCF`.
101+
///
102+
/// `region` is guaranteed to be the region originally passed to
103+
/// `transformCFGToSCF` and the op is guaranteed to always be an op in a block
104+
/// directly nested under `region` after the transformation.
105+
///
106+
/// Returns failure if incapable of creating an unreachable terminator.
107+
virtual FailureOr<Operation *>
108+
createUnreachableTerminator(Location loc, OpBuilder &builder,
109+
Region &region) = 0;
110+
111+
/// Helper function to create an unconditional branch using
112+
/// `createCFGSwitchOp`.
113+
void createSingleDestinationBranch(Location loc, OpBuilder &builder,
114+
Value dummyFlag, Block *destination,
115+
ValueRange arguments) {
116+
createCFGSwitchOp(loc, builder, dummyFlag, {}, {}, {}, destination,
117+
arguments);
118+
}
119+
120+
/// Helper function to create a conditional branch using
121+
/// `createCFGSwitchOp`.
122+
void createConditionalBranch(Location loc, OpBuilder &builder,
123+
Value condition, Block *trueDest,
124+
ValueRange trueArgs, Block *falseDest,
125+
ValueRange falseArgs) {
126+
createCFGSwitchOp(loc, builder, condition, {0}, {falseDest}, {falseArgs},
127+
trueDest, trueArgs);
128+
}
129+
};
130+
131+
/// Transformation lifting any dialect implementing control flow graph
132+
/// operations to a dialect implementing structured control flow operations.
133+
/// `region` is the region that should be transformed.
134+
/// The implementation of `interface` is responsible for the conversion of the
135+
/// control flow operations to the structured control flow operations.
136+
///
137+
/// If the region contains only a single kind of return-like operation, all
138+
/// control flow graph operations will be converted successfully.
139+
/// Otherwise a single control flow graph operation branching to one block
140+
/// per return-like operation kind remains.
141+
///
142+
/// The transformation currently requires that all control flow graph operations
143+
/// have no side effects, implement the BranchOpInterface and does not have any
144+
/// operation produced successor operands.
145+
/// Returns failure if any of the preconditions are violated or if any of the
146+
/// methods of `interface` failed. The IR is left in an unspecified state.
147+
///
148+
/// Otherwise, returns true or false if any changes to the IR have been made.
149+
FailureOr<bool> transformCFGToSCF(Region &region, CFGToSCFInterface &interface,
150+
DominanceInfo &dominanceInfo);
151+
152+
} // namespace mlir
153+
154+
#endif // MLIR_TRANSFORMS_CFGTOSCF_H

mlir/lib/Conversion/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_subdirectory(ComplexToLLVM)
1212
add_subdirectory(ComplexToSPIRV)
1313
add_subdirectory(ComplexToStandard)
1414
add_subdirectory(ControlFlowToLLVM)
15+
add_subdirectory(ControlFlowToSCF)
1516
add_subdirectory(ControlFlowToSPIRV)
1617
add_subdirectory(ConvertToLLVM)
1718
add_subdirectory(FuncToLLVM)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
add_mlir_conversion_library(MLIRControlFlowToSCF
2+
ControlFlowToSCF.cpp
3+
4+
ADDITIONAL_HEADER_DIRS
5+
${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/ControlFlowToSCF
6+
7+
DEPENDS
8+
MLIRConversionPassIncGen
9+
intrinsics_gen
10+
11+
LINK_COMPONENTS
12+
Core
13+
14+
LINK_LIBS PUBLIC
15+
MLIRAnalysis
16+
MLIRArithDialect
17+
MLIRControlFlowDialect
18+
MLIRFuncDialect
19+
MLIRSCFDialect
20+
MLIRUBDialect
21+
MLIRPass
22+
MLIRTransformUtils
23+
)

0 commit comments

Comments
 (0)