From 1d4c59d7ec14b69697894ec990e58dba6722d5c5 Mon Sep 17 00:00:00 2001 From: zhm <1978583449@qq.com> Date: Tue, 26 May 2026 18:55:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=BF=AB=E9=87=8D=E6=8E=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/include/mir/MIR.h | 3 + src/include/mir/analysis/CFGAnalysis.h | 29 ++ src/main.cpp | 1 + src/mir/CMakeLists.txt | 1 + src/mir/analysis/CFGAnalysis.cpp | 177 ++++++++++++ src/mir/passes/BlockLayoutOpt.cpp | 370 +++++++++++++++++++++++++ src/mir/passes/CMakeLists.txt | 1 + 7 files changed, 582 insertions(+) create mode 100644 src/include/mir/analysis/CFGAnalysis.h create mode 100644 src/mir/analysis/CFGAnalysis.cpp create mode 100644 src/mir/passes/BlockLayoutOpt.cpp diff --git a/src/include/mir/MIR.h b/src/include/mir/MIR.h index dabbd02c..64ff5c4d 100644 --- a/src/include/mir/MIR.h +++ b/src/include/mir/MIR.h @@ -408,6 +408,9 @@ namespace mir void RunPeephole(MachineFunction &function); void RunPeephole(MachineModule &module); + void RunBlockLayout(MachineFunction &function); + void RunBlockLayout(MachineModule &module); + void PrintAsm(const MachineFunction &function, std::ostream &os); void PrintAsm(const MachineModule &module, std::ostream &os); diff --git a/src/include/mir/analysis/CFGAnalysis.h b/src/include/mir/analysis/CFGAnalysis.h new file mode 100644 index 00000000..ea2af5d5 --- /dev/null +++ b/src/include/mir/analysis/CFGAnalysis.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace mir +{ + + class MachineBasicBlock; + class MachineFunction; + + struct CFGEdge + { + MachineBasicBlock *src = nullptr; + MachineBasicBlock *dst = nullptr; + double weight = 0.0; + }; + + struct CFGAnalysisResult + { + std::map> successors; + std::map> predecessors; + std::map block_freq; + std::vector edges; + }; + + CFGAnalysisResult AnalyzeCFG(MachineFunction &function); + +} // namespace mir diff --git a/src/main.cpp b/src/main.cpp index 643a987e..6fb62ae4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ int main(int argc, char** argv) { auto machine_module = mir::LowerModuleToMIR(*module); mir::RunRegAlloc(*machine_module); mir::RunFrameLowering(*machine_module); + mir::RunBlockLayout(*machine_module); mir::RunPeephole(*machine_module); std::ostringstream asm_ss; diff --git a/src/mir/CMakeLists.txt b/src/mir/CMakeLists.txt index 0b0996b4..86362e15 100644 --- a/src/mir/CMakeLists.txt +++ b/src/mir/CMakeLists.txt @@ -8,6 +8,7 @@ add_library(mir_core STATIC RegAlloc.cpp FrameLowering.cpp AsmPrinter.cpp + analysis/CFGAnalysis.cpp ) target_link_libraries(mir_core PUBLIC diff --git a/src/mir/analysis/CFGAnalysis.cpp b/src/mir/analysis/CFGAnalysis.cpp new file mode 100644 index 00000000..0f235726 --- /dev/null +++ b/src/mir/analysis/CFGAnalysis.cpp @@ -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(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(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 diff --git a/src/mir/passes/BlockLayoutOpt.cpp b/src/mir/passes/BlockLayoutOpt.cpp new file mode 100644 index 00000000..df701d5f --- /dev/null +++ b/src/mir/passes/BlockLayoutOpt.cpp @@ -0,0 +1,370 @@ +#include "mir/MIR.h" +#include "mir/analysis/CFGAnalysis.h" + +#include +#include +#include +#include +#include +#include +#include + +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 blocks; + double total_weight = 0.0; + }; + + struct EdgeInfo + { + MachineBasicBlock *src = nullptr; + MachineBasicBlock *dst = nullptr; + double weight = 0.0; + }; + + static std::vector GetOriginalOrder( + MachineFunction &function) + { + std::vector order; + for (auto &block : function.GetBlocks()) + { + if (block) + order.push_back(block.get()); + } + return order; + } + + static double ComputeLayoutScore(const std::vector &order, + const CFGAnalysisResult &cfg) + { + double score = 0.0; + std::map position; + for (int i = 0; i < static_cast(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 CollectEdges(const CFGAnalysisResult &cfg) + { + std::vector 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 PettisHansenLayout( + MachineFunction &function, + const CFGAnalysisResult &cfg) + { + auto &blocks = function.GetBlocks(); + if (blocks.size() <= 2) + return GetOriginalOrder(function); + + std::map block_to_chain; + std::vector> chains; + + for (auto &block : blocks) + { + if (!block) + continue; + auto chain = std::make_unique(); + 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 result; + std::set 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 &new_order) + { + auto &blocks = function.GetBlocks(); + + std::map> 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(cond_ops[0].GetImm()); + CondCode new_cond = NegateCondCode(orig_cond); + + auto new_condbr = MachineInstr( + Opcode::CondBr, + {Operand::Imm(static_cast(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 diff --git a/src/mir/passes/CMakeLists.txt b/src/mir/passes/CMakeLists.txt index 3b97b4f7..fdf46a93 100644 --- a/src/mir/passes/CMakeLists.txt +++ b/src/mir/passes/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(mir_passes STATIC PassManager.cpp Peephole.cpp + BlockLayoutOpt.cpp ) target_link_libraries(mir_passes PUBLIC