mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
parent
f2b8a4ee51
commit
615fbe6cbc
@ -114,6 +114,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_swap_commutable_operands.h
|
||||
fuzzer_pass_swap_conditional_branch_operands.h
|
||||
fuzzer_pass_toggle_access_chain_instruction.h
|
||||
fuzzer_pass_wrap_regions_in_selections.h
|
||||
fuzzer_util.h
|
||||
id_use_descriptor.h
|
||||
instruction_descriptor.h
|
||||
@ -211,6 +212,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_swap_conditional_branch_operands.h
|
||||
transformation_toggle_access_chain_instruction.h
|
||||
transformation_vector_shuffle.h
|
||||
transformation_wrap_region_in_selection.h
|
||||
uniform_buffer_element_descriptor.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
|
||||
|
||||
@ -290,6 +292,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
fuzzer_pass_swap_commutable_operands.cpp
|
||||
fuzzer_pass_swap_conditional_branch_operands.cpp
|
||||
fuzzer_pass_toggle_access_chain_instruction.cpp
|
||||
fuzzer_pass_wrap_regions_in_selections.cpp
|
||||
fuzzer_util.cpp
|
||||
id_use_descriptor.cpp
|
||||
instruction_descriptor.cpp
|
||||
@ -385,6 +388,7 @@ if(SPIRV_BUILD_FUZZER)
|
||||
transformation_swap_conditional_branch_operands.cpp
|
||||
transformation_toggle_access_chain_instruction.cpp
|
||||
transformation_vector_shuffle.cpp
|
||||
transformation_wrap_region_in_selection.cpp
|
||||
uniform_buffer_element_descriptor.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
|
||||
)
|
||||
|
@ -83,6 +83,7 @@
|
||||
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h"
|
||||
#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h"
|
||||
@ -294,6 +295,7 @@ Fuzzer::FuzzerResult Fuzzer::Run() {
|
||||
MaybeAddRepeatedPass<FuzzerPassSplitBlocks>(&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
|
||||
&pass_instances);
|
||||
MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances);
|
||||
// There is a theoretical possibility that no pass instances were created
|
||||
// until now; loop again if so.
|
||||
} while (pass_instances.GetPasses().empty());
|
||||
|
@ -134,6 +134,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
|
||||
{10, 70};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
|
||||
20, 90};
|
||||
const std::pair<uint32_t, uint32_t> kChanceOfWrappingRegionInSelection = {70,
|
||||
90};
|
||||
|
||||
// Default limits for various quantities that are chosen during fuzzing.
|
||||
// Keep them in alphabetical order.
|
||||
@ -320,6 +322,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
||||
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
|
||||
chance_of_toggling_access_chain_instruction_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
|
||||
chance_of_wrapping_region_in_selection_ =
|
||||
ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection);
|
||||
}
|
||||
|
||||
FuzzerContext::~FuzzerContext() = default;
|
||||
|
@ -307,6 +307,9 @@ class FuzzerContext {
|
||||
uint32_t GetChanceOfTogglingAccessChainInstruction() {
|
||||
return chance_of_toggling_access_chain_instruction_;
|
||||
}
|
||||
uint32_t GetChanceOfWrappingRegionInSelection() {
|
||||
return chance_of_wrapping_region_in_selection_;
|
||||
}
|
||||
|
||||
// Other functions to control transformations. Keep them in alphabetical
|
||||
// order.
|
||||
@ -472,6 +475,7 @@ class FuzzerContext {
|
||||
uint32_t chance_of_splitting_block_;
|
||||
uint32_t chance_of_swapping_conditional_branch_operands_;
|
||||
uint32_t chance_of_toggling_access_chain_instruction_;
|
||||
uint32_t chance_of_wrapping_region_in_selection_;
|
||||
|
||||
// Limits associated with various quantities for which random values are
|
||||
// chosen during fuzzing.
|
||||
|
140
source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
Normal file
140
source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// 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_wrap_regions_in_selections.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_context.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "source/fuzz/transformation_split_block.h"
|
||||
#include "source/fuzz/transformation_wrap_region_in_selection.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations)
|
||||
: FuzzerPass(ir_context, transformation_context, fuzzer_context,
|
||||
transformations) {}
|
||||
|
||||
FuzzerPassWrapRegionsInSelections::~FuzzerPassWrapRegionsInSelections() =
|
||||
default;
|
||||
|
||||
void FuzzerPassWrapRegionsInSelections::Apply() {
|
||||
for (auto& function : *GetIRContext()->module()) {
|
||||
if (!GetFuzzerContext()->ChoosePercentage(
|
||||
GetFuzzerContext()->GetChanceOfWrappingRegionInSelection())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// It is easier to select an element at random from a vector than from an
|
||||
// instruction list.
|
||||
std::vector<opt::BasicBlock*> header_block_candidates;
|
||||
for (auto& block : function) {
|
||||
header_block_candidates.push_back(&block);
|
||||
}
|
||||
|
||||
if (header_block_candidates.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to get a header block candidate that will increase the chances of the
|
||||
// transformation being applicable.
|
||||
auto* header_block_candidate = MaybeGetHeaderBlockCandidate(
|
||||
header_block_candidates[GetFuzzerContext()->RandomIndex(
|
||||
header_block_candidates)]);
|
||||
if (!header_block_candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<opt::BasicBlock*> merge_block_candidates;
|
||||
for (auto& block : function) {
|
||||
if (GetIRContext()->GetDominatorAnalysis(&function)->StrictlyDominates(
|
||||
header_block_candidate, &block) &&
|
||||
GetIRContext()
|
||||
->GetPostDominatorAnalysis(&function)
|
||||
->StrictlyDominates(&block, header_block_candidate)) {
|
||||
merge_block_candidates.push_back(&block);
|
||||
}
|
||||
}
|
||||
|
||||
if (merge_block_candidates.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to get a merge block candidate that will increase the chances of the
|
||||
// transformation being applicable.
|
||||
auto* merge_block_candidate = MaybeGetMergeBlockCandidate(
|
||||
merge_block_candidates[GetFuzzerContext()->RandomIndex(
|
||||
merge_block_candidates)]);
|
||||
if (!merge_block_candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TransformationWrapRegionInSelection::IsApplicableToBlockRange(
|
||||
GetIRContext(), header_block_candidate->id(),
|
||||
merge_block_candidate->id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This boolean constant will be used as a condition for the
|
||||
// OpBranchConditional instruction. We mark it as irrelevant to be able to
|
||||
// replace it with a more interesting value later.
|
||||
auto branch_condition = GetFuzzerContext()->ChooseEven();
|
||||
FindOrCreateBoolConstant(branch_condition, true);
|
||||
|
||||
ApplyTransformation(TransformationWrapRegionInSelection(
|
||||
header_block_candidate->id(), merge_block_candidate->id(),
|
||||
branch_condition));
|
||||
}
|
||||
}
|
||||
|
||||
opt::BasicBlock*
|
||||
FuzzerPassWrapRegionsInSelections::MaybeGetHeaderBlockCandidate(
|
||||
opt::BasicBlock* header_block_candidate) {
|
||||
// Try to create a preheader if |header_block_candidate| is a loop header.
|
||||
if (header_block_candidate->IsLoopHeader()) {
|
||||
// GetOrCreateSimpleLoopPreheader only supports reachable blocks.
|
||||
return GetIRContext()->cfg()->preds(header_block_candidate->id()).size() ==
|
||||
1
|
||||
? nullptr
|
||||
: GetOrCreateSimpleLoopPreheader(header_block_candidate->id());
|
||||
}
|
||||
|
||||
// Try to split |header_block_candidate| if it's already a header block.
|
||||
if (header_block_candidate->GetMergeInst()) {
|
||||
SplitBlockAfterOpPhiOrOpVariable(header_block_candidate->id());
|
||||
}
|
||||
|
||||
return header_block_candidate;
|
||||
}
|
||||
|
||||
opt::BasicBlock* FuzzerPassWrapRegionsInSelections::MaybeGetMergeBlockCandidate(
|
||||
opt::BasicBlock* merge_block_candidate) {
|
||||
// If |merge_block_candidate| is a merge block of some construct, try to split
|
||||
// it and return a newly created block.
|
||||
if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(
|
||||
merge_block_candidate->id())) {
|
||||
// We can't split a merge block if it's also a loop header.
|
||||
return merge_block_candidate->IsLoopHeader()
|
||||
? nullptr
|
||||
: SplitBlockAfterOpPhiOrOpVariable(merge_block_candidate->id());
|
||||
}
|
||||
|
||||
return merge_block_candidate;
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
55
source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
Normal file
55
source/fuzz/fuzzer_pass_wrap_regions_in_selections.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// 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_WRAP_REGIONS_IN_SELECTIONS_H_
|
||||
#define SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_
|
||||
|
||||
#include "source/fuzz/fuzzer_pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
// Randomly wraps a region of blocks in every function into a selection
|
||||
// construct.
|
||||
class FuzzerPassWrapRegionsInSelections : public FuzzerPass {
|
||||
public:
|
||||
FuzzerPassWrapRegionsInSelections(
|
||||
opt::IRContext* ir_context, TransformationContext* transformation_context,
|
||||
FuzzerContext* fuzzer_context,
|
||||
protobufs::TransformationSequence* transformations);
|
||||
|
||||
~FuzzerPassWrapRegionsInSelections() override;
|
||||
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
// Tries to adjust |header_block_candidate| such that
|
||||
// TransformationWrapRegionInSelection has higher chances of being
|
||||
// applied. In particular, tries to split |header_block_candidate| if it's
|
||||
// already a header block of some other construct.
|
||||
opt::BasicBlock* MaybeGetHeaderBlockCandidate(
|
||||
opt::BasicBlock* header_block_candidate);
|
||||
|
||||
// Tries to adjust |merge_block_candidate| such that
|
||||
// TransformationWrapRegionInSelection has higher chances of being
|
||||
// applied. In particular, tries to split |merge_block_candidate| if it's
|
||||
// already a merge block of some other construct.
|
||||
opt::BasicBlock* MaybeGetMergeBlockCandidate(
|
||||
opt::BasicBlock* merge_block_candidate);
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_
|
@ -67,6 +67,7 @@
|
||||
#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h"
|
||||
#include "source/fuzz/fuzzer_pass_split_blocks.h"
|
||||
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
@ -156,6 +157,7 @@ class RepeatedPassInstances {
|
||||
REPEATED_PASS_INSTANCE(ReplaceParamsWithStruct);
|
||||
REPEATED_PASS_INSTANCE(SplitBlocks);
|
||||
REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands);
|
||||
REPEATED_PASS_INSTANCE(WrapRegionsInSelections);
|
||||
#undef REPEATED_PASS_INSTANCE
|
||||
|
||||
public:
|
||||
|
@ -120,7 +120,8 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations(
|
||||
// outlining functions.
|
||||
return RandomOrderAndNonNull(
|
||||
{pass_instances_->GetDuplicateRegionsWithSelections(),
|
||||
pass_instances_->GetOutlineFunctions()});
|
||||
pass_instances_->GetOutlineFunctions(),
|
||||
pass_instances_->GetWrapRegionsInSelections()});
|
||||
}
|
||||
if (&pass == pass_instances_->GetAddLoopsToCreateIntConstantSynonyms()) {
|
||||
// - New synonyms can be applied
|
||||
@ -308,6 +309,16 @@ RepeatedPassRecommenderStandard::GetFuturePassRecommendations(
|
||||
// No obvious follow-on passes
|
||||
return {};
|
||||
}
|
||||
if (&pass == pass_instances_->GetWrapRegionsInSelections()) {
|
||||
// - This pass uses an irrelevant boolean constant - we can replace it with
|
||||
// something more interesting.
|
||||
// - We can obfuscate that very constant as well.
|
||||
// - We can flatten created selection construct.
|
||||
return RandomOrderAndNonNull(
|
||||
{pass_instances_->GetObfuscateConstants(),
|
||||
pass_instances_->GetReplaceIrrelevantIds(),
|
||||
pass_instances_->GetFlattenConditionalBranches()});
|
||||
}
|
||||
assert(false && "Unreachable: every fuzzer pass should be dealt with.");
|
||||
return {};
|
||||
}
|
||||
|
@ -498,6 +498,7 @@ message Transformation {
|
||||
TransformationFlattenConditionalBranch flatten_conditional_branch = 76;
|
||||
TransformationAddBitInstructionSynonym add_bit_instruction_synonym = 77;
|
||||
TransformationAddLoopToCreateIntConstantSynonym add_loop_to_create_int_constant_synonym = 78;
|
||||
TransformationWrapRegionInSelection wrap_region_in_selection = 79;
|
||||
// Add additional option using the next available number.
|
||||
}
|
||||
}
|
||||
@ -2072,3 +2073,33 @@ message TransformationVectorShuffle {
|
||||
repeated uint32 component = 5;
|
||||
|
||||
}
|
||||
|
||||
message TransformationWrapRegionInSelection {
|
||||
|
||||
// Transforms a single-entry-single-exit region R into
|
||||
// if (|branch_condition|) { R } else { R }
|
||||
// The entry block for R becomes a selection header and
|
||||
// the exit block - a selection merge.
|
||||
//
|
||||
// Note that the region R is not duplicated. Thus, the effect of
|
||||
// this transformation can be represented as follows:
|
||||
// entry
|
||||
// entry / \
|
||||
// | \ /
|
||||
// R --> R
|
||||
// | |
|
||||
// exit exit
|
||||
|
||||
// This behaviour is different from TransformationDuplicateRegionWithSelection
|
||||
// that copies the blocks in R.
|
||||
|
||||
// The entry block for the region R.
|
||||
uint32 region_entry_block_id = 1;
|
||||
|
||||
// The exit block for the region R.
|
||||
uint32 region_exit_block_id = 2;
|
||||
|
||||
// Boolean value for the condition expression.
|
||||
bool branch_condition = 3;
|
||||
|
||||
}
|
||||
|
@ -95,6 +95,7 @@
|
||||
#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
|
||||
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
|
||||
#include "source/fuzz/transformation_vector_shuffle.h"
|
||||
#include "source/fuzz/transformation_wrap_region_in_selection.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -342,6 +343,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
||||
message.toggle_access_chain_instruction());
|
||||
case protobufs::Transformation::TransformationCase::kVectorShuffle:
|
||||
return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
|
||||
case protobufs::Transformation::TransformationCase::kWrapRegionInSelection:
|
||||
return MakeUnique<TransformationWrapRegionInSelection>(
|
||||
message.wrap_region_in_selection());
|
||||
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
|
||||
assert(false && "An unset transformation was encountered.");
|
||||
return nullptr;
|
||||
|
166
source/fuzz/transformation_wrap_region_in_selection.cpp
Normal file
166
source/fuzz/transformation_wrap_region_in_selection.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// 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_wrap_region_in_selection.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
TransformationWrapRegionInSelection::TransformationWrapRegionInSelection(
|
||||
const protobufs::TransformationWrapRegionInSelection& message)
|
||||
: message_(message) {}
|
||||
|
||||
TransformationWrapRegionInSelection::TransformationWrapRegionInSelection(
|
||||
uint32_t region_entry_block_id, uint32_t region_exit_block_id,
|
||||
bool branch_condition) {
|
||||
message_.set_region_entry_block_id(region_entry_block_id);
|
||||
message_.set_region_exit_block_id(region_exit_block_id);
|
||||
message_.set_branch_condition(branch_condition);
|
||||
}
|
||||
|
||||
bool TransformationWrapRegionInSelection::IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const {
|
||||
// Check that it is possible to outline a region of blocks without breaking
|
||||
// domination and structured control flow rules.
|
||||
if (!IsApplicableToBlockRange(ir_context, message_.region_entry_block_id(),
|
||||
message_.region_exit_block_id())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// There must exist an irrelevant boolean constant to be used as a condition
|
||||
// in the OpBranchConditional instruction.
|
||||
return fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
|
||||
message_.branch_condition(),
|
||||
true) != 0;
|
||||
}
|
||||
|
||||
void TransformationWrapRegionInSelection::Apply(
|
||||
opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const {
|
||||
auto* new_header_block =
|
||||
ir_context->cfg()->block(message_.region_entry_block_id());
|
||||
assert(new_header_block->terminator()->opcode() == SpvOpBranch &&
|
||||
"This condition should have been checked in the IsApplicable");
|
||||
|
||||
const auto successor_id =
|
||||
new_header_block->terminator()->GetSingleWordInOperand(0);
|
||||
|
||||
// Change |entry_block|'s terminator to |OpBranchConditional|.
|
||||
new_header_block->terminator()->SetOpcode(SpvOpBranchConditional);
|
||||
new_header_block->terminator()->SetInOperands(
|
||||
{{SPV_OPERAND_TYPE_ID,
|
||||
{fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context,
|
||||
message_.branch_condition(), true)}},
|
||||
{SPV_OPERAND_TYPE_ID, {successor_id}},
|
||||
{SPV_OPERAND_TYPE_ID, {successor_id}}});
|
||||
|
||||
// Insert OpSelectionMerge before the terminator.
|
||||
new_header_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
|
||||
ir_context, SpvOpSelectionMerge, 0, 0,
|
||||
opt::Instruction::OperandList{
|
||||
{SPV_OPERAND_TYPE_ID, {message_.region_exit_block_id()}},
|
||||
{SPV_OPERAND_TYPE_SELECTION_CONTROL,
|
||||
{SpvSelectionControlMaskNone}}}));
|
||||
|
||||
// We've change the module so we must invalidate analyses.
|
||||
ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
|
||||
}
|
||||
|
||||
protobufs::Transformation TransformationWrapRegionInSelection::ToMessage()
|
||||
const {
|
||||
protobufs::Transformation result;
|
||||
*result.mutable_wrap_region_in_selection() = message_;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TransformationWrapRegionInSelection::IsApplicableToBlockRange(
|
||||
opt::IRContext* ir_context, uint32_t header_block_candidate_id,
|
||||
uint32_t merge_block_candidate_id) {
|
||||
// Check that |header_block_candidate_id| and |merge_block_candidate_id| are
|
||||
// valid.
|
||||
const auto* header_block_candidate =
|
||||
fuzzerutil::MaybeFindBlock(ir_context, header_block_candidate_id);
|
||||
if (!header_block_candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* merge_block_candidate =
|
||||
fuzzerutil::MaybeFindBlock(ir_context, merge_block_candidate_id);
|
||||
if (!merge_block_candidate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |header_block_candidate| and |merge_block_candidate| must be from the same
|
||||
// function.
|
||||
if (header_block_candidate->GetParent() !=
|
||||
merge_block_candidate->GetParent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* dominator_analysis =
|
||||
ir_context->GetDominatorAnalysis(header_block_candidate->GetParent());
|
||||
const auto* postdominator_analysis =
|
||||
ir_context->GetPostDominatorAnalysis(header_block_candidate->GetParent());
|
||||
|
||||
if (!dominator_analysis->StrictlyDominates(header_block_candidate,
|
||||
merge_block_candidate) ||
|
||||
!postdominator_analysis->StrictlyDominates(merge_block_candidate,
|
||||
header_block_candidate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |header_block_candidate| can't be a header since we are about to make it
|
||||
// one.
|
||||
if (header_block_candidate->GetMergeInst()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |header_block_candidate| must have an OpBranch terminator.
|
||||
if (header_block_candidate->terminator()->opcode() != SpvOpBranch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Every header block must have a unique merge block. Thus,
|
||||
// |merge_block_candidate| can't be a merge block of some other header.
|
||||
auto* structured_cfg = ir_context->GetStructuredCFGAnalysis();
|
||||
if (structured_cfg->IsMergeBlock(merge_block_candidate_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// |header_block_candidate|'s containing construct must also contain
|
||||
// |merge_block_candidate|.
|
||||
//
|
||||
// ContainingConstruct will return the id of a loop header for a block in the
|
||||
// loop's continue construct. Thus, we must also check the case when one of
|
||||
// the candidates is in continue construct and the other one is not.
|
||||
if (structured_cfg->ContainingConstruct(header_block_candidate_id) !=
|
||||
structured_cfg->ContainingConstruct(merge_block_candidate_id) ||
|
||||
structured_cfg->IsInContinueConstruct(header_block_candidate_id) !=
|
||||
structured_cfg->IsInContinueConstruct(merge_block_candidate_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unordered_set<uint32_t> TransformationWrapRegionInSelection::GetFreshIds()
|
||||
const {
|
||||
return std::unordered_set<uint32_t>();
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
88
source/fuzz/transformation_wrap_region_in_selection.h
Normal file
88
source/fuzz/transformation_wrap_region_in_selection.h
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// 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_WRAP_REGION_IN_SELECTION_H_
|
||||
#define SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/fuzz/transformation.h"
|
||||
#include "source/fuzz/transformation_context.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
|
||||
class TransformationWrapRegionInSelection : public Transformation {
|
||||
public:
|
||||
explicit TransformationWrapRegionInSelection(
|
||||
const protobufs::TransformationWrapRegionInSelection& message);
|
||||
|
||||
TransformationWrapRegionInSelection(uint32_t region_entry_block_id,
|
||||
uint32_t region_exit_block_id,
|
||||
bool branch_condition);
|
||||
|
||||
// - It should be possible to apply this transformation to a
|
||||
// single-exit-single-entry region of blocks dominated by
|
||||
// |region_entry_block_id| and postdominated by |region_exit_block_id|
|
||||
// (see IsApplicableToBlockRange method for further details).
|
||||
//
|
||||
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3828):
|
||||
// Consider applying this transformation to non-single-entry-single-exit
|
||||
// regions of blocks.
|
||||
// - There must exist an irrelevant boolean constant with value
|
||||
// |branch_condition|.
|
||||
bool IsApplicable(
|
||||
opt::IRContext* ir_context,
|
||||
const TransformationContext& transformation_context) const override;
|
||||
|
||||
// - Transforms |region_entry_block_id| into a selection header with both
|
||||
// branches pointing to the block's successor.
|
||||
// - |branch_condition| is used as a condition in the header's
|
||||
// OpBranchConditional instruction.
|
||||
// - Transforms |region_exit_block_id| into a merge block of the selection's
|
||||
// header.
|
||||
void Apply(opt::IRContext* ir_context,
|
||||
TransformationContext* transformation_context) const override;
|
||||
|
||||
protobufs::Transformation ToMessage() const override;
|
||||
|
||||
// Returns true if it's possible to apply this transformation to the
|
||||
// single-exit-single-entry region of blocks starting with
|
||||
// |header_block_candidate_id| and ending with |merge_block_candidate_id|.
|
||||
// Concretely:
|
||||
// - Both |header_block_candidate_id| and |merge_block_candidate_id| must be
|
||||
// result ids of some blocks in the module.
|
||||
// - Both blocks must belong to the same function.
|
||||
// - |header_block_candidate_id| must strictly dominate
|
||||
// |merge_block_candidate_id| and |merge_block_candidate_id| must strictly
|
||||
// postdominate |header_block_candidate_id|.
|
||||
// - |header_block_candidate_id| can't be a header block of any construct.
|
||||
// - |header_block_candidate_id|'s terminator must be an OpBranch.
|
||||
// - |merge_block_candidate_id| can't be a merge block of any other construct.
|
||||
// - Both |header_block_candidate_id| and |merge_block_candidate_id| must be
|
||||
// inside the same construct if any.
|
||||
static bool IsApplicableToBlockRange(opt::IRContext* ir_context,
|
||||
uint32_t header_block_candidate_id,
|
||||
uint32_t merge_block_candidate_id);
|
||||
|
||||
std::unordered_set<uint32_t> GetFreshIds() const override;
|
||||
|
||||
private:
|
||||
protobufs::TransformationWrapRegionInSelection message_;
|
||||
};
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_
|
@ -107,6 +107,7 @@ if (${SPIRV_BUILD_FUZZER})
|
||||
transformation_toggle_access_chain_instruction_test.cpp
|
||||
transformation_record_synonymous_constants_test.cpp
|
||||
transformation_vector_shuffle_test.cpp
|
||||
transformation_wrap_region_in_selection_test.cpp
|
||||
uniform_buffer_element_descriptor_test.cpp)
|
||||
|
||||
if (${SPIRV_ENABLE_LONG_FUZZER_TESTS})
|
||||
|
262
test/fuzz/transformation_wrap_region_in_selection_test.cpp
Normal file
262
test/fuzz/transformation_wrap_region_in_selection_test.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright (c) 2020 Vasyl Teliman
|
||||
//
|
||||
// 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_wrap_region_in_selection.h"
|
||||
|
||||
#include "source/fuzz/fuzzer_util.h"
|
||||
#include "source/fuzz/instruction_descriptor.h"
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace fuzz {
|
||||
namespace {
|
||||
|
||||
TEST(TransformationWrapRegionInSelectionTest, BasicTest) {
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %6
|
||||
|
||||
%6 = OpLabel
|
||||
OpSelectionMerge %12 None
|
||||
OpBranchConditional %8 %11 %12
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
|
||||
%12 = OpLabel
|
||||
OpSelectionMerge %15 None
|
||||
OpBranchConditional %8 %13 %14
|
||||
%13 = OpLabel
|
||||
OpBranch %15
|
||||
%14 = OpLabel
|
||||
OpBranch %15
|
||||
%15 = OpLabel
|
||||
OpBranch %16
|
||||
|
||||
%16 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%9 = OpFunction %2 None %3
|
||||
%10 = OpLabel
|
||||
OpBranch %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpLoopMerge %23 %22 None
|
||||
OpBranch %21
|
||||
%21 = OpLabel
|
||||
OpBranchConditional %8 %24 %23
|
||||
%24 = OpLabel
|
||||
OpBranch %22
|
||||
|
||||
; continue target
|
||||
%22 = OpLabel
|
||||
OpLoopMerge %25 %28 None
|
||||
OpBranchConditional %8 %27 %25
|
||||
%27 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
OpBranch %22
|
||||
%25 = OpLabel
|
||||
OpBranch %20
|
||||
|
||||
; merge block
|
||||
%23 = OpLabel
|
||||
OpBranch %26
|
||||
|
||||
%26 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
|
||||
spvtools::ValidatorOptions validator_options;
|
||||
TransformationContext transformation_context(
|
||||
MakeUnique<FactManager>(context.get()), validator_options);
|
||||
|
||||
// Boolean constant does not exist.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 6, false)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Irrelevant constant does not exist.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 6, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
transformation_context.GetFactManager()->AddFactIdIsIrrelevant(8);
|
||||
|
||||
// Block ids are invalid.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(100, 6, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 100, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Blocks are from different functions.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 10, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Header block candidate does not dominate merge block candidate.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(13, 16, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Header block candidate does not *strictly* dominate merge block candidate.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 5, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Merge block candidate does not postdominate header block candidate.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 16, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Header block candidate is already a header block of some other construct.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(12, 16, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Header block's terminator is not an OpBranch.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(21, 24, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Merge block candidate is already a merge block of some other construct.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(5, 15, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
// Header block candidate and merge block candidate are in different
|
||||
// constructs.
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(10, 21, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(24, 25, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(24, 22, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
ASSERT_FALSE(TransformationWrapRegionInSelection(24, 27, true)
|
||||
.IsApplicable(context.get(), transformation_context));
|
||||
|
||||
{
|
||||
// Header block candidate can be a merge block of some existing construct.
|
||||
TransformationWrapRegionInSelection transformation(15, 16, true);
|
||||
ASSERT_TRUE(
|
||||
transformation.IsApplicable(context.get(), transformation_context));
|
||||
ApplyAndCheckFreshIds(transformation, context.get(),
|
||||
&transformation_context);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
}
|
||||
{
|
||||
// Merge block candidate can be a header block of some existing construct.
|
||||
TransformationWrapRegionInSelection transformation(5, 6, true);
|
||||
ASSERT_TRUE(
|
||||
transformation.IsApplicable(context.get(), transformation_context));
|
||||
ApplyAndCheckFreshIds(transformation, context.get(),
|
||||
&transformation_context);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
}
|
||||
{
|
||||
// Wrap a loop construct.
|
||||
TransformationWrapRegionInSelection transformation(10, 26, true);
|
||||
ASSERT_TRUE(
|
||||
transformation.IsApplicable(context.get(), transformation_context));
|
||||
ApplyAndCheckFreshIds(transformation, 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 %4 "main"
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpSelectionMerge %6 None
|
||||
OpBranchConditional %8 %6 %6
|
||||
|
||||
%6 = OpLabel
|
||||
OpSelectionMerge %12 None
|
||||
OpBranchConditional %8 %11 %12
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
|
||||
%12 = OpLabel
|
||||
OpSelectionMerge %15 None
|
||||
OpBranchConditional %8 %13 %14
|
||||
%13 = OpLabel
|
||||
OpBranch %15
|
||||
%14 = OpLabel
|
||||
OpBranch %15
|
||||
|
||||
%15 = OpLabel
|
||||
OpSelectionMerge %16 None
|
||||
OpBranchConditional %8 %16 %16
|
||||
%16 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
%9 = OpFunction %2 None %3
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %26 None
|
||||
OpBranchConditional %8 %20 %20
|
||||
|
||||
%20 = OpLabel
|
||||
OpLoopMerge %23 %22 None
|
||||
OpBranch %21
|
||||
%21 = OpLabel
|
||||
OpBranchConditional %8 %24 %23
|
||||
%24 = OpLabel
|
||||
OpBranch %22
|
||||
|
||||
; continue target
|
||||
%22 = OpLabel
|
||||
OpLoopMerge %25 %28 None
|
||||
OpBranchConditional %8 %27 %25
|
||||
%27 = OpLabel
|
||||
OpBranch %28
|
||||
%28 = OpLabel
|
||||
OpBranch %22
|
||||
%25 = OpLabel
|
||||
OpBranch %20
|
||||
|
||||
; merge block
|
||||
%23 = OpLabel
|
||||
OpBranch %26
|
||||
|
||||
%26 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
Loading…
Reference in New Issue
Block a user