[midend-GVN&SideEffect]修复GVN的部分问题和副作用分析的缺陷

This commit is contained in:
rain2133
2025-08-16 18:52:29 +08:00
parent d038884ffb
commit c4eb1c3980
3 changed files with 83 additions and 6 deletions

View File

@ -56,6 +56,9 @@ private:
// 检查是否可以安全地用一个值替换另一个值
bool canReplace(Instruction* original, Value* replacement);
// 检查两个load指令之间是否有store指令修改了相同的内存位置
bool hasInterveningStore(LoadInst* earlierLoad, LoadInst* laterLoad, Value* ptr);
// 生成表达式的标准化字符串
std::string getCanonicalExpression(Instruction* inst);
};

View File

@ -26,10 +26,21 @@ const SideEffectInfo &SideEffectAnalysisResult::getInstructionSideEffect(Instruc
}
const SideEffectInfo &SideEffectAnalysisResult::getFunctionSideEffect(Function *func) const {
// 首先检查分析过的用户定义函数
auto it = functionSideEffects.find(func);
if (it != functionSideEffects.end()) {
return it->second;
}
// 如果没有找到,检查是否为已知的库函数
if (func) {
std::string funcName = func->getName();
const SideEffectInfo *knownInfo = getKnownFunctionSideEffect(funcName);
if (knownInfo) {
return *knownInfo;
}
}
// 返回默认的无副作用信息
static SideEffectInfo noEffect;
return noEffect;

View File

@ -201,7 +201,11 @@ Value *GVNContext::getValueNumber(Instruction *inst) {
} else if (auto load = dynamic_cast<LoadInst *>(inst)) {
return getValueNumber(load);
} else if (auto call = dynamic_cast<CallInst *>(inst)) {
return getValueNumber(call);
// 只为无副作用的函数调用进行GVN
if (sideEffectAnalysis && sideEffectAnalysis->isPureFunction(call->getCallee())) {
return getValueNumber(call);
}
return nullptr;
}
return nullptr;
@ -295,6 +299,10 @@ Value *GVNContext::getValueNumber(LoadInst *inst) {
auto loadPtr = checkHashtable(load->getPointer());
if (ptr == loadPtr && inst->getType() == load->getType()) {
// 检查两次load之间是否有store指令修改了内存
if (hasInterveningStore(load, inst, ptr)) {
continue; // 如果有store指令不能复用之前的load
}
return value;
}
}
@ -304,11 +312,7 @@ Value *GVNContext::getValueNumber(LoadInst *inst) {
}
Value *GVNContext::getValueNumber(CallInst *inst) {
// 只为无副作用的函数调用进行GVN
if (sideEffectAnalysis && !sideEffectAnalysis->isPureFunction(inst->getCallee())) {
return nullptr;
}
// 此时已经确认是无副作用的函数调用,可以安全进行GVN
for (auto [key, value] : hashtable) {
if (auto call = dynamic_cast<CallInst *>(key)) {
if (call->getCallee() == inst->getCallee() && call->getNumOperands() == inst->getNumOperands()) {
@ -427,6 +431,65 @@ bool GVNContext::canReplace(Instruction *original, Value *replacement) {
return false;
}
bool GVNContext::hasInterveningStore(LoadInst* earlierLoad, LoadInst* laterLoad, Value* ptr) {
// 如果两个load在不同的基本块需要更复杂的分析
auto earlierBB = earlierLoad->getParent();
auto laterBB = laterLoad->getParent();
if (earlierBB != laterBB) {
// 跨基本块的情况为了安全起见暂时认为有intervening store
// 这是保守的做法,可能会错过一些优化机会,但确保正确性
return true;
}
// 同一基本块内的情况:检查指令序列
auto &insts = earlierBB->getInstructions();
// 找到两个load指令的位置
auto earlierIt = std::find_if(insts.begin(), insts.end(),
[earlierLoad](const auto &ptr) { return ptr.get() == earlierLoad; });
auto laterIt = std::find_if(insts.begin(), insts.end(),
[laterLoad](const auto &ptr) { return ptr.get() == laterLoad; });
if (earlierIt == insts.end() || laterIt == insts.end()) {
return true; // 找不到指令保守返回true
}
// 检查两个load之间的所有指令
for (auto it = std::next(earlierIt); it != laterIt; ++it) {
auto inst = it->get();
// 检查是否是store指令
if (auto storeInst = dynamic_cast<StoreInst*>(inst)) {
auto storePtr = checkHashtable(storeInst->getPointer());
// 如果store的目标地址与load的地址相同说明内存被修改了
if (storePtr == ptr) {
if (DEBUG) {
std::cout << " Found intervening store to same address, cannot optimize load" << std::endl;
}
return true;
}
}
// TODO: 还需要检查函数调用是否可能修改内存
// 对于全局变量,任何函数调用都可能修改它
if (auto callInst = dynamic_cast<CallInst*>(inst)) {
if (sideEffectAnalysis && !sideEffectAnalysis->isPureFunction(callInst->getCallee())) {
// 如果是有副作用的函数调用且load的是全局变量则可能被修改
if (auto globalPtr = dynamic_cast<GlobalValue*>(ptr)) {
if (DEBUG) {
std::cout << " Found function call that may modify global variable, cannot optimize load" << std::endl;
}
return true;
}
}
}
}
return false; // 没有找到会修改内存的指令
}
std::string GVNContext::getCanonicalExpression(Instruction *inst) {
std::ostringstream oss;