From: Mogball Date: Fri, 4 Mar 2022 20:23:24 +0000 (+0000) Subject: [mlir] Region/BranchOpInterface: Allow implicit type conversions along control-flow... X-Git-Tag: upstream/15.0.7~14575 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e7c7b16a849fb40169a708d27cb59747139ed8c7;p=platform%2Fupstream%2Fllvm.git [mlir] Region/BranchOpInterface: Allow implicit type conversions along control-flow edges RegionBranchOpInterface and BranchOpInterface are allowed to make implicit type conversions along control-flow edges. In effect, this adds an interface method, `areTypesCompatible`, to both interfaces, which should return whether the types of corresponding successor operands and block arguments are compatible. Users of the interfaces, here on forth, must be aware that types may mismatch, although current users (in MLIR core), are not affected by this change. By default, type equality is used. `async.execute` already has unequal types along control-flow edges (`!async.value` vs. `f32`), but it opted out of calling `RegionBranchOpInterface::verifyTypes` in its verifier. That method has now been removed and `RegionBranchOpInterface` will verify types along control edges by default in its verifier. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D120790 --- diff --git a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td index d199bb2..f213813 100644 --- a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td +++ b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td @@ -30,7 +30,8 @@ class Async_Op traits = []> : def Async_ExecuteOp : Async_Op<"execute", [SingleBlockImplicitTerminator<"YieldOp">, DeclareOpInterfaceMethods, + ["getSuccessorEntryOperands", + "areTypesCompatible"]>, AttrSizedOperandSegments, AutomaticAllocationScope]> { let summary = "Asynchronous execute operation"; diff --git a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td index 63fceba..d94c01e 100644 --- a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td +++ b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td @@ -273,7 +273,6 @@ def MemRef_AllocaScopeOp : MemRef_Op<"alloca_scope", let results = (outs Variadic:$results); let regions = (region SizedRegion<1>:$bodyRegion); let hasCustomAssemblyFormat = 1; - let hasVerifier = 1; let hasCanonicalizer = 1; } diff --git a/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td b/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td index 31a6952..51534dd 100644 --- a/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td +++ b/mlir/include/mlir/Dialect/Shape/IR/ShapeOps.td @@ -865,7 +865,6 @@ def Shape_AssumingOp : Shape_Op<"assuming", [ let hasCanonicalizer = 1; let hasCustomAssemblyFormat = 1; - let hasVerifier = 1; } def Shape_AssumingYieldOp : Shape_Op<"assuming_yield", diff --git a/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td b/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td index 1fadf5b..25da768 100644 --- a/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td +++ b/mlir/include/mlir/Interfaces/ControlFlowInterfaces.td @@ -24,6 +24,13 @@ def BranchOpInterface : OpInterface<"BranchOpInterface"> { let description = [{ This interface provides information for branching terminator operations, i.e. terminator operations with successors. + + This interface is meant to model well-defined cases of control-flow of + value propagation, where what occurs along control-flow edges is assumed to + be side-effect free. For example, corresponding successor operands and + successor block arguments may have different types. In such cases, + `areTypesCompatible` can be implemented to compare types along control-flow + edges. By default, type equality is used. }]; let cppNamespace = "::mlir"; @@ -73,7 +80,15 @@ def BranchOpInterface : OpInterface<"BranchOpInterface"> { "::mlir::Block *", "getSuccessorForOperands", (ins "::mlir::ArrayRef<::mlir::Attribute>":$operands), [{}], /*defaultImplementation=*/[{ return nullptr; }] - > + >, + InterfaceMethod<[{ + This method is called to compare types along control-flow edges. By + default, the types are checked as equal. + }], + "bool", "areTypesCompatible", + (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], + [{ return lhs == rhs; }] + >, ]; let verify = [{ @@ -96,6 +111,13 @@ def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> { This interface provides information for region operations that contain branching behavior between held regions, i.e. this interface allows for expressing control flow information for region holding operations. + + This interface is meant to model well-defined cases of control-flow of + value propagation, where what occurs along control-flow edges is assumed to + be side-effect free. For example, corresponding successor operands and + successor block arguments may have different types. In such cases, + `areTypesCompatible` can be implemented to compare types along control-flow + edges. By default, type equality is used. }]; let cppNamespace = "::mlir"; @@ -156,12 +178,20 @@ def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> { [{ invocationBounds.append($_op->getNumRegions(), ::mlir::InvocationBounds::getUnknown()); }] >, + InterfaceMethod<[{ + This method is called to compare types along control-flow edges. By + default, the types are checked as equal. + }], + "bool", "areTypesCompatible", + (ins "::mlir::Type":$lhs, "::mlir::Type":$rhs), [{}], + [{ return lhs == rhs; }] + >, ]; let verify = [{ static_assert(!ConcreteOp::template hasTrait(), "expected operation to have non-zero regions"); - return success(); + return detail::verifyTypesAlongControlFlowEdges($_op); }]; let extraClassDeclaration = [{ @@ -171,11 +201,6 @@ def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> { SmallVector nullAttrs(getOperation()->getNumOperands()); getSuccessorRegions(index, nullAttrs, regions); } - - /// Verify types along control flow edges described by this interface. - static LogicalResult verifyTypes(Operation *op) { - return detail::verifyTypesAlongControlFlowEdges(op); - } }]; } diff --git a/mlir/lib/Dialect/Async/IR/Async.cpp b/mlir/lib/Dialect/Async/IR/Async.cpp index 916e2d1..541c69e 100644 --- a/mlir/lib/Dialect/Async/IR/Async.cpp +++ b/mlir/lib/Dialect/Async/IR/Async.cpp @@ -50,7 +50,6 @@ LogicalResult YieldOp::verify() { MutableOperandRange YieldOp::getMutableSuccessorOperands(Optional index) { - assert(!index.hasValue()); return operandsMutable(); } @@ -65,6 +64,15 @@ OperandRange ExecuteOp::getSuccessorEntryOperands(unsigned index) { return operands(); } +bool ExecuteOp::areTypesCompatible(Type lhs, Type rhs) { + const auto getValueOrTokenType = [](Type type) { + if (auto value = type.dyn_cast()) + return value.getValueType(); + return type; + }; + return getValueOrTokenType(lhs) == getValueOrTokenType(rhs); +} + void ExecuteOp::getSuccessorRegions(Optional index, ArrayRef, SmallVectorImpl ®ions) { diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp index bf8edc0..28f2052 100644 --- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp +++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp @@ -244,10 +244,6 @@ ParseResult AllocaScopeOp::parse(OpAsmParser &parser, OperationState &result) { return success(); } -LogicalResult AllocaScopeOp::verify() { - return RegionBranchOpInterface::verifyTypes(*this); -} - void AllocaScopeOp::getSuccessorRegions( Optional index, ArrayRef operands, SmallVectorImpl ®ions) { diff --git a/mlir/lib/Dialect/SCF/SCF.cpp b/mlir/lib/Dialect/SCF/SCF.cpp index 23ef80d..588c675 100644 --- a/mlir/lib/Dialect/SCF/SCF.cpp +++ b/mlir/lib/Dialect/SCF/SCF.cpp @@ -315,8 +315,7 @@ LogicalResult ForOp::verify() { i++; } - - return RegionBranchOpInterface::verifyTypes(*this); + return success(); } Optional ForOp::getSingleInductionVar() { return getInductionVar(); } @@ -1075,8 +1074,7 @@ void IfOp::build(OpBuilder &builder, OperationState &result, Value cond, LogicalResult IfOp::verify() { if (getNumResults() != 0 && getElseRegion().empty()) return emitOpError("must have an else block if defining values"); - - return RegionBranchOpInterface::verifyTypes(*this); + return success(); } ParseResult IfOp::parse(OpAsmParser &parser, OperationState &result) { @@ -2335,9 +2333,6 @@ static TerminatorTy verifyAndGetTerminator(scf::WhileOp op, Region ®ion, } LogicalResult scf::WhileOp::verify() { - if (failed(RegionBranchOpInterface::verifyTypes(*this))) - return failure(); - auto beforeTerminator = verifyAndGetTerminator( *this, getBefore(), "expects the 'before' region to terminate with 'scf.condition'"); diff --git a/mlir/lib/Dialect/Shape/IR/Shape.cpp b/mlir/lib/Dialect/Shape/IR/Shape.cpp index 0f63331..9396cab 100644 --- a/mlir/lib/Dialect/Shape/IR/Shape.cpp +++ b/mlir/lib/Dialect/Shape/IR/Shape.cpp @@ -415,10 +415,6 @@ void AssumingOp::build( result.addTypes(assumingTypes); } -LogicalResult AssumingOp::verify() { - return RegionBranchOpInterface::verifyTypes(*this); -} - //===----------------------------------------------------------------------===// // AddOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Interfaces/ControlFlowInterfaces.cpp b/mlir/lib/Interfaces/ControlFlowInterfaces.cpp index c8319bc..02845c0 100644 --- a/mlir/lib/Interfaces/ControlFlowInterfaces.cpp +++ b/mlir/lib/Interfaces/ControlFlowInterfaces.cpp @@ -62,7 +62,8 @@ detail::verifyBranchSuccessorOperands(Operation *op, unsigned succNo, // Check the types. auto operandIt = operands->begin(); for (unsigned i = 0; i != operandCount; ++i, ++operandIt) { - if ((*operandIt).getType() != destBB->getArgument(i).getType()) + if (!cast(op).areTypesCompatible( + (*operandIt).getType(), destBB->getArgument(i).getType())) return op->emitError() << "type mismatch for bb argument #" << i << " of successor #" << succNo; } @@ -132,7 +133,7 @@ verifyTypesAlongAllEdges(Operation *op, Optional sourceNo, llvm::enumerate(llvm::zip(*sourceTypes, succInputsTypes))) { Type sourceType = std::get<0>(typesIdx.value()); Type inputType = std::get<1>(typesIdx.value()); - if (sourceType != inputType) { + if (!regionInterface.areTypesCompatible(sourceType, inputType)) { InFlightDiagnostic diag = op->emitOpError(" along control flow edge "); return printEdgeName(diag) << ": source type #" << typesIdx.index() << " " << sourceType @@ -169,6 +170,18 @@ LogicalResult detail::verifyTypesAlongControlFlowEdges(Operation *op) { // attached regions. assert(op->getNumRegions() != 0); + auto areTypesCompatible = [&](TypeRange lhs, TypeRange rhs) { + if (lhs.size() != rhs.size()) + return false; + for (auto types : llvm::zip(lhs, rhs)) { + if (!regionInterface.areTypesCompatible(std::get<0>(types), + std::get<1>(types))) { + return false; + } + } + return true; + }; + // Verify types along control flow edges originating from each region. for (unsigned regionNo : llvm::seq(0U, op->getNumRegions())) { Region ®ion = op->getRegion(regionNo); @@ -192,7 +205,8 @@ LogicalResult detail::verifyTypesAlongControlFlowEdges(Operation *op) { // Found more than one ReturnLike terminator. Make sure the operand types // match with the first one. - if (regionReturnOperands->getTypes() != terminatorOperands->getTypes()) + if (!areTypesCompatible(regionReturnOperands->getTypes(), + terminatorOperands->getTypes())) return op->emitOpError("Region #") << regionNo << " operands mismatch between return-like terminators"; diff --git a/mlir/test/Transforms/control-flow-sink.mlir b/mlir/test/Transforms/control-flow-sink.mlir index 1327e01a..09ebd5b 100644 --- a/mlir/test/Transforms/control-flow-sink.mlir +++ b/mlir/test/Transforms/control-flow-sink.mlir @@ -3,32 +3,38 @@ // Test that operations can be sunk. // CHECK-LABEL: @test_simple_sink -// CHECK-SAME: (%[[ARG0:.*]]: i1, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) +// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) // CHECK-NEXT: %[[V0:.*]] = arith.subi %[[ARG2]], %[[ARG1]] -// CHECK-NEXT: %[[V1:.*]] = test.region_if %[[ARG0]]: i1 -> i32 then { +// CHECK-NEXT: %[[V1:.*]] = test.region_if %[[ARG0]]: i32 -> i32 then { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: %[[V2:.*]] = arith.subi %[[ARG1]], %[[ARG2]] // CHECK-NEXT: test.region_if_yield %[[V2]] // CHECK-NEXT: } else { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: %[[V2:.*]] = arith.addi %[[ARG1]], %[[ARG1]] // CHECK-NEXT: %[[V3:.*]] = arith.addi %[[V0]], %[[V2]] // CHECK-NEXT: test.region_if_yield %[[V3]] // CHECK-NEXT: } join { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: %[[V2:.*]] = arith.addi %[[ARG2]], %[[ARG2]] // CHECK-NEXT: %[[V3:.*]] = arith.addi %[[V2]], %[[V0]] // CHECK-NEXT: test.region_if_yield %[[V3]] // CHECK-NEXT: } // CHECK-NEXT: return %[[V1]] -func @test_simple_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { +func @test_simple_sink(%arg0: i32, %arg1: i32, %arg2: i32) -> i32 { %0 = arith.subi %arg1, %arg2 : i32 %1 = arith.subi %arg2, %arg1 : i32 %2 = arith.addi %arg1, %arg1 : i32 %3 = arith.addi %arg2, %arg2 : i32 - %4 = test.region_if %arg0: i1 -> i32 then { + %4 = test.region_if %arg0: i32 -> i32 then { + ^bb0(%arg3: i32): test.region_if_yield %0 : i32 } else { + ^bb0(%arg3: i32): %5 = arith.addi %1, %2 : i32 test.region_if_yield %5 : i32 } join { + ^bb0(%arg3: i32): %5 = arith.addi %3, %1 : i32 test.region_if_yield %5 : i32 } @@ -38,37 +44,49 @@ func @test_simple_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { // Test that a region op can be sunk. // CHECK-LABEL: @test_region_sink -// CHECK-SAME: (%[[ARG0:.*]]: i1, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) -// CHECK-NEXT: %[[V0:.*]] = test.region_if %[[ARG0]]: i1 -> i32 then { -// CHECK-NEXT: %[[V1:.*]] = test.region_if %[[ARG0]]: i1 -> i32 then { +// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) +// CHECK-NEXT: %[[V0:.*]] = test.region_if %[[ARG0]]: i32 -> i32 then { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): +// CHECK-NEXT: %[[V1:.*]] = test.region_if %[[ARG0]]: i32 -> i32 then { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } else { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: %[[V2:.*]] = arith.subi %[[ARG1]], %[[ARG2]] // CHECK-NEXT: test.region_if_yield %[[V2]] // CHECK-NEXT: } join { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG2]] // CHECK-NEXT: } // CHECK-NEXT: test.region_if_yield %[[V1]] // CHECK-NEXT: } else { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } join { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG2]] // CHECK-NEXT: } // CHECK-NEXT: return %[[V0]] -func @test_region_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { +func @test_region_sink(%arg0: i32, %arg1: i32, %arg2: i32) -> i32 { %0 = arith.subi %arg1, %arg2 : i32 - %1 = test.region_if %arg0: i1 -> i32 then { + %1 = test.region_if %arg0: i32 -> i32 then { + ^bb0(%arg3: i32): test.region_if_yield %arg1 : i32 } else { + ^bb0(%arg3: i32): test.region_if_yield %0 : i32 } join { + ^bb0(%arg3: i32): test.region_if_yield %arg2 : i32 } - %2 = test.region_if %arg0: i1 -> i32 then { + %2 = test.region_if %arg0: i32 -> i32 then { + ^bb0(%arg3: i32): test.region_if_yield %1 : i32 } else { + ^bb0(%arg3: i32): test.region_if_yield %arg1 : i32 } join { + ^bb0(%arg3: i32): test.region_if_yield %arg2 : i32 } return %2 : i32 @@ -77,8 +95,9 @@ func @test_region_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { // Test that an entire subgraph can be sunk. // CHECK-LABEL: @test_subgraph_sink -// CHECK-SAME: (%[[ARG0:.*]]: i1, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) -// CHECK-NEXT: %[[V0:.*]] = test.region_if %[[ARG0]]: i1 -> i32 then { +// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) +// CHECK-NEXT: %[[V0:.*]] = test.region_if %[[ARG0]]: i32 -> i32 then { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: %[[V1:.*]] = arith.subi %[[ARG1]], %[[ARG2]] // CHECK-NEXT: %[[V2:.*]] = arith.addi %[[ARG1]], %[[ARG2]] // CHECK-NEXT: %[[V3:.*]] = arith.subi %[[ARG2]], %[[ARG1]] @@ -87,23 +106,28 @@ func @test_region_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { // CHECK-NEXT: %[[V6:.*]] = arith.addi %[[V5]], %[[V4]] // CHECK-NEXT: test.region_if_yield %[[V6]] // CHECK-NEXT: } else { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } join { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG2]] // CHECK-NEXT: } // CHECK-NEXT: return %[[V0]] -func @test_subgraph_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { +func @test_subgraph_sink(%arg0: i32, %arg1: i32, %arg2: i32) -> i32 { %0 = arith.addi %arg1, %arg2 : i32 %1 = arith.subi %arg1, %arg2 : i32 %2 = arith.subi %arg2, %arg1 : i32 %3 = arith.muli %0, %1 : i32 %4 = arith.muli %2, %2 : i32 %5 = arith.addi %3, %4 : i32 - %6 = test.region_if %arg0: i1 -> i32 then { + %6 = test.region_if %arg0: i32 -> i32 then { + ^bb0(%arg3: i32): test.region_if_yield %5 : i32 } else { + ^bb0(%arg3: i32): test.region_if_yield %arg1 : i32 } join { + ^bb0(%arg3: i32): test.region_if_yield %arg2 : i32 } return %6 : i32 @@ -112,7 +136,7 @@ func @test_subgraph_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { // Test that ops can be sunk into regions with multiple blocks. // CHECK-LABEL: @test_multiblock_region_sink -// CHECK-SAME: (%[[ARG0:.*]]: i1, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) +// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32, %[[ARG2:.*]]: i32) // CHECK-NEXT: %[[V0:.*]] = arith.addi %[[ARG1]], %[[ARG2]] // CHECK-NEXT: %[[V1:.*]] = "test.any_cond"() ({ // CHECK-NEXT: %[[V3:.*]] = arith.addi %[[V0]], %[[ARG2]] @@ -124,7 +148,7 @@ func @test_subgraph_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { // CHECK-NEXT: }) // CHECK-NEXT: %[[V2:.*]] = arith.addi %[[V0]], %[[V1]] // CHECK-NEXT: return %[[V2]] -func @test_multiblock_region_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { +func @test_multiblock_region_sink(%arg0: i32, %arg1: i32, %arg2: i32) -> i32 { %0 = arith.addi %arg1, %arg2 : i32 %1 = arith.addi %0, %arg2 : i32 %2 = arith.addi %1, %arg1 : i32 @@ -141,37 +165,49 @@ func @test_multiblock_region_sink(%arg0: i1, %arg1: i32, %arg2: i32) -> i32 { // Test that ops can be sunk recursively into nested regions. // CHECK-LABEL: @test_nested_region_sink -// CHECK-SAME: (%[[ARG0:.*]]: i1, %[[ARG1:.*]]: i32) -> i32 { -// CHECK-NEXT: %[[V0:.*]] = test.region_if %[[ARG0]]: i1 -> i32 then { -// CHECK-NEXT: %[[V1:.*]] = test.region_if %[[ARG0]]: i1 -> i32 then { +// CHECK-SAME: (%[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32) -> i32 { +// CHECK-NEXT: %[[V0:.*]] = test.region_if %[[ARG0]]: i32 -> i32 then { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): +// CHECK-NEXT: %[[V1:.*]] = test.region_if %[[ARG0]]: i32 -> i32 then { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: %[[V2:.*]] = arith.addi %[[ARG1]], %[[ARG1]] // CHECK-NEXT: test.region_if_yield %[[V2]] // CHECK-NEXT: } else { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } join { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } // CHECK-NEXT: test.region_if_yield %[[V1]] // CHECK-NEXT: } else { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } join { +// CHECK-NEXT: ^bb0(%{{.*}}: i32): // CHECK-NEXT: test.region_if_yield %[[ARG1]] // CHECK-NEXT: } // CHECK-NEXT: return %[[V0]] -func @test_nested_region_sink(%arg0: i1, %arg1: i32) -> i32 { +func @test_nested_region_sink(%arg0: i32, %arg1: i32) -> i32 { %0 = arith.addi %arg1, %arg1 : i32 - %1 = test.region_if %arg0: i1 -> i32 then { - %2 = test.region_if %arg0: i1 -> i32 then { + %1 = test.region_if %arg0: i32 -> i32 then { + ^bb0(%arg3: i32): + %2 = test.region_if %arg0: i32 -> i32 then { + ^bb0(%arg4: i32): test.region_if_yield %0 : i32 } else { + ^bb0(%arg4: i32): test.region_if_yield %arg1 : i32 } join { + ^bb0(%arg4: i32): test.region_if_yield %arg1 : i32 } test.region_if_yield %2 : i32 } else { + ^bb0(%arg3: i32): test.region_if_yield %arg1 : i32 } join { + ^bb0(%arg3: i32): test.region_if_yield %arg1 : i32 } return %1 : i32 diff --git a/mlir/unittests/Interfaces/ControlFlowInterfacesTest.cpp b/mlir/unittests/Interfaces/ControlFlowInterfacesTest.cpp index b7f6d0a..1dfcc6d 100644 --- a/mlir/unittests/Interfaces/ControlFlowInterfacesTest.cpp +++ b/mlir/unittests/Interfaces/ControlFlowInterfacesTest.cpp @@ -54,8 +54,7 @@ struct SequentialRegionsOp void getSuccessorRegions(Optional index, ArrayRef operands, SmallVectorImpl ®ions) { - assert(index.hasValue() && "expected index"); - if (*index == 0) { + if (index == 0u) { Operation *thisOp = this->getOperation(); regions.push_back(RegionSuccessor(&thisOp->getRegion(1))); }