标量优化

mirror
mirror 1 week ago
parent 46dad22baa
commit 41be171f0a

@ -197,6 +197,7 @@ enum class Opcode {
Store,
Ret,
Gep, // getelementptr数组元素地址计算
Phi, // SSA phi 节点
};
enum class CmpOp { Eq, Ne, Lt, Le, Gt, Ge };
@ -214,6 +215,8 @@ class User : public Value {
protected:
// 统一的 operand 入口。
void AddOperand(Value* value);
// 清空所有 operand不清除 use 关系,调用者需自行处理)。
void ClearOperands();
private:
std::vector<Value*> operands_;
@ -355,6 +358,21 @@ class GepInst : public Instruction {
Value* GetIndex() const;
};
// PhiInstSSA phi 节点,用于控制流汇合点合并不同前驱传来的值。
// 操作数布局:[val_0, bb_0, val_1, bb_1, ...]
class PhiInst : public Instruction {
public:
PhiInst(std::shared_ptr<Type> ty, std::string name);
// 添加一组 (value, incoming_block) 入边。
void AddIncoming(Value* val, BasicBlock* bb);
size_t GetNumIncoming() const;
Value* GetIncomingValue(size_t i) const;
BasicBlock* GetIncomingBlock(size_t i) const;
void SetIncomingValue(size_t i, Value* val);
// 移除来自指定前驱块的入边。
void RemoveIncomingBlock(BasicBlock* bb);
};
// BasicBlock 已纳入 Value 体系,便于后续向更完整 IR 类图靠拢。
// 当前其类型仍使用 void 作为占位,后续可替换为专门的 label type。
class BasicBlock : public Value {
@ -364,10 +382,30 @@ class BasicBlock : public Value {
void SetParent(Function* parent);
bool HasTerminator() const;
const std::vector<std::unique_ptr<Instruction>>& GetInstructions() const;
std::vector<std::unique_ptr<Instruction>>& MutableInstructions();
const std::vector<BasicBlock*>& GetPredecessors() const;
const std::vector<BasicBlock*>& GetSuccessors() const;
std::vector<BasicBlock*>& MutablePredecessors();
std::vector<BasicBlock*>& MutableSuccessors();
void AddPredecessor(BasicBlock* pred);
void AddSuccessor(BasicBlock* succ);
void RemovePredecessor(BasicBlock* pred);
void RemoveSuccessor(BasicBlock* succ);
// 在块头部(所有 phi 之后)插入指令。
template <typename T, typename... Args>
T* Prepend(Args&&... args) {
auto inst = std::make_unique<T>(std::forward<Args>(args)...);
auto* ptr = inst.get();
ptr->SetParent(this);
// 插入到第一条非-phi 指令之前
auto it = instructions_.begin();
while (it != instructions_.end() &&
(*it)->GetOpcode() == Opcode::Phi) {
++it;
}
instructions_.insert(it, std::move(inst));
return ptr;
}
template <typename T, typename... Args>
T* Append(Args&&... args) {
if (HasTerminator()) {
@ -380,6 +418,12 @@ class BasicBlock : public Value {
instructions_.push_back(std::move(inst));
return ptr;
}
// 在块的最前面插入 phi 节点。
PhiInst* PrependPhi(std::shared_ptr<Type> ty, const std::string& name);
// 删除指定指令(从块中移除 ownership
void RemoveInstruction(Instruction* inst);
// 判断块是否为空(不含任何指令)。
bool IsEmpty() const { return instructions_.empty(); }
private:
Function* parent_ = nullptr;
@ -403,6 +447,9 @@ class Function : public Value {
size_t GetNumParams() const;
Argument* GetArgument(size_t index) const;
const std::vector<std::unique_ptr<BasicBlock>>& GetBlocks() const;
std::vector<std::unique_ptr<BasicBlock>>& MutableBlocks();
// 删除指定基本块(从函数中移除 ownership
void RemoveBlock(BasicBlock* bb);
// 外部函数声明(无函数体,打印为 declare
void SetExternal(bool v) { is_external_ = v; }

@ -0,0 +1,266 @@
#!/usr/bin/env bash
# 批量回归测试脚本:对 test/test_case 下全部 .sy 用例执行 IR 语义验证。
# 用法:./scripts/run_all_tests.sh [--ir | --asm | --both]
#
# 默认只测 IR通过 llc + clang 编译运行)。
# --asm 只测汇编(需要 aarch64-linux-gnu-gcc + qemu-aarch64
# --both 同时测 IR 和汇编。
set -uo pipefail
mode="ir"
if [[ "${1:-}" == "--asm" ]]; then
mode="asm"
elif [[ "${1:-}" == "--both" ]]; then
mode="both"
fi
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$ROOT_DIR"
compiler="./build/bin/compiler"
if [[ ! -x "$compiler" ]]; then
echo "❌ 未找到编译器: $compiler" >&2
echo "请先构建cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j \$(nproc)" >&2
exit 1
fi
total=0
passed=0
failed=0
skipped=0
fail_list=()
run_ir_test() {
local sy="$1"
local dir
dir=$(dirname "$sy")
local stem
stem=$(basename "$sy" .sy)
local out_dir="test/test_result/ir_batch"
mkdir -p "$out_dir"
local out_file="$out_dir/$stem.ll"
local stdin_file="$dir/$stem.in"
local expected_file="$dir/$stem.out"
local stdout_file="$out_dir/$stem.stdout"
local actual_file="$out_dir/$stem.actual.out"
# 生成 IR
if ! timeout 30 "$compiler" --emit-ir "$sy" > "$out_file" 2>/dev/null; then
echo " [SKIP-IR] $sy (编译器报错或超时)"
return 2
fi
# 需要 llc + clang
if ! command -v llc >/dev/null 2>&1 || ! command -v clang >/dev/null 2>&1; then
echo " [SKIP-IR] $sy (缺少 llc/clang)"
return 2
fi
local obj="$out_dir/$stem.o"
local exe="$out_dir/$stem"
if ! llc -filetype=obj "$out_file" -o "$obj" 2>/dev/null; then
echo " [SKIP-IR] $sy (llc 编译失败)"
return 2
fi
if ! clang -no-pie "$obj" sylib/sylib.c -o "$exe" -lm 2>/dev/null; then
echo " [SKIP-IR] $sy (clang 链接失败)"
return 2
fi
set +e
# performance 用例给更长的超时时间
local run_timeout=30
if [[ "$sy" == *"performance"* ]]; then
run_timeout=300
fi
if [[ -f "$stdin_file" ]]; then
timeout $run_timeout "$exe" < "$stdin_file" > "$stdout_file" 2>/dev/null
else
timeout $run_timeout "$exe" > "$stdout_file" 2>/dev/null
fi
local status=$?
set -e
# timeout 返回 124 表示超时,标记为 SKIP
if [[ $status -eq 124 ]]; then
echo " [SKIP-IR] $sy (运行超时)"
return 2
fi
# 组装实际输出
{
cat "$stdout_file"
if [[ -s "$stdout_file" ]] && (( $(tail -c 1 "$stdout_file" | wc -l) == 0 )); then
printf '\n'
fi
printf '%s\n' "$status"
} > "$actual_file"
if [[ ! -f "$expected_file" ]]; then
echo " [SKIP-IR] $sy (无预期输出)"
return 2
fi
if diff -q <(sed -e 's/\r$//' -e '$a\\' "$expected_file") \
<(sed -e 's/\r$//' -e '$a\\' "$actual_file") >/dev/null 2>&1; then
return 0
else
return 1
fi
}
run_asm_test() {
local sy="$1"
local dir
dir=$(dirname "$sy")
local stem
stem=$(basename "$sy" .sy)
local out_dir="test/test_result/asm_batch"
mkdir -p "$out_dir"
local asm_file="$out_dir/$stem.s"
local stdin_file="$dir/$stem.in"
local expected_file="$dir/$stem.out"
local stdout_file="$out_dir/$stem.stdout"
local actual_file="$out_dir/$stem.actual.out"
local exe="$out_dir/$stem"
# 生成汇编
if ! timeout 30 "$compiler" --emit-asm "$sy" > "$asm_file" 2>/dev/null; then
echo " [SKIP-ASM] $sy (编译器报错或超时)"
return 2
fi
if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then
echo " [SKIP-ASM] $sy (缺少 aarch64-linux-gnu-gcc)"
return 2
fi
if ! timeout 30 aarch64-linux-gnu-gcc "$asm_file" sylib/sylib.c -o "$exe" -static 2>/dev/null; then
echo " [SKIP-ASM] $sy (汇编/链接失败)"
return 2
fi
if ! command -v qemu-aarch64 >/dev/null 2>&1; then
echo " [SKIP-ASM] $sy (缺少 qemu-aarch64)"
return 2
fi
set +e
# performance 用例给更长的超时时间
local run_timeout=30
if [[ "$sy" == *"performance"* ]]; then
run_timeout=300
fi
if [[ -f "$stdin_file" ]]; then
timeout $run_timeout qemu-aarch64 -L /usr/aarch64-linux-gnu "$exe" < "$stdin_file" > "$stdout_file" 2>/dev/null
else
timeout $run_timeout qemu-aarch64 -L /usr/aarch64-linux-gnu "$exe" > "$stdout_file" 2>/dev/null
fi
local status=$?
set -e
# timeout 返回 124 表示超时,标记为 SKIP
if [[ $status -eq 124 ]]; then
echo " [SKIP-ASM] $sy (运行超时)"
return 2
fi
{
cat "$stdout_file"
if [[ -s "$stdout_file" ]] && (( $(tail -c 1 "$stdout_file" | wc -l) == 0 )); then
printf '\n'
fi
printf '%s\n' "$status"
} > "$actual_file"
if [[ ! -f "$expected_file" ]]; then
echo " [SKIP-ASM] $sy (无预期输出)"
return 2
fi
if diff -q <(sed -e 's/\r$//' -e '$a\\' "$expected_file") \
<(sed -e 's/\r$//' -e '$a\\' "$actual_file") >/dev/null 2>&1; then
return 0
else
return 1
fi
}
echo "========================================"
echo " Lab4 批量回归测试 (mode: $mode)"
echo "========================================"
echo ""
# 收集所有测试文件
mapfile -t test_files < <(find test/test_case -name '*.sy' | sort)
for sy in "${test_files[@]}"; do
total=$((total + 1))
if [[ "$mode" == "ir" || "$mode" == "both" ]]; then
run_ir_test "$sy"
rc=$?
if [[ $rc -eq 0 ]]; then
echo " [PASS-IR] $sy"
passed=$((passed + 1))
elif [[ $rc -eq 1 ]]; then
echo " [FAIL-IR] $sy"
failed=$((failed + 1))
fail_list+=("$sy (IR)")
else
skipped=$((skipped + 1))
fi
fi
if [[ "$mode" == "asm" || "$mode" == "both" ]]; then
run_asm_test "$sy"
rc=$?
if [[ $rc -eq 0 ]]; then
echo " [PASS-ASM] $sy"
if [[ "$mode" == "asm" ]]; then
passed=$((passed + 1))
fi
elif [[ $rc -eq 1 ]]; then
echo " [FAIL-ASM] $sy"
if [[ "$mode" == "asm" ]]; then
failed=$((failed + 1))
fi
fail_list+=("$sy (ASM)")
else
if [[ "$mode" == "asm" ]]; then
skipped=$((skipped + 1))
fi
fi
fi
done
echo ""
echo "========================================"
echo " 测试结果汇总"
echo "========================================"
echo " 总计: $total"
echo " 通过: $passed"
echo " 失败: $failed"
echo " 跳过: $skipped"
echo ""
if [[ ${#fail_list[@]} -gt 0 ]]; then
echo " 失败用例:"
for f in "${fail_list[@]}"; do
echo " - $f"
done
echo ""
fi
if [[ $failed -gt 0 ]]; then
echo "❌ 存在失败用例"
exit 1
else
echo "✅ 全部通过(跳过 $skipped 个)"
exit 0
fi

@ -65,4 +65,55 @@ void BasicBlock::AddSuccessor(BasicBlock* succ) {
successors_.push_back(succ);
}
std::vector<std::unique_ptr<Instruction>>& BasicBlock::MutableInstructions() {
return instructions_;
}
std::vector<BasicBlock*>& BasicBlock::MutablePredecessors() {
return predecessors_;
}
std::vector<BasicBlock*>& BasicBlock::MutableSuccessors() {
return successors_;
}
void BasicBlock::RemovePredecessor(BasicBlock* pred) {
predecessors_.erase(
std::remove(predecessors_.begin(), predecessors_.end(), pred),
predecessors_.end());
}
void BasicBlock::RemoveSuccessor(BasicBlock* succ) {
successors_.erase(
std::remove(successors_.begin(), successors_.end(), succ),
successors_.end());
}
PhiInst* BasicBlock::PrependPhi(std::shared_ptr<Type> ty,
const std::string& name) {
auto inst = std::make_unique<PhiInst>(std::move(ty), name);
auto* ptr = inst.get();
ptr->SetParent(this);
instructions_.insert(instructions_.begin(), std::move(inst));
return ptr;
}
void BasicBlock::RemoveInstruction(Instruction* inst) {
if (!inst) return;
// 清除该指令所有操作数的 use 关系
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
auto* operand = inst->GetOperand(i);
if (operand) {
operand->RemoveUse(inst, i);
}
}
inst->SetParent(nullptr);
instructions_.erase(
std::remove_if(instructions_.begin(), instructions_.end(),
[inst](const std::unique_ptr<Instruction>& p) {
return p.get() == inst;
}),
instructions_.end());
}
} // namespace ir

@ -3,6 +3,7 @@
// - 记录函数属性/元信息(按需要扩展)
#include "ir/IR.h"
#include <algorithm>
#include <stdexcept>
#include "utils/Log.h"
@ -55,4 +56,25 @@ const std::vector<std::unique_ptr<BasicBlock>>& Function::GetBlocks() const {
return blocks_;
}
std::vector<std::unique_ptr<BasicBlock>>& Function::MutableBlocks() {
return blocks_;
}
void Function::RemoveBlock(BasicBlock* bb) {
if (!bb) return;
if (bb == entry_) {
entry_ = nullptr;
}
bb->SetParent(nullptr);
blocks_.erase(
std::remove_if(blocks_.begin(), blocks_.end(),
[bb](const std::unique_ptr<BasicBlock>& p) {
return p.get() == bb;
}),
blocks_.end());
if (!entry_ && !blocks_.empty()) {
entry_ = blocks_.front().get();
}
}
} // namespace ir

@ -62,6 +62,8 @@ static const char* OpcodeToString(Opcode op) {
return "ret";
case Opcode::Gep:
return "getelementptr";
case Opcode::Phi:
return "phi";
}
return "?";
}
@ -275,6 +277,18 @@ void IRPrinter::Print(const Module& module, std::ostream& os) {
}
break;
}
case Opcode::Phi: {
auto* phi = static_cast<const PhiInst*>(inst);
os << " " << phi->GetName() << " = phi "
<< TypeToString(*phi->GetType()) << " ";
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
if (i != 0) os << ", ";
os << "[ " << ValueToString(phi->GetIncomingValue(i))
<< ", %" << phi->GetIncomingBlock(i)->GetName() << " ]";
}
os << "\n";
break;
}
}
}
}

@ -70,6 +70,10 @@ void User::AddOperand(Value* value) {
value->AddUse(this, operand_index);
}
void User::ClearOperands() {
operands_.clear();
}
Instruction::Instruction(Opcode op, std::shared_ptr<Type> ty, std::string name)
: User(std::move(ty), std::move(name)), opcode_(op) {}
@ -370,4 +374,53 @@ GepInst::GepInst(std::shared_ptr<Type> ptr_ty, Value* base, Value* index,
Value* GepInst::GetBase() const { return GetOperand(0); }
Value* GepInst::GetIndex() const { return GetOperand(1); }
// ---- PhiInst ----
PhiInst::PhiInst(std::shared_ptr<Type> ty, std::string name)
: Instruction(Opcode::Phi, std::move(ty), std::move(name)) {}
void PhiInst::AddIncoming(Value* val, BasicBlock* bb) {
if (!val || !bb) {
throw std::runtime_error(FormatError("ir", "PhiInst::AddIncoming 参数不完整"));
}
AddOperand(val);
AddOperand(bb);
}
size_t PhiInst::GetNumIncoming() const { return GetNumOperands() / 2; }
Value* PhiInst::GetIncomingValue(size_t i) const {
return GetOperand(i * 2);
}
BasicBlock* PhiInst::GetIncomingBlock(size_t i) const {
return static_cast<BasicBlock*>(GetOperand(i * 2 + 1));
}
void PhiInst::SetIncomingValue(size_t i, Value* val) {
SetOperand(i * 2, val);
}
void PhiInst::RemoveIncomingBlock(BasicBlock* bb) {
// 收集需要保留的 (val, bb) 对
std::vector<std::pair<Value*, BasicBlock*>> keep;
for (size_t i = 0; i < GetNumIncoming(); ++i) {
if (GetIncomingBlock(i) != bb) {
keep.push_back({GetIncomingValue(i), GetIncomingBlock(i)});
}
}
// 清除旧的 use 关系
for (size_t i = 0; i < GetNumOperands(); ++i) {
auto* old = GetOperand(i);
if (old) old->RemoveUse(this, i);
}
// 清空 operand 列表
ClearOperands();
// 重建保留的入边
for (auto& [val, blk] : keep) {
AddOperand(val);
AddOperand(blk);
}
}
} // namespace ir

@ -1,4 +1,171 @@
// 支配树分析:
// - 构建/查询 Dominator Tree 及相关关系
// - 为 mem2reg、CFG 优化与循环分析提供基础能力
//
// 算法简单迭代数据流方式计算支配关系Cooper, Harvey, Kennedy
// 支配边界采用经典 DF 算法
#include "ir/IR.h"
#include <algorithm>
#include <cassert>
#include <functional>
#include <queue>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace ir {
namespace analysis {
// ---------- DominatorTree ----------
class DominatorTree {
public:
explicit DominatorTree(Function& func) : func_(func) { Compute(); }
// idom[bb] 返回 bb 的直接支配者entry 的 idom 为自身。
BasicBlock* GetIDom(BasicBlock* bb) const {
auto it = idom_.find(bb);
return it != idom_.end() ? it->second : nullptr;
}
// 判断 a 是否支配 b。
bool Dominates(BasicBlock* a, BasicBlock* b) const {
if (!a || !b) return false;
while (b) {
if (b == a) return true;
auto* p = GetIDom(b);
if (p == b) break; // entry
b = p;
}
return false;
}
// 返回 bb 的支配边界。
const std::vector<BasicBlock*>& GetDF(BasicBlock* bb) const {
static const std::vector<BasicBlock*> empty;
auto it = df_.find(bb);
return it != df_.end() ? it->second : empty;
}
// 返回支配树中 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;
}
// 按逆后序返回所有基本块。
const std::vector<BasicBlock*>& GetRPO() const { return rpo_; }
private:
void Compute() {
auto* entry = func_.GetEntry();
if (!entry) return;
// 1. 计算逆后序RPO
ComputeRPO(entry);
if (rpo_.empty()) return;
// 2. 初始化
for (auto* bb : rpo_) {
idom_[bb] = nullptr;
rpo_index_[bb] = 0;
}
for (size_t i = 0; i < rpo_.size(); ++i) {
rpo_index_[rpo_[i]] = i;
}
idom_[entry] = entry;
// 3. 迭代计算 idomCooper-Harvey-Kennedy 算法)
bool changed = true;
while (changed) {
changed = false;
for (auto* bb : rpo_) {
if (bb == entry) continue;
BasicBlock* new_idom = nullptr;
for (auto* pred : bb->GetPredecessors()) {
if (idom_.count(pred) && idom_[pred] != nullptr) {
if (!new_idom) {
new_idom = pred;
} else {
new_idom = Intersect(new_idom, pred);
}
}
}
if (new_idom && idom_[bb] != new_idom) {
idom_[bb] = new_idom;
changed = true;
}
}
}
// 4. 建立 children 映射
for (auto* bb : rpo_) {
auto* p = GetIDom(bb);
if (p && p != bb) {
children_[p].push_back(bb);
}
}
// 5. 计算支配边界
ComputeDF();
}
void ComputeRPO(BasicBlock* entry) {
std::unordered_set<BasicBlock*> visited;
std::vector<BasicBlock*> post_order;
std::function<void(BasicBlock*)> dfs = [&](BasicBlock* bb) {
visited.insert(bb);
for (auto* succ : bb->GetSuccessors()) {
if (!visited.count(succ)) {
dfs(succ);
}
}
post_order.push_back(bb);
};
dfs(entry);
rpo_.assign(post_order.rbegin(), post_order.rend());
}
BasicBlock* Intersect(BasicBlock* b1, BasicBlock* b2) {
while (b1 != b2) {
while (rpo_index_[b1] > rpo_index_[b2]) b1 = idom_[b1];
while (rpo_index_[b2] > rpo_index_[b1]) b2 = idom_[b2];
}
return b1;
}
void ComputeDF() {
for (auto* bb : rpo_) {
df_[bb] = {};
}
for (auto* bb : rpo_) {
if (bb->GetPredecessors().size() < 2) continue;
for (auto* pred : bb->GetPredecessors()) {
auto* runner = pred;
while (runner && runner != idom_[bb]) {
// 避免重复
auto& df_set = df_[runner];
if (std::find(df_set.begin(), df_set.end(), bb) == df_set.end()) {
df_set.push_back(bb);
}
if (runner == idom_[runner]) break;
runner = idom_[runner];
}
}
}
}
Function& func_;
std::vector<BasicBlock*> rpo_;
std::unordered_map<BasicBlock*, size_t> rpo_index_;
std::unordered_map<BasicBlock*, BasicBlock*> idom_;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> children_;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> df_;
};
} // namespace analysis
} // namespace ir

@ -1,4 +1,190 @@
// CFG 简化:
// - 删除不可达块、合并空块、简化分支等
// - 改善 IR 结构,便于后续优化与后端生成
//
// 包含以下简化:
// 1. 常量条件分支折叠condbr(const) -> br
// 2. 删除不可达块
// 3. 合并只有一个前驱的后继块(线性块合并)
// 4. 跳过空的跳转块(线程跳转)
#include "ir/IR.h"
#include <algorithm>
#include <functional>
#include <queue>
#include <unordered_set>
#include <vector>
namespace ir {
namespace passes {
// 收集从 entry 可达的所有基本块
static std::unordered_set<BasicBlock*> ComputeReachable(Function& func) {
std::unordered_set<BasicBlock*> reachable;
auto* entry = func.GetEntry();
if (!entry) return reachable;
std::queue<BasicBlock*> worklist;
worklist.push(entry);
reachable.insert(entry);
while (!worklist.empty()) {
auto* bb = worklist.front();
worklist.pop();
for (auto* succ : bb->GetSuccessors()) {
if (!reachable.count(succ)) {
reachable.insert(succ);
worklist.push(succ);
}
}
}
return reachable;
}
bool RunCFGSimplify(Function& func, Context& ctx) {
if (func.IsExternal()) return false;
bool changed = false;
// ==== 1. 常量条件分支折叠 ====
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
if (!bb->HasTerminator()) continue;
auto& insts = bb->MutableInstructions();
auto* last = insts.back().get();
if (last->GetOpcode() == Opcode::CondBr) {
auto* cbr = static_cast<CondBranchInst*>(last);
auto* cond_ci = dynamic_cast<ConstantInt*>(cbr->GetCond());
if (cond_ci) {
BasicBlock* taken = cond_ci->GetValue() != 0
? cbr->GetTrueBlock()
: cbr->GetFalseBlock();
BasicBlock* not_taken = cond_ci->GetValue() != 0
? cbr->GetFalseBlock()
: cbr->GetTrueBlock();
// 从 not_taken 的前驱中移除当前块
not_taken->RemovePredecessor(bb.get());
bb->RemoveSuccessor(not_taken);
// 移除 condbr插入 br
bb->RemoveInstruction(last);
bb->Append<BranchInst>(Type::GetVoidType(), taken);
// 清理 not_taken 中 phi 的来自 bb 的入边
for (auto& inst_ptr : not_taken->MutableInstructions()) {
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
if (!phi) break;
phi->RemoveIncomingBlock(bb.get());
}
changed = true;
}
}
}
// ==== 2. 删除不可达块 ====
auto reachable = ComputeReachable(func);
std::vector<BasicBlock*> unreachable;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
if (!reachable.count(bb.get())) {
unreachable.push_back(bb.get());
}
}
for (auto* bb : unreachable) {
// 从后继的前驱列表中移除
for (auto* succ : bb->GetSuccessors()) {
succ->RemovePredecessor(bb);
}
// 清除块中所有指令的 use 关系
std::vector<Instruction*> all_insts;
for (auto& inst_ptr : bb->MutableInstructions()) {
all_insts.push_back(inst_ptr.get());
}
for (auto* inst : all_insts) {
// 如果指令还有使用者,用 undef (0) 替换
if (!inst->GetUses().empty()) {
if (inst->GetType() && inst->GetType()->IsInt32()) {
inst->ReplaceAllUsesWith(ctx.GetConstInt(0));
}
}
bb->RemoveInstruction(inst);
}
func.RemoveBlock(bb);
changed = true;
}
// ==== 3. 合并线性块 ====
// 如果一个块 B 只有一个前驱 A且 A 只有一个后继 B
// 则将 B 的指令合并到 A 的末尾。
bool merged = true;
while (merged) {
merged = false;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
if (bb->GetPredecessors().size() != 1) continue;
auto* pred = bb->GetPredecessors()[0];
if (pred->GetSuccessors().size() != 1) continue;
if (pred == bb.get()) continue; // 自循环
// pred 的 terminator 必须是 br无条件跳转到 bb
if (!pred->HasTerminator()) continue;
auto& pred_insts = pred->MutableInstructions();
auto* term = pred_insts.back().get();
if (term->GetOpcode() != Opcode::Br) continue;
// 删除 pred 的 terminator
pred->RemoveInstruction(term);
// 将 bb 的所有指令移到 pred
auto& bb_insts = bb->MutableInstructions();
for (auto& inst_ptr : bb_insts) {
inst_ptr->SetParent(pred);
}
for (auto& inst_ptr : bb_insts) {
pred_insts.push_back(std::move(inst_ptr));
}
bb_insts.clear();
// 更新 CFGpred 继承 bb 的后继
pred->MutableSuccessors().clear();
for (auto* succ : bb->GetSuccessors()) {
pred->AddSuccessor(succ);
// 在 succ 的前驱中把 bb 替换为 pred
auto& succ_preds = succ->MutablePredecessors();
for (auto& p : succ_preds) {
if (p == bb.get()) p = pred;
}
// 更新 succ 中 phi 的入边
for (auto& inst_ptr : succ->MutableInstructions()) {
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
if (!phi) break;
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
if (phi->GetIncomingBlock(i) == bb.get()) {
phi->SetOperand(i * 2 + 1, pred);
}
}
}
}
// 移除 bb
bb->MutablePredecessors().clear();
bb->MutableSuccessors().clear();
func.RemoveBlock(bb.get());
merged = true;
changed = true;
break; // 重新开始迭代
}
}
return changed;
}
} // namespace passes
} // namespace ir

@ -1,4 +1,123 @@
// 公共子表达式消除CSE
// - 识别并复用重复计算的等价表达式
// - 典型放置在 ConstFold 之后、DCE 之前
// - 当前为 Lab4 的框架占位,具体算法由实验实现
//
// 算法:在每个基本块内,使用哈希表记录已出现的表达式。
// 当遇到相同操作码 + 相同操作数的指令时,复用之前的结果。
// 这是局部 CSELocal CSE只在基本块内消除。
#include "ir/IR.h"
#include <string>
#include <unordered_map>
#include <vector>
namespace ir {
namespace passes {
namespace {
// 构造表达式的唯一键opcode + operands 的组合
struct ExprKey {
Opcode opcode;
CmpOp cmp_op; // 仅 Cmp 使用
std::vector<Value*> operands;
bool operator==(const ExprKey& other) const {
if (opcode != other.opcode) return false;
if (opcode == Opcode::Cmp && cmp_op != other.cmp_op) return false;
if (operands.size() != other.operands.size()) return false;
for (size_t i = 0; i < operands.size(); ++i) {
if (operands[i] != other.operands[i]) return false;
}
return true;
}
};
struct ExprKeyHash {
size_t operator()(const ExprKey& key) const {
size_t h = std::hash<int>()(static_cast<int>(key.opcode));
if (key.opcode == Opcode::Cmp) {
h ^= std::hash<int>()(static_cast<int>(key.cmp_op)) << 4;
}
for (auto* v : key.operands) {
h ^= std::hash<void*>()(v) + 0x9e3779b9 + (h << 6) + (h >> 2);
}
return h;
}
};
// 判断一条指令是否可以做 CSE
bool IsCSECandidate(Instruction* inst) {
Opcode op = inst->GetOpcode();
// 纯计算指令可以做 CSE
switch (op) {
case Opcode::Add:
case Opcode::Sub:
case Opcode::Mul:
case Opcode::Div:
case Opcode::Mod:
case Opcode::Cmp:
case Opcode::Gep:
return true;
default:
return false;
}
}
ExprKey MakeKey(Instruction* inst) {
ExprKey key;
key.opcode = inst->GetOpcode();
key.cmp_op = CmpOp::Eq; // 默认值
if (inst->GetOpcode() == Opcode::Cmp) {
key.cmp_op = static_cast<CmpInst*>(inst)->GetCmpOp();
}
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
key.operands.push_back(inst->GetOperand(i));
}
return key;
}
} // namespace
bool RunCSE(Function& func) {
if (func.IsExternal()) return false;
bool changed = false;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
std::unordered_map<ExprKey, Instruction*, ExprKeyHash> expr_map;
std::vector<Instruction*> to_remove;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
if (!IsCSECandidate(inst)) continue;
ExprKey key = MakeKey(inst);
auto it = expr_map.find(key);
if (it != expr_map.end()) {
// 找到了等价表达式,复用之前的结果
inst->ReplaceAllUsesWith(it->second);
to_remove.push_back(inst);
changed = true;
} else {
expr_map[key] = inst;
}
}
for (auto* inst : to_remove) {
bb->RemoveInstruction(inst);
}
}
return changed;
}
} // namespace passes
} // namespace ir

@ -1,4 +1,273 @@
// IR 常量折叠:
// - 折叠可判定的常量表达式
// - 简化常量控制流分支(按实现范围裁剪)
//
// 遍历每个函数中的每条指令,如果操作数全为常量,则编译期求值并替换。
#include "ir/IR.h"
#include <vector>
namespace ir {
namespace passes {
bool RunConstFold(Function& func) {
if (func.IsExternal()) return false;
bool changed = false;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
std::vector<Instruction*> to_remove;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
Opcode op = inst->GetOpcode();
// 二元运算折叠
if (op == Opcode::Add || op == Opcode::Sub || op == Opcode::Mul ||
op == Opcode::Div || op == Opcode::Mod) {
auto* bin = static_cast<BinaryInst*>(inst);
auto* lhs_ci = dynamic_cast<ConstantInt*>(bin->GetLhs());
auto* rhs_ci = dynamic_cast<ConstantInt*>(bin->GetRhs());
if (lhs_ci && rhs_ci) {
int lv = lhs_ci->GetValue();
int rv = rhs_ci->GetValue();
int result = 0;
bool valid = true;
switch (op) {
case Opcode::Add: result = lv + rv; break;
case Opcode::Sub: result = lv - rv; break;
case Opcode::Mul: result = lv * rv; break;
case Opcode::Div:
if (rv == 0) { valid = false; break; }
result = lv / rv;
break;
case Opcode::Mod:
if (rv == 0) { valid = false; break; }
result = lv % rv;
break;
default: valid = false; break;
}
if (valid) {
// 需要 Context 来创建常量,通过 entry block 获取
auto& ctx = bb->GetParent()->GetBlocks().front()->GetParent()
? *bb->GetParent()
: *bb->GetParent();
// 直接在 uses 上替换:找到结果常量
// 由于 ConstantInt 由 Context 管理,我们需要 Module 的 Context。
// 但 Function 没有直接指向 Module 的指针。
// Workaround: 遍历 uses 替换时用已存在的 ConstantInt。
// 实际上,我们可以在 PassManager 中传入 Module& 引用。
// 这里先用简单方法:检查 lhs_ci 或 rhs_ci 的值是否与 result 相同。
ConstantInt* result_ci = nullptr;
if (lhs_ci->GetValue() == result) {
result_ci = lhs_ci;
} else if (rhs_ci->GetValue() == result) {
result_ci = rhs_ci;
}
// 如果没有现成常量,暂时跳过(由 PassManager 传入 Context 后再处理)
// 实际上让 PassManager 传入 Module 是更好的做法。
// 这里我们假设 RunConstFold 接收的是 Module 级别的调用。
// 先标记但不替换,等后续改进。
// 更好的方案:利用 bin 的 parent 的 parent (Function) 暂存。
// 但 Function 也没有 Context。
// 最终方案:在 PassManager 中传入 Context&。
// 简化:这里先不做替换,留给 ConstProp + PassManager 配合完成。
// 实际上我们可以直接用 new ConstantInt但这会导致内存泄漏。
// 正确方案:让 RunConstFold 接受 Context& 参数。
(void)result_ci;
(void)result;
}
}
continue;
}
// 比较指令折叠
if (op == Opcode::Cmp) {
auto* cmp = static_cast<CmpInst*>(inst);
auto* lhs_ci = dynamic_cast<ConstantInt*>(cmp->GetLhs());
auto* rhs_ci = dynamic_cast<ConstantInt*>(cmp->GetRhs());
if (lhs_ci && rhs_ci) {
// 同上,需要 Context 创建结果常量
(void)cmp;
}
continue;
}
// 常量条件分支折叠
if (op == Opcode::CondBr) {
auto* cbr = static_cast<CondBranchInst*>(inst);
auto* cond_ci = dynamic_cast<ConstantInt*>(cbr->GetCond());
if (cond_ci) {
// 同上,需要修改 BB 的 terminator
(void)cbr;
}
continue;
}
}
for (auto* inst : to_remove) {
bb->RemoveInstruction(inst);
}
}
return changed;
}
// 接受 Module 引用的版本,可以使用 Context 创建常量
bool RunConstFoldWithCtx(Function& func, Context& ctx) {
if (func.IsExternal()) return false;
bool changed = false;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
std::vector<Instruction*> to_remove;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
Opcode op = inst->GetOpcode();
// 二元运算折叠i32
if (op == Opcode::Add || op == Opcode::Sub || op == Opcode::Mul ||
op == Opcode::Div || op == Opcode::Mod) {
auto* bin = static_cast<BinaryInst*>(inst);
auto* lhs_ci = dynamic_cast<ConstantInt*>(bin->GetLhs());
auto* rhs_ci = dynamic_cast<ConstantInt*>(bin->GetRhs());
if (lhs_ci && rhs_ci) {
int lv = lhs_ci->GetValue();
int rv = rhs_ci->GetValue();
int result = 0;
bool valid = true;
switch (op) {
case Opcode::Add: result = lv + rv; break;
case Opcode::Sub: result = lv - rv; break;
case Opcode::Mul: result = lv * rv; break;
case Opcode::Div:
if (rv == 0) { valid = false; break; }
result = lv / rv;
break;
case Opcode::Mod:
if (rv == 0) { valid = false; break; }
result = lv % rv;
break;
default: valid = false; break;
}
if (valid) {
auto* result_ci = ctx.GetConstInt(result);
inst->ReplaceAllUsesWith(result_ci);
to_remove.push_back(inst);
changed = true;
}
}
// 代数化简x + 0 = x, x * 1 = x, x - 0 = x, x * 0 = 0, x / 1 = x
if (!lhs_ci || !rhs_ci) {
auto* bin2 = static_cast<BinaryInst*>(inst);
auto* lci = dynamic_cast<ConstantInt*>(bin2->GetLhs());
auto* rci = dynamic_cast<ConstantInt*>(bin2->GetRhs());
if (op == Opcode::Add) {
if (rci && rci->GetValue() == 0) {
inst->ReplaceAllUsesWith(bin2->GetLhs());
to_remove.push_back(inst);
changed = true;
} else if (lci && lci->GetValue() == 0) {
inst->ReplaceAllUsesWith(bin2->GetRhs());
to_remove.push_back(inst);
changed = true;
}
} else if (op == Opcode::Sub) {
if (rci && rci->GetValue() == 0) {
inst->ReplaceAllUsesWith(bin2->GetLhs());
to_remove.push_back(inst);
changed = true;
} else if (bin2->GetLhs() == bin2->GetRhs()) {
auto* zero = ctx.GetConstInt(0);
inst->ReplaceAllUsesWith(zero);
to_remove.push_back(inst);
changed = true;
}
} else if (op == Opcode::Mul) {
if (rci && rci->GetValue() == 1) {
inst->ReplaceAllUsesWith(bin2->GetLhs());
to_remove.push_back(inst);
changed = true;
} else if (lci && lci->GetValue() == 1) {
inst->ReplaceAllUsesWith(bin2->GetRhs());
to_remove.push_back(inst);
changed = true;
} else if ((rci && rci->GetValue() == 0) ||
(lci && lci->GetValue() == 0)) {
auto* zero = ctx.GetConstInt(0);
inst->ReplaceAllUsesWith(zero);
to_remove.push_back(inst);
changed = true;
}
} else if (op == Opcode::Div) {
if (rci && rci->GetValue() == 1) {
inst->ReplaceAllUsesWith(bin2->GetLhs());
to_remove.push_back(inst);
changed = true;
}
} else if (op == Opcode::Mod) {
if (rci && rci->GetValue() == 1) {
auto* zero = ctx.GetConstInt(0);
inst->ReplaceAllUsesWith(zero);
to_remove.push_back(inst);
changed = true;
}
}
}
continue;
}
// 比较指令折叠
if (op == Opcode::Cmp) {
auto* cmp = static_cast<CmpInst*>(inst);
auto* lhs_ci = dynamic_cast<ConstantInt*>(cmp->GetLhs());
auto* rhs_ci = dynamic_cast<ConstantInt*>(cmp->GetRhs());
if (lhs_ci && rhs_ci) {
int lv = lhs_ci->GetValue();
int rv = rhs_ci->GetValue();
int result = 0;
switch (cmp->GetCmpOp()) {
case CmpOp::Eq: result = (lv == rv) ? 1 : 0; break;
case CmpOp::Ne: result = (lv != rv) ? 1 : 0; break;
case CmpOp::Lt: result = (lv < rv) ? 1 : 0; break;
case CmpOp::Le: result = (lv <= rv) ? 1 : 0; break;
case CmpOp::Gt: result = (lv > rv) ? 1 : 0; break;
case CmpOp::Ge: result = (lv >= rv) ? 1 : 0; break;
}
auto* result_ci = ctx.GetConstInt(result);
inst->ReplaceAllUsesWith(result_ci);
to_remove.push_back(inst);
changed = true;
}
continue;
}
}
for (auto* inst : to_remove) {
bb->RemoveInstruction(inst);
}
}
return changed;
}
} // namespace passes
} // namespace ir

@ -2,4 +2,121 @@
// - 沿 use-def 关系传播已知常量
// - 将可替换的 SSA 值改写为常量,暴露更多折叠机会
// - 常与 ConstFold、DCE、CFGSimplify 迭代配合使用
//
// 算法:工作列表驱动的稀疏条件常量传播(简化版 SCCP
// 遍历所有指令,如果某条指令的结果可以确定为常量,
// 则用该常量替换所有使用点,并将受影响的指令加入工作列表继续传播。
#include "ir/IR.h"
#include <queue>
#include <unordered_set>
#include <vector>
namespace ir {
namespace passes {
bool RunConstProp(Function& func, Context& ctx) {
if (func.IsExternal()) return false;
bool changed = false;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
std::vector<Instruction*> to_remove;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
Opcode op = inst->GetOpcode();
Value* replacement = nullptr;
// 二元运算:两个操作数都是常量则折叠
if (op == Opcode::Add || op == Opcode::Sub || op == Opcode::Mul ||
op == Opcode::Div || op == Opcode::Mod) {
auto* bin = static_cast<BinaryInst*>(inst);
auto* lhs_ci = dynamic_cast<ConstantInt*>(bin->GetLhs());
auto* rhs_ci = dynamic_cast<ConstantInt*>(bin->GetRhs());
if (lhs_ci && rhs_ci) {
int lv = lhs_ci->GetValue();
int rv = rhs_ci->GetValue();
int result = 0;
bool valid = true;
switch (op) {
case Opcode::Add: result = lv + rv; break;
case Opcode::Sub: result = lv - rv; break;
case Opcode::Mul: result = lv * rv; break;
case Opcode::Div:
if (rv == 0) { valid = false; }
else { result = lv / rv; }
break;
case Opcode::Mod:
if (rv == 0) { valid = false; }
else { result = lv % rv; }
break;
default: valid = false; break;
}
if (valid) {
replacement = ctx.GetConstInt(result);
}
}
}
// 比较指令
if (op == Opcode::Cmp) {
auto* cmp = static_cast<CmpInst*>(inst);
auto* lhs_ci = dynamic_cast<ConstantInt*>(cmp->GetLhs());
auto* rhs_ci = dynamic_cast<ConstantInt*>(cmp->GetRhs());
if (lhs_ci && rhs_ci) {
int lv = lhs_ci->GetValue();
int rv = rhs_ci->GetValue();
int result = 0;
switch (cmp->GetCmpOp()) {
case CmpOp::Eq: result = (lv == rv) ? 1 : 0; break;
case CmpOp::Ne: result = (lv != rv) ? 1 : 0; break;
case CmpOp::Lt: result = (lv < rv) ? 1 : 0; break;
case CmpOp::Le: result = (lv <= rv) ? 1 : 0; break;
case CmpOp::Gt: result = (lv > rv) ? 1 : 0; break;
case CmpOp::Ge: result = (lv >= rv) ? 1 : 0; break;
}
replacement = ctx.GetConstInt(result);
}
}
// Phi 节点:如果所有入边值相同(或只有一个非自引用的值),可简化
if (op == Opcode::Phi) {
auto* phi = static_cast<PhiInst*>(inst);
Value* unique_val = nullptr;
bool all_same = true;
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
Value* v = phi->GetIncomingValue(i);
if (v == phi) continue; // 跳过自引用
if (!unique_val) {
unique_val = v;
} else if (v != unique_val) {
all_same = false;
break;
}
}
if (all_same && unique_val) {
replacement = unique_val;
}
}
if (replacement && replacement != inst) {
inst->ReplaceAllUsesWith(replacement);
to_remove.push_back(inst);
changed = true;
}
}
for (auto* inst : to_remove) {
bb->RemoveInstruction(inst);
}
}
return changed;
}
} // namespace passes
} // namespace ir

@ -1,4 +1,130 @@
// 死代码删除DCE
// - 删除无用指令与无用基本块
// - 通常与 CFG 简化配合使用
//
// 算法:标记 + 清扫
// 1. 标记所有有副作用的指令为"有用"ret, br, condbr, store, call
// 2. 沿数据依赖反向传播,将有用指令依赖的定义也标记为有用
// 3. 删除所有未被标记的非终结指令
#include "ir/IR.h"
#include <queue>
#include <unordered_set>
#include <vector>
namespace ir {
namespace passes {
// 判断一条指令是否有副作用(不可随意删除)
static bool HasSideEffect(Instruction* inst) {
Opcode op = inst->GetOpcode();
// 终结指令、store、call 均有副作用
if (op == Opcode::Ret || op == Opcode::Br || op == Opcode::CondBr ||
op == Opcode::Store || op == Opcode::Call) {
return true;
}
return false;
}
bool RunDCE(Function& func) {
if (func.IsExternal()) return false;
bool changed = false;
// 标记阶段
std::unordered_set<Instruction*> useful;
std::queue<Instruction*> worklist;
// 初始标记:所有有副作用的指令
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
if (HasSideEffect(inst)) {
useful.insert(inst);
worklist.push(inst);
}
}
}
// 反向传播:有用指令的操作数定义也标记为有用
while (!worklist.empty()) {
auto* inst = worklist.front();
worklist.pop();
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
auto* operand = inst->GetOperand(i);
if (!operand) continue;
auto* def_inst = dynamic_cast<Instruction*>(operand);
if (def_inst && !useful.count(def_inst)) {
useful.insert(def_inst);
worklist.push(def_inst);
}
}
}
// 清扫阶段:删除未标记为有用的指令
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
std::vector<Instruction*> to_remove;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
if (!useful.count(inst)) {
to_remove.push_back(inst);
}
}
for (auto* inst : to_remove) {
// 如果还有使用者,不能直接删除(用 undef/0 替换)
// 在标记-清扫正确的前提下,未标记的指令不应有有用的使用者
// 但安全起见,先检查
if (!inst->GetUses().empty()) {
// 仍有使用者 —— 跳过(可能是循环引用的 phi
continue;
}
bb->RemoveInstruction(inst);
changed = true;
}
}
return changed;
}
// 简化版 DCE只删除没有使用者且无副作用的指令更安全的实现
bool RunSimpleDCE(Function& func) {
if (func.IsExternal()) return false;
bool changed = false;
bool local_changed = true;
while (local_changed) {
local_changed = false;
for (const auto& bb : func.GetBlocks()) {
if (!bb) continue;
std::vector<Instruction*> to_remove;
for (const auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
// 跳过有副作用的指令
if (HasSideEffect(inst)) continue;
// 跳过 alloca可能后续还会用到
if (inst->GetOpcode() == Opcode::Alloca) continue;
// 如果没有使用者,可以安全删除
if (inst->GetUses().empty()) {
to_remove.push_back(inst);
}
}
for (auto* inst : to_remove) {
bb->RemoveInstruction(inst);
local_changed = true;
changed = true;
}
}
}
return changed;
}
} // namespace passes
} // namespace ir

@ -1,4 +1,336 @@
// Mem2RegSSA 构造):
// - 将局部变量的 alloca/load/store 提升为 SSA 形式
// - 插入 PHI 并重写使用,依赖支配树等分析
//
// 算法流程:
// 1. 识别可提升的 alloca标量仅通过 load/store 访问)
// 2. 计算支配树与支配边界
// 3. 在支配边界处插入 phi
// 4. 沿支配树重命名变量
// 5. 删除冗余 alloca/load/store
#include "ir/IR.h"
#include <algorithm>
#include <cassert>
#include <functional>
#include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace ir {
namespace passes {
// ============ 内联支配树(与 analysis 版本相同) ============
namespace {
class DomTree {
public:
explicit DomTree(Function& func) : func_(func) { Compute(); }
BasicBlock* GetIDom(BasicBlock* bb) const {
auto it = idom_.find(bb);
return it != idom_.end() ? it->second : nullptr;
}
const std::vector<BasicBlock*>& GetDF(BasicBlock* bb) const {
static const std::vector<BasicBlock*> empty;
auto it = df_.find(bb);
return it != df_.end() ? it->second : empty;
}
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;
}
const std::vector<BasicBlock*>& GetRPO() const { return rpo_; }
private:
void Compute() {
auto* entry = func_.GetEntry();
if (!entry) return;
ComputeRPO(entry);
if (rpo_.empty()) return;
for (auto* bb : rpo_) {
idom_[bb] = nullptr;
rpo_index_[bb] = 0;
}
for (size_t i = 0; i < rpo_.size(); ++i) {
rpo_index_[rpo_[i]] = i;
}
idom_[entry] = entry;
bool changed = true;
while (changed) {
changed = false;
for (auto* bb : rpo_) {
if (bb == entry) continue;
BasicBlock* new_idom = nullptr;
for (auto* pred : bb->GetPredecessors()) {
if (idom_.count(pred) && idom_[pred] != nullptr) {
if (!new_idom) {
new_idom = pred;
} else {
new_idom = Intersect(new_idom, pred);
}
}
}
if (new_idom && idom_[bb] != new_idom) {
idom_[bb] = new_idom;
changed = true;
}
}
}
for (auto* bb : rpo_) {
auto* p = GetIDom(bb);
if (p && p != bb) {
children_[p].push_back(bb);
}
}
ComputeDF();
}
void ComputeRPO(BasicBlock* entry) {
std::unordered_set<BasicBlock*> visited;
std::vector<BasicBlock*> post_order;
std::function<void(BasicBlock*)> dfs = [&](BasicBlock* bb) {
visited.insert(bb);
for (auto* succ : bb->GetSuccessors()) {
if (!visited.count(succ)) {
dfs(succ);
}
}
post_order.push_back(bb);
};
dfs(entry);
rpo_.assign(post_order.rbegin(), post_order.rend());
}
BasicBlock* Intersect(BasicBlock* b1, BasicBlock* b2) {
while (b1 != b2) {
while (rpo_index_[b1] > rpo_index_[b2]) b1 = idom_[b1];
while (rpo_index_[b2] > rpo_index_[b1]) b2 = idom_[b2];
}
return b1;
}
void ComputeDF() {
for (auto* bb : rpo_) {
df_[bb] = {};
}
for (auto* bb : rpo_) {
if (bb->GetPredecessors().size() < 2) continue;
for (auto* pred : bb->GetPredecessors()) {
auto* runner = pred;
while (runner && runner != idom_[bb]) {
auto& df_set = df_[runner];
if (std::find(df_set.begin(), df_set.end(), bb) == df_set.end()) {
df_set.push_back(bb);
}
if (runner == idom_[runner]) break;
runner = idom_[runner];
}
}
}
}
Function& func_;
std::vector<BasicBlock*> rpo_;
std::unordered_map<BasicBlock*, size_t> rpo_index_;
std::unordered_map<BasicBlock*, BasicBlock*> idom_;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> children_;
std::unordered_map<BasicBlock*, std::vector<BasicBlock*>> df_;
};
// 判断一个 alloca 是否可以被提升为寄存器:
// - 必须是标量count == 1
// - 只被 load 和 store 使用
bool IsPromotable(AllocaInst* alloca) {
if (alloca->IsArray()) return false;
for (const auto& use : alloca->GetUses()) {
auto* user = use.GetUser();
if (!user) return false;
auto* inst = dynamic_cast<Instruction*>(user);
if (!inst) return false;
if (inst->GetOpcode() != Opcode::Load &&
inst->GetOpcode() != Opcode::Store) {
return false;
}
// store 只能把 alloca 作为 ptroperand 1不能作为 valoperand 0
if (inst->GetOpcode() == Opcode::Store) {
auto* store = static_cast<StoreInst*>(inst);
if (store->GetPtr() != alloca) return false;
}
}
return true;
}
} // namespace
bool RunMem2Reg(Function& func) {
if (func.IsExternal()) return false;
DomTree dom(func);
// 1. 收集可提升的 alloca
std::vector<AllocaInst*> promotable;
auto* entry = func.GetEntry();
if (!entry) return false;
for (const auto& inst : entry->GetInstructions()) {
if (auto* alloca = dynamic_cast<AllocaInst*>(inst.get())) {
if (IsPromotable(alloca)) {
promotable.push_back(alloca);
}
}
}
if (promotable.empty()) return false;
// 对每个可提升的 alloca 分别执行
for (auto* alloca : promotable) {
// 确定 alloca 值的类型
std::shared_ptr<Type> val_type;
if (alloca->GetType()->IsPtrInt32()) {
val_type = Type::GetInt32Type();
} else if (alloca->GetType()->IsPtrFloat32()) {
val_type = Type::GetFloat32Type();
} else {
continue;
}
// 2. 收集所有 def 块(包含 store 的块)和 use 块(包含 load 的块)
std::unordered_set<BasicBlock*> def_blocks;
std::vector<StoreInst*> stores;
std::vector<LoadInst*> loads;
for (const auto& use : alloca->GetUses()) {
auto* inst = dynamic_cast<Instruction*>(use.GetUser());
if (!inst || !inst->GetParent()) continue;
if (auto* store = dynamic_cast<StoreInst*>(inst)) {
if (store->GetPtr() == alloca) {
def_blocks.insert(store->GetParent());
stores.push_back(store);
}
} else if (auto* load = dynamic_cast<LoadInst*>(inst)) {
loads.push_back(load);
}
}
// 3. 插入 phi 节点(使用迭代支配边界)
// 用 map 精确记录当前 alloca 在每个块中插入的 phi
std::unordered_map<BasicBlock*, PhiInst*> phi_map;
std::unordered_set<BasicBlock*> phi_blocks;
std::queue<BasicBlock*> worklist;
for (auto* bb : def_blocks) {
worklist.push(bb);
}
static int phi_counter = 0;
while (!worklist.empty()) {
auto* bb = worklist.front();
worklist.pop();
for (auto* df_bb : dom.GetDF(bb)) {
if (!phi_blocks.count(df_bb)) {
phi_blocks.insert(df_bb);
auto* phi = df_bb->PrependPhi(val_type,
"%phi." + std::to_string(phi_counter++));
phi_map[df_bb] = phi;
worklist.push(df_bb);
}
}
}
// 4. 重命名:沿支配树 DFS
std::stack<Value*> val_stack;
std::function<void(BasicBlock*)> Rename = [&](BasicBlock* bb) {
size_t stack_size = val_stack.size();
// 处理当前块中我们插入的 phi
auto phi_it = phi_map.find(bb);
if (phi_it != phi_map.end()) {
val_stack.push(phi_it->second);
}
// 遍历块中所有指令
std::vector<Instruction*> to_remove;
for (auto& inst_ptr : bb->GetInstructions()) {
auto* inst = inst_ptr.get();
if (auto* store = dynamic_cast<StoreInst*>(inst)) {
if (store->GetPtr() == alloca) {
val_stack.push(store->GetValue());
to_remove.push_back(store);
}
} else if (auto* load = dynamic_cast<LoadInst*>(inst)) {
if (load->GetPtr() == alloca) {
Value* cur_val = val_stack.empty() ? nullptr : val_stack.top();
if (cur_val) {
load->ReplaceAllUsesWith(cur_val);
}
to_remove.push_back(load);
}
}
}
// 填充后继块中 phi 的入边
for (auto* succ : bb->GetSuccessors()) {
auto succ_phi_it = phi_map.find(succ);
if (succ_phi_it == phi_map.end()) continue;
Value* cur_val = val_stack.empty() ? nullptr : val_stack.top();
if (cur_val) {
succ_phi_it->second->AddIncoming(cur_val, bb);
}
}
// 递归处理支配树的孩子
for (auto* child : dom.GetChildren(bb)) {
Rename(child);
}
// 恢复栈
while (val_stack.size() > stack_size) {
val_stack.pop();
}
// 删除已标记的指令
for (auto* inst : to_remove) {
bb->RemoveInstruction(inst);
}
};
Rename(entry);
// 5. 删除 alloca
entry->RemoveInstruction(alloca);
// 6. 清理没有入边的 phi
for (auto* bb : dom.GetRPO()) {
std::vector<Instruction*> dead_phis;
for (auto& inst_ptr : bb->GetInstructions()) {
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
if (!phi) break;
if (phi->GetNumIncoming() == 0) {
dead_phis.push_back(phi);
}
// 如果 phi 只有一个入边,直接替换为该值
if (phi->GetNumIncoming() == 1) {
phi->ReplaceAllUsesWith(phi->GetIncomingValue(0));
dead_phis.push_back(phi);
}
}
for (auto* phi : dead_phis) {
bb->RemoveInstruction(phi);
}
}
}
return true;
}
} // namespace passes
} // namespace ir

@ -1 +1,49 @@
// IR Pass 管理骨架。
// 组织所有优化遍的执行顺序,支持多轮迭代直到 IR 不再变化。
//
// 执行顺序:
// 1. Mem2Reg只跑一次
// 2. 迭代ConstFold -> ConstProp -> CSE -> DCE -> CFGSimplify
// 直到 IR 不再变化或达到最大迭代次数
#include "ir/IR.h"
#include <iostream>
namespace ir {
namespace passes {
// 前向声明各 pass 入口
bool RunMem2Reg(Function& func);
bool RunConstFoldWithCtx(Function& func, Context& ctx);
bool RunConstProp(Function& func, Context& ctx);
bool RunCSE(Function& func);
bool RunSimpleDCE(Function& func);
bool RunCFGSimplify(Function& func, Context& ctx);
static const int kMaxIterations = 20;
void RunAllPasses(Module& module) {
auto& ctx = module.GetContext();
for (const auto& func : module.GetFunctions()) {
if (!func || func->IsExternal()) continue;
RunMem2Reg(*func);
for (int iter = 0; iter < kMaxIterations; ++iter) {
bool changed = false;
changed |= RunConstFoldWithCtx(*func, ctx);
changed |= RunConstProp(*func, ctx);
changed |= RunCSE(*func);
changed |= RunSimpleDCE(*func);
changed |= RunCFGSimplify(*func, ctx);
if (!changed) break;
}
}
}
} // namespace passes
} // namespace ir

@ -15,6 +15,8 @@
#include "irgen/IRGen.h"
#include "mir/MIR.h"
#include "sem/Sema.h"
// 前向声明优化 pass 入口
namespace ir { namespace passes { void RunAllPasses(ir::Module& module); } }
#endif
#include "utils/CLI.h"
#include "utils/Log.h"
@ -139,6 +141,10 @@ int main(int argc, char** argv) {
auto sema = RunSema(*comp_unit);
auto module = GenerateIR(*comp_unit, sema);
// 运行 IR 优化 passMem2Reg + 标量优化迭代)
ir::passes::RunAllPasses(*module);
if (opts.emit_ir) {
ir::IRPrinter printer;
if (need_blank_line) {

@ -2,8 +2,10 @@
#include <cstdint>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include <unordered_map>
#include <vector>
#include "ir/IR.h"
#include "utils/Log.h"
@ -946,6 +948,14 @@ std::unique_ptr<MachineModule> LowerToMIR(const ir::Module& module) {
{Operand::Reg(param_reg), Operand::FrameIndex(slot)});
}
// Phi 信息收集:每个 Phi 对应一个栈槽,以及各入边 (value, pred_block)
struct PhiInfo {
int slot;
bool is_float;
std::vector<std::pair<const ir::Value*, const ir::BasicBlock*>> incomings;
};
std::vector<PhiInfo> phi_infos;
// 遍历所有基本块,生成指令
for (const auto& bb_ptr : func.GetBlocks()) {
const auto& bb = *bb_ptr;
@ -956,6 +966,23 @@ std::unique_ptr<MachineModule> LowerToMIR(const ir::Module& module) {
const auto& inst = *ir_insts[i];
auto opcode = inst.GetOpcode();
// Phi 节点:分配栈槽,收集入边信息,后续统一插入 store
if (opcode == ir::Opcode::Phi) {
auto& phi = static_cast<const ir::PhiInst&>(inst);
bool is_float = phi.GetType() && phi.GetType()->IsFloat32();
int slot = machine_func->CreateFrameIndex();
slots.emplace(&phi, slot);
PhiInfo info;
info.slot = slot;
info.is_float = is_float;
for (size_t j = 0; j < phi.GetNumIncoming(); ++j) {
info.incomings.emplace_back(phi.GetIncomingValue(j),
phi.GetIncomingBlock(j));
}
phi_infos.push_back(std::move(info));
continue;
}
// Cmp + CondBr 融合:避免 cmp 结果落栈后再读回。
if (opcode == ir::Opcode::Cmp && i + 1 < ir_insts.size()) {
auto* cmp_inst = dynamic_cast<const ir::CmpInst*>(ir_insts[i].get());
@ -1035,6 +1062,52 @@ std::unique_ptr<MachineModule> LowerToMIR(const ir::Module& module) {
LowerInstruction(inst, *machine_func, *current_mbb, slots, geps);
}
}
// Phi 消除:在每个前驱块的跳转指令之前插入 store
for (const auto& phi_info : phi_infos) {
for (const auto& [val, pred_bb] : phi_info.incomings) {
if (!val) continue; // 安全检查
auto it_pred = block_map.find(pred_bb);
if (it_pred == block_map.end()) continue; // 前驱块可能已被优化掉
auto* pred_mbb = it_pred->second;
auto& pred_insts = pred_mbb->GetInstructions();
// 找到跳转指令的位置(从末尾往前找第一条 B/Bcond/Cbnz/FBcond
size_t insert_pos = pred_insts.size();
for (size_t j = pred_insts.size(); j > 0; --j) {
auto op = pred_insts[j - 1].GetOpcode();
if (op == Opcode::B || op == Opcode::Bcond ||
op == Opcode::Cbnz || op == Opcode::FBcond) {
insert_pos = j - 1;
} else {
break;
}
}
// 检查 val 是否在 slots 中或者是常量/全局变量
// 如果是常量EmitValueToReg 能直接处理;否则需要有栈槽
bool can_emit = false;
if (dynamic_cast<const ir::ConstantInt*>(val) ||
dynamic_cast<const ir::ConstantFloat*>(val) ||
dynamic_cast<const ir::GlobalVariable*>(val)) {
can_emit = true;
} else if (slots.find(val) != slots.end()) {
can_emit = true;
}
if (!can_emit) continue; // 跳过无法发射的值
PhysReg tmp = phi_info.is_float ? PhysReg::S8 : PhysReg::W8;
MachineBasicBlock tmp_block("__phi_tmp__");
EmitValueToReg(val, tmp, slots, tmp_block);
tmp_block.Append(Opcode::StoreStack,
{Operand::Reg(tmp), Operand::FrameIndex(phi_info.slot)});
auto& tmp_insts = tmp_block.GetInstructions();
pred_insts.insert(pred_insts.begin() + insert_pos,
std::make_move_iterator(tmp_insts.begin()),
std::make_move_iterator(tmp_insts.end()));
}
}
}
return machine_module;

Loading…
Cancel
Save