279 lines · cpp
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-exception6//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#include "llvm/Support/DebugLog.h"17 18#include <gtest/gtest.h>19 20using namespace mlir;21 22/// A dummy op that is also a terminator.23struct DummyOp : public Op<DummyOp, OpTrait::IsTerminator, OpTrait::ZeroResults,24 OpTrait::ZeroSuccessors,25 RegionBranchTerminatorOpInterface::Trait> {26 using Op::Op;27 static ArrayRef<StringRef> getAttributeNames() { return {}; }28 29 static StringRef getOperationName() { return "cftest.dummy_op"; }30 31 MutableOperandRange getMutableSuccessorOperands(RegionSuccessor point) {32 return MutableOperandRange(getOperation(), 0, 0);33 }34};35 36/// All regions of this op are mutually exclusive.37struct MutuallyExclusiveRegionsOp38 : public Op<MutuallyExclusiveRegionsOp, RegionBranchOpInterface::Trait> {39 using Op::Op;40 static ArrayRef<StringRef> getAttributeNames() { return {}; }41 42 static StringRef getOperationName() {43 return "cftest.mutually_exclusive_regions_op";44 }45 46 // Regions have no successors.47 void getSuccessorRegions(RegionBranchPoint point,48 SmallVectorImpl<RegionSuccessor> ®ions) {}49 using RegionBranchOpInterface::Trait<50 MutuallyExclusiveRegionsOp>::getSuccessorRegions;51};52 53/// All regions of this op call each other in a large circle.54struct LoopRegionsOp55 : public Op<LoopRegionsOp, RegionBranchOpInterface::Trait> {56 using Op::Op;57 static const unsigned kNumRegions = 3;58 59 static ArrayRef<StringRef> getAttributeNames() { return {}; }60 61 static StringRef getOperationName() { return "cftest.loop_regions_op"; }62 63 void getSuccessorRegions(RegionBranchPoint point,64 SmallVectorImpl<RegionSuccessor> ®ions) {65 if (point.getTerminatorPredecessorOrNull()) {66 Region *region =67 point.getTerminatorPredecessorOrNull()->getParentRegion();68 if (region == &(*this)->getRegion(1))69 // This region also branches back to the parent.70 regions.push_back(71 RegionSuccessor(getOperation()->getParentOp(),72 getOperation()->getParentOp()->getResults()));73 regions.push_back(RegionSuccessor(region));74 }75 }76 using RegionBranchOpInterface::Trait<LoopRegionsOp>::getSuccessorRegions;77};78 79/// Each region branches back it itself or the parent.80struct DoubleLoopRegionsOp81 : public Op<DoubleLoopRegionsOp, RegionBranchOpInterface::Trait> {82 using Op::Op;83 84 static ArrayRef<StringRef> getAttributeNames() { return {}; }85 86 static StringRef getOperationName() {87 return "cftest.double_loop_regions_op";88 }89 90 void getSuccessorRegions(RegionBranchPoint point,91 SmallVectorImpl<RegionSuccessor> ®ions) {92 if (point.getTerminatorPredecessorOrNull()) {93 Region *region =94 point.getTerminatorPredecessorOrNull()->getParentRegion();95 regions.push_back(96 RegionSuccessor(getOperation()->getParentOp(),97 getOperation()->getParentOp()->getResults()));98 regions.push_back(RegionSuccessor(region));99 }100 }101 using RegionBranchOpInterface::Trait<102 DoubleLoopRegionsOp>::getSuccessorRegions;103};104 105/// Regions are executed sequentially.106struct SequentialRegionsOp107 : public Op<SequentialRegionsOp, RegionBranchOpInterface::Trait> {108 using Op::Op;109 static ArrayRef<StringRef> getAttributeNames() { return {}; }110 111 static StringRef getOperationName() { return "cftest.sequential_regions_op"; }112 113 // Region 0 has Region 1 as a successor.114 void getSuccessorRegions(RegionBranchPoint point,115 SmallVectorImpl<RegionSuccessor> ®ions) {116 if (point.getTerminatorPredecessorOrNull() &&117 point.getTerminatorPredecessorOrNull()->getParentRegion() ==118 &(*this)->getRegion(0)) {119 Operation *thisOp = this->getOperation();120 regions.push_back(RegionSuccessor(&thisOp->getRegion(1)));121 }122 }123 using RegionBranchOpInterface::Trait<124 SequentialRegionsOp>::getSuccessorRegions;125};126 127/// A dialect putting all the above together.128struct CFTestDialect : Dialect {129 explicit CFTestDialect(MLIRContext *ctx)130 : Dialect(getDialectNamespace(), ctx, TypeID::get<CFTestDialect>()) {131 addOperations<DummyOp, MutuallyExclusiveRegionsOp, LoopRegionsOp,132 DoubleLoopRegionsOp, SequentialRegionsOp>();133 }134 static StringRef getDialectNamespace() { return "cftest"; }135};136 137TEST(RegionBranchOpInterface, MutuallyExclusiveOps) {138 const char *ir = R"MLIR(139"cftest.mutually_exclusive_regions_op"() (140 {"cftest.dummy_op"() : () -> ()}, // op1141 {"cftest.dummy_op"() : () -> ()} // op2142 ) : () -> ()143 )MLIR";144 145 DialectRegistry registry;146 registry.insert<CFTestDialect>();147 MLIRContext ctx(registry);148 149 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);150 Operation *testOp = &module->getBody()->getOperations().front();151 Operation *op1 = &testOp->getRegion(0).front().front();152 Operation *op2 = &testOp->getRegion(1).front().front();153 154 EXPECT_TRUE(insideMutuallyExclusiveRegions(op1, op2));155 EXPECT_TRUE(insideMutuallyExclusiveRegions(op2, op1));156}157 158TEST(RegionBranchOpInterface, MutuallyExclusiveOps2) {159 const char *ir = R"MLIR(160"cftest.double_loop_regions_op"() (161 {"cftest.dummy_op"() : () -> ()}, // op1162 {"cftest.dummy_op"() : () -> ()} // op2163 ) : () -> ()164 )MLIR";165 166 DialectRegistry registry;167 registry.insert<CFTestDialect>();168 MLIRContext ctx(registry);169 170 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);171 Operation *testOp = &module->getBody()->getOperations().front();172 Operation *op1 = &testOp->getRegion(0).front().front();173 Operation *op2 = &testOp->getRegion(1).front().front();174 175 EXPECT_TRUE(insideMutuallyExclusiveRegions(op1, op2));176 EXPECT_TRUE(insideMutuallyExclusiveRegions(op2, op1));177}178 179TEST(RegionBranchOpInterface, NotMutuallyExclusiveOps) {180 const char *ir = R"MLIR(181"cftest.sequential_regions_op"() (182 {"cftest.dummy_op"() : () -> ()}, // op1183 {"cftest.dummy_op"() : () -> ()} // op2184 ) : () -> ()185 )MLIR";186 187 DialectRegistry registry;188 registry.insert<CFTestDialect>();189 MLIRContext ctx(registry);190 191 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);192 Operation *testOp = &module->getBody()->getOperations().front();193 Operation *op1 = &testOp->getRegion(0).front().front();194 Operation *op2 = &testOp->getRegion(1).front().front();195 196 EXPECT_FALSE(insideMutuallyExclusiveRegions(op1, op2));197 EXPECT_FALSE(insideMutuallyExclusiveRegions(op2, op1));198}199 200TEST(RegionBranchOpInterface, NestedMutuallyExclusiveOps) {201 const char *ir = R"MLIR(202"cftest.mutually_exclusive_regions_op"() (203 {204 "cftest.sequential_regions_op"() (205 {"cftest.dummy_op"() : () -> ()}, // op1206 {"cftest.dummy_op"() : () -> ()} // op3207 ) : () -> ()208 "cftest.dummy_op"() : () -> ()209 },210 {"cftest.dummy_op"() : () -> ()} // op2211 ) : () -> ()212 )MLIR";213 214 DialectRegistry registry;215 registry.insert<CFTestDialect>();216 MLIRContext ctx(registry);217 218 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);219 Operation *testOp = &module->getBody()->getOperations().front();220 Operation *op1 =221 &testOp->getRegion(0).front().front().getRegion(0).front().front();222 Operation *op2 = &testOp->getRegion(1).front().front();223 Operation *op3 =224 &testOp->getRegion(0).front().front().getRegion(1).front().front();225 226 EXPECT_TRUE(insideMutuallyExclusiveRegions(op1, op2));227 EXPECT_TRUE(insideMutuallyExclusiveRegions(op3, op2));228 EXPECT_FALSE(insideMutuallyExclusiveRegions(op1, op3));229}230 231TEST(RegionBranchOpInterface, RecursiveRegions) {232 const char *ir = R"MLIR(233"cftest.loop_regions_op"() (234 {"cftest.dummy_op"() : () -> ()}, // op1235 {"cftest.dummy_op"() : () -> ()}, // op2236 {"cftest.dummy_op"() : () -> ()} // op3237 ) : () -> ()238 )MLIR";239 240 DialectRegistry registry;241 registry.insert<CFTestDialect>();242 MLIRContext ctx(registry);243 244 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);245 Operation *testOp = &module->getBody()->getOperations().front();246 auto regionOp = cast<RegionBranchOpInterface>(testOp);247 Operation *op1 = &testOp->getRegion(0).front().front();248 Operation *op2 = &testOp->getRegion(1).front().front();249 Operation *op3 = &testOp->getRegion(2).front().front();250 251 EXPECT_TRUE(regionOp.isRepetitiveRegion(0));252 EXPECT_TRUE(regionOp.isRepetitiveRegion(1));253 EXPECT_TRUE(regionOp.isRepetitiveRegion(2));254 EXPECT_NE(getEnclosingRepetitiveRegion(op1), nullptr);255 EXPECT_NE(getEnclosingRepetitiveRegion(op2), nullptr);256 EXPECT_NE(getEnclosingRepetitiveRegion(op3), nullptr);257}258 259TEST(RegionBranchOpInterface, NotRecursiveRegions) {260 const char *ir = R"MLIR(261"cftest.sequential_regions_op"() (262 {"cftest.dummy_op"() : () -> ()}, // op1263 {"cftest.dummy_op"() : () -> ()} // op2264 ) : () -> ()265 )MLIR";266 267 DialectRegistry registry;268 registry.insert<CFTestDialect>();269 MLIRContext ctx(registry);270 271 OwningOpRef<ModuleOp> module = parseSourceString<ModuleOp>(ir, &ctx);272 Operation *testOp = &module->getBody()->getOperations().front();273 Operation *op1 = &testOp->getRegion(0).front().front();274 Operation *op2 = &testOp->getRegion(1).front().front();275 276 EXPECT_EQ(getEnclosingRepetitiveRegion(op1), nullptr);277 EXPECT_EQ(getEnclosingRepetitiveRegion(op2), nullptr);278}279