diff --git a/mlir/lib/Transforms/BufferDeallocation.cpp b/mlir/lib/Transforms/BufferDeallocation.cpp --- a/mlir/lib/Transforms/BufferDeallocation.cpp +++ b/mlir/lib/Transforms/BufferDeallocation.cpp @@ -75,6 +75,31 @@ } } +/// Checks if any operation in a given region has an attached region and +/// implements the RegionBranchOpInterface. This is not required in edge cases, +/// where we have a single attached region and the parent operation has no +/// results. +static bool validateSupportedControlFlow(Region ®ion) { + bool success = true; + region.walk([&success](Operation *operation) { + auto regions = operation->getRegions(); + // Walk over all operations in a region and check if the operation has at + // least one region and implements the RegionBranchOpInterface. If there + // is an operation that does not fulfill this condition, we cannot apply + // the deallocation steps. Furthermore, we accept cases, where we have a + // region that returns no results, since the intra-region control flow + // does not affect the transformation. + size_t size = regions.size(); + if (((size == 1 && !operation->getResults().empty()) || size > 1) && + !dyn_cast(operation)) { + operation->emitError("All operations with attached regions need to " + "implement the RegionBranchOpInterface."); + success = false; + } + }); + return success; +} + namespace { //===----------------------------------------------------------------------===// @@ -510,7 +535,12 @@ if (backedges.size()) { getFunction().emitError( "Structured control-flow loops are supported only."); - return; + return signalPassFailure(); + } + + // Check that the control flow structures are supported. + if (!validateSupportedControlFlow(getFunction().getRegion())) { + return signalPassFailure(); } // Place all required temporary alloc, copy and dealloc nodes. diff --git a/mlir/test/Transforms/buffer-deallocation.mlir b/mlir/test/Transforms/buffer-deallocation.mlir --- a/mlir/test/Transforms/buffer-deallocation.mlir +++ b/mlir/test/Transforms/buffer-deallocation.mlir @@ -1,4 +1,4 @@ -// RUN: mlir-opt -buffer-deallocation -split-input-file %s | FileCheck %s +// RUN: mlir-opt -verify-diagnostics -buffer-deallocation -split-input-file %s | FileCheck %s // This file checks the behaviour of BufferDeallocation pass for moving and // inserting missing DeallocOps in their correct positions. Furthermore, @@ -1094,7 +1094,7 @@ // The BufferDeallocation transformation should fail on this explicit // control-flow loop since they are not supported. -// CHECK-LABEL: func @loop_dynalloc +// expected-error@+1 {{Structured control-flow loops are supported only}} func @loop_dynalloc( %arg0 : i32, %arg1 : i32, @@ -1121,15 +1121,13 @@ return } -// expected-error@+1 {{Structured control-flow loops are supported only}} - // ----- // Test Case: explicit control-flow loop with a dynamically allocated buffer. // The BufferDeallocation transformation should fail on this explicit // control-flow loop since they are not supported. -// CHECK-LABEL: func @do_loop_alloc +// expected-error@+1 {{Structured control-flow loops are supported only}} func @do_loop_alloc( %arg0 : i32, %arg1 : i32, @@ -1155,8 +1153,6 @@ return } -// expected-error@+1 {{Structured control-flow loops are supported only}} - // ----- // CHECK-LABEL: func @assumingOp( @@ -1193,3 +1189,20 @@ // CHECK-NEXT: shape.assuming_yield %[[RETURNING_ALLOC]] // CHECK: test.copy(%[[ASSUMING_RESULT:.*]], %[[ARG2]]) // CHECK-NEXT: dealloc %[[ASSUMING_RESULT]] + +// ----- + +// Test Case: The op "test.bar" does not implement the RegionBranchOpInterface. +// This is not allowed in buffer deallocation. + +func @noRegionBranchOpInterface() { +// expected-error@+1 {{All operations with attached regions need to implement the RegionBranchOpInterface.}} + %0 = "test.bar"() ( { +// expected-error@+1 {{All operations with attached regions need to implement the RegionBranchOpInterface.}} + %1 = "test.bar"() ( { + "test.yield"() : () -> () + }) : () -> (i32) + "test.yield"() : () -> () + }) : () -> (i32) + "test.terminator"() : () -> () +}