| // Copyright (c) 2021 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_OPT_CONTROL_DEPENDENCE_H_ |
| #define SOURCE_OPT_CONTROL_DEPENDENCE_H_ |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <functional> |
| #include <ostream> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "source/opt/cfg.h" |
| #include "source/opt/dominator_analysis.h" |
| |
| namespace spvtools { |
| namespace opt { |
| |
| class ControlDependence { |
| public: |
| // The label of the source of this dependence, i.e. the block on which the |
| // target is dependent on. |
| // A |source_bb_id| of 0 represents an "entry" dependence, meaning that the |
| // execution of |target_bb_id| is only dependent on entry to the function. |
| uint32_t source_bb_id() const { return source_bb_id_; } |
| // The label of the target of this dependence, i.e. the block which is |
| // dependent on the source. |
| uint32_t target_bb_id() const { return target_bb_id_; } |
| // The label of the target of the *branch* for this dependence. |
| // Equal to the ID of the entry block for entry dependences. |
| // |
| // For example, for the partial CFG pictured below: |
| // 1 ---> 2 ---> 4 ---> 6 |
| // \ \ ^ |
| // \-> 3 \-> 5 -----/ |
| // Block 6 is control dependent on block 1, but this dependence comes from the |
| // branch 1 -> 2, so in this case the branch target ID would be 2. |
| uint32_t branch_target_bb_id() const { return branch_target_bb_id_; } |
| |
| // Create a direct control dependence from BB ID |source| to |target|. |
| ControlDependence(uint32_t source, uint32_t target) |
| : source_bb_id_(source), |
| target_bb_id_(target), |
| branch_target_bb_id_(target) {} |
| // Create a control dependence from BB ID |source| to |target| through the |
| // branch from |source| to |branch_target|. |
| ControlDependence(uint32_t source, uint32_t target, uint32_t branch_target) |
| : source_bb_id_(source), |
| target_bb_id_(target), |
| branch_target_bb_id_(branch_target) {} |
| |
| // Gets the ID of the conditional value for the branch corresponding to this |
| // control dependence. This is the first input operand for both |
| // OpConditionalBranch and OpSwitch. |
| // Returns 0 for entry dependences. |
| uint32_t GetConditionID(const CFG& cfg) const; |
| |
| bool operator==(const ControlDependence& other) const; |
| bool operator!=(const ControlDependence& other) const { |
| return !(*this == other); |
| } |
| |
| // Comparison operators, ordered lexicographically. Total ordering. |
| bool operator<(const ControlDependence& other) const; |
| bool operator>(const ControlDependence& other) const { return other < *this; } |
| bool operator<=(const ControlDependence& other) const { |
| return !(*this > other); |
| } |
| bool operator>=(const ControlDependence& other) const { |
| return !(*this < other); |
| } |
| |
| private: |
| uint32_t source_bb_id_; |
| uint32_t target_bb_id_; |
| uint32_t branch_target_bb_id_; |
| }; |
| |
| // Prints |dep| to |os| in a human-readable way. For example, |
| // 1->2 (target_bb_id = branch_target_bb_id = 2) |
| // 3->4 through 5 (target_bb_id = 4, branch_target_bb_id = 5) |
| std::ostream& operator<<(std::ostream& os, const ControlDependence& dep); |
| |
| // Represents the control dependence graph. A basic block is control dependent |
| // on another if the result of that block (e.g. the condition of a conditional |
| // branch) influences whether it is executed or not. More formally, a block A is |
| // control dependent on B iff: |
| // 1. there exists a path from A to the exit node that does *not* go through B |
| // (i.e., A does not postdominate B), and |
| // 2. there exists a path B -> b_1 -> ... -> b_n -> A such that A post-dominates |
| // all nodes b_i. |
| class ControlDependenceAnalysis { |
| public: |
| // Map basic block labels to control dependencies/dependents. |
| // Not guaranteed to be in any particular order. |
| using ControlDependenceList = std::vector<ControlDependence>; |
| using ControlDependenceListMap = |
| std::unordered_map<uint32_t, ControlDependenceList>; |
| |
| // 0, the label number for the pseudo entry block. |
| // All control dependences on the pseudo entry block are of type kEntry, and |
| // vice versa. |
| static constexpr uint32_t kPseudoEntryBlock = 0; |
| |
| // Build the control dependence graph for the given control flow graph |cfg| |
| // and corresponding post-dominator analysis |pdom|. |
| void ComputeControlDependenceGraph(const CFG& cfg, |
| const PostDominatorAnalysis& pdom); |
| |
| // Get the list of the nodes that depend on a block. |
| // Return value is not guaranteed to be in any particular order. |
| const ControlDependenceList& GetDependenceTargets(uint32_t block) const { |
| return forward_nodes_.at(block); |
| } |
| |
| // Get the list of the nodes on which a block depends on. |
| // Return value is not guaranteed to be in any particular order. |
| const ControlDependenceList& GetDependenceSources(uint32_t block) const { |
| return reverse_nodes_.at(block); |
| } |
| |
| // Runs the function |f| on each block label in the CDG. If any iteration |
| // returns false, immediately stops iteration and returns false. Otherwise |
| // returns true. Nodes are iterated in some undefined order, including the |
| // pseudo-entry block. |
| bool WhileEachBlockLabel(std::function<bool(uint32_t)> f) const { |
| for (const auto& entry : forward_nodes_) { |
| if (!f(entry.first)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Runs the function |f| on each block label in the CDG. Nodes are iterated in |
| // some undefined order, including the pseudo-entry block. |
| void ForEachBlockLabel(std::function<void(uint32_t)> f) const { |
| WhileEachBlockLabel([&f](uint32_t label) { |
| f(label); |
| return true; |
| }); |
| } |
| |
| // Returns true if the block |id| exists in the control dependence graph. |
| // This can be false even if the block exists in the function when it is part |
| // of an infinite loop, since it is not part of the post-dominator tree. |
| bool HasBlock(uint32_t id) const { return forward_nodes_.count(id) > 0; } |
| |
| // Returns true if block |a| is dependent on block |b|. |
| bool IsDependent(uint32_t a, uint32_t b) const { |
| if (!HasBlock(a)) return false; |
| // BBs tend to have more dependents (targets) than they are dependent on |
| // (sources), so search sources. |
| const ControlDependenceList& a_sources = GetDependenceSources(a); |
| return std::find_if(a_sources.begin(), a_sources.end(), |
| [b](const ControlDependence& dep) { |
| return dep.source_bb_id() == b; |
| }) != a_sources.end(); |
| } |
| |
| private: |
| // Computes the post-dominance frontiers (i.e. the reverse CDG) for each node |
| // in the post-dominator tree. Only modifies reverse_nodes_; forward_nodes_ is |
| // not modified. |
| void ComputePostDominanceFrontiers(const CFG& cfg, |
| const PostDominatorAnalysis& pdom); |
| // Computes the post-dominance frontier for a specific node |pdom_node| in the |
| // post-dominator tree. Result is placed in reverse_nodes_[pdom_node.id()]. |
| void ComputePostDominanceFrontierForNode(const CFG& cfg, |
| const PostDominatorAnalysis& pdom, |
| uint32_t function_entry, |
| const DominatorTreeNode& pdom_node); |
| |
| // Computes the forward graph (forward_nodes_) from the reverse graph |
| // (reverse_nodes_). |
| void ComputeForwardGraphFromReverse(); |
| |
| ControlDependenceListMap forward_nodes_; |
| ControlDependenceListMap reverse_nodes_; |
| }; |
| |
| } // namespace opt |
| } // namespace spvtools |
| |
| #endif // SOURCE_OPT_CONTROL_DEPENDENCE_H_ |