| // 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( |
| protobufs::TransformationFlattenConditionalBranch message); |
| |
| TransformationFlattenConditionalBranch( |
| uint32_t header_block_id, bool true_branch_first, |
| uint32_t fresh_id_for_bvec2_selector, |
| uint32_t fresh_id_for_bvec3_selector, |
| uint32_t fresh_id_for_bvec4_selector, |
| 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 condition of the OpBranchConditional instruction must not be an |
| // irrelevant id. |
| // - 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. |
| // - The region must not define ids that are the base objects for existing |
| // synonym facts. |
| // - 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, |
| const TransformationContext& transformation_context, |
| 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); |
| |
| // Returns true if and only if the SPIR-V version is such that the arguments |
| // to OpSelect are restricted to only scalars, pointers (if the appropriate |
| // capability is enabled) and component-wise vectors. |
| static bool OpSelectArgumentsAreRestricted(opt::IRContext* ir_context); |
| |
| // Find the first block where flow converges (it is not necessarily the merge |
| // block) by walking the true branch until reaching a block that post- |
| // dominates the header. |
| // This is necessary because a potential common set of blocks at the end of |
| // the construct should not be duplicated. |
| static uint32_t FindConvergenceBlock(opt::IRContext* ir_context, |
| const opt::BasicBlock& header_block); |
| |
| 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. |
| static 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); |
| |
| // Turns every OpPhi instruction of |convergence_block| -- the convergence |
| // block for |header_block| (both in |ir_context|) into an OpSelect |
| // instruction. |
| void RewriteOpPhiInstructionsAtConvergenceBlock( |
| const opt::BasicBlock& header_block, uint32_t convergence_block_id, |
| opt::IRContext* ir_context) const; |
| |
| // Adds an OpCompositeExtract instruction to the start of |block| in |
| // |ir_context|, with result id given by |fresh_id|. The instruction will |
| // make a |dimension|-dimensional boolean vector with |
| // |branch_condition_operand| at every component. |
| static void AddBooleanVectorConstructorToBlock( |
| uint32_t fresh_id, uint32_t dimension, |
| const opt::Operand& branch_condition_operand, opt::IRContext* ir_context, |
| opt::BasicBlock* block); |
| |
| // 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_ |