1 // Copyright (c) 2019 Google LLC
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 <functional>
16 #include <vector>
17
18 #include "gtest/gtest.h"
19 #include "source/fuzz/fuzzer.h"
20 #include "source/fuzz/fuzzer_util.h"
21 #include "source/fuzz/pseudo_random_generator.h"
22 #include "source/fuzz/shrinker.h"
23 #include "source/fuzz/uniform_buffer_element_descriptor.h"
24 #include "test/fuzz/fuzz_test_util.h"
25
26 namespace spvtools {
27 namespace fuzz {
28 namespace {
29
30 // The following SPIR-V came from this GLSL:
31 //
32 // #version 310 es
33 //
34 // void foo() {
35 // int x;
36 // x = 2;
37 // for (int i = 0; i < 100; i++) {
38 // x += i;
39 // x = x * 2;
40 // }
41 // return;
42 // }
43 //
44 // void main() {
45 // foo();
46 // for (int i = 0; i < 10; i++) {
47 // int j = 20;
48 // while(j > 0) {
49 // foo();
50 // j--;
51 // }
52 // do {
53 // i++;
54 // } while(i < 4);
55 // }
56 // }
57
58 const std::string kTestShader1 = R"(
59 OpCapability Shader
60 %1 = OpExtInstImport "GLSL.std.450"
61 OpMemoryModel Logical GLSL450
62 OpEntryPoint Fragment %4 "main"
63 OpExecutionMode %4 OriginUpperLeft
64 OpSource ESSL 310
65 OpName %4 "main"
66 OpName %6 "foo("
67 OpName %10 "x"
68 OpName %12 "i"
69 OpName %33 "i"
70 OpName %42 "j"
71 OpDecorate %10 RelaxedPrecision
72 OpDecorate %12 RelaxedPrecision
73 OpDecorate %19 RelaxedPrecision
74 OpDecorate %23 RelaxedPrecision
75 OpDecorate %24 RelaxedPrecision
76 OpDecorate %25 RelaxedPrecision
77 OpDecorate %26 RelaxedPrecision
78 OpDecorate %27 RelaxedPrecision
79 OpDecorate %28 RelaxedPrecision
80 OpDecorate %30 RelaxedPrecision
81 OpDecorate %33 RelaxedPrecision
82 OpDecorate %39 RelaxedPrecision
83 OpDecorate %42 RelaxedPrecision
84 OpDecorate %49 RelaxedPrecision
85 OpDecorate %52 RelaxedPrecision
86 OpDecorate %53 RelaxedPrecision
87 OpDecorate %58 RelaxedPrecision
88 OpDecorate %59 RelaxedPrecision
89 OpDecorate %60 RelaxedPrecision
90 OpDecorate %63 RelaxedPrecision
91 OpDecorate %64 RelaxedPrecision
92 %2 = OpTypeVoid
93 %3 = OpTypeFunction %2
94 %8 = OpTypeInt 32 1
95 %9 = OpTypePointer Function %8
96 %11 = OpConstant %8 2
97 %13 = OpConstant %8 0
98 %20 = OpConstant %8 100
99 %21 = OpTypeBool
100 %29 = OpConstant %8 1
101 %40 = OpConstant %8 10
102 %43 = OpConstant %8 20
103 %61 = OpConstant %8 4
104 %4 = OpFunction %2 None %3
105 %5 = OpLabel
106 %33 = OpVariable %9 Function
107 %42 = OpVariable %9 Function
108 %32 = OpFunctionCall %2 %6
109 OpStore %33 %13
110 OpBranch %34
111 %34 = OpLabel
112 OpLoopMerge %36 %37 None
113 OpBranch %38
114 %38 = OpLabel
115 %39 = OpLoad %8 %33
116 %41 = OpSLessThan %21 %39 %40
117 OpBranchConditional %41 %35 %36
118 %35 = OpLabel
119 OpStore %42 %43
120 OpBranch %44
121 %44 = OpLabel
122 OpLoopMerge %46 %47 None
123 OpBranch %48
124 %48 = OpLabel
125 %49 = OpLoad %8 %42
126 %50 = OpSGreaterThan %21 %49 %13
127 OpBranchConditional %50 %45 %46
128 %45 = OpLabel
129 %51 = OpFunctionCall %2 %6
130 %52 = OpLoad %8 %42
131 %53 = OpISub %8 %52 %29
132 OpStore %42 %53
133 OpBranch %47
134 %47 = OpLabel
135 OpBranch %44
136 %46 = OpLabel
137 OpBranch %54
138 %54 = OpLabel
139 OpLoopMerge %56 %57 None
140 OpBranch %55
141 %55 = OpLabel
142 %58 = OpLoad %8 %33
143 %59 = OpIAdd %8 %58 %29
144 OpStore %33 %59
145 OpBranch %57
146 %57 = OpLabel
147 %60 = OpLoad %8 %33
148 %62 = OpSLessThan %21 %60 %61
149 OpBranchConditional %62 %54 %56
150 %56 = OpLabel
151 OpBranch %37
152 %37 = OpLabel
153 %63 = OpLoad %8 %33
154 %64 = OpIAdd %8 %63 %29
155 OpStore %33 %64
156 OpBranch %34
157 %36 = OpLabel
158 OpReturn
159 OpFunctionEnd
160 %6 = OpFunction %2 None %3
161 %7 = OpLabel
162 %10 = OpVariable %9 Function
163 %12 = OpVariable %9 Function
164 OpStore %10 %11
165 OpStore %12 %13
166 OpBranch %14
167 %14 = OpLabel
168 OpLoopMerge %16 %17 None
169 OpBranch %18
170 %18 = OpLabel
171 %19 = OpLoad %8 %12
172 %22 = OpSLessThan %21 %19 %20
173 OpBranchConditional %22 %15 %16
174 %15 = OpLabel
175 %23 = OpLoad %8 %12
176 %24 = OpLoad %8 %10
177 %25 = OpIAdd %8 %24 %23
178 OpStore %10 %25
179 %26 = OpLoad %8 %10
180 %27 = OpIMul %8 %26 %11
181 OpStore %10 %27
182 OpBranch %17
183 %17 = OpLabel
184 %28 = OpLoad %8 %12
185 %30 = OpIAdd %8 %28 %29
186 OpStore %12 %30
187 OpBranch %14
188 %16 = OpLabel
189 OpReturn
190 OpFunctionEnd
191
192 )";
193
194 // The following SPIR-V came from this GLSL, which was then optimized using
195 // spirv-opt with the -O argument:
196 //
197 // #version 310 es
198 //
199 // precision highp float;
200 //
201 // layout(location = 0) out vec4 _GLF_color;
202 //
203 // layout(set = 0, binding = 0) uniform buf0 {
204 // vec2 injectionSwitch;
205 // };
206 // layout(set = 0, binding = 1) uniform buf1 {
207 // vec2 resolution;
208 // };
209 // bool checkSwap(float a, float b)
210 // {
211 // return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b;
212 // }
213 // void main()
214 // {
215 // float data[10];
216 // for(int i = 0; i < 10; i++)
217 // {
218 // data[i] = float(10 - i) * injectionSwitch.y;
219 // }
220 // for(int i = 0; i < 9; i++)
221 // {
222 // for(int j = 0; j < 10; j++)
223 // {
224 // if(j < i + 1)
225 // {
226 // continue;
227 // }
228 // bool doSwap = checkSwap(data[i], data[j]);
229 // if(doSwap)
230 // {
231 // float temp = data[i];
232 // data[i] = data[j];
233 // data[j] = temp;
234 // }
235 // }
236 // }
237 // if(gl_FragCoord.x < resolution.x / 2.0)
238 // {
239 // _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0);
240 // }
241 // else
242 // {
243 // _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0);
244 // }
245 // }
246
247 const std::string kTestShader2 = R"(
248 OpCapability Shader
249 %1 = OpExtInstImport "GLSL.std.450"
250 OpMemoryModel Logical GLSL450
251 OpEntryPoint Fragment %4 "main" %16 %139 %25 %68
252 OpExecutionMode %4 OriginUpperLeft
253 OpSource ESSL 310
254 OpName %4 "main"
255 OpName %16 "gl_FragCoord"
256 OpName %23 "buf1"
257 OpMemberName %23 0 "resolution"
258 OpName %25 ""
259 OpName %61 "data"
260 OpName %66 "buf0"
261 OpMemberName %66 0 "injectionSwitch"
262 OpName %68 ""
263 OpName %139 "_GLF_color"
264 OpDecorate %16 BuiltIn FragCoord
265 OpMemberDecorate %23 0 Offset 0
266 OpDecorate %23 Block
267 OpDecorate %25 DescriptorSet 0
268 OpDecorate %25 Binding 1
269 OpDecorate %64 RelaxedPrecision
270 OpMemberDecorate %66 0 Offset 0
271 OpDecorate %66 Block
272 OpDecorate %68 DescriptorSet 0
273 OpDecorate %68 Binding 0
274 OpDecorate %75 RelaxedPrecision
275 OpDecorate %95 RelaxedPrecision
276 OpDecorate %126 RelaxedPrecision
277 OpDecorate %128 RelaxedPrecision
278 OpDecorate %139 Location 0
279 OpDecorate %182 RelaxedPrecision
280 OpDecorate %183 RelaxedPrecision
281 OpDecorate %184 RelaxedPrecision
282 %2 = OpTypeVoid
283 %3 = OpTypeFunction %2
284 %6 = OpTypeFloat 32
285 %7 = OpTypePointer Function %6
286 %8 = OpTypeBool
287 %14 = OpTypeVector %6 4
288 %15 = OpTypePointer Input %14
289 %16 = OpVariable %15 Input
290 %17 = OpTypeInt 32 0
291 %18 = OpConstant %17 1
292 %19 = OpTypePointer Input %6
293 %22 = OpTypeVector %6 2
294 %23 = OpTypeStruct %22
295 %24 = OpTypePointer Uniform %23
296 %25 = OpVariable %24 Uniform
297 %26 = OpTypeInt 32 1
298 %27 = OpConstant %26 0
299 %28 = OpTypePointer Uniform %6
300 %56 = OpConstant %26 10
301 %58 = OpConstant %17 10
302 %59 = OpTypeArray %6 %58
303 %60 = OpTypePointer Function %59
304 %66 = OpTypeStruct %22
305 %67 = OpTypePointer Uniform %66
306 %68 = OpVariable %67 Uniform
307 %74 = OpConstant %26 1
308 %83 = OpConstant %26 9
309 %129 = OpConstant %17 0
310 %138 = OpTypePointer Output %14
311 %139 = OpVariable %138 Output
312 %144 = OpConstant %26 5
313 %151 = OpConstant %6 1
314 %194 = OpConstant %6 0.5
315 %195 = OpConstant %6 0.100000001
316 %4 = OpFunction %2 None %3
317 %5 = OpLabel
318 %61 = OpVariable %60 Function
319 OpBranch %50
320 %50 = OpLabel
321 %182 = OpPhi %26 %27 %5 %75 %51
322 %57 = OpSLessThan %8 %182 %56
323 OpLoopMerge %52 %51 None
324 OpBranchConditional %57 %51 %52
325 %51 = OpLabel
326 %64 = OpISub %26 %56 %182
327 %65 = OpConvertSToF %6 %64
328 %69 = OpAccessChain %28 %68 %27 %18
329 %70 = OpLoad %6 %69
330 %71 = OpFMul %6 %65 %70
331 %72 = OpAccessChain %7 %61 %182
332 OpStore %72 %71
333 %75 = OpIAdd %26 %182 %74
334 OpBranch %50
335 %52 = OpLabel
336 OpBranch %77
337 %77 = OpLabel
338 %183 = OpPhi %26 %27 %52 %128 %88
339 %84 = OpSLessThan %8 %183 %83
340 OpLoopMerge %79 %88 None
341 OpBranchConditional %84 %78 %79
342 %78 = OpLabel
343 OpBranch %86
344 %86 = OpLabel
345 %184 = OpPhi %26 %27 %78 %126 %89
346 %92 = OpSLessThan %8 %184 %56
347 OpLoopMerge %1000 %89 None
348 OpBranchConditional %92 %87 %1000
349 %87 = OpLabel
350 %95 = OpIAdd %26 %183 %74
351 %96 = OpSLessThan %8 %184 %95
352 OpSelectionMerge %98 None
353 OpBranchConditional %96 %97 %98
354 %97 = OpLabel
355 OpBranch %89
356 %98 = OpLabel
357 %104 = OpAccessChain %7 %61 %183
358 %105 = OpLoad %6 %104
359 %107 = OpAccessChain %7 %61 %184
360 %108 = OpLoad %6 %107
361 %166 = OpAccessChain %19 %16 %18
362 %167 = OpLoad %6 %166
363 %168 = OpAccessChain %28 %25 %27 %18
364 %169 = OpLoad %6 %168
365 %170 = OpFMul %6 %169 %194
366 %171 = OpFOrdLessThan %8 %167 %170
367 OpSelectionMerge %172 None
368 OpBranchConditional %171 %173 %174
369 %173 = OpLabel
370 %177 = OpFOrdGreaterThan %8 %105 %108
371 OpBranch %172
372 %174 = OpLabel
373 %180 = OpFOrdLessThan %8 %105 %108
374 OpBranch %172
375 %172 = OpLabel
376 %186 = OpPhi %8 %177 %173 %180 %174
377 OpSelectionMerge %112 None
378 OpBranchConditional %186 %111 %112
379 %111 = OpLabel
380 %116 = OpLoad %6 %104
381 %120 = OpLoad %6 %107
382 OpStore %104 %120
383 OpStore %107 %116
384 OpBranch %112
385 %112 = OpLabel
386 OpBranch %89
387 %89 = OpLabel
388 %126 = OpIAdd %26 %184 %74
389 OpBranch %86
390 %1000 = OpLabel
391 OpBranch %88
392 %88 = OpLabel
393 %128 = OpIAdd %26 %183 %74
394 OpBranch %77
395 %79 = OpLabel
396 %130 = OpAccessChain %19 %16 %129
397 %131 = OpLoad %6 %130
398 %132 = OpAccessChain %28 %25 %27 %129
399 %133 = OpLoad %6 %132
400 %134 = OpFMul %6 %133 %194
401 %135 = OpFOrdLessThan %8 %131 %134
402 OpSelectionMerge %137 None
403 OpBranchConditional %135 %136 %153
404 %136 = OpLabel
405 %140 = OpAccessChain %7 %61 %27
406 %141 = OpLoad %6 %140
407 %143 = OpFMul %6 %141 %195
408 %145 = OpAccessChain %7 %61 %144
409 %146 = OpLoad %6 %145
410 %147 = OpFMul %6 %146 %195
411 %148 = OpAccessChain %7 %61 %83
412 %149 = OpLoad %6 %148
413 %150 = OpFMul %6 %149 %195
414 %152 = OpCompositeConstruct %14 %143 %147 %150 %151
415 OpStore %139 %152
416 OpBranch %137
417 %153 = OpLabel
418 %154 = OpAccessChain %7 %61 %144
419 %155 = OpLoad %6 %154
420 %156 = OpFMul %6 %155 %195
421 %157 = OpAccessChain %7 %61 %83
422 %158 = OpLoad %6 %157
423 %159 = OpFMul %6 %158 %195
424 %160 = OpAccessChain %7 %61 %27
425 %161 = OpLoad %6 %160
426 %162 = OpFMul %6 %161 %195
427 %163 = OpCompositeConstruct %14 %156 %159 %162 %151
428 OpStore %139 %163
429 OpBranch %137
430 %137 = OpLabel
431 OpReturn
432 OpFunctionEnd
433 )";
434
435 // The following SPIR-V came from this GLSL, which was then optimized using
436 // spirv-opt with the -O argument:
437 //
438 // #version 310 es
439 //
440 // precision highp float;
441 //
442 // layout(location = 0) out vec4 _GLF_color;
443 //
444 // layout(set = 0, binding = 0) uniform buf0 {
445 // vec2 resolution;
446 // };
447 // void main(void)
448 // {
449 // float A[50];
450 // for(
451 // int i = 0;
452 // i < 200;
453 // i ++
454 // )
455 // {
456 // if(i >= int(resolution.x))
457 // {
458 // break;
459 // }
460 // if((4 * (i / 4)) == i)
461 // {
462 // A[i / 4] = float(i);
463 // }
464 // }
465 // for(
466 // int i = 0;
467 // i < 50;
468 // i ++
469 // )
470 // {
471 // if(i < int(gl_FragCoord.x))
472 // {
473 // break;
474 // }
475 // if(i > 0)
476 // {
477 // A[i] += A[i - 1];
478 // }
479 // }
480 // if(int(gl_FragCoord.x) < 20)
481 // {
482 // _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0);
483 // }
484 // else
485 // if(int(gl_FragCoord.x) < 40)
486 // {
487 // _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0);
488 // }
489 // else
490 // if(int(gl_FragCoord.x) < 60)
491 // {
492 // _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y,
493 // 1.0, 1.0);
494 // }
495 // else
496 // if(int(gl_FragCoord.x) < 80)
497 // {
498 // _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y,
499 // 1.0, 1.0);
500 // }
501 // else
502 // if(int(gl_FragCoord.x) < 100)
503 // {
504 // _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y,
505 // 1.0, 1.0);
506 // }
507 // else
508 // if(int(gl_FragCoord.x) < 120)
509 // {
510 // _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y,
511 // 1.0, 1.0);
512 // }
513 // else
514 // if(int(gl_FragCoord.x) < 140)
515 // {
516 // _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y,
517 // 1.0, 1.0);
518 // }
519 // else
520 // if(int(gl_FragCoord.x) < 160)
521 // {
522 // _GLF_color = vec4(A[35] / resolution.x, A[39] /
523 // resolution.y, 1.0, 1.0);
524 // }
525 // else
526 // if(int(gl_FragCoord.x) < 180)
527 // {
528 // _GLF_color = vec4(A[40] / resolution.x, A[44] /
529 // resolution.y, 1.0, 1.0);
530 // }
531 // else
532 // if(int(gl_FragCoord.x) < 180)
533 // {
534 // _GLF_color = vec4(A[45] / resolution.x, A[49] /
535 // resolution.y, 1.0, 1.0);
536 // }
537 // else
538 // {
539 // discard;
540 // }
541 // }
542
543 const std::string kTestShader3 = R"(
544 OpCapability Shader
545 %1 = OpExtInstImport "GLSL.std.450"
546 OpMemoryModel Logical GLSL450
547 OpEntryPoint Fragment %4 "main" %68 %100 %24
548 OpExecutionMode %4 OriginUpperLeft
549 OpSource ESSL 310
550 OpName %4 "main"
551 OpName %22 "buf0"
552 OpMemberName %22 0 "resolution"
553 OpName %24 ""
554 OpName %46 "A"
555 OpName %68 "gl_FragCoord"
556 OpName %100 "_GLF_color"
557 OpMemberDecorate %22 0 Offset 0
558 OpDecorate %22 Block
559 OpDecorate %24 DescriptorSet 0
560 OpDecorate %24 Binding 0
561 OpDecorate %37 RelaxedPrecision
562 OpDecorate %38 RelaxedPrecision
563 OpDecorate %55 RelaxedPrecision
564 OpDecorate %68 BuiltIn FragCoord
565 OpDecorate %83 RelaxedPrecision
566 OpDecorate %91 RelaxedPrecision
567 OpDecorate %100 Location 0
568 OpDecorate %302 RelaxedPrecision
569 OpDecorate %304 RelaxedPrecision
570 %2 = OpTypeVoid
571 %3 = OpTypeFunction %2
572 %6 = OpTypeInt 32 1
573 %9 = OpConstant %6 0
574 %16 = OpConstant %6 200
575 %17 = OpTypeBool
576 %20 = OpTypeFloat 32
577 %21 = OpTypeVector %20 2
578 %22 = OpTypeStruct %21
579 %23 = OpTypePointer Uniform %22
580 %24 = OpVariable %23 Uniform
581 %25 = OpTypeInt 32 0
582 %26 = OpConstant %25 0
583 %27 = OpTypePointer Uniform %20
584 %35 = OpConstant %6 4
585 %43 = OpConstant %25 50
586 %44 = OpTypeArray %20 %43
587 %45 = OpTypePointer Function %44
588 %51 = OpTypePointer Function %20
589 %54 = OpConstant %6 1
590 %63 = OpConstant %6 50
591 %66 = OpTypeVector %20 4
592 %67 = OpTypePointer Input %66
593 %68 = OpVariable %67 Input
594 %69 = OpTypePointer Input %20
595 %95 = OpConstant %6 20
596 %99 = OpTypePointer Output %66
597 %100 = OpVariable %99 Output
598 %108 = OpConstant %25 1
599 %112 = OpConstant %20 1
600 %118 = OpConstant %6 40
601 %122 = OpConstant %6 5
602 %128 = OpConstant %6 9
603 %139 = OpConstant %6 60
604 %143 = OpConstant %6 10
605 %149 = OpConstant %6 14
606 %160 = OpConstant %6 80
607 %164 = OpConstant %6 15
608 %170 = OpConstant %6 19
609 %181 = OpConstant %6 100
610 %190 = OpConstant %6 24
611 %201 = OpConstant %6 120
612 %205 = OpConstant %6 25
613 %211 = OpConstant %6 29
614 %222 = OpConstant %6 140
615 %226 = OpConstant %6 30
616 %232 = OpConstant %6 34
617 %243 = OpConstant %6 160
618 %247 = OpConstant %6 35
619 %253 = OpConstant %6 39
620 %264 = OpConstant %6 180
621 %273 = OpConstant %6 44
622 %287 = OpConstant %6 45
623 %293 = OpConstant %6 49
624 %4 = OpFunction %2 None %3
625 %5 = OpLabel
626 %46 = OpVariable %45 Function
627 OpBranch %10
628 %10 = OpLabel
629 %302 = OpPhi %6 %9 %5 %55 %42
630 %18 = OpSLessThan %17 %302 %16
631 OpLoopMerge %12 %42 None
632 OpBranchConditional %18 %11 %12
633 %11 = OpLabel
634 %28 = OpAccessChain %27 %24 %9 %26
635 %29 = OpLoad %20 %28
636 %30 = OpConvertFToS %6 %29
637 %31 = OpSGreaterThanEqual %17 %302 %30
638 OpSelectionMerge %33 None
639 OpBranchConditional %31 %32 %33
640 %32 = OpLabel
641 OpBranch %12
642 %33 = OpLabel
643 %37 = OpSDiv %6 %302 %35
644 %38 = OpIMul %6 %35 %37
645 %40 = OpIEqual %17 %38 %302
646 OpBranchConditional %40 %41 %42
647 %41 = OpLabel
648 %50 = OpConvertSToF %20 %302
649 %52 = OpAccessChain %51 %46 %37
650 OpStore %52 %50
651 OpBranch %42
652 %42 = OpLabel
653 %55 = OpIAdd %6 %302 %54
654 OpBranch %10
655 %12 = OpLabel
656 OpBranch %57
657 %57 = OpLabel
658 %304 = OpPhi %6 %9 %12 %91 %80
659 %64 = OpSLessThan %17 %304 %63
660 OpLoopMerge %59 %80 None
661 OpBranchConditional %64 %58 %59
662 %58 = OpLabel
663 %70 = OpAccessChain %69 %68 %26
664 %71 = OpLoad %20 %70
665 %72 = OpConvertFToS %6 %71
666 %73 = OpSLessThan %17 %304 %72
667 OpSelectionMerge %75 None
668 OpBranchConditional %73 %74 %75
669 %74 = OpLabel
670 OpBranch %59
671 %75 = OpLabel
672 %78 = OpSGreaterThan %17 %304 %9
673 OpBranchConditional %78 %79 %80
674 %79 = OpLabel
675 %83 = OpISub %6 %304 %54
676 %84 = OpAccessChain %51 %46 %83
677 %85 = OpLoad %20 %84
678 %86 = OpAccessChain %51 %46 %304
679 %87 = OpLoad %20 %86
680 %88 = OpFAdd %20 %87 %85
681 OpStore %86 %88
682 OpBranch %80
683 %80 = OpLabel
684 %91 = OpIAdd %6 %304 %54
685 OpBranch %57
686 %59 = OpLabel
687 %92 = OpAccessChain %69 %68 %26
688 %93 = OpLoad %20 %92
689 %94 = OpConvertFToS %6 %93
690 %96 = OpSLessThan %17 %94 %95
691 OpSelectionMerge %98 None
692 OpBranchConditional %96 %97 %114
693 %97 = OpLabel
694 %101 = OpAccessChain %51 %46 %9
695 %102 = OpLoad %20 %101
696 %103 = OpAccessChain %27 %24 %9 %26
697 %104 = OpLoad %20 %103
698 %105 = OpFDiv %20 %102 %104
699 %106 = OpAccessChain %51 %46 %35
700 %107 = OpLoad %20 %106
701 %109 = OpAccessChain %27 %24 %9 %108
702 %110 = OpLoad %20 %109
703 %111 = OpFDiv %20 %107 %110
704 %113 = OpCompositeConstruct %66 %105 %111 %112 %112
705 OpStore %100 %113
706 OpBranch %98
707 %114 = OpLabel
708 %119 = OpSLessThan %17 %94 %118
709 OpSelectionMerge %121 None
710 OpBranchConditional %119 %120 %135
711 %120 = OpLabel
712 %123 = OpAccessChain %51 %46 %122
713 %124 = OpLoad %20 %123
714 %125 = OpAccessChain %27 %24 %9 %26
715 %126 = OpLoad %20 %125
716 %127 = OpFDiv %20 %124 %126
717 %129 = OpAccessChain %51 %46 %128
718 %130 = OpLoad %20 %129
719 %131 = OpAccessChain %27 %24 %9 %108
720 %132 = OpLoad %20 %131
721 %133 = OpFDiv %20 %130 %132
722 %134 = OpCompositeConstruct %66 %127 %133 %112 %112
723 OpStore %100 %134
724 OpBranch %121
725 %135 = OpLabel
726 %140 = OpSLessThan %17 %94 %139
727 OpSelectionMerge %142 None
728 OpBranchConditional %140 %141 %156
729 %141 = OpLabel
730 %144 = OpAccessChain %51 %46 %143
731 %145 = OpLoad %20 %144
732 %146 = OpAccessChain %27 %24 %9 %26
733 %147 = OpLoad %20 %146
734 %148 = OpFDiv %20 %145 %147
735 %150 = OpAccessChain %51 %46 %149
736 %151 = OpLoad %20 %150
737 %152 = OpAccessChain %27 %24 %9 %108
738 %153 = OpLoad %20 %152
739 %154 = OpFDiv %20 %151 %153
740 %155 = OpCompositeConstruct %66 %148 %154 %112 %112
741 OpStore %100 %155
742 OpBranch %142
743 %156 = OpLabel
744 %161 = OpSLessThan %17 %94 %160
745 OpSelectionMerge %163 None
746 OpBranchConditional %161 %162 %177
747 %162 = OpLabel
748 %165 = OpAccessChain %51 %46 %164
749 %166 = OpLoad %20 %165
750 %167 = OpAccessChain %27 %24 %9 %26
751 %168 = OpLoad %20 %167
752 %169 = OpFDiv %20 %166 %168
753 %171 = OpAccessChain %51 %46 %170
754 %172 = OpLoad %20 %171
755 %173 = OpAccessChain %27 %24 %9 %108
756 %174 = OpLoad %20 %173
757 %175 = OpFDiv %20 %172 %174
758 %176 = OpCompositeConstruct %66 %169 %175 %112 %112
759 OpStore %100 %176
760 OpBranch %163
761 %177 = OpLabel
762 %182 = OpSLessThan %17 %94 %181
763 OpSelectionMerge %184 None
764 OpBranchConditional %182 %183 %197
765 %183 = OpLabel
766 %185 = OpAccessChain %51 %46 %95
767 %186 = OpLoad %20 %185
768 %187 = OpAccessChain %27 %24 %9 %26
769 %188 = OpLoad %20 %187
770 %189 = OpFDiv %20 %186 %188
771 %191 = OpAccessChain %51 %46 %190
772 %192 = OpLoad %20 %191
773 %193 = OpAccessChain %27 %24 %9 %108
774 %194 = OpLoad %20 %193
775 %195 = OpFDiv %20 %192 %194
776 %196 = OpCompositeConstruct %66 %189 %195 %112 %112
777 OpStore %100 %196
778 OpBranch %184
779 %197 = OpLabel
780 %202 = OpSLessThan %17 %94 %201
781 OpSelectionMerge %204 None
782 OpBranchConditional %202 %203 %218
783 %203 = OpLabel
784 %206 = OpAccessChain %51 %46 %205
785 %207 = OpLoad %20 %206
786 %208 = OpAccessChain %27 %24 %9 %26
787 %209 = OpLoad %20 %208
788 %210 = OpFDiv %20 %207 %209
789 %212 = OpAccessChain %51 %46 %211
790 %213 = OpLoad %20 %212
791 %214 = OpAccessChain %27 %24 %9 %108
792 %215 = OpLoad %20 %214
793 %216 = OpFDiv %20 %213 %215
794 %217 = OpCompositeConstruct %66 %210 %216 %112 %112
795 OpStore %100 %217
796 OpBranch %204
797 %218 = OpLabel
798 %223 = OpSLessThan %17 %94 %222
799 OpSelectionMerge %225 None
800 OpBranchConditional %223 %224 %239
801 %224 = OpLabel
802 %227 = OpAccessChain %51 %46 %226
803 %228 = OpLoad %20 %227
804 %229 = OpAccessChain %27 %24 %9 %26
805 %230 = OpLoad %20 %229
806 %231 = OpFDiv %20 %228 %230
807 %233 = OpAccessChain %51 %46 %232
808 %234 = OpLoad %20 %233
809 %235 = OpAccessChain %27 %24 %9 %108
810 %236 = OpLoad %20 %235
811 %237 = OpFDiv %20 %234 %236
812 %238 = OpCompositeConstruct %66 %231 %237 %112 %112
813 OpStore %100 %238
814 OpBranch %225
815 %239 = OpLabel
816 %244 = OpSLessThan %17 %94 %243
817 OpSelectionMerge %246 None
818 OpBranchConditional %244 %245 %260
819 %245 = OpLabel
820 %248 = OpAccessChain %51 %46 %247
821 %249 = OpLoad %20 %248
822 %250 = OpAccessChain %27 %24 %9 %26
823 %251 = OpLoad %20 %250
824 %252 = OpFDiv %20 %249 %251
825 %254 = OpAccessChain %51 %46 %253
826 %255 = OpLoad %20 %254
827 %256 = OpAccessChain %27 %24 %9 %108
828 %257 = OpLoad %20 %256
829 %258 = OpFDiv %20 %255 %257
830 %259 = OpCompositeConstruct %66 %252 %258 %112 %112
831 OpStore %100 %259
832 OpBranch %246
833 %260 = OpLabel
834 %265 = OpSLessThan %17 %94 %264
835 OpSelectionMerge %267 None
836 OpBranchConditional %265 %266 %280
837 %266 = OpLabel
838 %268 = OpAccessChain %51 %46 %118
839 %269 = OpLoad %20 %268
840 %270 = OpAccessChain %27 %24 %9 %26
841 %271 = OpLoad %20 %270
842 %272 = OpFDiv %20 %269 %271
843 %274 = OpAccessChain %51 %46 %273
844 %275 = OpLoad %20 %274
845 %276 = OpAccessChain %27 %24 %9 %108
846 %277 = OpLoad %20 %276
847 %278 = OpFDiv %20 %275 %277
848 %279 = OpCompositeConstruct %66 %272 %278 %112 %112
849 OpStore %100 %279
850 OpBranch %267
851 %280 = OpLabel
852 OpSelectionMerge %285 None
853 OpBranchConditional %265 %285 %300
854 %285 = OpLabel
855 %288 = OpAccessChain %51 %46 %287
856 %289 = OpLoad %20 %288
857 %290 = OpAccessChain %27 %24 %9 %26
858 %291 = OpLoad %20 %290
859 %292 = OpFDiv %20 %289 %291
860 %294 = OpAccessChain %51 %46 %293
861 %295 = OpLoad %20 %294
862 %296 = OpAccessChain %27 %24 %9 %108
863 %297 = OpLoad %20 %296
864 %298 = OpFDiv %20 %295 %297
865 %299 = OpCompositeConstruct %66 %292 %298 %112 %112
866 OpStore %100 %299
867 OpBranch %267
868 %300 = OpLabel
869 OpKill
870 %267 = OpLabel
871 OpBranch %246
872 %246 = OpLabel
873 OpBranch %225
874 %225 = OpLabel
875 OpBranch %204
876 %204 = OpLabel
877 OpBranch %184
878 %184 = OpLabel
879 OpBranch %163
880 %163 = OpLabel
881 OpBranch %142
882 %142 = OpLabel
883 OpBranch %121
884 %121 = OpLabel
885 OpBranch %98
886 %98 = OpLabel
887 OpReturn
888 OpFunctionEnd
889 )";
890
891 // Abstract class exposing an interestingness function as a virtual method.
892 class InterestingnessTest {
893 public:
894 virtual ~InterestingnessTest() = default;
895
896 // Abstract method that subclasses should implement for specific notions of
897 // interestingness. Its signature matches Shrinker::InterestingnessFunction.
898 // Argument |binary| is the SPIR-V binary to be checked; |counter| is used for
899 // debugging purposes.
900 virtual bool Interesting(const std::vector<uint32_t>& binary,
901 uint32_t counter) = 0;
902
903 // Yields the Interesting instance method wrapped in a function object.
AsFunction()904 Shrinker::InterestingnessFunction AsFunction() {
905 return std::bind(&InterestingnessTest::Interesting, this,
906 std::placeholders::_1, std::placeholders::_2);
907 }
908 };
909
910 // A test that says all binaries are interesting.
911 class AlwaysInteresting : public InterestingnessTest {
912 public:
Interesting(const std::vector<uint32_t> &,uint32_t)913 bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
914 return true;
915 }
916 };
917
918 // A test that says a binary is interesting first time round, and uninteresting
919 // thereafter.
920 class OnlyInterestingFirstTime : public InterestingnessTest {
921 public:
OnlyInterestingFirstTime()922 explicit OnlyInterestingFirstTime() : first_time_(true) {}
923
Interesting(const std::vector<uint32_t> &,uint32_t)924 bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
925 if (first_time_) {
926 first_time_ = false;
927 return true;
928 }
929 return false;
930 }
931
932 private:
933 bool first_time_;
934 };
935
936 // A test that says a binary is interesting first time round, after which
937 // interestingness ping pongs between false and true.
938 class PingPong : public InterestingnessTest {
939 public:
PingPong()940 explicit PingPong() : interesting_(false) {}
941
Interesting(const std::vector<uint32_t> &,uint32_t)942 bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
943 interesting_ = !interesting_;
944 return interesting_;
945 }
946
947 private:
948 bool interesting_;
949 };
950
951 // A test that says a binary is interesting first time round, thereafter
952 // decides at random whether it is interesting. This allows the logic of the
953 // shrinker to be exercised quite a bit.
954 class InterestingThenRandom : public InterestingnessTest {
955 public:
InterestingThenRandom(const PseudoRandomGenerator & random_generator)956 InterestingThenRandom(const PseudoRandomGenerator& random_generator)
957 : first_time_(true), random_generator_(random_generator) {}
958
Interesting(const std::vector<uint32_t> &,uint32_t)959 bool Interesting(const std::vector<uint32_t>&, uint32_t) override {
960 if (first_time_) {
961 first_time_ = false;
962 return true;
963 }
964 return random_generator_.RandomBool();
965 }
966
967 private:
968 bool first_time_;
969 PseudoRandomGenerator random_generator_;
970 };
971
972 // |binary_in| and |initial_facts| are a SPIR-V binary and sequence of facts to
973 // which |transformation_sequence_in| can be applied. Shrinking of
974 // |transformation_sequence_in| gets performed with respect to
975 // |interestingness_function|. If |expected_binary_out| is non-empty, it must
976 // match the binary obtained by applying the final shrunk set of
977 // transformations, in which case the number of such transformations should
978 // equal |expected_transformations_out_size|.
979 //
980 // The |step_limit| parameter restricts the number of steps that the shrinker
981 // will try; it can be set to something small for a faster (but less thorough)
982 // test.
983 //
984 // The |validator_options| parameter provides validator options that should be
985 // used during shrinking.
RunAndCheckShrinker(const spv_target_env & target_env,const std::vector<uint32_t> & binary_in,const protobufs::FactSequence & initial_facts,const protobufs::TransformationSequence & transformation_sequence_in,const Shrinker::InterestingnessFunction & interestingness_function,const std::vector<uint32_t> & expected_binary_out,uint32_t expected_transformations_out_size,uint32_t step_limit,spv_validator_options validator_options)986 void RunAndCheckShrinker(
987 const spv_target_env& target_env, const std::vector<uint32_t>& binary_in,
988 const protobufs::FactSequence& initial_facts,
989 const protobufs::TransformationSequence& transformation_sequence_in,
990 const Shrinker::InterestingnessFunction& interestingness_function,
991 const std::vector<uint32_t>& expected_binary_out,
992 uint32_t expected_transformations_out_size, uint32_t step_limit,
993 spv_validator_options validator_options) {
994 // Run the shrinker.
995 auto shrinker_result =
996 Shrinker(target_env, kConsoleMessageConsumer, binary_in, initial_facts,
997 transformation_sequence_in, interestingness_function, step_limit,
998 false, validator_options)
999 .Run();
1000
1001 ASSERT_TRUE(Shrinker::ShrinkerResultStatus::kComplete ==
1002 shrinker_result.status ||
1003 Shrinker::ShrinkerResultStatus::kStepLimitReached ==
1004 shrinker_result.status);
1005
1006 // If a non-empty expected binary was provided, check that it matches the
1007 // result of shrinking and that the expected number of transformations remain.
1008 if (!expected_binary_out.empty()) {
1009 ASSERT_EQ(expected_binary_out, shrinker_result.transformed_binary);
1010 ASSERT_EQ(
1011 expected_transformations_out_size,
1012 static_cast<uint32_t>(
1013 shrinker_result.applied_transformations.transformation_size()));
1014 }
1015 }
1016
1017 // Assembles the given |shader| text, and then:
1018 // - Runs the fuzzer with |seed| to yield a set of transformations
1019 // - Shrinks the transformation with various interestingness functions,
1020 // asserting some properties about the result each time
RunFuzzerAndShrinker(const std::string & shader,const protobufs::FactSequence & initial_facts,uint32_t seed)1021 void RunFuzzerAndShrinker(const std::string& shader,
1022 const protobufs::FactSequence& initial_facts,
1023 uint32_t seed) {
1024 const auto env = SPV_ENV_UNIVERSAL_1_5;
1025
1026 std::vector<uint32_t> binary_in;
1027 SpirvTools t(env);
1028 t.SetMessageConsumer(kConsoleMessageConsumer);
1029 ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption));
1030 ASSERT_TRUE(t.Validate(binary_in));
1031
1032 std::vector<fuzzerutil::ModuleSupplier> donor_suppliers;
1033 for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3}) {
1034 donor_suppliers.emplace_back([donor]() {
1035 return BuildModule(env, kConsoleMessageConsumer, *donor,
1036 kFuzzAssembleOption);
1037 });
1038 }
1039
1040 // Run the fuzzer and check that it successfully yields a valid binary.
1041 spvtools::ValidatorOptions validator_options;
1042
1043 // Depending on the seed, decide whether to enable all passes and which
1044 // repeated pass manager to use.
1045 bool enable_all_passes = (seed % 4) == 0;
1046 RepeatedPassStrategy repeated_pass_strategy;
1047 if ((seed % 3) == 0) {
1048 repeated_pass_strategy = RepeatedPassStrategy::kSimple;
1049 } else if ((seed % 3) == 1) {
1050 repeated_pass_strategy = RepeatedPassStrategy::kLoopedWithRecommendations;
1051 } else {
1052 repeated_pass_strategy = RepeatedPassStrategy::kRandomWithRecommendations;
1053 }
1054
1055 std::unique_ptr<opt::IRContext> ir_context;
1056 ASSERT_TRUE(fuzzerutil::BuildIRContext(
1057 env, kConsoleMessageConsumer, binary_in, validator_options, &ir_context));
1058
1059 auto fuzzer_context = MakeUnique<FuzzerContext>(
1060 MakeUnique<PseudoRandomGenerator>(seed),
1061 FuzzerContext::GetMinFreshId(ir_context.get()), false);
1062
1063 auto transformation_context = MakeUnique<TransformationContext>(
1064 MakeUnique<FactManager>(ir_context.get()), validator_options);
1065 transformation_context->GetFactManager()->AddInitialFacts(
1066 kConsoleMessageConsumer, initial_facts);
1067
1068 Fuzzer fuzzer(std::move(ir_context), std::move(transformation_context),
1069 std::move(fuzzer_context), kConsoleMessageConsumer,
1070 donor_suppliers, enable_all_passes, repeated_pass_strategy,
1071 true, validator_options, false);
1072 auto fuzzer_result = fuzzer.Run(0);
1073 ASSERT_NE(Fuzzer::Status::kFuzzerPassLedToInvalidModule,
1074 fuzzer_result.status);
1075 std::vector<uint32_t> transformed_binary;
1076 fuzzer.GetIRContext()->module()->ToBinary(&transformed_binary, true);
1077 ASSERT_TRUE(t.Validate(transformed_binary));
1078
1079 const uint32_t kReasonableStepLimit = 50;
1080 const uint32_t kSmallStepLimit = 20;
1081
1082 // With the AlwaysInteresting test, we should quickly shrink to the original
1083 // binary with no transformations remaining.
1084 RunAndCheckShrinker(env, binary_in, initial_facts,
1085 fuzzer.GetTransformationSequence(),
1086 AlwaysInteresting().AsFunction(), binary_in, 0,
1087 kReasonableStepLimit, validator_options);
1088
1089 // With the OnlyInterestingFirstTime test, no shrinking should be achieved.
1090 RunAndCheckShrinker(
1091 env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
1092 OnlyInterestingFirstTime().AsFunction(), transformed_binary,
1093 static_cast<uint32_t>(
1094 fuzzer.GetTransformationSequence().transformation_size()),
1095 kReasonableStepLimit, validator_options);
1096
1097 // The PingPong test is unpredictable; passing an empty expected binary
1098 // means that we don't check anything beyond that shrinking completes
1099 // successfully.
1100 RunAndCheckShrinker(
1101 env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
1102 PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options);
1103
1104 // The InterestingThenRandom test is unpredictable; passing an empty
1105 // expected binary means that we do not check anything about shrinking
1106 // results.
1107 RunAndCheckShrinker(
1108 env, binary_in, initial_facts, fuzzer.GetTransformationSequence(),
1109 InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0,
1110 kSmallStepLimit, validator_options);
1111 }
1112
TEST(FuzzerShrinkerTest,Miscellaneous1)1113 TEST(FuzzerShrinkerTest, Miscellaneous1) {
1114 RunFuzzerAndShrinker(kTestShader1, protobufs::FactSequence(), 2);
1115 }
1116
TEST(FuzzerShrinkerTest,Miscellaneous2)1117 TEST(FuzzerShrinkerTest, Miscellaneous2) {
1118 RunFuzzerAndShrinker(kTestShader2, protobufs::FactSequence(), 19);
1119 }
1120
TEST(FuzzerShrinkerTest,Miscellaneous3)1121 TEST(FuzzerShrinkerTest, Miscellaneous3) {
1122 // Add the facts "resolution.x == 250" and "resolution.y == 100".
1123 protobufs::FactSequence facts;
1124 {
1125 protobufs::FactConstantUniform resolution_x_eq_250;
1126 *resolution_x_eq_250.mutable_uniform_buffer_element_descriptor() =
1127 MakeUniformBufferElementDescriptor(0, 0, {0, 0});
1128 *resolution_x_eq_250.mutable_constant_word()->Add() = 250;
1129 protobufs::Fact temp;
1130 *temp.mutable_constant_uniform_fact() = resolution_x_eq_250;
1131 *facts.mutable_fact()->Add() = temp;
1132 }
1133 {
1134 protobufs::FactConstantUniform resolution_y_eq_100;
1135 *resolution_y_eq_100.mutable_uniform_buffer_element_descriptor() =
1136 MakeUniformBufferElementDescriptor(0, 0, {0, 1});
1137 *resolution_y_eq_100.mutable_constant_word()->Add() = 100;
1138 protobufs::Fact temp;
1139 *temp.mutable_constant_uniform_fact() = resolution_y_eq_100;
1140 *facts.mutable_fact()->Add() = temp;
1141 }
1142 // Also add an invalid fact, which should be ignored.
1143 {
1144 protobufs::FactConstantUniform bad_fact;
1145 // The descriptor set, binding and indices used here deliberately make no
1146 // sense.
1147 *bad_fact.mutable_uniform_buffer_element_descriptor() =
1148 MakeUniformBufferElementDescriptor(22, 33, {44, 55});
1149 *bad_fact.mutable_constant_word()->Add() = 100;
1150 protobufs::Fact temp;
1151 *temp.mutable_constant_uniform_fact() = bad_fact;
1152 *facts.mutable_fact()->Add() = temp;
1153 }
1154
1155 // Do 2 fuzzer runs, starting from an initial seed of 194 (seed value chosen
1156 // arbitrarily).
1157 RunFuzzerAndShrinker(kTestShader3, facts, 194);
1158 }
1159
1160 } // namespace
1161 } // namespace fuzz
1162 } // namespace spvtools
1163