mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-30 06:50:06 +00:00
spirv-fuzz: Transformation to convert OpSelect to conditional branch (#3681)
This transformation takes an OpSelect instruction and replaces it with a conditional branch, selecting the correct value using an OpPhi instruction. Fixes part of the issue #3544.
This commit is contained in:
parent
2c60d16a64
commit
fd05605bef
@ -95,6 +95,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
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_opphi_ids_from_dead_predecessors.h
|
||||||
|
fuzzer_pass_replace_opselects_with_conditional_branches.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
|
||||||
@ -174,6 +175,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
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_opphi_id_from_dead_predecessor.h
|
||||||
|
transformation_replace_opselect_with_conditional_branch.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
|
||||||
@ -254,6 +256,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
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_opphi_ids_from_dead_predecessors.cpp
|
||||||
|
fuzzer_pass_replace_opselects_with_conditional_branches.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
|
||||||
@ -306,6 +309,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_composite_insert.cpp
|
transformation_composite_insert.cpp
|
||||||
transformation_compute_data_synonym_fact_closure.cpp
|
transformation_compute_data_synonym_fact_closure.cpp
|
||||||
transformation_context.cpp
|
transformation_context.cpp
|
||||||
|
transformation_replace_opselect_with_conditional_branch.cpp
|
||||||
transformation_equation_instruction.cpp
|
transformation_equation_instruction.cpp
|
||||||
transformation_function_call.cpp
|
transformation_function_call.cpp
|
||||||
transformation_inline_function.cpp
|
transformation_inline_function.cpp
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
#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_opphi_ids_from_dead_predecessors.h"
|
||||||
|
#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.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"
|
||||||
@ -325,6 +326,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||||||
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
MaybeAddPass<FuzzerPassReplaceLinearAlgebraInstructions>(
|
||||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
MaybeAddPass<FuzzerPassReplaceOpSelectsWithConditionalBranches>(
|
||||||
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
|
transformation_sequence_out);
|
||||||
MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
|
MaybeAddPass<FuzzerPassReplaceParamsWithStruct>(
|
||||||
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
@ -27,6 +27,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingAccessChain = {5, 50};
|
|||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingAnotherStructField = {20,
|
||||||
90};
|
90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingArrayOrStructType = {20, 90};
|
||||||
|
const std::pair<uint32_t, uint32_t>
|
||||||
|
kChanceOfAddingBothBranchesWhenReplacingOpSelect = {40, 60};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingCompositeInsert = {20, 50};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingCompositeInsert = {20, 50};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingCopyMemory = {20, 50};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingCopyMemory = {20, 50};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingDeadBlock = {20, 90};
|
||||||
@ -48,6 +50,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingParameters = {5, 70};
|
|||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingRelaxedDecoration = {20, 90};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingRelaxedDecoration = {20, 90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingStore = {5, 50};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingSynonyms = {20, 50};
|
||||||
|
const std::pair<uint32_t, uint32_t>
|
||||||
|
kChanceOfAddingTrueBranchWhenReplacingOpSelect = {40, 60};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorType = {20, 70};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
|
const std::pair<uint32_t, uint32_t> kChanceOfAddingVectorShuffle = {20, 70};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
|
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingBranchWeights = {20, 90};
|
||||||
@ -105,6 +109,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfReplacingLoadStoreWithCopyMemory =
|
|||||||
{20, 90};
|
{20, 90};
|
||||||
const std::pair<uint32_t, uint32_t>
|
const std::pair<uint32_t, uint32_t>
|
||||||
kChanceOfReplacingOpPhiIdFromDeadPredecessor = {20, 90};
|
kChanceOfReplacingOpPhiIdFromDeadPredecessor = {20, 90};
|
||||||
|
const std::pair<uint32_t, uint32_t>
|
||||||
|
kChanceOfReplacingOpSelectWithConditionalBranch = {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 = {
|
||||||
@ -162,6 +168,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
|
ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField);
|
||||||
chance_of_adding_array_or_struct_type_ =
|
chance_of_adding_array_or_struct_type_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
|
ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType);
|
||||||
|
chance_of_adding_both_branches_when_replacing_opselect_ =
|
||||||
|
ChooseBetweenMinAndMax(kChanceOfAddingBothBranchesWhenReplacingOpSelect);
|
||||||
chance_of_adding_composite_insert_ =
|
chance_of_adding_composite_insert_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert);
|
ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert);
|
||||||
chance_of_adding_copy_memory_ =
|
chance_of_adding_copy_memory_ =
|
||||||
@ -194,6 +202,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
chance_of_adding_relaxed_decoration_ =
|
chance_of_adding_relaxed_decoration_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration);
|
ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration);
|
||||||
chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
|
chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore);
|
||||||
|
chance_of_adding_true_branch_when_replacing_opselect_ =
|
||||||
|
ChooseBetweenMinAndMax(kChanceOfAddingTrueBranchWhenReplacingOpSelect);
|
||||||
chance_of_adding_vector_shuffle_ =
|
chance_of_adding_vector_shuffle_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
|
ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle);
|
||||||
chance_of_adding_vector_type_ =
|
chance_of_adding_vector_type_ =
|
||||||
@ -271,6 +281,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
|
ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory);
|
||||||
chance_of_replacing_opphi_id_from_dead_predecessor_ =
|
chance_of_replacing_opphi_id_from_dead_predecessor_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfReplacingOpPhiIdFromDeadPredecessor);
|
ChooseBetweenMinAndMax(kChanceOfReplacingOpPhiIdFromDeadPredecessor);
|
||||||
|
chance_of_replacing_opselect_with_conditional_branch_ =
|
||||||
|
ChooseBetweenMinAndMax(kChanceOfReplacingOpSelectWithConditionalBranch);
|
||||||
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_ =
|
||||||
|
@ -115,6 +115,9 @@ class FuzzerContext {
|
|||||||
uint32_t GetChanceOfAddingArrayOrStructType() {
|
uint32_t GetChanceOfAddingArrayOrStructType() {
|
||||||
return chance_of_adding_array_or_struct_type_;
|
return chance_of_adding_array_or_struct_type_;
|
||||||
}
|
}
|
||||||
|
uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() {
|
||||||
|
return chance_of_adding_both_branches_when_replacing_opselect_;
|
||||||
|
}
|
||||||
uint32_t GetChanceOfAddingCompositeInsert() {
|
uint32_t GetChanceOfAddingCompositeInsert() {
|
||||||
return chance_of_adding_composite_insert_;
|
return chance_of_adding_composite_insert_;
|
||||||
}
|
}
|
||||||
@ -157,6 +160,9 @@ class FuzzerContext {
|
|||||||
}
|
}
|
||||||
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
|
uint32_t GetChanceOfAddingStore() { return chance_of_adding_store_; }
|
||||||
uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
|
uint32_t GetChanceOfAddingSynonyms() { return chance_of_adding_synonyms_; }
|
||||||
|
uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() {
|
||||||
|
return chance_of_adding_true_branch_when_replacing_opselect_;
|
||||||
|
}
|
||||||
uint32_t GetChanceOfAddingVectorShuffle() {
|
uint32_t GetChanceOfAddingVectorShuffle() {
|
||||||
return chance_of_adding_vector_shuffle_;
|
return chance_of_adding_vector_shuffle_;
|
||||||
}
|
}
|
||||||
@ -264,6 +270,9 @@ class FuzzerContext {
|
|||||||
uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() {
|
uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() {
|
||||||
return chance_of_replacing_opphi_id_from_dead_predecessor_;
|
return chance_of_replacing_opphi_id_from_dead_predecessor_;
|
||||||
}
|
}
|
||||||
|
uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() {
|
||||||
|
return chance_of_replacing_opselect_with_conditional_branch_;
|
||||||
|
}
|
||||||
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
uint32_t GetChanceOfReplacingParametersWithGlobals() {
|
||||||
return chance_of_replacing_parameters_with_globals_;
|
return chance_of_replacing_parameters_with_globals_;
|
||||||
}
|
}
|
||||||
@ -364,6 +373,7 @@ class FuzzerContext {
|
|||||||
uint32_t chance_of_adding_access_chain_;
|
uint32_t chance_of_adding_access_chain_;
|
||||||
uint32_t chance_of_adding_another_struct_field_;
|
uint32_t chance_of_adding_another_struct_field_;
|
||||||
uint32_t chance_of_adding_array_or_struct_type_;
|
uint32_t chance_of_adding_array_or_struct_type_;
|
||||||
|
uint32_t chance_of_adding_both_branches_when_replacing_opselect_;
|
||||||
uint32_t chance_of_adding_composite_insert_;
|
uint32_t chance_of_adding_composite_insert_;
|
||||||
uint32_t chance_of_adding_copy_memory_;
|
uint32_t chance_of_adding_copy_memory_;
|
||||||
uint32_t chance_of_adding_dead_block_;
|
uint32_t chance_of_adding_dead_block_;
|
||||||
@ -382,6 +392,7 @@ class FuzzerContext {
|
|||||||
uint32_t chance_of_adding_relaxed_decoration_;
|
uint32_t chance_of_adding_relaxed_decoration_;
|
||||||
uint32_t chance_of_adding_store_;
|
uint32_t chance_of_adding_store_;
|
||||||
uint32_t chance_of_adding_synonyms_;
|
uint32_t chance_of_adding_synonyms_;
|
||||||
|
uint32_t chance_of_adding_true_branch_when_replacing_opselect_;
|
||||||
uint32_t chance_of_adding_vector_shuffle_;
|
uint32_t chance_of_adding_vector_shuffle_;
|
||||||
uint32_t chance_of_adding_vector_type_;
|
uint32_t chance_of_adding_vector_type_;
|
||||||
uint32_t chance_of_adjusting_branch_weights_;
|
uint32_t chance_of_adjusting_branch_weights_;
|
||||||
@ -421,6 +432,7 @@ class FuzzerContext {
|
|||||||
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_opphi_id_from_dead_predecessor_;
|
||||||
|
uint32_t chance_of_replacing_opselect_with_conditional_branch_;
|
||||||
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,162 @@
|
|||||||
|
// 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_opselects_with_conditional_branches.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_util.h"
|
||||||
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
|
#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h"
|
||||||
|
#include "source/fuzz/transformation_split_block.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
FuzzerPassReplaceOpSelectsWithConditionalBranches::
|
||||||
|
FuzzerPassReplaceOpSelectsWithConditionalBranches(
|
||||||
|
opt::IRContext* ir_context,
|
||||||
|
TransformationContext* transformation_context,
|
||||||
|
FuzzerContext* fuzzer_context,
|
||||||
|
protobufs::TransformationSequence* transformations)
|
||||||
|
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||||
|
transformations) {}
|
||||||
|
|
||||||
|
FuzzerPassReplaceOpSelectsWithConditionalBranches::
|
||||||
|
~FuzzerPassReplaceOpSelectsWithConditionalBranches() = default;
|
||||||
|
|
||||||
|
void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() {
|
||||||
|
// Keep track of the instructions that we want to replace. We need to collect
|
||||||
|
// them in a vector, since it's not safe to modify the module while iterating
|
||||||
|
// over it.
|
||||||
|
std::vector<uint32_t> replaceable_opselect_instruction_ids;
|
||||||
|
|
||||||
|
// Loop over all the instructions in the module.
|
||||||
|
for (auto& function : *GetIRContext()->module()) {
|
||||||
|
for (auto& block : function) {
|
||||||
|
// We cannot split loop headers, so we don't need to consider instructions
|
||||||
|
// in loop headers that are also merge blocks (since they would need to be
|
||||||
|
// split).
|
||||||
|
if (block.IsLoopHeader() &&
|
||||||
|
GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(
|
||||||
|
block.id())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& instruction : block) {
|
||||||
|
// We only care about OpSelect instructions.
|
||||||
|
if (instruction.opcode() != SpvOpSelect) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Randomly choose whether to consider this instruction for replacement.
|
||||||
|
if (!GetFuzzerContext()->ChoosePercentage(
|
||||||
|
GetFuzzerContext()
|
||||||
|
->GetChanceOfReplacingOpselectWithConditionalBranch())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the block is a loop header and we need to split it, the
|
||||||
|
// transformation cannot be applied because loop headers cannot be
|
||||||
|
// split. We can break out of this loop because the transformation can
|
||||||
|
// only be applied to at most the first instruction in a loop header.
|
||||||
|
if (block.IsLoopHeader() && InstructionNeedsSplitBefore(&instruction)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the instruction separates an OpSampledImage from its use, the
|
||||||
|
// block cannot be split around it and the instruction cannot be
|
||||||
|
// replaced.
|
||||||
|
if (fuzzerutil::
|
||||||
|
SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse(
|
||||||
|
&block, &instruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can apply the transformation to this instruction.
|
||||||
|
replaceable_opselect_instruction_ids.push_back(instruction.result_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the transformations, splitting the blocks containing the
|
||||||
|
// instructions, if necessary.
|
||||||
|
for (uint32_t instruction_id : replaceable_opselect_instruction_ids) {
|
||||||
|
auto instruction =
|
||||||
|
GetIRContext()->get_def_use_mgr()->GetDef(instruction_id);
|
||||||
|
|
||||||
|
// If the instruction requires the block containing it to be split before
|
||||||
|
// it, split the block.
|
||||||
|
if (InstructionNeedsSplitBefore(instruction)) {
|
||||||
|
ApplyTransformation(TransformationSplitBlock(
|
||||||
|
MakeInstructionDescriptor(GetIRContext(), instruction),
|
||||||
|
GetFuzzerContext()->GetFreshId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide whether to have two branches or just one.
|
||||||
|
bool two_branches = GetFuzzerContext()->ChoosePercentage(
|
||||||
|
GetFuzzerContext()
|
||||||
|
->GetChanceOfAddingBothBranchesWhenReplacingOpSelect());
|
||||||
|
|
||||||
|
// If there will be only one branch, decide whether it will be the true
|
||||||
|
// branch or the false branch.
|
||||||
|
bool true_branch_id_zero =
|
||||||
|
!two_branches &&
|
||||||
|
GetFuzzerContext()->ChoosePercentage(
|
||||||
|
GetFuzzerContext()
|
||||||
|
->GetChanceOfAddingTrueBranchWhenReplacingOpSelect());
|
||||||
|
bool false_branch_id_zero = !two_branches && !true_branch_id_zero;
|
||||||
|
|
||||||
|
uint32_t true_branch_id =
|
||||||
|
true_branch_id_zero ? 0 : GetFuzzerContext()->GetFreshId();
|
||||||
|
uint32_t false_branch_id =
|
||||||
|
false_branch_id_zero ? 0 : GetFuzzerContext()->GetFreshId();
|
||||||
|
|
||||||
|
ApplyTransformation(TransformationReplaceOpSelectWithConditionalBranch(
|
||||||
|
instruction_id, true_branch_id, false_branch_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FuzzerPassReplaceOpSelectsWithConditionalBranches::
|
||||||
|
InstructionNeedsSplitBefore(opt::Instruction* instruction) {
|
||||||
|
assert(instruction && instruction->opcode() == SpvOpSelect &&
|
||||||
|
"The instruction must be OpSelect.");
|
||||||
|
|
||||||
|
auto block = GetIRContext()->get_instr_block(instruction);
|
||||||
|
assert(block && "The instruction must be contained in a block.");
|
||||||
|
|
||||||
|
// We need to split the block if the instruction is not the first in its
|
||||||
|
// block.
|
||||||
|
if (instruction->unique_id() != block->begin()->unique_id()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to split the block if it is a merge block.
|
||||||
|
if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to split the block if it has more than one predecessor.
|
||||||
|
if (GetIRContext()->cfg()->preds(block->id()).size() != 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to split the block if its predecessor is a header or it does not
|
||||||
|
// branch unconditionally to the block.
|
||||||
|
auto predecessor = GetIRContext()->get_instr_block(
|
||||||
|
GetIRContext()->cfg()->preds(block->id())[0]);
|
||||||
|
return predecessor->MergeBlockIdIfAny() ||
|
||||||
|
predecessor->terminator()->opcode() != SpvOpBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
@ -0,0 +1,53 @@
|
|||||||
|
// 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_OPSELECTS_WITH_CONDITIONAL_BRANCHES_
|
||||||
|
#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_pass.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
// A fuzzer pass to replace OpSelect instructions (where the condition is a
|
||||||
|
// scalar boolean) with conditional branches and OpPhi instructions.
|
||||||
|
class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass {
|
||||||
|
public:
|
||||||
|
FuzzerPassReplaceOpSelectsWithConditionalBranches(
|
||||||
|
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||||
|
FuzzerContext* fuzzer_context,
|
||||||
|
protobufs::TransformationSequence* transformations);
|
||||||
|
|
||||||
|
~FuzzerPassReplaceOpSelectsWithConditionalBranches() override;
|
||||||
|
|
||||||
|
void Apply() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Returns true if any of the following holds:
|
||||||
|
// - the instruction is not the first in its block
|
||||||
|
// - the block containing it is a merge block
|
||||||
|
// - the block does not have a unique predecessor
|
||||||
|
// - the predecessor of the block is the header of a construct
|
||||||
|
// - the predecessor does not branch unconditionally to the block
|
||||||
|
// If this function returns true, the block must be split before the
|
||||||
|
// instruction for TransformationReplaceOpSelectWithConditionalBranch to be
|
||||||
|
// applicable.
|
||||||
|
// Assumes that the instruction is OpSelect.
|
||||||
|
bool InstructionNeedsSplitBefore(opt::Instruction* instruction);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_
|
@ -1486,6 +1486,39 @@ bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
|
|||||||
return builtin_count != 0;
|
return builtin_count != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse(
|
||||||
|
opt::BasicBlock* block_to_split, opt::Instruction* split_before) {
|
||||||
|
std::set<uint32_t> sampled_image_result_ids;
|
||||||
|
bool before_split = true;
|
||||||
|
|
||||||
|
// Check all the instructions in the block to split.
|
||||||
|
for (auto& instruction : *block_to_split) {
|
||||||
|
if (&instruction == &*split_before) {
|
||||||
|
before_split = false;
|
||||||
|
}
|
||||||
|
if (before_split) {
|
||||||
|
// If the instruction comes before the split and its opcode is
|
||||||
|
// OpSampledImage, record its result id.
|
||||||
|
if (instruction.opcode() == SpvOpSampledImage) {
|
||||||
|
sampled_image_result_ids.insert(instruction.result_id());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the instruction comes after the split, check if ids
|
||||||
|
// corresponding to OpSampledImage instructions defined before the split
|
||||||
|
// are used, and return true if they are.
|
||||||
|
if (!instruction.WhileEachInId(
|
||||||
|
[&sampled_image_result_ids](uint32_t* id) -> bool {
|
||||||
|
return !sampled_image_result_ids.count(*id);
|
||||||
|
})) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No usage that would be separated from the definition has been found.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fuzzerutil
|
} // namespace fuzzerutil
|
||||||
} // namespace fuzz
|
} // namespace fuzz
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -524,6 +524,11 @@ bool IdUseCanBeReplaced(opt::IRContext* ir_context,
|
|||||||
bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
|
bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context,
|
||||||
uint32_t struct_type_id);
|
uint32_t struct_type_id);
|
||||||
|
|
||||||
|
// Returns true iff splitting block |block_to_split| just before the instruction
|
||||||
|
// |split_before| would separate an OpSampledImage instruction from its usage.
|
||||||
|
bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse(
|
||||||
|
opt::BasicBlock* block_to_split, opt::Instruction* split_before);
|
||||||
|
|
||||||
} // namespace fuzzerutil
|
} // namespace fuzzerutil
|
||||||
} // namespace fuzz
|
} // namespace fuzz
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -418,6 +418,7 @@ message Transformation {
|
|||||||
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;
|
TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73;
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch replace_opselect_with_conditional_branch = 74;
|
||||||
// Add additional option using the next available number.
|
// Add additional option using the next available number.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1564,6 +1565,39 @@ message TransformationReplaceOpPhiIdFromDeadPredecessor {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message TransformationReplaceOpSelectWithConditionalBranch {
|
||||||
|
|
||||||
|
// A transformation that takes an OpSelect instruction with a
|
||||||
|
// scalar boolean condition and replaces it with a conditional
|
||||||
|
// branch and an OpPhi instruction.
|
||||||
|
// The OpSelect instruction must be the first instruction in its
|
||||||
|
// block, which must have a unique predecessor. The block will
|
||||||
|
// become the merge block of a new construct, while its predecessor
|
||||||
|
// will become the header.
|
||||||
|
// Given the original OpSelect instruction:
|
||||||
|
// %id = OpSelect %type %cond %then %else
|
||||||
|
// The branching instruction of the header will be:
|
||||||
|
// OpBranchConditional %cond %true_block_id %false_block_id
|
||||||
|
// and the OpSelect instruction will be turned into:
|
||||||
|
// %id = OpPhi %type %then %true_block_id %else %false_block_id
|
||||||
|
// At most one of |true_block_id| and |false_block_id| can be zero. In
|
||||||
|
// that case, there will be no such block and all references to it
|
||||||
|
// will be replaced by %merge_block (where %merge_block is the
|
||||||
|
// block containing the OpSelect instruction).
|
||||||
|
|
||||||
|
// The result id of the OpSelect instruction.
|
||||||
|
uint32 select_id = 1;
|
||||||
|
|
||||||
|
// A fresh id for the new block that the predecessor of the block
|
||||||
|
// containing |select_id| will branch to if the condition holds.
|
||||||
|
uint32 true_block_id = 2;
|
||||||
|
|
||||||
|
// A fresh id for the new block that the predecessor of the block
|
||||||
|
// containing |select_id| will branch to if the condition does not
|
||||||
|
// hold.
|
||||||
|
uint32 false_block_id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message TransformationReplaceParamsWithStruct {
|
message TransformationReplaceParamsWithStruct {
|
||||||
|
|
||||||
// Replaces parameters of the function with a struct containing
|
// Replaces parameters of the function with a struct containing
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
#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_opphi_id_from_dead_predecessor.h"
|
||||||
|
#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.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"
|
||||||
@ -276,6 +277,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
|||||||
kReplaceLoadStoreWithCopyMemory:
|
kReplaceLoadStoreWithCopyMemory:
|
||||||
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
|
return MakeUnique<TransformationReplaceLoadStoreWithCopyMemory>(
|
||||||
message.replace_load_store_with_copy_memory());
|
message.replace_load_store_with_copy_memory());
|
||||||
|
case protobufs::Transformation::TransformationCase::
|
||||||
|
kReplaceOpselectWithConditionalBranch:
|
||||||
|
return MakeUnique<TransformationReplaceOpSelectWithConditionalBranch>(
|
||||||
|
message.replace_opselect_with_conditional_branch());
|
||||||
case protobufs::Transformation::TransformationCase::
|
case protobufs::Transformation::TransformationCase::
|
||||||
kReplaceParameterWithGlobal:
|
kReplaceParameterWithGlobal:
|
||||||
return MakeUnique<TransformationReplaceParameterWithGlobal>(
|
return MakeUnique<TransformationReplaceParameterWithGlobal>(
|
||||||
|
@ -0,0 +1,204 @@
|
|||||||
|
// 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_opselect_with_conditional_branch.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_util.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch::
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(
|
||||||
|
const spvtools::fuzz::protobufs::
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch& message)
|
||||||
|
: message_(message) {}
|
||||||
|
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch::
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(
|
||||||
|
uint32_t select_id, uint32_t true_block_id, uint32_t false_block_id) {
|
||||||
|
message_.set_select_id(select_id);
|
||||||
|
message_.set_true_block_id(true_block_id);
|
||||||
|
message_.set_false_block_id(false_block_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationReplaceOpSelectWithConditionalBranch::IsApplicable(
|
||||||
|
opt::IRContext* ir_context,
|
||||||
|
const TransformationContext& /* unused */) const {
|
||||||
|
assert((message_.true_block_id() || message_.false_block_id()) &&
|
||||||
|
"At least one of the ids must be non-zero.");
|
||||||
|
|
||||||
|
// Check that the non-zero ids are fresh.
|
||||||
|
std::set<uint32_t> used_ids;
|
||||||
|
for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) {
|
||||||
|
if (id && !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context,
|
||||||
|
&used_ids)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto instruction =
|
||||||
|
ir_context->get_def_use_mgr()->GetDef(message_.select_id());
|
||||||
|
|
||||||
|
// The instruction must exist and it must be an OpSelect instruction.
|
||||||
|
if (!instruction || instruction->opcode() != SpvOpSelect) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the condition is a scalar boolean.
|
||||||
|
auto condition = ir_context->get_def_use_mgr()->GetDef(
|
||||||
|
instruction->GetSingleWordInOperand(0));
|
||||||
|
assert(condition && "The condition should always exist in a valid module.");
|
||||||
|
|
||||||
|
auto condition_type =
|
||||||
|
ir_context->get_type_mgr()->GetType(condition->type_id());
|
||||||
|
if (!condition_type->AsBool()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto block = ir_context->get_instr_block(instruction);
|
||||||
|
assert(block && "The block containing the instruction must be found");
|
||||||
|
|
||||||
|
// The instruction must be the first in its block.
|
||||||
|
if (instruction->unique_id() != block->begin()->unique_id()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The block must not be a merge block.
|
||||||
|
if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The block must have exactly one predecessor.
|
||||||
|
auto predecessors = ir_context->cfg()->preds(block->id());
|
||||||
|
if (predecessors.size() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pred_id = predecessors[0];
|
||||||
|
auto predecessor = ir_context->get_instr_block(pred_id);
|
||||||
|
|
||||||
|
// The predecessor must not be the header of a construct and it must end with
|
||||||
|
// OpBranch.
|
||||||
|
if (predecessor->GetMergeInst() != nullptr ||
|
||||||
|
predecessor->terminator()->opcode() != SpvOpBranch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformationReplaceOpSelectWithConditionalBranch::Apply(
|
||||||
|
opt::IRContext* ir_context, TransformationContext* /* unused */) const {
|
||||||
|
auto instruction =
|
||||||
|
ir_context->get_def_use_mgr()->GetDef(message_.select_id());
|
||||||
|
|
||||||
|
auto block = ir_context->get_instr_block(instruction);
|
||||||
|
|
||||||
|
auto predecessor =
|
||||||
|
ir_context->get_instr_block(ir_context->cfg()->preds(block->id())[0]);
|
||||||
|
|
||||||
|
// Create a new block for each non-zero id in {|message_.true_branch_id|,
|
||||||
|
// |message_.false_branch_id|}. Make each newly-created block branch
|
||||||
|
// unconditionally to the instruction block.
|
||||||
|
for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) {
|
||||||
|
if (id) {
|
||||||
|
fuzzerutil::UpdateModuleIdBound(ir_context, id);
|
||||||
|
|
||||||
|
// Create the new block.
|
||||||
|
auto new_block = MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpLabel, 0, id, opt::Instruction::OperandList{}));
|
||||||
|
|
||||||
|
// Add an unconditional branch from the new block to the instruction
|
||||||
|
// block.
|
||||||
|
new_block->AddInstruction(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpBranch, 0, 0,
|
||||||
|
opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}}}));
|
||||||
|
|
||||||
|
// Insert the new block right after the predecessor of the instruction
|
||||||
|
// block.
|
||||||
|
block->GetParent()->InsertBasicBlockBefore(std::move(new_block), block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the OpBranch instruction from the predecessor.
|
||||||
|
ir_context->KillInst(predecessor->terminator());
|
||||||
|
|
||||||
|
// Add an OpSelectionMerge instruction to the predecessor block, where the
|
||||||
|
// merge block is the instruction block.
|
||||||
|
predecessor->AddInstruction(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpSelectionMerge, 0, 0,
|
||||||
|
opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}},
|
||||||
|
{SPV_OPERAND_TYPE_SELECTION_CONTROL,
|
||||||
|
{SpvSelectionControlMaskNone}}}));
|
||||||
|
|
||||||
|
// |if_block| will be the true block, if it has been created, the instruction
|
||||||
|
// block otherwise.
|
||||||
|
uint32_t if_block =
|
||||||
|
message_.true_block_id() ? message_.true_block_id() : block->id();
|
||||||
|
|
||||||
|
// |else_block| will be the false block, if it has been created, the
|
||||||
|
// instruction block otherwise.
|
||||||
|
uint32_t else_block =
|
||||||
|
message_.false_block_id() ? message_.false_block_id() : block->id();
|
||||||
|
|
||||||
|
assert(if_block != else_block &&
|
||||||
|
"|if_block| and |else_block| should always be different, if the "
|
||||||
|
"transformation is applicable.");
|
||||||
|
|
||||||
|
// Add a conditional branching instruction to the predecessor, branching to
|
||||||
|
// |if_block| if the condition is true and to |if_false| otherwise.
|
||||||
|
predecessor->AddInstruction(MakeUnique<opt::Instruction>(
|
||||||
|
ir_context, SpvOpBranchConditional, 0, 0,
|
||||||
|
opt::Instruction::OperandList{
|
||||||
|
{SPV_OPERAND_TYPE_ID, {instruction->GetSingleWordInOperand(0)}},
|
||||||
|
{SPV_OPERAND_TYPE_ID, {if_block}},
|
||||||
|
{SPV_OPERAND_TYPE_ID, {else_block}}}));
|
||||||
|
|
||||||
|
// |if_pred| will be the true block, if it has been created, the existing
|
||||||
|
// predecessor otherwise.
|
||||||
|
uint32_t if_pred =
|
||||||
|
message_.true_block_id() ? message_.true_block_id() : predecessor->id();
|
||||||
|
|
||||||
|
// |else_pred| will be the false block, if it has been created, the existing
|
||||||
|
// predecessor otherwise.
|
||||||
|
uint32_t else_pred =
|
||||||
|
message_.false_block_id() ? message_.false_block_id() : predecessor->id();
|
||||||
|
|
||||||
|
// Replace the OpSelect instruction in the merge block with an OpPhi.
|
||||||
|
// This: OpSelect %type %cond %if %else
|
||||||
|
// will become: OpPhi %type %if %if_pred %else %else_pred
|
||||||
|
instruction->SetOpcode(SpvOpPhi);
|
||||||
|
std::vector<opt::Operand> operands;
|
||||||
|
|
||||||
|
operands.emplace_back(instruction->GetInOperand(1));
|
||||||
|
operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {if_pred}});
|
||||||
|
|
||||||
|
operands.emplace_back(instruction->GetInOperand(2));
|
||||||
|
operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {else_pred}});
|
||||||
|
|
||||||
|
instruction->SetInOperands(std::move(operands));
|
||||||
|
|
||||||
|
// Invalidate all analyses, since the structure of the module was changed.
|
||||||
|
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
protobufs::Transformation
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch::ToMessage() const {
|
||||||
|
protobufs::Transformation result;
|
||||||
|
*result.mutable_replace_opselect_with_conditional_branch() = message_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
@ -0,0 +1,61 @@
|
|||||||
|
// 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_OPSELECT_WITH_CONDITIONAL_BRANCH_H
|
||||||
|
#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H
|
||||||
|
|
||||||
|
#include "source/fuzz/transformation.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
class TransformationReplaceOpSelectWithConditionalBranch
|
||||||
|
: public Transformation {
|
||||||
|
public:
|
||||||
|
explicit TransformationReplaceOpSelectWithConditionalBranch(
|
||||||
|
const protobufs::TransformationReplaceOpSelectWithConditionalBranch&
|
||||||
|
message);
|
||||||
|
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(uint32_t select_id,
|
||||||
|
uint32_t true_block_id,
|
||||||
|
uint32_t false_block_id);
|
||||||
|
|
||||||
|
// - |message_.select_id| is the result id of an OpSelect instruction.
|
||||||
|
// - The condition of the OpSelect must be a scalar boolean.
|
||||||
|
// - The OpSelect instruction is the first instruction in its block.
|
||||||
|
// - The block containing the instruction is not a merge block, and it has a
|
||||||
|
// single predecessor, which is not a header and whose last instruction is
|
||||||
|
// OpBranch.
|
||||||
|
// - Each of |message_.true_block_id| and |message_.false_block_id| is either
|
||||||
|
// 0 or a valid fresh id, and at most one of them is 0. They must be
|
||||||
|
// distinct.
|
||||||
|
bool IsApplicable(
|
||||||
|
opt::IRContext* ir_context,
|
||||||
|
const TransformationContext& transformation_context) const override;
|
||||||
|
|
||||||
|
// Replaces the OpSelect instruction with id |message_.select_id| with a
|
||||||
|
// conditional branch and an OpPhi instruction.
|
||||||
|
void Apply(opt::IRContext* ir_context,
|
||||||
|
TransformationContext* transformation_context) const override;
|
||||||
|
|
||||||
|
protobufs::Transformation ToMessage() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
protobufs::TransformationReplaceOpSelectWithConditionalBranch message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H
|
@ -83,27 +83,9 @@ bool TransformationSplitBlock::IsApplicable(
|
|||||||
|
|
||||||
// Splitting the block must not separate the definition of an OpSampledImage
|
// Splitting the block must not separate the definition of an OpSampledImage
|
||||||
// from its use: the SPIR-V data rules require them to be in the same block.
|
// from its use: the SPIR-V data rules require them to be in the same block.
|
||||||
std::set<uint32_t> sampled_image_result_ids;
|
return !fuzzerutil::
|
||||||
bool before_split = true;
|
SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse(
|
||||||
for (auto& instruction : *block_to_split) {
|
block_to_split, instruction_to_split_before);
|
||||||
if (&instruction == &*split_before) {
|
|
||||||
before_split = false;
|
|
||||||
}
|
|
||||||
if (before_split) {
|
|
||||||
if (instruction.opcode() == SpvOpSampledImage) {
|
|
||||||
sampled_image_result_ids.insert(instruction.result_id());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!instruction.WhileEachInId(
|
|
||||||
[&sampled_image_result_ids](uint32_t* id) -> bool {
|
|
||||||
return !sampled_image_result_ids.count(*id);
|
|
||||||
})) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransformationSplitBlock::Apply(
|
void TransformationSplitBlock::Apply(
|
||||||
|
@ -87,6 +87,7 @@ if (${SPIRV_BUILD_FUZZER})
|
|||||||
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_opphi_id_from_dead_predecessor_test.cpp
|
||||||
|
transformation_replace_opselect_with_conditional_branch_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,278 @@
|
|||||||
|
// 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_opselect_with_conditional_branch.h"
|
||||||
|
|
||||||
|
#include "test/fuzz/fuzz_test_util.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h"
|
||||||
|
#include "source/fuzz/pseudo_random_generator.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(TransformationReplaceOpSelectWithConditionalBranchTest, Inapplicable) {
|
||||||
|
std::string shader = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %2 "main"
|
||||||
|
OpExecutionMode %2 OriginUpperLeft
|
||||||
|
OpSource ESSL 310
|
||||||
|
%3 = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %3
|
||||||
|
%5 = OpTypeInt 32 1
|
||||||
|
%6 = OpConstant %5 1
|
||||||
|
%7 = OpConstant %5 2
|
||||||
|
%8 = OpTypeVector %5 4
|
||||||
|
%9 = OpConstantNull %8
|
||||||
|
%10 = OpConstantComposite %8 %6 %6 %7 %7
|
||||||
|
%11 = OpTypeBool
|
||||||
|
%12 = OpTypeVector %11 4
|
||||||
|
%13 = OpConstantTrue %11
|
||||||
|
%14 = OpConstantFalse %11
|
||||||
|
%15 = OpConstantComposite %12 %13 %14 %14 %13
|
||||||
|
%2 = OpFunction %3 None %4
|
||||||
|
%16 = OpLabel
|
||||||
|
%17 = OpCopyObject %5 %6
|
||||||
|
%18 = OpCopyObject %5 %7
|
||||||
|
OpBranch %19
|
||||||
|
%19 = OpLabel
|
||||||
|
%20 = OpCopyObject %5 %17
|
||||||
|
%21 = OpSelect %5 %13 %17 %18
|
||||||
|
OpBranch %22
|
||||||
|
%22 = OpLabel
|
||||||
|
%23 = OpSelect %8 %15 %9 %10
|
||||||
|
OpBranch %24
|
||||||
|
%24 = OpLabel
|
||||||
|
OpSelectionMerge %25 None
|
||||||
|
OpBranchConditional %13 %26 %27
|
||||||
|
%26 = OpLabel
|
||||||
|
%28 = OpSelect %5 %13 %17 %18
|
||||||
|
OpBranch %27
|
||||||
|
%27 = OpLabel
|
||||||
|
%29 = OpSelect %5 %13 %17 %18
|
||||||
|
OpBranch %25
|
||||||
|
%25 = OpLabel
|
||||||
|
%30 = OpSelect %5 %13 %17 %18
|
||||||
|
OpBranch %31
|
||||||
|
%31 = OpLabel
|
||||||
|
OpLoopMerge %32 %33 None
|
||||||
|
OpBranch %33
|
||||||
|
%33 = OpLabel
|
||||||
|
%34 = OpSelect %5 %13 %17 %18
|
||||||
|
OpBranchConditional %13 %31 %32
|
||||||
|
%32 = OpLabel
|
||||||
|
%35 = OpSelect %5 %13 %17 %18
|
||||||
|
OpBranch %36
|
||||||
|
%36 = OpLabel
|
||||||
|
%37 = OpSelect %5 %13 %17 %18
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
const auto env = SPV_ENV_UNIVERSAL_1_5;
|
||||||
|
const auto consumer = nullptr;
|
||||||
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
FactManager fact_manager;
|
||||||
|
spvtools::ValidatorOptions validator_options;
|
||||||
|
TransformationContext transformation_context(&fact_manager,
|
||||||
|
validator_options);
|
||||||
|
|
||||||
|
// %20 is not an OpSelect instruction.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(20, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// %21 is not the first instruction in its block.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(21, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// The condition for %23 is not a scalar, but a vector of booleans.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(23, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// The predecessor (%24) of the block containing %28 is the header of a
|
||||||
|
// selection construct and does not branch unconditionally.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(24, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// The block containing %29 has two predecessors (%24 and %26).
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(29, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// The block containing %30 is the merge block for a selection construct.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(30, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// The predecessor (%31) of the block containing %34 is a loop header.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(31, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// The block containing %35 is the merge block for a loop construct.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(35, 100, 101)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// |true_block_id| and |false_block_id| are both 0.
|
||||||
|
ASSERT_DEATH(
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(37, 0, 0).IsApplicable(
|
||||||
|
context.get(), transformation_context),
|
||||||
|
"At least one of the ids must be non-zero.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The fresh ids are not distinct.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(37, 100, 100)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
|
||||||
|
// One of the ids is not fresh.
|
||||||
|
ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(37, 100, 10)
|
||||||
|
.IsApplicable(context.get(), transformation_context));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationReplaceOpSelectWithConditionalBranchTest, Simple) {
|
||||||
|
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 = OpTypeInt 32 1
|
||||||
|
%6 = OpConstant %5 1
|
||||||
|
%7 = OpConstant %5 2
|
||||||
|
%8 = OpTypeVector %5 4
|
||||||
|
%9 = OpConstantNull %8
|
||||||
|
%10 = OpConstantComposite %8 %6 %6 %7 %7
|
||||||
|
%11 = OpTypeBool
|
||||||
|
%12 = OpTypeVector %11 4
|
||||||
|
%13 = OpConstantTrue %11
|
||||||
|
%14 = OpConstantFalse %11
|
||||||
|
%15 = OpConstantComposite %12 %13 %14 %14 %13
|
||||||
|
%2 = OpFunction %3 None %4
|
||||||
|
%16 = OpLabel
|
||||||
|
%17 = OpCopyObject %5 %6
|
||||||
|
%18 = OpCopyObject %5 %7
|
||||||
|
OpBranch %19
|
||||||
|
%19 = OpLabel
|
||||||
|
%20 = OpSelect %5 %13 %17 %18
|
||||||
|
OpSelectionMerge %21 None
|
||||||
|
OpBranchConditional %13 %22 %21
|
||||||
|
%22 = OpLabel
|
||||||
|
OpBranch %23
|
||||||
|
%23 = OpLabel
|
||||||
|
%24 = OpSelect %8 %13 %9 %10
|
||||||
|
OpBranch %21
|
||||||
|
%21 = OpLabel
|
||||||
|
OpBranch %25
|
||||||
|
%25 = OpLabel
|
||||||
|
%26 = OpSelect %5 %13 %17 %18
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
const auto env = SPV_ENV_UNIVERSAL_1_5;
|
||||||
|
const auto consumer = nullptr;
|
||||||
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
FactManager fact_manager;
|
||||||
|
spvtools::ValidatorOptions validator_options;
|
||||||
|
TransformationContext transformation_context(&fact_manager,
|
||||||
|
validator_options);
|
||||||
|
|
||||||
|
auto transformation =
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(20, 100, 101);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation.Apply(context.get(), &transformation_context);
|
||||||
|
|
||||||
|
auto transformation2 =
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(24, 0, 102);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation2.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation2.Apply(context.get(), &transformation_context);
|
||||||
|
|
||||||
|
auto transformation3 =
|
||||||
|
TransformationReplaceOpSelectWithConditionalBranch(26, 103, 0);
|
||||||
|
ASSERT_TRUE(
|
||||||
|
transformation3.IsApplicable(context.get(), transformation_context));
|
||||||
|
transformation3.Apply(context.get(), &transformation_context);
|
||||||
|
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
std::string after_transformation = 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 = OpTypeInt 32 1
|
||||||
|
%6 = OpConstant %5 1
|
||||||
|
%7 = OpConstant %5 2
|
||||||
|
%8 = OpTypeVector %5 4
|
||||||
|
%9 = OpConstantNull %8
|
||||||
|
%10 = OpConstantComposite %8 %6 %6 %7 %7
|
||||||
|
%11 = OpTypeBool
|
||||||
|
%12 = OpTypeVector %11 4
|
||||||
|
%13 = OpConstantTrue %11
|
||||||
|
%14 = OpConstantFalse %11
|
||||||
|
%15 = OpConstantComposite %12 %13 %14 %14 %13
|
||||||
|
%2 = OpFunction %3 None %4
|
||||||
|
%16 = OpLabel
|
||||||
|
%17 = OpCopyObject %5 %6
|
||||||
|
%18 = OpCopyObject %5 %7
|
||||||
|
OpSelectionMerge %19 None
|
||||||
|
OpBranchConditional %13 %100 %101
|
||||||
|
%100 = OpLabel
|
||||||
|
OpBranch %19
|
||||||
|
%101 = OpLabel
|
||||||
|
OpBranch %19
|
||||||
|
%19 = OpLabel
|
||||||
|
%20 = OpPhi %5 %17 %100 %18 %101
|
||||||
|
OpSelectionMerge %21 None
|
||||||
|
OpBranchConditional %13 %22 %21
|
||||||
|
%22 = OpLabel
|
||||||
|
OpSelectionMerge %23 None
|
||||||
|
OpBranchConditional %13 %23 %102
|
||||||
|
%102 = OpLabel
|
||||||
|
OpBranch %23
|
||||||
|
%23 = OpLabel
|
||||||
|
%24 = OpPhi %8 %9 %22 %10 %102
|
||||||
|
OpBranch %21
|
||||||
|
%21 = OpLabel
|
||||||
|
OpSelectionMerge %25 None
|
||||||
|
OpBranchConditional %13 %103 %25
|
||||||
|
%103 = OpLabel
|
||||||
|
OpBranch %25
|
||||||
|
%25 = OpLabel
|
||||||
|
%26 = OpPhi %5 %17 %103 %18 %21
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
Loading…
Reference in New Issue
Block a user