// 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 "source/fuzz/instruction_descriptor.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 binary_in; SpirvTools t(env); t.SetMessageConsumer(kSilentConsumer); 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, kSilentConsumer, binary_in, empty_facts, transformations, 11, 0, 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_binary)); } { // Half replay protobufs::FactSequence empty_facts; auto replayer_result = Replayer(env, kSilentConsumer, binary_in, empty_facts, transformations, 5, 0, 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_binary)); } { // Empty replay protobufs::FactSequence empty_facts; auto replayer_result = Replayer(env, kSilentConsumer, binary_in, empty_facts, transformations, 0, 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_binary)); } { // 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, kSilentConsumer, binary_in, empty_facts, transformations, 12, 0, 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_TRUE(replayer_result.transformed_binary.empty()); } } } // namespace } // namespace fuzz } // namespace spvtools