spirv-fuzz: Replace id in OpPhi coming from a dead predecessor (#3744)

This transformation takes the id of an OpPhi instruction, of a dead
predecessor of the block containing it and a replacement id of
available to use and of the same type as the OpPhi, and changes
the id in the OpPhi corresponding to the given predecessor.

For example, %id = OpPhi %type %v1 %p1 %v2 %p2
becomes %id = OpPhi %type %v3 %p1 %v2 %p2
if the transformation is given %id, %p1 and %v3, %p1 is a dead block,
%v3 is type type and it is available to use at the end of %p1.

The fuzzer pass randomly decides to apply the transformation to OpPhi
instructions for which at least one of the predecessors is dead

Fixes #3726.
This commit is contained in:
Stefano Milizia 2020-09-02 02:06:38 +02:00 committed by GitHub
parent 3daabd3212
commit 788468408e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 570 additions and 0 deletions

View File

@ -94,6 +94,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_replace_irrelevant_ids.h
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_replace_loads_stores_with_copy_memories.h
fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
fuzzer_pass_replace_parameter_with_global.h
fuzzer_pass_replace_params_with_struct.h
fuzzer_pass_split_blocks.h
@ -172,6 +173,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_irrelevant_id.h
transformation_replace_linear_algebra_instruction.h
transformation_replace_load_store_with_copy_memory.h
transformation_replace_opphi_id_from_dead_predecessor.h
transformation_replace_parameter_with_global.h
transformation_replace_params_with_struct.h
transformation_set_function_control.h
@ -251,6 +253,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_replace_irrelevant_ids.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_replace_loads_stores_with_copy_memories.cpp
fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
fuzzer_pass_replace_parameter_with_global.cpp
fuzzer_pass_replace_params_with_struct.cpp
fuzzer_pass_split_blocks.cpp
@ -328,6 +331,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_irrelevant_id.cpp
transformation_replace_linear_algebra_instruction.cpp
transformation_replace_load_store_with_copy_memory.cpp
transformation_replace_opphi_id_from_dead_predecessor.cpp
transformation_replace_parameter_with_global.cpp
transformation_replace_params_with_struct.cpp
transformation_set_function_control.cpp

View File

@ -70,6 +70,7 @@
#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h"
#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h"
#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h"
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
@ -379,6 +380,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassPermutePhiOperands>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassReplaceIrrelevantIds>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);

View File

@ -103,6 +103,8 @@ const std::pair<uint32_t, uint32_t>
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
{20, 90};
const std::pair<uint32_t, uint32_t>
kChanceOfReplacingOpPhiIdFromDeadPredecessor = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
@ -267,6 +269,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
chance_of_replacing_load_store_with_copy_memory_ =
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
chance_of_replacing_opphi_id_from_dead_predecessor_ =
ChooseBetweenMinAndMax(kChanceOfReplacingOpPhiIdFromDeadPredecessor);
chance_of_replacing_parameters_with_globals_ =
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
chance_of_replacing_parameters_with_struct_ =

View File

@ -261,6 +261,9 @@ class FuzzerContext {
uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
return chance_of_replacing_load_store_with_copy_memory_;
}
uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() {
return chance_of_replacing_opphi_id_from_dead_predecessor_;
}
uint32_t GetChanceOfReplacingParametersWithGlobals() {
return chance_of_replacing_parameters_with_globals_;
}
@ -417,6 +420,7 @@ class FuzzerContext {
uint32_t chance_of_replacing_irrelevant_id_;
uint32_t chance_of_replacing_linear_algebra_instructions_;
uint32_t chance_of_replacing_load_store_with_copy_memory_;
uint32_t chance_of_replacing_opphi_id_from_dead_predecessor_;
uint32_t chance_of_replacing_parameters_with_globals_;
uint32_t chance_of_replacing_parameters_with_struct_;
uint32_t chance_of_splitting_block_;

View File

@ -0,0 +1,117 @@
// 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/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h"
#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h"
namespace spvtools {
namespace fuzz {
FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::
FuzzerPassReplaceOpPhiIdsFromDeadPredecessors(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}
FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::
~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors() = default;
void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() {
// Keep a vector of the transformations to apply.
std::vector<TransformationReplaceOpPhiIdFromDeadPredecessor> transformations;
// Loop through the blocks in the module.
for (auto& function : *GetIRContext()->module()) {
for (auto& block : function) {
// Only consider dead blocks.
if (!GetTransformationContext()->GetFactManager()->BlockIsDead(
block.id())) {
continue;
}
// Find all the uses of the label id of the block inside OpPhi
// instructions.
GetIRContext()->get_def_use_mgr()->ForEachUse(
block.id(), [this, &function, &block, &transformations](
opt::Instruction* instruction, uint32_t) {
// Only consider OpPhi instructions.
if (instruction->opcode() != SpvOpPhi) {
return;
}
// Randomly decide whether to consider this use.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()
->GetChanceOfReplacingOpPhiIdFromDeadPredecessor())) {
return;
}
// Get the current id corresponding to the predecessor.
uint32_t current_id = 0;
for (uint32_t i = 1; i < instruction->NumInOperands(); i += 2) {
if (instruction->GetSingleWordInOperand(i) == block.id()) {
// The corresponding id is at the index of the block - 1.
current_id = instruction->GetSingleWordInOperand(i - 1);
break;
}
}
assert(current_id != 0 &&
"The predecessor - and corresponding id - should always be "
"found.");
uint32_t type_id = instruction->type_id();
// Find all the suitable instructions to replace the id.
const auto& candidates = FindAvailableInstructions(
&function, &block, block.end(),
[type_id, current_id](opt::IRContext* /* unused */,
opt::Instruction* candidate) -> bool {
// Only consider instructions with a result id different from
// the currently-used one, and with the right type.
return candidate->HasResultId() &&
candidate->type_id() == type_id &&
candidate->result_id() != current_id;
});
// If there is no possible replacement, we cannot apply any
// transformation.
if (candidates.empty()) {
return;
}
// Choose one of the candidates.
uint32_t replacement_id =
candidates[GetFuzzerContext()->RandomIndex(candidates)]
->result_id();
// Add a new transformation to the list of transformations to apply.
transformations.emplace_back(
TransformationReplaceOpPhiIdFromDeadPredecessor(
instruction->result_id(), block.id(), replacement_id));
});
}
}
// Apply all the transformations.
for (const auto& transformation : transformations) {
ApplyTransformation(transformation);
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,39 @@
// 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.
#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_
#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_
#include "source/fuzz/fuzzer_pass.h"
namespace spvtools {
namespace fuzz {
// Replaces id operands in OpPhi instructions with other available ids of the
// right type, where the corresponding predecessor is dead.
class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass {
public:
FuzzerPassReplaceOpPhiIdsFromDeadPredecessors(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);
~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors();
void Apply() override;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_

View File

@ -417,6 +417,7 @@ message Transformation {
TransformationAddOpPhiSynonym add_opphi_synonym = 70;
TransformationMutatePointer mutate_pointer = 71;
TransformationReplaceIrrelevantId replace_irrelevant_id = 72;
TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73;
// Add additional option using the next available number.
}
}
@ -1543,6 +1544,26 @@ message TransformationReplaceLoadStoreWithCopyMemory {
InstructionDescriptor store_instruction_descriptor = 2;
}
message TransformationReplaceOpPhiIdFromDeadPredecessor {
// Replaces one of the ids used by an OpPhi instruction, when
// the corresponding predecessor is dead, with any available id
// of the correct type.
// The result id of the OpPhi instruction.
uint32 opphi_id = 1;
// The label id of one of the predecessors of the block containing
// the OpPhi instruction, corresponding to the id that we want to
// replace.
uint32 pred_label_id = 2;
// The id that, after the transformation, will be associated with
// the given predecessor.
uint32 replacement_id = 3;
}
message TransformationReplaceParamsWithStruct {
// Replaces parameters of the function with a struct containing

View File

@ -77,6 +77,7 @@
#include "source/fuzz/transformation_replace_irrelevant_id.h"
#include "source/fuzz/transformation_replace_linear_algebra_instruction.h"
#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h"
#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h"
#include "source/fuzz/transformation_replace_parameter_with_global.h"
#include "source/fuzz/transformation_replace_params_with_struct.h"
#include "source/fuzz/transformation_set_function_control.h"
@ -283,6 +284,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kReplaceParamsWithStruct:
return MakeUnique<TransformationReplaceParamsWithStruct>(
message.replace_params_with_struct());
case protobufs::Transformation::TransformationCase::
kReplaceOpphiIdFromDeadPredecessor:
return MakeUnique<TransformationReplaceOpPhiIdFromDeadPredecessor>(
message.replace_opphi_id_from_dead_predecessor());
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
return MakeUnique<TransformationSetFunctionControl>(
message.set_function_control());

View File

@ -0,0 +1,110 @@
// 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_replace_opphi_id_from_dead_predecessor.h"
#include "source/fuzz/fuzzer_util.h"
namespace spvtools {
namespace fuzz {
TransformationReplaceOpPhiIdFromDeadPredecessor::
TransformationReplaceOpPhiIdFromDeadPredecessor(
const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor&
message)
: message_(message) {}
TransformationReplaceOpPhiIdFromDeadPredecessor::
TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id,
uint32_t pred_label_id,
uint32_t replacement_id) {
message_.set_opphi_id(opphi_id);
message_.set_pred_label_id(pred_label_id);
message_.set_replacement_id(replacement_id);
}
bool TransformationReplaceOpPhiIdFromDeadPredecessor::IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const {
// |opphi_id| must be the id of an OpPhi instruction.
auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id());
if (!opphi_def || opphi_def->opcode() != SpvOpPhi) {
return false;
}
// |pred_label_id| must be the label id of a dead block.
auto pred_block = ir_context->get_instr_block(message_.pred_label_id());
if (!pred_block || pred_block->id() != message_.pred_label_id() ||
!transformation_context.GetFactManager()->BlockIsDead(pred_block->id())) {
return false;
}
// |pred_label_id| must be one of the predecessors of the block containing the
// OpPhi instruction.
bool found = false;
for (auto pred :
ir_context->cfg()->preds(ir_context->get_instr_block(opphi_def)->id())) {
if (pred == message_.pred_label_id()) {
found = true;
break;
}
}
if (!found) {
return false;
}
// |replacement_id| must have the same type id as the OpPhi instruction.
auto replacement_def =
ir_context->get_def_use_mgr()->GetDef(message_.replacement_id());
if (!replacement_def || replacement_def->type_id() != opphi_def->type_id()) {
return false;
}
// The replacement id must be available at the end of the predecessor.
return fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, pred_block->terminator(), replacement_def->result_id());
}
void TransformationReplaceOpPhiIdFromDeadPredecessor::Apply(
opt::IRContext* ir_context,
TransformationContext* /* transformation_context */) const {
// Get the OpPhi instruction.
auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id());
// Find the index corresponding to the operand being replaced and replace it,
// by looping through the odd-indexed input operands and finding
// |pred_label_id|. The index that we are interested in is the one before
// that.
for (uint32_t i = 1; i < opphi_def->NumInOperands(); i += 2) {
if (opphi_def->GetSingleWordInOperand(i) == message_.pred_label_id()) {
// The operand to be replaced is at index i-1.
opphi_def->SetInOperand(i - 1, {message_.replacement_id()});
}
}
// Invalidate the analyses because we have altered the usages of ids.
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
}
protobufs::Transformation
TransformationReplaceOpPhiIdFromDeadPredecessor::ToMessage() const {
protobufs::Transformation result;
*result.mutable_replace_opphi_id_from_dead_predecessor() = message_;
return result;
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,56 @@
// 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.
#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_
#include "source/fuzz/transformation.h"
namespace spvtools {
namespace fuzz {
class TransformationReplaceOpPhiIdFromDeadPredecessor : public Transformation {
public:
explicit TransformationReplaceOpPhiIdFromDeadPredecessor(
const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor&
message);
TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id,
uint32_t pred_label_id,
uint32_t replacement_id);
// - |message_.opphi_id| is the id of an OpPhi instruction.
// - |message_.pred_label_id| is the label id of one of the predecessors of
// the block containing the OpPhi instruction.
// - The predecessor has been recorded as dead.
// - |message_.replacement_id| is the id of an instruction with the same type
// as the OpPhi instruction, available at the end of the predecessor.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Replaces the id corresponding to predecessor |message_.pred_label_id|, in
// the OpPhi instruction |message_.opphi_id|, with |message_.replacement_id|.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_

View File

@ -86,6 +86,7 @@ if (${SPIRV_BUILD_FUZZER})
transformation_replace_irrelevant_id_test.cpp
transformation_replace_linear_algebra_instruction_test.cpp
transformation_replace_load_store_with_copy_memory_test.cpp
transformation_replace_opphi_id_from_dead_predecessor_test.cpp
transformation_replace_parameter_with_global_test.cpp
transformation_replace_params_with_struct_test.cpp
transformation_set_function_control_test.cpp

View File

@ -0,0 +1,205 @@
// 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_replace_opphi_id_from_dead_predecessor.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
namespace fuzz {
namespace {
std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpConstantFalse %5
%8 = OpTypeInt 32 1
%9 = OpConstant %8 2
%10 = OpConstant %8 3
%11 = OpConstant %8 4
%12 = OpConstant %8 5
%13 = OpConstant %8 6
%2 = OpFunction %3 None %4
%14 = OpLabel
OpSelectionMerge %15 None
OpBranchConditional %6 %16 %17
%16 = OpLabel
%18 = OpCopyObject %8 %9
OpSelectionMerge %19 None
OpBranchConditional %7 %20 %21
%20 = OpLabel
%22 = OpCopyObject %8 %10
%23 = OpCopyObject %8 %12
OpBranch %19
%21 = OpLabel
%24 = OpCopyObject %8 %9
OpBranch %19
%19 = OpLabel
%25 = OpPhi %8 %22 %20 %24 %21
OpBranch %15
%17 = OpLabel
%26 = OpCopyObject %8 %12
%27 = OpCopyObject %8 %13
OpBranch %28
%28 = OpLabel
%29 = OpPhi %8 %27 %17
OpBranch %15
%15 = OpLabel
%30 = OpPhi %8 %25 %19 %26 %28
OpReturn
OpFunctionEnd
)";
TEST(TransformationReplaceOpPhiIdFromDeadPredecessorTest, Inapplicable) {
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
ASSERT_TRUE(IsValid(env, context.get()));
// Record the fact that blocks 20, 17, 28 are dead.
transformation_context.GetFactManager()->AddFactBlockIsDead(20);
transformation_context.GetFactManager()->AddFactBlockIsDead(17);
transformation_context.GetFactManager()->AddFactBlockIsDead(28);
// %26 is not an OpPhi instruction.
ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(26, 14, 10)
.IsApplicable(context.get(), transformation_context));
// %25 is not a block label.
ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(30, 25, 10)
.IsApplicable(context.get(), transformation_context));
// %14 is not a predecessor of %28 (which contains %29).
ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(29, 14, 10)
.IsApplicable(context.get(), transformation_context));
// %19 is not a dead block.
ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(30, 19, 10)
.IsApplicable(context.get(), transformation_context));
// %7 does not have the same type id as %25.
ASSERT_FALSE(
TransformationReplaceOpPhiIdFromDeadPredecessor(25, 20, 7).IsApplicable(
context.get(), transformation_context));
// %29 is not available at the end of %20.
ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(25, 20, 29)
.IsApplicable(context.get(), transformation_context));
}
TEST(TransformationReplaceOpPhiIdFromDeadPredecessorTest, Apply) {
const auto env = SPV_ENV_UNIVERSAL_1_5;
const auto consumer = nullptr;
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
FactManager fact_manager;
spvtools::ValidatorOptions validator_options;
TransformationContext transformation_context(&fact_manager,
validator_options);
ASSERT_TRUE(IsValid(env, context.get()));
// Record the fact that blocks 20, 17, 28 are dead.
transformation_context.GetFactManager()->AddFactBlockIsDead(20);
transformation_context.GetFactManager()->AddFactBlockIsDead(17);
transformation_context.GetFactManager()->AddFactBlockIsDead(28);
auto transformation1 =
TransformationReplaceOpPhiIdFromDeadPredecessor(25, 20, 18);
ASSERT_TRUE(
transformation1.IsApplicable(context.get(), transformation_context));
transformation1.Apply(context.get(), &transformation_context);
auto transformation2 =
TransformationReplaceOpPhiIdFromDeadPredecessor(30, 28, 29);
ASSERT_TRUE(
transformation2.IsApplicable(context.get(), transformation_context));
transformation2.Apply(context.get(), &transformation_context);
auto transformation3 =
TransformationReplaceOpPhiIdFromDeadPredecessor(29, 17, 10);
ASSERT_TRUE(
transformation3.IsApplicable(context.get(), transformation_context));
transformation3.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 %2 "main"
OpExecutionMode %2 OriginUpperLeft
OpSource ESSL 310
OpName %2 "main"
%3 = OpTypeVoid
%4 = OpTypeFunction %3
%5 = OpTypeBool
%6 = OpConstantTrue %5
%7 = OpConstantFalse %5
%8 = OpTypeInt 32 1
%9 = OpConstant %8 2
%10 = OpConstant %8 3
%11 = OpConstant %8 4
%12 = OpConstant %8 5
%13 = OpConstant %8 6
%2 = OpFunction %3 None %4
%14 = OpLabel
OpSelectionMerge %15 None
OpBranchConditional %6 %16 %17
%16 = OpLabel
%18 = OpCopyObject %8 %9
OpSelectionMerge %19 None
OpBranchConditional %7 %20 %21
%20 = OpLabel
%22 = OpCopyObject %8 %10
%23 = OpCopyObject %8 %12
OpBranch %19
%21 = OpLabel
%24 = OpCopyObject %8 %9
OpBranch %19
%19 = OpLabel
%25 = OpPhi %8 %18 %20 %24 %21
OpBranch %15
%17 = OpLabel
%26 = OpCopyObject %8 %12
%27 = OpCopyObject %8 %13
OpBranch %28
%28 = OpLabel
%29 = OpPhi %8 %10 %17
OpBranch %15
%15 = OpLabel
%30 = OpPhi %8 %25 %19 %29 %28
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformations, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools