blob: afa782e4fcdde80b3192d9df0020780daa426f02 [file] [log] [blame]
// Copyright (c) 2020 Vasyl Teliman
//
// 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/transformation_replace_params_with_struct.h"
#include "gtest/gtest.h"
#include "source/fuzz/fuzzer_util.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
TEST(TransformationReplaceParamsWithStructTest, BasicTest) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %13 0 RelaxedPrecision
OpDecorate %16 RelaxedPrecision
%2 = OpTypeVoid
%6 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%7 = OpTypePointer Private %6
%8 = OpTypeFloat 32
%9 = OpTypePointer Private %8
%10 = OpTypeVector %8 2
%11 = OpTypePointer Private %10
%12 = OpTypeBool
%51 = OpTypeFunction %2 %12
%64 = OpTypeStruct %6
%63 = OpTypeFunction %2 %64
%65 = OpTypeFunction %2 %6
%75 = OpTypeStruct %8
%76 = OpTypeFunction %2 %75
%77 = OpTypeFunction %2 %8
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%45 = OpTypeStruct %6 %10 %13
%46 = OpTypeStruct %12
%47 = OpTypeStruct %8 %45 %46
%14 = OpTypePointer Private %13
%15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
%22 = OpConstant %6 0
%23 = OpConstant %8 0
%26 = OpConstantComposite %10 %23 %23
%27 = OpConstantTrue %12
%28 = OpConstantComposite %13 %22 %23
%4 = OpFunction %2 None %3
%5 = OpLabel
%41 = OpVariable %40 Function %27
%33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27
OpReturn
OpFunctionEnd
; adjust type of the function in-place
%20 = OpFunction %2 None %15
%16 = OpFunctionParameter %6
%17 = OpFunctionParameter %8
%18 = OpFunctionParameter %10
%19 = OpFunctionParameter %13
%42 = OpFunctionParameter %40
%43 = OpFunctionParameter %12
%21 = OpLabel
OpReturn
OpFunctionEnd
; create a new function type
%50 = OpFunction %2 None %51
%52 = OpFunctionParameter %12
%53 = OpLabel
OpReturn
OpFunctionEnd
%54 = OpFunction %2 None %51
%55 = OpFunctionParameter %12
%56 = OpLabel
OpReturn
OpFunctionEnd
; reuse an existing function type
%57 = OpFunction %2 None %63
%58 = OpFunctionParameter %64
%59 = OpLabel
OpReturn
OpFunctionEnd
%60 = OpFunction %2 None %65
%61 = OpFunctionParameter %6
%62 = OpLabel
OpReturn
OpFunctionEnd
%66 = OpFunction %2 None %65
%67 = OpFunctionParameter %6
%68 = OpLabel
OpReturn
OpFunctionEnd
; don't adjust the type of the function if it creates a duplicate
%69 = OpFunction %2 None %76
%70 = OpFunctionParameter %75
%71 = OpLabel
OpReturn
OpFunctionEnd
%72 = OpFunction %2 None %77
%73 = OpFunctionParameter %8
%74 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
// |parameter_id| is empty.
ASSERT_FALSE(
TransformationReplaceParamsWithStruct({}, 90, 91, {{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
// |parameter_id| has duplicates.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 16, 17}, 90, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
// |parameter_id| has invalid values.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({21, 16, 17}, 90, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 90, 17}, 90, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
// Parameter's belong to different functions.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 52}, 90, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
// Parameter has unsupported type.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 42, 43}, 90, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
// OpTypeStruct does not exist in the module.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 43}, 90, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
// |caller_id_to_fresh_composite_id| misses values.
ASSERT_FALSE(
TransformationReplaceParamsWithStruct({16, 17}, 90, 91, {{90, 93}})
.IsApplicable(context.get(), transformation_context));
// All fresh ids must be unique.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 90,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
{{33, 90}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
{{33, 92}, {90, 92}})
.IsApplicable(context.get(), transformation_context));
// All 'fresh' ids must be fresh.
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91,
{{33, 33}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 33, 91,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 33,
{{33, 92}, {90, 93}})
.IsApplicable(context.get(), transformation_context));
{
TransformationReplaceParamsWithStruct transformation({16, 18, 19}, 90, 91,
{{33, 92}, {90, 93}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
}
{
TransformationReplaceParamsWithStruct transformation({43}, 93, 94,
{{33, 95}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
}
{
TransformationReplaceParamsWithStruct transformation({17, 91, 94}, 96, 97,
{{33, 98}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
}
{
TransformationReplaceParamsWithStruct transformation({55}, 99, 100, {{}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
}
{
TransformationReplaceParamsWithStruct transformation({61}, 101, 102, {{}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
}
{
TransformationReplaceParamsWithStruct transformation({73}, 103, 104, {{}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
}
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
std::string expected_shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpMemberDecorate %13 0 RelaxedPrecision
OpDecorate %16 RelaxedPrecision
%2 = OpTypeVoid
%6 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%7 = OpTypePointer Private %6
%8 = OpTypeFloat 32
%9 = OpTypePointer Private %8
%10 = OpTypeVector %8 2
%11 = OpTypePointer Private %10
%12 = OpTypeBool
%51 = OpTypeFunction %2 %12
%64 = OpTypeStruct %6
%63 = OpTypeFunction %2 %64
%65 = OpTypeFunction %2 %6
%75 = OpTypeStruct %8
%76 = OpTypeFunction %2 %75
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%45 = OpTypeStruct %6 %10 %13
%46 = OpTypeStruct %12
%47 = OpTypeStruct %8 %45 %46
%14 = OpTypePointer Private %13
%22 = OpConstant %6 0
%23 = OpConstant %8 0
%26 = OpConstantComposite %10 %23 %23
%27 = OpConstantTrue %12
%28 = OpConstantComposite %13 %22 %23
%15 = OpTypeFunction %2 %40 %47
%99 = OpTypeFunction %2 %46
%4 = OpFunction %2 None %3
%5 = OpLabel
%41 = OpVariable %40 Function %27
%92 = OpCompositeConstruct %45 %22 %26 %28
%95 = OpCompositeConstruct %46 %27
%98 = OpCompositeConstruct %47 %23 %92 %95
%33 = OpFunctionCall %2 %20 %41 %98
OpReturn
OpFunctionEnd
%20 = OpFunction %2 None %15
%42 = OpFunctionParameter %40
%97 = OpFunctionParameter %47
%21 = OpLabel
%94 = OpCompositeExtract %46 %97 2
%91 = OpCompositeExtract %45 %97 1
%17 = OpCompositeExtract %8 %97 0
%43 = OpCompositeExtract %12 %94 0
%19 = OpCompositeExtract %13 %91 2
%18 = OpCompositeExtract %10 %91 1
%16 = OpCompositeExtract %6 %91 0
OpReturn
OpFunctionEnd
%50 = OpFunction %2 None %51
%52 = OpFunctionParameter %12
%53 = OpLabel
OpReturn
OpFunctionEnd
%54 = OpFunction %2 None %99
%100 = OpFunctionParameter %46
%56 = OpLabel
%55 = OpCompositeExtract %12 %100 0
OpReturn
OpFunctionEnd
%57 = OpFunction %2 None %63
%58 = OpFunctionParameter %64
%59 = OpLabel
OpReturn
OpFunctionEnd
%60 = OpFunction %2 None %63
%102 = OpFunctionParameter %64
%62 = OpLabel
%61 = OpCompositeExtract %6 %102 0
OpReturn
OpFunctionEnd
%66 = OpFunction %2 None %65
%67 = OpFunctionParameter %6
%68 = OpLabel
OpReturn
OpFunctionEnd
%69 = OpFunction %2 None %76
%70 = OpFunctionParameter %75
%71 = OpLabel
OpReturn
OpFunctionEnd
%72 = OpFunction %2 None %76
%104 = OpFunctionParameter %75
%74 = OpLabel
%73 = OpCompositeExtract %8 %104 0
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
}
TEST(TransformationReplaceParamsWithStructTest, ParametersRemainValid) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%6 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%8 = OpTypeFloat 32
%10 = OpTypeVector %8 2
%12 = OpTypeBool
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%45 = OpTypeStruct %6 %8 %13
%47 = OpTypeStruct %45 %12 %10
%15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
%20 = OpFunction %2 None %15
%16 = OpFunctionParameter %6
%17 = OpFunctionParameter %8
%18 = OpFunctionParameter %10
%19 = OpFunctionParameter %13
%42 = OpFunctionParameter %40
%43 = OpFunctionParameter %12
%21 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
{
// Try to replace parameters in "increasing" order of their declaration.
TransformationReplaceParamsWithStruct transformation({16, 17, 19}, 70, 71,
{{}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%6 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%8 = OpTypeFloat 32
%10 = OpTypeVector %8 2
%12 = OpTypeBool
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%45 = OpTypeStruct %6 %8 %13
%47 = OpTypeStruct %45 %12 %10
%15 = OpTypeFunction %2 %10 %40 %12 %45
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
%20 = OpFunction %2 None %15
%18 = OpFunctionParameter %10
%42 = OpFunctionParameter %40
%43 = OpFunctionParameter %12
%71 = OpFunctionParameter %45
%21 = OpLabel
%19 = OpCompositeExtract %13 %71 2
%17 = OpCompositeExtract %8 %71 1
%16 = OpCompositeExtract %6 %71 0
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
{
// Try to replace parameters in "decreasing" order of their declaration.
TransformationReplaceParamsWithStruct transformation({71, 43, 18}, 72, 73,
{{}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(),
&transformation_context);
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%6 = OpTypeInt 32 1
%3 = OpTypeFunction %2
%8 = OpTypeFloat 32
%10 = OpTypeVector %8 2
%12 = OpTypeBool
%40 = OpTypePointer Function %12
%13 = OpTypeStruct %6 %8
%45 = OpTypeStruct %6 %8 %13
%47 = OpTypeStruct %45 %12 %10
%15 = OpTypeFunction %2 %40 %47
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
%20 = OpFunction %2 None %15
%42 = OpFunctionParameter %40
%73 = OpFunctionParameter %47
%21 = OpLabel
%18 = OpCompositeExtract %10 %73 2
%43 = OpCompositeExtract %12 %73 1
%71 = OpCompositeExtract %45 %73 0
%19 = OpCompositeExtract %13 %71 2
%17 = OpCompositeExtract %8 %71 1
%16 = OpCompositeExtract %6 %71 0
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
TEST(TransformationReplaceParamsWithStructTest, IsomorphicStructs) {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %16 "main"
OpExecutionMode %16 OriginUpperLeft
OpSource ESSL 310
%2 = OpTypeVoid
%6 = OpTypeInt 32 1
%7 = OpTypeStruct %6
%8 = OpTypeStruct %6
%9 = OpTypeStruct %8
%10 = OpTypeFunction %2 %7
%15 = OpTypeFunction %2
%16 = OpFunction %2 None %15
%17 = OpLabel
OpReturn
OpFunctionEnd
%11 = OpFunction %2 None %10
%12 = OpFunctionParameter %7
%13 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options);
ASSERT_FALSE(TransformationReplaceParamsWithStruct({12}, 100, 101, {{}})
.IsApplicable(context.get(), transformation_context));
}
} // namespace
} // namespace fuzz
} // namespace spvtools