[llvm-branch-commits] [mlir] 43f34f5 - Added check if there are regions that do not implement the RegionBranchOpInterface.
Author: Julian Gross Date: 2021-01-20T12:15:28+01:00 New Revision: 43f34f58349ae178fd1c95d6a73c6858f35f2ea1 URL: https://github.com/llvm/llvm-project/commit/43f34f58349ae178fd1c95d6a73c6858f35f2ea1 DIFF: https://github.com/llvm/llvm-project/commit/43f34f58349ae178fd1c95d6a73c6858f35f2ea1.diff LOG: Added check if there are regions that do not implement the RegionBranchOpInterface. Add a check if regions do not implement the RegionBranchOpInterface. This is not allowed in the current deallocation steps. Furthermore, we handle edge-cases, where a single region is attached and the parent operation has no results. This fixes: https://bugs.llvm.org/show_bug.cgi?id=48575 Differential Revision: https://reviews.llvm.org/D94586 Added: Modified: mlir/lib/Transforms/BufferDeallocation.cpp mlir/test/Transforms/buffer-deallocation.mlir Removed: diff --git a/mlir/lib/Transforms/BufferDeallocation.cpp b/mlir/lib/Transforms/BufferDeallocation.cpp index bdd8515bdc05..a4155695fdf7 100644 --- a/mlir/lib/Transforms/BufferDeallocation.cpp +++ b/mlir/lib/Transforms/BufferDeallocation.cpp @@ -76,6 +76,31 @@ static void walkReturnOperations(Region *region, const FuncT &func) { } } +/// Checks if all operations in a given region have at least one attached region +/// that 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, in that case, 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 { //===--===// @@ -506,7 +531,12 @@ struct BufferDeallocationPass : BufferDeallocationBase { 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 index f61d501dff9c..90b39b202fb0 100644 --- 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 @@ func @loop_nested_alloc( // 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 @@ func @loop_dynalloc( 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 @@ func @do_loop_alloc( return } -// expected-error@+1 {{Structured control-flow loops are supported only}} - // - // CHECK-LABEL: func @assumingOp( @@ -1193,3 +1189,20 @@ func @assumingOp( // CHECK-NEXT:shape.assuming_yield %[[RETURNING_ALLOC]] // CHECK: test.copy(%[[ASSUMING_RESULT:.*]], %[[ARG2]]) // CHECK-NEXT: dealloc %[[ASSUMING_RESULT]] + +// - + +/
[llvm-branch-commits] [mlir] 8aeca73 - [MLIR] Added support for dynamic shaped allocas to promote-buffers-to-stack pass.
Author: Julian Gross Date: 2020-12-03T11:47:49+01:00 New Revision: 8aeca73702d84590e32e404a2d3038399cf71418 URL: https://github.com/llvm/llvm-project/commit/8aeca73702d84590e32e404a2d3038399cf71418 DIFF: https://github.com/llvm/llvm-project/commit/8aeca73702d84590e32e404a2d3038399cf71418.diff LOG: [MLIR] Added support for dynamic shaped allocas to promote-buffers-to-stack pass. Extended promote buffers to stack pass to support dynamically shaped allocas. The conversion is limited by the rank of the underlying tensor. An option is added to the pass to adjust the given rank. Differential Revision: https://reviews.llvm.org/D91969 Added: Modified: mlir/include/mlir/Transforms/Passes.h mlir/include/mlir/Transforms/Passes.td mlir/lib/Transforms/BufferOptimizations.cpp mlir/test/Transforms/promote-buffers-to-stack.mlir Removed: diff --git a/mlir/include/mlir/Transforms/Passes.h b/mlir/include/mlir/Transforms/Passes.h index 77d98ce79cca..c092d0120b60 100644 --- a/mlir/include/mlir/Transforms/Passes.h +++ b/mlir/include/mlir/Transforms/Passes.h @@ -42,9 +42,11 @@ std::unique_ptr createBufferLoopHoistingPass(); /// Creates a pass that promotes heap-based allocations to stack-based ones. /// Only buffers smaller than the provided size are promoted. +/// Dynamic shaped buffers are promoted up to the given rank. std::unique_ptr createPromoteBuffersToStackPass(unsigned maxAllocSizeInBytes = 1024, -unsigned bitwidthOfIndexType = 64); +unsigned bitwidthOfIndexType = 64, +unsigned maxRankOfAllocatedMemRef = 1); /// Creates a pass that finalizes a partial bufferization by removing remaining /// tensor_load and tensor_to_memref operations. diff --git a/mlir/include/mlir/Transforms/Passes.td b/mlir/include/mlir/Transforms/Passes.td index 29fe43fc0169..afad7cd5852f 100644 --- a/mlir/include/mlir/Transforms/Passes.td +++ b/mlir/include/mlir/Transforms/Passes.td @@ -207,17 +207,21 @@ def PromoteBuffersToStack : FunctionPass<"promote-buffers-to-stack"> { let description = [{ This pass implements a simple algorithm to convert heap-based memory allocations to stack-based ones. It uses a built-in heuristic to decide -whether it makes sense to convert an allocation. +whether it makes sense to convert an allocation. Furthermore, dynamic +shaped buffers that are limited by the rank of the tensor can be +converted. They are only transformed if they are considered to be small. }]; let constructor = "mlir::createPromoteBuffersToStackPass()"; let options = [ Option<"maxAllocSizeInBytes", "max-alloc-size-in-bytes", "unsigned", /*default=*/"1024", - "Define the maximum size in bytes to promote allocations to stack.">, + "Maximal size in bytes to promote allocations to stack.">, Option<"bitwidthOfIndexType", "bitwidth-of-index-type", "unsigned", /*default=*/"64", - "Define the bitwidth of the index type. Used for size estimation.">, - + "Bitwidth of the index type. Used for size estimation.">, +Option<"maxRankOfAllocatedMemRef", "max-rank-of-allocated-memref", "unsigned", + /*default=*/"1", + "Maximal memref rank to promote dynamic buffers.">, ]; } diff --git a/mlir/lib/Transforms/BufferOptimizations.cpp b/mlir/lib/Transforms/BufferOptimizations.cpp index 0693c39d3f6b..651948a9ae22 100644 --- a/mlir/lib/Transforms/BufferOptimizations.cpp +++ b/mlir/lib/Transforms/BufferOptimizations.cpp @@ -30,10 +30,23 @@ static bool isKnownControlFlowInterface(Operation *op) { /// transformation is only applied to small buffers since large buffers could /// exceed the stack space. static bool isSmallAlloc(Value alloc, unsigned maximumSizeInBytes, - unsigned bitwidthOfIndexType) { + unsigned bitwidthOfIndexType, + unsigned maxRankOfAllocatedMemRef) { auto type = alloc.getType().dyn_cast(); - if (!type || !type.hasStaticShape()) + if (!type || !alloc.getDefiningOp()) return false; + if (!type.hasStaticShape()) { +// Check if the dynamic shape dimension of the alloc is produced by RankOp. +// If this is the case, it is likely to be small. Furthermore, the dimension +// is limited to the maximum rank of the allocated memref to avoid large +// values by multiplying several small values. +if (type.getRank() <= maxRankOfAllocatedMemRef) { + return llvm::all_of( + alloc.getDefiningOp()->getOperands(), + [&](Value operand) { return operand.getDefiningOp(); }); +} +return false; + } // For index types, use the provided size, as the type does not know. unsigned int bitwidth = type.getElementType().isIndex() ? bitwidthOfI