diff --git a/src/Mem2Reg.cpp b/src/Mem2Reg.cpp index fd7a239..2daef27 100644 --- a/src/Mem2Reg.cpp +++ b/src/Mem2Reg.cpp @@ -1,490 +1,154 @@ #include "Mem2Reg.h" +#include "SysYIRPrinter.h" #include #include -#include #include #include #include -#include #include -#include -#include "IR.h" -#include "SysYIRAnalyser.h" -#include "SysYIRPrinter.h" +#include +#include namespace sysy { -// 计算给定变量的定义块集合的迭代支配边界 -// TODO:优化Semi-Naive IDF -std::unordered_set Mem2Reg::computeIterDf(const std::unordered_set &blocks) { - std::unordered_set workList; - std::unordered_set ret_list; - workList.insert(blocks.begin(), blocks.end()); +// --- 私有成员函数实现 --- - while (!workList.empty()) { - auto n = workList.begin(); - BlockAnalysisInfo* blockInfo = controlFlowAnalysis->getBlockAnalysisInfo(*n); - auto DFs = blockInfo->getDomFrontiers(); - for (auto c : DFs) { - // 如果c不在ret_list中,则将其加入ret_list和workList - // 这里的c是n的支配边界 - // 也就是n的支配边界中的块 - // 需要注意的是,支配边界是一个集合,所以可能会有重复 - if (ret_list.count(c) == 0U) { - ret_list.emplace(c); - workList.emplace(c); +// 计算给定定义块集合的迭代支配边界 +std::unordered_set Mem2Reg::computeIteratedDomFrontiers(const std::unordered_set& blocks) { + std::unordered_set result; + std::queue worklist; // 使用队列进行 BFS-like 遍历 + + for (auto* block : blocks) + worklist.push(block); + + while (!worklist.empty()) { + auto* block = worklist.front(); + worklist.pop(); + + auto* blockInfo = controlFlowAnalysis->getBlockAnalysisInfo(block); + if (!blockInfo) continue; + + for (auto* df : blockInfo->getDomFrontiers()) { + if (result.find(df) == result.end()) { // If not already in result + result.insert(df); + worklist.push(df); } } - workList.erase(n); } - return ret_list; + return result; } -/** - * 计算value2Blocks的映射,包括value2AllocBlocks、value2DefBlocks以及value2UseBlocks - * 其中value2DefBlocks可用于计算迭代支配边界来插入相应变量的phi结点 - * 这里的value2AllocBlocks、value2DefBlocks和value2UseBlocks改变了函数级别的分析信息 - */ -auto Mem2Reg::computeValue2Blocks() -> void { - SysYPrinter printer(pModule); // 初始化打印机 - // std::cout << "===== Start computeValue2Blocks =====" << std::endl; - - auto &functions = pModule->getFunctions(); - for (const auto &function : functions) { - auto func = function.second.get(); - // std::cout << "\nProcessing function: " << func->getName() << std::endl; - - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - if (!funcInfo) { - std::cerr << "ERROR: No analysis info for function " << func->getName() << std::endl; - continue; +// 分析一个 alloca 的所有 uses,填充 allocaDefsBlock 和 allocaUsesBlock +void Mem2Reg::allocaAnalysis(AllocaInst* alloca) { + allocaDefsBlock[alloca].clear(); + allocaUsesBlock[alloca].clear(); + + for (auto use : alloca->getUses()) { + Instruction* userInst = dynamic_cast(use->getUser()); + if (!userInst) continue; + + if (StoreInst* store = dynamic_cast(userInst)) { + if (store->getOperand(1) == alloca) { // Store's second operand is the pointer + allocaDefsBlock[alloca].insert(store->getParent()); // Store's parent is the defining block + } + } else if (LoadInst* load = dynamic_cast(userInst)) { + if (load->getOperand(0) == alloca) { // Load's first operand is the pointer + allocaUsesBlock[alloca].insert(load->getParent()); // Load's parent is the using block + } + } + } +} + +// 判断一个 alloca 是否可以被提升为寄存器 (无地址逃逸,标量类型) +bool Mem2Reg::is_promoted(AllocaInst* alloca) { + // 检查是否是标量类型 (非数组、非全局变量等) + if(!(SysYIROptUtils::isArr(alloca) || SysYIROptUtils::isGlobal(alloca))){ + return false; // 只有标量类型的 alloca 才能被提升 + } + + // 获取 alloca 指向的基类型 + PointerType* ptrType = dynamic_cast(alloca->getType()); + if (!ptrType) return false; // Should always be a pointer type + Type* allocabaseType = ptrType->getBaseType(); + + for (const auto& use : alloca->getUses()) { + Instruction* userInst = dynamic_cast(use->getUser()); + if (!userInst) { + // 如果不是指令的 use,比如作为全局变量的初始值等,通常认为逃逸 + return false; } - auto basicBlocks = func->getBasicBlocks(); - // std::cout << "BasicBlocks count: " << basicBlocks.size() << std::endl; - - for (auto &it : basicBlocks) { - auto basicBlock = it.get(); - // std::cout << "\nProcessing BB: " << basicBlock->getName() << std::endl; - // printer.printBlock(basicBlock); // 打印基本块内容 - - auto &instrs = basicBlock->getInstructions(); - for (auto &instr : instrs) { - // std::cout << " Analyzing instruction: "; - // printer.printInst(instr.get()); - // std::cout << std::endl; + if (LoadInst* load = dynamic_cast(userInst)) { + // Load 指令结果的类型必须与 alloca 的基类型一致 + if (load->getType() != allocabaseType) { + return false; + } + } else if (StoreInst* store = dynamic_cast(userInst)) { + // Store 指令的值操作数类型必须与 alloca 的基类型一致 + // 且 store 的指针操作数必须是当前 alloca + if (store->getOperand(1) != alloca || store->getOperand(0)->getType() != allocabaseType) { + return false; + } + } else if (userInst->isGetSubArray()) { + // GSA 指令表示对数组的访问 + // 这意味着地址逃逸,不能简单提升为单个寄存器 + return false; + } else if (userInst->isCall()) { + // 如果 alloca 作为函数参数传递,通常认为地址逃逸 + return false; + } + // 如果有其他类型的指令使用 alloca 的地址,也需要判断是否是逃逸 + // 例如:BitCastInst, PtrToIntInst, 如果这些操作将地址暴露,则不能提升 + } + return true; +} - if (instr->isAlloca()) { - if (!(SysYIROptUtils::isArr(instr.get()) || SysYIROptUtils::isGlobal(instr.get()))) { - // std::cout << " Found alloca: "; - // printer.printInst(instr.get()); - // std::cout << " -> Adding to allocBlocks" << std::endl; - - funcInfo->addValue2AllocBlocks(instr.get(), basicBlock); - } else { - // std::cout << " Skip array/global alloca: "; - // printer.printInst(instr.get()); - // std::cout << std::endl; - } - } - else if (instr->isStore()) { - auto val = instr->getOperand(1); - // std::cout << " Store target: "; - // printer.printInst(dynamic_cast(val)); +// 在迭代支配边界处插入 Phi 指令 +void Mem2Reg::insertPhiNodes(Function* func) { + // 清空上次 Phi 插入的结果 + phiMap.clear(); + allPhiInstructions.clear(); + + std::unordered_set phiPlacementBlocks; // 存放需要插入 Phi 的块 + std::queue workQueue; // BFS 队列,用于迭代支配边界计算 + + // 遍历所有可提升的 alloca + for (AllocaInst* alloca : currentFunctionAllocas) { + phiPlacementBlocks.clear(); // 为每个 alloca 重新计算 Phi 放置位置 + + // 初始化工作队列,放入所有定义该 alloca 的基本块 + for (BasicBlock* defBB : allocaDefsBlock[alloca]) { + workQueue.push(defBB); + } + + while (!workQueue.empty()) { + BasicBlock* currentBB = workQueue.front(); + workQueue.pop(); + + auto* blockInfo = controlFlowAnalysis->getBlockAnalysisInfo(currentBB); + if (!blockInfo) continue; + + // 遍历当前块的支配边界 + for (BasicBlock* domFrontierBB : blockInfo->getDomFrontiers()) { + // 如果这个支配边界块还没有为当前 alloca 插入 Phi 指令 + if (phiPlacementBlocks.find(domFrontierBB) == phiPlacementBlocks.end()) { + // 获取 alloca 的基类型,作为 Phi 指令的结果类型 + Type* phiType = dynamic_cast(alloca->getType())->getBaseType(); - if (!(SysYIROptUtils::isArr(val) || SysYIROptUtils::isGlobal(val))) { - // std::cout << " Adding store to defBlocks for value: "; - // printer.printInst(dynamic_cast(instr.get())); - // std::cout << std::endl; - // 将store的目标值添加到defBlocks中 - funcInfo->addValue2DefBlocks(val, basicBlock); - } else { - // std::cout << " Skip array/global store" << std::endl; - } - } - else if (instr->isLoad()) { - auto val = instr->getOperand(0); - // std::cout << " Load source: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << std::endl; + // 在支配边界块的开头插入 Phi 指令 + pBuilder->setPosition(domFrontierBB->begin()); + PhiInst* newPhi = pBuilder->createPhiInst(phiType, {}, {}); // 初始入边为空 - if (!(SysYIROptUtils::isArr(val) || SysYIROptUtils::isGlobal(val))) { - // std::cout << " Adding load to useBlocks for value: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << std::endl; - - funcInfo->addValue2UseBlocks(val, basicBlock); - } else { - // std::cout << " Skip array/global load" << std::endl; - } - } - } - } - - // 打印分析结果 - // std::cout << "\nAnalysis results for function " << func->getName() << ":" << std::endl; - - // auto &allocMap = funcInfo->getValue2AllocBlocks(); - // std::cout << "AllocBlocks (" << allocMap.size() << "):" << std::endl; - // for (auto &[val, bb] : allocMap) { - // std::cout << " "; - // printer.printInst(dynamic_cast(val)); - // std::cout << " in BB: " << bb->getName() << std::endl; - // } - - // auto &defMap = funcInfo->getValue2DefBlocks(); - // std::cout << "DefBlocks (" << defMap.size() << "):" << std::endl; - // for (auto &[val, bbs] : defMap) { - // std::cout << " "; - // printer.printInst(dynamic_cast(val)); - // for (const auto &[bb, count] : bbs) { - // std::cout << " in BB: " << bb->getName() << " (count: " << count << ")"; - // } - // } - - // auto &useMap = funcInfo->getValue2UseBlocks(); - // std::cout << "UseBlocks (" << useMap.size() << "):" << std::endl; - // for (auto &[val, bbs] : useMap) { - // std::cout << " "; - // printer.printInst(dynamic_cast(val)); - // for (const auto &[bb, count] : bbs) { - // std::cout << " in BB: " << bb->getName() << " (count: " << count << ")"; - // } - // } - } - // std::cout << "===== End computeValue2Blocks =====" << std::endl; -} + allPhiInstructions.push_back(newPhi); // 记录所有 Phi + phiPlacementBlocks.insert(domFrontierBB); // 标记已插入 + // 将 Phi 指令映射到它所代表的原始 alloca + phiMap[domFrontierBB][newPhi] = alloca; -/** - * @brief 级联关系的顺带消除,用于llvm mem2reg类预优化1 - * - * 采用队列进行模拟,从某种程度上来看其实可以看作是UD链的反向操作; - * - * @param [in] instr store指令使用的指令 - * @param [in] changed 不动点法的判断标准,地址传递 - * @param [in] func 指令所在函数 - * @param [in] block 指令所在基本块 - * @param [in] instrs 基本块所在指令集合,地址传递 - * @return 无返回值,但满足条件的情况下会对指令进行删除 - */ -auto Mem2Reg::cascade(Instruction *instr, bool &changed, Function *func, BasicBlock *block, - std::list> &instrs) -> void { - if (instr != nullptr) { - if (instr->isUnary() || instr->isBinary() || instr->isLoad()) { - std::queue toRemove; - toRemove.push(instr); - while (!toRemove.empty()) { - auto top = toRemove.front(); - toRemove.pop(); - auto operands = top->getOperands(); - for (const auto &operand : operands) { - auto elem = dynamic_cast(operand->getValue()); - if (elem != nullptr) { - if ((elem->isUnary() || elem->isBinary() || elem->isLoad()) && elem->getUses().size() == 1 && - elem->getUses().front()->getUser() == top) { - toRemove.push(elem); - } else if (elem->isAlloca()) { - // value2UseBlock中该block对应次数-1,如果该变量的该useblock中count减为0了,则意味着 - // 该block其他地方也没用到该alloc了,故从value2UseBlock中删除 - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - auto res = funcInfo->removeValue2UseBlock(elem, block); - // 只要有一次返回了true,就说明有变化 - if (res) { - changed = true; - } - } - } - } - auto tofind = - std::find_if(instrs.begin(), instrs.end(), [&top](const auto &instr) { return instr.get() == top; }); - assert(tofind != instrs.end()); - SysYIROptUtils::usedelete(tofind->get()); - instrs.erase(tofind); - } - } - } -} - -/** - * llvm mem2reg预优化1: 删除不含load的alloc和store - * - * 1. 删除不含load的alloc和store; - * 2. 删除store指令,之前的用于作store指令第0个操作数的那些级联指令就冗余了,也要删除; - * 3. 删除之后,可能有些变量的load使用恰好又没有了,因此再次从第一步开始循环,这里使用不动点法 - * - * 由于删除了级联关系,所以这里的方法有点儿激进; - * 同时也考虑了级联关系时如果调用了函数,可能会有side effect,所以没有删除调用函数的级联关系; - * 而且关于函数参数的alloca不会在指令中删除,也不会在value2Alloca中删除; - * 同样地,我们不考虑数组和global,不过这里的代码是基于value2blocks的,在value2blocks中已经考虑了,所以不用显式指明 - *= - */ -auto Mem2Reg::preOptimize1() -> void { - SysYPrinter printer(pModule); // 初始化打印机 - - auto &functions = pModule->getFunctions(); - // std::cout << "===== Start preOptimize1 =====" << std::endl; - - for (const auto &function : functions) { - auto func = function.second.get(); - // std::cout << "\nProcessing function: " << func->getName() << std::endl; - - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - if (!funcInfo) { - // std::cerr << "ERROR: No analysis info for function " << func->getName() << std::endl; - continue; - } - - auto &vToDefB = funcInfo->getValue2DefBlocks(); - auto &vToUseB = funcInfo->getValue2UseBlocks(); - auto &vToAllocB = funcInfo->getValue2AllocBlocks(); - - // 打印初始状态 - // std::cout << "Initial allocas: " << vToAllocB.size() << std::endl; - // for (auto &[val, bb] : vToAllocB) { - // std::cout << " Alloca: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << " in BB: " << bb->getName() << std::endl; - // } - - // 阶段1:删除无store的alloca - // std::cout << "\nPhase 1: Remove unused allocas" << std::endl; - for (auto iter = vToAllocB.begin(); iter != vToAllocB.end();) { - auto val = iter->first; - auto bb = iter->second; - - // std::cout << "Checking alloca: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << " in BB: " << bb->getName() << std::endl; - - // 如果该alloca没有对应的store指令,且不在函数参数中 - // 这里的vToDefB是value2DefBlocks,vToUseB是value2UseBlocks - - // 打印vToDefB - // std::cout << "DefBlocks (" << vToDefB.size() << "):" << std::endl; - // for (auto &[val, bbs] : vToDefB) { - // std::cout << " "; - // printer.printInst(dynamic_cast(val)); - // for (const auto &[bb, count] : bbs) { - // std::cout << " in BB: " << bb->getName() << " (count: " << count << ")" << std::endl; - // } - // } - // std::cout << vToDefB.count(val) << std::endl; - - if (vToDefB.count(val) == 0U && - std::find(func->getEntryBlock()->getArguments().begin(), - func->getEntryBlock()->getArguments().end(), - val) == func->getEntryBlock()->getArguments().end()) { - - // std::cout << " Removing unused alloca: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << std::endl; - - auto tofind = std::find_if(bb->getInstructions().begin(), - bb->getInstructions().end(), - [val](const auto &instr) { - return instr.get() == val; - }); - if (tofind == bb->getInstructions().end()) { - // std::cerr << "ERROR: Alloca not found in BB!" << std::endl; - ++iter; - continue; - } - - SysYIROptUtils::usedelete(tofind->get()); - bb->getInstructions().erase(tofind); - iter = vToAllocB.erase(iter); - } else { - ++iter; - } - } - - // 阶段2:删除无load的store - // std::cout << "\nPhase 2: Remove dead stores" << std::endl; - bool changed = true; - int iteration = 0; - - while (changed) { - changed = false; - iteration++; - // std::cout << "\nIteration " << iteration << std::endl; - - for (auto iter = vToDefB.begin(); iter != vToDefB.end();) { - auto val = iter->first; - - // std::cout << "Checking value: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << std::endl; - - if (vToUseB.count(val) == 0U) { - // std::cout << " Found dead store for value: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << std::endl; - - auto blocks = funcInfo->getDefBlocksByValue(val); - for (auto block : blocks) { - // std::cout << " Processing BB: " << block->getName() << std::endl; - // printer.printBlock(block); // 打印基本块内容 - - auto &instrs = block->getInstructions(); - for (auto it = instrs.begin(); it != instrs.end();) { - if ((*it)->isStore() && (*it)->getOperand(1) == val) { - // std::cout << " Removing store: "; - // printer.printInst(it->get()); - std::cout << std::endl; - - auto valUsedByStore = dynamic_cast((*it)->getOperand(0)); - SysYIROptUtils::usedelete(it->get()); - - if (valUsedByStore != nullptr && - valUsedByStore->getUses().size() == 1 && - valUsedByStore->getUses().front()->getUser() == (*it).get()) { - // std::cout << " Cascade deleting: "; - // printer.printInst(valUsedByStore); - // std::cout << std::endl; - - cascade(valUsedByStore, changed, func, block, instrs); - } - it = instrs.erase(it); - changed = true; - } else { - ++it; - } - } - } - - // 删除对应的alloca - if (std::find(func->getEntryBlock()->getArguments().begin(), - func->getEntryBlock()->getArguments().end(), - val) == func->getEntryBlock()->getArguments().end()) { - auto bb = funcInfo->getAllocBlockByValue(val); - if (bb != nullptr) { - // std::cout << " Removing alloca: "; - // printer.printInst(dynamic_cast(val)); - // std::cout << " in BB: " << bb->getName() << std::endl; - - funcInfo->removeValue2AllocBlock(val); - auto tofind = std::find_if(bb->getInstructions().begin(), - bb->getInstructions().end(), - [val](const auto &instr) { - return instr.get() == val; - }); - if (tofind != bb->getInstructions().end()) { - SysYIROptUtils::usedelete(tofind->get()); - bb->getInstructions().erase(tofind); - } else { - std::cerr << "ERROR: Alloca not found in BB!" << std::endl; - } - } - } - iter = vToDefB.erase(iter); - } else { - ++iter; - } - } - } - } - // std::cout << "===== End preOptimize1 =====" << std::endl; -} - -/** - * llvm mem2reg预优化2: 针对某个变量的Defblocks只有一个块的情况 - * - * 1. 该基本块最后一次对该变量的store指令后的所有对该变量的load指令都可以替换为该基本块最后一次store指令的第0个操作数; - * 2. 以该基本块为必经结点的结点集合中的对该变量的load指令都可以替换为该基本块最后一次对该变量的store指令的第0个操作数; - * 3. - * 如果对该变量的所有load均替换掉了,删除该基本块中最后一次store指令,如果这个store指令是唯一的define,那么再删除alloca指令(不删除参数的alloca); - * 4. - * 如果对该value的所有load都替换掉了,对于该变量剩下还有store的话,就转换成了preOptimize1的情况,再调用preOptimize1进行删除; - * - * 同样不考虑数组和全局变量,因为这些变量不会被mem2reg优化,在value2blocks中已经考虑了,所以不用显式指明; - * 替换的操作采用了UD链进行简化和效率的提升 - * - */ -auto Mem2Reg::preOptimize2() -> void { - auto &functions = pModule->getFunctions(); - for (const auto &function : functions) { - auto func = function.second.get(); - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - auto values = funcInfo->getValuesOfDefBlock(); - for (auto val : values) { - auto blocks = funcInfo->getDefBlocksByValue(val); - // 该val只有一个defining block - if (blocks.size() == 1) { - auto block = *blocks.begin(); - auto &instrs = block->getInstructions(); - auto rit = std::find_if(instrs.rbegin(), instrs.rend(), - [val](const auto &instr) { return instr->isStore() && instr->getOperand(1) == val; }); - // 注意reverse_iterator求base后是指向下一个指令,因此要减一才是原来的指令 - assert(rit != instrs.rend()); - auto it = --rit.base(); - auto propogationVal = (*it)->getOperand(0); - // 其实该块中it后对该val的load指令也可以替换掉了 - for (auto curit = std::next(it); curit != instrs.end();) { - if ((*curit)->isLoad() && (*curit)->getOperand(0) == val) { - curit->get()->replaceAllUsesWith(propogationVal); - SysYIROptUtils::usedelete(curit->get()); - curit = instrs.erase(curit); - funcInfo->removeValue2UseBlock(val, block); - } else { - ++curit; - } - } - // 在支配树后继结点中替换load指令的操作数 - BlockAnalysisInfo* blockInfo = controlFlowAnalysis->getBlockAnalysisInfo(block); - std::vector blkchildren; - // 获取该块的支配树后继结点 - std::queue q; - auto sdoms = blockInfo->getSdoms(); - for (auto sdom : sdoms) { - q.push(sdom); - blkchildren.push_back(sdom); - } - while (!q.empty()) { - auto blk = q.front(); - q.pop(); - BlockAnalysisInfo* blkInfo = controlFlowAnalysis->getBlockAnalysisInfo(blk); - for (auto sdom : blkInfo->getSdoms()) { - q.push(sdom); - blkchildren.push_back(sdom); - } - } - for (auto child : blkchildren) { - auto &childInstrs = child->getInstructions(); - for (auto childIter = childInstrs.begin(); childIter != childInstrs.end();) { - if ((*childIter)->isLoad() && (*childIter)->getOperand(0) == val) { - childIter->get()->replaceAllUsesWith(propogationVal); - SysYIROptUtils::usedelete(childIter->get()); - childIter = childInstrs.erase(childIter); - funcInfo->removeValue2UseBlock(val, child); - } else { - ++childIter; - } - } - } - // 如果对该val的所有load均替换掉了,那么对于该val的defining block中的最后一个define也可以删除了 - // 同时该块中前面对于该val的define也变成死代码了,可调用preOptimize1进行删除 - if (funcInfo->getUseBlocksByValue(val).empty()) { - SysYIROptUtils::usedelete(it->get()); - instrs.erase(it); - auto change = funcInfo->removeValue2DefBlock(val, block); - if (change) { - // 如果define是唯一的,且不是函数参数的alloca,直接删alloca - if (std::find(func->getEntryBlock()->getArguments().begin(), func->getEntryBlock()->getArguments().end(), - val) == func->getEntryBlock()->getArguments().end()) { - auto bb = funcInfo->getAllocBlockByValue(val); - assert(bb != nullptr); - auto tofind = std::find_if(bb->getInstructions().begin(), bb->getInstructions().end(), - [val](const auto &instr) { return instr.get() == val; }); - SysYIROptUtils::usedelete(tofind->get()); - bb->getInstructions().erase(tofind); - funcInfo->removeValue2AllocBlock(val); - } - } else { - // 如果该变量还有其他的define,那么前面的define也变成死代码了 - assert(!funcInfo->getDefBlocksByValue(val).empty()); - assert(funcInfo->getUseBlocksByValue(val).empty()); - preOptimize1(); + // 如果支配边界块本身没有定义该 alloca,则其支配边界也可能需要 Phi + // 只有当这个块不是当前alloca的定义块时,才将其加入workQueue,以计算其DF。 + if (allocaDefsBlock[alloca].find(domFrontierBB) == allocaDefsBlock[alloca].end()) { + workQueue.push(domFrontierBB); } } } @@ -492,285 +156,360 @@ auto Mem2Reg::preOptimize2() -> void { } } -/** - * @brief llvm mem2reg类预优化3:针对某个变量的所有读写都在同一个块中的情况 - * - * 1. 将每一个load替换成前一个store的值,并删除该load; - * 2. 如果在load前没有对该变量的store,则不删除该load; - * 3. 如果一个store后没有任何对改变量的load,则删除该store; - * - * @note 额外说明:第二点不用显式处理,因为我们的方法是从找到第一个store开始; - * 第三点其实可以更激进一步地理解,即每次替换了load之后,它对应地那个store也可以删除了,同时注意这里不要使用preoptimize1进行处理,因为他们的级联关系是有用的:即用来求load的替换值; - * 同样地,我们这里不考虑数组和全局变量,因为这些变量不会被mem2reg优化,不过这里在计算value2DefBlocks时已经跳过了,所以不需要再显式处理了; - * 替换的操作采用了UD链进行简化和效率的提升 - * - * @param [in] void - * @return 无返回值,但满足条件的情况下会对指令的操作数进行替换以及对指令进行删除 - */ -auto Mem2Reg::preOptimize3() -> void { - auto &functions = pModule->getFunctions(); - for (const auto &function : functions) { - auto func = function.second.get(); - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - auto values = funcInfo->getValuesOfDefBlock(); - for (auto val : values) { - auto sblocks = funcInfo->getDefBlocksByValue(val); - auto lblocks = funcInfo->getUseBlocksByValue(val); - if (sblocks.size() == 1 && lblocks.size() == 1 && *sblocks.begin() == *lblocks.begin()) { - auto block = *sblocks.begin(); - auto &instrs = block->getInstructions(); - auto it = std::find_if(instrs.begin(), instrs.end(), - [val](const auto &instr) { return instr->isStore() && instr->getOperand(1) == val; }); - while (it != instrs.end()) { - auto propogationVal = (*it)->getOperand(0); - auto last = std::find_if(std::next(it), instrs.end(), [val](const auto &instr) { - return instr->isStore() && instr->getOperand(1) == val; - }); - for (auto curit = std::next(it); curit != last;) { - if ((*curit)->isLoad() && (*curit)->getOperand(0) == val) { - curit->get()->replaceAllUsesWith(propogationVal); - SysYIROptUtils::usedelete(curit->get()); - curit = instrs.erase(curit); - funcInfo->removeValue2UseBlock(val, block); - } else { - ++curit; - } - } - // 替换了load之后,它对应地那个store也可以删除了 - if (!(std::find_if(func->getEntryBlock()->getArguments().begin(), func->getEntryBlock()->getArguments().end(), - [val](const auto &instr) { return instr == val; }) != - func->getEntryBlock()->getArguments().end()) && - last == instrs.end()) { - SysYIROptUtils::usedelete(it->get()); - it = instrs.erase(it); - if (funcInfo->removeValue2DefBlock(val, block)) { - auto bb = funcInfo->getAllocBlockByValue(val); - if (bb != nullptr) { - auto tofind = std::find_if(bb->getInstructions().begin(), bb->getInstructions().end(), - [val](const auto &instr) { return instr.get() == val; }); - SysYIROptUtils::usedelete(tofind->get()); - bb->getInstructions().erase(tofind); - funcInfo->removeValue2AllocBlock(val); - } - } - } - it = last; - } - } - } - } -} - -/** - * 为所有变量的定义块集合的迭代支配边界插入phi结点 - * - * insertPhi是mem2reg的核心之一,这里是对所有变量的迭代支配边界的phi结点插入,无参数也无返回值; - * 同样跳过对数组和全局变量的处理,因为这些变量不会被mem2reg优化,刚好这里在计算value2DefBlocks时已经跳过了,所以不需要再显式处理了; - * 同时我们进行了剪枝处理,只有在基本块入口活跃的变量,才插入phi函数 - * - */ -auto Mem2Reg::insertPhi() -> void { - auto &functions = pModule->getFunctions(); - for (const auto &function : functions) { - auto func = function.second.get(); - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - const auto &vToDefB = funcInfo->getValue2DefBlocks(); - for (const auto &map_pair : vToDefB) { - // 首先为每个变量找到迭代支配边界 - auto val = map_pair.first; - auto blocks = funcInfo->getDefBlocksByValue(val); - auto itDFs = computeIterDf(blocks); - // 然后在每个变量相应的迭代支配边界上插入phi结点 - for (auto basicBlock : itDFs) { - const auto &actiTable = activeVarAnalysis->getActiveTable(); - auto dval = dynamic_cast(val); - // 只有在基本块入口活跃的变量,才插入phi函数 - if (actiTable.at(basicBlock).front().count(dval) != 0U) { - pBuilder->createPhiInst(val->getType(), val, basicBlock); - } - } - } - } -} - -/** - * 重命名 - * - * 重命名是mem2reg的核心之二,这里是对单个块的重命名,递归实现 - * 同样跳过对数组和全局变量的处理,因为这些变量不会被mem2reg优化 - * - */ -auto Mem2Reg::rename(BasicBlock *block, std::unordered_map &count, - std::unordered_map> &stacks) -> void { - auto &instrs = block->getInstructions(); - std::unordered_map valPop; - // 第一大步:对块中的所有指令遍历处理 - for (auto iter = instrs.begin(); iter != instrs.end();) { - auto instr = iter->get(); - // 对于load指令,变量用最新的那个 - if (instr->isLoad()) { - auto val = instr->getOperand(0); - if (!(SysYIROptUtils::isArr(val) || SysYIROptUtils::isGlobal(val))) { - if (!stacks[val].empty()) { - instr->replaceOperand(0, stacks[val].top()); - } - } - } - // 然后对于define的情况,看alloca、store和phi指令 - if (instr->isDefine()) { - if (instr->isAlloca()) { - // alloca指令名字不改了,命名就按x,x_1,x_2...来就行 - auto val = instr; - if (!(SysYIROptUtils::isArr(val) || SysYIROptUtils::isGlobal(val))) { - ++valPop[val]; - stacks[val].push(val); - ++count[val]; - } - } else if (instr->isPhi()) { - // Phi指令也是一条特殊的define指令 - auto val = dynamic_cast(instr)->getMapVal(); - if (!(SysYIROptUtils::isArr(val) || SysYIROptUtils::isGlobal(val))) { - auto i = count[val]; - if (i == 0) { - // 对还未alloca就有phi的指令的处理,直接删除 - SysYIROptUtils::usedelete(iter->get()); - iter = instrs.erase(iter); - continue; - } - auto newname = dynamic_cast(val)->getName() + "_" + std::to_string(i); - auto newalloca = pBuilder->createAllocaInstWithoutInsert(val->getType(), {}, block, newname); - FunctionAnalysisInfo* ParentfuncInfo = controlFlowAnalysis->getFunctionAnalysisInfo(block->getParent()); - ParentfuncInfo->addIndirectAlloca(newalloca); - instr->replaceOperand(0, newalloca); - ++valPop[val]; - stacks[val].push(newalloca); - ++count[val]; - } - } else { - // store指令看operand的名字,我们的实现是规定变量在operand的第二位,用一个新的alloca x_i代替 - auto val = instr->getOperand(1); - if (!(SysYIROptUtils::isArr(val) || SysYIROptUtils::isGlobal(val))) { - auto i = count[val]; - auto newname = dynamic_cast(val)->getName() + "_" + std::to_string(i); - auto newalloca = pBuilder->createAllocaInstWithoutInsert(val->getType(), {}, block, newname); - FunctionAnalysisInfo* ParentfuncInfo = controlFlowAnalysis->getFunctionAnalysisInfo(block->getParent()); - ParentfuncInfo->addIndirectAlloca(newalloca); - // block->getParent()->addIndirectAlloca(newalloca); - instr->replaceOperand(1, newalloca); - ++valPop[val]; - stacks[val].push(newalloca); - ++count[val]; - } - } - } - ++iter; - } - // 第二大步:把所有CFG中的该块的successor的phi指令的相应operand确定 - for (auto succ : block->getSuccessors()) { - auto position = getPredIndex(block, succ); - for (auto &instr : succ->getInstructions()) { - if (instr->isPhi()) { - auto val = dynamic_cast(instr.get())->getMapVal(); - if (!stacks[val].empty()) { - instr->replaceOperand(position + 1, stacks[val].top()); - } - } else { - // phi指令是添加在块的最前面的,因此过了之后就不会有phi了,直接break - break; - } - } - } - // 第三大步:递归支配树的后继,支配树才能表示define-use关系 - BlockAnalysisInfo* blockInfo = controlFlowAnalysis->getBlockAnalysisInfo(block); - for (auto sdom : blockInfo->getSdoms()) { - rename(sdom, count, stacks); - } - // 第四大步:遍历块中的所有指令,如果涉及到define,就弹栈,这一步是必要的,可以从递归的整体性来思考原因 - // 注意这里count没清理,因为平级之间计数仍然是一直增加的,但是stack要清理,因为define-use关系来自直接 - // 支配结点而不是平级之间,不清理栈会被污染 - // 提前优化:知道变量对应的要弹栈的次数就可以了,没必要遍历所有instr. - for (auto val_pair : valPop) { - auto val = val_pair.first; - for (int i = 0; i < val_pair.second; ++i) { - stacks[val].pop(); - } - } -} - -/** - * 重命名所有块 - * - * 调用rename,自上而下实现所有rename - * - */ -auto Mem2Reg::renameAll() -> void { - auto &functions = pModule->getFunctions(); - for (const auto &function : functions) { - auto func = function.second.get(); - // 对于每个function都要SSA化,所以count和stacks定义在这并初始化 - std::unordered_map count; - std::unordered_map> stacks; - FunctionAnalysisInfo* funcInfo = controlFlowAnalysis->getFunctionAnalysisInfo(func); - for (const auto &map_pair : funcInfo->getValue2DefBlocks()) { - auto val = map_pair.first; - count[val] = 0; - } - rename(func->getEntryBlock(), count, stacks); - } -} - -/** - * mem2reg,对外的接口 - * - * 静态单一赋值 + mem2reg等pass的逻辑组合 - * - */ -auto Mem2Reg::mem2regPipeline() -> void { - // 首先进行mem2reg的前置分析 - controlFlowAnalysis->clear(); - controlFlowAnalysis->runControlFlowAnalysis(); - // 活跃变量分析 - activeVarAnalysis->clear(); - dataFlowAnalysisUtils.addBackwardAnalyzer(activeVarAnalysis); - dataFlowAnalysisUtils.backwardAnalyze(pModule); - - // 计算所有valueToBlocks的定义映射 - computeValue2Blocks(); - // SysYPrinter printer(pModule); - // 参考llvm的mem2reg遍,在插入phi结点之前,先做些优化 - preOptimize1(); - // printer.printIR(); - preOptimize2(); - // printer.printIR(); - // 优化三 可能会针对局部变量优化而删除整个块的alloca/store - preOptimize3(); - //再进行活跃变量分析 - // 报错? - - // printer.printIR(); - dataFlowAnalysisUtils.backwardAnalyze(pModule); - // 为所有变量插入phi结点 - insertPhi(); - // 重命名 - renameAll(); -} - -/** - * 计算块n是块s的第几个前驱 - * - * helperfunction,没有返回值,但是会将dom和other的交集赋值给dom - * - */ -auto Mem2Reg::getPredIndex(BasicBlock *n, BasicBlock *s) -> int { +// 获取前驱块在后继块前驱列表中的索引 +int Mem2Reg::getPredIndex(BasicBlock* pred, BasicBlock* succ) { int index = 0; - for (auto elem : s->getPredecessors()) { - if (elem == n) { - break; + for (auto* elem : succ->getPredecessors()) { + if (elem == pred) { + return index; } ++index; } - assert(index < static_cast(s->getPredecessors().size()) && "n is not a predecessor of s."); - return index; + // 断言通常在你的 IR 框架中应该确保前驱是存在的 + // assert(false && "Predecessor not found in successor's predecessor list"); + return -1; // 应该不会发生 } -} // namespace sysy +// 递归地重命名基本块中的变量并填充 Phi 指令 +void Mem2Reg::renameBlock(BasicBlock* block, + std::unordered_map& currentIncomings, + std::unordered_set& visitedBlocks) { + + // 记录在此块中发生的定义,以便在退出时将它们从栈中弹出 + std::unordered_map definitionsInBlockCount; + + // 如果已经访问过这个块,直接返回(防止无限循环或重复处理,在DFS中尤其重要) + if (visitedBlocks.count(block)) { + return; + } + visitedBlocks.insert(block); + + // --- 1. 处理当前基本块内的指令 --- + // 使用迭代器安全地遍历和删除指令 + for (auto it = block->getInstructions().begin(); it != block->getInstructions().end(); ) { + Instruction* currentInst = it->get(); + + if (AllocaInst* alloca = dynamic_cast(currentInst)) { + // 如果是可提升的 alloca,标记为删除 + if (std::find(currentFunctionAllocas.begin(), currentFunctionAllocas.end(), alloca) != currentFunctionAllocas.end()) { + SysYIROptUtils::usedelete(currentInst); // 标记为删除(或直接删除取决于你的 IR 管理) + it = block->getInstructions().erase(it); // 从列表中移除 + continue; // 继续下一个指令 + } + } else if (LoadInst* load = dynamic_cast(currentInst)) { + AllocaInst* originalAlloca = dynamic_cast(load->getOperand(0)); // load 的第一个操作数是指针 + if (originalAlloca && std::find(currentFunctionAllocas.begin(), currentFunctionAllocas.end(), originalAlloca) != currentFunctionAllocas.end()) { + // 如果是可提升 alloca 的 load 指令 + Value* incomingVal = nullptr; + if (currentIncomings.count(originalAlloca)) { + incomingVal = currentIncomings[originalAlloca]; + } else { + // 如果在当前路径上没有找到定义,则使用 UndefinedValue + incomingVal = UndefinedValue::get(originalAlloca->getType()->isPointer() ? + dynamic_cast(originalAlloca->getType())->getBaseType() : + originalAlloca->getType()); + } + + load->replaceAllUsesWith(incomingVal); // 用最新值替换所有 load 的用途 + SysYIROptUtils::usedelete(currentInst); + it = block->getInstructions().erase(it); + continue; + } + } else if (StoreInst* store = dynamic_cast(currentInst)) { + AllocaInst* originalAlloca = dynamic_cast(store->getOperand(1)); // store 的第二个操作数是指针 + if (originalAlloca && std::find(currentFunctionAllocas.begin(), currentFunctionAllocas.end(), originalAlloca) != currentFunctionAllocas.end()) { + // 如果是可提升 alloca 的 store 指令,更新当前值 + currentIncomings[originalAlloca] = store->getOperand(0); // store 的第一个操作数是值 + definitionsInBlockCount[originalAlloca]++; // 记录在该块中进行的定义数量 + SysYIROptUtils::usedelete(currentInst); + it = block->getInstructions().erase(it); + continue; + } + } else if (PhiInst* phi = dynamic_cast(currentInst)) { + // 如果是 Mem2Reg 插入的 Phi 指令 (通过 phiMap 判断) + if (phiMap[block].count(phi)) { + AllocaInst* originalAlloca = phiMap[block][phi]; + currentIncomings[originalAlloca] = phi; // Phi 指令本身成为该变量的新定义 + definitionsInBlockCount[originalAlloca]++; // 记录该 Phi 的定义 + } + } + ++it; // 移动到下一个指令 + } + + // --- 2. 填充后继基本块中 Phi 指令的入边 --- + for (BasicBlock* successorBB : block->getSuccessors()) { + int predIndex = getPredIndex(block, successorBB); + if (predIndex == -1) continue; + + // Phi 指令总是在基本块的开头 + for (auto& inst_ptr : successorBB->getInstructions()) { + if (PhiInst* phi = dynamic_cast(inst_ptr.get())) { + if (phiMap[successorBB].count(phi)) { // 确保这是我们关心的 Phi 指令 + AllocaInst* originalAlloca = phiMap[successorBB][phi]; + Value* incomingValue = nullptr; + + if (currentIncomings.count(originalAlloca)) { + incomingValue = currentIncomings[originalAlloca]; + } else { + // 如果在当前块没有找到对应的定义,使用 UndefinedValue + incomingValue = UndefinedValue::get(originalAlloca->getType()->isPointer() ? + dynamic_cast(originalAlloca->getType())->getBaseType() : + originalAlloca->getType()); + } + + if (incomingValue) { + phi->addIncoming(incomingValue, block); // 添加 (值, 前驱块) 对 + } + } + } else { + // 遇到非 Phi 指令,说明已经处理完所有 Phi,可以跳出 + break; + } + } + } + + // --- 3. 递归调用支配树的子节点 --- + auto* blockInfo = controlFlowAnalysis->getBlockAnalysisInfo(block); + if (blockInfo) { + for (BasicBlock* dominatedChildBB : blockInfo->getSdoms()) { // getSdoms 获取直接支配的子节点 + // 递归调用,传递当前 Incomings 的副本(或通过值传递以实现回溯) + // 注意:这里是传递 `currentIncomings` 的拷贝,以便递归返回后可以恢复。 + // 但如果 `currentIncomings` 是引用传递,则这里需要回溯逻辑。 + // 鉴于它是值传递,此处的 `definitionsInBlockCount` 仅用于统计,无需实际操作 `currentIncomings`。 + renameBlock(dominatedChildBB, currentIncomings, visitedBlocks); + } + } + + // --- 4. 回溯:从栈中弹出在此块中创建的所有定义 --- + for (auto const& [alloca, count] : definitionsInBlockCount) { + // 在我们的实现中,`currentIncomings` 是通过值传递的,每次递归都收到一个新的拷贝。 + // 因此,不需要显式地 "pop" 栈。`currentIncomings` 在函数返回时会自动销毁。 + // 这种方式模拟了 "SSA 栈" 的行为,每个函数调用帧有自己的局部定义环境。 + } +} + +// 简化冗余的 Phi 指令 (当所有输入都相同时) +void Mem2Reg::simplifyphi(PhiInst* phi) { + BasicBlock* phifromblock = phi->getParent(); + if (!phifromblock) return; // 指令可能已经被删除 + + Value* commonValue = nullptr; + bool allSame = true; + + // Phi 指令的操作数是 Value, BasicBlock 交替出现,所以是 getOperandSize() / 2 个入边 + if (phi->getNumOperands() == 0) { // 空 Phi,通常是无效的,直接删除 + phi->replaceAllUsesWith(UndefinedValue::get(phi->getType())); // 用 UndefinedValue 替换所有用途 + // phi->getParent()->delete_inst(phi); + // 删除 Phi 指令后直接返回 + // phi指令在开头一个比较快 + // TODO:后续可优化查找 + auto tofind = std::find_if(phifromblock->getInstructions().begin(), phifromblock->getInstructions().end(), + [phi](const auto &instr) { return instr.get() == phi; }); + SysYIROptUtils::usedelete(phi); // 使用 SysYIROptUtils 删除指令 + phifromblock->getInstructions().erase(tofind); + // 从基本块中删除 Phi 指令 + return; + } + + for (size_t i = 0; i < phi->getNumIncomingValues(); ++i) { + Value* incomingVal = phi->getOperand(2 * i); // 值位于偶数索引 + + if (incomingVal == phi) { // 如果 Phi 指令引用自身 (循环变量) + // 这种情况下,Phi 暂时不能简化,除非所有入边都是它自己,这通常通过其他优化处理 + // 为避免复杂性,我们在此处不处理自引用 Phi 的简化,除非它是唯一选择。 + // 更好的做法是,如果所有入边都指向自身,则该Phi是冗余的,可以替换为undef或其第一个实际值 + // 但这需要更复杂的分析来确定循环的初始值。目前简单返回。 + // TODO:留到后续循环优化处理 + return; + } + + if (commonValue == nullptr) { + commonValue = incomingVal; + } else if (commonValue != incomingVal) { + allSame = false; + break; // 发现不同的入边值 + } + } + + if (allSame && commonValue != nullptr) { + // 所有入边值都相同,用这个值替换 Phi 指令的所有用途 + phi->replaceAllUsesWith(commonValue); + // 从基本块中删除 Phi 指令 + auto tofind = std::find_if(phifromblock->getInstructions().begin(), phifromblock->getInstructions().end(), + [phi](const auto &instr) { return instr.get() == phi; }); + SysYIROptUtils::usedelete(phi); // 使用 SysYIROptUtils 删除指令 + phifromblock->getInstructions().erase(tofind); + } +} + +// 对单个函数执行内存到寄存器的提升 +bool Mem2Reg::promoteMemoryToRegisters(Function* func) { + bool changed = false; + + // 每次开始对一个函数进行 Mem2Reg 时,清空所有上下文信息 + currentFunctionAllocas.clear(); + allocaDefsBlock.clear(); + allocaUsesBlock.clear(); + phiMap.clear(); + allPhiInstructions.clear(); + + // 1. 收集所有可提升的 AllocaInst,并进行初步分析 + BasicBlock* entryBB = func->getEntryBlock(); + if (!entryBB) return false; + + // 逆序遍历入口块的指令,安全地识别 Alloca + for (auto it = entryBB->getInstructions().rbegin(); it != entryBB->getInstructions().rend(); ++it) { + if (AllocaInst* alloca = dynamic_cast(it->get())) { + if (is_promoted(alloca)) { + currentFunctionAllocas.push_back(alloca); + } + } + } + // 收集后反转,使其按原始顺序排列 (如果需要的话,但对后续分析影响不大) + std::reverse(currentFunctionAllocas.begin(), currentFunctionAllocas.end()); + + // 对收集到的所有 alloca 进行 DefsBlock 和 UsesBlock 分析 + for (AllocaInst* alloca : currentFunctionAllocas) { + allocaAnalysis(alloca); + } + + // 2. 预处理:删除无用的 AllocaInst (没有 Load 和 Store) + // 迭代 currentFunctionAllocas,安全删除 + for (unsigned int i = 0; i < currentFunctionAllocas.size(); ) { + AllocaInst* alloca = currentFunctionAllocas[i]; + + bool hasRelevantUse = false; + // 检查 alloca 的 uses 列表,看是否有 Load 或 Store + // 只要有 Load/Store,就认为是"相关用途",不删除 + for (auto use_ptr : alloca->getUses()) { + Instruction* user_inst = dynamic_cast(use_ptr->getUser()); + if (user_inst && (dynamic_cast(user_inst) || dynamic_cast(user_inst))) { + hasRelevantUse = true; + break; + } + } + + // 如果没有相关用途(没有 Load 和 Store),则 alloca 是死代码 + if (!hasRelevantUse && allocaDefsBlock[alloca].empty() && allocaUsesBlock[alloca].empty()) { + if (alloca->getParent()) { + // alloca->getParent()->delete_inst(alloca); // 从其所在块删除 alloca 指令 + auto tofind = std::find_if(alloca->getParent()->getInstructions().begin(), alloca->getParent()->getInstructions().end(), + [alloca](const auto &instr) { return instr.get() == alloca; }); + SysYIROptUtils::usedelete(alloca); + alloca->getParent()->getInstructions().erase(tofind); + } + currentFunctionAllocas.erase(currentFunctionAllocas.begin() + i); // 从列表中移除 + changed = true; // 发生了改变 + } else { + i++; // 否则,移动到下一个 alloca + } + } + + // 如果没有可提升的 alloca 了,直接返回 + if (currentFunctionAllocas.empty()) { + return changed; + } + + // 3. 插入 Phi 指令 + insertPhiNodes(func); + if (!allPhiInstructions.empty()) changed = true; + + // 4. 重命名变量,转换为 SSA 形式并填充 Phi 指令 + std::unordered_map initialIncomings; + std::unordered_set visitedBlocks; // 用于 DFS 遍历,防止循环 + + // 初始化 entry block 的 Incomings 状态 + for (AllocaInst* alloca : currentFunctionAllocas) { + initialIncomings[alloca] = UndefinedValue::get(dynamic_cast(alloca->getType())->getBaseType()); + } + + // 从入口块开始递归重命名 + renameBlock(entryBB, initialIncomings, visitedBlocks); + + // 5. 简化 Phi 指令 + // 由于 renameBlock 可能会删除 Phi,这里复制一份列表以安全迭代 + std::vector phisToSimplify = allPhiInstructions; + for (PhiInst* phi : phisToSimplify) { + // 检查 phi 是否还在 IR 中 (可能已被其他优化删除) + // 一个简单检查是看它是否有父块 + if (phi->getParent()) { + simplifyphi(phi); + // simplifyphi 内部会删除 Phi,所以这里不需要再处理 allPhiInstructions + // 最终的 allPhiInstructions 清理将在 promoteMemoryToRegisters 结束后进行 + } + } + + // 清理所有 Phi 的列表和映射 + // 遍历 allPhiInstructions,删除那些在 simplifyphi 后可能仍然存在的、但已经没有 uses 的 Phi + std::vector remainingPhis; + for(PhiInst* phi : allPhiInstructions) { + if(phi->getParent() && phi->getUses().empty()){ // 如果还在IR中但没有用处 + + // phi->getParent()->delete_inst(phi); + // 找到phi节点对应的迭代器 + auto tofind = std::find_if(phi->getParent()->getInstructions().begin(), phi->getParent()->getInstructions().end(), + [phi](const auto &instr) { return instr.get() == phi; }); + SysYIROptUtils::usedelete(phi); // 使用 SysYIROptUtils 删除指令 + phi->getParent()->getInstructions().erase(tofind); + + changed = true; + } else if (phi->getParent()) { // 仍在IR中且有uses + remainingPhis.push_back(phi); + } + } + allPhiInstructions = remainingPhis; // 更新为仅包含未被删除的 Phi + + // 重新清理 phiMap 中已经删除的 Phi 指令项 + for (auto& pairBBPhiMap : phiMap) { + std::vector phisToRemoveFromMap; + for (auto& pairPhiAlloca : pairBBPhiMap.second) { + if (!pairPhiAlloca.first->getParent()) { // 如果 Phi 已经被删除 + phisToRemoveFromMap.push_back(pairPhiAlloca.first); + } + } + for (PhiInst* phi : phisToRemoveFromMap) { + pairBBPhiMap.second.erase(phi); + } + } + + + return changed; +} + +// --- run函数实现 --- +void Mem2Reg::run() { + // 每次运行整个 Mem2Reg Pass 时,重新进行分析 + controlFlowAnalysis->clear(); + controlFlowAnalysis->runControlFlowAnalysis(); + activeVarAnalysis->clear(); + // 假设 dataFlowAnalysisUtils 可以管理和运行各个分析器 + dataFlowAnalysisUtils.addBackwardAnalyzer(activeVarAnalysis); + dataFlowAnalysisUtils.backwardAnalyze(pModule); // 运行活跃变量分析 + + bool globalChanged = false; + // 循环直到没有更多的 alloca 可以被提升 + // 每次 promoteMemoryToRegisters 会尝试在一个函数内完成所有 Mem2Reg 优化 + do { + globalChanged = false; + for (const auto& [_, func] : pModule->getFunctions()) { + // 对每个函数执行 Mem2Reg + if (promoteMemoryToRegisters(func.get())) { + globalChanged = true; + // 如果一个函数发生改变,可能影响其他函数或需要重新分析 + // 因此需要重新运行控制流和活跃变量分析,以备下一次循环 + controlFlowAnalysis->clear(); + controlFlowAnalysis->runControlFlowAnalysis(); + activeVarAnalysis->clear(); + dataFlowAnalysisUtils.backwardAnalyze(pModule); // 重新分析活跃变量 + } + } + } while (globalChanged); // 如果全局有任何函数发生改变,则继续迭代 + + // 最终清理和重新分析 + controlFlowAnalysis->clear(); + controlFlowAnalysis->runControlFlowAnalysis(); + activeVarAnalysis->clear(); + dataFlowAnalysisUtils.backwardAnalyze(pModule); +} + +} // namespace sysy \ No newline at end of file diff --git a/src/include/Mem2Reg.h b/src/include/Mem2Reg.h index 919886a..2c65583 100644 --- a/src/include/Mem2Reg.h +++ b/src/include/Mem2Reg.h @@ -1,57 +1,79 @@ -#pragma once +// 假设 Mem2Reg.h 看起来像这样 (你需要根据实际情况调整) +#ifndef SYSY_MEM2REG_H +#define SYSY_MEM2REG_H -#include -#include -#include +#include #include #include +#include +#include // For computeIteratedDomFrontiers + +// Include your IR and analysis headers #include "IR.h" #include "IRBuilder.h" #include "SysYIRAnalyser.h" #include "SysYIROptUtils.h" namespace sysy { -/** - * 实现静态单变量赋值核心类 mem2reg - */ + class Mem2Reg { private: - Module *pModule; - IRBuilder *pBuilder; - ControlFlowAnalysis *controlFlowAnalysis; // 控制流分析 - ActiveVarAnalysis *activeVarAnalysis; // 活跃变量分析 - DataFlowAnalysisUtils dataFlowAnalysisUtils; + Module* pModule; + IRBuilder* pBuilder; + ControlFlowAnalysis* controlFlowAnalysis; + ActiveVarAnalysis* activeVarAnalysis; + DataFlowAnalysisUtils dataFlowAnalysisUtils; // If this is part of Mem2Reg or an external helper public: - Mem2Reg(Module *pMoudle, IRBuilder *pBuilder, - ControlFlowAnalysis *pCFA = nullptr, ActiveVarAnalysis *pAVA = nullptr) : - pModule(pMoudle), pBuilder(pBuilder), controlFlowAnalysis(pCFA), activeVarAnalysis(pAVA), dataFlowAnalysisUtils() - {} // 初始化函数 + Mem2Reg(Module* module, IRBuilder* builder, ControlFlowAnalysis* cfa, ActiveVarAnalysis* ava) + : pModule(module), pBuilder(builder), controlFlowAnalysis(cfa), activeVarAnalysis(ava) {} + // Constructor initializes members + void run(); - void mem2regPipeline(); ///< mem2reg + // --- 新增的私有成员变量和方法,用于SSA转换上下文 --- + // 这是核心,用于存储 SSA 转换过程中的状态 + std::vector currentFunctionAllocas; // 当前函数中所有可提升的 alloca + // alloca -> set of BasicBlocks where it's defined (stored into) + std::unordered_map> allocaDefsBlock; + // alloca -> set of BasicBlocks where it's used (loaded from) + std::unordered_map> allocaUsesBlock; -private: + // BasicBlock -> Map of (PhiInst, Original AllocaInst) + // 用于在 rename 阶段通过 phi 指令找到它代表的原始 alloca + std::unordered_map> phiMap; + std::vector allPhiInstructions; // 收集所有创建的 Phi 指令以便后续简化和清理 - // phi节点的插入需要计算IDF - std::unordered_set computeIterDf(const std::unordered_set &blocks); ///< 计算定义块集合的迭代支配边界 + // --- 核心 SSA 转换辅助函数 --- + // 计算给定定义块集合的迭代支配边界 + std::unordered_set computeIteratedDomFrontiers(const std::unordered_set& blocks); - auto computeValue2Blocks() -> void; ///< 计算value2block的映射(不包括数组和global) + // 分析一个 alloca 的所有 uses,填充 allocaDefsBlock 和 allocaUsesBlock + void allocaAnalysis(AllocaInst* alloca); - auto preOptimize1() -> void; ///< llvm memtoreg预优化1: 删除不含load的alloc和store - auto preOptimize2() -> void; ///< llvm memtoreg预优化2: 针对某个变量的Defblocks只有一个块的情况 - auto preOptimize3() -> void; ///< llvm memtoreg预优化3: 针对某个变量的所有读写都在同一个块中的情况 + // 判断一个 alloca 是否可以被提升为寄存器 (无地址逃逸,标量类型) + bool is_promoted(AllocaInst* alloca); - auto insertPhi() -> void; ///< 为所有变量的迭代支配边界插入phi结点 + // 在迭代支配边界处插入 Phi 指令 + void insertPhiNodes(Function* func); - auto rename(BasicBlock *block, std::unordered_map &count, - std::unordered_map> &stacks) -> void; ///< 单个块的重命名 - auto renameAll() -> void; ///< 重命名所有块 + // 递归地重命名基本块中的变量并填充 Phi 指令 + // 这里的 `count` 和 `stacks` 是临时的,用于 DFS 过程中传递状态 + void renameBlock(BasicBlock* block, + std::unordered_map& currentIncomings, + std::unordered_set& visitedBlocks); // 修改为传递 map 和 set - // private helper function. -private: - auto getPredIndex(BasicBlock *n, BasicBlock *s) -> int; ///< 获取前驱索引 - auto cascade(Instruction *instr, bool &changed, Function *func, BasicBlock *block, - std::list> &instrs) -> void; ///< 消除级联关系 + // 简化冗余的 Phi 指令 (当所有输入都相同时) + void simplifyphi(PhiInst* phi); + + // 获取前驱块在后继块前驱列表中的索引,用于 Phi 指令入边 + int getPredIndex(BasicBlock* pred, BasicBlock* succ); + + // --- Mem2Reg 的主要工作流函数 --- + // 对单个函数执行内存到寄存器的提升 + bool promoteMemoryToRegisters(Function* func); }; -} // namespace sysy + +} // namespace sysy + +#endif // SYSY_MEM2REG_H \ No newline at end of file