mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 06:50:06 +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_irrelevant_ids.h
|
||||||
fuzzer_pass_replace_linear_algebra_instructions.h
|
fuzzer_pass_replace_linear_algebra_instructions.h
|
||||||
fuzzer_pass_replace_loads_stores_with_copy_memories.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_parameter_with_global.h
|
||||||
fuzzer_pass_replace_params_with_struct.h
|
fuzzer_pass_replace_params_with_struct.h
|
||||||
fuzzer_pass_split_blocks.h
|
fuzzer_pass_split_blocks.h
|
||||||
@ -172,6 +173,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_replace_irrelevant_id.h
|
transformation_replace_irrelevant_id.h
|
||||||
transformation_replace_linear_algebra_instruction.h
|
transformation_replace_linear_algebra_instruction.h
|
||||||
transformation_replace_load_store_with_copy_memory.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_parameter_with_global.h
|
||||||
transformation_replace_params_with_struct.h
|
transformation_replace_params_with_struct.h
|
||||||
transformation_set_function_control.h
|
transformation_set_function_control.h
|
||||||
@ -251,6 +253,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
fuzzer_pass_replace_irrelevant_ids.cpp
|
fuzzer_pass_replace_irrelevant_ids.cpp
|
||||||
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
fuzzer_pass_replace_linear_algebra_instructions.cpp
|
||||||
fuzzer_pass_replace_loads_stores_with_copy_memories.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_parameter_with_global.cpp
|
||||||
fuzzer_pass_replace_params_with_struct.cpp
|
fuzzer_pass_replace_params_with_struct.cpp
|
||||||
fuzzer_pass_split_blocks.cpp
|
fuzzer_pass_split_blocks.cpp
|
||||||
@ -328,6 +331,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_replace_irrelevant_id.cpp
|
transformation_replace_irrelevant_id.cpp
|
||||||
transformation_replace_linear_algebra_instruction.cpp
|
transformation_replace_linear_algebra_instruction.cpp
|
||||||
transformation_replace_load_store_with_copy_memory.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_parameter_with_global.cpp
|
||||||
transformation_replace_params_with_struct.cpp
|
transformation_replace_params_with_struct.cpp
|
||||||
transformation_set_function_control.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_irrelevant_ids.h"
|
||||||
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.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_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_parameter_with_global.h"
|
||||||
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
|
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
|
||||||
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
||||||
@ -379,6 +380,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||||||
MaybeAddPass<FuzzerPassPermutePhiOperands>(
|
MaybeAddPass<FuzzerPassPermutePhiOperands>(
|
||||||
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
MaybeAddPass<FuzzerPassReplaceOpPhiIdsFromDeadPredecessors>(
|
||||||
|
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
|
transformation_sequence_out);
|
||||||
MaybeAddPass<FuzzerPassReplaceIrrelevantIds>(
|
MaybeAddPass<FuzzerPassReplaceIrrelevantIds>(
|
||||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
@ -103,6 +103,8 @@ const std::pair<uint32_t, uint32_t>
|
|||||||
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
kChanceOfReplacingLinearAlgebraInstructions = {10, 90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
|
const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
|
||||||
{20, 90};
|
{20, 90};
|
||||||
|
const std::pair<uint32_t, uint32_t>
|
||||||
|
kChanceOfReplacingOpPhiIdFromDeadPredecessor = {20, 90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
|
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithGlobals = {
|
||||||
30, 70};
|
30, 70};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
|
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
|
||||||
@ -267,6 +269,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions);
|
||||||
chance_of_replacing_load_store_with_copy_memory_ =
|
chance_of_replacing_load_store_with_copy_memory_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
|
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
|
||||||
|
chance_of_replacing_opphi_id_from_dead_predecessor_ =
|
||||||
|
ChooseBetweenMinAndMax(kChanceOfReplacingOpPhiIdFromDeadPredecessor);
|
||||||
chance_of_replacing_parameters_with_globals_ =
|
chance_of_replacing_parameters_with_globals_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
|
ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals);
|
||||||
chance_of_replacing_parameters_with_struct_ =
|
chance_of_replacing_parameters_with_struct_ =
|
||||||
|
@ -261,6 +261,9 @@ class FuzzerContext {
|
|||||||
uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
|
uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() {
|
||||||
return chance_of_replacing_load_store_with_copy_memory_;
|
return chance_of_replacing_load_store_with_copy_memory_;
|
||||||
}
|
}
|
||||||
|
uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() {
|
||||||
|
return chance_of_replacing_opphi_id_from_dead_predecessor_;
|
||||||
|
}
|
||||||
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
||||||
return chance_of_replacing_parameters_with_globals_;
|
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_irrelevant_id_;
|
||||||
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
uint32_t chance_of_replacing_linear_algebra_instructions_;
|
||||||
uint32_t chance_of_replacing_load_store_with_copy_memory_;
|
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_globals_;
|
||||||
uint32_t chance_of_replacing_parameters_with_struct_;
|
uint32_t chance_of_replacing_parameters_with_struct_;
|
||||||
uint32_t chance_of_splitting_block_;
|
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;
|
TransformationAddOpPhiSynonym add_opphi_synonym = 70;
|
||||||
TransformationMutatePointer mutate_pointer = 71;
|
TransformationMutatePointer mutate_pointer = 71;
|
||||||
TransformationReplaceIrrelevantId replace_irrelevant_id = 72;
|
TransformationReplaceIrrelevantId replace_irrelevant_id = 72;
|
||||||
|
TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73;
|
||||||
// Add additional option using the next available number.
|
// Add additional option using the next available number.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1543,6 +1544,26 @@ message TransformationReplaceLoadStoreWithCopyMemory {
|
|||||||
InstructionDescriptor store_instruction_descriptor = 2;
|
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 {
|
message TransformationReplaceParamsWithStruct {
|
||||||
|
|
||||||
// Replaces parameters of the function with a struct containing
|
// 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_irrelevant_id.h"
|
||||||
#include "source/fuzz/transformation_replace_linear_algebra_instruction.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_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_parameter_with_global.h"
|
||||||
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
#include "source/fuzz/transformation_replace_params_with_struct.h"
|
||||||
#include "source/fuzz/transformation_set_function_control.h"
|
#include "source/fuzz/transformation_set_function_control.h"
|
||||||
@ -283,6 +284,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
|||||||
kReplaceParamsWithStruct:
|
kReplaceParamsWithStruct:
|
||||||
return MakeUnique<TransformationReplaceParamsWithStruct>(
|
return MakeUnique<TransformationReplaceParamsWithStruct>(
|
||||||
message.replace_params_with_struct());
|
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:
|
case protobufs::Transformation::TransformationCase::kSetFunctionControl:
|
||||||
return MakeUnique<TransformationSetFunctionControl>(
|
return MakeUnique<TransformationSetFunctionControl>(
|
||||||
message.set_function_control());
|
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_irrelevant_id_test.cpp
|
||||||
transformation_replace_linear_algebra_instruction_test.cpp
|
transformation_replace_linear_algebra_instruction_test.cpp
|
||||||
transformation_replace_load_store_with_copy_memory_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_parameter_with_global_test.cpp
|
||||||
transformation_replace_params_with_struct_test.cpp
|
transformation_replace_params_with_struct_test.cpp
|
||||||
transformation_set_function_control_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