1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/transform/robustness.h"
16
17 #include "src/transform/test_helper.h"
18
19 namespace tint {
20 namespace transform {
21 namespace {
22
23 using RobustnessTest = TransformTest;
24
TEST_F(RobustnessTest,Array_Idx_Clamp)25 TEST_F(RobustnessTest, Array_Idx_Clamp) {
26 auto* src = R"(
27 var<private> a : array<f32, 3>;
28
29 let c : u32 = 1u;
30
31 fn f() {
32 let b : f32 = a[c];
33 }
34 )";
35
36 auto* expect = R"(
37 var<private> a : array<f32, 3>;
38
39 let c : u32 = 1u;
40
41 fn f() {
42 let b : f32 = a[1u];
43 }
44 )";
45
46 auto got = Run<Robustness>(src);
47
48 EXPECT_EQ(expect, str(got));
49 }
50
TEST_F(RobustnessTest,Array_Idx_Nested_Scalar)51 TEST_F(RobustnessTest, Array_Idx_Nested_Scalar) {
52 auto* src = R"(
53 var<private> a : array<f32, 3>;
54
55 var<private> b : array<i32, 5>;
56
57 var<private> i : u32;
58
59 fn f() {
60 var c : f32 = a[ b[i] ];
61 }
62 )";
63
64 auto* expect = R"(
65 var<private> a : array<f32, 3>;
66
67 var<private> b : array<i32, 5>;
68
69 var<private> i : u32;
70
71 fn f() {
72 var c : f32 = a[min(u32(b[min(i, 4u)]), 2u)];
73 }
74 )";
75
76 auto got = Run<Robustness>(src);
77
78 EXPECT_EQ(expect, str(got));
79 }
80
TEST_F(RobustnessTest,Array_Idx_Scalar)81 TEST_F(RobustnessTest, Array_Idx_Scalar) {
82 auto* src = R"(
83 var<private> a : array<f32, 3>;
84
85 fn f() {
86 var b : f32 = a[1];
87 }
88 )";
89
90 auto* expect = R"(
91 var<private> a : array<f32, 3>;
92
93 fn f() {
94 var b : f32 = a[1];
95 }
96 )";
97
98 auto got = Run<Robustness>(src);
99
100 EXPECT_EQ(expect, str(got));
101 }
102
TEST_F(RobustnessTest,Array_Idx_Expr)103 TEST_F(RobustnessTest, Array_Idx_Expr) {
104 auto* src = R"(
105 var<private> a : array<f32, 3>;
106
107 var<private> c : i32;
108
109 fn f() {
110 var b : f32 = a[c + 2 - 3];
111 }
112 )";
113
114 auto* expect = R"(
115 var<private> a : array<f32, 3>;
116
117 var<private> c : i32;
118
119 fn f() {
120 var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
121 }
122 )";
123
124 auto got = Run<Robustness>(src);
125
126 EXPECT_EQ(expect, str(got));
127 }
128
TEST_F(RobustnessTest,Array_Idx_Negative)129 TEST_F(RobustnessTest, Array_Idx_Negative) {
130 auto* src = R"(
131 var<private> a : array<f32, 3>;
132
133 fn f() {
134 var b : f32 = a[-1];
135 }
136 )";
137
138 auto* expect = R"(
139 var<private> a : array<f32, 3>;
140
141 fn f() {
142 var b : f32 = a[0];
143 }
144 )";
145
146 auto got = Run<Robustness>(src);
147
148 EXPECT_EQ(expect, str(got));
149 }
150
TEST_F(RobustnessTest,Array_Idx_OutOfBounds)151 TEST_F(RobustnessTest, Array_Idx_OutOfBounds) {
152 auto* src = R"(
153 var<private> a : array<f32, 3>;
154
155 fn f() {
156 var b : f32 = a[3];
157 }
158 )";
159
160 auto* expect = R"(
161 var<private> a : array<f32, 3>;
162
163 fn f() {
164 var b : f32 = a[2];
165 }
166 )";
167
168 auto got = Run<Robustness>(src);
169
170 EXPECT_EQ(expect, str(got));
171 }
172
173 // TODO(crbug.com/tint/1177) - Validation currently forbids arrays larger than
174 // 0xffffffff. If WGSL supports 64-bit indexing, re-enable this test.
TEST_F(RobustnessTest,DISABLED_LargeArrays_Idx)175 TEST_F(RobustnessTest, DISABLED_LargeArrays_Idx) {
176 auto* src = R"(
177 [[block]]
178 struct S {
179 a : array<f32, 0x7fffffff>;
180 b : array<f32>;
181 };
182 [[group(0), binding(0)]] var<storage, read> s : S;
183
184 fn f() {
185 // Signed
186 var i32_a1 : f32 = s.a[ 0x7ffffffe];
187 var i32_a2 : f32 = s.a[ 1];
188 var i32_a3 : f32 = s.a[ 0];
189 var i32_a4 : f32 = s.a[-1];
190 var i32_a5 : f32 = s.a[-0x7fffffff];
191
192 var i32_b1 : f32 = s.b[ 0x7ffffffe];
193 var i32_b2 : f32 = s.b[ 1];
194 var i32_b3 : f32 = s.b[ 0];
195 var i32_b4 : f32 = s.b[-1];
196 var i32_b5 : f32 = s.b[-0x7fffffff];
197
198 // Unsigned
199 var u32_a1 : f32 = s.a[0u];
200 var u32_a2 : f32 = s.a[1u];
201 var u32_a3 : f32 = s.a[0x7ffffffeu];
202 var u32_a4 : f32 = s.a[0x7fffffffu];
203 var u32_a5 : f32 = s.a[0x80000000u];
204 var u32_a6 : f32 = s.a[0xffffffffu];
205
206 var u32_b1 : f32 = s.b[0u];
207 var u32_b2 : f32 = s.b[1u];
208 var u32_b3 : f32 = s.b[0x7ffffffeu];
209 var u32_b4 : f32 = s.b[0x7fffffffu];
210 var u32_b5 : f32 = s.b[0x80000000u];
211 var u32_b6 : f32 = s.b[0xffffffffu];
212 }
213 )";
214
215 auto* expect = R"(
216 [[block]]
217 struct S {
218 a : array<f32, 2147483647>;
219 b : array<f32>;
220 };
221
222 [[group(0), binding(0)]] var<storage, read> s : S;
223
224 fn f() {
225 var i32_a1 : f32 = s.a[2147483646];
226 var i32_a2 : f32 = s.a[1];
227 var i32_a3 : f32 = s.a[0];
228 var i32_a4 : f32 = s.a[0];
229 var i32_a5 : f32 = s.a[0];
230 var i32_b1 : f32 = s.b[min(2147483646u, (arrayLength(&(s.b)) - 1u))];
231 var i32_b2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
232 var i32_b3 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
233 var i32_b4 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
234 var i32_b5 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
235 var u32_a1 : f32 = s.a[0u];
236 var u32_a2 : f32 = s.a[1u];
237 var u32_a3 : f32 = s.a[2147483646u];
238 var u32_a4 : f32 = s.a[2147483646u];
239 var u32_a5 : f32 = s.a[2147483646u];
240 var u32_a6 : f32 = s.a[2147483646u];
241 var u32_b1 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
242 var u32_b2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
243 var u32_b3 : f32 = s.b[min(2147483646u, (arrayLength(&(s.b)) - 1u))];
244 var u32_b4 : f32 = s.b[min(2147483647u, (arrayLength(&(s.b)) - 1u))];
245 var u32_b5 : f32 = s.b[min(2147483648u, (arrayLength(&(s.b)) - 1u))];
246 var u32_b6 : f32 = s.b[min(4294967295u, (arrayLength(&(s.b)) - 1u))];
247 }
248 )";
249
250 auto got = Run<Robustness>(src);
251
252 EXPECT_EQ(expect, str(got));
253 }
254
TEST_F(RobustnessTest,Vector_Idx_Scalar)255 TEST_F(RobustnessTest, Vector_Idx_Scalar) {
256 auto* src = R"(
257 var<private> a : vec3<f32>;
258
259 fn f() {
260 var b : f32 = a[1];
261 }
262 )";
263
264 auto* expect = R"(
265 var<private> a : vec3<f32>;
266
267 fn f() {
268 var b : f32 = a[1];
269 }
270 )";
271
272 auto got = Run<Robustness>(src);
273
274 EXPECT_EQ(expect, str(got));
275 }
276
TEST_F(RobustnessTest,Vector_Idx_Expr)277 TEST_F(RobustnessTest, Vector_Idx_Expr) {
278 auto* src = R"(
279 var<private> a : vec3<f32>;
280
281 var<private> c : i32;
282
283 fn f() {
284 var b : f32 = a[c + 2 - 3];
285 }
286 )";
287
288 auto* expect = R"(
289 var<private> a : vec3<f32>;
290
291 var<private> c : i32;
292
293 fn f() {
294 var b : f32 = a[min(u32(((c + 2) - 3)), 2u)];
295 }
296 )";
297
298 auto got = Run<Robustness>(src);
299
300 EXPECT_EQ(expect, str(got));
301 }
302
TEST_F(RobustnessTest,Vector_Swizzle_Idx_Scalar)303 TEST_F(RobustnessTest, Vector_Swizzle_Idx_Scalar) {
304 auto* src = R"(
305 var<private> a : vec3<f32>;
306
307 fn f() {
308 var b : f32 = a.xy[2];
309 }
310 )";
311
312 auto* expect = R"(
313 var<private> a : vec3<f32>;
314
315 fn f() {
316 var b : f32 = a.xy[1];
317 }
318 )";
319
320 auto got = Run<Robustness>(src);
321
322 EXPECT_EQ(expect, str(got));
323 }
324
TEST_F(RobustnessTest,Vector_Swizzle_Idx_Var)325 TEST_F(RobustnessTest, Vector_Swizzle_Idx_Var) {
326 auto* src = R"(
327 var<private> a : vec3<f32>;
328
329 var<private> c : i32;
330
331 fn f() {
332 var b : f32 = a.xy[c];
333 }
334 )";
335
336 auto* expect = R"(
337 var<private> a : vec3<f32>;
338
339 var<private> c : i32;
340
341 fn f() {
342 var b : f32 = a.xy[min(u32(c), 1u)];
343 }
344 )";
345
346 auto got = Run<Robustness>(src);
347
348 EXPECT_EQ(expect, str(got));
349 }
TEST_F(RobustnessTest,Vector_Swizzle_Idx_Expr)350 TEST_F(RobustnessTest, Vector_Swizzle_Idx_Expr) {
351 auto* src = R"(
352 var<private> a : vec3<f32>;
353
354 var<private> c : i32;
355
356 fn f() {
357 var b : f32 = a.xy[c + 2 - 3];
358 }
359 )";
360
361 auto* expect = R"(
362 var<private> a : vec3<f32>;
363
364 var<private> c : i32;
365
366 fn f() {
367 var b : f32 = a.xy[min(u32(((c + 2) - 3)), 1u)];
368 }
369 )";
370
371 auto got = Run<Robustness>(src);
372
373 EXPECT_EQ(expect, str(got));
374 }
375
TEST_F(RobustnessTest,Vector_Idx_Negative)376 TEST_F(RobustnessTest, Vector_Idx_Negative) {
377 auto* src = R"(
378 var<private> a : vec3<f32>;
379
380 fn f() {
381 var b : f32 = a[-1];
382 }
383 )";
384
385 auto* expect = R"(
386 var<private> a : vec3<f32>;
387
388 fn f() {
389 var b : f32 = a[0];
390 }
391 )";
392
393 auto got = Run<Robustness>(src);
394
395 EXPECT_EQ(expect, str(got));
396 }
397
TEST_F(RobustnessTest,Vector_Idx_OutOfBounds)398 TEST_F(RobustnessTest, Vector_Idx_OutOfBounds) {
399 auto* src = R"(
400 var<private> a : vec3<f32>;
401
402 fn f() {
403 var b : f32 = a[3];
404 }
405 )";
406
407 auto* expect = R"(
408 var<private> a : vec3<f32>;
409
410 fn f() {
411 var b : f32 = a[2];
412 }
413 )";
414
415 auto got = Run<Robustness>(src);
416
417 EXPECT_EQ(expect, str(got));
418 }
419
TEST_F(RobustnessTest,Matrix_Idx_Scalar)420 TEST_F(RobustnessTest, Matrix_Idx_Scalar) {
421 auto* src = R"(
422 var<private> a : mat3x2<f32>;
423
424 fn f() {
425 var b : f32 = a[2][1];
426 }
427 )";
428
429 auto* expect = R"(
430 var<private> a : mat3x2<f32>;
431
432 fn f() {
433 var b : f32 = a[2][1];
434 }
435 )";
436
437 auto got = Run<Robustness>(src);
438
439 EXPECT_EQ(expect, str(got));
440 }
441
TEST_F(RobustnessTest,Matrix_Idx_Expr_Column)442 TEST_F(RobustnessTest, Matrix_Idx_Expr_Column) {
443 auto* src = R"(
444 var<private> a : mat3x2<f32>;
445
446 var<private> c : i32;
447
448 fn f() {
449 var b : f32 = a[c + 2 - 3][1];
450 }
451 )";
452
453 auto* expect = R"(
454 var<private> a : mat3x2<f32>;
455
456 var<private> c : i32;
457
458 fn f() {
459 var b : f32 = a[min(u32(((c + 2) - 3)), 2u)][1];
460 }
461 )";
462
463 auto got = Run<Robustness>(src);
464
465 EXPECT_EQ(expect, str(got));
466 }
467
TEST_F(RobustnessTest,Matrix_Idx_Expr_Row)468 TEST_F(RobustnessTest, Matrix_Idx_Expr_Row) {
469 auto* src = R"(
470 var<private> a : mat3x2<f32>;
471
472 var<private> c : i32;
473
474 fn f() {
475 var b : f32 = a[1][c + 2 - 3];
476 }
477 )";
478
479 auto* expect = R"(
480 var<private> a : mat3x2<f32>;
481
482 var<private> c : i32;
483
484 fn f() {
485 var b : f32 = a[1][min(u32(((c + 2) - 3)), 1u)];
486 }
487 )";
488
489 auto got = Run<Robustness>(src);
490
491 EXPECT_EQ(expect, str(got));
492 }
493
TEST_F(RobustnessTest,Matrix_Idx_Negative_Column)494 TEST_F(RobustnessTest, Matrix_Idx_Negative_Column) {
495 auto* src = R"(
496 var<private> a : mat3x2<f32>;
497
498 fn f() {
499 var b : f32 = a[-1][1];
500 }
501 )";
502
503 auto* expect = R"(
504 var<private> a : mat3x2<f32>;
505
506 fn f() {
507 var b : f32 = a[0][1];
508 }
509 )";
510
511 auto got = Run<Robustness>(src);
512
513 EXPECT_EQ(expect, str(got));
514 }
515
TEST_F(RobustnessTest,Matrix_Idx_Negative_Row)516 TEST_F(RobustnessTest, Matrix_Idx_Negative_Row) {
517 auto* src = R"(
518 var<private> a : mat3x2<f32>;
519
520 fn f() {
521 var b : f32 = a[2][-1];
522 }
523 )";
524
525 auto* expect = R"(
526 var<private> a : mat3x2<f32>;
527
528 fn f() {
529 var b : f32 = a[2][0];
530 }
531 )";
532
533 auto got = Run<Robustness>(src);
534
535 EXPECT_EQ(expect, str(got));
536 }
537
TEST_F(RobustnessTest,Matrix_Idx_OutOfBounds_Column)538 TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Column) {
539 auto* src = R"(
540 var<private> a : mat3x2<f32>;
541
542 fn f() {
543 var b : f32 = a[5][1];
544 }
545 )";
546
547 auto* expect = R"(
548 var<private> a : mat3x2<f32>;
549
550 fn f() {
551 var b : f32 = a[2][1];
552 }
553 )";
554
555 auto got = Run<Robustness>(src);
556
557 EXPECT_EQ(expect, str(got));
558 }
559
TEST_F(RobustnessTest,Matrix_Idx_OutOfBounds_Row)560 TEST_F(RobustnessTest, Matrix_Idx_OutOfBounds_Row) {
561 auto* src = R"(
562 var<private> a : mat3x2<f32>;
563
564 fn f() {
565 var b : f32 = a[2][5];
566 }
567 )";
568
569 auto* expect = R"(
570 var<private> a : mat3x2<f32>;
571
572 fn f() {
573 var b : f32 = a[2][1];
574 }
575 )";
576
577 auto got = Run<Robustness>(src);
578
579 EXPECT_EQ(expect, str(got));
580 }
581
582 // TODO(dsinclair): Implement when constant_id exists
TEST_F(RobustnessTest,DISABLED_Vector_Constant_Id_Clamps)583 TEST_F(RobustnessTest, DISABLED_Vector_Constant_Id_Clamps) {
584 // [[override(1300)]] let idx : i32;
585 // var a : vec3<f32>
586 // var b : f32 = a[idx]
587 //
588 // ->var b : f32 = a[min(u32(idx), 2)]
589 }
590
591 // TODO(dsinclair): Implement when constant_id exists
TEST_F(RobustnessTest,DISABLED_Array_Constant_Id_Clamps)592 TEST_F(RobustnessTest, DISABLED_Array_Constant_Id_Clamps) {
593 // [[override(1300)]] let idx : i32;
594 // var a : array<f32, 4>
595 // var b : f32 = a[idx]
596 //
597 // -> var b : f32 = a[min(u32(idx), 3)]
598 }
599
600 // TODO(dsinclair): Implement when constant_id exists
TEST_F(RobustnessTest,DISABLED_Matrix_Column_Constant_Id_Clamps)601 TEST_F(RobustnessTest, DISABLED_Matrix_Column_Constant_Id_Clamps) {
602 // [[override(1300)]] let idx : i32;
603 // var a : mat3x2<f32>
604 // var b : f32 = a[idx][1]
605 //
606 // -> var b : f32 = a[min(u32(idx), 2)][1]
607 }
608
609 // TODO(dsinclair): Implement when constant_id exists
TEST_F(RobustnessTest,DISABLED_Matrix_Row_Constant_Id_Clamps)610 TEST_F(RobustnessTest, DISABLED_Matrix_Row_Constant_Id_Clamps) {
611 // [[override(1300)]] let idx : i32;
612 // var a : mat3x2<f32>
613 // var b : f32 = a[1][idx]
614 //
615 // -> var b : f32 = a[1][min(u32(idx), 0, 1)]
616 }
617
TEST_F(RobustnessTest,RuntimeArray_Clamps)618 TEST_F(RobustnessTest, RuntimeArray_Clamps) {
619 auto* src = R"(
620 [[block]]
621 struct S {
622 a : f32;
623 b : array<f32>;
624 };
625 [[group(0), binding(0)]] var<storage, read> s : S;
626
627 fn f() {
628 var d : f32 = s.b[25];
629 }
630 )";
631
632 auto* expect = R"(
633 [[block]]
634 struct S {
635 a : f32;
636 b : array<f32>;
637 };
638
639 [[group(0), binding(0)]] var<storage, read> s : S;
640
641 fn f() {
642 var d : f32 = s.b[min(25u, (arrayLength(&(s.b)) - 1u))];
643 }
644 )";
645
646 auto got = Run<Robustness>(src);
647
648 EXPECT_EQ(expect, str(got));
649 }
650
651 // TODO(dsinclair): Clamp atomics when available.
TEST_F(RobustnessTest,DISABLED_Atomics_Clamp)652 TEST_F(RobustnessTest, DISABLED_Atomics_Clamp) {
653 FAIL();
654 }
655
656 // Clamp textureLoad() coord, array_index and level values
TEST_F(RobustnessTest,TextureLoad_Clamp)657 TEST_F(RobustnessTest, TextureLoad_Clamp) {
658 auto* src = R"(
659 [[group(0), binding(0)]] var tex_1d : texture_1d<f32>;
660 [[group(0), binding(0)]] var tex_2d : texture_2d<f32>;
661 [[group(0), binding(0)]] var tex_2d_arr : texture_2d_array<f32>;
662 [[group(0), binding(0)]] var tex_3d : texture_3d<f32>;
663 [[group(0), binding(0)]] var tex_ms_2d : texture_multisampled_2d<f32>;
664 [[group(0), binding(0)]] var tex_depth_2d : texture_depth_2d;
665 [[group(0), binding(0)]] var tex_depth_2d_arr : texture_depth_2d_array;
666 [[group(0), binding(0)]] var tex_external : texture_external;
667
668 fn f() {
669 var array_idx : i32;
670 var level_idx : i32;
671 var sample_idx : i32;
672
673 textureLoad(tex_1d, 1, level_idx);
674 textureLoad(tex_2d, vec2<i32>(1, 2), level_idx);
675 textureLoad(tex_2d_arr, vec2<i32>(1, 2), array_idx, level_idx);
676 textureLoad(tex_3d, vec3<i32>(1, 2, 3), level_idx);
677 textureLoad(tex_ms_2d, vec2<i32>(1, 2), sample_idx);
678 textureLoad(tex_depth_2d, vec2<i32>(1, 2), level_idx);
679 textureLoad(tex_depth_2d_arr, vec2<i32>(1, 2), array_idx, level_idx);
680 textureLoad(tex_external, vec2<i32>(1, 2));
681 }
682 )";
683
684 auto* expect =
685 R"(
686 [[group(0), binding(0)]] var tex_1d : texture_1d<f32>;
687
688 [[group(0), binding(0)]] var tex_2d : texture_2d<f32>;
689
690 [[group(0), binding(0)]] var tex_2d_arr : texture_2d_array<f32>;
691
692 [[group(0), binding(0)]] var tex_3d : texture_3d<f32>;
693
694 [[group(0), binding(0)]] var tex_ms_2d : texture_multisampled_2d<f32>;
695
696 [[group(0), binding(0)]] var tex_depth_2d : texture_depth_2d;
697
698 [[group(0), binding(0)]] var tex_depth_2d_arr : texture_depth_2d_array;
699
700 [[group(0), binding(0)]] var tex_external : texture_external;
701
702 fn f() {
703 var array_idx : i32;
704 var level_idx : i32;
705 var sample_idx : i32;
706 textureLoad(tex_1d, clamp(1, i32(), (textureDimensions(tex_1d, clamp(level_idx, 0, (textureNumLevels(tex_1d) - 1))) - i32(1))), clamp(level_idx, 0, (textureNumLevels(tex_1d) - 1)));
707 textureLoad(tex_2d, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_2d, clamp(level_idx, 0, (textureNumLevels(tex_2d) - 1))) - vec2<i32>(1))), clamp(level_idx, 0, (textureNumLevels(tex_2d) - 1)));
708 textureLoad(tex_2d_arr, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_2d_arr, clamp(level_idx, 0, (textureNumLevels(tex_2d_arr) - 1))) - vec2<i32>(1))), clamp(array_idx, 0, (textureNumLayers(tex_2d_arr) - 1)), clamp(level_idx, 0, (textureNumLevels(tex_2d_arr) - 1)));
709 textureLoad(tex_3d, clamp(vec3<i32>(1, 2, 3), vec3<i32>(), (textureDimensions(tex_3d, clamp(level_idx, 0, (textureNumLevels(tex_3d) - 1))) - vec3<i32>(1))), clamp(level_idx, 0, (textureNumLevels(tex_3d) - 1)));
710 textureLoad(tex_ms_2d, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_ms_2d) - vec2<i32>(1))), sample_idx);
711 textureLoad(tex_depth_2d, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_depth_2d, clamp(level_idx, 0, (textureNumLevels(tex_depth_2d) - 1))) - vec2<i32>(1))), clamp(level_idx, 0, (textureNumLevels(tex_depth_2d) - 1)));
712 textureLoad(tex_depth_2d_arr, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_depth_2d_arr, clamp(level_idx, 0, (textureNumLevels(tex_depth_2d_arr) - 1))) - vec2<i32>(1))), clamp(array_idx, 0, (textureNumLayers(tex_depth_2d_arr) - 1)), clamp(level_idx, 0, (textureNumLevels(tex_depth_2d_arr) - 1)));
713 textureLoad(tex_external, clamp(vec2<i32>(1, 2), vec2<i32>(), (textureDimensions(tex_external) - vec2<i32>(1))));
714 }
715 )";
716
717 auto got = Run<Robustness>(src);
718
719 EXPECT_EQ(expect, str(got));
720 }
721
722 // Clamp textureStore() coord, array_index and level values
TEST_F(RobustnessTest,TextureStore_Clamp)723 TEST_F(RobustnessTest, TextureStore_Clamp) {
724 auto* src = R"(
725 [[group(0), binding(0)]] var tex1d : texture_storage_1d<rgba8sint, write>;
726
727 [[group(0), binding(1)]] var tex2d : texture_storage_2d<rgba8sint, write>;
728
729 [[group(0), binding(2)]] var tex2d_arr : texture_storage_2d_array<rgba8sint, write>;
730
731 [[group(0), binding(3)]] var tex3d : texture_storage_3d<rgba8sint, write>;
732
733 fn f() {
734 textureStore(tex1d, 10, vec4<i32>());
735 textureStore(tex2d, vec2<i32>(10, 20), vec4<i32>());
736 textureStore(tex2d_arr, vec2<i32>(10, 20), 50, vec4<i32>());
737 textureStore(tex3d, vec3<i32>(10, 20, 30), vec4<i32>());
738 }
739 )";
740
741 auto* expect = R"(
742 [[group(0), binding(0)]] var tex1d : texture_storage_1d<rgba8sint, write>;
743
744 [[group(0), binding(1)]] var tex2d : texture_storage_2d<rgba8sint, write>;
745
746 [[group(0), binding(2)]] var tex2d_arr : texture_storage_2d_array<rgba8sint, write>;
747
748 [[group(0), binding(3)]] var tex3d : texture_storage_3d<rgba8sint, write>;
749
750 fn f() {
751 textureStore(tex1d, clamp(10, i32(), (textureDimensions(tex1d) - i32(1))), vec4<i32>());
752 textureStore(tex2d, clamp(vec2<i32>(10, 20), vec2<i32>(), (textureDimensions(tex2d) - vec2<i32>(1))), vec4<i32>());
753 textureStore(tex2d_arr, clamp(vec2<i32>(10, 20), vec2<i32>(), (textureDimensions(tex2d_arr) - vec2<i32>(1))), clamp(50, 0, (textureNumLayers(tex2d_arr) - 1)), vec4<i32>());
754 textureStore(tex3d, clamp(vec3<i32>(10, 20, 30), vec3<i32>(), (textureDimensions(tex3d) - vec3<i32>(1))), vec4<i32>());
755 }
756 )";
757
758 auto got = Run<Robustness>(src);
759
760 EXPECT_EQ(expect, str(got));
761 }
762
763 // TODO(dsinclair): Test for scoped variables when shadowing is implemented
TEST_F(RobustnessTest,DISABLED_Shadowed_Variable)764 TEST_F(RobustnessTest, DISABLED_Shadowed_Variable) {
765 // var a : array<f32, 3>;
766 // var i : u32;
767 // {
768 // var a : array<f32, 5>;
769 // var b : f32 = a[i];
770 // }
771 // var c : f32 = a[i];
772 //
773 // -> var b : f32 = a[min(u32(i), 4)];
774 // var c : f32 = a[min(u32(i), 2)];
775 FAIL();
776 }
777
778 // Check that existing use of min() and arrayLength() do not get renamed.
TEST_F(RobustnessTest,DontRenameSymbols)779 TEST_F(RobustnessTest, DontRenameSymbols) {
780 auto* src = R"(
781 [[block]]
782 struct S {
783 a : f32;
784 b : array<f32>;
785 };
786
787 [[group(0), binding(0)]] var<storage, read> s : S;
788
789 let c : u32 = 1u;
790
791 fn f() {
792 let b : f32 = s.b[c];
793 let x : i32 = min(1, 2);
794 let y : u32 = arrayLength(&s.b);
795 }
796 )";
797
798 auto* expect = R"(
799 [[block]]
800 struct S {
801 a : f32;
802 b : array<f32>;
803 };
804
805 [[group(0), binding(0)]] var<storage, read> s : S;
806
807 let c : u32 = 1u;
808
809 fn f() {
810 let b : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
811 let x : i32 = min(1, 2);
812 let y : u32 = arrayLength(&(s.b));
813 }
814 )";
815
816 auto got = Run<Robustness>(src);
817
818 EXPECT_EQ(expect, str(got));
819 }
820
821 const char* kOmitSourceShader = R"(
822 [[block]]
823 struct S {
824 a : array<f32, 4>;
825 b : array<f32>;
826 };
827 [[group(0), binding(0)]] var<storage, read> s : S;
828
829 type UArr = [[stride(16)]] array<f32, 4>;
830 [[block]] struct U {
831 a : UArr;
832 };
833 [[group(1), binding(0)]] var<uniform> u : U;
834
835 fn f() {
836 // Signed
837 var i32_sa1 : f32 = s.a[4];
838 var i32_sa2 : f32 = s.a[1];
839 var i32_sa3 : f32 = s.a[0];
840 var i32_sa4 : f32 = s.a[-1];
841 var i32_sa5 : f32 = s.a[-4];
842
843 var i32_sb1 : f32 = s.b[4];
844 var i32_sb2 : f32 = s.b[1];
845 var i32_sb3 : f32 = s.b[0];
846 var i32_sb4 : f32 = s.b[-1];
847 var i32_sb5 : f32 = s.b[-4];
848
849 var i32_ua1 : f32 = u.a[4];
850 var i32_ua2 : f32 = u.a[1];
851 var i32_ua3 : f32 = u.a[0];
852 var i32_ua4 : f32 = u.a[-1];
853 var i32_ua5 : f32 = u.a[-4];
854
855 // Unsigned
856 var u32_sa1 : f32 = s.a[0u];
857 var u32_sa2 : f32 = s.a[1u];
858 var u32_sa3 : f32 = s.a[3u];
859 var u32_sa4 : f32 = s.a[4u];
860 var u32_sa5 : f32 = s.a[10u];
861 var u32_sa6 : f32 = s.a[100u];
862
863 var u32_sb1 : f32 = s.b[0u];
864 var u32_sb2 : f32 = s.b[1u];
865 var u32_sb3 : f32 = s.b[3u];
866 var u32_sb4 : f32 = s.b[4u];
867 var u32_sb5 : f32 = s.b[10u];
868 var u32_sb6 : f32 = s.b[100u];
869
870 var u32_ua1 : f32 = u.a[0u];
871 var u32_ua2 : f32 = u.a[1u];
872 var u32_ua3 : f32 = u.a[3u];
873 var u32_ua4 : f32 = u.a[4u];
874 var u32_ua5 : f32 = u.a[10u];
875 var u32_ua6 : f32 = u.a[100u];
876 }
877 )";
878
TEST_F(RobustnessTest,OmitNone)879 TEST_F(RobustnessTest, OmitNone) {
880 auto* expect = R"(
881 [[block]]
882 struct S {
883 a : array<f32, 4>;
884 b : array<f32>;
885 };
886
887 [[group(0), binding(0)]] var<storage, read> s : S;
888
889 type UArr = [[stride(16)]] array<f32, 4>;
890
891 [[block]]
892 struct U {
893 a : UArr;
894 };
895
896 [[group(1), binding(0)]] var<uniform> u : U;
897
898 fn f() {
899 var i32_sa1 : f32 = s.a[3];
900 var i32_sa2 : f32 = s.a[1];
901 var i32_sa3 : f32 = s.a[0];
902 var i32_sa4 : f32 = s.a[0];
903 var i32_sa5 : f32 = s.a[0];
904 var i32_sb1 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
905 var i32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
906 var i32_sb3 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
907 var i32_sb4 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
908 var i32_sb5 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
909 var i32_ua1 : f32 = u.a[3];
910 var i32_ua2 : f32 = u.a[1];
911 var i32_ua3 : f32 = u.a[0];
912 var i32_ua4 : f32 = u.a[0];
913 var i32_ua5 : f32 = u.a[0];
914 var u32_sa1 : f32 = s.a[0u];
915 var u32_sa2 : f32 = s.a[1u];
916 var u32_sa3 : f32 = s.a[3u];
917 var u32_sa4 : f32 = s.a[3u];
918 var u32_sa5 : f32 = s.a[3u];
919 var u32_sa6 : f32 = s.a[3u];
920 var u32_sb1 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
921 var u32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
922 var u32_sb3 : f32 = s.b[min(3u, (arrayLength(&(s.b)) - 1u))];
923 var u32_sb4 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
924 var u32_sb5 : f32 = s.b[min(10u, (arrayLength(&(s.b)) - 1u))];
925 var u32_sb6 : f32 = s.b[min(100u, (arrayLength(&(s.b)) - 1u))];
926 var u32_ua1 : f32 = u.a[0u];
927 var u32_ua2 : f32 = u.a[1u];
928 var u32_ua3 : f32 = u.a[3u];
929 var u32_ua4 : f32 = u.a[3u];
930 var u32_ua5 : f32 = u.a[3u];
931 var u32_ua6 : f32 = u.a[3u];
932 }
933 )";
934
935 Robustness::Config cfg;
936 DataMap data;
937 data.Add<Robustness::Config>(cfg);
938
939 auto got = Run<Robustness>(kOmitSourceShader, data);
940
941 EXPECT_EQ(expect, str(got));
942 }
943
TEST_F(RobustnessTest,OmitStorage)944 TEST_F(RobustnessTest, OmitStorage) {
945 auto* expect = R"(
946 [[block]]
947 struct S {
948 a : array<f32, 4>;
949 b : array<f32>;
950 };
951
952 [[group(0), binding(0)]] var<storage, read> s : S;
953
954 type UArr = [[stride(16)]] array<f32, 4>;
955
956 [[block]]
957 struct U {
958 a : UArr;
959 };
960
961 [[group(1), binding(0)]] var<uniform> u : U;
962
963 fn f() {
964 var i32_sa1 : f32 = s.a[4];
965 var i32_sa2 : f32 = s.a[1];
966 var i32_sa3 : f32 = s.a[0];
967 var i32_sa4 : f32 = s.a[-1];
968 var i32_sa5 : f32 = s.a[-4];
969 var i32_sb1 : f32 = s.b[4];
970 var i32_sb2 : f32 = s.b[1];
971 var i32_sb3 : f32 = s.b[0];
972 var i32_sb4 : f32 = s.b[-1];
973 var i32_sb5 : f32 = s.b[-4];
974 var i32_ua1 : f32 = u.a[3];
975 var i32_ua2 : f32 = u.a[1];
976 var i32_ua3 : f32 = u.a[0];
977 var i32_ua4 : f32 = u.a[0];
978 var i32_ua5 : f32 = u.a[0];
979 var u32_sa1 : f32 = s.a[0u];
980 var u32_sa2 : f32 = s.a[1u];
981 var u32_sa3 : f32 = s.a[3u];
982 var u32_sa4 : f32 = s.a[4u];
983 var u32_sa5 : f32 = s.a[10u];
984 var u32_sa6 : f32 = s.a[100u];
985 var u32_sb1 : f32 = s.b[0u];
986 var u32_sb2 : f32 = s.b[1u];
987 var u32_sb3 : f32 = s.b[3u];
988 var u32_sb4 : f32 = s.b[4u];
989 var u32_sb5 : f32 = s.b[10u];
990 var u32_sb6 : f32 = s.b[100u];
991 var u32_ua1 : f32 = u.a[0u];
992 var u32_ua2 : f32 = u.a[1u];
993 var u32_ua3 : f32 = u.a[3u];
994 var u32_ua4 : f32 = u.a[3u];
995 var u32_ua5 : f32 = u.a[3u];
996 var u32_ua6 : f32 = u.a[3u];
997 }
998 )";
999
1000 Robustness::Config cfg;
1001 cfg.omitted_classes.insert(Robustness::StorageClass::kStorage);
1002
1003 DataMap data;
1004 data.Add<Robustness::Config>(cfg);
1005
1006 auto got = Run<Robustness>(kOmitSourceShader, data);
1007
1008 EXPECT_EQ(expect, str(got));
1009 }
1010
TEST_F(RobustnessTest,OmitUniform)1011 TEST_F(RobustnessTest, OmitUniform) {
1012 auto* expect = R"(
1013 [[block]]
1014 struct S {
1015 a : array<f32, 4>;
1016 b : array<f32>;
1017 };
1018
1019 [[group(0), binding(0)]] var<storage, read> s : S;
1020
1021 type UArr = [[stride(16)]] array<f32, 4>;
1022
1023 [[block]]
1024 struct U {
1025 a : UArr;
1026 };
1027
1028 [[group(1), binding(0)]] var<uniform> u : U;
1029
1030 fn f() {
1031 var i32_sa1 : f32 = s.a[3];
1032 var i32_sa2 : f32 = s.a[1];
1033 var i32_sa3 : f32 = s.a[0];
1034 var i32_sa4 : f32 = s.a[0];
1035 var i32_sa5 : f32 = s.a[0];
1036 var i32_sb1 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
1037 var i32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
1038 var i32_sb3 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
1039 var i32_sb4 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
1040 var i32_sb5 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
1041 var i32_ua1 : f32 = u.a[4];
1042 var i32_ua2 : f32 = u.a[1];
1043 var i32_ua3 : f32 = u.a[0];
1044 var i32_ua4 : f32 = u.a[-1];
1045 var i32_ua5 : f32 = u.a[-4];
1046 var u32_sa1 : f32 = s.a[0u];
1047 var u32_sa2 : f32 = s.a[1u];
1048 var u32_sa3 : f32 = s.a[3u];
1049 var u32_sa4 : f32 = s.a[3u];
1050 var u32_sa5 : f32 = s.a[3u];
1051 var u32_sa6 : f32 = s.a[3u];
1052 var u32_sb1 : f32 = s.b[min(0u, (arrayLength(&(s.b)) - 1u))];
1053 var u32_sb2 : f32 = s.b[min(1u, (arrayLength(&(s.b)) - 1u))];
1054 var u32_sb3 : f32 = s.b[min(3u, (arrayLength(&(s.b)) - 1u))];
1055 var u32_sb4 : f32 = s.b[min(4u, (arrayLength(&(s.b)) - 1u))];
1056 var u32_sb5 : f32 = s.b[min(10u, (arrayLength(&(s.b)) - 1u))];
1057 var u32_sb6 : f32 = s.b[min(100u, (arrayLength(&(s.b)) - 1u))];
1058 var u32_ua1 : f32 = u.a[0u];
1059 var u32_ua2 : f32 = u.a[1u];
1060 var u32_ua3 : f32 = u.a[3u];
1061 var u32_ua4 : f32 = u.a[4u];
1062 var u32_ua5 : f32 = u.a[10u];
1063 var u32_ua6 : f32 = u.a[100u];
1064 }
1065 )";
1066
1067 Robustness::Config cfg;
1068 cfg.omitted_classes.insert(Robustness::StorageClass::kUniform);
1069
1070 DataMap data;
1071 data.Add<Robustness::Config>(cfg);
1072
1073 auto got = Run<Robustness>(kOmitSourceShader, data);
1074
1075 EXPECT_EQ(expect, str(got));
1076 }
1077
TEST_F(RobustnessTest,OmitBoth)1078 TEST_F(RobustnessTest, OmitBoth) {
1079 auto* expect = R"(
1080 [[block]]
1081 struct S {
1082 a : array<f32, 4>;
1083 b : array<f32>;
1084 };
1085
1086 [[group(0), binding(0)]] var<storage, read> s : S;
1087
1088 type UArr = [[stride(16)]] array<f32, 4>;
1089
1090 [[block]]
1091 struct U {
1092 a : UArr;
1093 };
1094
1095 [[group(1), binding(0)]] var<uniform> u : U;
1096
1097 fn f() {
1098 var i32_sa1 : f32 = s.a[4];
1099 var i32_sa2 : f32 = s.a[1];
1100 var i32_sa3 : f32 = s.a[0];
1101 var i32_sa4 : f32 = s.a[-1];
1102 var i32_sa5 : f32 = s.a[-4];
1103 var i32_sb1 : f32 = s.b[4];
1104 var i32_sb2 : f32 = s.b[1];
1105 var i32_sb3 : f32 = s.b[0];
1106 var i32_sb4 : f32 = s.b[-1];
1107 var i32_sb5 : f32 = s.b[-4];
1108 var i32_ua1 : f32 = u.a[4];
1109 var i32_ua2 : f32 = u.a[1];
1110 var i32_ua3 : f32 = u.a[0];
1111 var i32_ua4 : f32 = u.a[-1];
1112 var i32_ua5 : f32 = u.a[-4];
1113 var u32_sa1 : f32 = s.a[0u];
1114 var u32_sa2 : f32 = s.a[1u];
1115 var u32_sa3 : f32 = s.a[3u];
1116 var u32_sa4 : f32 = s.a[4u];
1117 var u32_sa5 : f32 = s.a[10u];
1118 var u32_sa6 : f32 = s.a[100u];
1119 var u32_sb1 : f32 = s.b[0u];
1120 var u32_sb2 : f32 = s.b[1u];
1121 var u32_sb3 : f32 = s.b[3u];
1122 var u32_sb4 : f32 = s.b[4u];
1123 var u32_sb5 : f32 = s.b[10u];
1124 var u32_sb6 : f32 = s.b[100u];
1125 var u32_ua1 : f32 = u.a[0u];
1126 var u32_ua2 : f32 = u.a[1u];
1127 var u32_ua3 : f32 = u.a[3u];
1128 var u32_ua4 : f32 = u.a[4u];
1129 var u32_ua5 : f32 = u.a[10u];
1130 var u32_ua6 : f32 = u.a[100u];
1131 }
1132 )";
1133
1134 Robustness::Config cfg;
1135 cfg.omitted_classes.insert(Robustness::StorageClass::kStorage);
1136 cfg.omitted_classes.insert(Robustness::StorageClass::kUniform);
1137
1138 DataMap data;
1139 data.Add<Robustness::Config>(cfg);
1140
1141 auto got = Run<Robustness>(kOmitSourceShader, data);
1142
1143 EXPECT_EQ(expect, str(got));
1144 }
1145
1146 } // namespace
1147 } // namespace transform
1148 } // namespace tint
1149