mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2025-01-13 18:00:05 +00:00
8d4261bc44
Some transformations (e.g. TransformationAddFunction) rely on running the validator to decide whether the transformation is applicable. A recent change allowed spirv-fuzz to take validator options, to cater for the case where a module should be considered valid under particular conditions. However, validation during the checking of transformations had no access to these validator options. This change introduced TransformationContext, which currently consists of a fact manager and a set of validator options, but could in the future have other fields corresponding to other objects that it is useful to have access to when applying transformations. Now, instead of checking and applying transformations in the context of a FactManager, a TransformationContext is used. This gives access to the fact manager as before, and also access to the validator options when they are needed.
402 lines
16 KiB
C++
402 lines
16 KiB
C++
// Copyright (c) 2019 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/fuzzer_pass_add_useful_constructs.h"
|
|
#include "source/fuzz/pseudo_random_generator.h"
|
|
#include "source/fuzz/uniform_buffer_element_descriptor.h"
|
|
#include "test/fuzz/fuzz_test_util.h"
|
|
|
|
namespace spvtools {
|
|
namespace fuzz {
|
|
namespace {
|
|
|
|
bool AddFactHelper(
|
|
FactManager* fact_manager, opt::IRContext* context, uint32_t word,
|
|
const protobufs::UniformBufferElementDescriptor& descriptor) {
|
|
protobufs::FactConstantUniform constant_uniform_fact;
|
|
constant_uniform_fact.add_constant_word(word);
|
|
*constant_uniform_fact.mutable_uniform_buffer_element_descriptor() =
|
|
descriptor;
|
|
protobufs::Fact fact;
|
|
*fact.mutable_constant_uniform_fact() = constant_uniform_fact;
|
|
return fact_manager->AddFact(fact, context);
|
|
}
|
|
|
|
TEST(FuzzerPassAddUsefulConstructsTest, CheckBasicStuffIsAdded) {
|
|
// The SPIR-V came from the following empty GLSL shader:
|
|
//
|
|
// #version 450
|
|
//
|
|
// void main()
|
|
// {
|
|
// }
|
|
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %4 "main"
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
|
const auto consumer = nullptr;
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
FactManager fact_manager;
|
|
spvtools::ValidatorOptions validator_options;
|
|
TransformationContext transformation_context(&fact_manager,
|
|
validator_options);
|
|
|
|
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
|
|
protobufs::TransformationSequence transformation_sequence;
|
|
|
|
FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context,
|
|
&fuzzer_context, &transformation_sequence);
|
|
pass.Apply();
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
std::string after = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %4 "main"
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%100 = OpTypeBool
|
|
%101 = OpTypeInt 32 1
|
|
%102 = OpTypeInt 32 0
|
|
%103 = OpTypeFloat 32
|
|
%104 = OpConstantTrue %100
|
|
%105 = OpConstantFalse %100
|
|
%106 = OpConstant %101 0
|
|
%107 = OpConstant %101 1
|
|
%108 = OpConstant %102 0
|
|
%109 = OpConstant %102 1
|
|
%110 = OpConstant %103 0
|
|
%111 = OpConstant %103 1
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
ASSERT_TRUE(IsEqual(env, after, context.get()));
|
|
}
|
|
|
|
TEST(FuzzerPassAddUsefulConstructsTest,
|
|
CheckTypesIndicesAndConstantsAddedForUniformFacts) {
|
|
// The SPIR-V came from the following GLSL shader:
|
|
//
|
|
// #version 450
|
|
//
|
|
// struct S {
|
|
// int x;
|
|
// float y;
|
|
// int z;
|
|
// int w;
|
|
// };
|
|
//
|
|
// uniform buf {
|
|
// S s;
|
|
// uint w[10];
|
|
// };
|
|
//
|
|
// void main() {
|
|
// }
|
|
|
|
std::string shader = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %4 "main"
|
|
OpExecutionMode %4 OriginUpperLeft
|
|
OpSource GLSL 450
|
|
OpName %4 "main"
|
|
OpName %8 "S"
|
|
OpMemberName %8 0 "x"
|
|
OpMemberName %8 1 "y"
|
|
OpMemberName %8 2 "z"
|
|
OpMemberName %8 3 "w"
|
|
OpName %12 "buf"
|
|
OpMemberName %12 0 "s"
|
|
OpMemberName %12 1 "w"
|
|
OpName %14 ""
|
|
OpMemberDecorate %8 0 Offset 0
|
|
OpMemberDecorate %8 1 Offset 4
|
|
OpMemberDecorate %8 2 Offset 8
|
|
OpMemberDecorate %8 3 Offset 12
|
|
OpDecorate %11 ArrayStride 16
|
|
OpMemberDecorate %12 0 Offset 0
|
|
OpMemberDecorate %12 1 Offset 16
|
|
OpDecorate %12 Block
|
|
OpDecorate %14 DescriptorSet 0
|
|
OpDecorate %14 Binding 0
|
|
%2 = OpTypeVoid
|
|
%3 = OpTypeFunction %2
|
|
%6 = OpTypeInt 32 1
|
|
%7 = OpTypeFloat 32
|
|
%8 = OpTypeStruct %6 %7 %6 %6
|
|
%9 = OpTypeInt 32 0
|
|
%10 = OpConstant %9 10
|
|
%11 = OpTypeArray %9 %10
|
|
%12 = OpTypeStruct %8 %11
|
|
%13 = OpTypePointer Uniform %12
|
|
%14 = OpVariable %13 Uniform
|
|
%4 = OpFunction %2 None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
|
const auto consumer = nullptr;
|
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
FactManager fact_manager;
|
|
spvtools::ValidatorOptions validator_options;
|
|
TransformationContext transformation_context(&fact_manager,
|
|
validator_options);
|
|
|
|
FuzzerContext fuzzer_context(MakeUnique<PseudoRandomGenerator>(0).get(), 100);
|
|
protobufs::TransformationSequence transformation_sequence;
|
|
|
|
// Add some uniform facts.
|
|
|
|
// buf.s.x == 200
|
|
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200,
|
|
MakeUniformBufferElementDescriptor(0, 0, {0, 0})));
|
|
|
|
// buf.s.y == 0.5
|
|
const float float_value = 0.5;
|
|
uint32_t float_value_as_uint;
|
|
memcpy(&float_value_as_uint, &float_value, sizeof(float_value));
|
|
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), float_value_as_uint,
|
|
MakeUniformBufferElementDescriptor(0, 0, {0, 1})));
|
|
|
|
// buf.s.z == 300
|
|
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 300,
|
|
MakeUniformBufferElementDescriptor(0, 0, {0, 2})));
|
|
|
|
// buf.s.w == 400
|
|
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 400,
|
|
MakeUniformBufferElementDescriptor(0, 0, {0, 3})));
|
|
|
|
// buf.w[6] = 22
|
|
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 22,
|
|
MakeUniformBufferElementDescriptor(0, 0, {1, 6})));
|
|
|
|
// buf.w[8] = 23
|
|
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 23,
|
|
MakeUniformBufferElementDescriptor(0, 0, {1, 8})));
|
|
|
|
// Assert some things about the module that are not true prior to adding the
|
|
// pass
|
|
|
|
{
|
|
// No uniform int pointer
|
|
opt::analysis::Integer temp_type_signed_int(32, true);
|
|
opt::analysis::Integer* registered_type_signed_int =
|
|
context->get_type_mgr()
|
|
->GetRegisteredType(&temp_type_signed_int)
|
|
->AsInteger();
|
|
opt::analysis::Pointer type_pointer_uniform_signed_int(
|
|
registered_type_signed_int, SpvStorageClassUniform);
|
|
ASSERT_EQ(0,
|
|
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
|
|
|
|
// No uniform uint pointer
|
|
opt::analysis::Integer temp_type_unsigned_int(32, false);
|
|
opt::analysis::Integer* registered_type_unsigned_int =
|
|
context->get_type_mgr()
|
|
->GetRegisteredType(&temp_type_unsigned_int)
|
|
->AsInteger();
|
|
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
|
|
registered_type_unsigned_int, SpvStorageClassUniform);
|
|
ASSERT_EQ(
|
|
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
|
|
|
|
// No uniform float pointer
|
|
opt::analysis::Float temp_type_float(32);
|
|
opt::analysis::Float* registered_type_float =
|
|
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
|
|
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
|
|
SpvStorageClassUniform);
|
|
ASSERT_EQ(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
|
|
|
|
// No int constants 200, 300 nor 400
|
|
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
|
|
{200});
|
|
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
|
|
{300});
|
|
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
|
|
{400});
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_200));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_300));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_400));
|
|
|
|
// No float constant 0.5
|
|
opt::analysis::FloatConstant float_constant_zero_point_five(
|
|
registered_type_float, {float_value_as_uint});
|
|
ASSERT_EQ(nullptr, context->get_constant_mgr()->FindConstant(
|
|
&float_constant_zero_point_five));
|
|
|
|
// No uint constant 22
|
|
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
|
|
{22});
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&uint_constant_22));
|
|
|
|
// No uint constant 23
|
|
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
|
|
{23});
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&uint_constant_23));
|
|
|
|
// No int constants 0, 1, 2, 3, 6, 8
|
|
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
|
|
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
|
|
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
|
|
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
|
|
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
|
|
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_0));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_1));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_2));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_3));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_6));
|
|
ASSERT_EQ(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_8));
|
|
}
|
|
|
|
FuzzerPassAddUsefulConstructs pass(context.get(), &transformation_context,
|
|
&fuzzer_context, &transformation_sequence);
|
|
pass.Apply();
|
|
ASSERT_TRUE(IsValid(env, context.get()));
|
|
|
|
// Now assert some things about the module that should be true following the
|
|
// pass.
|
|
|
|
// We reconstruct all necessary types and constants to guard against the type
|
|
// and constant managers for the module having been invalidated.
|
|
|
|
{
|
|
// Uniform int pointer now present
|
|
opt::analysis::Integer temp_type_signed_int(32, true);
|
|
opt::analysis::Integer* registered_type_signed_int =
|
|
context->get_type_mgr()
|
|
->GetRegisteredType(&temp_type_signed_int)
|
|
->AsInteger();
|
|
opt::analysis::Pointer type_pointer_uniform_signed_int(
|
|
registered_type_signed_int, SpvStorageClassUniform);
|
|
ASSERT_NE(0,
|
|
context->get_type_mgr()->GetId(&type_pointer_uniform_signed_int));
|
|
|
|
// Uniform uint pointer now present
|
|
opt::analysis::Integer temp_type_unsigned_int(32, false);
|
|
opt::analysis::Integer* registered_type_unsigned_int =
|
|
context->get_type_mgr()
|
|
->GetRegisteredType(&temp_type_unsigned_int)
|
|
->AsInteger();
|
|
opt::analysis::Pointer type_pointer_uniform_unsigned_int(
|
|
registered_type_unsigned_int, SpvStorageClassUniform);
|
|
ASSERT_NE(
|
|
0, context->get_type_mgr()->GetId(&type_pointer_uniform_unsigned_int));
|
|
|
|
// Uniform float pointer now present
|
|
opt::analysis::Float temp_type_float(32);
|
|
opt::analysis::Float* registered_type_float =
|
|
context->get_type_mgr()->GetRegisteredType(&temp_type_float)->AsFloat();
|
|
opt::analysis::Pointer type_pointer_uniform_float(registered_type_float,
|
|
SpvStorageClassUniform);
|
|
ASSERT_NE(0, context->get_type_mgr()->GetId(&type_pointer_uniform_float));
|
|
|
|
// int constants 200, 300, 400 now present
|
|
opt::analysis::IntConstant int_constant_200(registered_type_signed_int,
|
|
{200});
|
|
opt::analysis::IntConstant int_constant_300(registered_type_signed_int,
|
|
{300});
|
|
opt::analysis::IntConstant int_constant_400(registered_type_signed_int,
|
|
{400});
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_200));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_300));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_400));
|
|
|
|
// float constant 0.5 now present
|
|
opt::analysis::FloatConstant float_constant_zero_point_five(
|
|
registered_type_float, {float_value_as_uint});
|
|
ASSERT_NE(nullptr, context->get_constant_mgr()->FindConstant(
|
|
&float_constant_zero_point_five));
|
|
|
|
// uint constant 22 now present
|
|
opt::analysis::IntConstant uint_constant_22(registered_type_unsigned_int,
|
|
{22});
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&uint_constant_22));
|
|
|
|
// uint constant 23 now present
|
|
opt::analysis::IntConstant uint_constant_23(registered_type_unsigned_int,
|
|
{23});
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&uint_constant_23));
|
|
|
|
// int constants 0, 1, 2, 3, 6, 8 now present
|
|
opt::analysis::IntConstant int_constant_0(registered_type_signed_int, {0});
|
|
opt::analysis::IntConstant int_constant_1(registered_type_signed_int, {1});
|
|
opt::analysis::IntConstant int_constant_2(registered_type_signed_int, {2});
|
|
opt::analysis::IntConstant int_constant_3(registered_type_signed_int, {3});
|
|
opt::analysis::IntConstant int_constant_6(registered_type_signed_int, {6});
|
|
opt::analysis::IntConstant int_constant_8(registered_type_signed_int, {8});
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_0));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_1));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_2));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_3));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_6));
|
|
ASSERT_NE(nullptr,
|
|
context->get_constant_mgr()->FindConstant(&int_constant_8));
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace fuzz
|
|
} // namespace spvtools
|