SPIRV-Tools/test/fuzz/transformation_composite_insert_test.cpp
Stefano Milizia 1e1c308ded
spirv-fuzz: Pass submanagers to other submanagers when necessary (#3796)
This PR changes the fact manager so that, when calling some of the
functions in submanagers, passes references to other submanagers if
necessary (e.g. to make consistency checks).

In particular:

- DataSynonymAndIdEquationFacts is passed to the AddFactIdIsIrrelevant
  function of IrrelevantValueFacts

- IrrelevantValueFacts is passed to the AddFact functions of
  DataSynonymAndIdEquationFacts

The IRContext is also passed when necessary and the calls to the
corresponding functions in FactManager were updated to be valid and
always use an updated context.

Fixes #3550.
2020-09-15 13:27:14 +01:00

815 lines
32 KiB
C++

// 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()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
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()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
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()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Add fact that the composite is irrelevant.
fact_manager.AddFactIdIsIrrelevant(30, context.get());
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(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
MakeDataDescriptor(50, {0})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 1}),
MakeDataDescriptor(50, {1, 1})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 2}),
MakeDataDescriptor(50, {1, 2})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 1}),
MakeDataDescriptor(50, {1, 0, 1})));
ASSERT_FALSE(fact_manager.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()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
// Add fact that the object is irrelevant.
fact_manager.AddFactIdIsIrrelevant(11, context.get());
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(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
MakeDataDescriptor(50, {0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 1}),
MakeDataDescriptor(50, {1, 1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 2}),
MakeDataDescriptor(50, {1, 2})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 1}),
MakeDataDescriptor(50, {1, 0, 1})));
// This synonym shouldn't have been added.
ASSERT_FALSE(fact_manager.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()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
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(fact_manager.IsSynonymous(MakeDataDescriptor(30, {0}),
MakeDataDescriptor(50, {0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 1}),
MakeDataDescriptor(50, {1, 1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 2}),
MakeDataDescriptor(50, {1, 2})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0, 1}),
MakeDataDescriptor(50, {1, 0, 1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {1, 0, 0}),
MakeDataDescriptor(11, {})));
// These synonyms should not have been added.
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1}),
MakeDataDescriptor(50, {1})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {1, 0}),
MakeDataDescriptor(50, {1, 0})));
ASSERT_FALSE(fact_manager.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(fact_manager.IsSynonymous(MakeDataDescriptor(50, {1}),
MakeDataDescriptor(51, {1})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 0}),
MakeDataDescriptor(51, {0, 0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 2}),
MakeDataDescriptor(51, {0, 2})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 1, 0}),
MakeDataDescriptor(51, {0, 1, 0})));
ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(51, {0, 1, 1}),
MakeDataDescriptor(11, {})));
// These synonyms should not have been added.
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0}),
MakeDataDescriptor(51, {0})));
ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(50, {0, 1}),
MakeDataDescriptor(51, {0, 1})));
ASSERT_FALSE(fact_manager.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()));
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
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