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