[all-commits] [llvm/llvm-project] ea4788: Fix for TOSA-to-linalg lowering of tosa.transpose ...

Rafael Ubal via All-commits all-commits at lists.llvm.org
Wed Nov 22 05:21:50 PST 2023


  Branch: refs/heads/main
  Home:   https://github.com/llvm/llvm-project
  Commit: ea47887e6f5fd2e394c7196203322be69ea142f9
      https://github.com/llvm/llvm-project/commit/ea47887e6f5fd2e394c7196203322be69ea142f9
  Author: Rafael Ubal <rubal at mathworks.com>
  Date:   2023-11-22 (Wed, 22 Nov 2023)

  Changed paths:
    M mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
    M mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir

  Log Message:
  -----------
  Fix for TOSA-to-linalg lowering of tosa.transpose op (#72698)

The TOSA-to-linalg conversion of `tosa.transpose` contains a bug in the
computation of the result tensor shape when using dynamic dimensions.
This bug may have widespread implications in projects such as
Tensorflow, where `tosa.transpose` is frequently generated.

Consider the following TOSA code using only static dimensions. The code
transposes a tensor of shape 10x11x12 into 12x10x11 by permuting
dimensions [2, 0, 1] into [0, 1, 2].
 
```
func.func @test_tosa_transpose(%input: tensor<10x11x12xf32>) -> tensor<12x10x11xf32> {
  %perms = "tosa.const"() <{value = dense<[2, 0, 1]> : tensor<3xi32>}> : () -> tensor<3xi32>
  %transposed = "tosa.transpose"(%input, %perms) : (tensor<10x11x12xf32>, tensor<3xi32>) -> tensor<12x10x11xf32>
  return %transposed : tensor<12x10x11xf32>
}
```
 
The code is correctly lowered to:
 
```
#map = affine_map<(d0, d1, d2) -> (d1, d2, d0)>
#map1 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
module {
  func.func @test_tosa_transpose(%arg0: tensor<10x11x12xf32>) -> tensor<12x10x11xf32> {
    %empty = tensor.empty() : tensor<12x10x11xf32>
    %transposed = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["parallel", "parallel", "parallel"]} ins(%arg0 : tensor<10x11x12xf32>) outs(%empty : tensor<12x10x11xf32>) {
    ^bb0(%in: f32, %out: f32):
      linalg.yield %in : f32
    } -> tensor<12x10x11xf32>
    return %transposed : tensor<12x10x11xf32>
  }
}
```
 
Now let's make all dimensions dynamic in the TOSA code:
 
```
func.func @test_tosa_transpose(%input: tensor<?x?x?xf32>) -> tensor<?x?x?xf32> {
  %perms = "tosa.const"() <{value = dense<[2, 0, 1]> : tensor<3xi32>}> : () -> tensor<3xi32>
  %transposed = "tosa.transpose"(%input, %perms) : (tensor<?x?x?xf32>, tensor<3xi32>) -> tensor<?x?x?xf32>
  return %transposed : tensor<?x?x?xf32>
}
```
 
The `tensor.empty()` op now needs additional information about the size
of the output tensor, which is computed dynamically with a set of
`tensor.dim` ops. The comments below assume an input tensor of size
10x11x12, as before. The code is lowered as:
 
```
#map = affine_map<(d0, d1, d2) -> (d1, d2, d0)>
#map1 = affine_map<(d0, d1, d2) -> (d0, d1, d2)>
module {
  func.func @test_tosa_transpose(%arg0: tensor<?x?x?xf32>) -> tensor<?x?x?xf32> {
    %c0 = arith.constant 0 : index
    %c1 = arith.constant 1 : index
    %c2 = arith.constant 2 : index
 
    %arg0_dim0 = tensor.dim %arg0, %c0 : tensor<?x?x?xf32>   // Evaluates to 10
    %arg0_dim1 = tensor.dim %arg0, %c1 : tensor<?x?x?xf32>   // Evaluates to 11
    %arg0_dim2 = tensor.dim %arg0, %c2 : tensor<?x?x?xf32>   // Evaluates to 12
 
    %empty = tensor.empty(%arg0_dim1, %arg0_dim2, %arg0_dim0) : tensor<?x?x?xf32>   // Output of type tensor<11x12x10>  WRONG!
    %transposed = linalg.generic {indexing_maps = [#map, #map1], iterator_types = ["parallel", "parallel", "parallel"]} ins(%arg0 : tensor<?x?x?xf32>) outs(%empty : tensor<?x?x?xf32>) {
    ^bb0(%in: f32, %out: f32):
      linalg.yield %in : f32
    } -> tensor<?x?x?xf32>
    return %transposed : tensor<?x?x?xf32>
  }
}
```
 
The output tensor shape is dynamically computed as 11x12x10 instead of
12x10x11. Since the total size of the output tensor is still the same,
the code does not segfault after bufferization. However, index
computations are invalid and lead to SWAs.




More information about the All-commits mailing list