// 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/transformation_equation_instruction.h" #include "source/fuzz/instruction_descriptor.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { namespace fuzz { namespace { TEST(TransformationEquationInstructionTest, SignedNegate) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 24 %40 = OpTypeBool %41 = OpConstantTrue %40 %20 = OpUndef %6 %12 = OpFunction %2 None %3 %13 = OpLabel %30 = OpCopyObject %6 %7 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); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); // Bad: id already in use. ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: identified instruction does not exist. ASSERT_FALSE( TransformationEquationInstruction( 14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: id 100 does not exist ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: id 20 is an OpUndef ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: id 30 is not available right before its definition ASSERT_FALSE(TransformationEquationInstruction( 14, SpvOpSNegate, {30}, MakeInstructionDescriptor(30, SpvOpCopyObject, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: too many arguments to OpSNegate. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: 40 is a type id. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: wrong type of argument to OpSNegate. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41}, return_instruction) .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, SpvOpSNegate, {7}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 15, SpvOpSNegate, {14}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}))); std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 24 %40 = OpTypeBool %41 = OpConstantTrue %40 %20 = OpUndef %6 %12 = OpFunction %2 None %3 %13 = OpLabel %30 = OpCopyObject %6 %7 %14 = OpSNegate %6 %7 %15 = OpSNegate %6 %14 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationEquationInstructionTest, LogicalNot) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeBool %7 = OpConstantTrue %6 %20 = OpTypeInt 32 0 %21 = OpConstant %20 5 %12 = OpFunction %2 None %3 %13 = 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); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); // Bad: too few arguments to OpLogicalNot. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: 6 is a type id. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: wrong type of argument to OpLogicalNot. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21}, return_instruction) .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, SpvOpLogicalNot, {7}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 15, SpvOpLogicalNot, {14}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}))); std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeBool %7 = OpConstantTrue %6 %20 = OpTypeInt 32 0 %21 = OpConstant %20 5 %12 = OpFunction %2 None %3 %13 = OpLabel %14 = OpLogicalNot %6 %7 %15 = OpLogicalNot %6 %14 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationEquationInstructionTest, AddSubNegate1) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %30 = OpTypeVector %6 3 %15 = OpConstant %6 24 %16 = OpConstant %6 37 %31 = OpConstantComposite %30 %15 %16 %15 %33 = OpTypeBool %32 = OpConstantTrue %33 %12 = OpFunction %2 None %3 %13 = 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); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); // Bad: too many arguments to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: boolean argument to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: type as argument to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: arguments of mismatched widths ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: arguments of mismatched widths ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15}, return_instruction) .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, SpvOpIAdd, {15, 16}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 19, SpvOpISub, {14, 16}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}))); auto transformation3 = TransformationEquationInstruction( 20, SpvOpISub, {14, 15}, return_instruction); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}))); auto transformation4 = TransformationEquationInstruction( 22, SpvOpISub, {16, 14}, return_instruction); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation5 = TransformationEquationInstruction( 24, SpvOpSNegate, {22}, return_instruction); ASSERT_TRUE( transformation5.IsApplicable(context.get(), transformation_context)); transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}))); std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %30 = OpTypeVector %6 3 %15 = OpConstant %6 24 %16 = OpConstant %6 37 %31 = OpConstantComposite %30 %15 %16 %15 %33 = OpTypeBool %32 = OpConstantTrue %33 %12 = OpFunction %2 None %3 %13 = OpLabel %14 = OpIAdd %6 %15 %16 %19 = OpISub %6 %14 %16 ; ==> synonymous(%19, %15) %20 = OpISub %6 %14 %15 ; ==> synonymous(%20, %16) %22 = OpISub %6 %16 %14 %24 = OpSNegate %6 %22 ; ==> synonymous(%24, %15) OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationEquationInstructionTest, AddSubNegate2) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %15 = OpConstant %6 24 %16 = OpConstant %6 37 %12 = OpFunction %2 None %3 %13 = 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); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); auto transformation1 = TransformationEquationInstruction( 14, SpvOpISub, {15, 16}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 17, SpvOpIAdd, {14, 16}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}))); auto transformation3 = TransformationEquationInstruction( 18, SpvOpIAdd, {16, 14}, return_instruction); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); transformation3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}))); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}))); auto transformation4 = TransformationEquationInstruction( 19, SpvOpISub, {14, 15}, return_instruction); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); transformation4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation5 = TransformationEquationInstruction( 20, SpvOpSNegate, {19}, return_instruction); ASSERT_TRUE( transformation5.IsApplicable(context.get(), transformation_context)); transformation5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}))); auto transformation6 = TransformationEquationInstruction( 21, SpvOpISub, {14, 19}, return_instruction); ASSERT_TRUE( transformation6.IsApplicable(context.get(), transformation_context)); transformation6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}))); auto transformation7 = TransformationEquationInstruction( 22, SpvOpISub, {14, 18}, return_instruction); ASSERT_TRUE( transformation7.IsApplicable(context.get(), transformation_context)); transformation7.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation8 = TransformationEquationInstruction( 23, SpvOpSNegate, {22}, return_instruction); ASSERT_TRUE( transformation8.IsApplicable(context.get(), transformation_context)); transformation8.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}))); std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %15 = OpConstant %6 24 %16 = OpConstant %6 37 %12 = OpFunction %2 None %3 %13 = OpLabel %14 = OpISub %6 %15 %16 %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15) %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15) %19 = OpISub %6 %14 %15 %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16) %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) %22 = OpISub %6 %14 %18 %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16) OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationEquationInstructionTest, Miscellaneous1) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %113 = OpConstant %6 24 %12 = OpFunction %2 None %3 %13 = 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); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); auto transformation1 = TransformationEquationInstruction( 522, SpvOpISub, {113, 113}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 570, SpvOpIAdd, {522, 113}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %113 = OpConstant %6 24 %12 = OpFunction %2 None %3 %13 = OpLabel %522 = OpISub %6 %113 %113 %570 = OpIAdd %6 %522 %113 OpReturn OpFunctionEnd )"; ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {}))); ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationEquationInstructionTest, Miscellaneous2) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %113 = OpConstant %6 24 %12 = OpFunction %2 None %3 %13 = 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); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); auto transformation1 = TransformationEquationInstruction( 522, SpvOpISub, {113, 113}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); transformation1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); auto transformation2 = TransformationEquationInstruction( 570, SpvOpIAdd, {522, 113}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); transformation2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %113 = OpConstant %6 24 %12 = OpFunction %2 None %3 %13 = OpLabel %522 = OpISub %6 %113 %113 %570 = OpIAdd %6 %522 %113 OpReturn OpFunctionEnd )"; ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {}))); ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } } // namespace } // namespace fuzz } // namespace spvtools