forked from ppxf25tqu/nudt-compiler-cpp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.8 KiB
206 lines
5.8 KiB
// 支配树分析:
|
|
// - 构建/查询 Dominator Tree 及相关关系
|
|
// - 使用 Cooper-Harvey-Kennedy 算法,近线性时间复杂度
|
|
|
|
#include "ir/IR.h"
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <queue>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
namespace ir {
|
|
|
|
void DominatorTree::Compute(Function& func) {
|
|
func.RebuildCFG();
|
|
|
|
// Build block list and reverse postorder (RPO)
|
|
std::vector<BasicBlock*> blocks;
|
|
std::unordered_map<BasicBlock*, int> rpo;
|
|
{
|
|
std::vector<BasicBlock*> rpo_vec;
|
|
std::unordered_set<BasicBlock*> visited;
|
|
std::function<void(BasicBlock*)> dfs = [&](BasicBlock* bb) {
|
|
if (!bb || visited.count(bb)) return;
|
|
visited.insert(bb);
|
|
for (auto* succ : bb->GetSuccessors()) {
|
|
dfs(succ);
|
|
}
|
|
rpo_vec.push_back(bb);
|
|
};
|
|
dfs(func.GetEntry());
|
|
// Reverse to get RPO (postorder reversed)
|
|
std::reverse(rpo_vec.begin(), rpo_vec.end());
|
|
blocks = rpo_vec;
|
|
for (int i = 0; i < (int)blocks.size(); ++i) {
|
|
rpo[blocks[i]] = i;
|
|
}
|
|
}
|
|
if (blocks.empty()) return;
|
|
int n = (int)blocks.size();
|
|
|
|
auto* entry = func.GetEntry();
|
|
if (!entry) return;
|
|
|
|
// ─── 1. CHK algorithm for immediate dominators ─────────────────────────
|
|
idom_.clear();
|
|
idom_[entry] = entry; // entry is its own dominator
|
|
|
|
// Intersect: find common ancestor of b1 and b2 walking up the dom tree
|
|
// Uses RPO number: a dominator always has lower RPO number
|
|
auto intersect = [&](BasicBlock* b1, BasicBlock* b2) -> BasicBlock* {
|
|
auto i1 = rpo.find(b1), i2 = rpo.find(b2);
|
|
if (i1 == rpo.end() || i2 == rpo.end()) return entry;
|
|
int r1 = i1->second, r2 = i2->second;
|
|
while (b1 != b2) {
|
|
while (r1 > r2) {
|
|
auto it = idom_.find(b1);
|
|
if (it == idom_.end() || it->second == b1) return b1;
|
|
b1 = it->second;
|
|
r1 = rpo[b1];
|
|
}
|
|
while (r2 > r1) {
|
|
auto it = idom_.find(b2);
|
|
if (it == idom_.end() || it->second == b2) return b2;
|
|
b2 = it->second;
|
|
r2 = rpo[b2];
|
|
}
|
|
}
|
|
return b1;
|
|
};
|
|
|
|
bool changed = true;
|
|
while (changed) {
|
|
changed = false;
|
|
// Process in RPO (skip entry which is first in RPO)
|
|
for (int i = 1; i < n; ++i) {
|
|
auto* bb = blocks[i];
|
|
// Find first predecessor with defined IDOM
|
|
BasicBlock* new_idom = nullptr;
|
|
for (auto* pred : bb->GetPredecessors()) {
|
|
if (idom_.count(pred) && pred != bb) {
|
|
new_idom = pred;
|
|
break;
|
|
}
|
|
}
|
|
if (!new_idom) continue;
|
|
|
|
// Intersect with remaining predecessors
|
|
for (auto* pred : bb->GetPredecessors()) {
|
|
if (pred == new_idom || pred == bb) continue;
|
|
if (idom_.count(pred)) {
|
|
new_idom = intersect(pred, new_idom);
|
|
}
|
|
}
|
|
|
|
auto old = idom_.find(bb);
|
|
if (old == idom_.end() || old->second != new_idom) {
|
|
idom_[bb] = new_idom;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Entry is its own IDOM, set to nullptr for external queries
|
|
idom_[entry] = nullptr;
|
|
|
|
// Unreached blocks get entry as IDOM
|
|
for (auto* bb : blocks) {
|
|
if (!idom_.count(bb)) idom_[bb] = entry;
|
|
}
|
|
|
|
// ─── 2. Build children map and dom levels ──────────────────────────────
|
|
children_.clear();
|
|
dom_level_.clear();
|
|
for (auto& [child, parent] : idom_) {
|
|
if (parent) children_[parent].push_back(child);
|
|
}
|
|
|
|
// BFS to compute dom levels
|
|
std::queue<BasicBlock*> q;
|
|
dom_level_[entry] = 0;
|
|
q.push(entry);
|
|
while (!q.empty()) {
|
|
auto* cur = q.front();
|
|
q.pop();
|
|
size_t cur_level = dom_level_[cur];
|
|
auto it = children_.find(cur);
|
|
if (it != children_.end()) {
|
|
for (auto* child : it->second) {
|
|
dom_level_[child] = cur_level + 1;
|
|
q.push(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─── 3. Compute dominance frontier ─────────────────────────────────────
|
|
df_.clear();
|
|
for (int i = 0; i < n; ++i) {
|
|
auto* b = blocks[i];
|
|
if (b->GetPredecessors().size() < 2) continue;
|
|
for (auto* p : b->GetPredecessors()) {
|
|
auto* runner = p;
|
|
auto* b_idom = GetIDom(b);
|
|
while (runner != b_idom) {
|
|
if (!runner) break;
|
|
df_[runner].push_back(b);
|
|
runner = GetIDom(runner);
|
|
}
|
|
}
|
|
}
|
|
// Deduplicate DF entries
|
|
for (auto& [bb, vec] : df_) {
|
|
std::sort(vec.begin(), vec.end());
|
|
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
|
|
}
|
|
|
|
// ─── 4. Compute DFS order of dominator tree ────────────────────────────
|
|
df_order_.clear();
|
|
visited_.clear();
|
|
std::function<void(BasicBlock*)> dfs_tree = [&](BasicBlock* bb) {
|
|
if (!bb || visited_.count(bb)) return;
|
|
visited_.insert(bb);
|
|
df_order_.push_back(bb);
|
|
auto it = children_.find(bb);
|
|
if (it != children_.end()) {
|
|
for (auto* child : it->second) {
|
|
dfs_tree(child);
|
|
}
|
|
}
|
|
};
|
|
dfs_tree(entry);
|
|
}
|
|
|
|
BasicBlock* DominatorTree::GetIDom(BasicBlock* bb) const {
|
|
auto it = idom_.find(bb);
|
|
return (it != idom_.end()) ? it->second : nullptr;
|
|
}
|
|
|
|
const std::vector<BasicBlock*>& DominatorTree::GetChildren(
|
|
BasicBlock* bb) const {
|
|
static const std::vector<BasicBlock*> empty;
|
|
auto it = children_.find(bb);
|
|
return (it != children_.end()) ? it->second : empty;
|
|
}
|
|
|
|
const std::vector<BasicBlock*>& DominatorTree::GetDominanceFrontier(
|
|
BasicBlock* bb) const {
|
|
static const std::vector<BasicBlock*> empty;
|
|
auto it = df_.find(bb);
|
|
return (it != df_.end()) ? it->second : empty;
|
|
}
|
|
|
|
bool DominatorTree::Dominates(BasicBlock* a, BasicBlock* b) const {
|
|
if (a == b) return true;
|
|
BasicBlock* runner = b;
|
|
while (runner) {
|
|
runner = GetIDom(runner);
|
|
if (runner == a) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace ir
|