SPIRV-Tools/source/fuzz/transformation_flatten_conditional_branch.h
Alastair Donaldson fcb22ecf0f
spirv-fuzz: Report fresh ids in transformations (#3856)
Adds a virtual method, GetFreshIds(), to Transformation. Every
transformation uses this to indicate which ids in its protobuf message
are fresh ids. This means that when replaying a sequence of
transformations the replayer can obtain a smallest id that is not in
use by the module already and that will not be used by any
transformation by necessity. Ids greater than or equal to this id
can be used as overflow ids.

Fixes #3851.
2020-09-29 22:12:49 +01:00

123 lines
5.7 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;
std::unordered_set<uint32_t> GetFreshIds() 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.
//
// |dead_blocks| and |irrelevant_ids| are used to record the ids of blocks
// and instructions for which dead block and irrelevant id facts should
// ultimately be created.
opt::BasicBlock* EncloseInstructionInConditional(
opt::IRContext* ir_context,
const TransformationContext& transformation_context,
opt::BasicBlock* block, opt::Instruction* instruction,
const protobufs::SideEffectWrapperInfo& wrapper_info,
uint32_t condition_id, bool exec_if_cond_true,
std::vector<uint32_t>* dead_blocks,
std::vector<uint32_t>* irrelevant_ids) 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