control_dependence.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Copyright (c) 2021 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. #include "source/opt/control_dependence.h"
  15. #include <cassert>
  16. #include <tuple>
  17. #include <utility>
  18. #include <vector>
  19. #include "source/opt/basic_block.h"
  20. #include "source/opt/cfg.h"
  21. #include "source/opt/dominator_analysis.h"
  22. #include "source/opt/function.h"
  23. #include "source/opt/instruction.h"
  24. #include "spirv/unified1/spirv.h"
  25. // Computes the control dependence graph (CDG) using the algorithm in Cytron
  26. // 1991, "Efficiently Computing Static Single Assignment Form and the Control
  27. // Dependence Graph." It relies on the fact that the control dependence sources
  28. // (blocks on which a block is control dependent) are exactly the post-dominance
  29. // frontier for that block. The explanation and proofs are given in Section 6 of
  30. // that paper.
  31. // Link: https://www.cs.utexas.edu/~pingali/CS380C/2010/papers/ssaCytron.pdf
  32. //
  33. // The algorithm in Section 4.2 of the same paper is used to construct the
  34. // dominance frontier. It uses the post-dominance tree, which is available in
  35. // the IR context.
  36. namespace spvtools {
  37. namespace opt {
  38. constexpr uint32_t ControlDependenceAnalysis::kPseudoEntryBlock;
  39. uint32_t ControlDependence::GetConditionID(const CFG& cfg) const {
  40. if (source_bb_id() == 0) {
  41. // Entry dependence; return 0.
  42. return 0;
  43. }
  44. const BasicBlock* source_bb = cfg.block(source_bb_id());
  45. const Instruction* branch = source_bb->terminator();
  46. assert((branch->opcode() == SpvOpBranchConditional ||
  47. branch->opcode() == SpvOpSwitch) &&
  48. "invalid control dependence; last instruction must be conditional "
  49. "branch or switch");
  50. return branch->GetSingleWordInOperand(0);
  51. }
  52. bool ControlDependence::operator<(const ControlDependence& other) const {
  53. return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) <
  54. std::tie(other.source_bb_id_, other.target_bb_id_,
  55. other.branch_target_bb_id_);
  56. }
  57. bool ControlDependence::operator==(const ControlDependence& other) const {
  58. return std::tie(source_bb_id_, target_bb_id_, branch_target_bb_id_) ==
  59. std::tie(other.source_bb_id_, other.target_bb_id_,
  60. other.branch_target_bb_id_);
  61. }
  62. std::ostream& operator<<(std::ostream& os, const ControlDependence& dep) {
  63. os << dep.source_bb_id() << "->" << dep.target_bb_id();
  64. if (dep.branch_target_bb_id() != dep.target_bb_id()) {
  65. os << " through " << dep.branch_target_bb_id();
  66. }
  67. return os;
  68. }
  69. void ControlDependenceAnalysis::ComputePostDominanceFrontiers(
  70. const CFG& cfg, const PostDominatorAnalysis& pdom) {
  71. // Compute post-dominance frontiers (reverse graph).
  72. // The dominance frontier for a block X is equal to (Equation 4)
  73. // DF_local(X) U { B in DF_up(Z) | X = ipdom(Z) }
  74. // (ipdom(Z) is the immediate post-dominator of Z.)
  75. // where
  76. // DF_local(X) = { Y | X -> Y in CFG, X does not strictly post-dominate Y }
  77. // represents the contribution of X's predecessors to the DF, and
  78. // DF_up(Z) = { Y | Y in DF(Z), ipdom(Z) does not strictly post-dominate Y }
  79. // (note: ipdom(Z) = X.)
  80. // represents the contribution of a block to its immediate post-
  81. // dominator's DF.
  82. // This is computed in one pass through a post-order traversal of the
  83. // post-dominator tree.
  84. // Assert that there is a block other than the pseudo exit in the pdom tree,
  85. // as we need one to get the function entry point (as the pseudo exit is not
  86. // actually part of the function.)
  87. assert(!cfg.IsPseudoExitBlock(pdom.GetDomTree().post_begin()->bb_));
  88. Function* function = pdom.GetDomTree().post_begin()->bb_->GetParent();
  89. uint32_t function_entry = function->entry()->id();
  90. // Explicitly initialize pseudo-entry block, as it doesn't depend on anything,
  91. // so it won't be initialized in the following loop.
  92. reverse_nodes_[kPseudoEntryBlock] = {};
  93. for (auto it = pdom.GetDomTree().post_cbegin();
  94. it != pdom.GetDomTree().post_cend(); ++it) {
  95. ComputePostDominanceFrontierForNode(cfg, pdom, function_entry, *it);
  96. }
  97. }
  98. void ControlDependenceAnalysis::ComputePostDominanceFrontierForNode(
  99. const CFG& cfg, const PostDominatorAnalysis& pdom, uint32_t function_entry,
  100. const DominatorTreeNode& pdom_node) {
  101. const uint32_t label = pdom_node.id();
  102. ControlDependenceList& edges = reverse_nodes_[label];
  103. for (uint32_t pred : cfg.preds(label)) {
  104. if (!pdom.StrictlyDominates(label, pred)) {
  105. edges.push_back(ControlDependence(pred, label));
  106. }
  107. }
  108. if (label == function_entry) {
  109. // Add edge from pseudo-entry to entry.
  110. // In CDG construction, an edge is added from entry to exit, so only the
  111. // exit node can post-dominate entry.
  112. edges.push_back(ControlDependence(kPseudoEntryBlock, label));
  113. }
  114. for (DominatorTreeNode* child : pdom_node) {
  115. // Note: iterate dependences by value, as we need a copy.
  116. for (const ControlDependence& dep : reverse_nodes_[child->id()]) {
  117. // Special-case pseudo-entry, as above.
  118. if (dep.source_bb_id() == kPseudoEntryBlock ||
  119. !pdom.StrictlyDominates(label, dep.source_bb_id())) {
  120. edges.push_back(ControlDependence(dep.source_bb_id(), label,
  121. dep.branch_target_bb_id()));
  122. }
  123. }
  124. }
  125. }
  126. void ControlDependenceAnalysis::ComputeControlDependenceGraph(
  127. const CFG& cfg, const PostDominatorAnalysis& pdom) {
  128. ComputePostDominanceFrontiers(cfg, pdom);
  129. ComputeForwardGraphFromReverse();
  130. }
  131. void ControlDependenceAnalysis::ComputeForwardGraphFromReverse() {
  132. for (const auto& entry : reverse_nodes_) {
  133. // Ensure an entry is created for each node.
  134. forward_nodes_[entry.first];
  135. for (const ControlDependence& dep : entry.second) {
  136. forward_nodes_[dep.source_bb_id()].push_back(dep);
  137. }
  138. }
  139. }
  140. } // namespace opt
  141. } // namespace spvtools