| // Copyright (c) 2020 Google LLC | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "source/fuzz/replayer.h" | 
 |  | 
 | #include "gtest/gtest.h" | 
 | #include "source/fuzz/data_descriptor.h" | 
 | #include "source/fuzz/fuzzer_util.h" | 
 | #include "source/fuzz/instruction_descriptor.h" | 
 | #include "source/fuzz/transformation_add_constant_scalar.h" | 
 | #include "source/fuzz/transformation_add_global_variable.h" | 
 | #include "source/fuzz/transformation_add_parameter.h" | 
 | #include "source/fuzz/transformation_add_synonym.h" | 
 | #include "source/fuzz/transformation_flatten_conditional_branch.h" | 
 | #include "source/fuzz/transformation_split_block.h" | 
 | #include "test/fuzz/fuzz_test_util.h" | 
 |  | 
 | namespace spvtools { | 
 | namespace fuzz { | 
 | namespace { | 
 |  | 
 | TEST(ReplayerTest, PartialReplay) { | 
 |   const std::string kTestShader = R"( | 
 |                OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource ESSL 310 | 
 |                OpName %4 "main" | 
 |                OpName %8 "g" | 
 |                OpName %11 "x" | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %6 = OpTypeInt 32 1 | 
 |           %7 = OpTypePointer Private %6 | 
 |           %8 = OpVariable %7 Private | 
 |           %9 = OpConstant %6 10 | 
 |          %10 = OpTypePointer Function %6 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |          %11 = OpVariable %10 Function | 
 |                OpStore %8 %9 | 
 |          %12 = OpLoad %6 %8 | 
 |                OpStore %11 %12 | 
 |          %13 = OpLoad %6 %8 | 
 |                OpStore %11 %13 | 
 |          %14 = OpLoad %6 %8 | 
 |                OpStore %11 %14 | 
 |          %15 = OpLoad %6 %8 | 
 |                OpStore %11 %15 | 
 |          %16 = OpLoad %6 %8 | 
 |                OpStore %11 %16 | 
 |          %17 = OpLoad %6 %8 | 
 |                OpStore %11 %17 | 
 |          %18 = OpLoad %6 %8 | 
 |                OpStore %11 %18 | 
 |          %19 = OpLoad %6 %8 | 
 |                OpStore %11 %19 | 
 |          %20 = OpLoad %6 %8 | 
 |                OpStore %11 %20 | 
 |          %21 = OpLoad %6 %8 | 
 |                OpStore %11 %21 | 
 |          %22 = OpLoad %6 %8 | 
 |                OpStore %11 %22 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |   )"; | 
 |  | 
 |   const auto env = SPV_ENV_UNIVERSAL_1_3; | 
 |   spvtools::ValidatorOptions validator_options; | 
 |  | 
 |   std::vector<uint32_t> binary_in; | 
 |   SpirvTools t(env); | 
 |   t.SetMessageConsumer(kConsoleMessageConsumer); | 
 |   ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption)); | 
 |   ASSERT_TRUE(t.Validate(binary_in)); | 
 |  | 
 |   protobufs::TransformationSequence transformations; | 
 |   for (uint32_t id = 12; id <= 22; id++) { | 
 |     *transformations.add_transformation() = | 
 |         TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0), | 
 |                                  id + 100) | 
 |             .ToMessage(); | 
 |   } | 
 |  | 
 |   { | 
 |     // Full replay | 
 |     protobufs::FactSequence empty_facts; | 
 |     auto replayer_result = | 
 |         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, | 
 |                  transformations, 11, true, validator_options) | 
 |             .Run(); | 
 |     // Replay should succeed. | 
 |     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, | 
 |               replayer_result.status); | 
 |     // All transformations should be applied. | 
 |     ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals( | 
 |         transformations, replayer_result.applied_transformations)); | 
 |  | 
 |     const std::string kFullySplitShader = R"( | 
 |                OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource ESSL 310 | 
 |                OpName %4 "main" | 
 |                OpName %8 "g" | 
 |                OpName %11 "x" | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %6 = OpTypeInt 32 1 | 
 |           %7 = OpTypePointer Private %6 | 
 |           %8 = OpVariable %7 Private | 
 |           %9 = OpConstant %6 10 | 
 |          %10 = OpTypePointer Function %6 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |          %11 = OpVariable %10 Function | 
 |                OpStore %8 %9 | 
 |                OpBranch %112 | 
 |         %112 = OpLabel | 
 |          %12 = OpLoad %6 %8 | 
 |                OpStore %11 %12 | 
 |                OpBranch %113 | 
 |         %113 = OpLabel | 
 |          %13 = OpLoad %6 %8 | 
 |                OpStore %11 %13 | 
 |                OpBranch %114 | 
 |         %114 = OpLabel | 
 |          %14 = OpLoad %6 %8 | 
 |                OpStore %11 %14 | 
 |                OpBranch %115 | 
 |         %115 = OpLabel | 
 |          %15 = OpLoad %6 %8 | 
 |                OpStore %11 %15 | 
 |                OpBranch %116 | 
 |         %116 = OpLabel | 
 |          %16 = OpLoad %6 %8 | 
 |                OpStore %11 %16 | 
 |                OpBranch %117 | 
 |         %117 = OpLabel | 
 |          %17 = OpLoad %6 %8 | 
 |                OpStore %11 %17 | 
 |                OpBranch %118 | 
 |         %118 = OpLabel | 
 |          %18 = OpLoad %6 %8 | 
 |                OpStore %11 %18 | 
 |                OpBranch %119 | 
 |         %119 = OpLabel | 
 |          %19 = OpLoad %6 %8 | 
 |                OpStore %11 %19 | 
 |                OpBranch %120 | 
 |         %120 = OpLabel | 
 |          %20 = OpLoad %6 %8 | 
 |                OpStore %11 %20 | 
 |                OpBranch %121 | 
 |         %121 = OpLabel | 
 |          %21 = OpLoad %6 %8 | 
 |                OpStore %11 %21 | 
 |                OpBranch %122 | 
 |         %122 = OpLabel | 
 |          %22 = OpLoad %6 %8 | 
 |                OpStore %11 %22 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |     )"; | 
 |     ASSERT_TRUE(IsEqual(env, kFullySplitShader, | 
 |                         replayer_result.transformed_module.get())); | 
 |   } | 
 |  | 
 |   { | 
 |     // Half replay | 
 |     protobufs::FactSequence empty_facts; | 
 |     auto replayer_result = | 
 |         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, | 
 |                  transformations, 5, true, validator_options) | 
 |             .Run(); | 
 |     // Replay should succeed. | 
 |     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, | 
 |               replayer_result.status); | 
 |     // The first 5 transformations should be applied | 
 |     ASSERT_EQ(5, replayer_result.applied_transformations.transformation_size()); | 
 |     for (uint32_t i = 0; i < 5; i++) { | 
 |       ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals( | 
 |           transformations.transformation(i), | 
 |           replayer_result.applied_transformations.transformation(i))); | 
 |     } | 
 |  | 
 |     const std::string kHalfSplitShader = R"( | 
 |                OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource ESSL 310 | 
 |                OpName %4 "main" | 
 |                OpName %8 "g" | 
 |                OpName %11 "x" | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %6 = OpTypeInt 32 1 | 
 |           %7 = OpTypePointer Private %6 | 
 |           %8 = OpVariable %7 Private | 
 |           %9 = OpConstant %6 10 | 
 |          %10 = OpTypePointer Function %6 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |          %11 = OpVariable %10 Function | 
 |                OpStore %8 %9 | 
 |                OpBranch %112 | 
 |         %112 = OpLabel | 
 |          %12 = OpLoad %6 %8 | 
 |                OpStore %11 %12 | 
 |                OpBranch %113 | 
 |         %113 = OpLabel | 
 |          %13 = OpLoad %6 %8 | 
 |                OpStore %11 %13 | 
 |                OpBranch %114 | 
 |         %114 = OpLabel | 
 |          %14 = OpLoad %6 %8 | 
 |                OpStore %11 %14 | 
 |                OpBranch %115 | 
 |         %115 = OpLabel | 
 |          %15 = OpLoad %6 %8 | 
 |                OpStore %11 %15 | 
 |                OpBranch %116 | 
 |         %116 = OpLabel | 
 |          %16 = OpLoad %6 %8 | 
 |                OpStore %11 %16 | 
 |          %17 = OpLoad %6 %8 | 
 |                OpStore %11 %17 | 
 |          %18 = OpLoad %6 %8 | 
 |                OpStore %11 %18 | 
 |          %19 = OpLoad %6 %8 | 
 |                OpStore %11 %19 | 
 |          %20 = OpLoad %6 %8 | 
 |                OpStore %11 %20 | 
 |          %21 = OpLoad %6 %8 | 
 |                OpStore %11 %21 | 
 |          %22 = OpLoad %6 %8 | 
 |                OpStore %11 %22 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |     )"; | 
 |     ASSERT_TRUE(IsEqual(env, kHalfSplitShader, | 
 |                         replayer_result.transformed_module.get())); | 
 |   } | 
 |  | 
 |   { | 
 |     // Empty replay | 
 |     protobufs::FactSequence empty_facts; | 
 |     auto replayer_result = | 
 |         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, | 
 |                  transformations, 0, true, validator_options) | 
 |             .Run(); | 
 |     // Replay should succeed. | 
 |     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, | 
 |               replayer_result.status); | 
 |     // No transformations should be applied | 
 |     ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size()); | 
 |     ASSERT_TRUE( | 
 |         IsEqual(env, kTestShader, replayer_result.transformed_module.get())); | 
 |   } | 
 |  | 
 |   { | 
 |     // Invalid replay: too many transformations | 
 |     protobufs::FactSequence empty_facts; | 
 |     // The number of transformations requested to be applied exceeds the number | 
 |     // of transformations | 
 |     auto replayer_result = | 
 |         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, | 
 |                  transformations, 12, true, validator_options) | 
 |             .Run(); | 
 |  | 
 |     // Replay should not succeed. | 
 |     ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested, | 
 |               replayer_result.status); | 
 |     // No transformations should be applied | 
 |     ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size()); | 
 |     // The output binary should be empty | 
 |     ASSERT_EQ(nullptr, replayer_result.transformed_module); | 
 |   } | 
 | } | 
 |  | 
 | TEST(ReplayerTest, CheckFactsAfterReplay) { | 
 |   const std::string kTestShader = R"( | 
 |                OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource ESSL 320 | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %8 = OpTypeInt 32 1 | 
 |           %9 = OpTypePointer Function %8 | 
 |          %50 = OpTypePointer Private %8 | 
 |          %11 = OpConstant %8 1 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |          %10 = OpVariable %9 Function | 
 |                OpStore %10 %11 | 
 |          %12 = OpFunctionCall %2 %6 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %3 | 
 |           %7 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |   )"; | 
 |  | 
 |   const auto env = SPV_ENV_UNIVERSAL_1_3; | 
 |   spvtools::ValidatorOptions validator_options; | 
 |  | 
 |   std::vector<uint32_t> binary_in; | 
 |   SpirvTools t(env); | 
 |   t.SetMessageConsumer(kConsoleMessageConsumer); | 
 |   ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption)); | 
 |   ASSERT_TRUE(t.Validate(binary_in)); | 
 |  | 
 |   protobufs::TransformationSequence transformations; | 
 |   *transformations.add_transformation() = | 
 |       TransformationAddConstantScalar(100, 8, {42}, true).ToMessage(); | 
 |   *transformations.add_transformation() = | 
 |       TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 100, | 
 |                                       true) | 
 |           .ToMessage(); | 
 |   *transformations.add_transformation() = | 
 |       TransformationAddParameter(6, 102, 8, {{12, 100}}, 103).ToMessage(); | 
 |   *transformations.add_transformation() = | 
 |       TransformationAddSynonym( | 
 |           11, | 
 |           protobufs::TransformationAddSynonym::SynonymType:: | 
 |               TransformationAddSynonym_SynonymType_COPY_OBJECT, | 
 |           104, MakeInstructionDescriptor(12, SpvOpFunctionCall, 0)) | 
 |           .ToMessage(); | 
 |  | 
 |   // Full replay | 
 |   protobufs::FactSequence empty_facts; | 
 |   auto replayer_result = | 
 |       Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, | 
 |                transformations, transformations.transformation_size(), true, | 
 |                validator_options) | 
 |           .Run(); | 
 |   // Replay should succeed. | 
 |   ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status); | 
 |   // All transformations should be applied. | 
 |   ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals( | 
 |       transformations, replayer_result.applied_transformations)); | 
 |  | 
 |   const std::string kExpected = R"( | 
 |                OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource ESSL 320 | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %8 = OpTypeInt 32 1 | 
 |           %9 = OpTypePointer Function %8 | 
 |          %50 = OpTypePointer Private %8 | 
 |          %11 = OpConstant %8 1 | 
 |         %100 = OpConstant %8 42 | 
 |         %101 = OpVariable %50 Private %100 | 
 |         %103 = OpTypeFunction %2 %8 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |          %10 = OpVariable %9 Function | 
 |                OpStore %10 %11 | 
 |         %104 = OpCopyObject %8 %11 | 
 |          %12 = OpFunctionCall %2 %6 %100 | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |           %6 = OpFunction %2 None %103 | 
 |         %102 = OpFunctionParameter %8 | 
 |           %7 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |   )"; | 
 |   ASSERT_TRUE( | 
 |       IsEqual(env, kExpected, replayer_result.transformed_module.get())); | 
 |  | 
 |   ASSERT_TRUE( | 
 |       replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant( | 
 |           100)); | 
 |   ASSERT_TRUE(replayer_result.transformation_context->GetFactManager() | 
 |                   ->PointeeValueIsIrrelevant(101)); | 
 |   ASSERT_TRUE( | 
 |       replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant( | 
 |           102)); | 
 |   ASSERT_TRUE( | 
 |       replayer_result.transformation_context->GetFactManager()->IsSynonymous( | 
 |           MakeDataDescriptor(11, {}), MakeDataDescriptor(104, {}))); | 
 | } | 
 |  | 
 | TEST(ReplayerTest, ReplayWithOverflowIds) { | 
 |   const std::string kTestShader = R"( | 
 |                OpCapability Shader | 
 |           %1 = OpExtInstImport "GLSL.std.450" | 
 |                OpMemoryModel Logical GLSL450 | 
 |                OpEntryPoint Fragment %4 "main" | 
 |                OpExecutionMode %4 OriginUpperLeft | 
 |                OpSource ESSL 320 | 
 |           %2 = OpTypeVoid | 
 |           %3 = OpTypeFunction %2 | 
 |           %6 = OpTypeInt 32 1 | 
 |           %7 = OpTypePointer Function %6 | 
 |          %50 = OpTypePointer Private %6 | 
 |           %9 = OpConstant %6 2 | 
 |          %11 = OpConstant %6 0 | 
 |          %12 = OpTypeBool | 
 |          %17 = OpConstant %6 1 | 
 |           %4 = OpFunction %2 None %3 | 
 |           %5 = OpLabel | 
 |           %8 = OpVariable %7 Function | 
 |                OpStore %8 %9 | 
 |          %10 = OpLoad %6 %8 | 
 |          %13 = OpSGreaterThan %12 %10 %11 | 
 |                OpSelectionMerge %15 None | 
 |                OpBranchConditional %13 %14 %15 | 
 |          %14 = OpLabel | 
 |          %16 = OpLoad %6 %8 | 
 |          %18 = OpIAdd %6 %16 %17 | 
 |                OpStore %8 %18 | 
 |                OpBranch %15 | 
 |          %15 = OpLabel | 
 |                OpReturn | 
 |                OpFunctionEnd | 
 |   )"; | 
 |  | 
 |   const auto env = SPV_ENV_UNIVERSAL_1_3; | 
 |   spvtools::ValidatorOptions validator_options; | 
 |  | 
 |   std::vector<uint32_t> binary_in; | 
 |   SpirvTools t(env); | 
 |   t.SetMessageConsumer(kConsoleMessageConsumer); | 
 |   ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption)); | 
 |   ASSERT_TRUE(t.Validate(binary_in)); | 
 |  | 
 |   protobufs::TransformationSequence transformations; | 
 |   *transformations.add_transformation() = | 
 |       TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}).ToMessage(); | 
 |   *transformations.add_transformation() = | 
 |       TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 11, true) | 
 |           .ToMessage(); | 
 |  | 
 |   protobufs::FactSequence empty_facts; | 
 |   auto replayer_result = | 
 |       Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, | 
 |                transformations, transformations.transformation_size(), true, | 
 |                validator_options) | 
 |           .Run(); | 
 |   // Replay should succeed. | 
 |   ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status); | 
 |   // All transformations should be applied. | 
 |   ASSERT_EQ(2, replayer_result.applied_transformations.transformation_size()); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace fuzz | 
 | }  // namespace spvtools |