236
|
1 //===- ControlFlowInterfacesTest.cpp - Unit Tests for Control Flow Interf. ===//
|
|
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 "mlir/Interfaces/ControlFlowInterfaces.h"
|
|
10 #include "mlir/IR/BuiltinOps.h"
|
|
11 #include "mlir/IR/Dialect.h"
|
|
12 #include "mlir/IR/DialectImplementation.h"
|
|
13 #include "mlir/IR/OpDefinition.h"
|
|
14 #include "mlir/IR/OpImplementation.h"
|
|
15 #include "mlir/Parser/Parser.h"
|
|
16
|
|
17 #include <gtest/gtest.h>
|
|
18
|
|
19 using namespace mlir;
|
|
20
|
|
21 /// A dummy op that is also a terminator.
|
|
22 struct DummyOp : public Op<DummyOp, OpTrait::IsTerminator> {
|
|
23 using Op::Op;
|
|
24 static ArrayRef<StringRef> getAttributeNames() { return {}; }
|
|
25
|
|
26 static StringRef getOperationName() { return "cftest.dummy_op"; }
|
|
27 };
|
|
28
|
|
29 /// All regions of this op are mutually exclusive.
|
|
30 struct MutuallyExclusiveRegionsOp
|
|
31 : public Op<MutuallyExclusiveRegionsOp, RegionBranchOpInterface::Trait> {
|
|
32 using Op::Op;
|
|
33 static ArrayRef<StringRef> getAttributeNames() { return {}; }
|
|
34
|
|
35 static StringRef getOperationName() {
|
|
36 return "cftest.mutually_exclusive_regions_op";
|
|
37 }
|
|
38
|
|
39 // Regions have no successors.
|
252
|
40 void getSuccessorRegions(std::optional<unsigned> index,
|
236
|
41 SmallVectorImpl<RegionSuccessor> ®ions) {}
|
|
42 };
|
|
43
|
|
44 /// All regions of this op call each other in a large circle.
|
|
45 struct LoopRegionsOp
|
|
46 : public Op<LoopRegionsOp, RegionBranchOpInterface::Trait> {
|
|
47 using Op::Op;
|
|
48 static const unsigned kNumRegions = 3;
|
|
49
|
|
50 static ArrayRef<StringRef> getAttributeNames() { return {}; }
|
|
51
|
|
52 static StringRef getOperationName() { return "cftest.loop_regions_op"; }
|
|
53
|
252
|
54 void getSuccessorRegions(std::optional<unsigned> index,
|
236
|
55 SmallVectorImpl<RegionSuccessor> ®ions) {
|
|
56 if (index) {
|
|
57 if (*index == 1)
|
|
58 // This region also branches back to the parent.
|
|
59 regions.push_back(RegionSuccessor());
|
|
60 regions.push_back(
|
|
61 RegionSuccessor(&getOperation()->getRegion(*index % kNumRegions)));
|
|
62 }
|
|
63 }
|
|
64 };
|
|
65
|
|
66 /// Each region branches back it itself or the parent.
|
|
67 struct DoubleLoopRegionsOp
|
|
68 : public Op<DoubleLoopRegionsOp, RegionBranchOpInterface::Trait> {
|
|
69 using Op::Op;
|
|
70
|
|
71 static ArrayRef<StringRef> getAttributeNames() { return {}; }
|
|
72
|
|
73 static StringRef getOperationName() {
|
|
74 return "cftest.double_loop_regions_op";
|
|
75 }
|
|
76
|
252
|
77 void getSuccessorRegions(std::optional<unsigned> index,
|
236
|
78 SmallVectorImpl<RegionSuccessor> ®ions) {
|
|
79 if (index.has_value()) {
|
|
80 regions.push_back(RegionSuccessor());
|
|
81 regions.push_back(RegionSuccessor(&getOperation()->getRegion(*index)));
|
|
82 }
|
|
83 }
|
|
84 };
|
|
85
|
|
86 /// Regions are executed sequentially.
|
|
87 struct SequentialRegionsOp
|
|
88 : public Op<SequentialRegionsOp, RegionBranchOpInterface::Trait> {
|
|
89 using Op::Op;
|
|
90 static ArrayRef<StringRef> getAttributeNames() { return {}; }
|
|
91
|
|
92 static StringRef getOperationName() { return "cftest.sequential_regions_op"; }
|
|
93
|
|
94 // Region 0 has Region 1 as a successor.
|
252
|
95 void getSuccessorRegions(std::optional<unsigned> index,
|
236
|
96 SmallVectorImpl<RegionSuccessor> ®ions) {
|
|
97 if (index == 0u) {
|
|
98 Operation *thisOp = this->getOperation();
|
|
99 regions.push_back(RegionSuccessor(&thisOp->getRegion(1)));
|
|
100 }
|
|
101 }
|
|
102 };
|
|
103
|
|
104 /// A dialect putting all the above together.
|
|
105 struct CFTestDialect : Dialect {
|
|
106 explicit CFTestDialect(MLIRContext *ctx)
|
|
107 : Dialect(getDialectNamespace(), ctx, TypeID::get<CFTestDialect>()) {
|
|
108 addOperations<DummyOp, MutuallyExclusiveRegionsOp, LoopRegionsOp,
|
|
109 DoubleLoopRegionsOp, SequentialRegionsOp>();
|
|
110 }
|
|
111 static StringRef getDialectNamespace() { return "cftest"; }
|
|
112 };
|
|
113
|
|
114 TEST(RegionBranchOpInterface, MutuallyExclusiveOps) {
|
|
115 const char *ir = R"MLIR(
|
|
116 "cftest.mutually_exclusive_regions_op"() (
|
|
117 {"cftest.dummy_op"() : () -> ()}, // op1
|
|
118 {"cftest.dummy_op"() : () -> ()} // op2
|
|
119 ) : () -> ()
|
|
120 )MLIR";
|
|
121
|
|
122 DialectRegistry registry;
|
|
123 registry.insert<CFTestDialect>();
|
|
124 MLIRContext ctx(registry);
|
|
125
|
|
126 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
|
|
127 Operation *testOp = &module->getBody()->getOperations().front();
|
|
128 Operation *op1 = &testOp->getRegion(0).front().front();
|
|
129 Operation *op2 = &testOp->getRegion(1).front().front();
|
|
130
|
|
131 EXPECT_TRUE(insideMutuallyExclusiveRegions(op1, op2));
|
|
132 EXPECT_TRUE(insideMutuallyExclusiveRegions(op2, op1));
|
|
133 }
|
|
134
|
|
135 TEST(RegionBranchOpInterface, MutuallyExclusiveOps2) {
|
|
136 const char *ir = R"MLIR(
|
|
137 "cftest.double_loop_regions_op"() (
|
|
138 {"cftest.dummy_op"() : () -> ()}, // op1
|
|
139 {"cftest.dummy_op"() : () -> ()} // op2
|
|
140 ) : () -> ()
|
|
141 )MLIR";
|
|
142
|
|
143 DialectRegistry registry;
|
|
144 registry.insert<CFTestDialect>();
|
|
145 MLIRContext ctx(registry);
|
|
146
|
|
147 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
|
|
148 Operation *testOp = &module->getBody()->getOperations().front();
|
|
149 Operation *op1 = &testOp->getRegion(0).front().front();
|
|
150 Operation *op2 = &testOp->getRegion(1).front().front();
|
|
151
|
|
152 EXPECT_TRUE(insideMutuallyExclusiveRegions(op1, op2));
|
|
153 EXPECT_TRUE(insideMutuallyExclusiveRegions(op2, op1));
|
|
154 }
|
|
155
|
|
156 TEST(RegionBranchOpInterface, NotMutuallyExclusiveOps) {
|
|
157 const char *ir = R"MLIR(
|
|
158 "cftest.sequential_regions_op"() (
|
|
159 {"cftest.dummy_op"() : () -> ()}, // op1
|
|
160 {"cftest.dummy_op"() : () -> ()} // op2
|
|
161 ) : () -> ()
|
|
162 )MLIR";
|
|
163
|
|
164 DialectRegistry registry;
|
|
165 registry.insert<CFTestDialect>();
|
|
166 MLIRContext ctx(registry);
|
|
167
|
|
168 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
|
|
169 Operation *testOp = &module->getBody()->getOperations().front();
|
|
170 Operation *op1 = &testOp->getRegion(0).front().front();
|
|
171 Operation *op2 = &testOp->getRegion(1).front().front();
|
|
172
|
|
173 EXPECT_FALSE(insideMutuallyExclusiveRegions(op1, op2));
|
|
174 EXPECT_FALSE(insideMutuallyExclusiveRegions(op2, op1));
|
|
175 }
|
|
176
|
|
177 TEST(RegionBranchOpInterface, NestedMutuallyExclusiveOps) {
|
|
178 const char *ir = R"MLIR(
|
|
179 "cftest.mutually_exclusive_regions_op"() (
|
|
180 {
|
|
181 "cftest.sequential_regions_op"() (
|
|
182 {"cftest.dummy_op"() : () -> ()}, // op1
|
|
183 {"cftest.dummy_op"() : () -> ()} // op3
|
|
184 ) : () -> ()
|
|
185 "cftest.dummy_op"() : () -> ()
|
|
186 },
|
|
187 {"cftest.dummy_op"() : () -> ()} // op2
|
|
188 ) : () -> ()
|
|
189 )MLIR";
|
|
190
|
|
191 DialectRegistry registry;
|
|
192 registry.insert<CFTestDialect>();
|
|
193 MLIRContext ctx(registry);
|
|
194
|
|
195 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
|
|
196 Operation *testOp = &module->getBody()->getOperations().front();
|
|
197 Operation *op1 =
|
|
198 &testOp->getRegion(0).front().front().getRegion(0).front().front();
|
|
199 Operation *op2 = &testOp->getRegion(1).front().front();
|
|
200 Operation *op3 =
|
|
201 &testOp->getRegion(0).front().front().getRegion(1).front().front();
|
|
202
|
|
203 EXPECT_TRUE(insideMutuallyExclusiveRegions(op1, op2));
|
|
204 EXPECT_TRUE(insideMutuallyExclusiveRegions(op3, op2));
|
|
205 EXPECT_FALSE(insideMutuallyExclusiveRegions(op1, op3));
|
|
206 }
|
|
207
|
|
208 TEST(RegionBranchOpInterface, RecursiveRegions) {
|
|
209 const char *ir = R"MLIR(
|
|
210 "cftest.loop_regions_op"() (
|
|
211 {"cftest.dummy_op"() : () -> ()}, // op1
|
|
212 {"cftest.dummy_op"() : () -> ()}, // op2
|
|
213 {"cftest.dummy_op"() : () -> ()} // op3
|
|
214 ) : () -> ()
|
|
215 )MLIR";
|
|
216
|
|
217 DialectRegistry registry;
|
|
218 registry.insert<CFTestDialect>();
|
|
219 MLIRContext ctx(registry);
|
|
220
|
|
221 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
|
|
222 Operation *testOp = &module->getBody()->getOperations().front();
|
|
223 auto regionOp = cast<RegionBranchOpInterface>(testOp);
|
|
224 Operation *op1 = &testOp->getRegion(0).front().front();
|
|
225 Operation *op2 = &testOp->getRegion(1).front().front();
|
|
226 Operation *op3 = &testOp->getRegion(2).front().front();
|
|
227
|
|
228 EXPECT_TRUE(regionOp.isRepetitiveRegion(0));
|
|
229 EXPECT_TRUE(regionOp.isRepetitiveRegion(1));
|
|
230 EXPECT_TRUE(regionOp.isRepetitiveRegion(2));
|
|
231 EXPECT_NE(getEnclosingRepetitiveRegion(op1), nullptr);
|
|
232 EXPECT_NE(getEnclosingRepetitiveRegion(op2), nullptr);
|
|
233 EXPECT_NE(getEnclosingRepetitiveRegion(op3), nullptr);
|
|
234 }
|
|
235
|
|
236 TEST(RegionBranchOpInterface, NotRecursiveRegions) {
|
|
237 const char *ir = R"MLIR(
|
|
238 "cftest.sequential_regions_op"() (
|
|
239 {"cftest.dummy_op"() : () -> ()}, // op1
|
|
240 {"cftest.dummy_op"() : () -> ()} // op2
|
|
241 ) : () -> ()
|
|
242 )MLIR";
|
|
243
|
|
244 DialectRegistry registry;
|
|
245 registry.insert<CFTestDialect>();
|
|
246 MLIRContext ctx(registry);
|
|
247
|
|
248 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);
|
|
249 Operation *testOp = &module->getBody()->getOperations().front();
|
|
250 Operation *op1 = &testOp->getRegion(0).front().front();
|
|
251 Operation *op2 = &testOp->getRegion(1).front().front();
|
|
252
|
|
253 EXPECT_EQ(getEnclosingRepetitiveRegion(op1), nullptr);
|
|
254 EXPECT_EQ(getEnclosingRepetitiveRegion(op2), nullptr);
|
|
255 }
|