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.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 570cc7a..288ea25 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -94,6 +94,7 @@
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 @@
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 @@
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 @@
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
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 6107f45..3656e92 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -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 @@
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);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 1dfa717..153b9ef 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -103,6 +103,8 @@
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 @@
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_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 14ada03..594cb90 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -261,6 +261,9 @@
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 @@
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_;
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
new file mode 100644
index 0000000..7d89e38
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp
@@ -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
diff --git a/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
new file mode 100644
index 0000000..972c5f9
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h
@@ -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_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 0ac485f..0cc7a78 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -417,6 +417,7 @@
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 @@
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
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index e6baa15..2360901 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -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 @@
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());
diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
new file mode 100644
index 0000000..d5d324b
--- /dev/null
+++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp
@@ -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
diff --git a/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h
new file mode 100644
index 0000000..2833eb2
--- /dev/null
+++ b/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h
@@ -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_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 105d4a6..34317d0 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -86,6 +86,7 @@
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
diff --git a/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp b/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp
new file mode 100644
index 0000000..fd1c180
--- /dev/null
+++ b/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp
@@ -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