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