1// RUN: mlir-opt %s -tensor-bufferize | FileCheck %s
2
3// CHECK-LABEL:   func @dim(
4// CHECK-SAME:              %[[TENSOR:.*]]: tensor<f32>,
5// CHECK-SAME:              %[[INDEX:.*]]: index) -> index {
6// CHECK:           %[[MEMREF:.*]] = memref.buffer_cast %[[TENSOR]] : memref<f32>
7// CHECK:           %[[EXTENT:.*]] = memref.dim %[[MEMREF]], %[[INDEX]] : memref<f32>
8// CHECK:           return %[[EXTENT]] : index
9func @dim(%arg0: tensor<f32>, %arg1: index) -> index {
10  %0 = tensor.dim %arg0, %arg1 : tensor<f32>
11  return %0 : index
12}
13
14// CHECK-LABEL:   func @tensor.cast(
15// CHECK-SAME:                      %[[TENSOR:.*]]: tensor<?xindex>) -> tensor<2xindex> {
16// CHECK:           %[[MEMREF:.*]] = memref.buffer_cast %[[TENSOR]]
17// CHECK:           %[[CASTED:.*]] = memref.cast %[[MEMREF]] : memref<?xindex> to memref<2xindex>
18// CHECK:           %[[RET:.*]] = memref.tensor_load %[[CASTED]]
19// CHECK:           return %[[RET]] : tensor<2xindex>
20func @tensor.cast(%arg0: tensor<?xindex>) -> tensor<2xindex> {
21  %0 = tensor.cast %arg0 : tensor<?xindex> to tensor<2xindex>
22  return %0 : tensor<2xindex>
23}
24
25// CHECK-LABEL:   func @tensor.cast_from_unranked(
26// CHECK-SAME:                                    %[[TENSOR:.*]]: tensor<*xf32>) -> tensor<2xf32> {
27// CHECK:           %[[MEMREF:.*]] = memref.buffer_cast %[[TENSOR]] : memref<*xf32>
28// CHECK:           %[[CASTED_MEMREF:.*]] = memref.cast %[[MEMREF]] : memref<*xf32> to memref<2xf32>
29// CHECK:           %[[RET:.*]] = memref.tensor_load %[[CASTED_MEMREF]] : memref<2xf32>
30// CHECK:           return %[[RET]] : tensor<2xf32>
31func @tensor.cast_from_unranked(%arg0: tensor<*xf32>) -> tensor<2xf32> {
32  %0 = tensor.cast %arg0 : tensor<*xf32> to tensor<2xf32>
33  return %0 : tensor<2xf32>
34}
35
36// CHECK-LABEL:   func @tensor.cast_to_unranked(
37// CHECK-SAME:                                  %[[TENSOR:.*]]: tensor<2xf32>) -> tensor<*xf32> {
38// CHECK:           %[[MEMREF:.*]] = memref.buffer_cast %[[TENSOR]] : memref<2xf32>
39// CHECK:           %[[CASTED_MEMREF:.*]] = memref.cast %[[MEMREF]] : memref<2xf32> to memref<*xf32>
40// CHECK:           %[[RET:.*]] = memref.tensor_load %[[CASTED_MEMREF]] : memref<*xf32>
41// CHECK:           return %[[RET]] : tensor<*xf32>
42func @tensor.cast_to_unranked(%arg0: tensor<2xf32>) -> tensor<*xf32> {
43  %0 = tensor.cast %arg0 : tensor<2xf32> to tensor<*xf32>
44  return %0 : tensor<*xf32>
45}
46
47// CHECK-LABEL:   func @tensor.extract(
48// CHECK-SAME:                  %[[TENSOR:.*]]: tensor<?xf32>,
49// CHECK-SAME:                  %[[IDX:.*]]: index) -> f32 {
50// CHECK:           %[[MEMREF:.*]] = memref.buffer_cast %[[TENSOR]] : memref<?xf32>
51// CHECK:           %[[RET:.*]] = memref.load %[[MEMREF]][%[[IDX]]] : memref<?xf32>
52// CHECK:           return %[[RET]] : f32
53// CHECK:         }
54func @tensor.extract(%arg0: tensor<?xf32>, %arg1: index) -> f32 {
55  %0 = tensor.extract %arg0[%arg1] : tensor<?xf32>
56  return %0 : f32
57}
58
59// CHECK-LABEL:   func @tensor.from_elements(
60// CHECK-SAME:                               %[[ELEM0:.*]]: index,
61// CHECK-SAME:                               %[[ELEM1:.*]]: index) -> tensor<2xindex> {
62// CHECK:           %[[MEMREF:.*]] = memref.alloc()
63// CHECK:           %[[C0:.*]] = constant 0 : index
64// CHECK:           store %[[ELEM0]], %[[MEMREF]][%[[C0]]]
65// CHECK:           %[[C1:.*]] = constant 1 : index
66// CHECK:           store %[[ELEM1]], %[[MEMREF]][%[[C1]]]
67// CHECK:           %[[RET:.*]] = memref.tensor_load %[[MEMREF]]
68// CHECK:           return %[[RET]] : tensor<2xindex>
69func @tensor.from_elements(%arg0: index, %arg1: index) -> tensor<2xindex> {
70  %0 = tensor.from_elements %arg0, %arg1 : tensor<2xindex>
71  return %0 : tensor<2xindex>
72}
73
74// CHECK-LABEL:   func @tensor.generate(
75// CHECK-SAME:                                       %[[ARG:.*]]: tensor<*xf32>,
76// CHECK-SAME:                                       %[[DYNAMIC_EXTENT:.*]]: index) -> tensor<?xindex> {
77// CHECK:           %[[MEMREF:.*]] = memref.alloc(%[[DYNAMIC_EXTENT]]) : memref<?xindex>
78// CHECK:           %[[C0:.*]] = constant 0 : index
79// CHECK:           %[[C1:.*]] = constant 1 : index
80// CHECK:           scf.parallel (%[[I:.*]]) = (%[[C0]]) to (%[[DYNAMIC_EXTENT]]) step (%[[C1]]) {
81// CHECK:             %[[CASTED:.*]] = memref.buffer_cast %[[ARG]] : memref<*xf32>
82// CHECK:             %[[ELEM:.*]] = memref.dim %[[CASTED]], %[[I]] : memref<*xf32>
83// CHECK:             store %[[ELEM]], %[[MEMREF]][%[[I]]] : memref<?xindex>
84// CHECK:             scf.yield
85// CHECK:           }
86// CHECK:           %[[RET:.*]] = memref.tensor_load %[[MEMREF]] : memref<?xindex>
87// CHECK:           return %[[RET]] : tensor<?xindex>
88// CHECK:         }
89func @tensor.generate(%arg: tensor<*xf32>, %dynamic_extent: index) -> tensor<?xindex> {
90  %result = tensor.generate %dynamic_extent {
91  ^bb0(%i : index):
92    %elem = tensor.dim %arg, %i : tensor<*xf32>
93    tensor.yield %elem : index
94  } : tensor<?xindex>
95  return %result : tensor<?xindex>
96}
97
98// Additional test that checks the logic for intermixed static and dynamic
99// extents.
100//
101// CHECK-LABEL:   func @tensor.generate_static_and_dynamic(
102// CHECK-SAME:                                                          %[[DYNAMIC_EXTENT:.*]]: index) -> tensor<16x?xindex> {
103// CHECK:           %[[MEMREF:.*]] = memref.alloc(%[[DYNAMIC_EXTENT]]) : memref<16x?xindex>
104// CHECK:           %[[C0:.*]] = constant 0 : index
105// CHECK:           %[[C1:.*]] = constant 1 : index
106// CHECK:           %[[C16:.*]] = constant 16 : index
107// CHECK:           scf.parallel (%[[I:.*]], %[[J:.*]]) = (%[[C0]], %[[C0]]) to (%[[C16]], %[[DYNAMIC_EXTENT]]) step (%[[C1]], %[[C1]]) {
108// CHECK:             %[[VAL_7:.*]] = addi %[[I]], %[[J]] : index
109// CHECK:             store %[[VAL_7]], %[[MEMREF]][%[[I]], %[[J]]] : memref<16x?xindex>
110// CHECK:             scf.yield
111// CHECK:           }
112// CHECK:           %[[RET:.*]] = memref.tensor_load %[[MEMREF]] : memref<16x?xindex>
113// CHECK:           return %[[RET]] : tensor<16x?xindex>
114// CHECK:         }
115func @tensor.generate_static_and_dynamic(%arg0: index) -> tensor<16x?xindex> {
116  %result = tensor.generate %arg0 {
117  ^bb0(%i: index, %j: index):
118    %sum = addi %i, %j : index
119    tensor.yield %sum : index
120  } : tensor<16x?xindex>
121  return %result : tensor<16x?xindex>
122}
123
124// The tensor.generate op needs to put its body into the
125// resulting scf.parallel. To handle unknown ops in the body, it cannot clone
126// the body because that would require the cloned ops to be legalized
127// immediately, which is usually not possible since they might be from various
128// other dialects.
129//
130// CHECK-LABEL: func @tensor.generate_unknown_ops_in_body
131func @tensor.generate_unknown_ops_in_body(%arg0: index) -> tensor<?xindex> {
132  // CHECK-NOT: tensor.generate
133  %tensor = tensor.generate %arg0 {
134  ^bb0(%iv: index):
135    // CHECK: test.source
136    %0 = "test.source"() : () -> index
137    tensor.yield %0 : index
138  } : tensor<?xindex>
139  return %tensor : tensor<?xindex>
140}
141