Skip to content

Commit

Permalink
spirv-fuzz: Add TransformationDuplicateRegionWithSelection (#3773)
Browse files Browse the repository at this point in the history
Adds a transformation that inserts a conditional statement with a
boolean expression of arbitrary value and duplicates a given
single-entry, single-exit region, so that it is present in each
conditional branch and will be executed regardless of which branch will
be taken.

Fixes #3614.
  • Loading branch information
Antoni Karpiński authored Sep 11, 2020
1 parent 5dcb576 commit 244e6c1
Show file tree
Hide file tree
Showing 12 changed files with 2,505 additions and 1 deletion.
6 changes: 5 additions & 1 deletion source/fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_construct_composites.h
fuzzer_pass_copy_objects.h
fuzzer_pass_donate_modules.h
fuzzer_pass_duplicate_regions_with_selections.h
fuzzer_pass_inline_functions.h
fuzzer_pass_invert_comparison_operators.h
fuzzer_pass_interchange_signedness_of_integer_operands.h
Expand Down Expand Up @@ -156,6 +157,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_composite_insert.h
transformation_compute_data_synonym_fact_closure.h
transformation_context.h
transformation_duplicate_region_with_selection.h
transformation_equation_instruction.h
transformation_function_call.h
transformation_inline_function.h
Expand Down Expand Up @@ -241,6 +243,7 @@ if(SPIRV_BUILD_FUZZER)
fuzzer_pass_construct_composites.cpp
fuzzer_pass_copy_objects.cpp
fuzzer_pass_donate_modules.cpp
fuzzer_pass_duplicate_regions_with_selections.cpp
fuzzer_pass_inline_functions.cpp
fuzzer_pass_invert_comparison_operators.cpp
fuzzer_pass_interchange_signedness_of_integer_operands.cpp
Expand Down Expand Up @@ -316,7 +319,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_composite_insert.cpp
transformation_compute_data_synonym_fact_closure.cpp
transformation_context.cpp
transformation_replace_opselect_with_conditional_branch.cpp
transformation_duplicate_region_with_selection.cpp
transformation_equation_instruction.cpp
transformation_function_call.cpp
transformation_inline_function.cpp
Expand All @@ -343,6 +346,7 @@ if(SPIRV_BUILD_FUZZER)
transformation_replace_linear_algebra_instruction.cpp
transformation_replace_load_store_with_copy_memory.cpp
transformation_replace_opphi_id_from_dead_predecessor.cpp
transformation_replace_opselect_with_conditional_branch.cpp
transformation_replace_parameter_with_global.cpp
transformation_replace_params_with_struct.cpp
transformation_set_function_control.cpp
Expand Down
4 changes: 4 additions & 0 deletions source/fuzz/fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "source/fuzz/fuzzer_pass_construct_composites.h"
#include "source/fuzz/fuzzer_pass_copy_objects.h"
#include "source/fuzz/fuzzer_pass_donate_modules.h"
#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h"
#include "source/fuzz/fuzzer_pass_inline_functions.h"
#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h"
#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h"
Expand Down Expand Up @@ -272,6 +273,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
MaybeAddPass<FuzzerPassDonateModules>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out, donor_suppliers);
MaybeAddPass<FuzzerPassDuplicateRegionsWithSelections>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
MaybeAddPass<FuzzerPassInlineFunctions>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
Expand Down
4 changes: 4 additions & 0 deletions source/fuzz/fuzzer_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfChoosingWorkgroupStorageClass = {
const std::pair<uint32_t, uint32_t> kChanceOfConstructingComposite = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDonatingAdditionalModule = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfDuplicatingRegionWithSelection = {
20, 50};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperToInsertInComposite = {
30, 70};
const std::pair<uint32_t, uint32_t> kChanceOfGoingDeeperWhenMakingAccessChain =
Expand Down Expand Up @@ -230,6 +232,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject);
chance_of_donating_additional_module_ =
ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule);
chance_of_duplicating_region_with_selection_ =
ChooseBetweenMinAndMax(kChanceOfDuplicatingRegionWithSelection);
chance_of_going_deeper_to_insert_in_composite_ =
ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite);
chance_of_going_deeper_when_making_access_chain_ =
Expand Down
4 changes: 4 additions & 0 deletions source/fuzz/fuzzer_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ class FuzzerContext {
uint32_t GetChanceOfDonatingAdditionalModule() {
return chance_of_donating_additional_module_;
}
uint32_t GetChanceOfDuplicatingRegionWithSelection() {
return chance_of_duplicating_region_with_selection_;
}
uint32_t GetChanceOfGoingDeeperToInsertInComposite() {
return chance_of_going_deeper_to_insert_in_composite_;
}
Expand Down Expand Up @@ -406,6 +409,7 @@ class FuzzerContext {
uint32_t chance_of_constructing_composite_;
uint32_t chance_of_copying_object_;
uint32_t chance_of_donating_additional_module_;
uint32_t chance_of_duplicating_region_with_selection_;
uint32_t chance_of_going_deeper_to_insert_in_composite_;
uint32_t chance_of_going_deeper_when_making_access_chain_;
uint32_t chance_of_inlining_function_;
Expand Down
133 changes: 133 additions & 0 deletions source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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_duplicate_regions_with_selections.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/transformation_duplicate_region_with_selection.h"

namespace spvtools {
namespace fuzz {

FuzzerPassDuplicateRegionsWithSelections::
FuzzerPassDuplicateRegionsWithSelections(
opt::IRContext* ir_context,
TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations)
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
transformations) {}

FuzzerPassDuplicateRegionsWithSelections::
~FuzzerPassDuplicateRegionsWithSelections() = default;

void FuzzerPassDuplicateRegionsWithSelections::Apply() {
// Iterate over all of the functions in the module.
for (auto& function : *GetIRContext()->module()) {
// Randomly decide whether to apply the transformation.
if (!GetFuzzerContext()->ChoosePercentage(
GetFuzzerContext()->GetChanceOfDuplicatingRegionWithSelection())) {
continue;
}
std::vector<opt::BasicBlock*> candidate_entry_blocks;
for (auto& block : function) {
// We don't consider the first block to be the entry block, since it
// could contain OpVariable instructions that would require additional
// operations to be reassigned.
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3778):
// Consider extending this fuzzer pass to allow the first block to be
// used in duplication.
if (&block == &*function.begin()) {
continue;
}
candidate_entry_blocks.push_back(&block);
}
if (candidate_entry_blocks.empty()) {
continue;
}
// Randomly choose the entry block.
auto entry_block = candidate_entry_blocks[GetFuzzerContext()->RandomIndex(
candidate_entry_blocks)];
auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function);
auto postdominator_analysis =
GetIRContext()->GetPostDominatorAnalysis(&function);
std::vector<opt::BasicBlock*> candidate_exit_blocks;
for (auto postdominates_entry_block = entry_block;
postdominates_entry_block != nullptr;
postdominates_entry_block = postdominator_analysis->ImmediateDominator(
postdominates_entry_block)) {
// The candidate exit block must be dominated by the entry block and the
// entry block must be post-dominated by the candidate exit block. Ignore
// the block if it heads a selection construct or a loop construct.
if (dominator_analysis->Dominates(entry_block,
postdominates_entry_block) &&
!postdominates_entry_block->GetLoopMergeInst()) {
candidate_exit_blocks.push_back(postdominates_entry_block);
}
}
if (candidate_exit_blocks.empty()) {
continue;
}
// Randomly choose the exit block.
auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
candidate_exit_blocks)];

auto region_blocks =
TransformationDuplicateRegionWithSelection::GetRegionBlocks(
GetIRContext(), entry_block, exit_block);

// Construct |original_label_to_duplicate_label| by iterating over all
// blocks in the region. Construct |original_id_to_duplicate_id| and
// |original_id_to_phi_id| by iterating over all instructions in each block.
std::map<uint32_t, uint32_t> original_label_to_duplicate_label;
std::map<uint32_t, uint32_t> original_id_to_duplicate_id;
std::map<uint32_t, uint32_t> original_id_to_phi_id;
for (auto& block : region_blocks) {
original_label_to_duplicate_label[block->id()] =
GetFuzzerContext()->GetFreshId();
for (auto& instr : *block) {
if (instr.result_id()) {
original_id_to_duplicate_id[instr.result_id()] =
GetFuzzerContext()->GetFreshId();
auto final_instruction = &*exit_block->tail();
// &*exit_block->tail() is the final instruction of the region.
// The instruction is available at the end of the region if and only
// if it is available before this final instruction or it is the final
// instruction.
if ((&instr == final_instruction ||
fuzzerutil::IdIsAvailableBeforeInstruction(
GetIRContext(), final_instruction, instr.result_id()))) {
original_id_to_phi_id[instr.result_id()] =
GetFuzzerContext()->GetFreshId();
}
}
}
}
// Randomly decide between value "true" or "false" for a bool constant.
// Make sure the transformation has access to a bool constant to be used
// while creating conditional construct.
auto condition_id =
FindOrCreateBoolConstant(GetFuzzerContext()->ChooseEven(), true);

TransformationDuplicateRegionWithSelection transformation =
TransformationDuplicateRegionWithSelection(
GetFuzzerContext()->GetFreshId(), condition_id,
GetFuzzerContext()->GetFreshId(), entry_block->id(),
exit_block->id(), std::move(original_label_to_duplicate_label),
std::move(original_id_to_duplicate_id),
std::move(original_id_to_phi_id));
MaybeApplyTransformation(transformation);
}
}
} // namespace fuzz
} // namespace spvtools
42 changes: 42 additions & 0 deletions source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// 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_DUPLICATE_REGIONS_WITH_SELECTIONS_H_
#define SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_

#include "source/fuzz/fuzzer_pass.h"

namespace spvtools {
namespace fuzz {

// A fuzzer pass that iterates over the whole module. For each function it
// finds a single-entry, single-exit region with its constructs and their merge
// blocks either completely within or completely outside the region. It
// duplicates this region using the corresponding transformation.
class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass {
public:
FuzzerPassDuplicateRegionsWithSelections(
opt::IRContext* ir_context, TransformationContext* transformation_context,
FuzzerContext* fuzzer_context,
protobufs::TransformationSequence* transformations);

~FuzzerPassDuplicateRegionsWithSelections() override;

void Apply() override;
};

} // namespace fuzz
} // namespace spvtools

#endif // SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_
36 changes: 36 additions & 0 deletions source/fuzz/protobufs/spvtoolsfuzz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ message Transformation {
TransformationReplaceIrrelevantId replace_irrelevant_id = 72;
TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73;
TransformationReplaceOpSelectWithConditionalBranch replace_opselect_with_conditional_branch = 74;
TransformationDuplicateRegionWithSelection duplicate_region_with_selection = 75;
// Add additional option using the next available number.
}
}
Expand Down Expand Up @@ -1082,6 +1083,41 @@ message TransformationComputeDataSynonymFactClosure {

}

message TransformationDuplicateRegionWithSelection {

// A transformation that inserts a conditional statement with a boolean expression
// of arbitrary value and duplicates a given single-entry, single-exit region, so
// that it is present in each conditional branch and will be executed regardless
// of which branch will be taken.

// Fresh id for a label of the new entry block.
uint32 new_entry_fresh_id = 1;

// Id for a boolean expression.
uint32 condition_id = 2;

// Fresh id for a label of the merge block of the conditional.
uint32 merge_label_fresh_id = 3;

// Block id of the entry block of the original region.
uint32 entry_block_id = 4;

// Block id of the exit block of the original region.
uint32 exit_block_id = 5;

// Map that maps from a label in the original region to the corresponding label
// in the duplicated region.
repeated UInt32Pair original_label_to_duplicate_label = 6;

// Map that maps from a result id in the original region to the corresponding
// result id in the duplicated region.
repeated UInt32Pair original_id_to_duplicate_id = 7;

// Map that maps from a result id in the original region to the result id of the
// corresponding OpPhi instruction.
repeated UInt32Pair original_id_to_phi_id = 8;
}

message TransformationEquationInstruction {

// A transformation that adds an instruction to the module that defines an
Expand Down
5 changes: 5 additions & 0 deletions source/fuzz/transformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include "source/fuzz/transformation_composite_extract.h"
#include "source/fuzz/transformation_composite_insert.h"
#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h"
#include "source/fuzz/transformation_duplicate_region_with_selection.h"
#include "source/fuzz/transformation_equation_instruction.h"
#include "source/fuzz/transformation_function_call.h"
#include "source/fuzz/transformation_inline_function.h"
Expand Down Expand Up @@ -196,6 +197,10 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
kComputeDataSynonymFactClosure:
return MakeUnique<TransformationComputeDataSynonymFactClosure>(
message.compute_data_synonym_fact_closure());
case protobufs::Transformation::TransformationCase::
kDuplicateRegionWithSelection:
return MakeUnique<TransformationDuplicateRegionWithSelection>(
message.duplicate_region_with_selection());
case protobufs::Transformation::TransformationCase::kEquationInstruction:
return MakeUnique<TransformationEquationInstruction>(
message.equation_instruction());
Expand Down
Loading

0 comments on commit 244e6c1

Please sign in to comment.