[miden]DCE引入ctx避免重复运行遍导致的状态污染。修复天然活跃判断条件

This commit is contained in:
rain2133
2025-07-25 16:33:18 +08:00
parent 12f63a0bf5
commit e2c97fd171
2 changed files with 117 additions and 51 deletions

View File

@ -1,48 +1,57 @@
#include "DCE.h"
#include "IR.h"
#include "SysYIROptUtils.h"
#include <iostream>
#include <set>
#include "DCE.h" // 包含DCE遍的头文件
#include "IR.h" // 包含IR相关的定义
#include "SysYIROptUtils.h" // 包含SysY IR优化工具类的定义
#include <cassert> // 用于断言
#include <iostream> // 用于调试输出
#include <set> // 包含set虽然DCEContext内部用unordered_set但这里保留
namespace sysy {
// DCE 遍的静态 ID
void *DCE::ID = (void *)&DCE::ID;
// DCE 遍的 runOnFunction 方法实现
bool DCE::runOnFunction(Function *func, AnalysisManager &AM) {
// ======================================================================
// DCEContext 类的实现
// 封装了 DCE 遍的核心逻辑和状态,确保每次函数优化运行时状态独立
// ======================================================================
// DCEContext 的 run 方法实现
void DCEContext::run(Function *func, AnalysisManager *AM, bool &changed) {
// 清空活跃指令集合,确保每次运行都是新的状态
alive_insts.clear();
bool changed = false;
// 第一次遍历:扫描所有指令,识别“天然活跃”的指令并将其及其依赖标记为活跃
// 使用 func->getBasicBlocks() 获取基本块列表,保留用户风格
auto basicBlocks = func->getBasicBlocks();
for (auto &basicBlock : basicBlocks) {
// 确保基本块有效
if (!basicBlock)
continue;
// 使用 basicBlock->getInstructions() 获取指令列表,保留用户风格
for (auto &inst : basicBlock->getInstructions()) {
// 确保指令有效
if (!inst)
continue;
// 调用 DCEContext 自身的 isAlive 和 addAlive 方法
if (isAlive(inst.get())) {
addAlive(inst.get());
}
}
}
// 第二:删除所有未被标记为活跃的指令。
// 第二次遍历:删除所有未被标记为活跃的指令。
for (auto &basicBlock : basicBlocks) {
if (!basicBlock)
continue;
// 使用传统的迭代器循环,并手动管理迭代器,
// 以便在删除元素后正确前进。
// 以便在删除元素后正确前进。保留用户风格
for (auto instIter = basicBlock->getInstructions().begin(); instIter != basicBlock->getInstructions().end();) {
auto &inst = *instIter;
Instruction *currentInst = inst.get();
// 如果指令不在活跃集合中,则删除它。
// 分支和返回指令由 isAlive 处理,并会被保留。
if (alive_insts.count(currentInst) == 0) {
// 删除指令
// 删除指令,保留用户风格的 SysYIROptUtils::usedelete 和 erase
changed = true; // 标记 IR 已被修改
SysYIROptUtils::usedelete(currentInst);
instIter = basicBlock->getInstructions().erase(instIter); // 删除后返回下一个迭代器
@ -51,27 +60,26 @@ bool DCE::runOnFunction(Function *func, AnalysisManager &AM) {
}
}
}
return changed;
}
// 判断指令是否是“天然活跃”的实现
// 只有具有副作用的指令(如存储、函数调用、原子操作)
// 和控制流指令(如分支、返回)是天然活跃的。
bool DCE::isAlive(Instruction *inst) {
bool DCEContext::isAlive(Instruction *inst) {
// TODO: 后续程序并发考虑原子操作
// 其结果不被其他指令使用的指令(例如 StoreInst, BranchInst, ReturnInst
// dynamic_cast<ir::CallInst>(inst) 检查是否是函数调用指令,
// 函数调用通常有副作用。
// 终止指令 (BranchInst, ReturnInst) 必须是活跃的,因为它控制了程序的执行流程。
// 保留用户提供的 isAlive 逻辑
bool isBranchOrReturn = inst->isBranch() || inst->isReturn();
bool isCall = inst->isCall();
bool isStoreOrMemset = inst->isStore() && inst->isMemset();
bool isStoreOrMemset = inst->isStore() || inst->isMemset();
return isBranchOrReturn || isCall || isStoreOrMemset;
}
// 递归地将活跃指令及其依赖加入到 alive_insts 集合中
void DCE::addAlive(Instruction *inst) {
void DCEContext::addAlive(Instruction *inst) {
// 如果指令已经存在于活跃集合中,则无需重复处理
if (alive_insts.count(inst) > 0) {
return;
@ -79,6 +87,7 @@ void DCE::addAlive(Instruction *inst) {
// 将当前指令标记为活跃
alive_insts.insert(inst);
// 遍历当前指令的所有操作数
// 保留用户提供的 getOperands() 和 getValue()
for (auto operand : inst->getOperands()) {
// 如果操作数是一个指令(即它是一个值的定义),
// 并且它还没有被标记为活跃
@ -88,4 +97,44 @@ void DCE::addAlive(Instruction *inst) {
}
}
// ======================================================================
// DCE Pass 类的实现
// 主要负责与 PassManager 交互,创建 DCEContext 实例并运行优化
// ======================================================================
// DCE 遍的 runOnFunction 方法实现
bool DCE::runOnFunction(Function *func, AnalysisManager &AM) {
DCEContext ctx;
bool changed = false;
ctx.run(func, &AM, changed); // 运行 DCE 优化
// 如果 IR 被修改,则使相关的分析结果失效
if (changed) {
// DCE 会删除指令,这会影响数据流分析,尤其是活跃性分析。
// 如果删除导致基本块变空,也可能间接影响 CFG 和支配树。
// AM.invalidateAnalysis(&LivenessAnalysisPass::ID, func); // 活跃性分析失效
// AM.invalidateAnalysis(&DominatorTreeAnalysisPass::ID, func); // 支配树分析可能失效
// 其他所有依赖于数据流或 IR 结构的分析都可能失效。
}
return changed;
}
// 声明DCE遍的分析依赖和失效信息
void DCE::getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const {
// DCE不依赖特定的分析结果它通过遍历和副作用判断来工作。
// DCE会删除指令这会影响许多分析结果。
// 至少,它会影响活跃性分析、支配树、控制流图(如果删除导致基本块为空并被合并)。
// 假设存在LivenessAnalysisPass和DominatorTreeAnalysisPass
// analysisInvalidations.insert(&LivenessAnalysisPass::ID);
// analysisInvalidations.insert(&DominatorTreeAnalysisPass::ID);
// 任何改变IR结构的优化都可能导致通用分析如活跃性、支配树、循环信息失效。
// 最保守的做法是使所有函数粒度的分析失效,或者只声明你明确知道会受影响的分析。
// 考虑到这个DCE仅删除指令如果它不删除基本块CFG可能不变但数据流分析会失效。
// 对于更激进的DCE如ADCECFG也会改变。
// 这里我们假设它主要影响数据流分析并且可能间接影响CFG相关分析。
// 如果有SideEffectInfo它也可能被修改但通常SideEffectInfo是静态的不因DCE而变。
}
} // namespace sysy

View File

@ -1,46 +1,63 @@
#pragma once
#include "IR.h" // 包含IR相关的定义如Instruction, Function, BasicBlock等
#include "IRBuilder.h" // 包含IR构建器的定义
#include "SysYIROptUtils.h" // 包含SysY IR优化工具类的
#include "Liveness.h"
#include "Dom.h" // 包含支配树的定义
#include "Pass.h" // 包含Pass的基类定义
#include <unordered_set> // 用于存储活跃指令
#include "Pass.h"
#include "IR.h"
#include "SysYIROptUtils.h"
#include "Dom.h"
#include <unordered_set>
#include <queue>
namespace sysy {
// 前向声明分析结果类,确保在需要时可以引用
// class DominatorTreeAnalysisResult; // Pass.h 中已包含,这里不再需要
class SideEffectInfoAnalysisResult; // 假设有副作用分析结果类
// DCEContext 类用于封装DCE的内部逻辑和状态
// 这样可以避免静态变量在多线程或多次运行时的冲突,并保持代码的模块化
class DCEContext {
public:
// 运行DCE的主要方法
// func: 当前要优化的函数
// tp: 分析管理器,用于获取其他分析结果(如果需要)
void run(Function* func, AnalysisManager* AM, bool &changed);
private:
// 存储活跃指令的集合
std::unordered_set<Instruction*> alive_insts;
// 判断指令是否是“天然活跃”的(即总是保留的)
// inst: 要检查的指令
// 返回值: 如果指令是天然活跃的则为true否则为false
bool isAlive(Instruction* inst);
// 递归地将活跃指令及其依赖加入到 alive_insts 集合中
// inst: 要标记为活跃的指令
void addAlive(Instruction* inst);
};
// DCE 优化遍类,继承自 OptimizationPass
class DCE : public OptimizationPass {
private:
std::unordered_set<Instruction *> alive_insts;
// 判断指令是否是“天然活跃”的(即总是保留的)
// inst: 要检查的指令
// 返回值: 如果指令是天然活跃的则为true否则为false
bool isAlive(Instruction *inst);
// 递归地将活跃指令及其依赖加入到 alive_insts 集合中
// inst: 要标记为活跃的指令
void addAlive(Instruction *inst);
public:
static void *ID;
DCE() : OptimizationPass("DCE", Granularity::Function) {}
bool runOnFunction(Function *func, AnalysisManager &AM) override;
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override{
// DCE不依赖特定的分析结果它通过遍历和副作用判断来工作。
// 构造函数
DCE() : OptimizationPass("DCE", Granularity::Function) {}
// DCE会删除指令这会影响许多分析结果。
// 至少,它会影响活跃性分析、支配树、控制流图(如果删除导致基本块为空并被合并)。
// 假设存在LivenessAnalysisPass和DominatorTreeAnalysisPass
// analysisInvalidations.insert(&LivenessAnalysisPass::ID);
// analysisInvalidations.insert(&DominatorTreeAnalysisPass::ID);
// 任何改变IR结构的优化都可能导致通用分析如活跃性、支配树、循环信息失效
// 最保守的做法是使所有函数粒度的分析失效,或者只声明你明确知道会受影响的分析。
// 考虑到这个DCE仅删除指令如果它不删除基本块CFG可能不变但数据流分析会失效。
// 对于更激进的DCE如ADCECFG也会改变。
// 这里我们假设它主要影响数据流分析并且可能间接影响CFG相关分析。
// 如果有SideEffectInfo它也可能被修改但通常SideEffectInfo是静态的不因DCE而变。
}
void *getPassID() const override { return &ID; }
// 静态成员作为该遍的唯一ID
static void *ID;
// 运行在函数上的优化逻辑
// F: 当前要优化的函数
// AM: 分析管理器,用于获取或使分析结果失效
// 返回值: 如果IR被修改则为true否则为false
bool runOnFunction(Function *F, AnalysisManager& AM) override;
// 声明该遍的分析依赖和失效信息
// analysisDependencies: 该遍运行前需要哪些分析结果
// analysisInvalidations: 该遍运行后会使哪些分析结果失效
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override;
// Pass 基类中的纯虚函数,必须实现
void *getPassID() const override { return &ID; }
};
} // namespace sysy