Commit Graph

184 Commits

Author SHA1 Message Date
Matthias Springer
dbfc38ed6b [mlir][bufferization] Add BufferOriginAnalysis (#86461)
This commit adds the `BufferOriginAnalysis`, which can be queried to
check if two buffer SSA values originate from the same allocation. This
new analysis is used in the buffer deallocation pass to fold away or
simplify `bufferization.dealloc` ops more aggressively.

The `BufferOriginAnalysis` is based on the `BufferViewFlowAnalysis`,
which collects buffer SSA value "same buffer" dependencies. E.g., given
IR such as:
```
%0 = memref.alloc()
%1 = memref.subview %0
%2 = memref.subview %1
```
The `BufferViewFlowAnalysis` will report the following "reverse"
dependencies (`resolveReverse`) for `%2`: {`%2`, `%1`, `%0`}. I.e., all
buffer SSA values in the reverse use-def chain that originate from the
same allocation as `%2`. The `BufferOriginAnalysis` is built on top of
that. It handles only simple cases at the moment and may conservatively
return "unknown" around certain IR with branches, memref globals and
function arguments.

This analysis enables additional simplifications during
`-buffer-deallocation-simplification`. In particular, "regular" scf.for
loop nests, that yield buffers (or reallocations thereof) in the same
order as they appear in the iter_args, are now handled much more
efficiently. Such IR patterns are generated by the sparse compiler.
2024-03-25 18:57:53 +09:00
Matthias Springer
35d3b3430e [mlir][bufferization] Add "bottom-up from terminators" analysis heuristic (#83964)
One-Shot Bufferize currently does not support loops where a yielded
value bufferizes to a buffer that is different from the buffer of the
region iter_arg. In such a case, the bufferization fails with an error
such as:
```
Yield operand #0 is not equivalent to the corresponding iter bbArg
    scf.yield %0 : tensor<5xf32>
```

One common reason for non-equivalent buffers is that an op on the path
from the region iter_arg to the terminator bufferizes out-of-place. Ops
that are analyzed earlier are more likely to bufferize in-place.

This commit adds a new heuristic that gives preference to ops that are
reachable on the reverse SSA use-def chain from a region terminator and
are within the parent region of the terminator. This is expected to work
better than the existing heuristics for loops where an iter_arg is
written to multiple times within a loop, but only one write is fed into
the terminator.

Current users of One-Shot Bufferize are not affected by this change.
"Bottom-up" is still the default heuristic. Users can switch to the new
heuristic manually.

This commit also turns the "fuzzer" pass option into a heuristic,
cleaning up the code a bit.
2024-03-21 14:16:02 +09:00
Aart Bik
f3a8af07fa [mlir][sparse] best effort finalization of escaping empty sparse tensors (#85482)
This change lifts the restriction that purely allocated empty sparse
tensors cannot escape the method. Instead it makes a best effort to add
a finalizing operation before the escape.

This assumes that
(1) we never build sparse tensors across method boundaries
    (e.g. allocate in one, insert in other method)
(2) if we have other uses of the empty allocation in the
    same method, we assume that either that op will fail
    or will do the finalization for us.

This is best-effort, but fixes some very obvious missing cases.
2024-03-15 16:43:09 -07:00
Matthias Springer
0940be1581 [mlir][bufferization] Never pass ownership to functions (#80655)
Even when `private-function-dynamic-ownership` is set, ownership should
never be passed to the callee. This can lead to double deallocs (#77096)
or use-after-free in the caller because ownership is currently passed
regardless of whether there are any further uses of the buffer in the
caller or not.

Note: This is consistent with the fact that ownership is never passed to
nested regions.

This commit fixes #77096.
2024-02-05 12:11:49 +01:00
lorenzo chelini
84c8d0377d [MLIR][Vector] Implement memory effect for print (#80400)
Add write memory effect for the print operation. The exact memory
behavior is implemented in other print-like operations such as
`transform::PrintOp` or `gpu::printf`.

Providing memory behavior allows using the operation in passes like
buffer deallocation instead of emitting an error.
2024-02-02 11:05:35 +01:00
Kohei Yamaguchi
bcd14b099d [mlir][bufferization] Fix SimplifyClones with dealloc before cloneOp (#79098)
The SimplifyClones pass relies on the assumption that the deallocOp
follows the cloneOp. However, a crash occurs when there is a
redundantDealloc preceding the cloneOp. This PR addresses the issue by
ensuring the presence of deallocOp after cloneOp. The verification is
performed by checking if the loop of the sub sequent node of cloneOp
reaches the tail of the list.

Fix #74306
2024-01-25 11:06:46 +01:00
Matthias Springer
fbb62d449c [mlir][bufferization] Buffer deallocation: Make op preconditions stricter (#75127)
The buffer deallocation pass checks the IR ("operation preconditions")
to make sure that there is no IR that is unsupported. In such a case,
the pass signals a failure.

The pass now rejects all ops with unknown memory effects. We do not know
whether such an op allocates memory or not. Therefore, the buffer
deallocation pass does not know whether a deallocation op should be
inserted or not.

Memory effects are queried from the `MemoryEffectOpInterface` interface.
Ops that do not implement this interface but have the
`RecursiveMemoryEffects` trait do not have any side effects (apart from
the ones that their nested ops may have).

Unregistered ops are now rejected by the pass because they do not
implement the `MemoryEffectOpInterface` and neither do we know if they
have `RecursiveMemoryEffects` or not. All test cases that currently have
unregistered ops are updated to use registered ops.
2024-01-21 11:10:09 +01:00
Matthias Springer
b4f24be7ef [mlir][bufferization] Simplify helper potentiallyAliasesMemref (#78690)
This commit simplifies a helper function in the ownership-based buffer
deallocation pass. Fixes a potential double-free (depending on the
scheduling of patterns).
2024-01-19 13:22:02 +01:00
Sergio Afonso
8fb685fb7e [MLIR][LLVM] Add explicit target_cpu attribute to llvm.func (#78287)
This patch adds the target_cpu attribute to llvm.func MLIR operations
and updates the translation to/from LLVM IR to match "target-cpu"
function attributes.
2024-01-17 14:55:02 +00:00
donald chen
eaa4b6cf29 [mlir][bufferization] Clone simplify fails when input and result type not cast compatiable (#71310)
The simplify of bufferization.clone generates a memref.cast op, but the
checks in simplify do not verify whether the operand types and return
types of clone op is compatiable, leading to errors. This patch
addresses this issue.
2024-01-12 16:11:00 +01:00
Matthias Springer
a43641c9db [mlir][bufferization] Fix regionOperatesOnMemrefValues (#75016)
`Region::walk([](Block *b) {...})` does not enumerate blocks that are
direct children of the region. These blocks must be checked manually.
2023-12-12 08:56:23 +09:00
Nicolas Vasilache
3a223f4414 [mlir][Bufferization] Add support for controlled bufferization of alloc_tensor (#70957)
This revision adds support to
`transform.structured.bufferize_to_allocation` to bufferize
`bufferization.alloc_tensor()` ops.
    
This is useful as a means path to control the bufferization of
`tensor.empty` ops that have bene previously
`bufferization.empty_tensor_to_alloc_tensor`'ed.
2023-11-02 11:34:10 +01:00
Mehdi Amini
5a71f7a4b7 [mlir] Fix bufferization.alloc_tensor canonicalization crash (#70891)
This make sure that an invalid negative dimension is ignored and stays
dynamic instead of crashing the compiler.

Fixes #70887
2023-10-31 20:58:29 -07:00
Matthias Springer
4fbbb7ad7c [mlir][bufferization] Fix ownership computation of unknown ops (#70773)
No ownership is assumed for memref results of ops that implement none of
the relevant interfaces and have no memref operands. This fixes #68948.
2023-11-01 09:26:47 +09:00
Oleksandr "Alex" Zinenko
e4384149b5 [mlir] use transform-interpreter in test passes (#70040)
Update most test passes to use the transform-interpreter pass instead of
the test-transform-dialect-interpreter-pass. The new "main" interpreter
pass has a named entry point instead of looking up the top-level op with
`PossibleTopLevelOpTrait`, which is arguably a more understandable
interface. The change is mechanical, rewriting an unnamed sequence into
a named one and wrapping the transform IR in to a module when necessary.

Add an option to the transform-interpreter pass to target a tagged
payload op instead of the root anchor op, which is also useful for repro
generation.

Only the test in the transform dialect proper and the examples have not
been updated yet. These will be updated separately after a more careful
consideration of testing coverage of the transform interpreter logic.
2023-10-24 16:12:34 +02:00
Matthias Springer
4d80eff861 [mlir][bufferization] Ownership-based deallocation: Allow manual (de)allocs (#68648)
Add a new attribute `bufferization.manual_deallocation` that can be
attached to allocation and deallocation ops. Buffers that are allocated
with this attribute are assigned an ownership of "false". Such buffers
can be deallocated manually (e.g., with `memref.dealloc`) if the
deallocation op also has the attribute set. Previously, the
ownership-based buffer deallocation pass used to reject IR with existing
deallocation ops. This is no longer the case if such ops have this new
attribute.

This change is useful for the sparse compiler, which currently
deallocates the sparse tensor buffers by itself.
2023-10-23 09:45:33 +09:00
Matthias Springer
6d88ac11d7 [mlir][bufferization] Transfer restrict during empty tensor elimination (#68729)
Empty tensor elimination is looking for
`bufferization.materialize_in_destination` ops with a `tensor.empty`
source. It replaces the `tensor.empty` with a `bufferization.to_tensor
restrict` of the memref destination. As part of this rewrite, the
`restrict` keyword should be removed, so that no second `to_tensor
restrict` op will be inserted. Such IR would be invalid.
`bufferization.materialize_in_destination` with memref destination and
without the `restrict` attribute are ignored by empty tensor
elimination.

Also relax the verifier of `materialize_in_destination`. The `restrict`
keyword is not generally needed because the op does not expose the
buffer as a tensor.
2023-10-11 08:51:16 -07:00
Matthias Springer
3d0ca2cfe3 [mlir][bufferization] Allow cyclic function graphs without tensors (#68632)
Cyclic function call graphs are generally not supported by One-Shot
Bufferize. However, they can be allowed when a function does not have
tensor arguments or results. This is because it is then no longer
necessary that the callee will be bufferized before the caller.
2023-10-09 17:52:04 -07:00
Matthias Springer
8ee38f3b32 [mlir][bufferization] Follow up for #68074 (#68488)
Address additional comments in #68074. This should have been part of
#68074.
2023-10-07 10:07:17 -07:00
Matthias Springer
0fcaca2fea [mlir][bufferization] MaterializeInDestinationOp: Support memref destinations (#68074)
Extend `bufferization.materialize_in_destination` to support memref
destinations. This op can now be used to indicate that a tensor
computation should materialize in a given buffer (that may have been
allocated by another component/runtime). The op still participates in
"empty tensor elimination".

Example:
```mlir
func.func @test(%out: memref<10xf32>) {
  %t = tensor.empty() : tensor<10xf32>
  %c = linalg.generic ... outs(%t: tensor<10xf32>) -> tensor<10xf32>
  bufferization.materialize_in_destination %c in restrict writable %out : (tensor<10xf32>, memref<10xf32>) -> ()
  return
}
```
After "empty tensor elimination", the above IR can bufferize without an
allocation:
```mlir
func.func @test(%out: memref<10xf32>) {
  linalg.generic ... outs(%out: memref<10xf32>)
  return
}
```

This change also clarifies the meaning of the `restrict` unit attribute
on `bufferization.to_tensor` ops.
2023-10-06 11:57:10 +02:00
long.chen
5979e1dfb1 [mlir] Fix empty-tensor-elimination around self-copies (#68129)
* Fixes #67977, a crash in `empty-tensor-elimination`.
* Also improves `linalg.copy` canonicalization.
* Also improves indentation indentation in `mlir-linalg-ods-yaml-gen.cpp`.
2023-10-05 12:04:20 +02:00
Matthias Springer
43198b0aa2 [mlir][bufferization] Better analysis around allocs and block arguments (#67923)
Values that are the result of buffer allocation ops are guaranteed to
*not* be the same allocation as block arguments of containing blocks.
This fact can be used to allow for more aggressive simplification of
`bufferization.dealloc` ops.
2023-10-02 11:01:12 +02:00
Matthias Springer
0ef990d57c [mlir][bufferization] Improve verifier for bufferization.dealloc (#67912)
Check that the number of retained operands and updated conditions match.
2023-10-01 19:36:43 +02:00
Martin Erhart
6a651c7f44 Revert "[mlir][bufferization] Don't clone on unknown ownership and verify function boundary ABI (#66626)"
This reverts commit aa9eb47da2.
It introduced a double free in a test case. Reverting to have some time
for fixing this and relanding later.
2023-09-28 09:14:46 +00:00
Martin Erhart
aa9eb47da2 [mlir][bufferization] Don't clone on unknown ownership and verify function boundary ABI (#66626)
Inserting clones requires a lot of assumptions to hold on the input IR, e.g., all writes to a buffer need to dominate all reads. This is not guaranteed by one-shot bufferization and isn't easy to verify, thus it could quickly lead to incorrect results that are hard to debug. This commit changes the mechanism of how an ownership indicator is materialized when there is not already a unique ownership present. Additionally, we don't create copies of returned memrefs anymore when we don't have ownership. Instead, we insert assert operations to make sure we have ownership at runtime, or otherwise report to the user that correctness could not be guaranteed.
2023-09-28 10:45:35 +02:00
Matthias Springer
5109cb28fd [mlir][bufferization] Make buffer deallocation pipeline op type independent (#67546)
The buffer deallocation pipeline now works on modules and functions.
Also add extra test cases that run the buffer deallocation pipeline on
modules and functions. (Test cases that insert a helper function.)
2023-09-27 15:06:25 +02:00
Matthias Springer
913286baed [mlir][linalg] Add SubsetInsertionOpInterface to linalg.copy (#67524)
This commit enables empty tensor elimination on `linalg.copy` ops.
2023-09-27 10:04:37 +02:00
Martin Erhart
77813b088e [mlir][bufferization] OwnershipBasedBufferDeallocation fixes (#67418)
* Properly handle the case where an op is deleted and thus no other
interfaces should be processed anymore.
* Don't add ownership indicator arguments and results to function
declarations
2023-09-27 09:16:43 +02:00
Martin Erhart
5c6eefb252 [mlir][bufferization] LowerDeallocation: declare helper function private (#67408) 2023-09-26 12:35:42 +02:00
Martin Erhart
6bf043e743 [mlir][bufferization] Remove allow-return-allocs and create-deallocs pass options, remove bufferization.escape attribute (#66619)
This commit removes the deallocation capabilities of
one-shot-bufferization. One-shot-bufferization should never deallocate
any memrefs as this should be entirely handled by the
ownership-based-buffer-deallocation pass going forward. This means the
`allow-return-allocs` pass option will default to true now,
`create-deallocs` defaults to false and they, as well as the escape
attribute indicating whether a memref escapes the current region, will
be removed. A new `allow-return-allocs-from-loops` option is added as a
temporary workaround for some bufferization limitations.
2023-09-18 16:44:48 +02:00
Matthias Springer
64839fbd45 [mlir][bufferization] Empty tensor elimination for materialize_in_destination (#65468)
This revision adds support for empty tensor elimination to
"bufferization.materialize_in_destination" by implementing the
`SubsetInsertionOpInterface`.

Furthermore, the One-Shot Bufferize conflict detection is improved for
"bufferization.materialize_in_destination".
2023-09-18 15:34:28 +02:00
Martin Erhart
1a4dd8d362 [mlir][bufferization] Switch tests to new deallocation pass pipeline (#66517)
Use the new ownership based deallocation pass pipeline in the regression
and integration tests. Some one-shot bufferization tests tested one-shot
bufferize and deallocation at the same time. I removed the deallocation
pass there because the deallocation pass is already thoroughly tested by
itself.

Fixed version of #66471
2023-09-18 12:00:27 +02:00
Martin Erhart
3d51010a33 Revert "[mlir][bufferization] Switch tests to new deallocation pass pipeline (#66471)"
This reverts commit ea42b49f10.

Some GPU integration tests are failing that I didn't observe locally.
Reverting until I have a fix.
2023-09-15 09:19:54 +00:00
Martin Erhart
ea42b49f10 [mlir][bufferization] Switch tests to new deallocation pass pipeline (#66471)
Use the new ownership based deallocation pass pipeline in the regression
and integration tests. Some one-shot bufferization tests tested one-shot
bufferize and deallocation at the same time. I removed the deallocation
pass there because the deallocation pass is already thoroughly tested by
itself.
2023-09-15 11:08:53 +02:00
Martin Erhart
08b7a71bcc [mlir][bufferization] Define a pipeline for buffer deallocation (#66352)
Since ownership based buffer deallocation requires a few passes to be run in a somewhat fixed sequence, it makes sense to have a pipeline for convenience (and to reduce the number of transform ops to represent default deallocation).
2023-09-15 09:39:17 +02:00
Yinying Li
2a07f0fd40 [mlir][sparse] Migrate more tests to use new syntax (#66443)
**Dense**
`lvlTypes = [ "dense", "dense" ]` to `map = (d0, d1) -> (d0 : dense, d1
: dense)`
`lvlTypes = [ "dense", "dense" ], dimToLvl = affine_map<(i,j) -> (j,i)>`
to `map = (d0, d1) -> (d1 : dense, d0 : dense)`

**DCSR**
`lvlTypes = [ "compressed", "compressed" ]` to `map = (d0, d1) -> (d0 :
compressed, d1 : compressed)`

**DCSC**
`lvlTypes = [ "compressed", "compressed" ], dimToLvl = affine_map<(i,j)
-> (j,i)>` to `map = (d0, d1) -> (d1 : compressed, d0 : compressed)`

**Block Row**
`lvlTypes = [ "compressed", "dense" ]` to `map = (d0, d1) -> (d0 :
compressed, d1 : dense)`

**Block Column**
`lvlTypes = [ "compressed", "dense" ], dimToLvl = affine_map<(i,j) ->
(j,i)>` to `map = (d0, d1) -> (d1 : compressed, d0 : dense)`

This is an ongoing effort: #66146, #66309
2023-09-14 23:19:57 +00:00
Yinying Li
e2e429d994 [mlir][sparse] Migrate more tests to new syntax (#66309)
CSR:
`lvlTypes = [ "dense", "compressed" ]` to `map = (d0, d1) -> (d0 :
dense, d1 : compressed)`

CSC:
`lvlTypes = [ "dense", "compressed" ], dimToLvl = affine_map<(d0, d1) ->
(d1, d0)>` to `map = (d0, d1) -> (d1 : dense, d0 : compressed)`

This is an ongoing effort: #66146
2023-09-14 12:21:13 -04:00
Martin Erhart
942ce31985 [mlir][bufferization] BufferDeallocationOpInterface: support custom ownership update logic (#66350)
Add a method to the BufferDeallocationOpInterface that allows operations to implement the interface and provide custom logic to compute the ownership indicators of values it defines. As a demonstrating example, this new method is implemented by the `arith.select` operation.
2023-09-14 14:34:04 +02:00
Martin Erhart
8160bce969 [mlir][bufferization][NFC] Introduce BufferDeallocationOpInterface (#66349)
This new interface allows operations to implement custom handling of ownership values and insertion of dealloc operations which is useful when an op cannot implement the interfaces supported by default by the buffer deallocation pass (e.g., because they are not exactly compatible or because there are some additional semantics to it that would render the default implementations in buffer deallocation invalid, or because no interfaces exist for this
kind of behavior and it's not worth introducing one plus a default implementation in buffer deallocation). Additionally, it can also be used to provide more efficient handling for a specific op than the interface based default
implementations can.
2023-09-14 13:58:30 +02:00
Martin Erhart
01334d1abb [mlir][bufferization] Add an ownership based buffer deallocation pass (#66337)
Add a new Buffer Deallocation pass with the intend to replace the old
one. For now it is added as a separate pass alongside in order to allow
downstream users to migrate over gradually. This new pass has the goal
of inserting fewer clone operations and supporting additional use-cases.
Please refer to the Buffer Deallocation section in the updated
Bufferization.md file for more information on how this new pass works.
2023-09-14 12:13:37 +02:00
Martin Erhart
c199f7dc62 Revert "[mlir][bufferization] Remove allow-return-allocs and create-deallocs pass options, remove bufferization.escape attribute"
This reverts commit 6a91dfedeb.

This caused problems in downstream projects. We are reverting to give
them more time for integration.
2023-09-13 13:53:48 +00:00
Martin Erhart
520407a7c8 Revert "[mlir][bufferization] Improve buffer deallocation pass"
This reverts commit 1bebb60a75.

This caused problems in downstream projects. We are reverting to give
them more time for integration.
2023-09-13 13:53:48 +00:00
Martin Erhart
792caac0f8 Revert "[mlir][bufferization][NFC] Introduce BufferDeallocationOpInterface"
This reverts commit 29d86175e6.

This caused problems in downstream projects. We are reverting to give
them more time for integration.
2023-09-13 13:53:47 +00:00
Martin Erhart
9782232ec7 Revert "[mlir][bufferization] BufferDeallocationOpInterface: support custom ownership update logic"
This reverts commit 89117f1807.

This caused problems in downstream projects. We are reverting to give
them more time for integration.
2023-09-13 13:53:47 +00:00
Martin Erhart
7995a4701d Revert "[mlir][bufferization] Define a pipeline for buffer deallocation"
This reverts commit f0c4663942.

This caused problems in downstream projects. We are reverting to give
them more time for integration.
2023-09-13 13:53:47 +00:00
Martin Erhart
f0c4663942 [mlir][bufferization] Define a pipeline for buffer deallocation
Since buffer deallocation requires a few passes to be run in a somewhat fixed
sequence, it makes sense to have a pipeline for convenience (and to reduce the
number of transform ops to represent default deallocation).

Reviewed By: springerm

Differential Revision: https://reviews.llvm.org/D159432
2023-09-13 09:30:24 +00:00
Martin Erhart
89117f1807 [mlir][bufferization] BufferDeallocationOpInterface: support custom ownership update logic
Add a method to the BufferDeallocationOpInterface that allows operations to
implement the interface and provide custom logic to compute the ownership
indicators of values it defines. As a demonstrating example, this new method is
implemented by the `arith.select` operation.

Reviewed By: springerm

Differential Revision: https://reviews.llvm.org/D158828
2023-09-13 09:30:23 +00:00
Martin Erhart
29d86175e6 [mlir][bufferization][NFC] Introduce BufferDeallocationOpInterface
This new interface allows operations to implement custom handling of ownership
values and insertion of dealloc operations which is useful when an op cannot
implement the interfaces supported by default by the buffer deallocation pass
(e.g., because they are not exactly compatible or because there are some
additional semantics to it that would render the default implementations in
buffer deallocation invalid, or because no interfaces exist for this kind of
behavior and it's not worth introducing one plus a default implementation in
buffer deallocation). Additionally, it can also be used to provide more
efficient handling for a specific op than the interface based default
implementations can.

Reviewed By: springerm

Differential Revision: https://reviews.llvm.org/D158756
2023-09-13 09:30:23 +00:00
Martin Erhart
1bebb60a75 [mlir][bufferization] Improve buffer deallocation pass
Add a new Buffer Deallocation pass replacing the old one with the goal of
inserting fewer clone operations and supporting additional use-cases.
Please refer to the Buffer Deallocation section in the updated
Bufferization.md file for more information on how this new pass works.

Reviewed By: springerm

Differential Revision: https://reviews.llvm.org/D158421
2023-09-13 09:30:23 +00:00
Martin Erhart
6a91dfedeb [mlir][bufferization] Remove allow-return-allocs and create-deallocs pass options, remove bufferization.escape attribute
This is the first commit in a series with the goal to rework the
BufferDeallocation pass. Currently, this pass heavily relies on copies
to perform correct deallocations, which leads to very slow code and
potentially high memory usage. Additionally, there are unsupported cases
such as returning memrefs which this series of commits aims to add
support for as well.

This first commit removes the deallocation capabilities of
one-shot-bufferization.One-shot-bufferization should never deallocate any
memrefs as this should be entirely handled by the buffer-deallocation pass
going forward. This means the allow-return-allocs pass option will
default to true now, create-deallocs defaults to false and they, as well
as the escape attribute indicating whether a memref escapes the current region,
will be removed.

The documentation should w.r.t. these pass option changes should also be
updated in this commit.

Reviewed By: springerm

Differential Revision: https://reviews.llvm.org/D156662
2023-09-13 09:30:22 +00:00