mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 21:30:07 +00:00
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:
parent
3daabd3212
commit
788468408e
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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_ =
|
||||
|
@ -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_;
|
||||
|
@ -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
|
@ -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_
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
@ -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_
|
@ -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
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user