| // 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_composite_insert.h" |
| |
| #include "source/fuzz/instruction_descriptor.h" |
| #include "test/fuzz/fuzz_test_util.h" |
| |
| namespace spvtools { |
| namespace fuzz { |
| namespace { |
| |
| TEST(TransformationCompositeInsertTest, NotApplicableScenarios) { |
| // This test handles cases where IsApplicable() returns false. |
| |
| 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| OpName %18 "level_1" |
| OpMemberName %18 0 "b1" |
| OpMemberName %18 1 "b2" |
| OpName %20 "l1" |
| OpName %24 "level_2" |
| OpMemberName %24 0 "c1" |
| OpMemberName %24 1 "c2" |
| OpName %26 "l2" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %12 = OpTypeStruct %6 %6 |
| %13 = OpTypePointer Function %12 |
| %18 = OpTypeStruct %12 %12 |
| %19 = OpTypePointer Function %18 |
| %24 = OpTypeStruct %18 %18 |
| %25 = OpTypePointer Function %24 |
| %30 = OpTypeBool |
| %31 = OpConstantTrue %30 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| %20 = OpVariable %19 Function |
| %26 = OpVariable %25 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| OpStore %14 %17 |
| %21 = OpLoad %12 %14 |
| %22 = OpLoad %12 %14 |
| %23 = OpCompositeConstruct %18 %21 %22 |
| OpStore %20 %23 |
| %27 = OpLoad %18 %20 |
| %28 = OpLoad %18 %20 |
| %29 = OpCompositeConstruct %24 %27 %28 |
| OpStore %26 %29 |
| OpSelectionMerge %33 None |
| OpBranchConditional %31 %32 %33 |
| %32 = OpLabel |
| OpBranch %33 |
| %33 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_4; |
| 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<FactManager>(context.get()), validator_options); |
| // Bad: |fresh_id| is not fresh. |
| auto transformation_bad_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 20, 29, 11, {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_1.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |composite_id| does not refer to a existing instruction. |
| auto transformation_bad_2 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 40, 11, {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_2.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |composite_id| does not refer to a composite value. |
| auto transformation_bad_3 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 9, 11, {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_3.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |object_id| does not refer to a defined instruction. |
| auto transformation_bad_4 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 40, {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_4.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |object_id| cannot refer to a pointer. |
| auto transformation_bad_5 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 8, {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_5.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |index| is not a correct index. |
| auto transformation_bad_6 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {2, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_6.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: Type id of the object to be inserted and the type id of the |
| // component at |index| are not the same. |
| auto transformation_bad_7 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {1, 0}); |
| ASSERT_FALSE( |
| transformation_bad_7.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |instruction_to_insert_before| does not refer to a defined |
| // instruction. |
| auto transformation_bad_8 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpIMul, 0), 50, 29, 11, {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_8.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with |
| // OpSelectionMerge above it. |
| auto transformation_bad_9 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpBranchConditional, 0), 50, 29, 11, |
| {1, 0, 0}); |
| ASSERT_FALSE( |
| transformation_bad_9.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: |composite_id| does not have a type_id. |
| auto transformation_bad_10 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 1, 11, {1, 0, 0}); |
| ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(), |
| transformation_context)); |
| } |
| |
| TEST(TransformationCompositeInsertTest, EmptyCompositeScenarios) { |
| // This test handles cases where either the composite is empty or the |
| // composite contains an empty composite. |
| |
| 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| %2 = OpTypeVoid |
| %60 = OpTypeStruct |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %61 = OpConstantComposite %60 |
| %62 = OpConstantComposite %60 |
| %12 = OpTypeStruct %6 %6 |
| %63 = OpTypeStruct %6 %60 |
| %13 = OpTypePointer Function %12 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| %64 = OpCompositeConstruct %63 %15 %61 |
| OpStore %14 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_4; |
| 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<FactManager>(context.get()), validator_options); |
| // Bad: The composite with |composite_id| cannot be empty. |
| auto transformation_bad_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 61, 62, {1}); |
| ASSERT_FALSE( |
| transformation_bad_1.IsApplicable(context.get(), transformation_context)); |
| |
| // Good: It is possible to insert into a composite an element which is an |
| // empty composite. |
| auto transformation_good_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 64, 62, {1}); |
| ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), |
| transformation_context)); |
| transformation_good_1.Apply(context.get(), &transformation_context); |
| ASSERT_TRUE(IsValid(env, context.get())); |
| |
| std::string after_transformations = 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| %2 = OpTypeVoid |
| %60 = OpTypeStruct |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %61 = OpConstantComposite %60 |
| %62 = OpConstantComposite %60 |
| %12 = OpTypeStruct %6 %6 |
| %63 = OpTypeStruct %6 %60 |
| %13 = OpTypePointer Function %12 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| %64 = OpCompositeConstruct %63 %15 %61 |
| %50 = OpCompositeInsert %63 %62 %64 1 |
| OpStore %14 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); |
| } |
| |
| TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) { |
| // This test handles cases where either |composite| is irrelevant. |
| // The transformation shouldn't create any synonyms. |
| // The member composite has a different number of elements than the parent |
| // composite. |
| |
| 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| OpName %18 "level_1" |
| OpMemberName %18 0 "b1" |
| OpMemberName %18 1 "b2" |
| OpMemberName %18 2 "b3" |
| OpName %20 "l1" |
| OpName %25 "level_2" |
| OpMemberName %25 0 "c1" |
| OpMemberName %25 1 "c2" |
| OpName %27 "l2" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %12 = OpTypeStruct %6 %6 |
| %13 = OpTypePointer Function %12 |
| %18 = OpTypeStruct %12 %12 %12 |
| %19 = OpTypePointer Function %18 |
| %25 = OpTypeStruct %18 %18 |
| %26 = OpTypePointer Function %25 |
| %31 = OpTypeBool |
| %32 = OpConstantTrue %31 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| %20 = OpVariable %19 Function |
| %27 = OpVariable %26 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| OpStore %14 %17 |
| %21 = OpLoad %12 %14 |
| %22 = OpLoad %12 %14 |
| %23 = OpLoad %12 %14 |
| %24 = OpCompositeConstruct %18 %21 %22 %23 |
| OpStore %20 %24 |
| %28 = OpLoad %18 %20 |
| %29 = OpLoad %18 %20 |
| %30 = OpCompositeConstruct %25 %28 %29 |
| OpStore %27 %30 |
| OpSelectionMerge %34 None |
| OpBranchConditional %32 %33 %34 |
| %33 = OpLabel |
| OpBranch %34 |
| %34 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_4; |
| 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<FactManager>(context.get()), validator_options); |
| // Add fact that the composite is irrelevant. |
| transformation_context.GetFactManager()->AddFactIdIsIrrelevant(30); |
| |
| auto transformation_good_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0}); |
| ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), |
| transformation_context)); |
| transformation_good_1.Apply(context.get(), &transformation_context); |
| ASSERT_TRUE(IsValid(env, context.get())); |
| |
| // No synonyms should have been added. |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); |
| } |
| TEST(TransformationCompositeInsertTest, IrrelevantObjectSomeSynonyms) { |
| // This test handles cases where |object| is irrelevant. |
| // The transformation should create some synonyms. It shouldn't create a |
| // synonym related to |object|. The member composite has a different number of |
| // elements than the parent composite. |
| |
| 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| OpName %18 "level_1" |
| OpMemberName %18 0 "b1" |
| OpMemberName %18 1 "b2" |
| OpMemberName %18 2 "b3" |
| OpName %20 "l1" |
| OpName %25 "level_2" |
| OpMemberName %25 0 "c1" |
| OpMemberName %25 1 "c2" |
| OpName %27 "l2" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %12 = OpTypeStruct %6 %6 |
| %13 = OpTypePointer Function %12 |
| %18 = OpTypeStruct %12 %12 %12 |
| %19 = OpTypePointer Function %18 |
| %25 = OpTypeStruct %18 %18 |
| %26 = OpTypePointer Function %25 |
| %31 = OpTypeBool |
| %32 = OpConstantTrue %31 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| %20 = OpVariable %19 Function |
| %27 = OpVariable %26 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| OpStore %14 %17 |
| %21 = OpLoad %12 %14 |
| %22 = OpLoad %12 %14 |
| %23 = OpLoad %12 %14 |
| %24 = OpCompositeConstruct %18 %21 %22 %23 |
| OpStore %20 %24 |
| %28 = OpLoad %18 %20 |
| %29 = OpLoad %18 %20 |
| %30 = OpCompositeConstruct %25 %28 %29 |
| OpStore %27 %30 |
| OpSelectionMerge %34 None |
| OpBranchConditional %32 %33 %34 |
| %33 = OpLabel |
| OpBranch %34 |
| %34 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_4; |
| 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<FactManager>(context.get()), validator_options); |
| // Add fact that the object is irrelevant. |
| transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11); |
| |
| auto transformation_good_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0}); |
| ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), |
| transformation_context)); |
| transformation_good_1.Apply(context.get(), &transformation_context); |
| ASSERT_TRUE(IsValid(env, context.get())); |
| |
| // These synonyms should have been added. |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); |
| // This synonym shouldn't have been added. |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); |
| } |
| |
| TEST(TransformationCompositeInsertTest, ApplicableCreatedSynonyms) { |
| // This test handles cases where neither |composite| nor |object| is |
| // irrelevant. The transformation should create synonyms. |
| // The member composite has a different number of elements than the parent |
| // composite. |
| |
| 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| OpName %18 "level_1" |
| OpMemberName %18 0 "b1" |
| OpMemberName %18 1 "b2" |
| OpMemberName %18 2 "b3" |
| OpName %20 "l1" |
| OpName %25 "level_2" |
| OpMemberName %25 0 "c1" |
| OpMemberName %25 1 "c2" |
| OpName %27 "l2" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %12 = OpTypeStruct %6 %6 |
| %13 = OpTypePointer Function %12 |
| %18 = OpTypeStruct %12 %12 %12 |
| %19 = OpTypePointer Function %18 |
| %25 = OpTypeStruct %18 %18 |
| %26 = OpTypePointer Function %25 |
| %31 = OpTypeBool |
| %32 = OpConstantTrue %31 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| %20 = OpVariable %19 Function |
| %27 = OpVariable %26 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| OpStore %14 %17 |
| %21 = OpLoad %12 %14 |
| %22 = OpLoad %12 %14 |
| %23 = OpLoad %12 %14 |
| %24 = OpCompositeConstruct %18 %21 %22 %23 |
| OpStore %20 %24 |
| %28 = OpLoad %18 %20 |
| %29 = OpLoad %18 %20 |
| %30 = OpCompositeConstruct %25 %28 %29 |
| OpStore %27 %30 |
| OpSelectionMerge %34 None |
| OpBranchConditional %32 %33 %34 |
| %33 = OpLabel |
| OpBranch %34 |
| %34 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_4; |
| 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<FactManager>(context.get()), validator_options); |
| auto transformation_good_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0}); |
| ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), |
| transformation_context)); |
| transformation_good_1.Apply(context.get(), &transformation_context); |
| ASSERT_TRUE(IsValid(env, context.get())); |
| |
| // These synonyms should have been added. |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); |
| |
| // These synonyms should not have been added. |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1}), MakeDataDescriptor(50, {1}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 0}), MakeDataDescriptor(50, {1, 0}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(30, {1, 0, 0}), MakeDataDescriptor(50, {1, 0, 0}))); |
| |
| auto transformation_good_2 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(50, SpvOpStore, 0), 51, 50, 11, {0, 1, 1}); |
| ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), |
| transformation_context)); |
| transformation_good_2.Apply(context.get(), &transformation_context); |
| ASSERT_TRUE(IsValid(env, context.get())); |
| |
| // These synonyms should have been added. |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {1}), MakeDataDescriptor(51, {1}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {0, 0}), MakeDataDescriptor(51, {0, 0}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {0, 2}), MakeDataDescriptor(51, {0, 2}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {0, 1, 0}), MakeDataDescriptor(51, {0, 1, 0}))); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(51, {0, 1, 1}), MakeDataDescriptor(11, {}))); |
| |
| // These synonyms should not have been added. |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {0}), MakeDataDescriptor(51, {0}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {0, 1}), MakeDataDescriptor(51, {0, 1}))); |
| ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( |
| MakeDataDescriptor(50, {0, 1, 1}), MakeDataDescriptor(51, {0, 1, 1}))); |
| |
| std::string after_transformations = 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b" |
| OpName %18 "level_1" |
| OpMemberName %18 0 "b1" |
| OpMemberName %18 1 "b2" |
| OpMemberName %18 2 "b3" |
| OpName %20 "l1" |
| OpName %25 "level_2" |
| OpMemberName %25 0 "c1" |
| OpMemberName %25 1 "c2" |
| OpName %27 "l2" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %12 = OpTypeStruct %6 %6 |
| %13 = OpTypePointer Function %12 |
| %18 = OpTypeStruct %12 %12 %12 |
| %19 = OpTypePointer Function %18 |
| %25 = OpTypeStruct %18 %18 |
| %26 = OpTypePointer Function %25 |
| %31 = OpTypeBool |
| %32 = OpConstantTrue %31 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| %20 = OpVariable %19 Function |
| %27 = OpVariable %26 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| OpStore %14 %17 |
| %21 = OpLoad %12 %14 |
| %22 = OpLoad %12 %14 |
| %23 = OpLoad %12 %14 |
| %24 = OpCompositeConstruct %18 %21 %22 %23 |
| OpStore %20 %24 |
| %28 = OpLoad %18 %20 |
| %29 = OpLoad %18 %20 |
| %30 = OpCompositeConstruct %25 %28 %29 |
| %50 = OpCompositeInsert %25 %11 %30 1 0 0 |
| %51 = OpCompositeInsert %25 %11 %50 0 1 1 |
| OpStore %27 %30 |
| OpSelectionMerge %34 None |
| OpBranchConditional %32 %33 %34 |
| %33 = OpLabel |
| OpBranch %34 |
| %34 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); |
| } |
| |
| TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) { |
| // This test handles cases where either the composite or the object is not |
| // available before the |instruction_to_insert_before|. |
| |
| 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 "i1" |
| OpName %10 "i2" |
| OpName %12 "base" |
| OpMemberName %12 0 "a1" |
| OpMemberName %12 1 "a2" |
| OpName %14 "b1" |
| OpName %18 "b2" |
| OpName %22 "lvl1" |
| OpMemberName %22 0 "b1" |
| OpMemberName %22 1 "b2" |
| OpName %24 "l1" |
| OpName %28 "i3" |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %6 = OpTypeInt 32 1 |
| %7 = OpTypePointer Function %6 |
| %9 = OpConstant %6 1 |
| %11 = OpConstant %6 2 |
| %12 = OpTypeStruct %6 %6 |
| %13 = OpTypePointer Function %12 |
| %22 = OpTypeStruct %12 %12 |
| %23 = OpTypePointer Function %22 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %8 = OpVariable %7 Function |
| %10 = OpVariable %7 Function |
| %14 = OpVariable %13 Function |
| %18 = OpVariable %13 Function |
| %24 = OpVariable %23 Function |
| %28 = OpVariable %7 Function |
| OpStore %8 %9 |
| OpStore %10 %11 |
| %15 = OpLoad %6 %8 |
| %16 = OpLoad %6 %10 |
| %17 = OpCompositeConstruct %12 %15 %16 |
| OpStore %14 %17 |
| %19 = OpLoad %6 %10 |
| %20 = OpLoad %6 %8 |
| %21 = OpCompositeConstruct %12 %19 %20 |
| OpStore %18 %21 |
| %25 = OpLoad %12 %14 |
| %26 = OpLoad %12 %18 |
| %27 = OpCompositeConstruct %22 %25 %26 |
| OpStore %24 %27 |
| %29 = OpLoad %6 %8 |
| %30 = OpLoad %6 %10 |
| %31 = OpIMul %6 %29 %30 |
| OpStore %28 %31 |
| %60 = OpCompositeConstruct %12 %20 %19 |
| %61 = OpCompositeConstruct %22 %26 %25 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_4; |
| 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<FactManager>(context.get()), validator_options); |
| // Bad: The object with |object_id| is not available at |
| // |instruction_to_insert_before|. |
| auto transformation_bad_1 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 27, 60, {1}); |
| ASSERT_FALSE( |
| transformation_bad_1.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: The composite with |composite_id| is not available at |
| // |instruction_to_insert_before|. |
| auto transformation_bad_2 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 61, 21, {1}); |
| ASSERT_FALSE( |
| transformation_bad_2.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: The |instruction_to_insert_before| is the composite itself and is |
| // available. |
| auto transformation_bad_3 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(61, SpvOpCompositeConstruct, 0), 50, 61, 21, |
| {1}); |
| ASSERT_FALSE( |
| transformation_bad_3.IsApplicable(context.get(), transformation_context)); |
| |
| // Bad: The |instruction_to_insert_before| is the object itself and is not |
| // available. |
| auto transformation_bad_4 = TransformationCompositeInsert( |
| MakeInstructionDescriptor(60, SpvOpCompositeConstruct, 0), 50, 27, 60, |
| {1}); |
| ASSERT_FALSE( |
| transformation_bad_4.IsApplicable(context.get(), transformation_context)); |
| } |
| } // namespace |
| } // namespace fuzz |
| } // namespace spvtools |