invocation_interlock_placement_pass.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Copyright (c) 2023 Google Inc.
  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_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
  15. #define SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_
  16. #include <algorithm>
  17. #include <array>
  18. #include <functional>
  19. #include <optional>
  20. #include <unordered_map>
  21. #include <unordered_set>
  22. #include "source/enum_set.h"
  23. #include "source/extensions.h"
  24. #include "source/opt/ir_context.h"
  25. #include "source/opt/module.h"
  26. #include "source/opt/pass.h"
  27. #include "source/spirv_target_env.h"
  28. namespace spvtools {
  29. namespace opt {
  30. // This pass will ensure that an entry point will only have at most one
  31. // OpBeginInterlockInvocationEXT and one OpEndInterlockInvocationEXT, in that
  32. // order
  33. class InvocationInterlockPlacementPass : public Pass {
  34. public:
  35. InvocationInterlockPlacementPass() {}
  36. InvocationInterlockPlacementPass(const InvocationInterlockPlacementPass&) =
  37. delete;
  38. InvocationInterlockPlacementPass(InvocationInterlockPlacementPass&&) = delete;
  39. const char* name() const override { return "dedupe-interlock-invocation"; }
  40. Status Process() override;
  41. private:
  42. using BlockSet = std::unordered_set<uint32_t>;
  43. // Specifies whether a function originally had a begin or end instruction.
  44. struct ExtractionResult {
  45. bool had_begin : 1;
  46. bool had_end : 2;
  47. };
  48. // Check if a block has only a single next block, depending on the directing
  49. // that we are traversing the CFG. If reverse_cfg is true, we are walking
  50. // forward through the CFG, and will return if the block has only one
  51. // successor. Otherwise, we are walking backward through the CFG, and will
  52. // return if the block has only one predecessor.
  53. bool hasSingleNextBlock(uint32_t block_id, bool reverse_cfg);
  54. // Iterate over each of a block's predecessors or successors, depending on
  55. // direction. If reverse_cfg is true, we are walking forward through the CFG,
  56. // and need to iterate over the successors. Otherwise, we are walking backward
  57. // through the CFG, and need to iterate over the predecessors.
  58. void forEachNext(uint32_t block_id, bool reverse_cfg,
  59. std::function<void(uint32_t)> f);
  60. // Add either a begin or end instruction to the edge of the basic block. If
  61. // at_end is true, add the instruction to the end of the block; otherwise add
  62. // the instruction to the beginning of the basic block.
  63. void addInstructionAtBlockBoundary(BasicBlock* block, spv::Op opcode,
  64. bool at_end);
  65. // Remove every OpBeginInvocationInterlockEXT instruction in block after the
  66. // first. Returns whether any instructions were removed.
  67. bool killDuplicateBegin(BasicBlock* block);
  68. // Remove every OpBeginInvocationInterlockEXT instruction in block before the
  69. // last. Returns whether any instructions were removed.
  70. bool killDuplicateEnd(BasicBlock* block);
  71. // Records whether a function will potentially execute a begin or end
  72. // instruction.
  73. void recordBeginOrEndInFunction(Function* func);
  74. // Recursively removes any begin or end instructions from func and any
  75. // function func calls. Returns whether any instructions were removed.
  76. bool removeBeginAndEndInstructionsFromFunction(Function* func);
  77. // For every function call in any of the passed blocks, move any begin or end
  78. // instructions outside of the function call. Returns whether any extractions
  79. // occurred.
  80. bool extractInstructionsFromCalls(std::vector<BasicBlock*> blocks);
  81. // Finds the sets of blocks that contain OpBeginInvocationInterlockEXT and
  82. // OpEndInvocationInterlockEXT, storing them in the member variables begin_
  83. // and end_ respectively.
  84. void recordExistingBeginAndEndBlock(std::vector<BasicBlock*> blocks);
  85. // Compute the set of blocks including or after the barrier instruction, and
  86. // the set of blocks with any previous blocks inside the barrier instruction.
  87. // If reverse_cfg is true, move forward through the CFG, computing
  88. // after_begin_ and predecessors_after_begin_computing after_begin_ and
  89. // predecessors_after_begin_, otherwise, move backward through the CFG,
  90. // computing before_end_ and successors_before_end_.
  91. BlockSet computeReachableBlocks(BlockSet& in_set,
  92. const BlockSet& starting_nodes,
  93. bool reverse_cfg);
  94. // Remove unneeded begin and end instructions in block.
  95. bool removeUnneededInstructions(BasicBlock* block);
  96. // Given a block which branches to multiple successors, and a specific
  97. // successor, creates a new empty block, and update the branch instruction to
  98. // branch to the new block instead.
  99. BasicBlock* splitEdge(BasicBlock* block, uint32_t succ_id);
  100. // For the edge from block to next_id, places a begin or end instruction on
  101. // the edge, based on the direction we are walking the CFG, specified in
  102. // reverse_cfg.
  103. bool placeInstructionsForEdge(BasicBlock* block, uint32_t next_id,
  104. BlockSet& inside, BlockSet& previous_inside,
  105. spv::Op opcode, bool reverse_cfg);
  106. // Calls placeInstructionsForEdge for each edge in block.
  107. bool placeInstructions(BasicBlock* block);
  108. // Processes a single fragment shader entry function.
  109. bool processFragmentShaderEntry(Function* entry_func);
  110. // Returns whether the module has the SPV_EXT_fragment_shader_interlock
  111. // extension and one of the FragmentShader*InterlockEXT capabilities.
  112. bool isFragmentShaderInterlockEnabled();
  113. // Maps a function to whether that function originally held a begin or end
  114. // instruction.
  115. std::unordered_map<Function*, ExtractionResult> extracted_functions_;
  116. // The set of blocks which have an OpBeginInvocationInterlockEXT instruction.
  117. BlockSet begin_;
  118. // The set of blocks which have an OpEndInvocationInterlockEXT instruction.
  119. BlockSet end_;
  120. // The set of blocks which either have a begin instruction, or have a
  121. // predecessor which has a begin instruction.
  122. BlockSet after_begin_;
  123. // The set of blocks which either have an end instruction, or have a successor
  124. // which have an end instruction.
  125. BlockSet before_end_;
  126. // The set of blocks which have a predecessor in after_begin_.
  127. BlockSet predecessors_after_begin_;
  128. // The set of blocks which have a successor in before_end_.
  129. BlockSet successors_before_end_;
  130. };
  131. } // namespace opt
  132. } // namespace spvtools
  133. #endif // SOURCE_OPT_DEDUPE_INTERLOCK_INVOCATION_PASS_H_