// 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(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(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(context.get()), validator_options); ASSERT_FALSE(TransformationReplaceParamsWithStruct({12}, 100, 101, {{}}) .IsApplicable(context.get(), transformation_context)); } } // namespace } // namespace fuzz } // namespace spvtools