// 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_propagate_instruction_down.h" #include "source/fuzz/counter_overflow_id_source.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { namespace fuzz { namespace { TEST(TransformationPropagateInstructionDownTest, 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 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %9 = OpTypePointer Function %6 %4 = OpFunction %2 None %3 ; Has no instruction to propagate %5 = OpLabel %10 = OpVariable %9 Function %8 = OpCopyObject %6 %7 OpStore %10 %8 OpBranch %11 ; Unreachable block %100 = OpLabel %101 = OpCopyObject %6 %7 OpBranch %11 ; Selection header ; ; One of acceptable successors has an OpPhi that uses propagated ; instruction's id %11 = OpLabel %19 = OpCopyObject %6 %7 OpSelectionMerge %18 None OpBranchConditional %13 %14 %18 ; %16 has no acceptable successors %14 = OpLabel %20 = OpPhi %6 %19 %11 %15 = OpCopyObject %6 %7 ; dependency OpBranch %16 %16 = OpLabel %17 = OpCopyObject %6 %15 OpBranch %18 ; Can be applied %18 = OpLabel %21 = OpCopyObject %6 %7 OpSelectionMerge %24 None OpBranchConditional %13 %22 %23 %22 = OpLabel %29 = OpPhi %6 %7 %18 OpStore %10 %21 OpBranch %24 %23 = OpLabel OpStore %10 %21 OpBranch %24 %24 = OpLabel OpStore %10 %21 OpBranch %32 ; Can't replace all uses of the propagated instruction: %30 is ; propagated into %27. %32 = OpLabel OpLoopMerge %28 %27 None OpBranchConditional %13 %26 %28 %26 = OpLabel %25 = OpCopyObject %6 %7 %30 = OpCopyObject %6 %25 OpBranchConditional %13 %27 %28 %27 = OpLabel OpBranch %32 %28 = OpLabel %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); // Invalid block id. ASSERT_FALSE(TransformationPropagateInstructionDown(200, 200, {{}}) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationPropagateInstructionDown(101, 200, {{}}) .IsApplicable(context.get(), transformation_context)); // The block is unreachable. ASSERT_FALSE(TransformationPropagateInstructionDown(100, 200, {{}}) .IsApplicable(context.get(), transformation_context)); // The block has no instruction to propagate. ASSERT_FALSE(TransformationPropagateInstructionDown(5, 200, {{{11, 201}}}) .IsApplicable(context.get(), transformation_context)); // The block has no acceptable successors. ASSERT_FALSE(TransformationPropagateInstructionDown(16, 200, {{{18, 201}}}) .IsApplicable(context.get(), transformation_context)); // One of acceptable successors has an OpPhi that uses propagated // instruction's id. ASSERT_FALSE( TransformationPropagateInstructionDown(11, 200, {{{14, 201}, {18, 202}}}) .IsApplicable(context.get(), transformation_context)); #ifndef NDEBUG // Not all fresh ids are provided. ASSERT_DEATH( TransformationPropagateInstructionDown(18, 200, {{{22, 201}, {202, 203}}}) .IsApplicable(context.get(), transformation_context), "Bad attempt to query whether overflow ids are available."); #endif // Not all fresh ids are fresh. ASSERT_FALSE(TransformationPropagateInstructionDown( 18, 18, {{{22, 201}, {23, 202}, {202, 203}}}) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationPropagateInstructionDown( 18, 200, {{{22, 22}, {23, 202}, {202, 203}}}) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationPropagateInstructionDown( 18, 18, {{{22, 22}, {23, 202}, {202, 203}}}) .IsApplicable(context.get(), transformation_context)); // Not all fresh ids are unique. ASSERT_FALSE(TransformationPropagateInstructionDown( 18, 200, {{{22, 200}, {23, 202}, {202, 200}}}) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationPropagateInstructionDown( 18, 200, {{{22, 201}, {23, 202}, {202, 200}}}) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationPropagateInstructionDown( 18, 200, {{{22, 201}, {23, 201}, {202, 203}}}) .IsApplicable(context.get(), transformation_context)); // Can't replace all uses of the propagated instruction: %30 is propagated // into %27. ASSERT_FALSE(TransformationPropagateInstructionDown(26, 200, {{{27, 201}}}) .IsApplicable(context.get(), transformation_context)); { TransformationPropagateInstructionDown transformation( 18, 200, {{{22, 201}, {23, 202}, {202, 203}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {}))); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {}))); } 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %9 = OpTypePointer Function %6 %4 = OpFunction %2 None %3 ; Has no instruction to propagate %5 = OpLabel %10 = OpVariable %9 Function %8 = OpCopyObject %6 %7 OpStore %10 %8 OpBranch %11 ; Unreachable block %100 = OpLabel %101 = OpCopyObject %6 %7 OpBranch %11 ; Selection header ; ; One of acceptable successors has an OpPhi that uses propagated ; instruction's id %11 = OpLabel %19 = OpCopyObject %6 %7 OpSelectionMerge %18 None OpBranchConditional %13 %14 %18 ; %16 has no acceptable successors %14 = OpLabel %20 = OpPhi %6 %19 %11 %15 = OpCopyObject %6 %7 ; dependency OpBranch %16 %16 = OpLabel %17 = OpCopyObject %6 %15 OpBranch %18 ; Can be applied %18 = OpLabel OpSelectionMerge %24 None OpBranchConditional %13 %22 %23 %22 = OpLabel %29 = OpPhi %6 %7 %18 %201 = OpCopyObject %6 %7 OpStore %10 %201 OpBranch %24 %23 = OpLabel %202 = OpCopyObject %6 %7 OpStore %10 %202 OpBranch %24 %24 = OpLabel %200 = OpPhi %6 %201 %22 %202 %23 OpStore %10 %200 OpBranch %32 ; Can't replace all uses of the propagated instruction: %30 is ; propagated into %27. %32 = OpLabel OpLoopMerge %28 %27 None OpBranchConditional %13 %26 %28 %26 = OpLabel %25 = OpCopyObject %6 %7 %30 = OpCopyObject %6 %25 OpBranchConditional %13 %27 %28 %27 = OpLabel OpBranch %32 %28 = OpLabel %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, CantCreateOpPhiTest) { 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 ; %5 doesn't belong to any construct %5 = OpLabel %15 = OpCopyObject %6 %7 OpBranch %16 ; The merge block (%19) is unreachable %16 = OpLabel %17 = OpCopyObject %6 %7 OpSelectionMerge %19 None OpBranchConditional %13 %18 %18 ; %21 doesn't dominate the merge block - %20 %18 = OpLabel OpSelectionMerge %20 None OpBranchConditional %13 %20 %21 %21 = OpLabel %22 = OpCopyObject %6 %7 OpBranch %20 ; The merge block (%24) is an acceptable successor of the propagated ; instruction's block %20 = OpLabel %23 = OpCopyObject %6 %7 OpSelectionMerge %24 None OpBranchConditional %13 %24 %30 %30 = OpLabel OpBranch %24 ; One of the predecessors of the merge block is not dominated by any ; successor of the propagated instruction's block %24 = OpLabel %26 = OpCopyObject %6 %7 OpLoopMerge %29 %25 None OpBranch %25 %25 = OpLabel OpBranchConditional %13 %24 %29 %28 = OpLabel ; unreachable predecessor of %29 OpBranch %29 %29 = OpLabel OpReturn %19 = 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); TransformationPropagateInstructionDown transformations[] = { // %5 doesn't belong to any construct. {5, 200, {{{16, 201}}}}, // The merge block (%19) is unreachable. {16, 200, {{{18, 202}}}}, // %21 doesn't dominate the merge block - %20. {21, 200, {{{20, 203}}}}, // The merge block (%24) is an acceptable successor of the propagated // instruction's block. {20, 200, {{{24, 204}, {30, 205}}}}, // One of the predecessors of the merge block is not dominated by any // successor of the propagated instruction's block. {24, 200, {{{25, 206}}}}, }; for (const auto& transformation : transformations) { ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } // No transformation has introduced an OpPhi instruction. ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200)); 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 ; %5 doesn't belong to any construct %5 = OpLabel OpBranch %16 ; The merge block (%19) is unreachable %16 = OpLabel %201 = OpCopyObject %6 %7 OpSelectionMerge %19 None OpBranchConditional %13 %18 %18 ; %21 doesn't dominate the merge block - %20 %18 = OpLabel %202 = OpCopyObject %6 %7 OpSelectionMerge %20 None OpBranchConditional %13 %20 %21 %21 = OpLabel OpBranch %20 ; The merge block (%24) is an acceptable successor of the propagated ; instruction's block %20 = OpLabel %203 = OpCopyObject %6 %7 OpSelectionMerge %24 None OpBranchConditional %13 %24 %30 %30 = OpLabel %205 = OpCopyObject %6 %7 OpBranch %24 ; One of the predecessors of the merge block is not dominated by any ; successor of the propagated instruction's block %24 = OpLabel %204 = OpCopyObject %6 %7 OpLoopMerge %29 %25 None OpBranch %25 %25 = OpLabel %206 = OpCopyObject %6 %7 OpBranchConditional %13 %24 %29 %28 = OpLabel ; unreachable predecessor of %29 OpBranch %29 %29 = OpLabel OpReturn %19 = OpLabel OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, VariablePointersCapability) { 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %10 = OpTypePointer Workgroup %6 %11 = OpVariable %10 Workgroup %4 = OpFunction %2 None %3 %5 = OpLabel %18 = OpCopyObject %10 %11 %14 = OpCopyObject %10 %11 OpSelectionMerge %17 None OpBranchConditional %13 %15 %16 %15 = OpLabel OpBranch %17 %16 = OpLabel OpBranch %17 %17 = OpLabel OpStore %18 %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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); { // Can propagate a pointer only if we don't have to create an OpPhi. TransformationPropagateInstructionDown transformation( 5, 200, {{{15, 201}, {16, 202}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200)); } { // Can't propagate a pointer if there is no VariablePointersStorageBuffer // capability and we need to create an OpPhi. TransformationPropagateInstructionDown transformation( 5, 200, {{{15, 203}, {16, 204}}}); ASSERT_FALSE(context->get_feature_mgr()->HasCapability( SpvCapabilityVariablePointersStorageBuffer)); ASSERT_FALSE( transformation.IsApplicable(context.get(), transformation_context)); context->AddCapability(SpvCapabilityVariablePointers); ASSERT_TRUE(context->get_feature_mgr()->HasCapability( SpvCapabilityVariablePointersStorageBuffer)); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } std::string after_transformation = R"( OpCapability Shader OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %10 = OpTypePointer Workgroup %6 %11 = OpVariable %10 Workgroup %4 = OpFunction %2 None %3 %5 = OpLabel OpSelectionMerge %17 None OpBranchConditional %13 %15 %16 %15 = OpLabel %203 = OpCopyObject %10 %11 %201 = OpCopyObject %10 %11 OpBranch %17 %16 = OpLabel %204 = OpCopyObject %10 %11 %202 = OpCopyObject %10 %11 OpBranch %17 %17 = OpLabel %200 = OpPhi %10 %203 %15 %204 %16 OpStore %200 %7 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, UseOverflowIdsTest) { 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %10 = OpTypePointer Private %6 %11 = OpVariable %10 Private %4 = OpFunction %2 None %3 %5 = OpLabel %20 = OpCopyObject %6 %7 OpSelectionMerge %23 None OpBranchConditional %13 %21 %22 %21 = OpLabel OpStore %11 %20 OpBranch %23 %22 = OpLabel OpStore %11 %20 OpBranch %23 %23 = OpLabel OpStore %11 %20 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options, MakeUnique(300)); TransformationPropagateInstructionDown transformation(5, 200, {{{21, 201}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context, {300}); ASSERT_TRUE(IsValid(env, context.get())); 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %10 = OpTypePointer Private %6 %11 = OpVariable %10 Private %4 = OpFunction %2 None %3 %5 = OpLabel OpSelectionMerge %23 None OpBranchConditional %13 %21 %22 %21 = OpLabel %201 = OpCopyObject %6 %7 OpStore %11 %201 OpBranch %23 %22 = OpLabel %300 = OpCopyObject %6 %7 OpStore %11 %300 OpBranch %23 %23 = OpLabel %200 = OpPhi %6 %201 %21 %300 %22 OpStore %11 %200 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, TestCreatedFacts) { std::string shader = R"( OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %10 = OpTypePointer Private %6 %11 = OpVariable %10 Private %4 = OpFunction %2 None %3 %5 = OpLabel %20 = OpCopyObject %6 %7 %24 = OpCopyObject %6 %7 ; Irrelevant id %25 = OpCopyObject %10 %11 ; Pointee is irrelevant OpSelectionMerge %23 None OpBranchConditional %13 %21 %22 %21 = OpLabel OpStore %25 %20 OpBranch %23 %22 = OpLabel ; Dead block OpStore %25 %20 OpBranch %23 %23 = OpLabel OpStore %25 %20 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); transformation_context.GetFactManager()->AddFactBlockIsDead(22); transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24); transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( 25); { // Propagate pointer with PointeeIsIrrelevant fact. TransformationPropagateInstructionDown transformation( 5, 200, {{{21, 201}, {22, 202}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(201)); ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(202)); ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(200)); ASSERT_TRUE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201)); ASSERT_TRUE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(202)); ASSERT_TRUE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {}))); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {}))); } { // Propagate an irrelevant id. TransformationPropagateInstructionDown transformation( 5, 203, {{{21, 204}, {22, 205}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(203)); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(204)); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(205)); ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(203)); ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(204)); ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(205)); ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(204, {}), MakeDataDescriptor(205, {}))); ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(204, {}), MakeDataDescriptor(203, {}))); } { // Propagate a regular id. TransformationPropagateInstructionDown transformation( 5, 206, {{{21, 207}, {22, 208}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(206)); ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(207)); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(208)); ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(206)); ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(207)); ASSERT_FALSE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(208)); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(206, {}), MakeDataDescriptor(207, {}))); ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(206, {}), MakeDataDescriptor(208, {}))); } std::string after_transformation = R"( OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %10 = OpTypePointer Private %6 %11 = OpVariable %10 Private %4 = OpFunction %2 None %3 %5 = OpLabel OpSelectionMerge %23 None OpBranchConditional %13 %21 %22 %21 = OpLabel %207 = OpCopyObject %6 %7 %204 = OpCopyObject %6 %7 ; Irrelevant id %201 = OpCopyObject %10 %11 ; Pointee is irrelevant OpStore %201 %207 OpBranch %23 %22 = OpLabel ; Dead block %208 = OpCopyObject %6 %7 %205 = OpCopyObject %6 %7 ; Irrelevant id %202 = OpCopyObject %10 %11 ; Pointee is irrelevant OpStore %202 %208 OpBranch %23 %23 = OpLabel %206 = OpPhi %6 %207 %21 %208 %22 %203 = OpPhi %6 %204 %21 %205 %22 ; Irrelevant id %200 = OpPhi %10 %201 %21 %202 %22 ; Pointee is irrelevant OpStore %200 %206 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, TestLoops1) { 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 %5 = OpLabel OpBranch %20 %20 = OpLabel OpLoopMerge %26 %25 None OpBranch %21 %21 = OpLabel %22 = OpCopyObject %6 %7 %31 = OpCopyObject %6 %7 OpSelectionMerge %35 None OpBranchConditional %13 %23 %24 %23 = OpLabel %27 = OpCopyObject %6 %22 %32 = OpCopyObject %6 %31 OpBranch %26 %24 = OpLabel %28 = OpCopyObject %6 %22 %33 = OpCopyObject %6 %31 OpBranchConditional %13 %26 %25 %35 = OpLabel OpBranch %25 %25 = OpLabel %29 = OpCopyObject %6 %22 %34 = OpCopyObject %6 %31 OpBranch %20 %26 = OpLabel %30 = OpCopyObject %6 %22 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); { TransformationPropagateInstructionDown transformation( 21, 200, {{{23, 201}, {24, 202}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } // Can't replace usage of %22 in %26. ASSERT_FALSE( TransformationPropagateInstructionDown(21, 200, {{{23, 201}, {24, 202}}}) .IsApplicable(context.get(), transformation_context)); 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 %5 = OpLabel OpBranch %20 %20 = OpLabel OpLoopMerge %26 %25 None OpBranch %21 %21 = OpLabel %22 = OpCopyObject %6 %7 OpSelectionMerge %35 None OpBranchConditional %13 %23 %24 %23 = OpLabel %201 = OpCopyObject %6 %7 %27 = OpCopyObject %6 %22 %32 = OpCopyObject %6 %201 OpBranch %26 %24 = OpLabel %202 = OpCopyObject %6 %7 %28 = OpCopyObject %6 %22 %33 = OpCopyObject %6 %202 OpBranchConditional %13 %26 %25 %35 = OpLabel OpBranch %25 %25 = OpLabel %29 = OpCopyObject %6 %22 %34 = OpCopyObject %6 %202 OpBranch %20 %26 = OpLabel %30 = OpCopyObject %6 %22 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, TestLoops2) { 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 %5 = OpLabel OpBranch %20 %20 = OpLabel %23 = OpPhi %6 %7 %5 %24 %21 OpLoopMerge %22 %21 None OpBranch %21 %21 = OpLabel %24 = OpCopyObject %6 %23 %25 = OpCopyObject %6 %7 OpBranchConditional %13 %22 %20 %22 = 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); { // Can propagate %25 from %21 into %20. TransformationPropagateInstructionDown transformation( 21, 200, {{{20, 201}, {22, 202}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { // Can propagate %201 from %20 into %21. TransformationPropagateInstructionDown transformation(20, 200, {{{21, 203}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } // Can't propagate %24 from %21 into %20. ASSERT_FALSE( TransformationPropagateInstructionDown(21, 200, {{{20, 204}, {22, 205}}}) .IsApplicable(context.get(), transformation_context)); 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 %5 = OpLabel OpBranch %20 %20 = OpLabel %23 = OpPhi %6 %7 %5 %24 %21 OpLoopMerge %22 %21 None OpBranch %21 %21 = OpLabel %203 = OpCopyObject %6 %7 %24 = OpCopyObject %6 %23 OpBranchConditional %13 %22 %20 %22 = OpLabel %200 = OpPhi %6 %203 %21 %202 = OpCopyObject %6 %7 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationPropagateInstructionDownTest, TestLoops3) { 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 %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 %5 = OpLabel OpBranch %20 %20 = OpLabel %27 = OpPhi %6 %7 %5 %26 %20 %25 = OpCopyObject %6 %7 %26 = OpCopyObject %6 %7 OpLoopMerge %22 %20 None OpBranchConditional %13 %20 %22 %22 = 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())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); { // Propagate %25 into %20 and %22. Not that we are skipping %26 since not // all of its users are in different blocks (%27).h TransformationPropagateInstructionDown transformation( 20, 200, {{{20, 201}, {22, 202}}}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, 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 %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 1 %12 = OpTypeBool %13 = OpConstantTrue %12 %4 = OpFunction %2 None %3 %5 = OpLabel OpBranch %20 %20 = OpLabel %27 = OpPhi %6 %7 %5 %26 %20 %201 = OpCopyObject %6 %7 %26 = OpCopyObject %6 %7 OpLoopMerge %22 %20 None OpBranchConditional %13 %20 %22 %22 = OpLabel %202 = OpCopyObject %6 %7 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } } // namespace } // namespace fuzz } // namespace spvtools