// 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 "gtest/gtest.h" #include "source/fuzz/fuzzer_util.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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Bad: id already in use. ASSERT_FALSE(TransformationEquationInstruction(7, spv::Op::OpSNegate, {7}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: identified instruction does not exist. ASSERT_FALSE(TransformationEquationInstruction( 14, spv::Op::OpSNegate, {7}, MakeInstructionDescriptor(13, spv::Op::OpLoad, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: id 100 does not exist ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {100}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: id 20 is an OpUndef ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {20}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: id 30 is not available right before its definition ASSERT_FALSE(TransformationEquationInstruction( 14, spv::Op::OpSNegate, {30}, MakeInstructionDescriptor(30, spv::Op::OpCopyObject, 0)) .IsApplicable(context.get(), transformation_context)); // Bad: too many arguments to OpSNegate. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {7, 7}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: 40 is a type id. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {40}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: wrong type of argument to OpSNegate. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpSNegate, {41}, return_instruction) .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, spv::Op::OpSNegate, {7}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(14)); ASSERT_EQ(nullptr, context->get_instr_block(14)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_EQ(spv::Op::OpSNegate, context->get_def_use_mgr()->GetDef(14)->opcode()); ASSERT_EQ(13, context->get_instr_block(14)->id()); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation2 = TransformationEquationInstruction( 15, spv::Op::OpSNegate, {14}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); 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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Bad: too few arguments to OpLogicalNot. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpLogicalNot, {}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: 6 is a type id. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpLogicalNot, {6}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: wrong type of argument to OpLogicalNot. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpLogicalNot, {21}, return_instruction) .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, spv::Op::OpLogicalNot, {7}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation2 = TransformationEquationInstruction( 15, spv::Op::OpLogicalNot, {14}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); 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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Bad: too many arguments to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction( 14, spv::Op::OpIAdd, {15, 16, 16}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: boolean argument to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {15, 32}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: type as argument to OpIAdd. ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {33, 16}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: arguments of mismatched widths ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {15, 31}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Bad: arguments of mismatched widths ASSERT_FALSE(TransformationEquationInstruction(14, spv::Op::OpIAdd, {31, 15}, return_instruction) .IsApplicable(context.get(), transformation_context)); auto transformation1 = TransformationEquationInstruction( 14, spv::Op::OpIAdd, {15, 16}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation2 = TransformationEquationInstruction( 19, spv::Op::OpISub, {14, 16}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}))); auto transformation3 = TransformationEquationInstruction( 20, spv::Op::OpISub, {14, 15}, return_instruction); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation3, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}))); auto transformation4 = TransformationEquationInstruction( 22, spv::Op::OpISub, {16, 14}, return_instruction); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation4, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation5 = TransformationEquationInstruction( 24, spv::Op::OpSNegate, {22}, return_instruction); ASSERT_TRUE( transformation5.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation5, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); 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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); auto transformation1 = TransformationEquationInstruction( 14, spv::Op::OpISub, {15, 16}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation2 = TransformationEquationInstruction( 17, spv::Op::OpIAdd, {14, 16}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}))); auto transformation3 = TransformationEquationInstruction( 18, spv::Op::OpIAdd, {16, 14}, return_instruction); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation3, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}))); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}))); auto transformation4 = TransformationEquationInstruction( 19, spv::Op::OpISub, {14, 15}, return_instruction); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation4, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation5 = TransformationEquationInstruction( 20, spv::Op::OpSNegate, {19}, return_instruction); ASSERT_TRUE( transformation5.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation5, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}))); auto transformation6 = TransformationEquationInstruction( 21, spv::Op::OpISub, {14, 19}, return_instruction); ASSERT_TRUE( transformation6.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation6, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}))); auto transformation7 = TransformationEquationInstruction( 22, spv::Op::OpISub, {14, 18}, return_instruction); ASSERT_TRUE( transformation7.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation7, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation8 = TransformationEquationInstruction( 23, spv::Op::OpSNegate, {22}, return_instruction); ASSERT_TRUE( transformation8.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation8, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); 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, Bitcast) { 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 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %6 2 %10 = OpTypeVector %7 2 %11 = OpTypeVector %8 2 %21 = OpTypeBool %22 = OpTypeVector %21 2 %15 = OpConstant %6 24 %16 = OpConstant %7 24 %17 = OpConstant %8 24 %18 = OpConstantComposite %9 %15 %15 %19 = OpConstantComposite %10 %16 %16 %20 = OpConstantComposite %11 %17 %17 %23 = OpConstantTrue %21 %24 = OpConstantComposite %22 %23 %23 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Too many operands. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {15, 16}, insert_before) .IsApplicable(context.get(), transformation_context)); // Too few operands. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {}, insert_before) .IsApplicable(context.get(), transformation_context)); // Operand's id is invalid. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {50}, insert_before) .IsApplicable(context.get(), transformation_context)); // Operand's type is invalid ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {13}, insert_before) .IsApplicable(context.get(), transformation_context)); // Operand must be a scalar or a vector of numerical type. #ifndef NDEBUG ASSERT_DEATH(TransformationEquationInstruction(50, spv::Op::OpBitcast, {23}, insert_before) .IsApplicable(context.get(), transformation_context), "Operand is not a scalar or a vector of numerical type"); ASSERT_DEATH(TransformationEquationInstruction(50, spv::Op::OpBitcast, {24}, insert_before) .IsApplicable(context.get(), transformation_context), "Only vectors of numerical components are supported"); #else ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {23}, insert_before) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {24}, insert_before) .IsApplicable(context.get(), transformation_context)); #endif for (uint32_t operand_id = 15, fresh_id = 50; operand_id <= 20; ++operand_id, ++fresh_id) { TransformationEquationInstruction transformation( fresh_id, spv::Op::OpBitcast, {operand_id}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } std::string expected_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 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %6 2 %10 = OpTypeVector %7 2 %11 = OpTypeVector %8 2 %21 = OpTypeBool %22 = OpTypeVector %21 2 %15 = OpConstant %6 24 %16 = OpConstant %7 24 %17 = OpConstant %8 24 %18 = OpConstantComposite %9 %15 %15 %19 = OpConstantComposite %10 %16 %16 %20 = OpConstantComposite %11 %17 %17 %23 = OpConstantTrue %21 %24 = OpConstantComposite %22 %23 %23 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %8 %15 %51 = OpBitcast %8 %16 %52 = OpBitcast %6 %17 %53 = OpBitcast %11 %18 %54 = OpBitcast %11 %19 %55 = OpBitcast %9 %20 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationEquationInstructionTest, BitcastResultTypeFloatDoesNotExist) { 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 = OpTypeInt 32 0 %9 = OpTypeVector %6 2 %10 = OpTypeVector %7 2 %15 = OpConstant %6 24 %16 = OpConstant %7 24 %18 = OpConstantComposite %9 %15 %15 %19 = OpConstantComposite %10 %16 %16 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Scalar floating-point type does not exist. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {15}, insert_before) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {16}, insert_before) .IsApplicable(context.get(), transformation_context)); // Vector of floating-point components does not exist. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {18}, insert_before) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {19}, insert_before) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist1) { 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 %8 = OpTypeFloat 32 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Scalar integral type does not exist. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {17}, insert_before) .IsApplicable(context.get(), transformation_context)); // Vector of integral components does not exist. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpBitcast, {20}, insert_before) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist2) { 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 %4 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %4 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); { TransformationEquationInstruction transformation(50, spv::Op::OpBitcast, {17}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(51, spv::Op::OpBitcast, {20}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } std::string expected_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 %4 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %4 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %4 %17 %51 = OpBitcast %9 %20 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist3) { 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 %4 = OpTypeInt 32 1 %8 = OpTypeFloat 32 %9 = OpTypeVector %4 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); { TransformationEquationInstruction transformation(50, spv::Op::OpBitcast, {17}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(51, spv::Op::OpBitcast, {20}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } std::string expected_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 %4 = OpTypeInt 32 1 %8 = OpTypeFloat 32 %9 = OpTypeVector %4 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %4 %17 %51 = OpBitcast %9 %20 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist4) { 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 %4 = OpTypeInt 32 1 %8 = OpTypeFloat 32 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); { TransformationEquationInstruction transformation(50, spv::Op::OpBitcast, {17}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } ASSERT_FALSE(TransformationEquationInstruction(51, spv::Op::OpBitcast, {20}, insert_before) .IsApplicable(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 %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %4 = OpTypeInt 32 1 %8 = OpTypeFloat 32 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %4 %17 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist5) { 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 %4 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); { TransformationEquationInstruction transformation(50, spv::Op::OpBitcast, {17}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } ASSERT_FALSE(TransformationEquationInstruction(51, spv::Op::OpBitcast, {20}, insert_before) .IsApplicable(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 %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %4 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %4 %17 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist6) { 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 %4 = OpTypeInt 32 1 %5 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %5 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); { TransformationEquationInstruction transformation(50, spv::Op::OpBitcast, {17}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(51, spv::Op::OpBitcast, {20}, insert_before); 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 %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %4 = OpTypeInt 32 1 %5 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %5 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %4 %17 %51 = OpBitcast %9 %20 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist7) { 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 %4 = OpTypeInt 32 1 %5 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %4 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto insert_before = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); { TransformationEquationInstruction transformation(50, spv::Op::OpBitcast, {17}, insert_before); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(51, spv::Op::OpBitcast, {20}, insert_before); 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 %12 "main" OpExecutionMode %12 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %4 = OpTypeInt 32 1 %5 = OpTypeInt 32 0 %8 = OpTypeFloat 32 %9 = OpTypeVector %4 2 %11 = OpTypeVector %8 2 %17 = OpConstant %8 24 %20 = OpConstantComposite %11 %17 %17 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpBitcast %4 %17 %51 = OpBitcast %9 %20 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, 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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); auto transformation1 = TransformationEquationInstruction( 522, spv::Op::OpISub, {113, 113}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation2 = TransformationEquationInstruction( 570, spv::Op::OpIAdd, {522, 113}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, 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 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); auto transformation1 = TransformationEquationInstruction( 522, spv::Op::OpISub, {113, 113}, return_instruction); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); auto transformation2 = TransformationEquationInstruction( 570, spv::Op::OpIAdd, {522, 113}, return_instruction); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, 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 %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, ConversionInstructions) { 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 %4 = OpTypeInt 32 0 %5 = OpTypeFloat 32 %7 = OpTypeVector %6 3 %8 = OpTypeVector %4 3 %9 = OpTypeVector %5 3 %10 = OpConstant %6 12 %20 = OpConstant %6 12 %11 = OpConstant %4 12 %21 = OpConstant %4 12 %14 = OpConstant %5 12 %15 = OpConstantComposite %7 %10 %10 %10 %18 = OpConstantComposite %7 %10 %10 %10 %16 = OpConstantComposite %8 %11 %11 %11 %19 = OpConstantComposite %8 %11 %11 %11 %17 = OpConstantComposite %9 %14 %14 %14 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Too few instruction operands. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF, {}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Too many instruction operands. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF, {15, 16}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Operand has no type id. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF, {7}, return_instruction) .IsApplicable(context.get(), transformation_context)); // OpConvertSToF and OpConvertUToF require an operand to have scalar or vector // of integral components type. ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF, {17}, return_instruction) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertSToF, {14}, return_instruction) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertUToF, {17}, return_instruction) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(50, spv::Op::OpConvertUToF, {14}, return_instruction) .IsApplicable(context.get(), transformation_context)); { TransformationEquationInstruction transformation(50, spv::Op::OpConvertSToF, {15}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(51, spv::Op::OpConvertSToF, {10}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(52, spv::Op::OpConvertUToF, {16}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(53, spv::Op::OpConvertUToF, {11}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(58, spv::Op::OpConvertSToF, {18}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(59, spv::Op::OpConvertUToF, {19}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(60, spv::Op::OpConvertSToF, {20}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); } { TransformationEquationInstruction transformation(61, spv::Op::OpConvertUToF, {21}, return_instruction); 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_transformations = 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 %4 = OpTypeInt 32 0 %5 = OpTypeFloat 32 %7 = OpTypeVector %6 3 %8 = OpTypeVector %4 3 %9 = OpTypeVector %5 3 %10 = OpConstant %6 12 %20 = OpConstant %6 12 %11 = OpConstant %4 12 %21 = OpConstant %4 12 %14 = OpConstant %5 12 %15 = OpConstantComposite %7 %10 %10 %10 %18 = OpConstantComposite %7 %10 %10 %10 %16 = OpConstantComposite %8 %11 %11 %11 %19 = OpConstantComposite %8 %11 %11 %11 %17 = OpConstantComposite %9 %14 %14 %14 %12 = OpFunction %2 None %3 %13 = OpLabel %50 = OpConvertSToF %9 %15 %51 = OpConvertSToF %5 %10 %52 = OpConvertUToF %9 %16 %53 = OpConvertUToF %5 %11 %58 = OpConvertSToF %9 %18 %59 = OpConvertUToF %9 %19 %60 = OpConvertSToF %5 %20 %61 = OpConvertUToF %5 %21 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); } TEST(TransformationEquationInstructionTest, FloatResultTypeDoesNotExist) { 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 0 %7 = OpTypeInt 32 1 %8 = OpTypeVector %6 3 %9 = OpTypeVector %7 3 %10 = OpConstant %6 24 %11 = OpConstant %7 25 %14 = OpConstantComposite %8 %10 %10 %10 %15 = OpConstantComposite %9 %11 %11 %11 %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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::InstructionDescriptor return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Scalar float type doesn't exist. ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertUToF, {10}, return_instruction) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertSToF, {11}, return_instruction) .IsApplicable(context.get(), transformation_context)); // Vector float type doesn't exist. ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertUToF, {14}, return_instruction) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationEquationInstruction(16, spv::Op::OpConvertSToF, {15}, return_instruction) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationEquationInstructionTest, HandlesIrrelevantIds) { 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); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); auto return_instruction = MakeInstructionDescriptor(13, spv::Op::OpReturn, 0); // Applicable. TransformationEquationInstruction transformation( 14, spv::Op::OpIAdd, {15, 16}, return_instruction); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); // Handles irrelevant ids. transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); transformation_context.GetFactManager()->AddFactIdIsIrrelevant(15); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); } TEST(TransformationEquationInstructionTest, HandlesDeadBlock) { 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 OpSelectionMerge %40 None OpBranchConditional %32 %40 %41 %41 = OpLabel OpBranch %40 %40 = 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); transformation_context.GetFactManager()->AddFactBlockIsDead(41); TransformationEquationInstruction transformation1( 14, spv::Op::OpIAdd, {15, 16}, MakeInstructionDescriptor(13, spv::Op::OpSelectionMerge, 0)); // No synonym is created since block is dead. TransformationEquationInstruction transformation2( 100, spv::Op::OpISub, {14, 16}, MakeInstructionDescriptor(41, spv::Op::OpBranch, 0)); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), &transformation_context); ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {}))); } } // namespace } // namespace fuzz } // namespace spvtools