xref: /llvm-project/mlir/test/Dialect/Affine/memref-bound-check.mlir (revision da0ce32cc3221686f30a6267f43d8c5dd469ef80)
1// RUN: mlir-opt %s -test-memref-bound-check -split-input-file -verify-diagnostics | FileCheck %s
2
3// -----
4
5// CHECK-LABEL: func @test() {
6func.func @test() {
7  %zero = arith.constant 0 : index
8  %minusone = arith.constant -1 : index
9  %sym = arith.constant 111 : index
10
11  %A = memref.alloc() : memref<9 x 9 x i32>
12  %B = memref.alloc() : memref<111 x i32>
13
14  affine.for %i = -1 to 10 {
15    affine.for %j = -1 to 10 {
16      %idx0 = affine.apply affine_map<(d0, d1) -> (d0)>(%i, %j)
17      %idx1 = affine.apply affine_map<(d0, d1) -> (d1)>(%i, %j)
18      // Out of bound access.
19      %x  = affine.load %A[%idx0, %idx1] : memref<9 x 9 x i32>
20      // expected-error@-1 {{'affine.load' op memref out of upper bound access along dimension #1}}
21      // expected-error@-2 {{'affine.load' op memref out of lower bound access along dimension #1}}
22      // expected-error@-3 {{'affine.load' op memref out of upper bound access along dimension #2}}
23      // expected-error@-4 {{'affine.load' op memref out of lower bound access along dimension #2}}
24      // This will access 0 to 110 - hence an overflow.
25      %idy = affine.apply affine_map<(d0, d1) -> (10*d0 - d1 + 19)>(%i, %j)
26      %y = affine.load %B[%idy] : memref<111 x i32>
27    }
28  }
29
30  affine.for %k = 0 to 10 {
31      // In bound.
32      %u = affine.load %B[%zero] : memref<111 x i32>
33      // Out of bounds.
34      %v = affine.load %B[%sym] : memref<111 x i32> // expected-error {{'affine.load' op memref out of upper bound access along dimension #1}}
35      // Out of bounds.
36      affine.store %v, %B[%minusone] : memref<111 x i32>  // expected-error {{'affine.store' op memref out of lower bound access along dimension #1}}
37  }
38  return
39}
40
41// CHECK-LABEL: func @test_mod_floordiv_ceildiv
42func.func @test_mod_floordiv_ceildiv() {
43  %zero = arith.constant 0 : index
44  %A = memref.alloc() : memref<128 x 64 x 64 x i32>
45
46  affine.for %i = 0 to 256 {
47    affine.for %j = 0 to 256 {
48      %idx0 = affine.apply affine_map<(d0, d1, d2) -> (d0 mod 128 + 1)>(%i, %j, %j)
49      %idx1 = affine.apply affine_map<(d0, d1, d2) -> (d1 floordiv 4 + 1)>(%i, %j, %j)
50      %idx2 = affine.apply affine_map<(d0, d1, d2) -> (d2 ceildiv 4)>(%i, %j, %j)
51      %x  = affine.load %A[%idx0, %idx1, %idx2] : memref<128 x 64 x 64 x i32>
52      // expected-error@-1 {{'affine.load' op memref out of upper bound access along dimension #1}}
53      // expected-error@-2 {{'affine.load' op memref out of upper bound access along dimension #2}}
54      // expected-error@-3 {{'affine.load' op memref out of upper bound access along dimension #3}}
55      %idy0 = affine.apply affine_map<(d0, d1, d2) -> (d0 mod 128)>(%i, %j, %j)
56      %idy1 = affine.apply affine_map<(d0, d1, d2) -> (d1 floordiv 4)>(%i, %j, %j)
57      %idy2 = affine.apply affine_map<(d0, d1, d2) -> (d2 ceildiv 4 - 1)>(%i, %j, %j)
58      affine.store %x, %A[%idy0, %idy1, %idy2] : memref<128 x 64 x 64 x i32> // expected-error {{'affine.store' op memref out of lower bound access along dimension #3}}
59    } // CHECK: }
60  } // CHECK: }
61  return
62}
63
64// CHECK-LABEL: func @test_no_out_of_bounds()
65func.func @test_no_out_of_bounds() {
66  %zero = arith.constant 0 : index
67  %A = memref.alloc() : memref<257 x 256 x i32>
68  %C = memref.alloc() : memref<257 x i32>
69  %B = memref.alloc() : memref<1 x i32>
70
71  affine.for %i = 0 to 256 {
72    affine.for %j = 0 to 256 {
73      // All of these accesses are in bound; check that no errors are emitted.
74      // CHECK: %{{.*}} = affine.apply {{#map.*}}(%{{.*}}, %{{.*}})
75      // CHECK-NEXT: %{{.*}} = affine.load %{{.*}}[%{{.*}}, %{{.*}}] : memref<257x256xi32>
76      // CHECK-NEXT: %{{.*}} = affine.apply {{#map.*}}(%{{.*}}, %{{.*}})
77      // CHECK-NEXT: %{{.*}} = affine.load %{{.*}}[%{{.*}}] : memref<1xi32>
78      %idx0 = affine.apply affine_map<(d0, d1) -> ( 64 * (d0 ceildiv 64))>(%i, %j)
79      // Without GCDTightenInequalities(), the upper bound on the region
80      // accessed along first memref dimension would have come out as d0 <= 318
81      // (instead of d0 <= 256), and led to a false positive out of bounds.
82      %x  = affine.load %A[%idx0, %zero] : memref<257 x 256 x i32>
83      %idy = affine.apply affine_map<(d0, d1) -> (d0 floordiv 256)>(%i, %i)
84      %y  = affine.load %B[%idy] : memref<1 x i32>
85    } // CHECK-NEXT: }
86  }
87  return
88}
89
90// CHECK-LABEL: func @mod_div
91func.func @mod_div() {
92  %zero = arith.constant 0 : index
93  %A = memref.alloc() : memref<128 x 64 x 64 x i32>
94
95  affine.for %i = 0 to 256 {
96    affine.for %j = 0 to 256 {
97      %idx0 = affine.apply affine_map<(d0, d1, d2) -> (d0 mod 128 + 1)>(%i, %j, %j)
98      %idx1 = affine.apply affine_map<(d0, d1, d2) -> (d1 floordiv 4 + 1)>(%i, %j, %j)
99      %idx2 = affine.apply affine_map<(d0, d1, d2) -> (d2 ceildiv 4)>(%i, %j, %j)
100      %x  = affine.load %A[%idx0, %idx1, %idx2] : memref<128 x 64 x 64 x i32>
101      // expected-error@-1 {{'affine.load' op memref out of upper bound access along dimension #1}}
102      // expected-error@-2 {{'affine.load' op memref out of upper bound access along dimension #2}}
103      // expected-error@-3 {{'affine.load' op memref out of upper bound access along dimension #3}}
104      %idy0 = affine.apply affine_map<(d0, d1, d2) -> (d0 mod 128)>(%i, %j, %j)
105      %idy1 = affine.apply affine_map<(d0, d1, d2) -> (d1 floordiv 4)>(%i, %j, %j)
106      %idy2 = affine.apply affine_map<(d0, d1, d2) -> (d2 ceildiv 4 - 1)>(%i, %j, %j)
107      affine.store %x, %A[%idy0, %idy1, %idy2] : memref<128 x 64 x 64 x i32> // expected-error {{'affine.store' op memref out of lower bound access along dimension #3}}
108    }
109  }
110  return
111}
112
113// Tests with nested mod's and floordiv's.
114// CHECK-LABEL: func @mod_floordiv_nested() {
115func.func @mod_floordiv_nested() {
116  %A = memref.alloc() : memref<256 x 256 x i32>
117  affine.for %i = 0 to 256 {
118    affine.for %j = 0 to 256 {
119      %idx0 = affine.apply affine_map<(d0, d1) -> ((d0 mod 1024) floordiv 4)>(%i, %j)
120      %idx1 = affine.apply affine_map<(d0, d1) -> ((((d1 mod 128) mod 32) ceildiv 4) * 32)>(%i, %j)
121      affine.load %A[%idx0, %idx1] : memref<256 x 256 x i32> // expected-error {{'affine.load' op memref out of upper bound access along dimension #2}}
122    }
123  }
124  return
125}
126
127// CHECK-LABEL: func @test_semi_affine_bailout
128func.func @test_semi_affine_bailout(%N : index) {
129  %B = memref.alloc() : memref<10 x i32>
130  affine.for %i = 0 to 10 {
131    %idx = affine.apply affine_map<(d0)[s0] -> (d0 * s0)>(%i)[%N]
132    %y = affine.load %B[%idx] : memref<10 x i32>
133    // expected-error@-1 {{getMemRefRegion: compose affine map failed}}
134  }
135  return
136}
137
138// CHECK-LABEL: func @multi_mod_floordiv
139func.func @multi_mod_floordiv() {
140  %A = memref.alloc() : memref<2x2xi32>
141  affine.for %ii = 0 to 64 {
142      %idx0 = affine.apply affine_map<(d0) -> ((d0 mod 147456) floordiv 1152)> (%ii)
143      %idx1 = affine.apply affine_map<(d0) -> (((d0 mod 147456) mod 1152) floordiv 384)> (%ii)
144      %v = affine.load %A[%idx0, %idx1] : memref<2x2xi32>
145  }
146  return
147}
148
149// CHECK-LABEL: func @delinearize_mod_floordiv
150func.func @delinearize_mod_floordiv() {
151  %c0 = arith.constant 0 : index
152  %in = memref.alloc() : memref<2x2x3x3x16x1xi32>
153  %out = memref.alloc() : memref<64x9xi32>
154
155  // Reshape '%in' into '%out'.
156  affine.for %ii = 0 to 64 {
157    affine.for %jj = 0 to 9 {
158      %a0 = affine.apply affine_map<(d0, d1) -> (d0 * (9 * 1024) + d1 * 128)> (%ii, %jj)
159      %a10 = affine.apply affine_map<(d0) ->
160        (d0 floordiv (2 * 3 * 3 * 128 * 128))> (%a0)
161      %a11 = affine.apply affine_map<(d0) ->
162        ((d0 mod 294912) floordiv (3 * 3 * 128 * 128))> (%a0)
163      %a12 = affine.apply affine_map<(d0) ->
164        ((((d0 mod 294912) mod 147456) floordiv 1152) floordiv 8)> (%a0)
165      %a13 = affine.apply affine_map<(d0) ->
166        ((((d0 mod 294912) mod 147456) mod 1152) floordiv 384)> (%a0)
167      %a14 = affine.apply affine_map<(d0) ->
168        (((((d0 mod 294912) mod 147456) mod 1152) mod 384) floordiv 128)> (%a0)
169      %a15 = affine.apply affine_map<(d0) ->
170        ((((((d0 mod 294912) mod 147456) mod 1152) mod 384) mod 128)
171          floordiv 128)> (%a0)
172      %v0 = affine.load %in[%a10, %a11, %a13, %a14, %a12, %a15]
173        : memref<2x2x3x3x16x1xi32>
174    }
175  }
176  return
177}
178
179// CHECK-LABEL: func @zero_d_memref
180func.func @zero_d_memref(%arg0: memref<i32>) {
181  %c0 = arith.constant 0 : i32
182  // A 0-d memref always has in-bound accesses!
183  affine.store %c0, %arg0[] : memref<i32>
184  return
185}
186
187// CHECK-LABEL: func @out_of_bounds
188func.func @out_of_bounds() {
189  %in = memref.alloc() : memref<1xi32>
190  %c9 = arith.constant 9 : i32
191
192  affine.for %i0 = 10 to 11 {
193    %idy = affine.apply affine_map<(d0) ->  (100 * d0 floordiv 1000)> (%i0)
194    affine.store %c9, %in[%idy] : memref<1xi32> // expected-error {{'affine.store' op memref out of upper bound access along dimension #1}}
195  }
196  return
197}
198
199// -----
200
201// This test case accesses within bounds. Without removal of a certain type of
202// trivially redundant constraints (those differing only in their constant
203// term), the number of constraints here explodes, and this would return out of
204// bounds errors conservatively due to IntegerRelation::kExplosionFactor.
205#map3 = affine_map<(d0, d1) -> ((d0 * 72 + d1) floordiv 2304 + ((((d0 * 72 + d1) mod 2304) mod 1152) mod 9) floordiv 3)>
206#map4 = affine_map<(d0, d1) -> ((d0 * 72 + d1) mod 2304 - (((d0 * 72 + d1) mod 2304) floordiv 1152) * 1151 - ((((d0 * 72 + d1) mod 2304) mod 1152) floordiv 9) * 9 - (((((d0 * 72 + d1) mod 2304) mod 1152) mod 9) floordiv 3) * 3)>
207#map5 = affine_map<(d0, d1) -> (((((d0 * 72 + d1) mod 2304) mod 1152) floordiv 9) floordiv 8)>
208// CHECK-LABEL: func @test_complex_mod_floordiv
209func.func @test_complex_mod_floordiv(%arg0: memref<4x4x16x1xf32>) {
210  %c0 = arith.constant 0 : index
211  %0 = memref.alloc() : memref<1x2x3x3x16x1xf32>
212  affine.for %i0 = 0 to 64 {
213    affine.for %i1 = 0 to 9 {
214      %2 = affine.apply #map3(%i0, %i1)
215      %3 = affine.apply #map4(%i0, %i1)
216      %4 = affine.apply #map5(%i0, %i1)
217      %5 = affine.load %arg0[%2, %c0, %4, %c0] : memref<4x4x16x1xf32>
218    }
219  }
220  return
221}
222
223// -----
224
225// The first load is within bounds, but not the second one.
226#map0 = affine_map<(d0) -> (d0 mod 4)>
227#map1 = affine_map<(d0) -> (d0 mod 4 + 4)>
228
229// CHECK-LABEL: func @test_mod_bound
230func.func @test_mod_bound() {
231  %0 = memref.alloc() : memref<7 x f32>
232  %1 = memref.alloc() : memref<6 x f32>
233  affine.for %i0 = 0 to 4096 {
234    affine.for %i1 = #map0(%i0) to #map1(%i0) {
235      affine.load %0[%i1] : memref<7 x f32>
236      affine.load %1[%i1] : memref<6 x f32>
237      // expected-error@-1 {{'affine.load' op memref out of upper bound access along dimension #1}}
238    }
239  }
240  return
241}
242
243// -----
244
245#map0 = affine_map<(d0) -> (d0 floordiv 4)>
246#map1 = affine_map<(d0) -> (d0 floordiv 4 + 4)>
247#map2 = affine_map<(d0) -> (4 * (d0 floordiv 4)  + d0 mod 4)>
248
249// CHECK-LABEL: func @test_floordiv_bound
250func.func @test_floordiv_bound() {
251  %0 = memref.alloc() : memref<1027 x f32>
252  %1 = memref.alloc() : memref<1026 x f32>
253  %2 = memref.alloc() : memref<4096 x f32>
254  %N = arith.constant 2048 : index
255  affine.for %i0 = 0 to 4096 {
256    affine.for %i1 = #map0(%i0) to #map1(%i0) {
257      affine.load %0[%i1] : memref<1027 x f32>
258      affine.load %1[%i1] : memref<1026 x f32>
259      // expected-error@-1 {{'affine.load' op memref out of upper bound access along dimension #1}}
260    }
261    affine.for %i2 = 0 to #map2(%N) {
262      // Within bounds.
263      %v = affine.load %2[%i2] : memref<4096 x f32>
264    }
265  }
266  return
267}
268
269// -----
270
271// This should not give an out of bounds error. The result of the affine.apply
272// is composed into the bound map during analysis.
273
274#map_lb = affine_map<(d0) -> (d0)>
275#map_ub = affine_map<(d0) -> (d0 + 4)>
276
277// CHECK-LABEL: func @non_composed_bound_operand
278func.func @non_composed_bound_operand(%arg0: memref<1024xf32>) {
279  affine.for %i0 = 4 to 1028 step 4 {
280    %i1 = affine.apply affine_map<(d0) -> (d0 - 4)> (%i0)
281    affine.for %i2 = #map_lb(%i1) to #map_ub(%i1) {
282        %0 = affine.load %arg0[%i2] : memref<1024xf32>
283    }
284  }
285  return
286}
287
288// CHECK-LABEL: func @zero_d_memref
289func.func @zero_d_memref() {
290  %Z = memref.alloc() : memref<f32>
291  affine.for %i = 0 to 100 {
292    affine.load %Z[] : memref<f32>
293  }
294  return
295}
296
297// CHECK-LABEL: func @affine_parallel
298func.func @affine_parallel(%M: memref<2048x2048xf64>) {
299  affine.parallel (%i) = (0) to (3000) {
300    affine.for %j = 0 to 2048 {
301      affine.load %M[%i, %j] : memref<2048x2048xf64>
302      // expected-error@above {{'affine.load' op memref out of upper bound access along dimension #1}}
303    }
304  }
305  return
306}
307