transformation_flatten_conditional_branch.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Copyright (c) 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_
  15. #define SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_
  16. #include "source/fuzz/transformation.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. class TransformationFlattenConditionalBranch : public Transformation {
  20. public:
  21. explicit TransformationFlattenConditionalBranch(
  22. protobufs::TransformationFlattenConditionalBranch message);
  23. TransformationFlattenConditionalBranch(
  24. uint32_t header_block_id, bool true_branch_first,
  25. uint32_t fresh_id_for_bvec2_selector,
  26. uint32_t fresh_id_for_bvec3_selector,
  27. uint32_t fresh_id_for_bvec4_selector,
  28. const std::vector<protobufs::SideEffectWrapperInfo>&
  29. side_effect_wrappers_info);
  30. // - |message_.header_block_id| must be the label id of a reachable selection
  31. // header, which ends with an OpBranchConditional instruction.
  32. // - The condition of the OpBranchConditional instruction must not be an
  33. // irrelevant id.
  34. // - The header block and the merge block must describe a single-entry,
  35. // single-exit region.
  36. // - The region must not contain barrier or OpSampledImage instructions.
  37. // - The region must not contain selection or loop constructs.
  38. // - The region must not define ids that are the base objects for existing
  39. // synonym facts.
  40. // - For each instruction that requires additional fresh ids, then:
  41. // - if the instruction is mapped to the required ids for enclosing it by
  42. // |message_.side_effect_wrapper_info|, these must be valid (the
  43. // fresh ids must be non-zero, fresh and distinct);
  44. // - if there is no such mapping, the transformation context must have
  45. // overflow ids available.
  46. bool IsApplicable(
  47. opt::IRContext* ir_context,
  48. const TransformationContext& transformation_context) const override;
  49. // Flattens the selection construct with header |message_.header_block_id|,
  50. // changing any OpPhi in the block where the flow converges to OpSelect and
  51. // enclosing any instruction with side effects in conditionals so that
  52. // they are only executed when they should.
  53. void Apply(opt::IRContext* ir_context,
  54. TransformationContext* transformation_context) const override;
  55. std::unordered_set<uint32_t> GetFreshIds() const override;
  56. protobufs::Transformation ToMessage() const override;
  57. // Returns true if the conditional headed by |header| can be flattened,
  58. // according to the conditions of the IsApplicable method, assuming that
  59. // enough fresh ids would be provided. In this case, it fills the
  60. // |instructions_that_need_ids| set with all the instructions that would
  61. // require fresh ids.
  62. // Returns false otherwise.
  63. // Assumes that |header| is the header of a conditional, so its last two
  64. // instructions are OpSelectionMerge and OpBranchConditional.
  65. static bool GetProblematicInstructionsIfConditionalCanBeFlattened(
  66. opt::IRContext* ir_context, opt::BasicBlock* header,
  67. const TransformationContext& transformation_context,
  68. std::set<opt::Instruction*>* instructions_that_need_ids);
  69. // Returns true iff the given instruction needs a placeholder to be enclosed
  70. // inside a conditional. So, it returns:
  71. // - true if the instruction has a non-void result id,
  72. // - false if the instruction does not have a result id or has a void result
  73. // id.
  74. // Assumes that the instruction has side effects, requiring it to be enclosed
  75. // inside a conditional, and that it can be enclosed inside a conditional
  76. // keeping the module valid. Assumes that, if the instruction has a void
  77. // result type, its result id is not used in the module.
  78. static bool InstructionNeedsPlaceholder(opt::IRContext* ir_context,
  79. const opt::Instruction& instruction);
  80. // Returns true if and only if the SPIR-V version is such that the arguments
  81. // to OpSelect are restricted to only scalars, pointers (if the appropriate
  82. // capability is enabled) and component-wise vectors.
  83. static bool OpSelectArgumentsAreRestricted(opt::IRContext* ir_context);
  84. // Find the first block where flow converges (it is not necessarily the merge
  85. // block) by walking the true branch until reaching a block that post-
  86. // dominates the header.
  87. // This is necessary because a potential common set of blocks at the end of
  88. // the construct should not be duplicated.
  89. static uint32_t FindConvergenceBlock(opt::IRContext* ir_context,
  90. const opt::BasicBlock& header_block);
  91. private:
  92. // Returns an unordered_map mapping instructions to the info required to
  93. // enclose them inside a conditional. It maps the instructions to the
  94. // corresponding entry in |message_.side_effect_wrapper_info|.
  95. std::unordered_map<opt::Instruction*, protobufs::SideEffectWrapperInfo>
  96. GetInstructionsToWrapperInfo(opt::IRContext* ir_context) const;
  97. // Splits the given block, adding a new selection construct so that the given
  98. // instruction is only executed if the boolean value of |condition_id| matches
  99. // the value of |exec_if_cond_true|.
  100. // Assumes that all parameters are consistent.
  101. // 2 fresh ids are required if the instruction does not have a result id (the
  102. // first two ids in |wrapper_info| must be valid fresh ids), 5 otherwise.
  103. // Returns the merge block created.
  104. //
  105. // |dead_blocks| and |irrelevant_ids| are used to record the ids of blocks
  106. // and instructions for which dead block and irrelevant id facts should
  107. // ultimately be created.
  108. static opt::BasicBlock* EncloseInstructionInConditional(
  109. opt::IRContext* ir_context,
  110. const TransformationContext& transformation_context,
  111. opt::BasicBlock* block, opt::Instruction* instruction,
  112. const protobufs::SideEffectWrapperInfo& wrapper_info,
  113. uint32_t condition_id, bool exec_if_cond_true,
  114. std::vector<uint32_t>* dead_blocks,
  115. std::vector<uint32_t>* irrelevant_ids);
  116. // Turns every OpPhi instruction of |convergence_block| -- the convergence
  117. // block for |header_block| (both in |ir_context|) into an OpSelect
  118. // instruction.
  119. void RewriteOpPhiInstructionsAtConvergenceBlock(
  120. const opt::BasicBlock& header_block, uint32_t convergence_block_id,
  121. opt::IRContext* ir_context) const;
  122. // Adds an OpCompositeExtract instruction to the start of |block| in
  123. // |ir_context|, with result id given by |fresh_id|. The instruction will
  124. // make a |dimension|-dimensional boolean vector with
  125. // |branch_condition_operand| at every component.
  126. static void AddBooleanVectorConstructorToBlock(
  127. uint32_t fresh_id, uint32_t dimension,
  128. const opt::Operand& branch_condition_operand, opt::IRContext* ir_context,
  129. opt::BasicBlock* block);
  130. // Returns true if the given instruction either has no side effects or it can
  131. // be handled by being enclosed in a conditional.
  132. static bool InstructionCanBeHandled(opt::IRContext* ir_context,
  133. const opt::Instruction& instruction);
  134. protobufs::TransformationFlattenConditionalBranch message_;
  135. };
  136. } // namespace fuzz
  137. } // namespace spvtools
  138. #endif // SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_