forked from NUDT-compiler/nudt-compiler-cpp
新增优化遍: - GVN (全局值编号) - SCCP (稀疏条件常量传播) - Inline (函数内联, 暂禁用) - LoopSimplify (循环简化) - InductionVar (归纳变量分析) - LoopInterchange (循环交换分析) - LoopUnroll (循环展开) - Memoize (记忆化优化) - TailCallOpt (尾调用优化) 关键 bug 修复: 1. Peephole madd 合并: 检查 mul_dst 在 AddRR 之后是否还有使用 2. TailCallOpt: 使用 func->GetType() 替代硬编码 int32,支持浮点累加器 3. TailCallOpt: 检查 call 结果是否只被 ret/binop 使用,避免错误转换非尾调用 4. Use-def 链清理: RemoveInstruction/DCE/CFGSimplify/ConstFold/ConstProp/SCCP 测试: 200/200 全量通过,平均运行时间从 10228ms 降至 3962ms
parent
e3e01256cd
commit
f6047f7d85
@ -0,0 +1,71 @@
|
||||
// 支配树分析:
|
||||
// - 构建/查询 Dominator Tree 及相关关系
|
||||
// - 为 mem2reg、CFG 优化与循环分析提供基础能力
|
||||
|
||||
#ifndef IR_ANALYSIS_DOMINATORTREE_H_
|
||||
#define IR_ANALYSIS_DOMINATORTREE_H_
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
|
||||
// Utility: get successors of a single basic block by examining its terminator.
|
||||
inline std::vector<BasicBlock*> GetSuccessors(BasicBlock* bb) {
|
||||
std::vector<BasicBlock*> succs;
|
||||
if (!bb || !bb->HasTerminator()) return succs;
|
||||
|
||||
auto& insts = bb->GetInstructions();
|
||||
if (insts.empty()) return succs;
|
||||
|
||||
auto* term = insts.back().get();
|
||||
if (!term) return succs;
|
||||
|
||||
switch (term->GetOpcode()) {
|
||||
case Opcode::Br: {
|
||||
auto* br = static_cast<BranchInst*>(term);
|
||||
succs.push_back(br->GetTarget());
|
||||
break;
|
||||
}
|
||||
case Opcode::CondBr: {
|
||||
auto* cbr = static_cast<CondBranchInst*>(term);
|
||||
succs.push_back(cbr->GetTrueTarget());
|
||||
succs.push_back(cbr->GetFalseTarget());
|
||||
break;
|
||||
}
|
||||
case Opcode::Ret:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return succs;
|
||||
}
|
||||
|
||||
class DominatorTree {
|
||||
public:
|
||||
void Compute(Function* func);
|
||||
|
||||
bool Dominates(BasicBlock* a, BasicBlock* b) const;
|
||||
BasicBlock* GetIdom(BasicBlock* bb) const;
|
||||
const std::vector<BasicBlock*>& GetChildren(BasicBlock* bb) const;
|
||||
const std::unordered_set<BasicBlock*>& GetDominanceFrontier(
|
||||
BasicBlock* bb) const;
|
||||
const std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>>&
|
||||
GetAllDominanceFrontiers() const;
|
||||
const std::vector<BasicBlock*>& GetPostOrder() const;
|
||||
|
||||
private:
|
||||
std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>> doms_;
|
||||
std::unordered_map<BasicBlock*, BasicBlock*> idom_;
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> children_;
|
||||
std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>> df_;
|
||||
std::vector<BasicBlock*> post_order_;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
|
||||
#endif // IR_ANALYSIS_DOMINATORTREE_H_
|
||||
@ -0,0 +1,47 @@
|
||||
#ifndef IR_ANALYSIS_LOOPINFO_H_
|
||||
#define IR_ANALYSIS_LOOPINFO_H_
|
||||
|
||||
#include "ir/IR.h"
|
||||
#include "ir/analysis/DominatorTree.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
|
||||
struct Loop {
|
||||
BasicBlock* header = nullptr;
|
||||
std::vector<BasicBlock*> blocks;
|
||||
std::unordered_set<BasicBlock*> blocks_set; // for O(1) membership test
|
||||
std::vector<BasicBlock*> exits;
|
||||
BasicBlock* preheader = nullptr;
|
||||
BasicBlock* latch = nullptr;
|
||||
Loop* parent = nullptr;
|
||||
std::vector<std::unique_ptr<Loop>> sub_loops;
|
||||
int depth = 0;
|
||||
};
|
||||
|
||||
class LoopInfo {
|
||||
public:
|
||||
void Compute(Function* func, const DominatorTree& dt);
|
||||
|
||||
const std::vector<std::unique_ptr<Loop>>& GetTopLevelLoops() const { return top_level_loops_; }
|
||||
Loop* GetLoopForBlock(BasicBlock* bb) const;
|
||||
|
||||
private:
|
||||
void DiscoverLoops(Function* func, const DominatorTree& dt);
|
||||
void PopulateLoopBlocks(Loop* loop,
|
||||
const std::vector<std::pair<BasicBlock*, BasicBlock*>>& back_edges,
|
||||
const std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>& preds);
|
||||
void FindPreheaderAndLatch(Loop* loop, const DominatorTree& dt,
|
||||
const std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>& preds);
|
||||
void PopulateBlockToLoop(Loop* loop);
|
||||
|
||||
std::vector<std::unique_ptr<Loop>> top_level_loops_;
|
||||
std::unordered_map<BasicBlock*, Loop*> block_to_loop_;
|
||||
};
|
||||
|
||||
} // namespace ir
|
||||
|
||||
#endif // IR_ANALYSIS_LOOPINFO_H_
|
||||
@ -1,317 +1,257 @@
|
||||
#include "ir/IR.h"
|
||||
// 支配树分析:
|
||||
// - 构建/查询 Dominator Tree 及相关关系
|
||||
// - 为 mem2reg、CFG 优化与循环分析提供基础能力
|
||||
|
||||
#include "ir/analysis/DominatorTree.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace ir
|
||||
{
|
||||
namespace ir {
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace {
|
||||
|
||||
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> ComputePredecessors(Function *func)
|
||||
{
|
||||
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> preds;
|
||||
for (const auto &bb : func->GetBlocks())
|
||||
{
|
||||
preds[bb.get()] = {};
|
||||
}
|
||||
for (const auto &bb : func->GetBlocks())
|
||||
{
|
||||
if (!bb->HasTerminator())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto *terminator = bb->GetInstructions().back().get();
|
||||
if (auto *br = dynamic_cast<BranchInst *>(terminator))
|
||||
{
|
||||
preds[br->GetTarget()].push_back(bb.get());
|
||||
}
|
||||
else if (auto *condbr = dynamic_cast<CondBranchInst *>(terminator))
|
||||
{
|
||||
preds[condbr->GetTrueTarget()].push_back(bb.get());
|
||||
preds[condbr->GetFalseTarget()].push_back(bb.get());
|
||||
}
|
||||
}
|
||||
return preds;
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> ComputePredecessors(
|
||||
Function* func) {
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> preds;
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
preds[bb.get()] = {};
|
||||
}
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
if (!bb->HasTerminator()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> ComputeSuccessors(Function *func)
|
||||
{
|
||||
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> succs;
|
||||
for (const auto &bb : func->GetBlocks())
|
||||
{
|
||||
succs[bb.get()] = {};
|
||||
if (!bb->HasTerminator())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto *terminator = bb->GetInstructions().back().get();
|
||||
if (auto *br = dynamic_cast<BranchInst *>(terminator))
|
||||
{
|
||||
succs[bb.get()].push_back(br->GetTarget());
|
||||
}
|
||||
else if (auto *condbr = dynamic_cast<CondBranchInst *>(terminator))
|
||||
{
|
||||
succs[bb.get()].push_back(condbr->GetTrueTarget());
|
||||
succs[bb.get()].push_back(condbr->GetFalseTarget());
|
||||
}
|
||||
}
|
||||
return succs;
|
||||
auto* terminator = bb->GetInstructions().back().get();
|
||||
if (auto* br = dynamic_cast<BranchInst*>(terminator)) {
|
||||
preds[br->GetTarget()].push_back(bb.get());
|
||||
} else if (auto* condbr = dynamic_cast<CondBranchInst*>(terminator)) {
|
||||
preds[condbr->GetTrueTarget()].push_back(bb.get());
|
||||
preds[condbr->GetFalseTarget()].push_back(bb.get());
|
||||
}
|
||||
}
|
||||
return preds;
|
||||
}
|
||||
|
||||
std::vector<BasicBlock *> PostOrder(Function *func,
|
||||
const std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> &succs)
|
||||
{
|
||||
std::vector<BasicBlock *> order;
|
||||
std::unordered_set<BasicBlock *> visited;
|
||||
std::vector<std::pair<BasicBlock *, size_t>> stack;
|
||||
|
||||
auto *entry = func->GetEntry();
|
||||
if (!entry)
|
||||
{
|
||||
return order;
|
||||
}
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> ComputeSuccessors(
|
||||
Function* func) {
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> succs;
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
succs[bb.get()] = GetSuccessors(bb.get());
|
||||
}
|
||||
return succs;
|
||||
}
|
||||
|
||||
stack.push_back({entry, 0});
|
||||
visited.insert(entry);
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
auto &top = stack.back();
|
||||
auto *bb = top.first;
|
||||
auto &idx = top.second;
|
||||
|
||||
auto it = succs.find(bb);
|
||||
const auto &children = (it != succs.end()) ? it->second : std::vector<BasicBlock *>{};
|
||||
|
||||
bool found_next = false;
|
||||
while (idx < children.size())
|
||||
{
|
||||
auto *child = children[idx++];
|
||||
if (visited.find(child) == visited.end())
|
||||
{
|
||||
visited.insert(child);
|
||||
stack.push_back({child, 0});
|
||||
found_next = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::vector<BasicBlock*> PostOrder(
|
||||
Function* func,
|
||||
const std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>& succs) {
|
||||
std::vector<BasicBlock*> order;
|
||||
std::unordered_set<BasicBlock*> visited;
|
||||
std::vector<std::pair<BasicBlock*, size_t>> stack;
|
||||
|
||||
if (!found_next)
|
||||
{
|
||||
order.push_back(bb);
|
||||
stack.pop_back();
|
||||
}
|
||||
auto* entry = func->GetEntry();
|
||||
if (!entry) {
|
||||
return order;
|
||||
}
|
||||
|
||||
stack.push_back({entry, 0});
|
||||
visited.insert(entry);
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto& top = stack.back();
|
||||
auto* bb = top.first;
|
||||
auto& idx = top.second;
|
||||
|
||||
auto it = succs.find(bb);
|
||||
const auto& children =
|
||||
(it != succs.end()) ? it->second : std::vector<BasicBlock*>{};
|
||||
|
||||
bool found_next = false;
|
||||
while (idx < children.size()) {
|
||||
auto* child = children[idx++];
|
||||
if (visited.find(child) == visited.end()) {
|
||||
visited.insert(child);
|
||||
stack.push_back({child, 0});
|
||||
found_next = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return order;
|
||||
if (!found_next) {
|
||||
order.push_back(bb);
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<BasicBlock *> Intersect(const std::unordered_set<BasicBlock *> &a,
|
||||
const std::unordered_set<BasicBlock *> &b)
|
||||
{
|
||||
std::unordered_set<BasicBlock *> result;
|
||||
for (auto *bb : a)
|
||||
{
|
||||
if (b.find(bb) != b.end())
|
||||
{
|
||||
result.insert(bb);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return order;
|
||||
}
|
||||
|
||||
std::unordered_set<BasicBlock*> Intersect(
|
||||
const std::unordered_set<BasicBlock*>& a,
|
||||
const std::unordered_set<BasicBlock*>& b) {
|
||||
std::unordered_set<BasicBlock*> result;
|
||||
for (auto* bb : a) {
|
||||
if (b.find(bb) != b.end()) {
|
||||
result.insert(bb);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DominatorTree::Compute(Function* func) {
|
||||
doms_.clear();
|
||||
idom_.clear();
|
||||
children_.clear();
|
||||
df_.clear();
|
||||
post_order_.clear();
|
||||
|
||||
auto preds = ComputePredecessors(func);
|
||||
auto succs = ComputeSuccessors(func);
|
||||
post_order_ = PostOrder(func, succs);
|
||||
|
||||
std::unordered_map<BasicBlock*, size_t> post_order_idx;
|
||||
for (size_t i = 0; i < post_order_.size(); ++i) {
|
||||
post_order_idx[post_order_[i]] = i;
|
||||
}
|
||||
|
||||
class DominatorTree
|
||||
{
|
||||
public:
|
||||
void Compute(Function *func)
|
||||
{
|
||||
doms_.clear();
|
||||
idom_.clear();
|
||||
children_.clear();
|
||||
df_.clear();
|
||||
|
||||
auto preds = ComputePredecessors(func);
|
||||
auto succs = ComputeSuccessors(func);
|
||||
auto post_order = PostOrder(func, succs);
|
||||
|
||||
std::unordered_map<BasicBlock *, size_t> post_order_idx;
|
||||
for (size_t i = 0; i < post_order.size(); ++i)
|
||||
{
|
||||
post_order_idx[post_order[i]] = i;
|
||||
}
|
||||
auto* entry = func->GetEntry();
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *entry = func->GetEntry();
|
||||
if (!entry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::unordered_set<BasicBlock*> all_blocks;
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
all_blocks.insert(bb.get());
|
||||
}
|
||||
|
||||
std::unordered_set<BasicBlock *> all_blocks;
|
||||
for (const auto &bb : func->GetBlocks())
|
||||
{
|
||||
all_blocks.insert(bb.get());
|
||||
}
|
||||
doms_[entry] = {entry};
|
||||
for (auto* bb : post_order_) {
|
||||
if (bb != entry) {
|
||||
doms_[bb] = all_blocks;
|
||||
}
|
||||
}
|
||||
|
||||
doms_[entry] = {entry};
|
||||
for (auto *bb : post_order)
|
||||
{
|
||||
if (bb != entry)
|
||||
{
|
||||
doms_[bb] = all_blocks;
|
||||
}
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (auto it = post_order_.rbegin(); it != post_order_.rend(); ++it) {
|
||||
auto* bb = *it;
|
||||
if (bb == entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
for (auto it = post_order.rbegin(); it != post_order.rend(); ++it)
|
||||
{
|
||||
auto *bb = *it;
|
||||
if (bb == entry)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pred_it = preds.find(bb);
|
||||
if (pred_it == preds.end() || pred_it->second.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unordered_set<BasicBlock *> new_dom;
|
||||
bool first = true;
|
||||
for (auto *pred : pred_it->second)
|
||||
{
|
||||
auto dom_it = doms_.find(pred);
|
||||
if (dom_it == doms_.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
new_dom = dom_it->second;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_dom = Intersect(new_dom, dom_it->second);
|
||||
}
|
||||
}
|
||||
new_dom.insert(bb);
|
||||
|
||||
if (doms_[bb] != new_dom)
|
||||
{
|
||||
doms_[bb] = new_dom;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
auto pred_it = preds.find(bb);
|
||||
if (pred_it == preds.end() || pred_it->second.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto *bb : post_order)
|
||||
{
|
||||
if (bb == entry)
|
||||
{
|
||||
idom_[bb] = nullptr;
|
||||
std::unordered_set<BasicBlock*> new_dom;
|
||||
bool first = true;
|
||||
for (auto* pred : pred_it->second) {
|
||||
auto dom_it = doms_.find(pred);
|
||||
if (dom_it == doms_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &dom_set = doms_[bb];
|
||||
BasicBlock *idom = nullptr;
|
||||
size_t min_idx = post_order.size();
|
||||
|
||||
for (auto *d : dom_set)
|
||||
{
|
||||
if (d == bb)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto idx_it = post_order_idx.find(d);
|
||||
if (idx_it != post_order_idx.end() && idx_it->second < min_idx)
|
||||
{
|
||||
min_idx = idx_it->second;
|
||||
idom = d;
|
||||
}
|
||||
if (first) {
|
||||
new_dom = dom_it->second;
|
||||
first = false;
|
||||
} else {
|
||||
new_dom = Intersect(new_dom, dom_it->second);
|
||||
}
|
||||
}
|
||||
new_dom.insert(bb);
|
||||
|
||||
idom_[bb] = idom;
|
||||
if (idom)
|
||||
{
|
||||
children_[idom].push_back(bb);
|
||||
}
|
||||
if (doms_[bb] != new_dom) {
|
||||
doms_[bb] = new_dom;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto *bb : post_order)
|
||||
{
|
||||
if (bb == entry)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (auto* bb : post_order_) {
|
||||
if (bb == entry) {
|
||||
idom_[bb] = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pred_it = preds.find(bb);
|
||||
if (pred_it == preds.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto& dom_set = doms_[bb];
|
||||
BasicBlock* idom = nullptr;
|
||||
size_t min_idx = post_order_.size();
|
||||
|
||||
for (auto *pred : pred_it->second)
|
||||
{
|
||||
auto *runner = pred;
|
||||
while (runner && runner != idom_[bb])
|
||||
{
|
||||
df_[runner].insert(bb);
|
||||
runner = idom_[runner];
|
||||
}
|
||||
}
|
||||
for (auto* d : dom_set) {
|
||||
if (d == bb) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool Dominates(BasicBlock *a, BasicBlock *b) const
|
||||
{
|
||||
auto it = doms_.find(b);
|
||||
if (it == doms_.end())
|
||||
{
|
||||
return false;
|
||||
auto idx_it = post_order_idx.find(d);
|
||||
if (idx_it != post_order_idx.end() && idx_it->second < min_idx) {
|
||||
min_idx = idx_it->second;
|
||||
idom = d;
|
||||
}
|
||||
return it->second.find(a) != it->second.end();
|
||||
}
|
||||
|
||||
BasicBlock *GetIdom(BasicBlock *bb) const
|
||||
{
|
||||
auto it = idom_.find(bb);
|
||||
return (it != idom_.end()) ? it->second : nullptr;
|
||||
idom_[bb] = idom;
|
||||
if (idom) {
|
||||
children_[idom].push_back(bb);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<BasicBlock *> &GetChildren(BasicBlock *bb) const
|
||||
{
|
||||
static const std::vector<BasicBlock *> empty;
|
||||
auto it = children_.find(bb);
|
||||
return (it != children_.end()) ? it->second : empty;
|
||||
for (auto* bb : post_order_) {
|
||||
if (bb == entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::unordered_set<BasicBlock *> &GetDominanceFrontier(BasicBlock *bb) const
|
||||
{
|
||||
static const std::unordered_set<BasicBlock *> empty;
|
||||
auto it = df_.find(bb);
|
||||
return (it != df_.end()) ? it->second : empty;
|
||||
auto pred_it = preds.find(bb);
|
||||
if (pred_it == preds.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::unordered_map<BasicBlock *, std::unordered_set<BasicBlock *>> &GetAllDominanceFrontiers() const
|
||||
{
|
||||
return df_;
|
||||
for (auto* pred : pred_it->second) {
|
||||
auto* runner = pred;
|
||||
while (runner && runner != idom_[bb]) {
|
||||
df_[runner].insert(bb);
|
||||
runner = idom_[runner];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<BasicBlock *, std::unordered_set<BasicBlock *>> doms_;
|
||||
std::unordered_map<BasicBlock *, BasicBlock *> idom_;
|
||||
std::unordered_map<BasicBlock *, std::vector<BasicBlock *>> children_;
|
||||
std::unordered_map<BasicBlock *, std::unordered_set<BasicBlock *>> df_;
|
||||
};
|
||||
bool DominatorTree::Dominates(BasicBlock* a, BasicBlock* b) const {
|
||||
auto it = doms_.find(b);
|
||||
if (it == doms_.end()) {
|
||||
return false;
|
||||
}
|
||||
return it->second.find(a) != it->second.end();
|
||||
}
|
||||
|
||||
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::unordered_set<BasicBlock*>& DominatorTree::GetDominanceFrontier(
|
||||
BasicBlock* bb) const {
|
||||
static const std::unordered_set<BasicBlock*> empty;
|
||||
auto it = df_.find(bb);
|
||||
return (it != df_.end()) ? it->second : empty;
|
||||
}
|
||||
|
||||
const std::unordered_map<BasicBlock*, std::unordered_set<BasicBlock*>>&
|
||||
DominatorTree::GetAllDominanceFrontiers() const {
|
||||
return df_;
|
||||
}
|
||||
|
||||
const std::vector<BasicBlock*>& DominatorTree::GetPostOrder() const {
|
||||
return post_order_;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
@ -1,4 +1,224 @@
|
||||
// 循环分析:
|
||||
// - 识别循环结构与层级关系
|
||||
// - 为后续优化(可选)提供循环信息
|
||||
// - 为后续优化(LICM、LoopUnroll、LoopInterchange、InductionVar)提供循环信息
|
||||
|
||||
#include "ir/analysis/LoopInfo.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
|
||||
namespace {
|
||||
|
||||
// Build predecessor lists by inverting successor edges.
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> ComputePredecessors(
|
||||
Function* func) {
|
||||
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> preds;
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
preds[bb.get()] = {};
|
||||
}
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
for (auto* succ : GetSuccessors(bb.get())) {
|
||||
preds[succ].push_back(bb.get());
|
||||
}
|
||||
}
|
||||
return preds;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void LoopInfo::Compute(Function* func, const DominatorTree& dt) {
|
||||
top_level_loops_.clear();
|
||||
block_to_loop_.clear();
|
||||
DiscoverLoops(func, dt);
|
||||
}
|
||||
|
||||
void LoopInfo::DiscoverLoops(Function* func, const DominatorTree& dt) {
|
||||
// Compute predecessor maps for the entire function.
|
||||
auto preds = ComputePredecessors(func);
|
||||
|
||||
// Step 1: Detect back edges.
|
||||
// A -> B is a back edge if B dominates A.
|
||||
// B is the loop header.
|
||||
std::vector<std::pair<BasicBlock*, BasicBlock*>> back_edges; // (from, to=header)
|
||||
std::unordered_map<BasicBlock*, std::unique_ptr<Loop>> header_to_loop;
|
||||
|
||||
for (const auto& bb : func->GetBlocks()) {
|
||||
for (auto* succ : GetSuccessors(bb.get())) {
|
||||
if (dt.Dominates(succ, bb.get())) {
|
||||
back_edges.emplace_back(bb.get(), succ);
|
||||
if (header_to_loop.find(succ) == header_to_loop.end()) {
|
||||
auto loop = std::make_unique<Loop>();
|
||||
loop->header = succ;
|
||||
header_to_loop[succ] = std::move(loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (header_to_loop.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Populate loop blocks and identify exit blocks.
|
||||
for (auto& [header, loop] : header_to_loop) {
|
||||
PopulateLoopBlocks(loop.get(), back_edges, preds);
|
||||
}
|
||||
|
||||
// Step 3: Find preheader and latch for each loop.
|
||||
for (auto& [header, loop] : header_to_loop) {
|
||||
FindPreheaderAndLatch(loop.get(), dt, preds);
|
||||
}
|
||||
|
||||
// Step 4: Build the loop nest tree.
|
||||
// Determine parent for each loop: the innermost loop that contains this
|
||||
// loop's header.
|
||||
for (auto& [h_inner, inner_ptr] : header_to_loop) {
|
||||
Loop* inner = inner_ptr.get();
|
||||
Loop* best_parent = nullptr;
|
||||
size_t best_size = std::numeric_limits<size_t>::max();
|
||||
|
||||
for (auto& [h_outer, outer_ptr] : header_to_loop) {
|
||||
Loop* outer = outer_ptr.get();
|
||||
if (outer == inner) continue;
|
||||
// Is inner's header inside outer's body?
|
||||
if (outer->blocks_set.find(inner->header) != outer->blocks_set.end()) {
|
||||
// Pick the innermost containing loop (smallest body).
|
||||
if (outer->blocks_set.size() < best_size) {
|
||||
best_size = outer->blocks_set.size();
|
||||
best_parent = outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner->parent = best_parent;
|
||||
if (best_parent) {
|
||||
inner->depth = best_parent->depth + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: Transfer ownership into the tree.
|
||||
// Top-level (parentless) loops go to top_level_loops_.
|
||||
for (auto& [header, loop_ptr] : header_to_loop) {
|
||||
if (loop_ptr->parent == nullptr) {
|
||||
top_level_loops_.push_back(std::move(loop_ptr));
|
||||
}
|
||||
}
|
||||
// Nested loops go into their parent's sub_loops.
|
||||
for (auto& [header, loop_ptr] : header_to_loop) {
|
||||
if (loop_ptr) {
|
||||
loop_ptr->parent->sub_loops.push_back(std::move(loop_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: Populate block_to_loop_ mapping (innermost loop for each block).
|
||||
for (auto& top : top_level_loops_) {
|
||||
PopulateBlockToLoop(top.get());
|
||||
}
|
||||
}
|
||||
|
||||
void LoopInfo::PopulateLoopBlocks(
|
||||
Loop* loop,
|
||||
const std::vector<std::pair<BasicBlock*, BasicBlock*>>& back_edges,
|
||||
const std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>& preds) {
|
||||
BasicBlock* header = loop->header;
|
||||
|
||||
// Collect back-edge sources for this header.
|
||||
std::vector<BasicBlock*> worklist;
|
||||
for (const auto& [from, to] : back_edges) {
|
||||
if (to == header && loop->blocks_set.find(from) == loop->blocks_set.end()) {
|
||||
worklist.push_back(from);
|
||||
loop->blocks_set.insert(from);
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse traversal: follow predecessors, stopping at the header.
|
||||
while (!worklist.empty()) {
|
||||
BasicBlock* bb = worklist.back();
|
||||
worklist.pop_back();
|
||||
|
||||
loop->blocks.push_back(bb);
|
||||
|
||||
auto it = preds.find(bb);
|
||||
if (it == preds.end()) continue;
|
||||
|
||||
for (auto* pred : it->second) {
|
||||
if (pred != header &&
|
||||
loop->blocks_set.find(pred) == loop->blocks_set.end()) {
|
||||
loop->blocks_set.insert(pred);
|
||||
worklist.push_back(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the header itself to the loop body.
|
||||
loop->blocks.push_back(header);
|
||||
loop->blocks_set.insert(header);
|
||||
|
||||
// Identify exit blocks: blocks in the loop with a successor outside.
|
||||
for (auto* bb : loop->blocks) {
|
||||
for (auto* succ : GetSuccessors(bb)) {
|
||||
if (loop->blocks_set.find(succ) == loop->blocks_set.end()) {
|
||||
loop->exits.push_back(bb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoopInfo::FindPreheaderAndLatch(
|
||||
Loop* loop, const DominatorTree& dt,
|
||||
const std::unordered_map<BasicBlock*, std::vector<BasicBlock*>>& preds) {
|
||||
BasicBlock* header = loop->header;
|
||||
|
||||
auto it = preds.find(header);
|
||||
if (it == preds.end()) return;
|
||||
|
||||
// Find preheader: unique predecessor NOT in the loop.
|
||||
BasicBlock* preheader_candidate = nullptr;
|
||||
int preheader_count = 0;
|
||||
for (auto* pred : it->second) {
|
||||
if (loop->blocks_set.find(pred) == loop->blocks_set.end()) {
|
||||
preheader_candidate = pred;
|
||||
++preheader_count;
|
||||
}
|
||||
}
|
||||
if (preheader_count == 1) {
|
||||
loop->preheader = preheader_candidate;
|
||||
}
|
||||
|
||||
// Find latch: unique predecessor IN the loop that has a back edge to the
|
||||
// header. A back edge means the header dominates the predecessor.
|
||||
BasicBlock* latch_candidate = nullptr;
|
||||
int latch_count = 0;
|
||||
for (auto* pred : it->second) {
|
||||
if (loop->blocks_set.find(pred) != loop->blocks_set.end()) {
|
||||
if (dt.Dominates(header, pred)) {
|
||||
latch_candidate = pred;
|
||||
++latch_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (latch_count == 1) {
|
||||
loop->latch = latch_candidate;
|
||||
}
|
||||
}
|
||||
|
||||
void LoopInfo::PopulateBlockToLoop(Loop* loop) {
|
||||
for (auto* bb : loop->blocks) {
|
||||
block_to_loop_[bb] = loop;
|
||||
}
|
||||
for (auto& sub : loop->sub_loops) {
|
||||
PopulateBlockToLoop(sub.get());
|
||||
}
|
||||
}
|
||||
|
||||
Loop* LoopInfo::GetLoopForBlock(BasicBlock* bb) const {
|
||||
auto it = block_to_loop_.find(bb);
|
||||
return (it != block_to_loop_.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
@ -0,0 +1,134 @@
|
||||
#include "ir/IR.h"
|
||||
#include "ir/analysis/DominatorTree.h"
|
||||
#include "ir/passes/PassManager.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
|
||||
struct ExprKey {
|
||||
Opcode opcode;
|
||||
Value* lhs;
|
||||
Value* rhs;
|
||||
|
||||
bool IsCommutative() const {
|
||||
return opcode == Opcode::Add || opcode == Opcode::Mul ||
|
||||
opcode == Opcode::Eq || opcode == Opcode::Ne;
|
||||
}
|
||||
|
||||
bool operator==(const ExprKey& o) const {
|
||||
if (opcode != o.opcode) return false;
|
||||
if (IsCommutative()) {
|
||||
return (lhs == o.lhs && rhs == o.rhs) || (lhs == o.rhs && rhs == o.lhs);
|
||||
}
|
||||
return lhs == o.lhs && rhs == o.rhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExprKeyHash {
|
||||
size_t operator()(const ExprKey& k) const {
|
||||
size_t h = std::hash<int>()(static_cast<int>(k.opcode));
|
||||
std::hash<Value*> ptr_hash;
|
||||
if (k.IsCommutative()) {
|
||||
h ^= ptr_hash(k.lhs) ^ ptr_hash(k.rhs);
|
||||
} else {
|
||||
h ^= ptr_hash(k.lhs);
|
||||
h ^= ptr_hash(k.rhs) << 1;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
static bool IsSafeToReplace(Instruction* inst, Value* replacement, const DominatorTree& dt) {
|
||||
auto* repl_inst = dynamic_cast<Instruction*>(replacement);
|
||||
if (!repl_inst) return true;
|
||||
|
||||
BasicBlock* repl_bb = repl_inst->GetParent();
|
||||
if (!repl_bb) return true;
|
||||
|
||||
for (auto& use : inst->GetUses()) {
|
||||
auto* phi = dynamic_cast<PhiInst*>(use.GetUser());
|
||||
if (!phi) continue;
|
||||
|
||||
auto* phi_bb = phi->GetParent();
|
||||
if (!phi_bb) continue;
|
||||
|
||||
for (size_t i = 0; i + 1 < phi->GetNumOperands(); i += 2) {
|
||||
if (phi->GetOperand(i) == inst) {
|
||||
auto* pred_bb = static_cast<BasicBlock*>(phi->GetOperand(i + 1));
|
||||
if (!dt.Dominates(repl_bb, pred_bb)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void GVNOnDomTree(BasicBlock* bb,
|
||||
std::unordered_map<ExprKey, Value*, ExprKeyHash>& expr_map,
|
||||
std::vector<ExprKey>& added_keys,
|
||||
const DominatorTree& dt, bool& changed) {
|
||||
|
||||
std::vector<Instruction*> to_remove;
|
||||
|
||||
for (auto& inst : bb->GetInstructions()) {
|
||||
auto* bin = dynamic_cast<BinaryInst*>(inst.get());
|
||||
if (!bin) continue;
|
||||
|
||||
if (bin->GetUses().empty()) continue;
|
||||
|
||||
ExprKey key{bin->GetOpcode(), bin->GetOperand(0), bin->GetOperand(1)};
|
||||
auto it = expr_map.find(key);
|
||||
if (it != expr_map.end() && it->second != bin) {
|
||||
if (IsSafeToReplace(bin, it->second, dt)) {
|
||||
bin->ReplaceAllUsesWith(it->second);
|
||||
to_remove.push_back(bin);
|
||||
changed = true;
|
||||
}
|
||||
} else if (it == expr_map.end()) {
|
||||
expr_map[key] = bin;
|
||||
added_keys.push_back(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* inst : to_remove) {
|
||||
bb->RemoveInstruction(inst);
|
||||
}
|
||||
|
||||
size_t saved = added_keys.size();
|
||||
for (auto* child : dt.GetChildren(bb)) {
|
||||
GVNOnDomTree(child, expr_map, added_keys, dt, changed);
|
||||
}
|
||||
|
||||
while (added_keys.size() > saved) {
|
||||
expr_map.erase(added_keys.back());
|
||||
added_keys.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
bool RunGVN(Module& module) {
|
||||
bool changed = false;
|
||||
|
||||
for (auto& func : module.GetFunctions()) {
|
||||
if (func->IsExternal()) continue;
|
||||
|
||||
DominatorTree dt;
|
||||
dt.Compute(func.get());
|
||||
|
||||
std::unordered_map<ExprKey, Value*, ExprKeyHash> expr_map;
|
||||
std::vector<ExprKey> added_keys;
|
||||
|
||||
BasicBlock* entry = func->GetEntry();
|
||||
if (entry) {
|
||||
GVNOnDomTree(entry, expr_map, added_keys, dt, changed);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
@ -0,0 +1,366 @@
|
||||
#include "ir/IR.h"
|
||||
#include "ir/analysis/DominatorTree.h"
|
||||
#include "ir/passes/PassManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kMaxInlineInstructions = 50;
|
||||
constexpr int kMaxInlineRounds = 3;
|
||||
|
||||
bool IsInlineable(Function* callee, Function* caller) {
|
||||
if (!callee) return false;
|
||||
if (callee->IsExternal()) return false;
|
||||
if (callee == caller) return false;
|
||||
|
||||
int count = 0;
|
||||
for (auto& bb : callee->GetBlocks()) {
|
||||
for (auto& inst : bb->GetInstructions()) {
|
||||
(void)inst;
|
||||
if (++count > kMaxInlineInstructions) return false;
|
||||
}
|
||||
}
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
int CountReturns(Function* func) {
|
||||
int count = 0;
|
||||
for (auto& bb : func->GetBlocks()) {
|
||||
if (bb->HasTerminator()) {
|
||||
auto& insts = bb->GetInstructions();
|
||||
if (!insts.empty() && insts.back()->GetOpcode() == Opcode::Ret) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static Value* MapValue(Value* v, const std::unordered_map<Value*, Value*>& vmap) {
|
||||
auto it = vmap.find(v);
|
||||
return (it != vmap.end()) ? it->second : v;
|
||||
}
|
||||
|
||||
Instruction* CloneInstruction(
|
||||
Instruction* inst, BasicBlock* target_bb,
|
||||
std::unordered_map<Value*, Value*>& vmap) {
|
||||
|
||||
switch (inst->GetOpcode()) {
|
||||
|
||||
case Opcode::Add: case Opcode::Sub: case Opcode::Mul:
|
||||
case Opcode::Div: case Opcode::Mod:
|
||||
case Opcode::Eq: case Opcode::Ne: case Opcode::Lt:
|
||||
case Opcode::Le: case Opcode::Gt: case Opcode::Ge: {
|
||||
auto* bin = static_cast<BinaryInst*>(inst);
|
||||
return target_bb->Append<BinaryInst>(
|
||||
inst->GetOpcode(), inst->GetType(),
|
||||
MapValue(bin->GetLhs(), vmap), MapValue(bin->GetRhs(), vmap),
|
||||
inst->GetName());
|
||||
}
|
||||
|
||||
case Opcode::SIToFP:
|
||||
return target_bb->Append<CastInst>(
|
||||
Opcode::SIToFP, Type::GetFloat32Type(),
|
||||
MapValue(static_cast<CastInst*>(inst)->GetOperandValue(), vmap),
|
||||
inst->GetName());
|
||||
|
||||
case Opcode::FPToSI:
|
||||
return target_bb->Append<CastInst>(
|
||||
Opcode::FPToSI, Type::GetInt32Type(),
|
||||
MapValue(static_cast<CastInst*>(inst)->GetOperandValue(), vmap),
|
||||
inst->GetName());
|
||||
|
||||
case Opcode::ZExt:
|
||||
return target_bb->Append<CastInst>(
|
||||
Opcode::ZExt, inst->GetType(),
|
||||
MapValue(static_cast<CastInst*>(inst)->GetOperandValue(), vmap),
|
||||
inst->GetName());
|
||||
|
||||
case Opcode::Alloca: {
|
||||
auto* alloca = static_cast<AllocaInst*>(inst);
|
||||
if (alloca->IsArrayAlloca()) {
|
||||
auto* count = alloca->GetCount();
|
||||
return target_bb->InsertAlloca<AllocaInst>(
|
||||
alloca->GetElementType(), inst->GetName(),
|
||||
count ? MapValue(count, vmap) : nullptr);
|
||||
}
|
||||
return target_bb->InsertAlloca<AllocaInst>(
|
||||
alloca->GetElementType(), inst->GetName(), nullptr);
|
||||
}
|
||||
|
||||
case Opcode::Load: {
|
||||
auto* load = static_cast<LoadInst*>(inst);
|
||||
return target_bb->Append<LoadInst>(
|
||||
inst->GetType(),
|
||||
MapValue(load->GetPtr(), vmap),
|
||||
inst->GetName());
|
||||
}
|
||||
|
||||
case Opcode::Store: {
|
||||
auto* store = static_cast<StoreInst*>(inst);
|
||||
return target_bb->Append<StoreInst>(
|
||||
Type::GetVoidType(),
|
||||
MapValue(store->GetValue(), vmap),
|
||||
MapValue(store->GetPtr(), vmap));
|
||||
}
|
||||
|
||||
case Opcode::GEP: {
|
||||
auto* gep = static_cast<GetElementPtrInst*>(inst);
|
||||
return target_bb->Append<GetElementPtrInst>(
|
||||
gep->GetType(),
|
||||
MapValue(gep->GetBasePtr(), vmap),
|
||||
MapValue(gep->GetIndex(), vmap),
|
||||
inst->GetName());
|
||||
}
|
||||
|
||||
case Opcode::Call: {
|
||||
auto* call = static_cast<CallInst*>(inst);
|
||||
std::vector<Value*> args;
|
||||
for (size_t i = 0; i < call->GetNumArgs(); i++) {
|
||||
args.push_back(MapValue(call->GetArg(i), vmap));
|
||||
}
|
||||
return target_bb->Append<CallInst>(
|
||||
call->GetType(), call->GetCallee(), args, inst->GetName());
|
||||
}
|
||||
|
||||
case Opcode::Br: {
|
||||
auto* br = static_cast<BranchInst*>(inst);
|
||||
return target_bb->Append<BranchInst>(
|
||||
Type::GetVoidType(),
|
||||
static_cast<BasicBlock*>(MapValue(br->GetTarget(), vmap)));
|
||||
}
|
||||
|
||||
case Opcode::CondBr: {
|
||||
auto* cbr = static_cast<CondBranchInst*>(inst);
|
||||
return target_bb->Append<CondBranchInst>(
|
||||
Type::GetVoidType(),
|
||||
MapValue(cbr->GetCond(), vmap),
|
||||
static_cast<BasicBlock*>(MapValue(cbr->GetTrueTarget(), vmap)),
|
||||
static_cast<BasicBlock*>(MapValue(cbr->GetFalseTarget(), vmap)));
|
||||
}
|
||||
|
||||
case Opcode::Phi: {
|
||||
auto* old_phi = static_cast<PhiInst*>(inst);
|
||||
auto* new_phi =
|
||||
target_bb->Append<PhiInst>(inst->GetType(), inst->GetName());
|
||||
for (size_t i = 0; i < old_phi->GetNumOperands(); i++) {
|
||||
Value* op = old_phi->GetOperand(i);
|
||||
new_phi->AddOperand(MapValue(op, vmap));
|
||||
}
|
||||
return new_phi;
|
||||
}
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DropAllOperandUses(Instruction* inst) {
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
if (auto* op = inst->GetOperand(i)) {
|
||||
auto& uses = const_cast<std::vector<Use>&>(op->GetUses());
|
||||
uses.erase(std::remove_if(uses.begin(), uses.end(),
|
||||
[inst](const Use& use) {
|
||||
return use.GetUser() == inst;
|
||||
}),
|
||||
uses.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePhiPredecessor(BasicBlock* old_pred, BasicBlock* new_pred, Function* func) {
|
||||
for (auto& bb : func->GetBlocks()) {
|
||||
for (auto& inst_ptr : bb->GetInstructions()) {
|
||||
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
|
||||
if (!phi) break;
|
||||
for (size_t i = 0; i + 1 < phi->GetNumOperands(); i += 2) {
|
||||
if (phi->GetOperand(i + 1) == old_pred) {
|
||||
phi->SetOperand(i + 1, new_pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool InlineCall(CallInst* call, Function* caller) {
|
||||
auto* callee = call->GetCallee();
|
||||
if (!callee) return false;
|
||||
|
||||
BasicBlock* call_bb = call->GetParent();
|
||||
if (!call_bb) return false;
|
||||
|
||||
std::unordered_map<Value*, Value*> vmap;
|
||||
|
||||
auto& params = callee->GetParams();
|
||||
for (size_t i = 0; i < params.size() && i < call->GetNumArgs(); i++) {
|
||||
vmap[params[i].get()] = call->GetArg(i);
|
||||
}
|
||||
|
||||
std::unordered_map<BasicBlock*, BasicBlock*> block_map;
|
||||
|
||||
for (auto& bb : callee->GetBlocks()) {
|
||||
auto* new_bb = caller->CreateBlock(
|
||||
callee->GetName() + "." + bb->GetName() + ".inline");
|
||||
block_map[bb.get()] = new_bb;
|
||||
vmap[bb.get()] = new_bb;
|
||||
}
|
||||
|
||||
for (auto& bb : callee->GetBlocks()) {
|
||||
auto* new_bb = block_map[bb.get()];
|
||||
|
||||
for (auto& inst_ptr : bb->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
|
||||
if (inst->GetOpcode() == Opcode::Ret) continue;
|
||||
|
||||
auto* cloned = CloneInstruction(inst, new_bb, vmap);
|
||||
if (cloned) {
|
||||
vmap[inst] = cloned;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value* ret_val = nullptr;
|
||||
for (auto& bb : callee->GetBlocks()) {
|
||||
if (!bb->HasTerminator()) continue;
|
||||
auto& insts = bb->GetInstructions();
|
||||
if (insts.empty()) continue;
|
||||
|
||||
auto* term = insts.back().get();
|
||||
if (term->GetOpcode() != Opcode::Ret) continue;
|
||||
|
||||
auto* ret = static_cast<ReturnInst*>(term);
|
||||
if (ret->HasValue()) {
|
||||
Value* orig_val = ret->GetValue();
|
||||
ret_val = MapValue(orig_val, vmap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
auto* cont_bb = caller->CreateBlock(call_bb->GetName() + ".cont");
|
||||
|
||||
std::vector<BasicBlock*> call_bb_successors;
|
||||
if (call_bb->HasTerminator()) {
|
||||
auto succs = GetSuccessors(call_bb);
|
||||
call_bb_successors = succs;
|
||||
}
|
||||
|
||||
auto& call_insts =
|
||||
const_cast<std::vector<std::unique_ptr<Instruction>>&>(
|
||||
call_bb->GetInstructions());
|
||||
|
||||
std::vector<std::unique_ptr<Instruction>> moved_insts;
|
||||
bool found_call = false;
|
||||
for (auto it = call_insts.begin(); it != call_insts.end();) {
|
||||
if ((*it).get() == call) {
|
||||
found_call = true;
|
||||
if (ret_val) {
|
||||
call->ReplaceAllUsesWith(ret_val);
|
||||
}
|
||||
DropAllOperandUses(call);
|
||||
it = call_insts.erase(it);
|
||||
} else if (found_call) {
|
||||
moved_insts.push_back(std::move(*it));
|
||||
it = call_insts.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* succ : call_bb_successors) {
|
||||
auto& preds = succ->GetMutablePredecessors();
|
||||
preds.erase(std::remove(preds.begin(), preds.end(), call_bb), preds.end());
|
||||
}
|
||||
call_bb->GetMutableSuccessors().clear();
|
||||
|
||||
auto* entry_clone = block_map[callee->GetEntry()];
|
||||
call_bb->Append<BranchInst>(Type::GetVoidType(), entry_clone);
|
||||
|
||||
{
|
||||
auto& cont_vec =
|
||||
const_cast<std::vector<std::unique_ptr<Instruction>>&>(
|
||||
cont_bb->GetInstructions());
|
||||
for (auto& inst : moved_insts) {
|
||||
inst->SetParent(cont_bb);
|
||||
cont_vec.push_back(std::move(inst));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* succ : call_bb_successors) {
|
||||
cont_bb->GetMutableSuccessors().push_back(succ);
|
||||
succ->GetMutablePredecessors().push_back(cont_bb);
|
||||
}
|
||||
|
||||
UpdatePhiPredecessor(call_bb, cont_bb, caller);
|
||||
|
||||
for (auto& bb : callee->GetBlocks()) {
|
||||
if (!bb->HasTerminator()) continue;
|
||||
auto& insts = bb->GetInstructions();
|
||||
if (insts.empty()) continue;
|
||||
|
||||
auto* term = insts.back().get();
|
||||
if (term->GetOpcode() != Opcode::Ret) continue;
|
||||
|
||||
auto* cloned_bb = block_map[bb.get()];
|
||||
cloned_bb->Append<BranchInst>(Type::GetVoidType(), cont_bb);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunInline(Module& module) {
|
||||
bool changed = false;
|
||||
|
||||
for (int round = 0; round < kMaxInlineRounds; round++) {
|
||||
bool round_changed = false;
|
||||
|
||||
struct CallSite {
|
||||
CallInst* call;
|
||||
Function* callee;
|
||||
Function* caller;
|
||||
};
|
||||
std::vector<CallSite> candidates;
|
||||
|
||||
for (auto& func_ptr : module.GetFunctions()) {
|
||||
auto* caller = func_ptr.get();
|
||||
if (caller->IsExternal()) continue;
|
||||
|
||||
for (auto& bb : caller->GetBlocks()) {
|
||||
for (auto& inst_ptr : bb->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (inst->GetOpcode() != Opcode::Call) continue;
|
||||
|
||||
auto* call = static_cast<CallInst*>(inst);
|
||||
auto* callee = call->GetCallee();
|
||||
if (!callee) continue;
|
||||
|
||||
if (IsInlineable(callee, caller) && CountReturns(callee) == 1) {
|
||||
candidates.push_back({call, callee, caller});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& cs : candidates) {
|
||||
if (InlineCall(cs.call, cs.caller)) {
|
||||
round_changed = true;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!round_changed) break;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
Loading…
Reference in new issue