Hello all!
It seems there are two bufferization-related transformations in MLIR whose correctnesses are unclear or wrong.
If indeed wrong, I think there are low-cost solutions hopefully; I wrote my thoughts as well.
-
-linalg-bufferize
introduceslinalg.fill
to a read-only block:
func @bufferize_fill(%arg0: tensor<?xf32>) -> tensor<?xf32> {
%c0 = arith.constant 0.0 : f32
%0 = linalg.fill(%c0, %arg0) : f32, tensor<?xf32> -> tensor<?xf32>
return %0 : tensor<?xf32>
}
=>
func @bufferize_fill(%arg0: tensor<?xf32>) -> tensor<?xf32> {
%0 = bufferization.to_memref %arg0 : memref<?xf32>
// ^^^ According to BufferizationOps document, mutating %0 is UB.
%cst = arith.constant 0.000000e+00 : f32
linalg.fill(%cst, %0) : f32, memref<?xf32>
// ^^^ This is mutating %0 (hence UB)?
%1 = bufferization.to_tensor %0 : memref<?xf32>
return %1 : tensor<?xf32>
}
Perhaps a straightforward solution would be to add writable
attribute to bufferization.to_memref
.
If it is set to true, the returned buffer is writable. This will make -linalg-bufferize
correct.
-
-buffer-deallocation
introduces dellocating read-only blocks, which affects the lifetime of the original block if thebufferization.clone
is returning an aliased pointer:
func @dealloc_existing_clones(%arg0: memref<?x?xf64>, %arg1: memref<?x?xf64>) -> memref<?x?xf64> {
%0 = bufferization.clone %arg0 : memref<?x?xf64> to memref<?x?xf64>
%1 = bufferization.clone %arg1 : memref<?x?xf64> to memref<?x?xf64>
return %0 : memref<?x?xf64>
}
=>
func @dealloc_existing_clones(%arg0: memref<?x?xf64>, %arg1: memref<?x?xf64>) -> memref<?x?xf64> {
%0 = bufferization.clone %arg0 : memref<?x?xf64> to memref<?x?xf64>
%1 = bufferization.clone %arg1 : memref<?x?xf64> to memref<?x?xf64>
memref.dealloc %1 : memref<?x?xf64> // <- THIS ONE
return %0 : memref<?x?xf64>
}
To resolve this, we can add another operation bufferization.dealloc %p
that
(1) is no-op if the %p is an aliased pointer
(2) is equivalent to memref.dealloc %p
otherwise.
Thanks!