diff options
Diffstat (limited to 'test/fuzz/transformation_replace_id_with_synonym_test.cpp')
-rw-r--r-- | test/fuzz/transformation_replace_id_with_synonym_test.cpp | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp new file mode 100644 index 00000000..d3a23449 --- /dev/null +++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -0,0 +1,569 @@ +// Copyright (c) 2019 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_replace_id_with_synonym.h" +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/id_use_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// The following shader was obtained from this GLSL, which was then optimized +// with spirv-opt -O and manually edited to include some uses of OpCopyObject +// (to introduce id synonyms). +// +// #version 310 es +// +// precision highp int; +// precision highp float; +// +// layout(set = 0, binding = 0) uniform buf { +// int a; +// int b; +// int c; +// }; +// +// layout(location = 0) out vec4 color; +// +// void main() { +// int x = a; +// float f = 0.0; +// while (x < b) { +// switch(x % 4) { +// case 0: +// color[0] = f; +// break; +// case 1: +// color[1] = f; +// break; +// case 2: +// color[2] = f; +// break; +// case 3: +// color[3] = f; +// break; +// default: +// break; +// } +// if (x > c) { +// x++; +// } else { +// x += 2; +// } +// } +// color[0] += color[1] + float(x); +// } +const std::string kComplexShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %42 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "buf" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpMemberName %9 2 "c" + OpName %11 "" + OpName %42 "color" + OpMemberDecorate %9 0 Offset 0 + OpMemberDecorate %9 1 Offset 4 + OpMemberDecorate %9 2 Offset 8 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + OpDecorate %42 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 %6 %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypePointer Uniform %6 + %16 = OpTypeFloat 32 + %19 = OpConstant %16 0 + %26 = OpConstant %6 1 + %29 = OpTypeBool + %32 = OpConstant %6 4 + %40 = OpTypeVector %16 4 + %41 = OpTypePointer Output %40 + %42 = OpVariable %41 Output + %44 = OpTypeInt 32 0 + %45 = OpConstant %44 0 + %46 = OpTypePointer Output %16 + %50 = OpConstant %44 1 + %54 = OpConstant %44 2 + %58 = OpConstant %44 3 + %64 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %209 = OpCopyObject %6 %12 + %14 = OpAccessChain %13 %11 %12 + %15 = OpLoad %6 %14 + %200 = OpCopyObject %6 %15 + OpBranch %20 + %20 = OpLabel + %84 = OpPhi %6 %15 %5 %86 %69 + %27 = OpAccessChain %13 %11 %26 + %28 = OpLoad %6 %27 + %207 = OpCopyObject %6 %84 + %201 = OpCopyObject %6 %15 + %30 = OpSLessThan %29 %84 %28 + OpLoopMerge %22 %69 None + OpBranchConditional %30 %21 %22 + %21 = OpLabel + %33 = OpSMod %6 %84 %32 + %208 = OpCopyObject %6 %33 + OpSelectionMerge %39 None + OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37 + %38 = OpLabel + %202 = OpCopyObject %6 %15 + OpBranch %39 + %34 = OpLabel + %210 = OpCopyObject %16 %19 + %47 = OpAccessChain %46 %42 %45 + OpStore %47 %19 + OpBranch %39 + %35 = OpLabel + %51 = OpAccessChain %46 %42 %50 + OpStore %51 %19 + OpBranch %39 + %36 = OpLabel + %204 = OpCopyObject %44 %54 + %55 = OpAccessChain %46 %42 %54 + %203 = OpCopyObject %46 %55 + OpStore %55 %19 + OpBranch %39 + %37 = OpLabel + %59 = OpAccessChain %46 %42 %58 + OpStore %59 %19 + OpBranch %39 + %39 = OpLabel + %300 = OpIAdd %6 %15 %15 + %65 = OpAccessChain %13 %11 %64 + %66 = OpLoad %6 %65 + %67 = OpSGreaterThan %29 %84 %66 + OpSelectionMerge %69 None + OpBranchConditional %67 %68 %72 + %68 = OpLabel + %71 = OpIAdd %6 %84 %26 + OpBranch %69 + %72 = OpLabel + %74 = OpIAdd %6 %84 %64 + %205 = OpCopyObject %6 %74 + OpBranch %69 + %69 = OpLabel + %86 = OpPhi %6 %71 %68 %74 %72 + %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %20 + %22 = OpLabel + %75 = OpAccessChain %46 %42 %50 + %76 = OpLoad %16 %75 + %78 = OpConvertSToF %16 %84 + %80 = OpAccessChain %46 %42 %45 + %206 = OpCopyObject %16 %78 + %81 = OpLoad %16 %80 + %79 = OpFAdd %16 %76 %78 + %82 = OpFAdd %16 %81 %79 + OpStore %80 %82 + OpReturn + OpFunctionEnd +)"; + +protobufs::Fact MakeFact(uint32_t id, uint32_t copy_id) { + protobufs::FactIdSynonym id_synonym_fact; + id_synonym_fact.set_id(id); + id_synonym_fact.mutable_data_descriptor()->set_object(copy_id); + protobufs::Fact result; + *result.mutable_id_synonym_fact() = id_synonym_fact; + return result; +} + +// Equips the fact manager with synonym facts for the above shader. +void SetUpIdSynonyms(FactManager* fact_manager, opt::IRContext* context) { + fact_manager->AddFact(MakeFact(15, 200), context); + fact_manager->AddFact(MakeFact(15, 201), context); + fact_manager->AddFact(MakeFact(15, 202), context); + fact_manager->AddFact(MakeFact(55, 203), context); + fact_manager->AddFact(MakeFact(54, 204), context); + fact_manager->AddFact(MakeFact(74, 205), context); + fact_manager->AddFact(MakeFact(78, 206), context); + fact_manager->AddFact(MakeFact(84, 207), context); + fact_manager->AddFact(MakeFact(33, 208), context); + fact_manager->AddFact(MakeFact(12, 209), context); + fact_manager->AddFact(MakeFact(19, 210), context); +} + +TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + SetUpIdSynonyms(&fact_manager, context.get()); + + // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not + // dominate %300. + auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 0, 300, 0), + MakeDataDescriptor(202, {}), 0); + ASSERT_FALSE( + synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager)); + + // %202 cannot replace %15 as in-operand 2 of %301, since this is the OpPhi's + // incoming value for block %72, and %202 does not dominate %72. + auto synonym_does_not_dominate_use_op_phi = + TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(15, SpvOpPhi, 2, 301, 0), + MakeDataDescriptor(202, {}), 0); + ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(), + fact_manager)); + + // %200 is not a synonym for %84 + auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(84, SpvOpSGreaterThan, 0, 67, 0), + MakeDataDescriptor(200, {}), 0); + ASSERT_FALSE( + id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager)); + + // %86 is not a synonym for anything (and in particular not for %74) + auto id_has_no_synonyms = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(86, SpvOpPhi, 2, 84, 0), + MakeDataDescriptor(74, {}), 0); + ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager)); + + // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed + auto synonym_use_is_in_synonym_definition = + TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 207, 0), + MakeDataDescriptor(207, {}), 0); + ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(), + fact_manager)); + + // The id use descriptor does not lead to a use (%84 is not used in the + // definition of %207) + auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 200, 0), + MakeDataDescriptor(207, {}), 0); + ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager)); + + // This replacement would lead to an access chain into a struct using a + // non-constant index. + auto bad_access_chain = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(12, SpvOpAccessChain, 1, 14, 0), + MakeDataDescriptor(209, {}), 0); + ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager)); +} + +TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + SetUpIdSynonyms(&fact_manager, context.get()); + + auto global_constant_synonym = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(19, SpvOpStore, 1, 47, 0), + MakeDataDescriptor(210, {}), 0); + ASSERT_TRUE( + global_constant_synonym.IsApplicable(context.get(), fact_manager)); + global_constant_synonym.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(54, SpvOpAccessChain, 1, 55, 0), + MakeDataDescriptor(204, {}), 0); + ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(), + fact_manager)); + replace_vector_access_chain_index.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // This is an interesting case because it replaces something that is being + // copied with something that is already a synonym. + auto regular_replacement = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(15, SpvOpCopyObject, 0, 202, 0), + MakeDataDescriptor(201, {}), 0); + ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager)); + regular_replacement.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto regular_replacement2 = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(55, SpvOpStore, 0, 203, 0), + MakeDataDescriptor(203, {}), 0); + ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager)); + regular_replacement2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + auto good_op_phi = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(74, SpvOpPhi, 2, 86, 0), + MakeDataDescriptor(205, {}), 0); + ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager)); + good_op_phi.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %42 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "buf" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpMemberName %9 2 "c" + OpName %11 "" + OpName %42 "color" + OpMemberDecorate %9 0 Offset 0 + OpMemberDecorate %9 1 Offset 4 + OpMemberDecorate %9 2 Offset 8 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + OpDecorate %42 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 %6 %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypePointer Uniform %6 + %16 = OpTypeFloat 32 + %19 = OpConstant %16 0 + %26 = OpConstant %6 1 + %29 = OpTypeBool + %32 = OpConstant %6 4 + %40 = OpTypeVector %16 4 + %41 = OpTypePointer Output %40 + %42 = OpVariable %41 Output + %44 = OpTypeInt 32 0 + %45 = OpConstant %44 0 + %46 = OpTypePointer Output %16 + %50 = OpConstant %44 1 + %54 = OpConstant %44 2 + %58 = OpConstant %44 3 + %64 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %209 = OpCopyObject %6 %12 + %14 = OpAccessChain %13 %11 %12 + %15 = OpLoad %6 %14 + %200 = OpCopyObject %6 %15 + OpBranch %20 + %20 = OpLabel + %84 = OpPhi %6 %15 %5 %86 %69 + %27 = OpAccessChain %13 %11 %26 + %28 = OpLoad %6 %27 + %207 = OpCopyObject %6 %84 + %201 = OpCopyObject %6 %15 + %30 = OpSLessThan %29 %84 %28 + OpLoopMerge %22 %69 None + OpBranchConditional %30 %21 %22 + %21 = OpLabel + %33 = OpSMod %6 %84 %32 + %208 = OpCopyObject %6 %33 + OpSelectionMerge %39 None + OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37 + %38 = OpLabel + %202 = OpCopyObject %6 %201 + OpBranch %39 + %34 = OpLabel + %210 = OpCopyObject %16 %19 + %47 = OpAccessChain %46 %42 %45 + OpStore %47 %210 + OpBranch %39 + %35 = OpLabel + %51 = OpAccessChain %46 %42 %50 + OpStore %51 %19 + OpBranch %39 + %36 = OpLabel + %204 = OpCopyObject %44 %54 + %55 = OpAccessChain %46 %42 %204 + %203 = OpCopyObject %46 %55 + OpStore %203 %19 + OpBranch %39 + %37 = OpLabel + %59 = OpAccessChain %46 %42 %58 + OpStore %59 %19 + OpBranch %39 + %39 = OpLabel + %300 = OpIAdd %6 %15 %15 + %65 = OpAccessChain %13 %11 %64 + %66 = OpLoad %6 %65 + %67 = OpSGreaterThan %29 %84 %66 + OpSelectionMerge %69 None + OpBranchConditional %67 %68 %72 + %68 = OpLabel + %71 = OpIAdd %6 %84 %26 + OpBranch %69 + %72 = OpLabel + %74 = OpIAdd %6 %84 %64 + %205 = OpCopyObject %6 %74 + OpBranch %69 + %69 = OpLabel + %86 = OpPhi %6 %71 %68 %205 %72 + %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %20 + %22 = OpLabel + %75 = OpAccessChain %46 %42 %50 + %76 = OpLoad %16 %75 + %78 = OpConvertSToF %16 %84 + %80 = OpAccessChain %46 %42 %45 + %206 = OpCopyObject %16 %78 + %81 = OpLoad %16 %80 + %79 = OpFAdd %16 %76 %78 + %82 = OpFAdd %16 %81 %79 + OpStore %80 %82 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { + // The following SPIR-V comes from this GLSL, with object copies added: + // + // #version 310 es + // + // precision highp int; + // + // int g; + // + // void main() { + // int l; + // l = g; + // g = l; + // } + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "l" + OpName %10 "g" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypePointer Private %6 + %10 = OpVariable %9 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpCopyObject %9 %10 + %101 = OpCopyObject %7 %8 + %11 = OpLoad %6 %10 + OpStore %8 %11 + %12 = OpLoad %6 %8 + OpStore %10 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + + fact_manager.AddFact(MakeFact(10, 100), context.get()); + fact_manager.AddFact(MakeFact(8, 101), context.get()); + + // Replace %10 with %100 in: + // %11 = OpLoad %6 %10 + auto replacement1 = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(10, SpvOpLoad, 0, 11, 0), + MakeDataDescriptor(100, {}), 0); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager)); + replacement1.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %8 with %101 in: + // OpStore %8 %11 + auto replacement2 = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(8, SpvOpStore, 0, 11, 0), + MakeDataDescriptor(101, {}), 0); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager)); + replacement2.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %8 with %101 in: + // %12 = OpLoad %6 %8 + auto replacement3 = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(8, SpvOpLoad, 0, 12, 0), + MakeDataDescriptor(101, {}), 0); + ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager)); + replacement3.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + // Replace %10 with %100 in: + // OpStore %10 %12 + auto replacement4 = TransformationReplaceIdWithSynonym( + transformation::MakeIdUseDescriptor(10, SpvOpStore, 0, 12, 0), + MakeDataDescriptor(100, {}), 0); + ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager)); + replacement4.Apply(context.get(), &fact_manager); + ASSERT_TRUE(IsValid(env, context.get())); + + const 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 + OpName %4 "main" + OpName %8 "l" + OpName %10 "g" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypePointer Private %6 + %10 = OpVariable %9 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpCopyObject %9 %10 + %101 = OpCopyObject %7 %8 + %11 = OpLoad %6 %100 + OpStore %101 %11 + %12 = OpLoad %6 %101 + OpStore %100 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools |