1// RUN: mlir-opt %s --sparse-compiler | \
2// RUN: mlir-cpu-runner -e entry -entry-point-result=void \
3// RUN:  -shared-libs=%mlir_integration_test_dir/libmlir_c_runner_utils%shlibext | \
4// RUN: FileCheck %s
5
6#SparseVector = #sparse_tensor.encoding<{
7  dimLevelType = ["compressed"]
8}>
9
10#SparseMatrix = #sparse_tensor.encoding<{
11  dimLevelType = ["compressed", "compressed"]
12}>
13
14#trait_1d = {
15  indexing_maps = [
16    affine_map<(i) -> (i)>,  // a
17    affine_map<(i) -> (i)>   // x (out)
18  ],
19  iterator_types = ["parallel"],
20  doc = "X(i) = a(i) op i"
21}
22
23#trait_2d = {
24  indexing_maps = [
25    affine_map<(i,j) -> (i,j)>,  // A
26    affine_map<(i,j) -> (i,j)>   // X (out)
27  ],
28  iterator_types = ["parallel", "parallel"],
29  doc = "X(i,j) = A(i,j) op i op j"
30}
31
32//
33// Test with indices. Note that a lot of results are actually
34// dense, but this is done to stress test all the operations.
35//
36module {
37
38  //
39  // Kernel that uses index in the index notation (conjunction).
40  //
41  func.func @sparse_index_1d_conj(%arga: tensor<8xi64, #SparseVector>)
42                                 -> tensor<8xi64, #SparseVector> {
43    %d0 = arith.constant 8 : index
44    %init = sparse_tensor.init [%d0] : tensor<8xi64, #SparseVector>
45    %r = linalg.generic #trait_1d
46        ins(%arga: tensor<8xi64, #SparseVector>)
47       outs(%init: tensor<8xi64, #SparseVector>) {
48        ^bb(%a: i64, %x: i64):
49          %i = linalg.index 0 : index
50          %ii = arith.index_cast %i : index to i64
51          %m1 = arith.muli %a, %ii : i64
52          linalg.yield %m1 : i64
53    } -> tensor<8xi64, #SparseVector>
54    return %r : tensor<8xi64, #SparseVector>
55  }
56
57  //
58  // Kernel that uses index in the index notation (disjunction).
59  //
60  func.func @sparse_index_1d_disj(%arga: tensor<8xi64, #SparseVector>)
61                                 -> tensor<8xi64, #SparseVector> {
62    %d0 = arith.constant 8 : index
63    %init = sparse_tensor.init [%d0] : tensor<8xi64, #SparseVector>
64    %r = linalg.generic #trait_1d
65        ins(%arga: tensor<8xi64, #SparseVector>)
66       outs(%init: tensor<8xi64, #SparseVector>) {
67        ^bb(%a: i64, %x: i64):
68          %i = linalg.index 0 : index
69          %ii = arith.index_cast %i : index to i64
70          %m1 = arith.addi %a, %ii : i64
71          linalg.yield %m1 : i64
72    } -> tensor<8xi64, #SparseVector>
73    return %r : tensor<8xi64, #SparseVector>
74  }
75
76  //
77  // Kernel that uses indices in the index notation (conjunction).
78  //
79  func.func @sparse_index_2d_conj(%arga: tensor<3x4xi64, #SparseMatrix>)
80                                 -> tensor<3x4xi64, #SparseMatrix> {
81    %d0 = arith.constant 3 : index
82    %d1 = arith.constant 4 : index
83    %init = sparse_tensor.init [%d0, %d1] : tensor<3x4xi64, #SparseMatrix>
84    %r = linalg.generic #trait_2d
85        ins(%arga: tensor<3x4xi64, #SparseMatrix>)
86       outs(%init: tensor<3x4xi64, #SparseMatrix>) {
87        ^bb(%a: i64, %x: i64):
88          %i = linalg.index 0 : index
89          %j = linalg.index 1 : index
90          %ii = arith.index_cast %i : index to i64
91          %jj = arith.index_cast %j : index to i64
92          %m1 = arith.muli %ii, %a : i64
93          %m2 = arith.muli %jj, %m1 : i64
94          linalg.yield %m2 : i64
95    } -> tensor<3x4xi64, #SparseMatrix>
96    return %r : tensor<3x4xi64, #SparseMatrix>
97  }
98
99  //
100  // Kernel that uses indices in the index notation (disjunction).
101  //
102  func.func @sparse_index_2d_disj(%arga: tensor<3x4xi64, #SparseMatrix>)
103                                 -> tensor<3x4xi64, #SparseMatrix> {
104    %d0 = arith.constant 3 : index
105    %d1 = arith.constant 4 : index
106    %init = sparse_tensor.init [%d0, %d1] : tensor<3x4xi64, #SparseMatrix>
107    %r = linalg.generic #trait_2d
108        ins(%arga: tensor<3x4xi64, #SparseMatrix>)
109       outs(%init: tensor<3x4xi64, #SparseMatrix>) {
110        ^bb(%a: i64, %x: i64):
111          %i = linalg.index 0 : index
112          %j = linalg.index 1 : index
113          %ii = arith.index_cast %i : index to i64
114          %jj = arith.index_cast %j : index to i64
115          %m1 = arith.addi %ii, %a : i64
116          %m2 = arith.addi %jj, %m1 : i64
117          linalg.yield %m2 : i64
118    } -> tensor<3x4xi64, #SparseMatrix>
119    return %r : tensor<3x4xi64, #SparseMatrix>
120  }
121
122  func.func @add_outer_2d(%arg0: tensor<2x3xf32, #SparseMatrix>)
123                         -> tensor<2x3xf32, #SparseMatrix> {
124    %c2 = arith.constant 2 : index
125    %c3 = arith.constant 3 : index
126    %0 = sparse_tensor.init[%c2, %c3] : tensor<2x3xf32, #SparseMatrix>
127    %1 = linalg.generic #trait_2d
128      ins(%arg0 : tensor<2x3xf32, #SparseMatrix>)
129      outs(%0 : tensor<2x3xf32, #SparseMatrix>) {
130    ^bb0(%arg1: f32, %arg2: f32):
131      %2 = linalg.index 0 : index
132      %3 = arith.index_cast %2 : index to i64
133      %4 = arith.uitofp %3 : i64 to f32
134      %5 = arith.addf %arg1, %4 : f32
135      linalg.yield %5 : f32
136    } -> tensor<2x3xf32, #SparseMatrix>
137    return %1 : tensor<2x3xf32, #SparseMatrix>
138  }
139
140  //
141  // Main driver.
142  //
143  func.func @entry() {
144    %c0 = arith.constant 0 : index
145    %du = arith.constant -1 : i64
146    %df = arith.constant -1.0 : f32
147
148    // Setup input sparse vector.
149    %v1 = arith.constant sparse<[[2], [4]], [ 10, 20]> : tensor<8xi64>
150    %sv = sparse_tensor.convert %v1 : tensor<8xi64> to tensor<8xi64, #SparseVector>
151
152    // Setup input "sparse" vector.
153    %v2 = arith.constant dense<[ 1,  2,  4,  8,  16,  32,  64,  128 ]> : tensor<8xi64>
154    %dv = sparse_tensor.convert %v2 : tensor<8xi64> to tensor<8xi64, #SparseVector>
155
156    // Setup input sparse matrix.
157    %m1 = arith.constant sparse<[[1,1], [2,3]], [10, 20]> : tensor<3x4xi64>
158    %sm = sparse_tensor.convert %m1 : tensor<3x4xi64> to tensor<3x4xi64, #SparseMatrix>
159
160    // Setup input "sparse" matrix.
161    %m2 = arith.constant dense <[ [ 1,  1,  1,  1 ],
162                                  [ 1,  2,  1,  1 ],
163                                  [ 1,  1,  3,  4 ] ]> : tensor<3x4xi64>
164    %dm = sparse_tensor.convert %m2 : tensor<3x4xi64> to tensor<3x4xi64, #SparseMatrix>
165
166    // Setup input sparse f32 matrix.
167    %mf32 = arith.constant sparse<[[0,1], [1,2]], [10.0, 41.0]> : tensor<2x3xf32>
168    %sf32 = sparse_tensor.convert %mf32 : tensor<2x3xf32> to tensor<2x3xf32, #SparseMatrix>
169
170    // Call the kernels.
171    %0 = call @sparse_index_1d_conj(%sv) : (tensor<8xi64, #SparseVector>)
172      -> tensor<8xi64, #SparseVector>
173    %1 = call @sparse_index_1d_disj(%sv) : (tensor<8xi64, #SparseVector>)
174      -> tensor<8xi64, #SparseVector>
175    %2 = call @sparse_index_1d_conj(%dv) : (tensor<8xi64, #SparseVector>)
176      -> tensor<8xi64, #SparseVector>
177    %3 = call @sparse_index_1d_disj(%dv) : (tensor<8xi64, #SparseVector>)
178      -> tensor<8xi64, #SparseVector>
179    %4 = call @sparse_index_2d_conj(%sm) : (tensor<3x4xi64, #SparseMatrix>)
180      -> tensor<3x4xi64, #SparseMatrix>
181    %5 = call @sparse_index_2d_disj(%sm) : (tensor<3x4xi64, #SparseMatrix>)
182      -> tensor<3x4xi64, #SparseMatrix>
183    %6 = call @sparse_index_2d_conj(%dm) : (tensor<3x4xi64, #SparseMatrix>)
184      -> tensor<3x4xi64, #SparseMatrix>
185    %7 = call @sparse_index_2d_disj(%dm) : (tensor<3x4xi64, #SparseMatrix>)
186      -> tensor<3x4xi64, #SparseMatrix>
187
188    //
189    // Verify result.
190    //
191    // CHECK:      ( 20, 80, -1, -1, -1, -1, -1, -1 )
192    // CHECK-NEXT: ( 0, 1, 12, 3, 24, 5, 6, 7 )
193    // CHECK-NEXT: ( 0, 2, 8, 24, 64, 160, 384, 896 )
194    // CHECK-NEXT: ( 1, 3, 6, 11, 20, 37, 70, 135 )
195    // CHECK-NEXT: ( 10, 120, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 )
196    // CHECK-NEXT: ( 0, 1, 2, 3, 1, 12, 3, 4, 2, 3, 4, 25 )
197    // CHECK-NEXT: ( 0, 0, 0, 0, 0, 2, 2, 3, 0, 2, 12, 24 )
198    // CHECK-NEXT: ( 1, 2, 3, 4, 2, 4, 4, 5, 3, 4, 7, 9 )
199    //
200    %8 = sparse_tensor.values %0 : tensor<8xi64, #SparseVector> to memref<?xi64>
201    %9 = sparse_tensor.values %1 : tensor<8xi64, #SparseVector> to memref<?xi64>
202    %10 = sparse_tensor.values %2 : tensor<8xi64, #SparseVector> to memref<?xi64>
203    %11 = sparse_tensor.values %3 : tensor<8xi64, #SparseVector> to memref<?xi64>
204    %12 = sparse_tensor.values %4 : tensor<3x4xi64, #SparseMatrix> to memref<?xi64>
205    %13 = sparse_tensor.values %5 : tensor<3x4xi64, #SparseMatrix> to memref<?xi64>
206    %14 = sparse_tensor.values %6 : tensor<3x4xi64, #SparseMatrix> to memref<?xi64>
207    %15 = sparse_tensor.values %7 : tensor<3x4xi64, #SparseMatrix> to memref<?xi64>
208    %16 = vector.transfer_read %8[%c0], %du: memref<?xi64>, vector<8xi64>
209    %17 = vector.transfer_read %9[%c0], %du: memref<?xi64>, vector<8xi64>
210    %18 = vector.transfer_read %10[%c0], %du: memref<?xi64>, vector<8xi64>
211    %19 = vector.transfer_read %11[%c0], %du: memref<?xi64>, vector<8xi64>
212    %20 = vector.transfer_read %12[%c0], %du: memref<?xi64>, vector<12xi64>
213    %21 = vector.transfer_read %13[%c0], %du: memref<?xi64>, vector<12xi64>
214    %22 = vector.transfer_read %14[%c0], %du: memref<?xi64>, vector<12xi64>
215    %23 = vector.transfer_read %15[%c0], %du: memref<?xi64>, vector<12xi64>
216    vector.print %16 : vector<8xi64>
217    vector.print %17 : vector<8xi64>
218    vector.print %18 : vector<8xi64>
219    vector.print %19 : vector<8xi64>
220    vector.print %20 : vector<12xi64>
221    vector.print %21 : vector<12xi64>
222    vector.print %22 : vector<12xi64>
223    vector.print %23 : vector<12xi64>
224
225    // Release resources.
226    sparse_tensor.release %sv : tensor<8xi64, #SparseVector>
227    sparse_tensor.release %dv : tensor<8xi64, #SparseVector>
228    sparse_tensor.release %0 : tensor<8xi64, #SparseVector>
229    sparse_tensor.release %1 : tensor<8xi64, #SparseVector>
230    sparse_tensor.release %2 : tensor<8xi64, #SparseVector>
231    sparse_tensor.release %3 : tensor<8xi64, #SparseVector>
232    sparse_tensor.release %sm : tensor<3x4xi64, #SparseMatrix>
233    sparse_tensor.release %dm : tensor<3x4xi64, #SparseMatrix>
234    sparse_tensor.release %4 : tensor<3x4xi64, #SparseMatrix>
235    sparse_tensor.release %5 : tensor<3x4xi64, #SparseMatrix>
236    sparse_tensor.release %6 : tensor<3x4xi64, #SparseMatrix>
237    sparse_tensor.release %7 : tensor<3x4xi64, #SparseMatrix>
238
239    //
240    // Call the f32 kernel, verify the result, release the resources.
241    //
242    // CHECK-NEXT: ( 0, 10, 0, 1, 1, 42 )
243    //
244    %100 = call @add_outer_2d(%sf32) : (tensor<2x3xf32, #SparseMatrix>)
245      -> tensor<2x3xf32, #SparseMatrix>
246    %101 = sparse_tensor.values %100 : tensor<2x3xf32, #SparseMatrix> to memref<?xf32>
247    %102 = vector.transfer_read %101[%c0], %df: memref<?xf32>, vector<6xf32>
248    vector.print %102 : vector<6xf32>
249    sparse_tensor.release %sf32 : tensor<2x3xf32, #SparseMatrix>
250    sparse_tensor.release %100 : tensor<2x3xf32, #SparseMatrix>
251
252    return
253  }
254}
255