SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.h
Stefano Milizia f62357e7b8
spirv-fuzz: Transformation to flatten conditional branch (#3667)
This transformation, given the header of a selection construct with
branching instruction OpBranchConditional, flattens it.
Side-effecting operations such as OpLoad, OpStore and OpFunctionCall
are enclosed within smaller conditionals.
It is applicable if the construct does not contain inner selection
constructs or loops, or atomic or barrier instructions.

The corresponding fuzzer pass looks for selection headers and
tries to flatten them.

Needed for the issue #3544, but it does not fix it completely.
2020-09-15 10:31:01 +01:00

114 lines
5.4 KiB
C++

// 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_FLATTEN_CONDITIONAL_BRANCH_H
#define SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H
#include "source/fuzz/transformation.h"
namespace spvtools {
namespace fuzz {
class TransformationFlattenConditionalBranch : public Transformation {
public:
explicit TransformationFlattenConditionalBranch(
const protobufs::TransformationFlattenConditionalBranch& message);
TransformationFlattenConditionalBranch(
uint32_t header_block_id, bool true_branch_first,
const std::vector<protobufs::SideEffectWrapperInfo>&
side_effect_wrappers_info);
// - |message_.header_block_id| must be the label id of a reachable selection
// header, which ends with an OpBranchConditional instruction.
// - The header block and the merge block must describe a single-entry,
// single-exit region.
// - The region must not contain barrier or OpSampledImage instructions.
// - The region must not contain selection or loop constructs.
// - For each instruction that requires additional fresh ids, then:
// - if the instruction is mapped to the required ids for enclosing it by
// |message_.side_effect_wrapper_info|, these must be valid (the
// fresh ids must be non-zero, fresh and distinct);
// - if there is no such mapping, the transformation context must have
// overflow ids available.
bool IsApplicable(
opt::IRContext* ir_context,
const TransformationContext& transformation_context) const override;
// Flattens the selection construct with header |message_.header_block_id|,
// changing any OpPhi in the block where the flow converges to OpSelect and
// enclosing any instruction with side effects in conditionals so that
// they are only executed when they should.
void Apply(opt::IRContext* ir_context,
TransformationContext* transformation_context) const override;
protobufs::Transformation ToMessage() const override;
// Returns true if the conditional headed by |header| can be flattened,
// according to the conditions of the IsApplicable method, assuming that
// enough fresh ids would be provided. In this case, it fills the
// |instructions_that_need_ids| set with all the instructions that would
// require fresh ids.
// Returns false otherwise.
// Assumes that |header| is the header of a conditional, so its last two
// instructions are OpSelectionMerge and OpBranchConditional.
static bool GetProblematicInstructionsIfConditionalCanBeFlattened(
opt::IRContext* ir_context, opt::BasicBlock* header,
std::set<opt::Instruction*>* instructions_that_need_ids);
// Returns true iff the given instruction needs a placeholder to be enclosed
// inside a conditional. So, it returns:
// - true if the instruction has a non-void result id,
// - false if the instruction does not have a result id or has a void result
// id.
// Assumes that the instruction has side effects, requiring it to be enclosed
// inside a conditional, and that it can be enclosed inside a conditional
// keeping the module valid. Assumes that, if the instruction has a void
// result type, its result id is not used in the module.
static bool InstructionNeedsPlaceholder(opt::IRContext* ir_context,
const opt::Instruction& instruction);
private:
// Returns an unordered_map mapping instructions to the info required to
// enclose them inside a conditional. It maps the instructions to the
// corresponding entry in |message_.side_effect_wrapper_info|.
std::unordered_map<opt::Instruction*, protobufs::SideEffectWrapperInfo>
GetInstructionsToWrapperInfo(opt::IRContext* ir_context) const;
// Splits the given block, adding a new selection construct so that the given
// instruction is only executed if the boolean value of |condition_id| matches
// the value of |exec_if_cond_true|.
// Assumes that all parameters are consistent.
// 2 fresh ids are required if the instruction does not have a result id (the
// first two ids in |wrapper_info| must be valid fresh ids), 5 otherwise.
// Returns the merge block created.
opt::BasicBlock* EncloseInstructionInConditional(
opt::IRContext* ir_context, TransformationContext* transformation_context,
opt::BasicBlock* block, opt::Instruction* instruction,
const protobufs::SideEffectWrapperInfo& wrapper_info,
uint32_t condition_id, bool exec_if_cond_true) const;
// Returns true if the given instruction either has no side effects or it can
// be handled by being enclosed in a conditional.
static bool InstructionCanBeHandled(opt::IRContext* ir_context,
const opt::Instruction& instruction);
protobufs::TransformationFlattenConditionalBranch message_;
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H