1// Copyright (c) 2020 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/transformation_composite_insert.h" 16 17#include "gtest/gtest.h" 18#include "source/fuzz/data_descriptor.h" 19#include "source/fuzz/fuzzer_util.h" 20#include "source/fuzz/instruction_descriptor.h" 21#include "test/fuzz/fuzz_test_util.h" 22 23namespace spvtools { 24namespace fuzz { 25namespace { 26 27TEST(TransformationCompositeInsertTest, NotApplicableScenarios) { 28 // This test handles cases where IsApplicable() returns false. 29 30 std::string shader = R"( 31 OpCapability Shader 32 %1 = OpExtInstImport "GLSL.std.450" 33 OpMemoryModel Logical GLSL450 34 OpEntryPoint Fragment %4 "main" 35 OpExecutionMode %4 OriginUpperLeft 36 OpSource ESSL 310 37 OpName %4 "main" 38 OpName %8 "i1" 39 OpName %10 "i2" 40 OpName %12 "base" 41 OpMemberName %12 0 "a1" 42 OpMemberName %12 1 "a2" 43 OpName %14 "b" 44 OpName %18 "level_1" 45 OpMemberName %18 0 "b1" 46 OpMemberName %18 1 "b2" 47 OpName %20 "l1" 48 OpName %24 "level_2" 49 OpMemberName %24 0 "c1" 50 OpMemberName %24 1 "c2" 51 OpName %26 "l2" 52 %2 = OpTypeVoid 53 %3 = OpTypeFunction %2 54 %6 = OpTypeInt 32 1 55 %7 = OpTypePointer Function %6 56 %9 = OpConstant %6 1 57 %11 = OpConstant %6 2 58 %12 = OpTypeStruct %6 %6 59 %13 = OpTypePointer Function %12 60 %18 = OpTypeStruct %12 %12 61 %19 = OpTypePointer Function %18 62 %24 = OpTypeStruct %18 %18 63 %25 = OpTypePointer Function %24 64 %30 = OpTypeBool 65 %31 = OpConstantTrue %30 66 %4 = OpFunction %2 None %3 67 %5 = OpLabel 68 %8 = OpVariable %7 Function 69 %10 = OpVariable %7 Function 70 %14 = OpVariable %13 Function 71 %20 = OpVariable %19 Function 72 %26 = OpVariable %25 Function 73 OpStore %8 %9 74 OpStore %10 %11 75 %15 = OpLoad %6 %8 76 %16 = OpLoad %6 %10 77 %17 = OpCompositeConstruct %12 %15 %16 78 OpStore %14 %17 79 %21 = OpLoad %12 %14 80 %22 = OpLoad %12 %14 81 %23 = OpCompositeConstruct %18 %21 %22 82 OpStore %20 %23 83 %27 = OpLoad %18 %20 84 %28 = OpLoad %18 %20 85 %29 = OpCompositeConstruct %24 %27 %28 86 OpStore %26 %29 87 OpSelectionMerge %33 None 88 OpBranchConditional %31 %32 %33 89 %32 = OpLabel 90 OpBranch %33 91 %33 = OpLabel 92 OpReturn 93 OpFunctionEnd 94 )"; 95 96 const auto env = SPV_ENV_UNIVERSAL_1_4; 97 const auto consumer = nullptr; 98 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 99 spvtools::ValidatorOptions validator_options; 100 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 101 kConsoleMessageConsumer)); 102 TransformationContext transformation_context( 103 MakeUnique<FactManager>(context.get()), validator_options); 104 // Bad: |fresh_id| is not fresh. 105 auto transformation_bad_1 = TransformationCompositeInsert( 106 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 20, 29, 11, 107 {1, 0, 0}); 108 ASSERT_FALSE( 109 transformation_bad_1.IsApplicable(context.get(), transformation_context)); 110 111 // Bad: |composite_id| does not refer to a existing instruction. 112 auto transformation_bad_2 = TransformationCompositeInsert( 113 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 40, 11, 114 {1, 0, 0}); 115 ASSERT_FALSE( 116 transformation_bad_2.IsApplicable(context.get(), transformation_context)); 117 118 // Bad: |composite_id| does not refer to a composite value. 119 auto transformation_bad_3 = TransformationCompositeInsert( 120 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 9, 11, {1, 0, 0}); 121 ASSERT_FALSE( 122 transformation_bad_3.IsApplicable(context.get(), transformation_context)); 123 124 // Bad: |object_id| does not refer to a defined instruction. 125 auto transformation_bad_4 = TransformationCompositeInsert( 126 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 40, 127 {1, 0, 0}); 128 ASSERT_FALSE( 129 transformation_bad_4.IsApplicable(context.get(), transformation_context)); 130 131 // Bad: |object_id| cannot refer to a pointer. 132 auto transformation_bad_5 = TransformationCompositeInsert( 133 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 8, {1, 0, 0}); 134 ASSERT_FALSE( 135 transformation_bad_5.IsApplicable(context.get(), transformation_context)); 136 137 // Bad: |index| is not a correct index. 138 auto transformation_bad_6 = TransformationCompositeInsert( 139 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 11, 140 {2, 0, 0}); 141 ASSERT_FALSE( 142 transformation_bad_6.IsApplicable(context.get(), transformation_context)); 143 144 // Bad: Type id of the object to be inserted and the type id of the 145 // component at |index| are not the same. 146 auto transformation_bad_7 = TransformationCompositeInsert( 147 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 29, 11, {1, 0}); 148 ASSERT_FALSE( 149 transformation_bad_7.IsApplicable(context.get(), transformation_context)); 150 151 // Bad: |instruction_to_insert_before| does not refer to a defined 152 // instruction. 153 auto transformation_bad_8 = TransformationCompositeInsert( 154 MakeInstructionDescriptor(29, spv::Op::OpIMul, 0), 50, 29, 11, {1, 0, 0}); 155 ASSERT_FALSE( 156 transformation_bad_8.IsApplicable(context.get(), transformation_context)); 157 158 // Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with 159 // OpSelectionMerge above it. 160 auto transformation_bad_9 = TransformationCompositeInsert( 161 MakeInstructionDescriptor(29, spv::Op::OpBranchConditional, 0), 50, 29, 162 11, {1, 0, 0}); 163 ASSERT_FALSE( 164 transformation_bad_9.IsApplicable(context.get(), transformation_context)); 165 166 // Bad: |composite_id| does not have a type_id. 167 auto transformation_bad_10 = TransformationCompositeInsert( 168 MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 50, 1, 11, {1, 0, 0}); 169 ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(), 170 transformation_context)); 171} 172 173TEST(TransformationCompositeInsertTest, EmptyCompositeScenarios) { 174 // This test handles cases where either the composite is empty or the 175 // composite contains an empty composite. 176 177 std::string shader = R"( 178 OpCapability Shader 179 %1 = OpExtInstImport "GLSL.std.450" 180 OpMemoryModel Logical GLSL450 181 OpEntryPoint Fragment %4 "main" 182 OpExecutionMode %4 OriginUpperLeft 183 OpSource ESSL 310 184 OpName %4 "main" 185 OpName %8 "i1" 186 OpName %10 "i2" 187 OpName %12 "base" 188 OpMemberName %12 0 "a1" 189 OpMemberName %12 1 "a2" 190 OpName %14 "b" 191 %2 = OpTypeVoid 192 %60 = OpTypeStruct 193 %3 = OpTypeFunction %2 194 %6 = OpTypeInt 32 1 195 %7 = OpTypePointer Function %6 196 %9 = OpConstant %6 1 197 %11 = OpConstant %6 2 198 %61 = OpConstantComposite %60 199 %62 = OpConstantComposite %60 200 %12 = OpTypeStruct %6 %6 201 %63 = OpTypeStruct %6 %60 202 %13 = OpTypePointer Function %12 203 %4 = OpFunction %2 None %3 204 %5 = OpLabel 205 %8 = OpVariable %7 Function 206 %10 = OpVariable %7 Function 207 %14 = OpVariable %13 Function 208 OpStore %8 %9 209 OpStore %10 %11 210 %15 = OpLoad %6 %8 211 %16 = OpLoad %6 %10 212 %17 = OpCompositeConstruct %12 %15 %16 213 %64 = OpCompositeConstruct %63 %15 %61 214 OpStore %14 %17 215 OpReturn 216 OpFunctionEnd 217 )"; 218 219 const auto env = SPV_ENV_UNIVERSAL_1_4; 220 const auto consumer = nullptr; 221 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 222 spvtools::ValidatorOptions validator_options; 223 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 224 kConsoleMessageConsumer)); 225 TransformationContext transformation_context( 226 MakeUnique<FactManager>(context.get()), validator_options); 227 // Bad: The composite with |composite_id| cannot be empty. 228 auto transformation_bad_1 = TransformationCompositeInsert( 229 MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 50, 61, 62, {1}); 230 ASSERT_FALSE( 231 transformation_bad_1.IsApplicable(context.get(), transformation_context)); 232 233 // Good: It is possible to insert into a composite an element which is an 234 // empty composite. 235 auto transformation_good_1 = TransformationCompositeInsert( 236 MakeInstructionDescriptor(64, spv::Op::OpStore, 0), 50, 64, 62, {1}); 237 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), 238 transformation_context)); 239 ApplyAndCheckFreshIds(transformation_good_1, context.get(), 240 &transformation_context); 241 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 242 kConsoleMessageConsumer)); 243 244 std::string after_transformations = R"( 245 OpCapability Shader 246 %1 = OpExtInstImport "GLSL.std.450" 247 OpMemoryModel Logical GLSL450 248 OpEntryPoint Fragment %4 "main" 249 OpExecutionMode %4 OriginUpperLeft 250 OpSource ESSL 310 251 OpName %4 "main" 252 OpName %8 "i1" 253 OpName %10 "i2" 254 OpName %12 "base" 255 OpMemberName %12 0 "a1" 256 OpMemberName %12 1 "a2" 257 OpName %14 "b" 258 %2 = OpTypeVoid 259 %60 = OpTypeStruct 260 %3 = OpTypeFunction %2 261 %6 = OpTypeInt 32 1 262 %7 = OpTypePointer Function %6 263 %9 = OpConstant %6 1 264 %11 = OpConstant %6 2 265 %61 = OpConstantComposite %60 266 %62 = OpConstantComposite %60 267 %12 = OpTypeStruct %6 %6 268 %63 = OpTypeStruct %6 %60 269 %13 = OpTypePointer Function %12 270 %4 = OpFunction %2 None %3 271 %5 = OpLabel 272 %8 = OpVariable %7 Function 273 %10 = OpVariable %7 Function 274 %14 = OpVariable %13 Function 275 OpStore %8 %9 276 OpStore %10 %11 277 %15 = OpLoad %6 %8 278 %16 = OpLoad %6 %10 279 %17 = OpCompositeConstruct %12 %15 %16 280 %64 = OpCompositeConstruct %63 %15 %61 281 %50 = OpCompositeInsert %63 %62 %64 1 282 OpStore %14 %17 283 OpReturn 284 OpFunctionEnd 285 )"; 286 ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); 287} 288 289TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) { 290 // This test handles cases where either |composite| is irrelevant. 291 // The transformation shouldn't create any synonyms. 292 // The member composite has a different number of elements than the parent 293 // composite. 294 295 std::string shader = R"( 296 OpCapability Shader 297 %1 = OpExtInstImport "GLSL.std.450" 298 OpMemoryModel Logical GLSL450 299 OpEntryPoint Fragment %4 "main" 300 OpExecutionMode %4 OriginUpperLeft 301 OpSource ESSL 310 302 OpName %4 "main" 303 OpName %8 "i1" 304 OpName %10 "i2" 305 OpName %12 "base" 306 OpMemberName %12 0 "a1" 307 OpMemberName %12 1 "a2" 308 OpName %14 "b" 309 OpName %18 "level_1" 310 OpMemberName %18 0 "b1" 311 OpMemberName %18 1 "b2" 312 OpMemberName %18 2 "b3" 313 OpName %20 "l1" 314 OpName %25 "level_2" 315 OpMemberName %25 0 "c1" 316 OpMemberName %25 1 "c2" 317 OpName %27 "l2" 318 %2 = OpTypeVoid 319 %3 = OpTypeFunction %2 320 %6 = OpTypeInt 32 1 321 %7 = OpTypePointer Function %6 322 %9 = OpConstant %6 1 323 %11 = OpConstant %6 2 324 %12 = OpTypeStruct %6 %6 325 %13 = OpTypePointer Function %12 326 %18 = OpTypeStruct %12 %12 %12 327 %19 = OpTypePointer Function %18 328 %25 = OpTypeStruct %18 %18 329 %26 = OpTypePointer Function %25 330 %31 = OpTypeBool 331 %32 = OpConstantTrue %31 332 %4 = OpFunction %2 None %3 333 %5 = OpLabel 334 %8 = OpVariable %7 Function 335 %10 = OpVariable %7 Function 336 %14 = OpVariable %13 Function 337 %20 = OpVariable %19 Function 338 %27 = OpVariable %26 Function 339 OpStore %8 %9 340 OpStore %10 %11 341 %15 = OpLoad %6 %8 342 %16 = OpLoad %6 %10 343 %17 = OpCompositeConstruct %12 %15 %16 344 OpStore %14 %17 345 %21 = OpLoad %12 %14 346 %22 = OpLoad %12 %14 347 %23 = OpLoad %12 %14 348 %24 = OpCompositeConstruct %18 %21 %22 %23 349 OpStore %20 %24 350 %28 = OpLoad %18 %20 351 %29 = OpLoad %18 %20 352 %30 = OpCompositeConstruct %25 %28 %29 353 OpStore %27 %30 354 OpSelectionMerge %34 None 355 OpBranchConditional %32 %33 %34 356 %33 = OpLabel 357 OpBranch %34 358 %34 = OpLabel 359 OpReturn 360 OpFunctionEnd 361 )"; 362 363 const auto env = SPV_ENV_UNIVERSAL_1_4; 364 const auto consumer = nullptr; 365 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 366 spvtools::ValidatorOptions validator_options; 367 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 368 kConsoleMessageConsumer)); 369 TransformationContext transformation_context( 370 MakeUnique<FactManager>(context.get()), validator_options); 371 // Add fact that the composite is irrelevant. 372 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(30); 373 374 auto transformation_good_1 = TransformationCompositeInsert( 375 MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11, 376 {1, 0, 0}); 377 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), 378 transformation_context)); 379 ApplyAndCheckFreshIds(transformation_good_1, context.get(), 380 &transformation_context); 381 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 382 kConsoleMessageConsumer)); 383 384 // No synonyms that involve the original object - %30 - should have been 385 // added. 386 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 387 MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); 388 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 389 MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); 390 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 391 MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); 392 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 393 MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); 394 // We *should* have a synonym between %11 and the component of %50 into which 395 // it has been inserted. 396 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 397 MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); 398} 399 400TEST(TransformationCompositeInsertTest, IrrelevantObjectNoSynonyms) { 401 std::string shader = R"( 402 OpCapability Shader 403 %1 = OpExtInstImport "GLSL.std.450" 404 OpMemoryModel Logical GLSL450 405 OpEntryPoint Fragment %4 "main" 406 OpExecutionMode %4 OriginUpperLeft 407 OpSource ESSL 310 408 OpName %4 "main" 409 OpName %8 "i1" 410 OpName %10 "i2" 411 OpName %12 "base" 412 OpMemberName %12 0 "a1" 413 OpMemberName %12 1 "a2" 414 OpName %14 "b" 415 OpName %18 "level_1" 416 OpMemberName %18 0 "b1" 417 OpMemberName %18 1 "b2" 418 OpMemberName %18 2 "b3" 419 OpName %20 "l1" 420 OpName %25 "level_2" 421 OpMemberName %25 0 "c1" 422 OpMemberName %25 1 "c2" 423 OpName %27 "l2" 424 %2 = OpTypeVoid 425 %3 = OpTypeFunction %2 426 %6 = OpTypeInt 32 1 427 %7 = OpTypePointer Function %6 428 %9 = OpConstant %6 1 429 %11 = OpConstant %6 2 430 %12 = OpTypeStruct %6 %6 431 %13 = OpTypePointer Function %12 432 %18 = OpTypeStruct %12 %12 %12 433 %19 = OpTypePointer Function %18 434 %25 = OpTypeStruct %18 %18 435 %26 = OpTypePointer Function %25 436 %31 = OpTypeBool 437 %32 = OpConstantTrue %31 438 %4 = OpFunction %2 None %3 439 %5 = OpLabel 440 %8 = OpVariable %7 Function 441 %10 = OpVariable %7 Function 442 %14 = OpVariable %13 Function 443 %20 = OpVariable %19 Function 444 %27 = OpVariable %26 Function 445 OpStore %8 %9 446 OpStore %10 %11 447 %15 = OpLoad %6 %8 448 %16 = OpLoad %6 %10 449 %17 = OpCompositeConstruct %12 %15 %16 450 OpStore %14 %17 451 %21 = OpLoad %12 %14 452 %22 = OpLoad %12 %14 453 %23 = OpLoad %12 %14 454 %24 = OpCompositeConstruct %18 %21 %22 %23 455 OpStore %20 %24 456 %28 = OpLoad %18 %20 457 %29 = OpLoad %18 %20 458 %30 = OpCompositeConstruct %25 %28 %29 459 OpStore %27 %30 460 OpSelectionMerge %34 None 461 OpBranchConditional %32 %33 %34 462 %33 = OpLabel 463 OpBranch %34 464 %34 = OpLabel 465 OpReturn 466 OpFunctionEnd 467 )"; 468 469 const auto env = SPV_ENV_UNIVERSAL_1_4; 470 const auto consumer = nullptr; 471 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 472 spvtools::ValidatorOptions validator_options; 473 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 474 kConsoleMessageConsumer)); 475 TransformationContext transformation_context( 476 MakeUnique<FactManager>(context.get()), validator_options); 477 // Add fact that the object is irrelevant. 478 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11); 479 480 auto transformation_good_1 = TransformationCompositeInsert( 481 MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11, 482 {1, 0, 0}); 483 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), 484 transformation_context)); 485 ApplyAndCheckFreshIds(transformation_good_1, context.get(), 486 &transformation_context); 487 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 488 kConsoleMessageConsumer)); 489 490 // Since %30 and %50 are not irrelevant, they should be synonymous at all 491 // indices unaffected by the insertion. 492 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 493 MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); 494 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 495 MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); 496 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 497 MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); 498 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 499 MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); 500 // Since %11 is irrelevant it should not be synonymous with the component into 501 // which it has been inserted. 502 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 503 MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); 504} 505 506TEST(TransformationCompositeInsertTest, ApplicableCreatedSynonyms) { 507 // This test handles cases where neither |composite| nor |object| is 508 // irrelevant. The transformation should create synonyms. 509 // The member composite has a different number of elements than the parent 510 // composite. 511 512 std::string shader = R"( 513 OpCapability Shader 514 %1 = OpExtInstImport "GLSL.std.450" 515 OpMemoryModel Logical GLSL450 516 OpEntryPoint Fragment %4 "main" 517 OpExecutionMode %4 OriginUpperLeft 518 OpSource ESSL 310 519 OpName %4 "main" 520 OpName %8 "i1" 521 OpName %10 "i2" 522 OpName %12 "base" 523 OpMemberName %12 0 "a1" 524 OpMemberName %12 1 "a2" 525 OpName %14 "b" 526 OpName %18 "level_1" 527 OpMemberName %18 0 "b1" 528 OpMemberName %18 1 "b2" 529 OpMemberName %18 2 "b3" 530 OpName %20 "l1" 531 OpName %25 "level_2" 532 OpMemberName %25 0 "c1" 533 OpMemberName %25 1 "c2" 534 OpName %27 "l2" 535 %2 = OpTypeVoid 536 %3 = OpTypeFunction %2 537 %6 = OpTypeInt 32 1 538 %7 = OpTypePointer Function %6 539 %9 = OpConstant %6 1 540 %11 = OpConstant %6 2 541 %12 = OpTypeStruct %6 %6 542 %13 = OpTypePointer Function %12 543 %18 = OpTypeStruct %12 %12 %12 544 %19 = OpTypePointer Function %18 545 %25 = OpTypeStruct %18 %18 546 %26 = OpTypePointer Function %25 547 %31 = OpTypeBool 548 %32 = OpConstantTrue %31 549 %4 = OpFunction %2 None %3 550 %5 = OpLabel 551 %8 = OpVariable %7 Function 552 %10 = OpVariable %7 Function 553 %14 = OpVariable %13 Function 554 %20 = OpVariable %19 Function 555 %27 = OpVariable %26 Function 556 OpStore %8 %9 557 OpStore %10 %11 558 %15 = OpLoad %6 %8 559 %16 = OpLoad %6 %10 560 %17 = OpCompositeConstruct %12 %15 %16 561 OpStore %14 %17 562 %21 = OpLoad %12 %14 563 %22 = OpLoad %12 %14 564 %23 = OpLoad %12 %14 565 %24 = OpCompositeConstruct %18 %21 %22 %23 566 OpStore %20 %24 567 %28 = OpLoad %18 %20 568 %29 = OpLoad %18 %20 569 %30 = OpCompositeConstruct %25 %28 %29 570 OpStore %27 %30 571 OpSelectionMerge %34 None 572 OpBranchConditional %32 %33 %34 573 %33 = OpLabel 574 OpBranch %34 575 %34 = OpLabel 576 OpReturn 577 OpFunctionEnd 578 )"; 579 580 const auto env = SPV_ENV_UNIVERSAL_1_4; 581 const auto consumer = nullptr; 582 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 583 spvtools::ValidatorOptions validator_options; 584 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 585 kConsoleMessageConsumer)); 586 TransformationContext transformation_context( 587 MakeUnique<FactManager>(context.get()), validator_options); 588 auto transformation_good_1 = TransformationCompositeInsert( 589 MakeInstructionDescriptor(30, spv::Op::OpStore, 0), 50, 30, 11, 590 {1, 0, 0}); 591 ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), 592 transformation_context)); 593 ApplyAndCheckFreshIds(transformation_good_1, context.get(), 594 &transformation_context); 595 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 596 kConsoleMessageConsumer)); 597 598 // These synonyms should have been added. 599 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 600 MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); 601 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 602 MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); 603 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 604 MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); 605 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 606 MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); 607 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 608 MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); 609 610 // These synonyms should not have been added. 611 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 612 MakeDataDescriptor(30, {1}), MakeDataDescriptor(50, {1}))); 613 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 614 MakeDataDescriptor(30, {1, 0}), MakeDataDescriptor(50, {1, 0}))); 615 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 616 MakeDataDescriptor(30, {1, 0, 0}), MakeDataDescriptor(50, {1, 0, 0}))); 617 618 auto transformation_good_2 = TransformationCompositeInsert( 619 MakeInstructionDescriptor(50, spv::Op::OpStore, 0), 51, 50, 11, 620 {0, 1, 1}); 621 ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), 622 transformation_context)); 623 ApplyAndCheckFreshIds(transformation_good_2, context.get(), 624 &transformation_context); 625 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 626 kConsoleMessageConsumer)); 627 628 // These synonyms should have been added. 629 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 630 MakeDataDescriptor(50, {1}), MakeDataDescriptor(51, {1}))); 631 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 632 MakeDataDescriptor(50, {0, 0}), MakeDataDescriptor(51, {0, 0}))); 633 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 634 MakeDataDescriptor(50, {0, 2}), MakeDataDescriptor(51, {0, 2}))); 635 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 636 MakeDataDescriptor(50, {0, 1, 0}), MakeDataDescriptor(51, {0, 1, 0}))); 637 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 638 MakeDataDescriptor(51, {0, 1, 1}), MakeDataDescriptor(11, {}))); 639 640 // These synonyms should not have been added. 641 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 642 MakeDataDescriptor(50, {0}), MakeDataDescriptor(51, {0}))); 643 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 644 MakeDataDescriptor(50, {0, 1}), MakeDataDescriptor(51, {0, 1}))); 645 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 646 MakeDataDescriptor(50, {0, 1, 1}), MakeDataDescriptor(51, {0, 1, 1}))); 647 648 std::string after_transformations = R"( 649 OpCapability Shader 650 %1 = OpExtInstImport "GLSL.std.450" 651 OpMemoryModel Logical GLSL450 652 OpEntryPoint Fragment %4 "main" 653 OpExecutionMode %4 OriginUpperLeft 654 OpSource ESSL 310 655 OpName %4 "main" 656 OpName %8 "i1" 657 OpName %10 "i2" 658 OpName %12 "base" 659 OpMemberName %12 0 "a1" 660 OpMemberName %12 1 "a2" 661 OpName %14 "b" 662 OpName %18 "level_1" 663 OpMemberName %18 0 "b1" 664 OpMemberName %18 1 "b2" 665 OpMemberName %18 2 "b3" 666 OpName %20 "l1" 667 OpName %25 "level_2" 668 OpMemberName %25 0 "c1" 669 OpMemberName %25 1 "c2" 670 OpName %27 "l2" 671 %2 = OpTypeVoid 672 %3 = OpTypeFunction %2 673 %6 = OpTypeInt 32 1 674 %7 = OpTypePointer Function %6 675 %9 = OpConstant %6 1 676 %11 = OpConstant %6 2 677 %12 = OpTypeStruct %6 %6 678 %13 = OpTypePointer Function %12 679 %18 = OpTypeStruct %12 %12 %12 680 %19 = OpTypePointer Function %18 681 %25 = OpTypeStruct %18 %18 682 %26 = OpTypePointer Function %25 683 %31 = OpTypeBool 684 %32 = OpConstantTrue %31 685 %4 = OpFunction %2 None %3 686 %5 = OpLabel 687 %8 = OpVariable %7 Function 688 %10 = OpVariable %7 Function 689 %14 = OpVariable %13 Function 690 %20 = OpVariable %19 Function 691 %27 = OpVariable %26 Function 692 OpStore %8 %9 693 OpStore %10 %11 694 %15 = OpLoad %6 %8 695 %16 = OpLoad %6 %10 696 %17 = OpCompositeConstruct %12 %15 %16 697 OpStore %14 %17 698 %21 = OpLoad %12 %14 699 %22 = OpLoad %12 %14 700 %23 = OpLoad %12 %14 701 %24 = OpCompositeConstruct %18 %21 %22 %23 702 OpStore %20 %24 703 %28 = OpLoad %18 %20 704 %29 = OpLoad %18 %20 705 %30 = OpCompositeConstruct %25 %28 %29 706 %50 = OpCompositeInsert %25 %11 %30 1 0 0 707 %51 = OpCompositeInsert %25 %11 %50 0 1 1 708 OpStore %27 %30 709 OpSelectionMerge %34 None 710 OpBranchConditional %32 %33 %34 711 %33 = OpLabel 712 OpBranch %34 713 %34 = OpLabel 714 OpReturn 715 OpFunctionEnd 716 )"; 717 718 ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); 719} 720 721TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) { 722 // This test handles cases where either the composite or the object is not 723 // available before the |instruction_to_insert_before|. 724 725 std::string shader = R"( 726 OpCapability Shader 727 %1 = OpExtInstImport "GLSL.std.450" 728 OpMemoryModel Logical GLSL450 729 OpEntryPoint Fragment %4 "main" 730 OpExecutionMode %4 OriginUpperLeft 731 OpSource ESSL 310 732 OpName %4 "main" 733 OpName %8 "i1" 734 OpName %10 "i2" 735 OpName %12 "base" 736 OpMemberName %12 0 "a1" 737 OpMemberName %12 1 "a2" 738 OpName %14 "b1" 739 OpName %18 "b2" 740 OpName %22 "lvl1" 741 OpMemberName %22 0 "b1" 742 OpMemberName %22 1 "b2" 743 OpName %24 "l1" 744 OpName %28 "i3" 745 %2 = OpTypeVoid 746 %3 = OpTypeFunction %2 747 %6 = OpTypeInt 32 1 748 %7 = OpTypePointer Function %6 749 %9 = OpConstant %6 1 750 %11 = OpConstant %6 2 751 %12 = OpTypeStruct %6 %6 752 %13 = OpTypePointer Function %12 753 %22 = OpTypeStruct %12 %12 754 %23 = OpTypePointer Function %22 755 %4 = OpFunction %2 None %3 756 %5 = OpLabel 757 %8 = OpVariable %7 Function 758 %10 = OpVariable %7 Function 759 %14 = OpVariable %13 Function 760 %18 = OpVariable %13 Function 761 %24 = OpVariable %23 Function 762 %28 = OpVariable %7 Function 763 OpStore %8 %9 764 OpStore %10 %11 765 %15 = OpLoad %6 %8 766 %16 = OpLoad %6 %10 767 %17 = OpCompositeConstruct %12 %15 %16 768 OpStore %14 %17 769 %19 = OpLoad %6 %10 770 %20 = OpLoad %6 %8 771 %21 = OpCompositeConstruct %12 %19 %20 772 OpStore %18 %21 773 %25 = OpLoad %12 %14 774 %26 = OpLoad %12 %18 775 %27 = OpCompositeConstruct %22 %25 %26 776 OpStore %24 %27 777 %29 = OpLoad %6 %8 778 %30 = OpLoad %6 %10 779 %31 = OpIMul %6 %29 %30 780 OpStore %28 %31 781 %60 = OpCompositeConstruct %12 %20 %19 782 %61 = OpCompositeConstruct %22 %26 %25 783 OpReturn 784 OpFunctionEnd 785 )"; 786 787 const auto env = SPV_ENV_UNIVERSAL_1_4; 788 const auto consumer = nullptr; 789 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 790 spvtools::ValidatorOptions validator_options; 791 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 792 kConsoleMessageConsumer)); 793 TransformationContext transformation_context( 794 MakeUnique<FactManager>(context.get()), validator_options); 795 // Bad: The object with |object_id| is not available at 796 // |instruction_to_insert_before|. 797 auto transformation_bad_1 = TransformationCompositeInsert( 798 MakeInstructionDescriptor(31, spv::Op::OpIMul, 0), 50, 27, 60, {1}); 799 ASSERT_FALSE( 800 transformation_bad_1.IsApplicable(context.get(), transformation_context)); 801 802 // Bad: The composite with |composite_id| is not available at 803 // |instruction_to_insert_before|. 804 auto transformation_bad_2 = TransformationCompositeInsert( 805 MakeInstructionDescriptor(31, spv::Op::OpIMul, 0), 50, 61, 21, {1}); 806 ASSERT_FALSE( 807 transformation_bad_2.IsApplicable(context.get(), transformation_context)); 808 809 // Bad: The |instruction_to_insert_before| is the composite itself and is 810 // available. 811 auto transformation_bad_3 = TransformationCompositeInsert( 812 MakeInstructionDescriptor(61, spv::Op::OpCompositeConstruct, 0), 50, 61, 813 21, {1}); 814 ASSERT_FALSE( 815 transformation_bad_3.IsApplicable(context.get(), transformation_context)); 816 817 // Bad: The |instruction_to_insert_before| is the object itself and is not 818 // available. 819 auto transformation_bad_4 = TransformationCompositeInsert( 820 MakeInstructionDescriptor(60, spv::Op::OpCompositeConstruct, 0), 50, 27, 821 60, {1}); 822 ASSERT_FALSE( 823 transformation_bad_4.IsApplicable(context.get(), transformation_context)); 824} 825 826TEST(TransformationCompositeInsertTest, CompositeInsertionWithIrrelevantIds) { 827 // This checks that we do *not* get data synonym facts when we do composite 828 // insertion using irrelevant ids or in dead blocks. 829 830 std::string shader = R"( 831 OpCapability Shader 832 %1 = OpExtInstImport "GLSL.std.450" 833 OpMemoryModel Logical GLSL450 834 OpEntryPoint Fragment %12 "main" 835 OpExecutionMode %12 OriginUpperLeft 836 OpSource ESSL 310 837 %2 = OpTypeVoid 838 %3 = OpTypeFunction %2 839 %6 = OpTypeInt 32 1 840 %7 = OpTypeVector %6 2 841 %8 = OpConstant %6 0 842 %9 = OpConstantComposite %7 %8 %8 843 %10 = OpTypeBool 844 %11 = OpConstantFalse %10 845 %16 = OpConstant %6 0 846 %17 = OpConstant %6 1 847 %18 = OpConstantComposite %7 %8 %8 848 %12 = OpFunction %2 None %3 849 %13 = OpLabel 850 OpSelectionMerge %15 None 851 OpBranchConditional %11 %14 %15 852 %14 = OpLabel 853 OpBranch %15 854 %15 = OpLabel 855 OpReturn 856 OpFunctionEnd 857 )"; 858 859 const auto env = SPV_ENV_UNIVERSAL_1_3; 860 const auto consumer = nullptr; 861 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 862 spvtools::ValidatorOptions validator_options; 863 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 864 kConsoleMessageConsumer)); 865 TransformationContext transformation_context( 866 MakeUnique<FactManager>(context.get()), validator_options); 867 868 transformation_context.GetFactManager()->AddFactBlockIsDead(14); 869 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16); 870 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(18); 871 872 // Leads to synonyms - nothing is irrelevant. 873 auto transformation1 = TransformationCompositeInsert( 874 MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 100, 9, 17, 875 {0}); 876 ASSERT_TRUE( 877 transformation1.IsApplicable(context.get(), transformation_context)); 878 ApplyAndCheckFreshIds(transformation1, context.get(), 879 &transformation_context); 880 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 881 MakeDataDescriptor(100, {0}), MakeDataDescriptor(17, {}))); 882 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 883 MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {1}))); 884 885 // Because %16 is irrelevant, we don't get a synonym with the component to 886 // which it has been inserted (but we do for the other component). 887 auto transformation2 = TransformationCompositeInsert( 888 MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 101, 9, 16, 889 {0}); 890 ASSERT_TRUE( 891 transformation2.IsApplicable(context.get(), transformation_context)); 892 ApplyAndCheckFreshIds(transformation2, context.get(), 893 &transformation_context); 894 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 895 MakeDataDescriptor(101, {0}), MakeDataDescriptor(16, {}))); 896 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 897 MakeDataDescriptor(101, {1}), MakeDataDescriptor(9, {1}))); 898 899 // Because %18 is irrelevant we only get a synonym for the component into 900 // which insertion has taken place. 901 auto transformation3 = TransformationCompositeInsert( 902 MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0), 102, 18, 17, 903 {0}); 904 ASSERT_TRUE( 905 transformation3.IsApplicable(context.get(), transformation_context)); 906 ApplyAndCheckFreshIds(transformation3, context.get(), 907 &transformation_context); 908 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 909 MakeDataDescriptor(102, {0}), MakeDataDescriptor(17, {}))); 910 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 911 MakeDataDescriptor(102, {1}), MakeDataDescriptor(18, {1}))); 912 913 // Does not lead to synonyms as block %14 is dead. 914 auto transformation4 = TransformationCompositeInsert( 915 MakeInstructionDescriptor(14, spv::Op::OpBranch, 0), 103, 9, 17, {0}); 916 ASSERT_TRUE( 917 transformation4.IsApplicable(context.get(), transformation_context)); 918 ApplyAndCheckFreshIds(transformation4, context.get(), 919 &transformation_context); 920 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 921 MakeDataDescriptor(103, {0}), MakeDataDescriptor(17, {}))); 922 ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( 923 MakeDataDescriptor(103, {1}), MakeDataDescriptor(9, {1}))); 924 925 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 926 kConsoleMessageConsumer)); 927} 928 929} // namespace 930} // namespace fuzz 931} // namespace spvtools 932