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 26namespace spvtools { 27namespace fuzz { 28namespace { 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 58const 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 247const 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 543const 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. 892class 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. 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. 911class AlwaysInteresting : public InterestingnessTest { 912 public: 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. 920class OnlyInterestingFirstTime : public InterestingnessTest { 921 public: 922 explicit OnlyInterestingFirstTime() : first_time_(true) {} 923 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. 938class PingPong : public InterestingnessTest { 939 public: 940 explicit PingPong() : interesting_(false) {} 941 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. 954class InterestingThenRandom : public InterestingnessTest { 955 public: 956 InterestingThenRandom(const PseudoRandomGenerator& random_generator) 957 : first_time_(true), random_generator_(random_generator) {} 958 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. 986void 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 1021void 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 1113TEST(FuzzerShrinkerTest, Miscellaneous1) { 1114 RunFuzzerAndShrinker(kTestShader1, protobufs::FactSequence(), 2); 1115} 1116 1117TEST(FuzzerShrinkerTest, Miscellaneous2) { 1118 RunFuzzerAndShrinker(kTestShader2, protobufs::FactSequence(), 19); 1119} 1120 1121TEST(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