forked from NUDT-compiler/nudt-compiler-cpp
parent
a669efb7a5
commit
1d4c59d7ec
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace mir
|
||||
{
|
||||
|
||||
class MachineBasicBlock;
|
||||
class MachineFunction;
|
||||
|
||||
struct CFGEdge
|
||||
{
|
||||
MachineBasicBlock *src = nullptr;
|
||||
MachineBasicBlock *dst = nullptr;
|
||||
double weight = 0.0;
|
||||
};
|
||||
|
||||
struct CFGAnalysisResult
|
||||
{
|
||||
std::map<MachineBasicBlock *, std::vector<MachineBasicBlock *>> successors;
|
||||
std::map<MachineBasicBlock *, std::vector<MachineBasicBlock *>> predecessors;
|
||||
std::map<MachineBasicBlock *, double> block_freq;
|
||||
std::vector<CFGEdge> edges;
|
||||
};
|
||||
|
||||
CFGAnalysisResult AnalyzeCFG(MachineFunction &function);
|
||||
|
||||
} // namespace mir
|
||||
@ -0,0 +1,177 @@
|
||||
#include "mir/analysis/CFGAnalysis.h"
|
||||
|
||||
#include "mir/MIR.h"
|
||||
|
||||
namespace mir
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
MachineBasicBlock *FindBlockByLabel(MachineFunction &function,
|
||||
int label_id)
|
||||
{
|
||||
if (label_id < 0)
|
||||
return nullptr;
|
||||
for (auto &block : function.GetBlocks())
|
||||
{
|
||||
if (block && block->GetLabelId() == label_id)
|
||||
return block.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BuildSuccessors(MachineFunction &function,
|
||||
CFGAnalysisResult &result)
|
||||
{
|
||||
for (auto &block : function.GetBlocks())
|
||||
{
|
||||
if (!block)
|
||||
continue;
|
||||
const auto &insts = block->GetInstructions();
|
||||
if (insts.empty())
|
||||
continue;
|
||||
|
||||
auto &succs = result.successors[block.get()];
|
||||
for (const auto &inst : insts)
|
||||
{
|
||||
if (inst.GetOpcode() == Opcode::Br)
|
||||
{
|
||||
const auto &ops = inst.GetOperands();
|
||||
if (!ops.empty() && ops[0].GetKind() == Operand::Kind::Label)
|
||||
{
|
||||
auto *target = FindBlockByLabel(function, ops[0].GetLabel());
|
||||
if (target)
|
||||
{
|
||||
bool dup = false;
|
||||
for (auto *s : succs)
|
||||
if (s == target)
|
||||
{
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
if (!dup)
|
||||
succs.push_back(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inst.GetOpcode() == Opcode::CondBr)
|
||||
{
|
||||
const auto &ops = inst.GetOperands();
|
||||
if (ops.size() >= 2 && ops[1].GetKind() == Operand::Kind::Label)
|
||||
{
|
||||
auto *target = FindBlockByLabel(function, ops[1].GetLabel());
|
||||
if (target)
|
||||
{
|
||||
bool dup = false;
|
||||
for (auto *s : succs)
|
||||
if (s == target)
|
||||
{
|
||||
dup = true;
|
||||
break;
|
||||
}
|
||||
if (!dup)
|
||||
succs.push_back(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildPredecessors(CFGAnalysisResult &result)
|
||||
{
|
||||
for (auto &kv : result.successors)
|
||||
{
|
||||
auto *src = kv.first;
|
||||
for (auto *dst : kv.second)
|
||||
{
|
||||
result.predecessors[dst].push_back(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildEdges(CFGAnalysisResult &result)
|
||||
{
|
||||
for (auto &kv : result.successors)
|
||||
{
|
||||
auto *src = kv.first;
|
||||
for (auto *dst : kv.second)
|
||||
{
|
||||
CFGEdge edge;
|
||||
edge.src = src;
|
||||
edge.dst = dst;
|
||||
result.edges.push_back(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EstimateBlockFrequencies(MachineFunction &function,
|
||||
CFGAnalysisResult &result)
|
||||
{
|
||||
if (function.GetBlocks().empty())
|
||||
return;
|
||||
|
||||
auto *entry = function.GetEntryPtr();
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
result.block_freq[entry] = 1.0;
|
||||
for (auto &block : function.GetBlocks())
|
||||
{
|
||||
if (block && block.get() != entry)
|
||||
result.block_freq[block.get()] = 0.0;
|
||||
}
|
||||
|
||||
for (int iter = 0; iter < 20; ++iter)
|
||||
{
|
||||
for (auto &block : function.GetBlocks())
|
||||
{
|
||||
if (!block)
|
||||
continue;
|
||||
auto it = result.successors.find(block.get());
|
||||
if (it == result.successors.end() || it->second.empty())
|
||||
continue;
|
||||
|
||||
double freq = result.block_freq[block.get()];
|
||||
if (freq <= 0.0)
|
||||
continue;
|
||||
|
||||
double per_succ = freq / static_cast<double>(it->second.size());
|
||||
for (auto *succ : it->second)
|
||||
{
|
||||
result.block_freq[succ] += per_succ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeEdgeWeights(CFGAnalysisResult &result)
|
||||
{
|
||||
for (auto &edge : result.edges)
|
||||
{
|
||||
auto it = result.successors.find(edge.src);
|
||||
if (it == result.successors.end() || it->second.empty())
|
||||
continue;
|
||||
double src_freq = 0.0;
|
||||
auto fit = result.block_freq.find(edge.src);
|
||||
if (fit != result.block_freq.end())
|
||||
src_freq = fit->second;
|
||||
edge.weight = src_freq / static_cast<double>(it->second.size());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CFGAnalysisResult AnalyzeCFG(MachineFunction &function)
|
||||
{
|
||||
CFGAnalysisResult result;
|
||||
BuildSuccessors(function, result);
|
||||
BuildPredecessors(result);
|
||||
BuildEdges(result);
|
||||
EstimateBlockFrequencies(function, result);
|
||||
ComputeEdgeWeights(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace mir
|
||||
@ -0,0 +1,370 @@
|
||||
#include "mir/MIR.h"
|
||||
#include "mir/analysis/CFGAnalysis.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mir
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static CondCode NegateCondCode(CondCode cond)
|
||||
{
|
||||
switch (cond)
|
||||
{
|
||||
case CondCode::EQ:
|
||||
return CondCode::NE;
|
||||
case CondCode::NE:
|
||||
return CondCode::EQ;
|
||||
case CondCode::LT:
|
||||
return CondCode::GE;
|
||||
case CondCode::LE:
|
||||
return CondCode::GT;
|
||||
case CondCode::GT:
|
||||
return CondCode::LE;
|
||||
case CondCode::GE:
|
||||
return CondCode::LT;
|
||||
default:
|
||||
return CondCode::NE;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsDebugEnabled()
|
||||
{
|
||||
return std::getenv("UPDATE_COMPILER_BLOCK_LAYOUT_DEBUG") != nullptr;
|
||||
}
|
||||
|
||||
static std::string GetDebugFilter()
|
||||
{
|
||||
const char *filter = std::getenv("UPDATE_COMPILER_BLOCK_LAYOUT_DEBUG_FILTER");
|
||||
return filter ? filter : "";
|
||||
}
|
||||
|
||||
struct Chain
|
||||
{
|
||||
std::vector<MachineBasicBlock *> blocks;
|
||||
double total_weight = 0.0;
|
||||
};
|
||||
|
||||
struct EdgeInfo
|
||||
{
|
||||
MachineBasicBlock *src = nullptr;
|
||||
MachineBasicBlock *dst = nullptr;
|
||||
double weight = 0.0;
|
||||
};
|
||||
|
||||
static std::vector<MachineBasicBlock *> GetOriginalOrder(
|
||||
MachineFunction &function)
|
||||
{
|
||||
std::vector<MachineBasicBlock *> order;
|
||||
for (auto &block : function.GetBlocks())
|
||||
{
|
||||
if (block)
|
||||
order.push_back(block.get());
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
static double ComputeLayoutScore(const std::vector<MachineBasicBlock *> &order,
|
||||
const CFGAnalysisResult &cfg)
|
||||
{
|
||||
double score = 0.0;
|
||||
std::map<MachineBasicBlock *, int> position;
|
||||
for (int i = 0; i < static_cast<int>(order.size()); ++i)
|
||||
position[order[i]] = i;
|
||||
|
||||
for (const auto &edge : cfg.edges)
|
||||
{
|
||||
auto src_it = position.find(edge.src);
|
||||
auto dst_it = position.find(edge.dst);
|
||||
if (src_it == position.end() || dst_it == position.end())
|
||||
continue;
|
||||
if (src_it->second + 1 == dst_it->second)
|
||||
score += edge.weight;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
static std::vector<EdgeInfo> CollectEdges(const CFGAnalysisResult &cfg)
|
||||
{
|
||||
std::vector<EdgeInfo> edges;
|
||||
for (const auto &edge : cfg.edges)
|
||||
{
|
||||
if (edge.src == edge.dst)
|
||||
continue;
|
||||
EdgeInfo ei;
|
||||
ei.src = edge.src;
|
||||
ei.dst = edge.dst;
|
||||
ei.weight = edge.weight;
|
||||
edges.push_back(ei);
|
||||
}
|
||||
std::sort(edges.begin(), edges.end(),
|
||||
[](const EdgeInfo &a, const EdgeInfo &b)
|
||||
{
|
||||
return a.weight > b.weight;
|
||||
});
|
||||
return edges;
|
||||
}
|
||||
|
||||
static std::vector<MachineBasicBlock *> PettisHansenLayout(
|
||||
MachineFunction &function,
|
||||
const CFGAnalysisResult &cfg)
|
||||
{
|
||||
auto &blocks = function.GetBlocks();
|
||||
if (blocks.size() <= 2)
|
||||
return GetOriginalOrder(function);
|
||||
|
||||
std::map<MachineBasicBlock *, Chain *> block_to_chain;
|
||||
std::vector<std::unique_ptr<Chain>> chains;
|
||||
|
||||
for (auto &block : blocks)
|
||||
{
|
||||
if (!block)
|
||||
continue;
|
||||
auto chain = std::make_unique<Chain>();
|
||||
chain->blocks.push_back(block.get());
|
||||
block_to_chain[block.get()] = chain.get();
|
||||
chains.push_back(std::move(chain));
|
||||
}
|
||||
|
||||
auto edges = CollectEdges(cfg);
|
||||
|
||||
for (const auto &edge : edges)
|
||||
{
|
||||
auto *src_chain = block_to_chain[edge.src];
|
||||
auto *dst_chain = block_to_chain[edge.dst];
|
||||
if (!src_chain || !dst_chain)
|
||||
continue;
|
||||
if (src_chain == dst_chain)
|
||||
continue;
|
||||
|
||||
bool src_is_tail = (edge.src == src_chain->blocks.back());
|
||||
bool dst_is_head = (edge.dst == dst_chain->blocks.front());
|
||||
|
||||
if (src_is_tail && dst_is_head)
|
||||
{
|
||||
for (auto *b : dst_chain->blocks)
|
||||
{
|
||||
src_chain->blocks.push_back(b);
|
||||
block_to_chain[b] = src_chain;
|
||||
}
|
||||
src_chain->total_weight += edge.weight;
|
||||
dst_chain->blocks.clear();
|
||||
dst_chain->total_weight = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MachineBasicBlock *> result;
|
||||
std::set<Chain *> emitted;
|
||||
MachineBasicBlock *entry = function.GetEntryPtr();
|
||||
if (entry)
|
||||
{
|
||||
auto *entry_chain = block_to_chain[entry];
|
||||
if (entry_chain && !entry_chain->blocks.empty())
|
||||
{
|
||||
for (auto *b : entry_chain->blocks)
|
||||
result.push_back(b);
|
||||
emitted.insert(entry_chain);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &chain : chains)
|
||||
{
|
||||
if (!chain || chain->blocks.empty())
|
||||
continue;
|
||||
if (emitted.count(chain.get()))
|
||||
continue;
|
||||
for (auto *b : chain->blocks)
|
||||
result.push_back(b);
|
||||
emitted.insert(chain.get());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ApplyLayout(MachineFunction &function,
|
||||
const std::vector<MachineBasicBlock *> &new_order)
|
||||
{
|
||||
auto &blocks = function.GetBlocks();
|
||||
|
||||
std::map<MachineBasicBlock *, std::unique_ptr<MachineBasicBlock>> block_map;
|
||||
for (auto &block : blocks)
|
||||
{
|
||||
if (block)
|
||||
block_map[block.get()] = std::move(block);
|
||||
}
|
||||
|
||||
blocks.clear();
|
||||
for (auto *block : new_order)
|
||||
{
|
||||
auto it = block_map.find(block);
|
||||
if (it != block_map.end())
|
||||
{
|
||||
blocks.push_back(std::move(it->second));
|
||||
block_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &kv : block_map)
|
||||
{
|
||||
blocks.push_back(std::move(kv.second));
|
||||
}
|
||||
}
|
||||
|
||||
static MachineBasicBlock *GetFallThroughTarget(
|
||||
const MachineFunction &function,
|
||||
MachineBasicBlock *block)
|
||||
{
|
||||
const auto &blocks = function.GetBlocks();
|
||||
for (size_t i = 0; i + 1 < blocks.size(); ++i)
|
||||
{
|
||||
if (blocks[i].get() == block)
|
||||
return blocks[i + 1].get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void FixFallThrough(MachineFunction &function)
|
||||
{
|
||||
for (auto &block : function.GetBlocks())
|
||||
{
|
||||
if (!block)
|
||||
continue;
|
||||
auto &insts = block->GetInstructions();
|
||||
|
||||
MachineBasicBlock *fallthrough = GetFallThroughTarget(function, block.get());
|
||||
|
||||
for (auto it = insts.begin(); it != insts.end();)
|
||||
{
|
||||
if (it->GetOpcode() == Opcode::Br)
|
||||
{
|
||||
const auto &ops = it->GetOperands();
|
||||
if (!ops.empty() && ops[0].GetKind() == Operand::Kind::Label)
|
||||
{
|
||||
int target_label = ops[0].GetLabel();
|
||||
if (fallthrough && fallthrough->GetLabelId() == target_label)
|
||||
{
|
||||
it = insts.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
auto rit = insts.rbegin();
|
||||
if (rit != insts.rend() && rit->GetOpcode() == Opcode::CondBr)
|
||||
{
|
||||
auto next_rit = std::next(rit);
|
||||
if (next_rit != insts.rend() && next_rit->GetOpcode() == Opcode::Br)
|
||||
{
|
||||
const auto &cond_ops = rit->GetOperands();
|
||||
const auto &br_ops = next_rit->GetOperands();
|
||||
|
||||
if (cond_ops.size() >= 2 && br_ops.size() >= 1 &&
|
||||
cond_ops[1].GetKind() == Operand::Kind::Label &&
|
||||
br_ops[0].GetKind() == Operand::Kind::Label)
|
||||
{
|
||||
int cond_target = cond_ops[1].GetLabel();
|
||||
int br_target = br_ops[0].GetLabel();
|
||||
|
||||
if (fallthrough)
|
||||
{
|
||||
int ft_label = fallthrough->GetLabelId();
|
||||
if (ft_label == br_target)
|
||||
{
|
||||
// CondBr already falls through to Br target on false, nothing to do
|
||||
}
|
||||
else if (ft_label == cond_target)
|
||||
{
|
||||
CondCode orig_cond = static_cast<CondCode>(cond_ops[0].GetImm());
|
||||
CondCode new_cond = NegateCondCode(orig_cond);
|
||||
|
||||
auto new_condbr = MachineInstr(
|
||||
Opcode::CondBr,
|
||||
{Operand::Imm(static_cast<int>(new_cond)),
|
||||
Operand::Label(br_target)});
|
||||
|
||||
*rit = std::move(new_condbr);
|
||||
|
||||
auto br_it = std::prev(insts.end());
|
||||
insts.erase(br_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RunBlockLayoutOnFunction(MachineFunction &function)
|
||||
{
|
||||
auto &blocks = function.GetBlocks();
|
||||
if (blocks.size() <= 1)
|
||||
return;
|
||||
|
||||
bool debug = IsDebugEnabled();
|
||||
std::string filter = GetDebugFilter();
|
||||
bool should_log = debug;
|
||||
if (!filter.empty() && function.GetName().find(filter) == std::string::npos)
|
||||
should_log = false;
|
||||
|
||||
CFGAnalysisResult cfg = AnalyzeCFG(function);
|
||||
|
||||
auto original_order = GetOriginalOrder(function);
|
||||
double original_score = ComputeLayoutScore(original_order, cfg);
|
||||
|
||||
auto new_order = PettisHansenLayout(function, cfg);
|
||||
double new_score = ComputeLayoutScore(new_order, cfg);
|
||||
|
||||
static constexpr double kBenefitThreshold = 0.01;
|
||||
|
||||
bool should_apply = (new_score > original_score + kBenefitThreshold);
|
||||
|
||||
if (should_log)
|
||||
{
|
||||
std::cerr << "[BlockLayout] " << function.GetName()
|
||||
<< ": original_score=" << original_score
|
||||
<< " candidate_score=" << new_score
|
||||
<< " apply=" << (should_apply ? "yes" : "no") << "\n";
|
||||
}
|
||||
|
||||
if (!should_apply)
|
||||
return;
|
||||
|
||||
ApplyLayout(function, new_order);
|
||||
FixFallThrough(function);
|
||||
|
||||
if (should_log)
|
||||
{
|
||||
auto final_order = GetOriginalOrder(function);
|
||||
double final_score = ComputeLayoutScore(final_order, cfg);
|
||||
std::cerr << "[BlockLayout] " << function.GetName()
|
||||
<< ": final_score=" << final_score << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RunBlockLayout(MachineFunction &function)
|
||||
{
|
||||
RunBlockLayoutOnFunction(function);
|
||||
}
|
||||
|
||||
void RunBlockLayout(MachineModule &module)
|
||||
{
|
||||
for (auto &function : module.GetFunctions())
|
||||
{
|
||||
if (function)
|
||||
RunBlockLayout(*function);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mir
|
||||
Loading…
Reference in new issue