// 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_add_opphi_synonym.h" #include "gtest/gtest.h" #include "source/fuzz/fuzzer_util.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { namespace fuzz { namespace { protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) { protobufs::FactDataSynonym data_synonym_fact; *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {}); *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {}); protobufs::Fact result; *result.mutable_data_synonym_fact() = data_synonym_fact; return result; } // Adds synonym facts to the fact manager. void SetUpIdSynonyms(FactManager* fact_manager) { fact_manager->MaybeAddFact(MakeSynonymFact(11, 9)); fact_manager->MaybeAddFact(MakeSynonymFact(13, 9)); fact_manager->MaybeAddFact(MakeSynonymFact(14, 9)); fact_manager->MaybeAddFact(MakeSynonymFact(19, 9)); fact_manager->MaybeAddFact(MakeSynonymFact(20, 9)); fact_manager->MaybeAddFact(MakeSynonymFact(10, 21)); } TEST(TransformationAddOpPhiSynonymTest, Inapplicable) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 OpName %2 "main" %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeBool %6 = OpConstantTrue %5 %7 = OpTypeInt 32 1 %8 = OpTypeInt 32 0 %22 = OpTypePointer Function %7 %9 = OpConstant %7 1 %10 = OpConstant %7 2 %11 = OpConstant %8 1 %2 = OpFunction %3 None %4 %12 = OpLabel %23 = OpVariable %22 Function %13 = OpCopyObject %7 %9 %14 = OpCopyObject %8 %11 OpBranch %15 %15 = OpLabel OpSelectionMerge %16 None OpBranchConditional %6 %17 %18 %17 = OpLabel %19 = OpCopyObject %7 %13 %20 = OpCopyObject %8 %14 %21 = OpCopyObject %7 %10 OpBranch %16 %18 = OpLabel %24 = OpCopyObject %22 %23 OpBranch %16 %16 = OpLabel OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_5; 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); SetUpIdSynonyms(transformation_context.GetFactManager()); transformation_context.GetFactManager()->MaybeAddFact( MakeSynonymFact(23, 24)); // %13 is not a block label. ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{}}, 100) .IsApplicable(context.get(), transformation_context)); // Block %12 does not have a predecessor. ASSERT_FALSE(TransformationAddOpPhiSynonym(12, {{}}, 100) .IsApplicable(context.get(), transformation_context)); // Not all predecessors of %16 (%17 and %18) are considered in the map. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}}}, 100) .IsApplicable(context.get(), transformation_context)); // %30 does not exist in the module. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{30, 19}}}, 100) .IsApplicable(context.get(), transformation_context)); // %20 is not a block label. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{20, 19}}}, 100) .IsApplicable(context.get(), transformation_context)); // %15 is not the id of one of the predecessors of the block. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{15, 19}}}, 100) .IsApplicable(context.get(), transformation_context)); // %30 does not exist in the module. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 30}, {18, 13}}}, 100) .IsApplicable(context.get(), transformation_context)); // %19 and %10 are not synonymous. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 10}}}, 100) .IsApplicable(context.get(), transformation_context)); // %19 and %14 do not have the same type. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 14}}}, 100) .IsApplicable(context.get(), transformation_context)); // %19 is not available at the end of %18. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 19}}}, 100) .IsApplicable(context.get(), transformation_context)); // %21 is not a fresh id. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 9}}}, 21) .IsApplicable(context.get(), transformation_context)); // %23 and %24 have pointer id. ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 23}, {18, 24}}}, 100) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddOpPhiSynonymTest, Apply) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 OpName %2 "main" %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeBool %6 = OpConstantTrue %5 %7 = OpTypeInt 32 1 %8 = OpTypeInt 32 0 %9 = OpConstant %7 1 %10 = OpConstant %7 2 %11 = OpConstant %8 1 %2 = OpFunction %3 None %4 %12 = OpLabel %13 = OpCopyObject %7 %9 %14 = OpCopyObject %8 %11 OpBranch %15 %15 = OpLabel OpSelectionMerge %16 None OpBranchConditional %6 %17 %18 %17 = OpLabel %19 = OpCopyObject %7 %13 %20 = OpCopyObject %8 %14 %21 = OpCopyObject %7 %10 OpBranch %16 %18 = OpLabel OpBranch %16 %16 = OpLabel OpBranch %22 %22 = OpLabel OpLoopMerge %23 %24 None OpBranchConditional %6 %25 %23 %25 = OpLabel OpSelectionMerge %26 None OpBranchConditional %6 %27 %26 %27 = OpLabel %28 = OpCopyObject %7 %13 OpBranch %23 %26 = OpLabel OpSelectionMerge %29 None OpBranchConditional %6 %29 %24 %29 = OpLabel %30 = OpCopyObject %7 %13 OpBranch %23 %24 = OpLabel OpBranch %22 %23 = OpLabel OpSelectionMerge %31 None OpBranchConditional %6 %31 %31 %31 = OpLabel OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_5; 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); SetUpIdSynonyms(transformation_context.GetFactManager()); // Add some further synonym facts. transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(28, 9)); transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(30, 9)); auto transformation1 = TransformationAddOpPhiSynonym(17, {{{15, 13}}}, 100); ASSERT_TRUE( transformation1.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation1, context.get(), &transformation_context); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(100, {}), MakeDataDescriptor(9, {}))); auto transformation2 = TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 13}}}, 101); ASSERT_TRUE( transformation2.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation2, context.get(), &transformation_context); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(101, {}), MakeDataDescriptor(9, {}))); auto transformation3 = TransformationAddOpPhiSynonym(23, {{{22, 13}, {27, 28}, {29, 30}}}, 102); ASSERT_TRUE( transformation3.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation3, context.get(), &transformation_context); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(102, {}), MakeDataDescriptor(9, {}))); auto transformation4 = TransformationAddOpPhiSynonym(31, {{{23, 13}}}, 103); ASSERT_TRUE( transformation4.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation4, context.get(), &transformation_context); ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( MakeDataDescriptor(103, {}), MakeDataDescriptor(9, {}))); 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 %2 "main" OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 OpName %2 "main" %3 = OpTypeVoid %4 = OpTypeFunction %3 %5 = OpTypeBool %6 = OpConstantTrue %5 %7 = OpTypeInt 32 1 %8 = OpTypeInt 32 0 %9 = OpConstant %7 1 %10 = OpConstant %7 2 %11 = OpConstant %8 1 %2 = OpFunction %3 None %4 %12 = OpLabel %13 = OpCopyObject %7 %9 %14 = OpCopyObject %8 %11 OpBranch %15 %15 = OpLabel OpSelectionMerge %16 None OpBranchConditional %6 %17 %18 %17 = OpLabel %100 = OpPhi %7 %13 %15 %19 = OpCopyObject %7 %13 %20 = OpCopyObject %8 %14 %21 = OpCopyObject %7 %10 OpBranch %16 %18 = OpLabel OpBranch %16 %16 = OpLabel %101 = OpPhi %7 %19 %17 %13 %18 OpBranch %22 %22 = OpLabel OpLoopMerge %23 %24 None OpBranchConditional %6 %25 %23 %25 = OpLabel OpSelectionMerge %26 None OpBranchConditional %6 %27 %26 %27 = OpLabel %28 = OpCopyObject %7 %13 OpBranch %23 %26 = OpLabel OpSelectionMerge %29 None OpBranchConditional %6 %29 %24 %29 = OpLabel %30 = OpCopyObject %7 %13 OpBranch %23 %24 = OpLabel OpBranch %22 %23 = OpLabel %102 = OpPhi %7 %13 %22 %28 %27 %30 %29 OpSelectionMerge %31 None OpBranchConditional %6 %31 %31 %31 = OpLabel %103 = OpPhi %7 %13 %23 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); } TEST(TransformationAddOpPhiSynonymTest, VariablePointers) { std::string shader = R"( OpCapability Shader OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" %3 OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %4 = OpTypeVoid %5 = OpTypeFunction %4 %6 = OpTypeBool %7 = OpConstantTrue %6 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %10 = OpTypePointer Workgroup %8 %3 = OpVariable %10 Workgroup %2 = OpFunction %4 None %5 %11 = OpLabel %12 = OpVariable %9 Function OpSelectionMerge %13 None OpBranchConditional %7 %14 %13 %14 = OpLabel %15 = OpCopyObject %10 %3 %16 = OpCopyObject %9 %12 OpBranch %13 %13 = OpLabel OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_5; 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); // Declare synonyms transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(3, 15)); transformation_context.GetFactManager()->MaybeAddFact( MakeSynonymFact(12, 16)); // Remove the VariablePointers capability. context.get()->get_feature_mgr()->RemoveCapability( spv::Capability::VariablePointers); // The VariablePointers capability is required to add an OpPhi instruction of // pointer type. ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100) .IsApplicable(context.get(), transformation_context)); // Add the VariablePointers capability back. context.get()->get_feature_mgr()->AddCapability( spv::Capability::VariablePointers); // If the ids have pointer type, the storage class must be Workgroup or // StorageBuffer, but it is Function in this case. ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 12}, {14, 16}}}, 100) .IsApplicable(context.get(), transformation_context)); auto transformation = TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); std::string after_transformation = R"( OpCapability Shader OpCapability VariablePointers %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %2 "main" %3 OpExecutionMode %2 OriginUpperLeft OpSource ESSL 310 %4 = OpTypeVoid %5 = OpTypeFunction %4 %6 = OpTypeBool %7 = OpConstantTrue %6 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %10 = OpTypePointer Workgroup %8 %3 = OpVariable %10 Workgroup %2 = OpFunction %4 None %5 %11 = OpLabel %12 = OpVariable %9 Function OpSelectionMerge %13 None OpBranchConditional %7 %14 %13 %14 = OpLabel %15 = OpCopyObject %10 %3 %16 = OpCopyObject %9 %12 OpBranch %13 %13 = OpLabel %100 = OpPhi %10 %3 %11 %15 %14 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } TEST(TransformationAddOpPhiSynonymTest, DeadBlock) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 320 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %9 = OpConstant %6 2 %10 = OpTypeBool %11 = OpConstantFalse %10 %15 = OpConstant %6 0 %50 = OpConstant %6 0 %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function OpStore %8 %9 OpSelectionMerge %13 None OpBranchConditional %11 %12 %13 %12 = OpLabel %14 = OpLoad %6 %8 %16 = OpIEqual %10 %14 %15 OpSelectionMerge %18 None OpBranchConditional %16 %17 %40 %17 = OpLabel OpBranch %18 %40 = OpLabel OpBranch %18 %18 = OpLabel OpBranch %13 %13 = OpLabel OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_5; 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); // Dead blocks transformation_context.GetFactManager()->AddFactBlockIsDead(12); transformation_context.GetFactManager()->AddFactBlockIsDead(17); transformation_context.GetFactManager()->AddFactBlockIsDead(18); // Declare synonym ASSERT_TRUE(transformation_context.GetFactManager()->MaybeAddFact( MakeSynonymFact(15, 50))); // Bad because the block 18 is dead. ASSERT_FALSE(TransformationAddOpPhiSynonym(18, {{{17, 15}, {40, 50}}}, 100) .IsApplicable(context.get(), transformation_context)); } } // namespace } // namespace fuzz } // namespace spvtools