SPIRV-Tools/test/fuzz/replayer_test.cpp
alan-baker d35a78db57
Switch SPIRV-Tools to use spirv.hpp11 internally (#4981)
Fixes #4960

* Switches to using enum classes with an underlying type to avoid
  undefined behaviour
2022-11-04 17:27:10 -04:00

477 lines
16 KiB
C++

// 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, spv::Op::OpLoad, 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, spv::StorageClass::Private, 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, spv::Op::OpFunctionCall, 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, spv::StorageClass::Private, 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