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,32 @@ } } +/// 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 checkForControlFlow(Region ®ion) { + for (Block &block : region) + for (Operation &operation : block) { + 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().size() > 0) || size > 1) && + !dyn_cast(&operation)) + return false; + for (Region ®ion : regions) + // Walk recursively over all sub regions. + if (!checkForControlFlow(region)) + return false; + } + return true; +} + namespace { //===----------------------------------------------------------------------===// @@ -510,7 +536,14 @@ if (backedges.size()) { getFunction().emitError( "Structured control-flow loops are supported only."); - return; + return signalPassFailure(); + } + + // Check that the control flow structures are supported. + if (!checkForControlFlow(getFunction().getRegion())) { + getFunction().emitError("All operations with attached regions need to " + "implement the RegionBranchOpInterface."); + 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,16 @@ // 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. + +// expected-error@+1 {{All operations with attached regions need to implement the RegionBranchOpInterface.}} +func @noRegionBranchOpInterface() { + %0 = "test.bar"() ( { + "test.yield"() : () -> () + }) : () -> (i32) + "test.terminator"() : () -> () +} \ No newline at end of file