SPIRV-Tools/test/fuzz/transformation_propagate_instruction_down_test.cpp
Alastair Donaldson 502e982956
spirv-fuzz: Fix to TransformationInlineFunction (#3913)
This fixes a problem where TransformationInlineFunction could lead to
distinct instructions having identical unique ids. It adds a validity
check to detect this problem in general.

Fixes #3911.
2020-10-16 22:58:09 +01:00

1139 lines
40 KiB
C++

// 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 "gtest/gtest.h"
#include "source/fuzz/counter_overflow_id_source.h"
#include "source/fuzz/fuzzer_util.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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
// 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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(context.get()), validator_options,
MakeUnique<CounterOverflowIdSource>(300));
TransformationPropagateInstructionDown transformation(5, 200, {{{21, 201}}});
ASSERT_TRUE(
transformation.IsApplicable(context.get(), transformation_context));
ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context,
{300});
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
// 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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
{
// 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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
// 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);
spvtools::ValidatorOptions validator_options;
ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
kConsoleMessageConsumer));
TransformationContext transformation_context(
MakeUnique<FactManager>(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(fuzzerutil::IsValidAndWellFormed(
context.get(), validator_options, kConsoleMessageConsumer));
}
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