1// Copyright (c) 2020 Vasyl Teliman 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_add_synonym.h" 16 17#include "gtest/gtest.h" 18#include "source/fuzz/fuzzer_util.h" 19#include "source/fuzz/instruction_descriptor.h" 20#include "test/fuzz/fuzz_test_util.h" 21 22namespace spvtools { 23namespace fuzz { 24namespace { 25 26TEST(TransformationAddSynonymTest, NotApplicable) { 27 std::string shader = R"( 28 OpCapability Shader 29 %1 = OpExtInstImport "GLSL.std.450" 30 OpMemoryModel Logical GLSL450 31 OpEntryPoint Fragment %4 "main" 32 OpExecutionMode %4 OriginUpperLeft 33 OpSource ESSL 310 34 OpDecorate %8 RelaxedPrecision 35 OpDecorate %22 RelaxedPrecision 36 %2 = OpTypeVoid 37 %3 = OpTypeFunction %2 38 %6 = OpTypeInt 32 1 39 %7 = OpTypePointer Function %6 40 %9 = OpConstant %6 3 41 %10 = OpTypeFloat 32 42 %11 = OpTypePointer Function %10 43 %13 = OpConstant %10 4.5 44 %14 = OpTypeVector %10 2 45 %15 = OpTypePointer Function %14 46 %17 = OpConstant %10 3 47 %18 = OpConstant %10 4 48 %19 = OpConstantComposite %14 %17 %18 49 %20 = OpTypeVector %6 2 50 %21 = OpTypePointer Function %20 51 %23 = OpConstant %6 4 52 %24 = OpConstantComposite %20 %9 %23 53 %26 = OpConstantNull %6 54 %4 = OpFunction %2 None %3 55 %5 = OpLabel 56 %8 = OpVariable %7 Function 57 %12 = OpVariable %11 Function 58 %16 = OpVariable %15 Function 59 %22 = OpVariable %21 Function 60 OpStore %8 %9 61 OpStore %12 %13 62 OpStore %16 %19 63 OpStore %22 %24 64 %25 = OpUndef %6 65 %27 = OpLoad %6 %8 66 OpReturn 67 OpFunctionEnd 68 )"; 69 70 const auto env = SPV_ENV_UNIVERSAL_1_3; 71 const auto consumer = nullptr; 72 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 73 spvtools::ValidatorOptions validator_options; 74 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 75 kConsoleMessageConsumer)); 76 TransformationContext transformation_context( 77 MakeUnique<FactManager>(context.get()), validator_options); 78 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24); 79 80 auto insert_before = MakeInstructionDescriptor(22, spv::Op::OpReturn, 0); 81 82#ifndef NDEBUG 83 ASSERT_DEATH( 84 TransformationAddSynonym( 85 9, static_cast<protobufs::TransformationAddSynonym::SynonymType>(-1), 86 40, insert_before) 87 .IsApplicable(context.get(), transformation_context), 88 "Synonym type is invalid"); 89#endif 90 91 // These tests should succeed regardless of the synonym type. 92 for (int i = 0; 93 i < protobufs::TransformationAddSynonym::SynonymType_descriptor() 94 ->value_count(); 95 ++i) { 96 const auto* synonym_value = 97 protobufs::TransformationAddSynonym::SynonymType_descriptor()->value(i); 98 ASSERT_TRUE(protobufs::TransformationAddSynonym::SynonymType_IsValid( 99 synonym_value->number())); 100 auto synonym_type = 101 static_cast<protobufs::TransformationAddSynonym::SynonymType>( 102 synonym_value->number()); 103 104 // |synonym_fresh_id| is not fresh. 105 ASSERT_FALSE(TransformationAddSynonym(9, synonym_type, 9, insert_before) 106 .IsApplicable(context.get(), transformation_context)); 107 108 // |result_id| is invalid. 109 ASSERT_FALSE(TransformationAddSynonym(40, synonym_type, 40, insert_before) 110 .IsApplicable(context.get(), transformation_context)); 111 112 // Instruction with |result_id| has no type id. 113 ASSERT_FALSE(TransformationAddSynonym(5, synonym_type, 40, insert_before) 114 .IsApplicable(context.get(), transformation_context)); 115 116 // Instruction with |result_id| is an OpUndef. 117 ASSERT_FALSE(TransformationAddSynonym(25, synonym_type, 40, insert_before) 118 .IsApplicable(context.get(), transformation_context)); 119 120 // Instruction with |result_id| is an OpConstantNull. 121 ASSERT_FALSE(TransformationAddSynonym(26, synonym_type, 40, insert_before) 122 .IsApplicable(context.get(), transformation_context)); 123 124 // |result_id| is irrelevant. 125 ASSERT_FALSE(TransformationAddSynonym(24, synonym_type, 40, insert_before) 126 .IsApplicable(context.get(), transformation_context)); 127 128 // |insert_before| is invalid. 129 ASSERT_FALSE(TransformationAddSynonym( 130 9, synonym_type, 40, 131 MakeInstructionDescriptor(25, spv::Op::OpStore, 0)) 132 .IsApplicable(context.get(), transformation_context)); 133 134 // Can't insert before |insert_before|. 135 ASSERT_FALSE(TransformationAddSynonym( 136 9, synonym_type, 40, 137 MakeInstructionDescriptor(5, spv::Op::OpLabel, 0)) 138 .IsApplicable(context.get(), transformation_context)); 139 ASSERT_FALSE(TransformationAddSynonym( 140 9, synonym_type, 40, 141 MakeInstructionDescriptor(22, spv::Op::OpVariable, 0)) 142 .IsApplicable(context.get(), transformation_context)); 143 ASSERT_FALSE(TransformationAddSynonym( 144 9, synonym_type, 40, 145 MakeInstructionDescriptor(25, spv::Op::OpFunctionEnd, 0)) 146 .IsApplicable(context.get(), transformation_context)); 147 148 // Domination rules are not satisfied. 149 ASSERT_FALSE(TransformationAddSynonym( 150 27, synonym_type, 40, 151 MakeInstructionDescriptor(27, spv::Op::OpLoad, 0)) 152 .IsApplicable(context.get(), transformation_context)); 153 ASSERT_FALSE(TransformationAddSynonym( 154 27, synonym_type, 40, 155 MakeInstructionDescriptor(22, spv::Op::OpStore, 1)) 156 .IsApplicable(context.get(), transformation_context)); 157 } 158} 159 160TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { 161 std::string shader = R"( 162 OpCapability Shader 163 %1 = OpExtInstImport "GLSL.std.450" 164 OpMemoryModel Logical GLSL450 165 OpEntryPoint Fragment %4 "main" 166 OpExecutionMode %4 OriginUpperLeft 167 OpSource ESSL 310 168 %2 = OpTypeVoid 169 %3 = OpTypeFunction %2 170 %6 = OpTypeInt 32 1 171 %7 = OpConstant %6 0 172 %8 = OpConstant %6 1 173 %9 = OpConstant %6 34 174 %10 = OpTypeInt 32 0 175 %13 = OpConstant %10 34 176 %14 = OpTypeFloat 32 177 %15 = OpConstant %14 0 178 %16 = OpConstant %14 1 179 %17 = OpConstant %14 34 180 %18 = OpTypeVector %14 2 181 %19 = OpConstantComposite %18 %15 %15 182 %20 = OpConstantComposite %18 %16 %16 183 %21 = OpConstant %14 3 184 %22 = OpConstant %14 4 185 %23 = OpConstantComposite %18 %21 %22 186 %24 = OpTypeVector %6 2 187 %25 = OpConstantComposite %24 %7 %7 188 %26 = OpConstantComposite %24 %8 %8 189 %27 = OpConstant %6 3 190 %28 = OpConstant %6 4 191 %29 = OpConstantComposite %24 %27 %28 192 %30 = OpTypeVector %10 2 193 %33 = OpConstant %10 3 194 %34 = OpConstant %10 4 195 %35 = OpConstantComposite %30 %33 %34 196 %36 = OpTypeBool 197 %37 = OpTypeVector %36 2 198 %38 = OpConstantTrue %36 199 %39 = OpConstantComposite %37 %38 %38 200 %40 = OpConstant %6 37 201 %4 = OpFunction %2 None %3 202 %5 = OpLabel 203 OpReturn 204 OpFunctionEnd 205 )"; 206 207 const auto env = SPV_ENV_UNIVERSAL_1_3; 208 const auto consumer = nullptr; 209 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 210 spvtools::ValidatorOptions validator_options; 211 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 212 kConsoleMessageConsumer)); 213 TransformationContext transformation_context( 214 MakeUnique<FactManager>(context.get()), validator_options); 215 auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0); 216 217 uint32_t fresh_id = 50; 218 for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO, 219 protobufs::TransformationAddSynonym::SUB_ZERO, 220 protobufs::TransformationAddSynonym::MUL_ONE}) { 221 ASSERT_TRUE( 222 TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type)); 223 224 // Can't create a synonym of a scalar or a vector of a wrong (in this case - 225 // boolean) type. 226 ASSERT_FALSE( 227 TransformationAddSynonym(38, synonym_type, fresh_id, insert_before) 228 .IsApplicable(context.get(), transformation_context)); 229 ASSERT_FALSE( 230 TransformationAddSynonym(39, synonym_type, fresh_id, insert_before) 231 .IsApplicable(context.get(), transformation_context)); 232 233 // Required constant is not present in the module. 234 ASSERT_FALSE( 235 TransformationAddSynonym(13, synonym_type, fresh_id, insert_before) 236 .IsApplicable(context.get(), transformation_context)); 237 ASSERT_FALSE( 238 TransformationAddSynonym(35, synonym_type, fresh_id, insert_before) 239 .IsApplicable(context.get(), transformation_context)); 240 241 for (auto result_id : {9, 17, 23, 29}) { 242 TransformationAddSynonym transformation(result_id, synonym_type, fresh_id, 243 insert_before); 244 ASSERT_TRUE( 245 transformation.IsApplicable(context.get(), transformation_context)); 246 ApplyAndCheckFreshIds(transformation, context.get(), 247 &transformation_context); 248 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 249 MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {}))); 250 ++fresh_id; 251 } 252 } 253 { 254 TransformationAddSynonym transformation( 255 40, protobufs::TransformationAddSynonym::BITWISE_OR, fresh_id, 256 insert_before); 257 ASSERT_TRUE( 258 transformation.IsApplicable(context.get(), transformation_context)); 259 ApplyAndCheckFreshIds(transformation, context.get(), 260 &transformation_context); 261 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 262 MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {}))); 263 ++fresh_id; 264 } 265 { 266 TransformationAddSynonym transformation( 267 40, protobufs::TransformationAddSynonym::BITWISE_XOR, fresh_id, 268 insert_before); 269 ASSERT_TRUE( 270 transformation.IsApplicable(context.get(), transformation_context)); 271 ApplyAndCheckFreshIds(transformation, context.get(), 272 &transformation_context); 273 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 274 MakeDataDescriptor(40, {}), MakeDataDescriptor(fresh_id, {}))); 275 } 276 277 std::string expected_shader = R"( 278 OpCapability Shader 279 %1 = OpExtInstImport "GLSL.std.450" 280 OpMemoryModel Logical GLSL450 281 OpEntryPoint Fragment %4 "main" 282 OpExecutionMode %4 OriginUpperLeft 283 OpSource ESSL 310 284 %2 = OpTypeVoid 285 %3 = OpTypeFunction %2 286 %6 = OpTypeInt 32 1 287 %7 = OpConstant %6 0 288 %8 = OpConstant %6 1 289 %9 = OpConstant %6 34 290 %10 = OpTypeInt 32 0 291 %13 = OpConstant %10 34 292 %14 = OpTypeFloat 32 293 %15 = OpConstant %14 0 294 %16 = OpConstant %14 1 295 %17 = OpConstant %14 34 296 %18 = OpTypeVector %14 2 297 %19 = OpConstantComposite %18 %15 %15 298 %20 = OpConstantComposite %18 %16 %16 299 %21 = OpConstant %14 3 300 %22 = OpConstant %14 4 301 %23 = OpConstantComposite %18 %21 %22 302 %24 = OpTypeVector %6 2 303 %25 = OpConstantComposite %24 %7 %7 304 %26 = OpConstantComposite %24 %8 %8 305 %27 = OpConstant %6 3 306 %28 = OpConstant %6 4 307 %29 = OpConstantComposite %24 %27 %28 308 %30 = OpTypeVector %10 2 309 %33 = OpConstant %10 3 310 %34 = OpConstant %10 4 311 %35 = OpConstantComposite %30 %33 %34 312 %36 = OpTypeBool 313 %37 = OpTypeVector %36 2 314 %38 = OpConstantTrue %36 315 %39 = OpConstantComposite %37 %38 %38 316 %40 = OpConstant %6 37 317 %4 = OpFunction %2 None %3 318 %5 = OpLabel 319 %50 = OpIAdd %6 %9 %7 320 %51 = OpFAdd %14 %17 %15 321 %52 = OpFAdd %18 %23 %19 322 %53 = OpIAdd %24 %29 %25 323 %54 = OpISub %6 %9 %7 324 %55 = OpFSub %14 %17 %15 325 %56 = OpFSub %18 %23 %19 326 %57 = OpISub %24 %29 %25 327 %58 = OpIMul %6 %9 %8 328 %59 = OpFMul %14 %17 %16 329 %60 = OpFMul %18 %23 %20 330 %61 = OpIMul %24 %29 %26 331 %62 = OpBitwiseOr %6 %40 %7 332 %63 = OpBitwiseXor %6 %40 %7 333 OpReturn 334 OpFunctionEnd 335 )"; 336 337 ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); 338} 339 340TEST(TransformationAddSynonymTest, LogicalAndLogicalOr) { 341 std::string shader = R"( 342 OpCapability Shader 343 %1 = OpExtInstImport "GLSL.std.450" 344 OpMemoryModel Logical GLSL450 345 OpEntryPoint Fragment %4 "main" 346 OpExecutionMode %4 OriginUpperLeft 347 OpSource ESSL 310 348 %2 = OpTypeVoid 349 %3 = OpTypeFunction %2 350 %6 = OpTypeBool 351 %7 = OpConstantFalse %6 352 %9 = OpConstantTrue %6 353 %10 = OpTypeVector %6 2 354 %11 = OpConstantComposite %10 %7 %9 355 %12 = OpConstantComposite %10 %7 %7 356 %13 = OpConstantComposite %10 %9 %9 357 %14 = OpTypeFloat 32 358 %17 = OpConstant %14 35 359 %18 = OpTypeVector %14 2 360 %21 = OpConstant %14 3 361 %22 = OpConstant %14 4 362 %23 = OpConstantComposite %18 %21 %22 363 %4 = OpFunction %2 None %3 364 %5 = OpLabel 365 OpReturn 366 OpFunctionEnd 367 )"; 368 369 const auto env = SPV_ENV_UNIVERSAL_1_3; 370 const auto consumer = nullptr; 371 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 372 spvtools::ValidatorOptions validator_options; 373 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 374 kConsoleMessageConsumer)); 375 TransformationContext transformation_context( 376 MakeUnique<FactManager>(context.get()), validator_options); 377 auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0); 378 379 uint32_t fresh_id = 50; 380 for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND, 381 protobufs::TransformationAddSynonym::LOGICAL_OR}) { 382 ASSERT_TRUE( 383 TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type)); 384 385 // Can't create a synonym of a scalar or a vector of a wrong (in this case - 386 // float) type. 387 ASSERT_FALSE( 388 TransformationAddSynonym(17, synonym_type, fresh_id, insert_before) 389 .IsApplicable(context.get(), transformation_context)); 390 ASSERT_FALSE( 391 TransformationAddSynonym(23, synonym_type, fresh_id, insert_before) 392 .IsApplicable(context.get(), transformation_context)); 393 394 for (auto result_id : {9, 11}) { 395 TransformationAddSynonym transformation(result_id, synonym_type, fresh_id, 396 insert_before); 397 ASSERT_TRUE( 398 transformation.IsApplicable(context.get(), transformation_context)); 399 ApplyAndCheckFreshIds(transformation, context.get(), 400 &transformation_context); 401 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 402 MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {}))); 403 ++fresh_id; 404 } 405 } 406 407 std::string expected_shader = R"( 408 OpCapability Shader 409 %1 = OpExtInstImport "GLSL.std.450" 410 OpMemoryModel Logical GLSL450 411 OpEntryPoint Fragment %4 "main" 412 OpExecutionMode %4 OriginUpperLeft 413 OpSource ESSL 310 414 %2 = OpTypeVoid 415 %3 = OpTypeFunction %2 416 %6 = OpTypeBool 417 %7 = OpConstantFalse %6 418 %9 = OpConstantTrue %6 419 %10 = OpTypeVector %6 2 420 %11 = OpConstantComposite %10 %7 %9 421 %12 = OpConstantComposite %10 %7 %7 422 %13 = OpConstantComposite %10 %9 %9 423 %14 = OpTypeFloat 32 424 %17 = OpConstant %14 35 425 %18 = OpTypeVector %14 2 426 %21 = OpConstant %14 3 427 %22 = OpConstant %14 4 428 %23 = OpConstantComposite %18 %21 %22 429 %4 = OpFunction %2 None %3 430 %5 = OpLabel 431 %50 = OpLogicalAnd %6 %9 %9 432 %51 = OpLogicalAnd %10 %11 %13 433 %52 = OpLogicalOr %6 %9 %7 434 %53 = OpLogicalOr %10 %11 %12 435 OpReturn 436 OpFunctionEnd 437 )"; 438 439 ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); 440} 441 442TEST(TransformationAddSynonymTest, LogicalAndConstantIsNotPresent) { 443 std::string shader = R"( 444 OpCapability Shader 445 %1 = OpExtInstImport "GLSL.std.450" 446 OpMemoryModel Logical GLSL450 447 OpEntryPoint Fragment %4 "main" 448 OpExecutionMode %4 OriginUpperLeft 449 OpSource ESSL 310 450 %2 = OpTypeVoid 451 %3 = OpTypeFunction %2 452 %6 = OpTypeBool 453 %7 = OpConstantFalse %6 454 %10 = OpTypeVector %6 2 455 %12 = OpConstantComposite %10 %7 %7 456 %4 = OpFunction %2 None %3 457 %5 = OpLabel 458 OpReturn 459 OpFunctionEnd 460 )"; 461 462 const auto env = SPV_ENV_UNIVERSAL_1_3; 463 const auto consumer = nullptr; 464 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 465 spvtools::ValidatorOptions validator_options; 466 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 467 kConsoleMessageConsumer)); 468 TransformationContext transformation_context( 469 MakeUnique<FactManager>(context.get()), validator_options); 470 auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0); 471 const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND; 472 473 // Required constant is not present in the module. 474 ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before) 475 .IsApplicable(context.get(), transformation_context)); 476 ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before) 477 .IsApplicable(context.get(), transformation_context)); 478} 479 480TEST(TransformationAddSynonymTest, LogicalOrConstantIsNotPresent) { 481 std::string shader = R"( 482 OpCapability Shader 483 %1 = OpExtInstImport "GLSL.std.450" 484 OpMemoryModel Logical GLSL450 485 OpEntryPoint Fragment %4 "main" 486 OpExecutionMode %4 OriginUpperLeft 487 OpSource ESSL 310 488 %2 = OpTypeVoid 489 %3 = OpTypeFunction %2 490 %6 = OpTypeBool 491 %7 = OpConstantTrue %6 492 %10 = OpTypeVector %6 2 493 %12 = OpConstantComposite %10 %7 %7 494 %4 = OpFunction %2 None %3 495 %5 = OpLabel 496 OpReturn 497 OpFunctionEnd 498 )"; 499 500 const auto env = SPV_ENV_UNIVERSAL_1_3; 501 const auto consumer = nullptr; 502 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 503 spvtools::ValidatorOptions validator_options; 504 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 505 kConsoleMessageConsumer)); 506 TransformationContext transformation_context( 507 MakeUnique<FactManager>(context.get()), validator_options); 508 auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0); 509 const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR; 510 511 // Required constant is not present in the module. 512 ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before) 513 .IsApplicable(context.get(), transformation_context)); 514 ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before) 515 .IsApplicable(context.get(), transformation_context)); 516} 517 518TEST(TransformationAddSynonymTest, CopyObject) { 519 std::string shader = R"( 520 OpCapability Shader 521 %1 = OpExtInstImport "GLSL.std.450" 522 OpMemoryModel Logical GLSL450 523 OpEntryPoint Fragment %4 "main" 524 OpExecutionMode %4 OriginUpperLeft 525 OpSource ESSL 310 526 OpDecorate %8 RelaxedPrecision 527 %2 = OpTypeVoid 528 %3 = OpTypeFunction %2 529 %6 = OpTypeInt 32 1 530 %7 = OpTypePointer Function %6 531 %9 = OpConstant %6 4 532 %10 = OpTypeFloat 32 533 %11 = OpTypePointer Function %10 534 %13 = OpConstant %10 4 535 %14 = OpTypeVector %10 2 536 %15 = OpTypePointer Function %14 537 %17 = OpConstant %10 3.4000001 538 %18 = OpConstantComposite %14 %17 %17 539 %19 = OpTypeBool 540 %20 = OpTypeStruct %19 541 %21 = OpTypePointer Function %20 542 %23 = OpConstantTrue %19 543 %24 = OpConstantComposite %20 %23 544 %4 = OpFunction %2 None %3 545 %5 = OpLabel 546 %8 = OpVariable %7 Function 547 %12 = OpVariable %11 Function 548 %16 = OpVariable %15 Function 549 %22 = OpVariable %21 Function 550 OpStore %8 %9 551 OpStore %12 %13 552 OpStore %16 %18 553 OpStore %22 %24 554 OpReturn 555 OpFunctionEnd 556 )"; 557 558 const auto env = SPV_ENV_UNIVERSAL_1_3; 559 const auto consumer = nullptr; 560 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 561 spvtools::ValidatorOptions validator_options; 562 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 563 kConsoleMessageConsumer)); 564 TransformationContext transformation_context( 565 MakeUnique<FactManager>(context.get()), validator_options); 566 auto insert_before = MakeInstructionDescriptor(5, spv::Op::OpReturn, 0); 567 const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT; 568 569 ASSERT_FALSE( 570 TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type)); 571 572 uint32_t fresh_id = 50; 573 for (auto result_id : {9, 13, 17, 18, 23, 24, 22}) { 574 TransformationAddSynonym transformation(result_id, synonym_type, fresh_id, 575 insert_before); 576 ASSERT_TRUE( 577 transformation.IsApplicable(context.get(), transformation_context)); 578 ApplyAndCheckFreshIds(transformation, context.get(), 579 &transformation_context); 580 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 581 MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {}))); 582 ++fresh_id; 583 } 584 585 std::string expected_shader = R"( 586 OpCapability Shader 587 %1 = OpExtInstImport "GLSL.std.450" 588 OpMemoryModel Logical GLSL450 589 OpEntryPoint Fragment %4 "main" 590 OpExecutionMode %4 OriginUpperLeft 591 OpSource ESSL 310 592 OpDecorate %8 RelaxedPrecision 593 %2 = OpTypeVoid 594 %3 = OpTypeFunction %2 595 %6 = OpTypeInt 32 1 596 %7 = OpTypePointer Function %6 597 %9 = OpConstant %6 4 598 %10 = OpTypeFloat 32 599 %11 = OpTypePointer Function %10 600 %13 = OpConstant %10 4 601 %14 = OpTypeVector %10 2 602 %15 = OpTypePointer Function %14 603 %17 = OpConstant %10 3.4000001 604 %18 = OpConstantComposite %14 %17 %17 605 %19 = OpTypeBool 606 %20 = OpTypeStruct %19 607 %21 = OpTypePointer Function %20 608 %23 = OpConstantTrue %19 609 %24 = OpConstantComposite %20 %23 610 %4 = OpFunction %2 None %3 611 %5 = OpLabel 612 %8 = OpVariable %7 Function 613 %12 = OpVariable %11 Function 614 %16 = OpVariable %15 Function 615 %22 = OpVariable %21 Function 616 OpStore %8 %9 617 OpStore %12 %13 618 OpStore %16 %18 619 OpStore %22 %24 620 %50 = OpCopyObject %6 %9 621 %51 = OpCopyObject %10 %13 622 %52 = OpCopyObject %10 %17 623 %53 = OpCopyObject %14 %18 624 %54 = OpCopyObject %19 %23 625 %55 = OpCopyObject %20 %24 626 %56 = OpCopyObject %21 %22 627 OpReturn 628 OpFunctionEnd 629 )"; 630 631 ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); 632} 633 634TEST(TransformationAddSynonymTest, CopyBooleanConstants) { 635 std::string shader = R"( 636 OpCapability Shader 637 %1 = OpExtInstImport "GLSL.std.450" 638 OpMemoryModel Logical GLSL450 639 OpEntryPoint Fragment %4 "main" 640 OpExecutionMode %4 OriginUpperLeft 641 OpSource ESSL 310 642 OpName %4 "main" 643 %2 = OpTypeVoid 644 %6 = OpTypeBool 645 %7 = OpConstantTrue %6 646 %8 = OpConstantFalse %6 647 %3 = OpTypeFunction %2 648 %4 = OpFunction %2 None %3 649 %5 = OpLabel 650 OpReturn 651 OpFunctionEnd 652 )"; 653 654 const auto env = SPV_ENV_UNIVERSAL_1_3; 655 const auto consumer = nullptr; 656 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 657 spvtools::ValidatorOptions validator_options; 658 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 659 kConsoleMessageConsumer)); 660 TransformationContext transformation_context( 661 MakeUnique<FactManager>(context.get()), validator_options); 662 ASSERT_EQ(0, transformation_context.GetFactManager() 663 ->GetIdsForWhichSynonymsAreKnown() 664 .size()); 665 666 { 667 TransformationAddSynonym copy_true( 668 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, 669 MakeInstructionDescriptor(5, spv::Op::OpReturn, 0)); 670 ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context)); 671 ApplyAndCheckFreshIds(copy_true, context.get(), &transformation_context); 672 673 std::vector<uint32_t> ids_for_which_synonyms_are_known = 674 transformation_context.GetFactManager() 675 ->GetIdsForWhichSynonymsAreKnown(); 676 ASSERT_EQ(2, ids_for_which_synonyms_are_known.size()); 677 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), 678 ids_for_which_synonyms_are_known.end(), 679 7) != ids_for_which_synonyms_are_known.end()); 680 ASSERT_EQ( 681 2, transformation_context.GetFactManager()->GetSynonymsForId(7).size()); 682 protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {}); 683 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 684 MakeDataDescriptor(7, {}), descriptor_100)); 685 } 686 687 { 688 TransformationAddSynonym copy_false( 689 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101, 690 MakeInstructionDescriptor(100, spv::Op::OpReturn, 0)); 691 ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context)); 692 ApplyAndCheckFreshIds(copy_false, context.get(), &transformation_context); 693 std::vector<uint32_t> ids_for_which_synonyms_are_known = 694 transformation_context.GetFactManager() 695 ->GetIdsForWhichSynonymsAreKnown(); 696 ASSERT_EQ(4, ids_for_which_synonyms_are_known.size()); 697 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), 698 ids_for_which_synonyms_are_known.end(), 699 8) != ids_for_which_synonyms_are_known.end()); 700 ASSERT_EQ( 701 2, transformation_context.GetFactManager()->GetSynonymsForId(8).size()); 702 protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {}); 703 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 704 MakeDataDescriptor(8, {}), descriptor_101)); 705 } 706 707 { 708 TransformationAddSynonym copy_false_again( 709 101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102, 710 MakeInstructionDescriptor(5, spv::Op::OpReturn, 0)); 711 ASSERT_TRUE( 712 copy_false_again.IsApplicable(context.get(), transformation_context)); 713 ApplyAndCheckFreshIds(copy_false_again, context.get(), 714 &transformation_context); 715 std::vector<uint32_t> ids_for_which_synonyms_are_known = 716 transformation_context.GetFactManager() 717 ->GetIdsForWhichSynonymsAreKnown(); 718 ASSERT_EQ(5, ids_for_which_synonyms_are_known.size()); 719 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), 720 ids_for_which_synonyms_are_known.end(), 721 101) != ids_for_which_synonyms_are_known.end()); 722 ASSERT_EQ( 723 3, 724 transformation_context.GetFactManager()->GetSynonymsForId(101).size()); 725 protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {}); 726 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 727 MakeDataDescriptor(101, {}), descriptor_102)); 728 } 729 730 { 731 TransformationAddSynonym copy_true_again( 732 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103, 733 MakeInstructionDescriptor(102, spv::Op::OpReturn, 0)); 734 ASSERT_TRUE( 735 copy_true_again.IsApplicable(context.get(), transformation_context)); 736 ApplyAndCheckFreshIds(copy_true_again, context.get(), 737 &transformation_context); 738 std::vector<uint32_t> ids_for_which_synonyms_are_known = 739 transformation_context.GetFactManager() 740 ->GetIdsForWhichSynonymsAreKnown(); 741 ASSERT_EQ(6, ids_for_which_synonyms_are_known.size()); 742 ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), 743 ids_for_which_synonyms_are_known.end(), 744 7) != ids_for_which_synonyms_are_known.end()); 745 ASSERT_EQ( 746 3, transformation_context.GetFactManager()->GetSynonymsForId(7).size()); 747 protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {}); 748 ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( 749 MakeDataDescriptor(7, {}), descriptor_103)); 750 } 751 752 std::string after_transformation = R"( 753 OpCapability Shader 754 %1 = OpExtInstImport "GLSL.std.450" 755 OpMemoryModel Logical GLSL450 756 OpEntryPoint Fragment %4 "main" 757 OpExecutionMode %4 OriginUpperLeft 758 OpSource ESSL 310 759 OpName %4 "main" 760 %2 = OpTypeVoid 761 %6 = OpTypeBool 762 %7 = OpConstantTrue %6 763 %8 = OpConstantFalse %6 764 %3 = OpTypeFunction %2 765 %4 = OpFunction %2 None %3 766 %5 = OpLabel 767 %100 = OpCopyObject %6 %7 768 %101 = OpCopyObject %6 %8 769 %102 = OpCopyObject %6 %101 770 %103 = OpCopyObject %6 %7 771 OpReturn 772 OpFunctionEnd 773 )"; 774 775 ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); 776} 777 778TEST(TransformationAddSynonymTest, CheckIllegalCases) { 779 // The following SPIR-V comes from this GLSL, pushed through spirv-opt 780 // and then doctored a bit. 781 // 782 // #version 310 es 783 // 784 // precision highp float; 785 // 786 // struct S { 787 // int a; 788 // float b; 789 // }; 790 // 791 // layout(set = 0, binding = 2) uniform block { 792 // S s; 793 // lowp float f; 794 // int ii; 795 // } ubuf; 796 // 797 // layout(location = 0) out vec4 color; 798 // 799 // void main() { 800 // float c = 0.0; 801 // lowp float d = 0.0; 802 // S localS = ubuf.s; 803 // for (int i = 0; i < ubuf.s.a; i++) { 804 // switch (ubuf.ii) { 805 // case 0: 806 // c += 0.1; 807 // d += 0.2; 808 // case 1: 809 // c += 0.1; 810 // if (c > d) { 811 // d += 0.2; 812 // } else { 813 // d += c; 814 // } 815 // break; 816 // default: 817 // i += 1; 818 // localS.b += d; 819 // } 820 // } 821 // color = vec4(c, d, localS.b, 1.0); 822 // } 823 824 std::string shader = R"( 825 OpCapability Shader 826 %1 = OpExtInstImport "GLSL.std.450" 827 OpMemoryModel Logical GLSL450 828 OpEntryPoint Fragment %4 "main" %80 829 OpExecutionMode %4 OriginUpperLeft 830 OpSource ESSL 310 831 OpName %4 "main" 832 OpName %12 "S" 833 OpMemberName %12 0 "a" 834 OpMemberName %12 1 "b" 835 OpName %15 "S" 836 OpMemberName %15 0 "a" 837 OpMemberName %15 1 "b" 838 OpName %16 "block" 839 OpMemberName %16 0 "s" 840 OpMemberName %16 1 "f" 841 OpMemberName %16 2 "ii" 842 OpName %18 "ubuf" 843 OpName %80 "color" 844 OpMemberDecorate %12 0 RelaxedPrecision 845 OpMemberDecorate %15 0 RelaxedPrecision 846 OpMemberDecorate %15 0 Offset 0 847 OpMemberDecorate %15 1 Offset 4 848 OpMemberDecorate %16 0 Offset 0 849 OpMemberDecorate %16 1 RelaxedPrecision 850 OpMemberDecorate %16 1 Offset 16 851 OpMemberDecorate %16 2 RelaxedPrecision 852 OpMemberDecorate %16 2 Offset 20 853 OpDecorate %16 Block 854 OpDecorate %18 DescriptorSet 0 855 OpDecorate %18 Binding 2 856 OpDecorate %38 RelaxedPrecision 857 OpDecorate %43 RelaxedPrecision 858 OpDecorate %53 RelaxedPrecision 859 OpDecorate %62 RelaxedPrecision 860 OpDecorate %69 RelaxedPrecision 861 OpDecorate %77 RelaxedPrecision 862 OpDecorate %80 Location 0 863 OpDecorate %101 RelaxedPrecision 864 OpDecorate %102 RelaxedPrecision 865 OpDecorate %96 RelaxedPrecision 866 OpDecorate %108 RelaxedPrecision 867 OpDecorate %107 RelaxedPrecision 868 OpDecorate %98 RelaxedPrecision 869 %2 = OpTypeVoid 870 %3 = OpTypeFunction %2 871 %6 = OpTypeFloat 32 872 %9 = OpConstant %6 0 873 %11 = OpTypeInt 32 1 874 %12 = OpTypeStruct %11 %6 875 %15 = OpTypeStruct %11 %6 876 %16 = OpTypeStruct %15 %6 %11 877 %17 = OpTypePointer Uniform %16 878 %18 = OpVariable %17 Uniform 879 %19 = OpConstant %11 0 880 %20 = OpTypePointer Uniform %15 881 %27 = OpConstant %11 1 882 %36 = OpTypePointer Uniform %11 883 %39 = OpTypeBool 884 %41 = OpConstant %11 2 885 %48 = OpConstant %6 0.100000001 886 %51 = OpConstant %6 0.200000003 887 %78 = OpTypeVector %6 4 888 %79 = OpTypePointer Output %78 889 %80 = OpVariable %79 Output 890 %85 = OpConstant %6 1 891 %95 = OpUndef %12 892 %112 = OpTypePointer Uniform %6 893 %113 = OpTypeInt 32 0 894 %114 = OpConstant %113 1 895 %179 = OpTypePointer Function %39 896 %4 = OpFunction %2 None %3 897 %5 = OpLabel 898 %180 = OpVariable %179 Function 899 %181 = OpVariable %179 Function 900 %182 = OpVariable %179 Function 901 %21 = OpAccessChain %20 %18 %19 902 %115 = OpAccessChain %112 %21 %114 903 %116 = OpLoad %6 %115 904 %90 = OpCompositeInsert %12 %116 %95 1 905 OpBranch %30 906 %30 = OpLabel 907 %99 = OpPhi %12 %90 %5 %109 %47 908 %98 = OpPhi %6 %9 %5 %107 %47 909 %97 = OpPhi %6 %9 %5 %105 %47 910 %96 = OpPhi %11 %19 %5 %77 %47 911 %37 = OpAccessChain %36 %18 %19 %19 912 %38 = OpLoad %11 %37 913 %40 = OpSLessThan %39 %96 %38 914 OpLoopMerge %32 %47 None 915 OpBranchConditional %40 %31 %32 916 %31 = OpLabel 917 %42 = OpAccessChain %36 %18 %41 918 %43 = OpLoad %11 %42 919 OpSelectionMerge %45 None 920 OpSwitch %43 %46 0 %44 1 %45 921 %46 = OpLabel 922 %69 = OpIAdd %11 %96 %27 923 %72 = OpCompositeExtract %6 %99 1 924 %73 = OpFAdd %6 %72 %98 925 %93 = OpCompositeInsert %12 %73 %99 1 926 OpBranch %47 927 %44 = OpLabel 928 %50 = OpFAdd %6 %97 %48 929 %53 = OpFAdd %6 %98 %51 930 OpBranch %45 931 %45 = OpLabel 932 %101 = OpPhi %6 %98 %31 %53 %44 933 %100 = OpPhi %6 %97 %31 %50 %44 934 %55 = OpFAdd %6 %100 %48 935 %58 = OpFOrdGreaterThan %39 %55 %101 936 OpSelectionMerge %60 None 937 OpBranchConditional %58 %59 %63 938 %59 = OpLabel 939 %62 = OpFAdd %6 %101 %51 940 OpBranch %60 941 %63 = OpLabel 942 %66 = OpFAdd %6 %101 %55 943 OpBranch %60 944 %60 = OpLabel 945 %108 = OpPhi %6 %62 %59 %66 %63 946 OpBranch %47 947 %47 = OpLabel 948 %109 = OpPhi %12 %93 %46 %99 %60 949 %107 = OpPhi %6 %98 %46 %108 %60 950 %105 = OpPhi %6 %97 %46 %55 %60 951 %102 = OpPhi %11 %69 %46 %96 %60 952 %77 = OpIAdd %11 %102 %27 953 OpBranch %30 954 %32 = OpLabel 955 %84 = OpCompositeExtract %6 %99 1 956 %86 = OpCompositeConstruct %78 %97 %98 %84 %85 957 OpStore %80 %86 958 OpReturn 959 OpFunctionEnd 960 )"; 961 962 const auto env = SPV_ENV_UNIVERSAL_1_3; 963 const auto consumer = nullptr; 964 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 965 spvtools::ValidatorOptions validator_options; 966 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 967 kConsoleMessageConsumer)); 968 TransformationContext transformation_context( 969 MakeUnique<FactManager>(context.get()), validator_options); 970 // Inapplicable because %18 is decorated. 971 ASSERT_FALSE(TransformationAddSynonym( 972 18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 973 MakeInstructionDescriptor(21, spv::Op::OpAccessChain, 0)) 974 .IsApplicable(context.get(), transformation_context)); 975 976 // Inapplicable because %77 is decorated. 977 ASSERT_FALSE(TransformationAddSynonym( 978 77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 979 MakeInstructionDescriptor(77, spv::Op::OpBranch, 0)) 980 .IsApplicable(context.get(), transformation_context)); 981 982 // Inapplicable because %80 is decorated. 983 ASSERT_FALSE(TransformationAddSynonym( 984 80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 985 MakeInstructionDescriptor(77, spv::Op::OpIAdd, 0)) 986 .IsApplicable(context.get(), transformation_context)); 987 988 // Inapplicable because %84 is not available at the requested point 989 ASSERT_FALSE( 990 TransformationAddSynonym( 991 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 992 MakeInstructionDescriptor(32, spv::Op::OpCompositeExtract, 0)) 993 .IsApplicable(context.get(), transformation_context)); 994 995 // Fine because %84 is available at the requested point 996 ASSERT_TRUE( 997 TransformationAddSynonym( 998 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 999 MakeInstructionDescriptor(32, spv::Op::OpCompositeConstruct, 0)) 1000 .IsApplicable(context.get(), transformation_context)); 1001 1002 // Inapplicable because id %9 is already in use 1003 ASSERT_FALSE( 1004 TransformationAddSynonym( 1005 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9, 1006 MakeInstructionDescriptor(32, spv::Op::OpCompositeConstruct, 0)) 1007 .IsApplicable(context.get(), transformation_context)); 1008 1009 // Inapplicable because the requested point does not exist 1010 ASSERT_FALSE(TransformationAddSynonym( 1011 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1012 MakeInstructionDescriptor(86, spv::Op::OpReturn, 2)) 1013 .IsApplicable(context.get(), transformation_context)); 1014 1015 // Inapplicable because %9 is not in a function 1016 ASSERT_FALSE(TransformationAddSynonym( 1017 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1018 MakeInstructionDescriptor(9, spv::Op::OpTypeInt, 0)) 1019 .IsApplicable(context.get(), transformation_context)); 1020 1021 // Inapplicable because the insert point is right before, or inside, a chunk 1022 // of OpPhis 1023 ASSERT_FALSE(TransformationAddSynonym( 1024 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1025 MakeInstructionDescriptor(30, spv::Op::OpPhi, 0)) 1026 .IsApplicable(context.get(), transformation_context)); 1027 ASSERT_FALSE(TransformationAddSynonym( 1028 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1029 MakeInstructionDescriptor(99, spv::Op::OpPhi, 1)) 1030 .IsApplicable(context.get(), transformation_context)); 1031 1032 // OK, because the insert point is just after a chunk of OpPhis. 1033 ASSERT_TRUE(TransformationAddSynonym( 1034 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1035 MakeInstructionDescriptor(96, spv::Op::OpAccessChain, 0)) 1036 .IsApplicable(context.get(), transformation_context)); 1037 1038 // Inapplicable because the insert point is right after an OpSelectionMerge 1039 ASSERT_FALSE( 1040 TransformationAddSynonym( 1041 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1042 MakeInstructionDescriptor(58, spv::Op::OpBranchConditional, 0)) 1043 .IsApplicable(context.get(), transformation_context)); 1044 1045 // OK, because the insert point is right before the OpSelectionMerge 1046 ASSERT_TRUE(TransformationAddSynonym( 1047 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1048 MakeInstructionDescriptor(58, spv::Op::OpSelectionMerge, 0)) 1049 .IsApplicable(context.get(), transformation_context)); 1050 1051 // Inapplicable because the insert point is right after an OpSelectionMerge 1052 ASSERT_FALSE(TransformationAddSynonym( 1053 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1054 MakeInstructionDescriptor(43, spv::Op::OpSwitch, 0)) 1055 .IsApplicable(context.get(), transformation_context)); 1056 1057 // OK, because the insert point is right before the OpSelectionMerge 1058 ASSERT_TRUE(TransformationAddSynonym( 1059 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1060 MakeInstructionDescriptor(43, spv::Op::OpSelectionMerge, 0)) 1061 .IsApplicable(context.get(), transformation_context)); 1062 1063 // Inapplicable because the insert point is right after an OpLoopMerge 1064 ASSERT_FALSE( 1065 TransformationAddSynonym( 1066 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1067 MakeInstructionDescriptor(40, spv::Op::OpBranchConditional, 0)) 1068 .IsApplicable(context.get(), transformation_context)); 1069 1070 // OK, because the insert point is right before the OpLoopMerge 1071 ASSERT_TRUE(TransformationAddSynonym( 1072 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1073 MakeInstructionDescriptor(40, spv::Op::OpLoopMerge, 0)) 1074 .IsApplicable(context.get(), transformation_context)); 1075 1076 // Inapplicable because id %300 does not exist 1077 ASSERT_FALSE(TransformationAddSynonym( 1078 300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1079 MakeInstructionDescriptor(40, spv::Op::OpLoopMerge, 0)) 1080 .IsApplicable(context.get(), transformation_context)); 1081 1082 // Inapplicable because the following instruction is OpVariable 1083 ASSERT_FALSE(TransformationAddSynonym( 1084 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1085 MakeInstructionDescriptor(180, spv::Op::OpVariable, 0)) 1086 .IsApplicable(context.get(), transformation_context)); 1087 ASSERT_FALSE(TransformationAddSynonym( 1088 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1089 MakeInstructionDescriptor(181, spv::Op::OpVariable, 0)) 1090 .IsApplicable(context.get(), transformation_context)); 1091 ASSERT_FALSE(TransformationAddSynonym( 1092 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1093 MakeInstructionDescriptor(182, spv::Op::OpVariable, 0)) 1094 .IsApplicable(context.get(), transformation_context)); 1095 1096 // OK, because this is just past the group of OpVariable instructions. 1097 ASSERT_TRUE(TransformationAddSynonym( 1098 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, 1099 MakeInstructionDescriptor(182, spv::Op::OpAccessChain, 0)) 1100 .IsApplicable(context.get(), transformation_context)); 1101} 1102 1103TEST(TransformationAddSynonymTest, MiscellaneousCopies) { 1104 // The following SPIR-V comes from this GLSL: 1105 // 1106 // #version 310 es 1107 // 1108 // precision highp float; 1109 // 1110 // float g; 1111 // 1112 // vec4 h; 1113 // 1114 // void main() { 1115 // int a; 1116 // int b; 1117 // b = int(g); 1118 // h.x = float(a); 1119 // } 1120 1121 std::string shader = R"( 1122 OpCapability Shader 1123 %1 = OpExtInstImport "GLSL.std.450" 1124 OpMemoryModel Logical GLSL450 1125 OpEntryPoint Fragment %4 "main" 1126 OpExecutionMode %4 OriginUpperLeft 1127 OpSource ESSL 310 1128 OpName %4 "main" 1129 OpName %8 "b" 1130 OpName %11 "g" 1131 OpName %16 "h" 1132 OpName %17 "a" 1133 %2 = OpTypeVoid 1134 %3 = OpTypeFunction %2 1135 %6 = OpTypeInt 32 1 1136 %7 = OpTypePointer Function %6 1137 %9 = OpTypeFloat 32 1138 %10 = OpTypePointer Private %9 1139 %11 = OpVariable %10 Private 1140 %14 = OpTypeVector %9 4 1141 %15 = OpTypePointer Private %14 1142 %16 = OpVariable %15 Private 1143 %20 = OpTypeInt 32 0 1144 %21 = OpConstant %20 0 1145 %4 = OpFunction %2 None %3 1146 %5 = OpLabel 1147 %8 = OpVariable %7 Function 1148 %17 = OpVariable %7 Function 1149 %12 = OpLoad %9 %11 1150 %13 = OpConvertFToS %6 %12 1151 OpStore %8 %13 1152 %18 = OpLoad %6 %17 1153 %19 = OpConvertSToF %9 %18 1154 %22 = OpAccessChain %10 %16 %21 1155 OpStore %22 %19 1156 OpReturn 1157 OpFunctionEnd 1158 )"; 1159 1160 const auto env = SPV_ENV_UNIVERSAL_1_3; 1161 const auto consumer = nullptr; 1162 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 1163 spvtools::ValidatorOptions validator_options; 1164 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 1165 kConsoleMessageConsumer)); 1166 TransformationContext transformation_context( 1167 MakeUnique<FactManager>(context.get()), validator_options); 1168 std::vector<TransformationAddSynonym> transformations = { 1169 TransformationAddSynonym( 1170 19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, 1171 MakeInstructionDescriptor(22, spv::Op::OpStore, 0)), 1172 TransformationAddSynonym( 1173 22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101, 1174 MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)), 1175 TransformationAddSynonym( 1176 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102, 1177 MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)), 1178 TransformationAddSynonym( 1179 11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103, 1180 MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)), 1181 TransformationAddSynonym( 1182 16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104, 1183 MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)), 1184 TransformationAddSynonym( 1185 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105, 1186 MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0)), 1187 TransformationAddSynonym( 1188 17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106, 1189 MakeInstructionDescriptor(22, spv::Op::OpCopyObject, 0))}; 1190 1191 for (auto& transformation : transformations) { 1192 ASSERT_TRUE( 1193 transformation.IsApplicable(context.get(), transformation_context)); 1194 ApplyAndCheckFreshIds(transformation, context.get(), 1195 &transformation_context); 1196 } 1197 1198 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 1199 kConsoleMessageConsumer)); 1200 1201 std::string after_transformation = R"( 1202 OpCapability Shader 1203 %1 = OpExtInstImport "GLSL.std.450" 1204 OpMemoryModel Logical GLSL450 1205 OpEntryPoint Fragment %4 "main" 1206 OpExecutionMode %4 OriginUpperLeft 1207 OpSource ESSL 310 1208 OpName %4 "main" 1209 OpName %8 "b" 1210 OpName %11 "g" 1211 OpName %16 "h" 1212 OpName %17 "a" 1213 %2 = OpTypeVoid 1214 %3 = OpTypeFunction %2 1215 %6 = OpTypeInt 32 1 1216 %7 = OpTypePointer Function %6 1217 %9 = OpTypeFloat 32 1218 %10 = OpTypePointer Private %9 1219 %11 = OpVariable %10 Private 1220 %14 = OpTypeVector %9 4 1221 %15 = OpTypePointer Private %14 1222 %16 = OpVariable %15 Private 1223 %20 = OpTypeInt 32 0 1224 %21 = OpConstant %20 0 1225 %4 = OpFunction %2 None %3 1226 %5 = OpLabel 1227 %8 = OpVariable %7 Function 1228 %17 = OpVariable %7 Function 1229 %12 = OpLoad %9 %11 1230 %13 = OpConvertFToS %6 %12 1231 OpStore %8 %13 1232 %18 = OpLoad %6 %17 1233 %19 = OpConvertSToF %9 %18 1234 %22 = OpAccessChain %10 %16 %21 1235 %106 = OpCopyObject %7 %17 1236 %105 = OpCopyObject %7 %8 1237 %104 = OpCopyObject %15 %16 1238 %103 = OpCopyObject %10 %11 1239 %102 = OpCopyObject %9 %12 1240 %101 = OpCopyObject %10 %22 1241 %100 = OpCopyObject %9 %19 1242 OpStore %22 %19 1243 OpReturn 1244 OpFunctionEnd 1245 )"; 1246 1247 ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); 1248} 1249 1250TEST(TransformationAddSynonymTest, DoNotCopyNullPointers) { 1251 std::string shader = R"( 1252 OpCapability Shader 1253 OpCapability VariablePointers 1254 %1 = OpExtInstImport "GLSL.std.450" 1255 OpMemoryModel Logical GLSL450 1256 OpEntryPoint Fragment %4 "main" 1257 OpExecutionMode %4 OriginUpperLeft 1258 OpSource ESSL 310 1259 %2 = OpTypeVoid 1260 %3 = OpTypeFunction %2 1261 %6 = OpTypeInt 32 1 1262 %7 = OpTypePointer Function %6 1263 %8 = OpConstantNull %7 1264 %4 = OpFunction %2 None %3 1265 %5 = OpLabel 1266 OpReturn 1267 OpFunctionEnd 1268 )"; 1269 1270 const auto env = SPV_ENV_UNIVERSAL_1_3; 1271 const auto consumer = nullptr; 1272 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 1273 spvtools::ValidatorOptions validator_options; 1274 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 1275 kConsoleMessageConsumer)); 1276 TransformationContext transformation_context( 1277 MakeUnique<FactManager>(context.get()), validator_options); 1278 // Illegal to copy null. 1279 ASSERT_FALSE(TransformationAddSynonym( 1280 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, 1281 MakeInstructionDescriptor(5, spv::Op::OpReturn, 0)) 1282 .IsApplicable(context.get(), transformation_context)); 1283} 1284 1285TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) { 1286 // Checks that if a pointer is known to have an irrelevant value, the same 1287 // holds after the pointer is copied. 1288 1289 std::string shader = R"( 1290 OpCapability Shader 1291 %1 = OpExtInstImport "GLSL.std.450" 1292 OpMemoryModel Logical GLSL450 1293 OpEntryPoint Fragment %4 "main" 1294 OpExecutionMode %4 OriginUpperLeft 1295 OpSource ESSL 310 1296 %2 = OpTypeVoid 1297 %3 = OpTypeFunction %2 1298 %6 = OpTypeInt 32 1 1299 %7 = OpTypePointer Function %6 1300 %4 = OpFunction %2 None %3 1301 %5 = OpLabel 1302 %8 = OpVariable %7 Function 1303 %9 = OpVariable %7 Function 1304 OpReturn 1305 OpFunctionEnd 1306 )"; 1307 1308 const auto env = SPV_ENV_UNIVERSAL_1_3; 1309 const auto consumer = nullptr; 1310 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 1311 spvtools::ValidatorOptions validator_options; 1312 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, 1313 kConsoleMessageConsumer)); 1314 TransformationContext transformation_context( 1315 MakeUnique<FactManager>(context.get()), validator_options); 1316 transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8); 1317 1318 TransformationAddSynonym transformation1( 1319 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, 1320 MakeInstructionDescriptor(9, spv::Op::OpReturn, 0)); 1321 TransformationAddSynonym transformation2( 1322 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101, 1323 MakeInstructionDescriptor(9, spv::Op::OpReturn, 0)); 1324 TransformationAddSynonym transformation3( 1325 100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102, 1326 MakeInstructionDescriptor(9, spv::Op::OpReturn, 0)); 1327 1328 ASSERT_TRUE( 1329 transformation1.IsApplicable(context.get(), transformation_context)); 1330 ApplyAndCheckFreshIds(transformation1, context.get(), 1331 &transformation_context); 1332 ASSERT_TRUE( 1333 transformation2.IsApplicable(context.get(), transformation_context)); 1334 ApplyAndCheckFreshIds(transformation2, context.get(), 1335 &transformation_context); 1336 ASSERT_TRUE( 1337 transformation3.IsApplicable(context.get(), transformation_context)); 1338 ApplyAndCheckFreshIds(transformation3, context.get(), 1339 &transformation_context); 1340 1341 ASSERT_TRUE( 1342 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8)); 1343 ASSERT_TRUE( 1344 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); 1345 ASSERT_TRUE( 1346 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); 1347 ASSERT_FALSE( 1348 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9)); 1349 ASSERT_FALSE( 1350 transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); 1351} 1352 1353TEST(TransformationAddSynonymTest, DoNotCopyOpSampledImage) { 1354 // This checks that we do not try to copy the result id of an OpSampledImage 1355 // instruction. 1356 std::string shader = R"( 1357 OpCapability Shader 1358 OpCapability SampledBuffer 1359 OpCapability ImageBuffer 1360 %1 = OpExtInstImport "GLSL.std.450" 1361 OpMemoryModel Logical GLSL450 1362 OpEntryPoint Fragment %2 "main" %40 %41 1363 OpExecutionMode %2 OriginUpperLeft 1364 OpSource GLSL 450 1365 OpDecorate %40 DescriptorSet 0 1366 OpDecorate %40 Binding 69 1367 OpDecorate %41 DescriptorSet 0 1368 OpDecorate %41 Binding 1 1369 %54 = OpTypeFloat 32 1370 %76 = OpTypeVector %54 4 1371 %55 = OpConstant %54 0 1372 %56 = OpTypeVector %54 3 1373 %94 = OpTypeVector %54 2 1374 %112 = OpConstantComposite %94 %55 %55 1375 %57 = OpConstantComposite %56 %55 %55 %55 1376 %15 = OpTypeImage %54 2D 2 0 0 1 Unknown 1377 %114 = OpTypePointer UniformConstant %15 1378 %38 = OpTypeSampler 1379 %125 = OpTypePointer UniformConstant %38 1380 %132 = OpTypeVoid 1381 %133 = OpTypeFunction %132 1382 %45 = OpTypeSampledImage %15 1383 %40 = OpVariable %114 UniformConstant 1384 %41 = OpVariable %125 UniformConstant 1385 %2 = OpFunction %132 None %133 1386 %164 = OpLabel 1387 %184 = OpLoad %15 %40 1388 %213 = OpLoad %38 %41 1389 %216 = OpSampledImage %45 %184 %213 1390 %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 1391 OpReturn 1392 OpFunctionEnd 1393 )"; 1394 1395 const auto env = SPV_ENV_UNIVERSAL_1_3; 1396 const auto consumer = nullptr; 1397 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 1398 1399 spvtools::ValidatorOptions validator_options; 1400 TransformationContext transformation_context( 1401 MakeUnique<FactManager>(context.get()), validator_options); 1402 ASSERT_FALSE( 1403 TransformationAddSynonym( 1404 216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500, 1405 MakeInstructionDescriptor(217, spv::Op::OpImageSampleImplicitLod, 0)) 1406 .IsApplicable(context.get(), transformation_context)); 1407} 1408 1409TEST(TransformationAddSynonymTest, DoNotCopyVoidRunctionResult) { 1410 // This checks that we do not try to copy the result of a void function. 1411 std::string shader = R"( 1412 OpCapability Shader 1413 %1 = OpExtInstImport "GLSL.std.450" 1414 OpMemoryModel Logical GLSL450 1415 OpEntryPoint Fragment %4 "main" 1416 OpExecutionMode %4 OriginUpperLeft 1417 OpSource ESSL 320 1418 OpName %4 "main" 1419 OpName %6 "foo(" 1420 %2 = OpTypeVoid 1421 %3 = OpTypeFunction %2 1422 %4 = OpFunction %2 None %3 1423 %5 = OpLabel 1424 %8 = OpFunctionCall %2 %6 1425 OpReturn 1426 OpFunctionEnd 1427 %6 = OpFunction %2 None %3 1428 %7 = OpLabel 1429 OpReturn 1430 OpFunctionEnd 1431 )"; 1432 1433 const auto env = SPV_ENV_UNIVERSAL_1_3; 1434 const auto consumer = nullptr; 1435 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 1436 1437 spvtools::ValidatorOptions validator_options; 1438 TransformationContext transformation_context( 1439 MakeUnique<FactManager>(context.get()), validator_options); 1440 ASSERT_FALSE(TransformationAddSynonym( 1441 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 500, 1442 MakeInstructionDescriptor(8, spv::Op::OpReturn, 0)) 1443 .IsApplicable(context.get(), transformation_context)); 1444} 1445 1446TEST(TransformationAddSynonymTest, HandlesDeadBlocks) { 1447 std::string shader = R"( 1448 OpCapability Shader 1449 %1 = OpExtInstImport "GLSL.std.450" 1450 OpMemoryModel Logical GLSL450 1451 OpEntryPoint Fragment %4 "main" 1452 OpExecutionMode %4 OriginUpperLeft 1453 OpSource ESSL 320 1454 %2 = OpTypeVoid 1455 %3 = OpTypeFunction %2 1456 %6 = OpTypeBool 1457 %7 = OpConstantTrue %6 1458 %11 = OpTypePointer Function %6 1459 %4 = OpFunction %2 None %3 1460 %5 = OpLabel 1461 %12 = OpVariable %11 Function 1462 OpSelectionMerge %10 None 1463 OpBranchConditional %7 %8 %9 1464 %8 = OpLabel 1465 OpBranch %10 1466 %9 = OpLabel 1467 OpBranch %10 1468 %10 = OpLabel 1469 OpReturn 1470 OpFunctionEnd 1471 )"; 1472 1473 const auto env = SPV_ENV_UNIVERSAL_1_3; 1474 const auto consumer = nullptr; 1475 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); 1476 1477 spvtools::ValidatorOptions validator_options; 1478 TransformationContext transformation_context( 1479 MakeUnique<FactManager>(context.get()), validator_options); 1480 1481 transformation_context.GetFactManager()->AddFactBlockIsDead(9); 1482 1483 auto insert_before = MakeInstructionDescriptor(9, spv::Op::OpBranch, 0); 1484 1485 ASSERT_FALSE(TransformationAddSynonym( 1486 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, 1487 insert_before) 1488 .IsApplicable(context.get(), transformation_context)); 1489 1490 ASSERT_FALSE(TransformationAddSynonym( 1491 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, 1492 insert_before) 1493 .IsApplicable(context.get(), transformation_context)); 1494} 1495 1496} // namespace 1497} // namespace fuzz 1498} // namespace spvtools 1499