diff --git a/Pass_ID_List.md b/Pass_ID_List.md index 14f3379..5618060 100644 --- a/Pass_ID_List.md +++ b/Pass_ID_List.md @@ -3,4 +3,26 @@ | 名称 | 优化级别 | 开发进度 | | ------------ | ------------ | ---------- | | CFG优化 | 函数级 | 已完成 | -| DCE | 函数级 | 待测试 | \ No newline at end of file +| DCE | 函数级 | 待正确性测试 | +| Mem2Reg | 函数级 | 待正确性测试 | +| Reg2Mem | 函数级 | 待正确性测试 | + + +# 部分优化遍的说明 + +## Mem2Reg + +Mem2Reg 遍的主要目标是将那些不必要的、只用于局部标量变量的内存分配 (alloca 指令) 消除,并将这些变量的值转换为 SSA 形式。这有助于减少内存访问,提高代码效率,并为后续的优化创造更好的条件。 + +## Reg2Mem + +我们的Reg2Mem 遍的主要目标是作为 Mem2Reg 的一种逆操作,但更具体是解决后端无法识别 PhiInst 指令的问题。主要的速录是将函数参数和 PhiInst 指令的结果从 SSA 形式转换回内存形式,通过插入 alloca、load 和 store 指令来实现。其他非 Phi 的指令结果将保持 SSA 形式。 + + +# 后续优化可能涉及的改动 + +## 1)将所有的alloca集中到entryblock中 + +好处:优化友好性,方便mem2reg提升 +目前没有实现这个机制,如果想要实现首先解决同一函数不同域的同名变量命名区分 +需要保证符号表能正确维护域中的局部变量 \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 48ab612..2ccdd2c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,8 +27,8 @@ add_executable(sysyc Liveness.cpp DCE.cpp AddressCalculationExpansion.cpp - # Mem2Reg.cpp - # Reg2Mem.cpp + Mem2Reg.cpp + Reg2Mem.cpp RISCv64Backend.cpp RISCv64ISel.cpp RISCv64RegAlloc.cpp diff --git a/src/Dom.cpp b/src/Dom.cpp index d476c49..73726c5 100644 --- a/src/Dom.cpp +++ b/src/Dom.cpp @@ -38,6 +38,14 @@ const std::set *DominatorTree::getDominanceFrontier(BasicBlock *BB return nullptr; } +const std::set* DominatorTree::getDominatorTreeChildren(BasicBlock* BB) const { + auto it = DominatorTreeChildren.find(BB); + if (it != DominatorTreeChildren.end()) { + return &(it->second); + } + return nullptr; +} + void DominatorTree::computeDominators(Function *F) { // 经典的迭代算法计算支配者集合 // TODO: 可以替换为更高效的算法,如 Lengauer-Tarjan 算法 @@ -159,6 +167,19 @@ void DominatorTree::computeDominanceFrontiers(Function *F) { } } +void DominatorTree::computeDominatorTreeChildren(Function *F) { + for (auto &bb_ptr : F->getBasicBlocks()) { + BasicBlock *B = bb_ptr.get(); + auto it = getImmediateDominator(B); + if (it != nullptr) { + BasicBlock *A = it; + if (A) { + DominatorTreeChildren[A].insert(B); + } + } + } +} + // ============================================================== // DominatorTreeAnalysisPass 的实现 // ============================================================== @@ -169,6 +190,7 @@ bool DominatorTreeAnalysisPass::runOnFunction(Function* F, AnalysisManager &AM) CurrentDominatorTree->computeDominators(F); CurrentDominatorTree->computeIDoms(F); CurrentDominatorTree->computeDominanceFrontiers(F); + CurrentDominatorTree->computeDominatorTreeChildren(F); return false; } diff --git a/src/Mem2Reg.cpp b/src/Mem2Reg.cpp new file mode 100644 index 0000000..c6e856f --- /dev/null +++ b/src/Mem2Reg.cpp @@ -0,0 +1,388 @@ +#include "Mem2Reg.h" // 包含 Mem2Reg 遍的头文件 +#include "Dom.h" // 包含支配树分析的头文件 +#include "Liveness.h" +#include "IR.h" // 包含 IR 相关的定义 +#include "SysYIROptUtils.h" +#include // 用于断言 +#include // 用于调试输出 + +namespace sysy { + +void *Mem2Reg::ID = (void *)&Mem2Reg::ID; + +void Mem2RegContext::run(Function *func, AnalysisManager *AM) { + if (func->getBasicBlocks().empty()) { + return; + } + + // 清空所有状态,确保每次运行都是新的状态 + promotableAllocas.clear(); + allocaToPhiMap.clear(); + allocaToValueStackMap.clear(); + allocaToStoresMap.clear(); + allocaToDefBlocksMap.clear(); + + // 获取支配树分析结果 + dt = AM->getAnalysisResult(func); + assert(dt && "DominatorTreeAnalysisResult not available for Mem2Reg!"); + + // -------------------------------------------------------------------- + // 阶段1: 识别可提升的 AllocaInst 并收集其 Store 指令 + // -------------------------------------------------------------------- + // 遍历函数入口块?中的所有指令,寻找 AllocaInst + // 必须是要入口块的吗 + for (auto &inst : func->getEntryBlock()->getInstructions_Range()) { + Value *allocainst = inst.get(); + if (auto alloca = dynamic_cast(allocainst)) { + if (isPromotableAlloca(alloca)) { + promotableAllocas.push_back(alloca); + collectStores(alloca); // 收集所有对该 alloca 的 store + } + } + } + + // -------------------------------------------------------------------- + // 阶段2: 插入 Phi 指令 + // -------------------------------------------------------------------- + for (auto alloca : promotableAllocas) { + // 为每个可提升的 alloca 插入 Phi 指令 + insertPhis(alloca, allocaToDefBlocksMap[alloca]); + } + + // -------------------------------------------------------------------- + // 阶段3: 变量重命名 + // -------------------------------------------------------------------- + // 为每个可提升的 alloca 初始化其值栈 + for (auto alloca : promotableAllocas) { + // 初始值通常是 undef 或 null,取决于 IR 类型系统 + UndefinedValue *undefValue = UndefinedValue::get(alloca->getType()->as()->getBaseType()); + allocaToValueStackMap[alloca].push(undefValue); // 压入一个初始的“未定义”值 + } + + // 从入口基本块开始,对支配树进行 DFS 遍历,进行变量重命名 + renameVariables(nullptr, func->getEntryBlock()); // 第一个参数 alloca 在这里不使用,因为是递归入口点 + + // -------------------------------------------------------------------- + // 阶段4: 清理 + // -------------------------------------------------------------------- + cleanup(); +} + +// 判断一个 AllocaInst 是否可以被提升到寄存器 +bool Mem2RegContext::isPromotableAlloca(AllocaInst *alloca) { + // 1. 必须是标量类型(非数组、非结构体)sysy不支持结构体 + if (alloca->getType()->as()->getBaseType()->isArray()) { + return false; + } + + // 2. 其所有用途都必须是 LoadInst 或 StoreInst + // (或 GetElementPtrInst,但 GEP 的结果也必须只被 Load/Store 使用) + for (auto use : alloca->getUses()) { + auto user = use->getUser(); + if (!user) + return false; // 用户无效 + + if (dynamic_cast(user)) { + // OK + } else if (dynamic_cast(user)) { + // OK + } else if (auto gep = dynamic_cast(user)) { + // 如果是 GetElementPtrInst (GEP) + // 需要判断这个 GEP 是否代表了数组元素的访问,而非简单的指针操作 + // LLVM 的 mem2reg 通常不提升用于数组元素访问的 alloca。 + // 启发式判断: + // 如果 GEP 有多个索引(例如 `getelementptr i32, i32* %ptr, i32 0, i32 %idx`), + // 或者第一个索引(对于指针类型)不是常量 0,则很可能是数组访问。 + // 对于 `alloca i32* %a.param` (对应 `int a[]` 参数),其 `allocatedType()` 是 `i32*`。 + // 访问 `a[i]` 会生成类似 `getelementptr i32, i32* %a.param, i32 %i` 的 GEP。 + // 这种 GEP 有两个操作数:基指针和索引。 + + // 检查 GEP 的操作数数量和索引值 + // GEP 的操作数通常是:, , , ... + // 对于一个 `i32*` 类型的 `alloca`,如果它被 GEP 使用,那么 GEP 的第一个索引通常是 `0` + // (表示解引用指针本身),后续索引才是数组元素的索引。 + // 如果 GEP 的操作数数量大于 2 (即 `base_ptr` 和 `index_0` 之外还有其他索引), + // 或者 `index_0` 不是常量 0,则它可能是一个复杂的数组访问。 + // 假设 `gep->getNumOperands()` 和 `gep->getOperand(idx)->getValue()` + // 假设 `ConstantInt` 类用于表示常量整数值 + if (gep->getNumOperands() > 2) { // 如果有超过一个索引(除了基指针的第一个隐式索引) + // std::cerr << "Mem2Reg: Not promotable (GEP with multiple indices): " << alloca->name() << std::endl; + return false; // 复杂 GEP,通常表示数组或结构体字段访问 + } + if (gep->getNumOperands() == 2) { // 只有基指针和一个索引 + Value *firstIndexVal = gep->getOperand(1); // 获取第一个索引值 + if (auto constInt = dynamic_cast(firstIndexVal)) { + if (constInt->getInt() != 0) { + // std::cerr << "Mem2Reg: Not promotable (GEP with non-zero first index): " << alloca->name() << std::endl; + return false; // 索引不是0,表示访问数组的非第一个元素 + } + } else { + // std::cerr << "Mem2Reg: Not promotable (GEP with non-constant first index): " << alloca->name() << + // std::endl; + return false; // 索引不是常量,表示动态数组访问 + } + } + + // 此外,GEP 的结果也必须只被 LoadInst 或 StoreInst 使用 + for (auto gep_use : gep->getUses()) { + auto gep_user = gep_use->getUser(); + if (!gep_user) { + // std::cerr << "Mem2Reg: Not promotable (GEP result null user): " << alloca->name() << std::endl; + return false; + } + if (!dynamic_cast(gep_user) && !dynamic_cast(gep_user)) { + // std::cerr << "Mem2Reg: Not promotable (GEP result used by non-load/store): " << alloca->name() << + // std::endl; + return false; // GEP 结果被其他指令使用,地址逃逸或复杂用途 + } + } + } else { + // 其他类型的用户,如 CallInst (如果地址逃逸),则不能提升 + return false; + } + } + // 3. 不能是 volatile 内存访问 (假设 AllocaInst 有 isVolatile() 方法) + // if (alloca->isVolatile()) return false; // 如果有这样的属性 + + return true; +} + +// 收集所有对给定 AllocaInst 进行存储的 StoreInst +void Mem2RegContext::collectStores(AllocaInst *alloca) { + // 遍历 alloca 的所有用途 + for (auto use : alloca->getUses()) { + auto user = use->getUser(); + if (!user) + continue; + + if (auto storeInst = dynamic_cast(user)) { + allocaToStoresMap[alloca].insert(storeInst); + allocaToDefBlocksMap[alloca].insert(storeInst->getParent()); + } else if (auto gep = dynamic_cast(user)) { + // 如果是 GEP,递归收集其下游的 store + for (auto gep_use : gep->getUses()) { + if (auto gep_store = dynamic_cast(gep_use->getUser())) { + allocaToStoresMap[alloca].insert(gep_store); + allocaToDefBlocksMap[alloca].insert(gep_store->getParent()); + } + } + } + } +} + +// 为给定的 AllocaInst 插入必要的 Phi 指令 +void Mem2RegContext::insertPhis(AllocaInst *alloca, const std::unordered_set &defBlocks) { + std::queue workQueue; + std::unordered_set phiHasBeenInserted; // 记录已插入 Phi 的基本块 + + // 将所有定义块加入工作队列 + for (auto bb : defBlocks) { + workQueue.push(bb); + } + + while (!workQueue.empty()) { + BasicBlock *currentDefBlock = workQueue.front(); + workQueue.pop(); + + // 遍历当前定义块的支配边界 (Dominance Frontier) + const std::set *frontierBlocks = dt->getDominanceFrontier(currentDefBlock); + for (auto frontierBlock : *frontierBlocks) { + // 如果该支配边界块还没有为当前 alloca 插入 Phi 指令 + if (phiHasBeenInserted.find(frontierBlock) == phiHasBeenInserted.end()) { + // 在支配边界块的开头插入一个新的 Phi 指令 + // Phi 指令的类型与 alloca 的类型指向的类型相同 + + builder->setPosition(frontierBlock, frontierBlock->begin()); // 设置插入位置为基本块开头 + PhiInst *phiInst = builder->createPhiInst(alloca->getAllocatedType(), {}, {}, ""); + + allocaToPhiMap[alloca][frontierBlock] = phiInst; // 记录 Phi 指令 + + phiHasBeenInserted.insert(frontierBlock); // 标记已插入 Phi + + // 如果这个支配边界块本身也是一个定义块(即使没有 store,但插入了 Phi), + // 那么它的支配边界也可能需要插入 Phi + // 例如一个xx型的cfg,如果在第一个交叉处插入phi节点,那么第二个交叉处可能也需要插入phi + workQueue.push(frontierBlock); + } + } + } +} + +// 对支配树进行深度优先遍历,重命名变量并替换 load/store 指令 +void Mem2RegContext::renameVariables(AllocaInst *currentAlloca, BasicBlock *currentBB) { + // 维护一个局部栈,用于存储当前基本块中为 Phi 和 Store 创建的 SSA 值,以便在退出时弹出 + std::stack localStackPushed; + + // -------------------------------------------------------------------- + // 处理当前基本块的指令 + // -------------------------------------------------------------------- + for (auto instIter = currentBB->getInstructions().begin(); instIter != currentBB->getInstructions().end();) { + Instruction *inst = instIter->get(); + bool instDeleted = false; + + // 处理 Phi 指令 (如果是当前 alloca 的 Phi) + if (auto phiInst = dynamic_cast(inst)) { + // 检查这个 Phi 是否是为某个可提升的 alloca 插入的 + for (auto alloca : promotableAllocas) { + if (allocaToPhiMap[alloca].count(currentBB) && allocaToPhiMap[alloca][currentBB] == phiInst) { + // 为 Phi 指令的输出创建一个新的 SSA 值,并压入值栈 + allocaToValueStackMap[alloca].push(phiInst); + localStackPushed.push(phiInst); // 记录以便弹出 + break; // 找到对应的 alloca,处理下一个指令 + } + } + } + // 处理 LoadInst + else if (auto loadInst = dynamic_cast(inst)) { + // 检查这个 LoadInst 是否是为某个可提升的 alloca + for (auto alloca : promotableAllocas) { + if (loadInst->getPointer() == alloca) { + // loadInst->getPointer() 返回 AllocaInst* + // 将 LoadInst 的所有用途替换为当前 alloca 值栈顶部的 SSA 值 + assert(!allocaToValueStackMap[alloca].empty() && "Value stack empty for alloca during load replacement!"); + loadInst->replaceAllUsesWith(allocaToValueStackMap[alloca].top()); + // instIter = currentBB->force_delete_inst(loadInst); // 删除 LoadInst + SysYIROptUtils::usedelete(loadInst); // 仅删除 use 关系 + instIter = currentBB->getInstructions().erase(instIter); // 删除 LoadInst + instDeleted = true; + // std::cerr << "Mem2Reg: Replaced load " << loadInst->name() << " with SSA value." << std::endl; + break; + } + } + } + // 处理 StoreInst + else if (auto storeInst = dynamic_cast(inst)) { + // 检查这个 StoreInst 是否是为某个可提升的 alloca + for (auto alloca : promotableAllocas) { + if (storeInst->getPointer() == alloca) { + // 假设 storeInst->getPointer() 返回 AllocaInst* + // 将 StoreInst 存储的值作为新的 SSA 值,压入值栈 + allocaToValueStackMap[alloca].push(storeInst->getValue()); + localStackPushed.push(storeInst->getValue()); // 记录以便弹出 + SysYIROptUtils::usedelete(storeInst); + instIter = currentBB->getInstructions().erase(instIter); // 删除 StoreInst + instDeleted = true; + // std::cerr << "Mem2Reg: Replaced store to " << storeInst->ptr()->name() << " with SSA value." << std::endl; + break; + } + } + } + + if (!instDeleted) { + ++instIter; // 如果指令没有被删除,移动到下一个 + } + } + + // -------------------------------------------------------------------- + // 处理后继基本块的 Phi 指令参数 + // -------------------------------------------------------------------- + for (auto successorBB : currentBB->getSuccessors()) { + if (!successorBB) + continue; + for (auto alloca : promotableAllocas) { + // 如果后继基本块包含为当前 alloca 插入的 Phi 指令 + if (allocaToPhiMap[alloca].count(successorBB)) { + auto phiInst = allocaToPhiMap[alloca][successorBB]; + // 为 Phi 指令添加来自当前基本块的参数 + // 参数值是当前 alloca 值栈顶部的 SSA 值 + assert(!allocaToValueStackMap[alloca].empty() && "Value stack empty for alloca when setting phi operand!"); + phiInst->addIncoming(allocaToValueStackMap[alloca].top(), currentBB); + } + } + } + + // -------------------------------------------------------------------- + // 递归访问支配树的子节点 + // -------------------------------------------------------------------- + const std::set *dominatedBlocks = dt->getDominatorTreeChildren(currentBB); + if(dominatedBlocks){ + for (auto dominatedBB : *dominatedBlocks) { + if (dominatedBB) { + std::cout << "Mem2Reg: Recursively renaming variables in dominated block: " << dominatedBB->getName() << std::endl; + renameVariables(currentAlloca, dominatedBB); + } + } + } + + + // -------------------------------------------------------------------- + // 退出基本块时,弹出在此块中压入值栈的 SSA 值 + // -------------------------------------------------------------------- + while (!localStackPushed.empty()) { + Value *val = localStackPushed.top(); + localStackPushed.pop(); + // 找到是哪个 alloca 对应的栈 + for (auto alloca : promotableAllocas) { + if (!allocaToValueStackMap[alloca].empty() && allocaToValueStackMap[alloca].top() == val) { + allocaToValueStackMap[alloca].pop(); + break; + } + } + } +} + +// 删除所有原始的 AllocaInst、LoadInst 和 StoreInst +void Mem2RegContext::cleanup() { + for (auto alloca : promotableAllocas) { + if (alloca && alloca->getParent()) { + // 删除 alloca 指令本身 + SysYIROptUtils::usedelete(alloca); + alloca->getParent()->removeInst(alloca); // 从基本块中删除 alloca + + // std::cerr << "Mem2Reg: Deleted alloca " << alloca->name() << std::endl; + } + } + // LoadInst 和 StoreInst 已经在 renameVariables 阶段被删除了 +} + +// Mem2Reg 遍的 runOnFunction 方法实现 +bool Mem2Reg::runOnFunction(Function *F, AnalysisManager &AM) { + // 记录初始的指令数量,用于判断优化是否发生了改变 + size_t initial_inst_count = 0; + for (auto &bb : F->getBasicBlocks()) { + initial_inst_count += bb->getInstructions().size(); + } + + Mem2RegContext ctx(builder); + ctx.run(F, &AM); // 运行 Mem2Reg 优化 + + // 运行优化后,再次计算指令数量 + size_t final_inst_count = 0; + for (auto &bb : F->getBasicBlocks()) { + final_inst_count += bb->getInstructions().size(); + } + + // 如果指令数量发生变化(通常是减少,因为 load/store 被删除,phi 被添加),说明 IR 被修改了 + // TODO:不保险,后续修改为更精确的判断 + // 直接在添加和删除指令时维护changed值 + bool changed = (initial_inst_count != final_inst_count); + + // 如果 IR 被修改,则使相关的分析结果失效 + if (changed) { + // Mem2Reg 会显著改变 IR 结构,特别是数据流和控制流(通过 Phi)。 + // 这会使几乎所有数据流分析和部分控制流分析失效。 + // AM.invalidateAnalysis(&DominatorTreeAnalysisPass::ID, F); // 支配树可能间接改变(如果基本块被删除) + // AM.invalidateAnalysis(&LivenessAnalysisPass::ID, F); // 活跃性分析肯定失效 + // AM.invalidateAnalysis(&LoopInfoAnalysisPass::ID, F); // 循环信息可能失效 + // AM.invalidateAnalysis(&SideEffectInfoAnalysisPass::ID); // 副作用分析可能失效(如果 Alloca/Load/Store + // 被替换为寄存器) + // ... 其他数据流分析,如到达定义、可用表达式等,也应失效 + } + return changed; +} + +// 声明Mem2Reg遍的分析依赖和失效信息 +void Mem2Reg::getAnalysisUsage(std::set &analysisDependencies, std::set &analysisInvalidations) const { + // Mem2Reg 强烈依赖于支配树分析来插入 Phi 指令 + analysisDependencies.insert(&DominatorTreeAnalysisPass::ID); // 假设 DominatorTreeAnalysisPass 的 ID + + // Mem2Reg 会删除 Alloca/Load/Store 指令,插入 Phi 指令,这会大幅改变 IR 结构。 + // 因此,它会使许多分析结果失效。 + analysisInvalidations.insert(&DominatorTreeAnalysisPass::ID); // 支配树可能受影响 + analysisInvalidations.insert(&LivenessAnalysisPass::ID); // 活跃性分析肯定失效 + // analysisInvalidations.insert(&LoopInfoAnalysisPass::ID); // 循环信息可能失效 + // analysisInvalidations.insert(&SideEffectInfoAnalysisPass::ID); // 副作用分析可能失效 + // 其他所有依赖于数据流或 IR 结构的分析都可能失效。 +} + +} // namespace sysy \ No newline at end of file diff --git a/src/Pass.cpp b/src/Pass.cpp index bba8e36..d6087fe 100644 --- a/src/Pass.cpp +++ b/src/Pass.cpp @@ -3,6 +3,8 @@ #include "SysYIRCFGOpt.h" #include "SysYIRPrinter.h" #include "DCE.h" +#include "Mem2Reg.h" +#include "Reg2Mem.h" #include "Pass.h" #include #include @@ -44,6 +46,10 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR registerOptimizationPass(builderIR); registerOptimizationPass(builderIR); + registerOptimizationPass(); + registerOptimizationPass(builderIR); + registerOptimizationPass(builderIR); + if (optLevel >= 1) { //经过设计安排优化遍的执行顺序以及执行逻辑 if (DEBUG) std::cout << "Applying -O1 optimizations.\n"; @@ -58,10 +64,38 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR this->addPass(&SysYAddReturnPass::ID); this->run(); + if(DEBUG) { + std::cout << "=== IR After CFGOpt Optimizations ===\n"; + printPasses(); + } + this->clearPasses(); this->addPass(&DCE::ID); this->run(); + if(DEBUG) { + std::cout << "=== IR After DCE Optimizations ===\n"; + printPasses(); + } + + this->clearPasses(); + this->addPass(&Mem2Reg::ID); + this->run(); + + if(DEBUG) { + std::cout << "=== IR After Mem2Reg Optimizations ===\n"; + printPasses(); + } + + this->clearPasses(); + this->addPass(&Reg2Mem::ID); + this->run(); + + if(DEBUG) { + std::cout << "=== IR After Reg2Mem Optimizations ===\n"; + printPasses(); + } + if (DEBUG) std::cout << "--- Custom optimization sequence finished ---\n"; } @@ -152,6 +186,20 @@ bool PassManager::run() { } +void PassManager::printPasses() const { + std::cout << "Registered Passes:\n"; + for (const auto &p : passes) { + std::cout << " - " << p->getName() << " (Granularity: " + << static_cast(p->getGranularity()) + << ", Kind: " << static_cast(p->getPassKind()) << ")\n"; + } + std::cout << "Total Passes: " << passes.size() << "\n"; + if (pmodule) { + SysYPrinter printer(pmodule); + std::cout << "Module IR:\n"; + printer.printIR(); + } +} template void registerAnalysisPass() { PassRegistry::getPassRegistry().registerPass(&AnalysisPassType::ID, diff --git a/src/Reg2Mem.cpp b/src/Reg2Mem.cpp new file mode 100644 index 0000000..b2034c0 --- /dev/null +++ b/src/Reg2Mem.cpp @@ -0,0 +1,289 @@ +#include "Reg2Mem.h" +#include "SysYIROptUtils.h" +#include "SysYIRPrinter.h" + +extern int DEBUG; // 全局调试标志 + +namespace sysy { + +void *Reg2Mem::ID = (void *)&Reg2Mem::ID; + +void Reg2MemContext::run(Function *func) { + if (func->getBasicBlocks().empty()) { + return; + } + + // 清空状态,确保每次运行都是新的 + valueToAllocaMap.clear(); + + // 阶段1: 识别并为 SSA Value 分配 AllocaInst + allocateMemoryForSSAValues(func); + + // 阶段2: 将 Phi 指令转换为 Load/Store 逻辑 (此阶段需要先于通用 Load/Store 插入) + // 这样做是因为 Phi 指令的特殊性,它需要在前驱块的末尾插入 Store + // 如果先处理通用 Load/Store,可能无法正确处理 Phi 的复杂性 + rewritePhis(func); // Phi 指令可能在 rewritePhis 中被删除或标记删除 + + // 阶段3: 将其他 SSA Value 的使用替换为 Load/Store + insertLoadsAndStores(func); + + // 阶段4: 清理(删除不再需要的 Phi 指令) + cleanup(func); +} + +bool Reg2MemContext::isPromotableToMemory(Value *val) { + // 参数和指令结果是 SSA 值 + if(DEBUG){ + // if(val->getName() == ""){ + // assert(false && "Value name should not be empty in Reg2MemContext::isPromotableToMemory"); + // } + // std::cout << "Checking if value is promotable to memory: " << val->getName() << std::endl; + } + // if (dynamic_cast(val) || dynamic_cast(val)) { + // // 如果值已经是指针类型,则通常不为其分配额外的内存,因为它已经是一个地址。 + // // (除非我们想将其值也存储起来,这通常不用于 Reg2Mem) + // // // Reg2Mem 关注的是将非指针值从寄存器语义转换为内存语义。 + // if (val->getType()->isPointer()) { + // return false; + // } + // return true; + // } + // 1. 如果是 Argument,则可以提升到内存 + if (dynamic_cast(val)) { + // 参数类型(i32, i32* 等)都可以为其分配内存 + // 因为它们在 Mem2Reg 逆操作中,被认为是从寄存器分配到内存 + return true; + } + if (dynamic_cast(val)) { + // Phi 指令的结果也是一个 SSA 值,需要将其转换为 Load/Store + return true; + } + return false; +} + +void Reg2MemContext::allocateMemoryForSSAValues(Function *func) { + // AllocaInst 必须在函数的入口基本块中 + BasicBlock *entryBlock = func->getEntryBlock(); + if (!entryBlock) { + return; // 函数可能没有入口块 (例如声明) + } + + // 1. 为函数参数分配内存 + builder->setPosition(entryBlock, entryBlock->begin()); // 确保在入口块的开始位置插入 + for (auto arg : func->getArguments()) { + // 默认情况下,将所有参数是提升到内存 + if (isPromotableToMemory(arg)) { + // 参数的类型就是 AllocaInst 需要分配的类型 + AllocaInst *alloca = builder->createAllocaInst(Type::getPointerType(arg->getType()), {}, arg->getName() + ".reg2mem"); + // 将参数值 store 到 alloca 中 (这是 Mem2Reg 逆转的关键一步) + valueToAllocaMap[arg] = alloca; + + // 确保 alloca 位于入口块的顶部,但在所有参数的 store 指令之前 + // 通常 alloca 都在 entry block 的最开始 + // 这里我们只是创建,并让 builder 决定插入位置 (通常在当前插入点) + // 如果需要严格控制顺序,可能需要手动 insert 到 instruction list + } + } + + // 2. 为指令结果分配内存 + // 遍历所有基本块和指令,找出所有需要分配 Alloca 的指令结果 + for (auto &bb : func->getBasicBlocks()) { + for (auto &inst : bb->getInstructions_Range()) { + // SysYPrinter::printInst(inst.get()); + // 只有有结果的指令才可能需要分配内存 + // (例如 BinaryInst, CallInst, LoadInst, PhiInst 等) + // StoreInst, BranchInst, ReturnInst 等没有结果的指令不需要 + + if (dynamic_cast(inst.get()) || inst.get()->getType()->isVoid()) { + continue; + } + + if (isPromotableToMemory(inst.get())) { + // 为指令的结果分配内存 + // AllocaInst 应该在入口块,而不是当前指令所在块 + // 这里我们只是创建,并稍后调整其位置 + // 通常的做法是在循环结束后统一将 alloca 放到 entryBlock 的顶部 + AllocaInst *alloca = builder->createAllocaInst(Type::getPointerType(inst.get()->getType()), {}, inst.get()->getName() + ".reg2mem"); + valueToAllocaMap[inst.get()] = alloca; + } + } + } + Instruction *firstNonAlloca = nullptr; + for (auto instIter = entryBlock->getInstructions().begin(); instIter != entryBlock->getInstructions().end(); instIter++) { + if (!dynamic_cast(instIter->get())) { + firstNonAlloca = instIter->get(); + break; + } + } + + if (firstNonAlloca) { + builder->setPosition(entryBlock, entryBlock->findInstIterator(firstNonAlloca)); + } else { // 如果 entryBlock 只有 AllocaInst 或为空,则设置到 terminator 前 + builder->setPosition(entryBlock, entryBlock->terminator()); + } + + // 插入所有参数的初始 Store 指令 + for (auto arg : func->getArguments()) { + if (valueToAllocaMap.count(arg)) { // 检查是否为其分配了 alloca + builder->createStoreInst(arg, valueToAllocaMap[arg]); + } + } + + builder->setPosition(entryBlock, entryBlock->terminator()); +} + +void Reg2MemContext::rewritePhis(Function *func) { + std::vector phisToErase; // 收集要删除的 Phi + + // 遍历所有基本块和其中的指令,查找 Phi 指令 + for (auto &bb : func->getBasicBlocks()) { + // auto insts = bb->getInstructions(); // 复制一份,因为要修改 + for (auto instIter = bb->getInstructions().begin(); instIter != bb->getInstructions().end(); instIter++) { + Instruction *inst = instIter->get(); + if (auto phiInst = dynamic_cast(inst)) { + // 检查 Phi 指令是否是需要处理的 SSA 值 + if (valueToAllocaMap.count(phiInst)) { + AllocaInst *alloca = valueToAllocaMap[phiInst]; + + // 1. 为 Phi 指令的每个入边,在前驱块的末尾插入 Store 指令 + // PhiInst 假设有 getIncomingValues() 和 getIncomingBlocks() + for (unsigned i = 0; i < phiInst->getNumIncomingValues(); ++i) { // 假设 PhiInst 是通过操作数来管理入边的 + Value *incomingValue = phiInst->getValue(i); // 获取入值 + BasicBlock *incomingBlock = phiInst->getBlock(i); // 获取对应的入块 + + // 在入块的跳转指令之前插入 StoreInst + // 需要找到 incomingBlock 的终结指令 (Terminator Instruction) + // 并将 StoreInst 插入到它前面 + if (incomingBlock->terminator()->get()->isTerminator()) { + builder->setPosition(incomingBlock, incomingBlock->terminator()); + } else { + // 如果没有终结指令,插入到末尾 + builder->setPosition(incomingBlock, incomingBlock->end()); + } + builder->createStoreInst(incomingValue, alloca); + } + + // 2. 在当前 Phi 所在基本块的开头,插入 Load 指令 + // 将 Load 指令插入到 Phi 指令之后,因为 Phi 指令即将被删除 + builder->setPosition(bb.get(), bb.get()->findInstIterator(phiInst)); + LoadInst *newLoad = builder->createLoadInst(alloca); + + // 3. 将 Phi 指令的所有用途替换为新的 Load 指令 + phiInst->replaceAllUsesWith(newLoad); + + // 标记 Phi 指令待删除 + phisToErase.push_back(phiInst); + } + } + } + } + + // 实际删除 Phi 指令 + for (auto phi : phisToErase) { + if (phi && phi->getParent()) { + SysYIROptUtils::usedelete(phi); // 清理 use-def 链 + phi->getParent()->removeInst(phi); // 从基本块中删除 + } + } +} + +void Reg2MemContext::insertLoadsAndStores(Function *func) { + // 收集所有需要替换的 uses,避免在迭代时修改 use 链表 + std::vector> usesToReplace; + std::vector instsToStore; // 收集需要插入 Store 的指令 + + // 遍历所有基本块和指令 + for (auto &bb : func->getBasicBlocks()) { + for (auto instIter = bb->getInstructions().begin(); instIter != bb->getInstructions().end(); instIter++) { + Instruction *inst = instIter->get(); + + // 如果指令有结果且我们为其分配了 alloca (Phi 已在 rewritePhis 处理) + // 并且其类型不是 void + if (!inst->getType()->isVoid() && valueToAllocaMap.count(inst)) { + // 在指令之后插入 Store 指令 + // StoreInst 应该插入到当前指令之后 + builder->setPosition(bb.get(), bb.get()->findInstIterator(inst)); + builder->createStoreInst(inst, valueToAllocaMap[inst]); + } + + // 处理指令的操作数:如果操作数是一个 SSA 值,且为其分配了 alloca + // (并且这个操作数不是 Phi Inst 的 incoming value,因为 Phi 的 incoming value 已经在 rewritePhis 中处理了) + // 注意:Phi Inst 的操作数是特殊的,它们表示来自不同前驱块的值。 + // 这里的处理主要是针对非 Phi 指令的操作数。 + for (auto use = inst->getUses().begin(); use != inst->getUses().end(); ++use) { + // 如果当前 use 的 Value 是一个 Instruction 或 Argument + Value *operand = use->get()->getValue(); + if (isPromotableToMemory(operand) && valueToAllocaMap.count(operand)) { + // 确保这个 operand 不是一个即将被删除的 Phi 指令 + // (在 rewritePhis 阶段,Phi 已经被处理并可能被标记删除) + // 或者检查 use 的 user 不是 PhiInst + if (dynamic_cast(inst)) { + continue; // Phi 的操作数已在 rewritePhis 中处理 + } + + AllocaInst *alloca = valueToAllocaMap[operand]; + + // 在使用点之前插入 Load 指令 + // LoadInst 应该插入到使用它的指令之前 + builder->setPosition(bb.get(), bb.get()->findInstIterator(inst)); + LoadInst *newLoad = builder->createLoadInst(alloca); + + // 记录要替换的 use + usesToReplace.push_back({use->get(), newLoad}); + } + } + } + } + + // 执行所有替换操作 + for (auto &pair : usesToReplace) { + pair.first->setValue(pair.second); // 替换 use 的 Value + } +} + +void Reg2MemContext::cleanup(Function *func) { + // 此时,所有原始的 Phi 指令应该已经被删除。 + // 如果有其他需要删除的临时指令,可以在这里处理。 + // 通常,Reg2Mem 的清理比 Mem2Reg 简单,因为主要是在插入指令。 + // 这里可以作为一个占位符,以防未来有其他清理需求。 +} + +bool Reg2Mem::runOnFunction(Function *F, AnalysisManager &AM) { + // 记录初始指令数量 + size_t initial_inst_count = 0; + for (auto &bb : F->getBasicBlocks()) { + initial_inst_count += bb->getInstructions().size(); + } + + Reg2MemContext ctx(builder); // 假设 builder 是一个全局或可访问的 IRBuilder 实例 + ctx.run(F); + + // 记录最终指令数量 + size_t final_inst_count = 0; + for (auto &bb : F->getBasicBlocks()) { + final_inst_count += bb->getInstructions().size(); + } + // TODO: 添加更精确的变化检测逻辑,例如在run函数中维护changed状态 + bool changed = (initial_inst_count != final_inst_count); // 粗略判断是否改变 + + if (changed) { + // Reg2Mem 会显著改变 IR 结构,特别是数据流。 + // 它会插入大量的 Load/Store 指令,改变 Value 的来源。 + // 这会使几乎所有数据流分析失效。 + // 例如: + // AM.invalidateAnalysis(&DominatorTreeAnalysisPass::ID, F); // 如果基本块结构改变,可能失效 + // AM.invalidateAnalysis(&LivenessAnalysisPass::ID, F); // 活跃性分析肯定失效 + // AM.invalidateAnalysis(&DCEPass::ID, F); // 可能产生新的死代码 + // ... 其他所有数据流分析 + } + return changed; +} + +void Reg2Mem::getAnalysisUsage(std::set &analysisDependencies, std::set &analysisInvalidations) const { + // Reg2Mem 通常不需要特定的分析作为依赖,因为它主要是一个转换。 + // 但它会使许多分析失效。 + analysisInvalidations.insert(&LivenessAnalysisPass::ID); // 例如 + analysisInvalidations.insert(&DominatorTreeAnalysisPass::ID); +} + +} // namespace sysy \ No newline at end of file diff --git a/src/SysYIRPrinter.cpp b/src/SysYIRPrinter.cpp index 50c56cf..877e4ab 100644 --- a/src/SysYIRPrinter.cpp +++ b/src/SysYIRPrinter.cpp @@ -79,6 +79,15 @@ std::string SysYPrinter::getValueName(Value *value) { return ""; } +std::string SysYPrinter::getBlockName(BasicBlock *block) { + static int blockId = 0; // 用于生成唯一的基本块ID + if (block->getName().empty()) { + return "bb" + std::to_string(blockId++); // 如果没有名字,生成一个唯一的基本块ID + } else { + return block->getName(); + } +} + void SysYPrinter::printType(Type *type) { std::cout << getTypeString(type); } @@ -128,6 +137,10 @@ void SysYPrinter::printGlobalVariable() { } } +void SysYPrinter::printBlock(BasicBlock *block) { + std::cout << getBlockName(block); +} + void SysYPrinter::printFunction(Function *function) { // Function signature std::cout << "define "; @@ -351,7 +364,8 @@ void SysYPrinter::printInst(Instruction *pInst) { // AllocaInst 的类型现在应该是一个 PointerType,指向正确的 ArrayType 或 ScalarType // 例如:alloca i32, align 4 或者 alloca [10 x i32], align 4 - auto allocatedType = dynamic_cast(allocaInst->getType())->getBaseType(); + // auto allocatedType = dynamic_cast(allocaInst->getType())->getBaseType(); + auto allocatedType = allocaInst->getAllocatedType(); printType(allocatedType); // 仍然打印维度信息,如果存在的话 @@ -466,13 +480,13 @@ void SysYPrinter::printInst(Instruction *pInst) { // 如果你的 PhiInst 存储方式是 getIncomingValues() 和 getIncomingBlocks(),请相应调整 // LLVM IR 格式: phi type [value1, block1], [value2, block2] bool firstPair = true; - for (unsigned i = 0; i < phiInst->getNumOperands() / 2; ++i) { // 遍历成对的操作数 + for (unsigned i = 0; i < phiInst->getNumIncomingValues(); ++i) { // 遍历成对的操作数 if (!firstPair) std::cout << ", "; firstPair = false; std::cout << "[ "; - printValue(phiInst->getOperand(i * 2)); // value + printValue(phiInst->getValue(i)); std::cout << ", %"; - printValue(phiInst->getOperand(i * 2 + 1)); // block + printBlock(phiInst->getBlock(i)); std::cout << " ]"; } std::cout << std::endl; diff --git a/src/include/Dom.h b/src/include/Dom.h index f69dcb9..80e0883 100644 --- a/src/include/Dom.h +++ b/src/include/Dom.h @@ -16,17 +16,20 @@ public: const std::set* getDominators(BasicBlock* BB) const; BasicBlock* getImmediateDominator(BasicBlock* BB) const; const std::set* getDominanceFrontier(BasicBlock* BB) const; + const std::set* getDominatorTreeChildren(BasicBlock* BB) const; const std::map>& getDominatorsMap() const { return Dominators; } const std::map& getIDomsMap() const { return IDoms; } const std::map>& getDominanceFrontiersMap() const { return DominanceFrontiers; } void computeDominators(Function* F); void computeIDoms(Function* F); void computeDominanceFrontiers(Function* F); + void computeDominatorTreeChildren(Function* F); private: Function* AssociatedFunction; std::map> Dominators; std::map IDoms; std::map> DominanceFrontiers; + std::map> DominatorTreeChildren; }; diff --git a/src/include/IR.h b/src/include/IR.h index 0fb26e9..6d21f04 100644 --- a/src/include/IR.h +++ b/src/include/IR.h @@ -525,6 +525,10 @@ public: iterator begin() { return instructions.begin(); } iterator end() { return instructions.end(); } iterator terminator() { return std::prev(end()); } + iterator findInstIterator(Instruction *inst) { + return std::find_if(instructions.begin(), instructions.end(), + [inst](const std::unique_ptr &i) { return i.get() == inst; }); + } ///< 查找指定指令的迭代器 bool hasSuccessor(BasicBlock *block) const { return std::find(successors.begin(), successors.end(), block) != successors.end(); } ///< 判断是否有后继块 @@ -1112,7 +1116,10 @@ protected: } public: - + //! 获取分配的类型 + Type* getAllocatedType() const { + return getType()->as()->getBaseType(); + } ///< 获取分配的类型 int getNumDims() const { return getNumOperands(); } auto getDims() const { return getOperands(); } Value* getDim(int index) { return getOperand(index); } diff --git a/src/include/IRBuilder.h b/src/include/IRBuilder.h index d9e92ef..760ef85 100644 --- a/src/include/IRBuilder.h +++ b/src/include/IRBuilder.h @@ -294,7 +294,16 @@ class IRBuilder { return inst; } ///< 创建store指令 PhiInst * createPhiInst(Type *type, const std::vector &vals = {}, const std::vector &blks = {}, const std::string &name = "") { - auto inst = new PhiInst(type, vals, blks, block, name); + std::string newName; + if (name.empty()) { + std::stringstream ss; + ss << tmpIndex; + newName = ss.str(); + tmpIndex++; + } else { + newName = name; + } + auto inst = new PhiInst(type, vals, blks, block, newName); assert(inst); block->getInstructions().emplace(block->begin(), inst); return inst; diff --git a/src/include/Mem2Reg.h b/src/include/Mem2Reg.h new file mode 100644 index 0000000..52a64fd --- /dev/null +++ b/src/include/Mem2Reg.h @@ -0,0 +1,118 @@ +#pragma once + +#include "Pass.h" // 包含Pass的基类定义 +#include "IR.h" // 包含IR相关的定义,如Instruction, Function, BasicBlock, AllocaInst, LoadInst, StoreInst, PhiInst等 +#include "Dom.h" // 假设支配树分析的头文件,提供 DominatorTreeAnalysisResult +#include +#include +#include +#include +#include // 用于变量重命名阶段的SSA值栈 + +namespace sysy { + +// 前向声明分析结果类,确保在需要时可以引用 +class DominatorTree; + +// Mem2RegContext 类,封装 mem2reg 遍的核心逻辑和状态 +// 这样可以避免静态变量在多线程或多次运行时的冲突,并保持代码的模块化 +class Mem2RegContext { +public: + + Mem2RegContext(IRBuilder *builder) : builder(builder) {} + // 运行 mem2reg 优化的主要方法 + // func: 当前要优化的函数 + // tp: 分析管理器,用于获取支配树等分析结果 + void run(Function* func, AnalysisManager* tp); + +private: + IRBuilder *builder; // IR 构建器,用于插入指令 + // 存储所有需要被提升的 AllocaInst + std::vector promotableAllocas; + + // 存储每个 AllocaInst 对应的 Phi 指令列表 + // 键是 AllocaInst,值是该 AllocaInst 在各个基本块中插入的 Phi 指令的列表 + // (实际上,一个 AllocaInst 在一个基本块中只会有一个 Phi) + std::unordered_map> allocaToPhiMap; + + // 存储每个 AllocaInst 对应的当前活跃 SSA 值栈 + // 用于在变量重命名阶段追踪每个 AllocaInst 在不同控制流路径上的最新值 + std::unordered_map> allocaToValueStackMap; + + // 辅助映射,存储每个 AllocaInst 的所有 store 指令 + std::unordered_map> allocaToStoresMap; + + // 辅助映射,存储每个 AllocaInst 对应的定义基本块(包含 store 指令的块) + std::unordered_map> allocaToDefBlocksMap; + + // 支配树分析结果,用于 Phi 插入和变量重命名 + DominatorTree* dt; + + // -------------------------------------------------------------------- + // 阶段1: 识别可提升的 AllocaInst + // -------------------------------------------------------------------- + + // 判断一个 AllocaInst 是否可以被提升到寄存器 + // alloca: 要检查的 AllocaInst + // 返回值: 如果可以提升,则为 true,否则为 false + bool isPromotableAlloca(AllocaInst* alloca); + + // 收集所有对给定 AllocaInst 进行存储的 StoreInst + // alloca: 目标 AllocaInst + void collectStores(AllocaInst* alloca); + + // -------------------------------------------------------------------- + // 阶段2: 插入 Phi 指令 (Phi Insertion) + // -------------------------------------------------------------------- + + // 为给定的 AllocaInst 插入必要的 Phi 指令 + // alloca: 目标 AllocaInst + // defBlocks: 包含对该 AllocaInst 进行 store 操作的基本块集合 + void insertPhis(AllocaInst* alloca, const std::unordered_set& defBlocks); + + // -------------------------------------------------------------------- + // 阶段3: 变量重命名 (Variable Renaming) + // -------------------------------------------------------------------- + + // 对支配树进行深度优先遍历,重命名变量并替换 load/store 指令 + // alloca: 当前正在处理的 AllocaInst + // currentBB: 当前正在遍历的基本块 + // dt: 支配树分析结果 + // valueStack: 存储当前 AllocaInst 在当前路径上可见的 SSA 值栈 + void renameVariables(AllocaInst* alloca, BasicBlock* currentBB); + + // -------------------------------------------------------------------- + // 阶段4: 清理 + // -------------------------------------------------------------------- + + // 删除所有原始的 AllocaInst、LoadInst 和 StoreInst + void cleanup(); +}; + +// Mem2Reg 优化遍类,继承自 OptimizationPass +// 粒度为 Function,表示它在每个函数上独立运行 +class Mem2Reg : public OptimizationPass { +private: + IRBuilder *builder; + +public: + // 构造函数 + Mem2Reg(IRBuilder *builder) : OptimizationPass("Mem2Reg", Granularity::Function), builder(builder) {} + + // 静态成员,作为该遍的唯一ID + static void *ID; + + // 运行在函数上的优化逻辑 + // F: 当前要优化的函数 + // AM: 分析管理器,用于获取支配树等分析结果,或使分析结果失效 + // 返回值: 如果IR被修改,则为true,否则为false + bool runOnFunction(Function *F, AnalysisManager& AM) override; + + // 声明该遍的分析依赖和失效信息 + // analysisDependencies: 该遍运行前需要哪些分析结果 + // analysisInvalidations: 该遍运行后会使哪些分析结果失效 + void getAnalysisUsage(std::set &analysisDependencies, std::set &analysisInvalidations) const override; + void *getPassID() const override { return &ID; } +}; + +} // namespace sysy \ No newline at end of file diff --git a/src/include/Pass.h b/src/include/Pass.h index d387e9e..887ad3f 100644 --- a/src/include/Pass.h +++ b/src/include/Pass.h @@ -11,6 +11,8 @@ #include "IR.h" #include "IRBuilder.h" +extern int DEBUG; // 全局调试标志 + namespace sysy { //前向声明 @@ -149,6 +151,9 @@ public: } AnalysisPass *analysisPass = static_cast(basePass.get()); + if(DEBUG){ + std::cout << "Running Analysis Pass: " << analysisPass->getName() << "\n"; + } // 根据分析遍的粒度处理 switch (analysisPass->getGranularity()) { case Pass::Granularity::Module: { @@ -292,6 +297,9 @@ public: AnalysisManager &getAnalysisManager() { return analysisManager; } void clearPasses(); + + // 输出pass列表并打印IR信息供观察优化遍效果 + void printPasses() const; }; // ====================================================================== diff --git a/src/include/Reg2Mem.h b/src/include/Reg2Mem.h new file mode 100644 index 0000000..84f5535 --- /dev/null +++ b/src/include/Reg2Mem.h @@ -0,0 +1,59 @@ +#pragma once + +#include "IR.h" +#include "IRBuilder.h" // 你的 IR Builder +#include "Liveness.h" +#include "Dom.h" +#include "Pass.h" // 你的 Pass 框架基类 +#include // 调试用 +#include // 用于 Value 到 AllocaInst 的映射 +#include // 可能用于其他辅助集合 +#include +#include + +namespace sysy { + +class Reg2MemContext { +public: + Reg2MemContext(IRBuilder *b) : builder(b) {} + + // 运行 Reg2Mem 优化 + void run(Function *func); + +private: + IRBuilder *builder; // IR 构建器 + + // 存储 SSA Value 到对应的 AllocaInst 的映射 + // 只有那些需要被"溢出"到内存的 SSA 值才会被记录在这里 + std::map valueToAllocaMap; + + // 辅助函数: + // 1. 识别并为 SSA Value 分配 AllocaInst + void allocateMemoryForSSAValues(Function *func); + + // 2. 将 SSA 值的使用替换为 Load/Store + void insertLoadsAndStores(Function *func); + + // 3. 处理 Phi 指令,将其转换为 Load/Store + void rewritePhis(Function *func); + + // 4. 清理 (例如,可能删除不再需要的 Phi 指令) + void cleanup(Function *func); + + // 判断一个 Value 是否是 AllocaInst 可以为其分配内存的目标 + // 通常指非指针类型的Instruction结果和Argument + bool isPromotableToMemory(Value *val); +}; + +class Reg2Mem : public OptimizationPass { +private: + IRBuilder *builder; ///< IR构建器,用于插入指令 +public: + static void *ID; ///< Pass的唯一标识符 + Reg2Mem(IRBuilder* builder) : OptimizationPass("Reg2Mem", Pass::Granularity::Function), builder(builder) {} + bool runOnFunction(Function *F, AnalysisManager &AM) override; + void getAnalysisUsage(std::set &analysisDependencies, std::set &analysisInvalidations) const override; + void *getPassID() const override { return &ID; } ///< 获取 Pass ID +}; + +} // namespace sysy \ No newline at end of file diff --git a/src/include/SysYIRPrinter.h b/src/include/SysYIRPrinter.h index bfd78bd..deb22c0 100644 --- a/src/include/SysYIRPrinter.h +++ b/src/include/SysYIRPrinter.h @@ -22,6 +22,8 @@ public: static void printInst(Instruction *pInst); static void printType(Type *type); static void printValue(Value *value); + static void printBlock(BasicBlock *block); + static std::string getBlockName(BasicBlock *block); static std::string getOperandName(Value *operand); static std::string getTypeString(Type *type); static std::string getValueName(Value *value);