transformation_outline_function.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // Copyright (c) 2019 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_OUTLINE_FUNCTION_H_
  15. #define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_
  16. #include <map>
  17. #include <set>
  18. #include <vector>
  19. #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
  20. #include "source/fuzz/transformation.h"
  21. #include "source/fuzz/transformation_context.h"
  22. #include "source/opt/ir_context.h"
  23. namespace spvtools {
  24. namespace fuzz {
  25. class TransformationOutlineFunction : public Transformation {
  26. public:
  27. explicit TransformationOutlineFunction(
  28. protobufs::TransformationOutlineFunction message);
  29. TransformationOutlineFunction(
  30. uint32_t entry_block, uint32_t exit_block,
  31. uint32_t new_function_struct_return_type_id,
  32. uint32_t new_function_type_id, uint32_t new_function_id,
  33. uint32_t new_function_region_entry_block, uint32_t new_caller_result_id,
  34. uint32_t new_callee_result_id,
  35. const std::map<uint32_t, uint32_t>& input_id_to_fresh_id,
  36. const std::map<uint32_t, uint32_t>& output_id_to_fresh_id);
  37. // - All the fresh ids occurring in the transformation must be distinct and
  38. // fresh
  39. // - |message_.entry_block| and |message_.exit_block| must form a single-entry
  40. // single-exit control flow graph region
  41. // - |message_.entry_block| must not start with OpVariable
  42. // - |message_.entry_block| must not be a loop header
  43. // - |message_.exit_block| must not be a merge block or the continue target
  44. // of a loop
  45. // - A structured control flow construct must lie either completely within the
  46. // region or completely outside it
  47. // - |message.entry_block| must not start with OpPhi; this is to keep the
  48. // transformation simple - another transformation should be used to split
  49. // a desired entry block that starts with OpPhi if needed
  50. // - |message_.input_id_to_fresh_id| must contain an entry for every id
  51. // defined outside the region but used in the region
  52. // - |message_.output_id_to_fresh_id| must contain an entry for every id
  53. // defined in the region but used outside the region
  54. bool IsApplicable(
  55. opt::IRContext* ir_context,
  56. const TransformationContext& transformation_context) const override;
  57. // - A new function with id |message_.new_function_id| is added to the module.
  58. // - If the region generates output ids, the return type of this function is
  59. // a new struct type with one field per output id, and with type id
  60. // |message_.new_function_struct_return_type|, otherwise the function return
  61. // types is void and |message_.new_function_struct_return_type| is not used.
  62. // - If the region generates input ids, the new function has one parameter per
  63. // input id. Fresh ids for these parameters are provided by
  64. // |message_.input_id_to_fresh_id|.
  65. // - Unless the type required for the new function is already known,
  66. // |message_.new_function_type_id| is used as the type id for a new function
  67. // type, and the new function uses this type.
  68. // - The new function starts with a placeholder block with id
  69. // |message_.new_function_first_block|, which jumps straight to a successor
  70. // block, to avoid violating rules on what the first block in a function may
  71. // look like.
  72. // - The outlined region is replaced with a single block, with the same id
  73. // as |message_.entry_block|, and which calls the new function, passing the
  74. // region's input ids as parameters. The result is stored in
  75. // |message_.new_caller_result_id|, which has type
  76. // |message_.new_function_struct_return_type| (unless there are
  77. // no output ids, in which case the return type is void). The components
  78. // of this returned struct are then copied out into the region's output ids.
  79. // The block ends with the merge instruction (if any) and terminator of
  80. // |message_.exit_block|.
  81. // - The body of the new function is identical to the outlined region, except
  82. // that (a) the region's entry block has id
  83. // |message_.new_function_region_entry_block|, (b) input id uses are
  84. // replaced with parameter accesses, (c) and definitions of output ids are
  85. // replaced with definitions of corresponding fresh ids provided by
  86. // |message_.output_id_to_fresh_id|, and (d) the block of the function
  87. // ends by returning a composite of type
  88. // |message_.new_function_struct_return_type| comprised of all the fresh
  89. // output ids (unless the return type is void, in which case no value is
  90. // returned.
  91. void Apply(opt::IRContext* ir_context,
  92. TransformationContext* transformation_context) const override;
  93. std::unordered_set<uint32_t> GetFreshIds() const override;
  94. protobufs::Transformation ToMessage() const override;
  95. // Returns the set of blocks dominated by |entry_block| and post-dominated
  96. // by |exit_block|.
  97. static std::set<opt::BasicBlock*> GetRegionBlocks(
  98. opt::IRContext* ir_context, opt::BasicBlock* entry_block,
  99. opt::BasicBlock* exit_block);
  100. // Yields ids that are used in |region_set| and that are either parameters
  101. // to the function containing |region_set|, or are defined by blocks of this
  102. // function that are outside |region_set|.
  103. //
  104. // Special cases: OpPhi instructions in |region_entry_block| and the
  105. // terminator of |region_exit_block| do not get outlined, therefore
  106. // - id uses in OpPhi instructions in |region_entry_block| are ignored
  107. // - id uses in the terminator instruction of |region_exit_block| are ignored
  108. static std::vector<uint32_t> GetRegionInputIds(
  109. opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
  110. opt::BasicBlock* region_exit_block);
  111. // Yields all ids that are defined in |region_set| and used outside
  112. // |region_set|.
  113. //
  114. // Special cases: for similar reasons as for |GetRegionInputIds|,
  115. // - ids defined in the region and used in the terminator of
  116. // |region_exit_block| count as output ids
  117. static std::vector<uint32_t> GetRegionOutputIds(
  118. opt::IRContext* ir_context, const std::set<opt::BasicBlock*>& region_set,
  119. opt::BasicBlock* region_exit_block);
  120. private:
  121. // Ensures that the module's id bound is at least the maximum of any fresh id
  122. // associated with the transformation.
  123. void UpdateModuleIdBoundForFreshIds(
  124. opt::IRContext* ir_context,
  125. const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
  126. const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
  127. // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert,
  128. // in the region to be outlined, all the input ids in |region_input_ids| and
  129. // the output ids in |region_output_ids| to their fresh counterparts.
  130. // Parameters |region_blocks| provides access to the blocks that must be
  131. // modified, and |original_region_exit_block| allows for some special cases
  132. // where ids should not be remapped.
  133. void RemapInputAndOutputIdsInRegion(
  134. opt::IRContext* ir_context,
  135. const opt::BasicBlock& original_region_exit_block,
  136. const std::set<opt::BasicBlock*>& region_blocks,
  137. const std::vector<uint32_t>& region_input_ids,
  138. const std::vector<uint32_t>& region_output_ids,
  139. const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
  140. const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map) const;
  141. // Produce a Function object that has the right function type and parameter
  142. // declarations. The function argument types and parameter ids are dictated
  143. // by |region_input_ids| and |input_id_to_fresh_id_map|. The function return
  144. // type is dictated by |region_output_ids|.
  145. //
  146. // A new struct type to represent the function return type, and a new function
  147. // type for the function, will be added to the module (unless suitable types
  148. // are already present).
  149. //
  150. // Facts about the function containing the outlined region that are relevant
  151. // to the new function are propagated via the vact manager in
  152. // |transformation_context|.
  153. std::unique_ptr<opt::Function> PrepareFunctionPrototype(
  154. const std::vector<uint32_t>& region_input_ids,
  155. const std::vector<uint32_t>& region_output_ids,
  156. const std::map<uint32_t, uint32_t>& input_id_to_fresh_id_map,
  157. opt::IRContext* ir_context,
  158. TransformationContext* transformation_context) const;
  159. // Creates the body of the outlined function by cloning blocks from the
  160. // original region, given by |region_blocks|, adapting the cloned version
  161. // of |original_region_exit_block| so that it returns something appropriate,
  162. // and patching up branches to |original_region_entry_block| to refer to its
  163. // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are
  164. // used to determine what the function should return. Parameter
  165. // |output_id_to_type_id| provides the type of each output id.
  166. //
  167. // The |transformation_context| argument allow facts about blocks being
  168. // outlined, e.g. whether they are dead blocks, to be asserted about blocks
  169. // that get created during outlining.
  170. void PopulateOutlinedFunction(
  171. const opt::BasicBlock& original_region_entry_block,
  172. const opt::BasicBlock& original_region_exit_block,
  173. const std::set<opt::BasicBlock*>& region_blocks,
  174. const std::vector<uint32_t>& region_output_ids,
  175. const std::map<uint32_t, uint32_t>& output_id_to_type_id,
  176. const std::map<uint32_t, uint32_t>& output_id_to_fresh_id_map,
  177. opt::IRContext* ir_context, opt::Function* outlined_function) const;
  178. // Shrinks the outlined region, given by |region_blocks|, down to the single
  179. // block |original_region_entry_block|. This block is itself shrunk to just
  180. // contain:
  181. // - any OpPhi instructions that were originally present
  182. // - a call to the outlined function, with parameters provided by
  183. // |region_input_ids|
  184. // - instructions to route components of the call's return value into
  185. // |region_output_ids|
  186. // - The merge instruction (if any) and terminator of the original region's
  187. // exit block, given by |cloned_exit_block_merge| and
  188. // |cloned_exit_block_terminator|
  189. // Parameters |output_id_to_type_id| and |return_type_id| provide the
  190. // provide types for the region's output ids, and the return type of the
  191. // outlined function: as the module is in an inconsistent state when this
  192. // function is called, this information cannot be gotten from the def-use
  193. // manager.
  194. void ShrinkOriginalRegion(
  195. opt::IRContext* ir_context,
  196. const std::set<opt::BasicBlock*>& region_blocks,
  197. const std::vector<uint32_t>& region_input_ids,
  198. const std::vector<uint32_t>& region_output_ids,
  199. const std::map<uint32_t, uint32_t>& output_id_to_type_id,
  200. uint32_t return_type_id,
  201. std::unique_ptr<opt::Instruction> cloned_exit_block_merge,
  202. std::unique_ptr<opt::Instruction> cloned_exit_block_terminator,
  203. opt::BasicBlock* original_region_entry_block) const;
  204. protobufs::TransformationOutlineFunction message_;
  205. };
  206. } // namespace fuzz
  207. } // namespace spvtools
  208. #endif // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_