Compare commits
51 Commits
SCCP
...
backend-re
| Author | SHA1 | Date | |
|---|---|---|---|
| 792dc9c1f6 | |||
| 429e477776 | |||
| 2e8b564d8f | |||
| af318b6c0e | |||
| 9bea4d5343 | |||
| 540742be0c | |||
| 8ae7478ef3 | |||
| a616ec085e | |||
| 828515bc2f | |||
| a231267fc5 | |||
| 4b181261ce | |||
| 3df9b3bb06 | |||
| e57ac7709d | |||
| 2643eb1edd | |||
| 5bfa6d72a2 | |||
| 14fb3dbe48 | |||
| 04c5c6b44d | |||
| e2c97fd171 | |||
| 12f63a0bf5 | |||
| d50912ee4c | |||
| 259d71cde5 | |||
| 1e6f6ed711 | |||
| 96c6b0ab6e | |||
| 10a533b0cc | |||
| 5f8bf15d4d | |||
| 18dc8dbfee | |||
| 9c56bc1310 | |||
| c68b031c01 | |||
| 2556ab7315 | |||
| 4b9d067c12 | |||
| f4d231b989 | |||
| 56b376914b | |||
| 2157cf6aa6 | |||
| 0e492cd6d7 | |||
| 87d38be255 | |||
| 2040670f8c | |||
| 3df3d7a097 | |||
| b20bda2f52 | |||
| f61b51b2fa | |||
| 20a5c5cbfb | |||
| cf88ca77cb | |||
| fd6fe22020 | |||
| e8fe710c26 | |||
| 9c87cb397b | |||
| c45938d41d | |||
| 3baccbc03a | |||
| 24d8e730f1 | |||
| bbfbf96b5e | |||
| a72fc541fb | |||
| 550f4017be | |||
| f7e811b756 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -23,7 +23,6 @@
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
@ -53,4 +52,5 @@ __init__.py
|
||||
|
||||
.DS_*
|
||||
|
||||
antlr/
|
||||
antlr/
|
||||
.clang-format
|
||||
|
||||
6
Pass_ID_List.md
Normal file
6
Pass_ID_List.md
Normal file
@ -0,0 +1,6 @@
|
||||
# 记录中端遍的开发进度
|
||||
|
||||
| 名称 | 优化级别 | 开发进度 |
|
||||
| ------------ | ------------ | ---------- |
|
||||
| CFG优化 | 函数级 | 已完成 |
|
||||
| DCE | 函数级 | 待测试 |
|
||||
27
README.md
27
README.md
@ -46,4 +46,29 @@ mysysy/ $ bash setup.sh
|
||||
|
||||
- store load指令由于gep指令的引入, 维度信息的记录是非必须的, 考虑删除
|
||||
|
||||
- use def关系经过mem2reg和phi函数明确转换为ssa形式, 以及函数参数通过value数组明确定义, 使得基本块的args参数信息记录非必须, 考虑删除
|
||||
- use def关系经过mem2reg和phi函数明确转换为ssa形式, 以及函数参数通过value数组明确定义, 使得基本块的args参数信息记录非必须, 考虑删除
|
||||
|
||||
---
|
||||
|
||||
## 编译器后端 TODO 列表
|
||||
|
||||
### 1. `CALL` 指令处理不完善 (高优先级)
|
||||
|
||||
* **问题描述**:当前 `RISCv64RegAlloc::getInstrUseDef()` 方法中,对 `CALL` 指令的 `use`/`def` 分析不完整。它正确识别了返回值为 `def` 和参数为 `use`,但**没有将所有调用者保存 (Caller-saved) 的物理寄存器(`T0-T6`, `A0-A7`)标记为隐式 `def` (即 `CALL` 会破坏它们)**。
|
||||
* **潜在后果**:
|
||||
* **活跃性分析错误**:寄存器分配器可能会错误地认为某个跨函数调用活跃的虚拟寄存器是安全的,并将其分配给 `T` 或 `A` 寄存器。
|
||||
* **值被破坏**:在 `CALL` 指令执行后,这些 `T` 或 `A` 寄存器中本应保留的值会被被调用的函数破坏,导致程序行为异常。
|
||||
* **参考文件**:`RISCv64RegAlloc.cpp` (在 `getInstrUseDef` 函数中对 `RVOpcodes::CALL` 的处理)。
|
||||
|
||||
### 2. `T6` 寄存器作为溢出寄存器的问题 (中等优先级)
|
||||
|
||||
* **问题描述**:`RISCv64RegAlloc::rewriteFunction()` 方法中,所有未能成功着色并被溢出 (spilled) 的虚拟寄存器,都被统一替换为物理寄存器 `T6`。
|
||||
* **问题 2.1:`T6` 是调用者保存寄存器,但未被调用者保存**:
|
||||
* `T6` 属于调用者保存寄存器 (`T0-T6` 范围)。
|
||||
* 标准 ABI 要求,如果一个调用者保存寄存器在函数调用前后都活跃(例如,它存储了一个被溢出的变量,而这个变量在 `CALL` 指令之后还需要用到),那么**调用者**有责任在 `CALL` 前保存该寄存器,并在 `CALL` 后恢复它。
|
||||
* 目前的 `rewriteFunction` 没有为 `T6` 插入这种保存/恢复逻辑。
|
||||
* **潜在后果**:如果一个溢出变量被分配到 `T6`,并且它跨函数调用活跃,那么 `putint` 或其他任何被调用的函数可能会随意使用 `T6`,从而破坏该溢出变量的值。
|
||||
* **问题 2.2:所有溢出变量共用一个 `T6`**:
|
||||
* 将所有溢出变量映射到同一个物理寄存器 `T6` 是一种简化的溢出策略。
|
||||
* **潜在后果**:这意味着,每当需要使用一个溢出变量时,其值必须从栈中加载到 `T6`;每当一个溢出变量被定义时,其值必须从 `T6` 存储回栈。这会引入大量的 `load`/`store` 指令,并导致 `T6` 本身成为一个高度冲突的寄存器,严重降低代码效率。
|
||||
* **参考文件**:`RISCv64RegAlloc.cpp` (在 `rewriteFunction` 函数中处理 `spilled_vregs` 的部分)。
|
||||
|
||||
BIN
lib/libsysy_arm.a
Normal file
BIN
lib/libsysy_arm.a
Normal file
Binary file not shown.
BIN
lib/libsysy_riscv.a
Normal file
BIN
lib/libsysy_riscv.a
Normal file
Binary file not shown.
@ -57,11 +57,22 @@ bool AddressCalculationExpansion::run() {
|
||||
}
|
||||
}
|
||||
} else if (GlobalValue* globalValue = dynamic_cast<GlobalValue*>(basePointer)) {
|
||||
std::cerr << "Warning: GlobalValue dimension handling needs explicit implementation for GEP expansion. Skipping GEP for: ";
|
||||
SysYPrinter::printValue(globalValue);
|
||||
std::cerr << "\n";
|
||||
++it;
|
||||
continue;
|
||||
// 遍历 GlobalValue 的所有维度操作数
|
||||
for (const auto& use_ptr : globalValue->getDims()) {
|
||||
Value* dimValue = use_ptr->getValue();
|
||||
// 将维度值转换为常量整数
|
||||
if (ConstantInteger* constVal = dynamic_cast<ConstantInteger*>(dimValue)) {
|
||||
dims.push_back(constVal->getInt());
|
||||
} else {
|
||||
// 如果维度不是常量整数,则无法处理。
|
||||
// 根据 IR.h 中 GlobalValue 的构造函数,这种情况不应发生,但作为安全检查是好的。
|
||||
std::cerr << "Warning: GlobalValue dimension is not a constant integer. Skipping GEP expansion for: ";
|
||||
SysYPrinter::printValue(globalValue);
|
||||
std::cerr << "\n";
|
||||
dims.clear(); // 清空已收集的部分维度信息
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Warning: Base pointer is not AllocaInst/GlobalValue or its array dimensions cannot be determined for GEP expansion. Skipping GEP for: ";
|
||||
SysYPrinter::printValue(basePointer);
|
||||
|
||||
@ -22,8 +22,10 @@ add_executable(sysyc
|
||||
SysYIRGenerator.cpp
|
||||
SysYIRPrinter.cpp
|
||||
SysYIRCFGOpt.cpp
|
||||
# SysYIRAnalyser.cpp
|
||||
# DeadCodeElimination.cpp
|
||||
Pass.cpp
|
||||
Dom.cpp
|
||||
Liveness.cpp
|
||||
DCE.cpp
|
||||
AddressCalculationExpansion.cpp
|
||||
# Mem2Reg.cpp
|
||||
# Reg2Mem.cpp
|
||||
@ -32,6 +34,7 @@ add_executable(sysyc
|
||||
RISCv64RegAlloc.cpp
|
||||
RISCv64AsmPrinter.cpp
|
||||
RISCv64Passes.cpp
|
||||
RISCv64LLIR.cpp
|
||||
)
|
||||
|
||||
# 设置 include 路径,包含 ANTLR 运行时库和项目头文件
|
||||
|
||||
140
src/DCE.cpp
Normal file
140
src/DCE.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#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;
|
||||
|
||||
// ======================================================================
|
||||
// DCEContext 类的实现
|
||||
// 封装了 DCE 遍的核心逻辑和状态,确保每次函数优化运行时状态独立
|
||||
// ======================================================================
|
||||
|
||||
// DCEContext 的 run 方法实现
|
||||
void DCEContext::run(Function *func, AnalysisManager *AM, bool &changed) {
|
||||
// 清空活跃指令集合,确保每次运行都是新的状态
|
||||
alive_insts.clear();
|
||||
|
||||
// 第一次遍历:扫描所有指令,识别“天然活跃”的指令并将其及其依赖标记为活跃
|
||||
// 使用 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); // 删除后返回下一个迭代器
|
||||
} else {
|
||||
++instIter; // 指令活跃,移动到下一个
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断指令是否是“天然活跃”的实现
|
||||
// 只有具有副作用的指令(如存储、函数调用、原子操作)
|
||||
// 和控制流指令(如分支、返回)是天然活跃的。
|
||||
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();
|
||||
return isBranchOrReturn || isCall || isStoreOrMemset;
|
||||
}
|
||||
|
||||
// 递归地将活跃指令及其依赖加入到 alive_insts 集合中
|
||||
void DCEContext::addAlive(Instruction *inst) {
|
||||
// 如果指令已经存在于活跃集合中,则无需重复处理
|
||||
if (alive_insts.count(inst) > 0) {
|
||||
return;
|
||||
}
|
||||
// 将当前指令标记为活跃
|
||||
alive_insts.insert(inst);
|
||||
// 遍历当前指令的所有操作数
|
||||
// 保留用户提供的 getOperands() 和 getValue()
|
||||
for (auto operand : inst->getOperands()) {
|
||||
// 如果操作数是一个指令(即它是一个值的定义),
|
||||
// 并且它还没有被标记为活跃
|
||||
if (auto opInst = dynamic_cast<Instruction *>(operand->getValue())) {
|
||||
addAlive(opInst); // 递归地将操作数指令标记为活跃
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// 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(如ADCE),CFG也会改变。
|
||||
// 这里我们假设它主要影响数据流分析,并且可能间接影响CFG相关分析。
|
||||
// 如果有SideEffectInfo,它也可能被修改,但通常SideEffectInfo是静态的,不因DCE而变。
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,259 +0,0 @@
|
||||
#include "DeadCodeElimination.h"
|
||||
#include <iostream>
|
||||
|
||||
extern int DEBUG;
|
||||
namespace sysy {
|
||||
|
||||
void DeadCodeElimination::runDCEPipeline() {
|
||||
const auto& functions = pModule->getFunctions();
|
||||
for (const auto& function : functions) {
|
||||
const auto& func = function.second;
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
eliminateDeadStores(func.get(), changed);
|
||||
eliminateDeadLoads(func.get(), changed);
|
||||
eliminateDeadAllocas(func.get(), changed);
|
||||
eliminateDeadRedundantLoadStore(func.get(), changed);
|
||||
eliminateDeadGlobals(changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消除无用存储 消除条件:
|
||||
// 存储的目标指针(pointer)不是全局变量(!isGlobal(pointer))。
|
||||
// 存储的目标指针不是数组参数(!isArr(pointer) 或不在函数参数列表里)。
|
||||
// 该指针的所有使用者(uses)仅限 alloca 或 store(即没有 load 或其他指令使用它)。
|
||||
void DeadCodeElimination::eliminateDeadStores(Function* func, bool& changed) {
|
||||
for (const auto& block : func->getBasicBlocks()) {
|
||||
auto& instrs = block->getInstructions();
|
||||
for (auto iter = instrs.begin(); iter != instrs.end();) {
|
||||
auto inst = iter->get();
|
||||
if (!inst->isStore()) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto storeInst = dynamic_cast<StoreInst*>(inst);
|
||||
auto pointer = storeInst->getPointer();
|
||||
// 如果是全局变量或者是函数的数组参数
|
||||
if (SysYIROptUtils::isGlobal(pointer) || (SysYIROptUtils::isArr(pointer) &&
|
||||
std::find(func->getEntryBlock()->getArguments().begin(),
|
||||
func->getEntryBlock()->getArguments().end(),
|
||||
pointer) != func->getEntryBlock()->getArguments().end())) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool changetag = true;
|
||||
for (auto& use : pointer->getUses()) {
|
||||
// 依次判断store的指针是否被其他指令使用
|
||||
auto user = use->getUser();
|
||||
auto userInst = dynamic_cast<Instruction*>(user);
|
||||
// 如果使用store的指针的指令不是Alloca或Store,则不删除
|
||||
if (userInst != nullptr && !userInst->isAlloca() && !userInst->isStore()) {
|
||||
changetag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changetag) {
|
||||
changed = true;
|
||||
if(DEBUG){
|
||||
std::cout << "=== Dead Store Found ===\n";
|
||||
SysYPrinter::printInst(storeInst);
|
||||
}
|
||||
SysYIROptUtils::usedelete(storeInst);
|
||||
iter = instrs.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 消除无用加载 消除条件:
|
||||
// 该指令的结果未被使用(inst->getUses().empty())。
|
||||
void DeadCodeElimination::eliminateDeadLoads(Function* func, bool& changed) {
|
||||
for (const auto& block : func->getBasicBlocks()) {
|
||||
auto& instrs = block->getInstructions();
|
||||
for (auto iter = instrs.begin(); iter != instrs.end();) {
|
||||
auto inst = iter->get();
|
||||
if (inst->isBinary() || inst->isUnary() || inst->isLoad()) {
|
||||
if (inst->getUses().empty()) {
|
||||
changed = true;
|
||||
if(DEBUG){
|
||||
std::cout << "=== Dead Load Binary Unary Found ===\n";
|
||||
SysYPrinter::printInst(inst);
|
||||
}
|
||||
SysYIROptUtils::usedelete(inst);
|
||||
iter = instrs.erase(iter);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消除无用加载 消除条件:
|
||||
// 该 alloca 未被任何指令使用(allocaInst->getUses().empty())。
|
||||
// 该 alloca 不是函数的参数(不在 entry 块的参数列表里)。
|
||||
void DeadCodeElimination::eliminateDeadAllocas(Function* func, bool& changed) {
|
||||
for (const auto& block : func->getBasicBlocks()) {
|
||||
auto& instrs = block->getInstructions();
|
||||
for (auto iter = instrs.begin(); iter != instrs.end();) {
|
||||
auto inst = iter->get();
|
||||
if (inst->isAlloca()) {
|
||||
auto allocaInst = dynamic_cast<AllocaInst*>(inst);
|
||||
if (allocaInst->getUses().empty() &&
|
||||
std::find(func->getEntryBlock()->getArguments().begin(),
|
||||
func->getEntryBlock()->getArguments().end(),
|
||||
allocaInst) == func->getEntryBlock()->getArguments().end()) {
|
||||
changed = true;
|
||||
if(DEBUG){
|
||||
std::cout << "=== Dead Alloca Found ===\n";
|
||||
SysYPrinter::printInst(inst);
|
||||
}
|
||||
SysYIROptUtils::usedelete(inst);
|
||||
iter = instrs.erase(iter);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeadCodeElimination::eliminateDeadIndirectiveAllocas(Function* func, bool& changed) {
|
||||
// 删除mem2reg时引入的且现在已经没有value使用了的隐式alloca
|
||||
FunctionAnalysisInfo* funcInfo = pCFA->getFunctionAnalysisInfo(func);
|
||||
for (auto it = funcInfo->getIndirectAllocas().begin(); it != funcInfo->getIndirectAllocas().end();) {
|
||||
auto &allocaInst = *it;
|
||||
if (allocaInst->getUses().empty()) {
|
||||
changed = true;
|
||||
if(DEBUG){
|
||||
std::cout << "=== Dead Indirect Alloca Found ===\n";
|
||||
SysYPrinter::printInst(allocaInst.get());
|
||||
}
|
||||
it = funcInfo->getIndirectAllocas().erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 该全局变量未被任何指令使用(global->getUses().empty())。
|
||||
void DeadCodeElimination::eliminateDeadGlobals(bool& changed) {
|
||||
auto& globals = pModule->getGlobals();
|
||||
for (auto it = globals.begin(); it != globals.end();) {
|
||||
auto& global = *it;
|
||||
if (global->getUses().empty()) {
|
||||
changed = true;
|
||||
if(DEBUG){
|
||||
std::cout << "=== Dead Global Found ===\n";
|
||||
SysYPrinter::printValue(global.get());
|
||||
}
|
||||
it = globals.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消除冗余加载和存储 消除条件:
|
||||
// phi 指令的目标指针仅被该 phi 使用(无其他 store/load 使用)。
|
||||
// memset 指令的目标指针未被使用(pointer->getUses().empty())
|
||||
// store -> load -> store 模式
|
||||
void DeadCodeElimination::eliminateDeadRedundantLoadStore(Function* func, bool& changed) {
|
||||
for (const auto& block : func->getBasicBlocks()) {
|
||||
auto& instrs = block->getInstructions();
|
||||
for (auto iter = instrs.begin(); iter != instrs.end();) {
|
||||
auto inst = iter->get();
|
||||
if (inst->isPhi()) {
|
||||
auto phiInst = dynamic_cast<PhiInst*>(inst);
|
||||
auto pointer = phiInst->getPointer();
|
||||
bool tag = true;
|
||||
for (const auto& use : pointer->getUses()) {
|
||||
auto user = use->getUser();
|
||||
if (user != inst) {
|
||||
tag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// 如果 pointer 仅被该 phi 使用,可以删除 ph
|
||||
if (tag) {
|
||||
changed = true;
|
||||
SysYIROptUtils::usedelete(inst);
|
||||
iter = instrs.erase(iter);
|
||||
continue;
|
||||
}
|
||||
// 数组指令还不完善,不保证memset优化效果
|
||||
} else if (inst->isMemset()) {
|
||||
auto memsetInst = dynamic_cast<MemsetInst*>(inst);
|
||||
auto pointer = memsetInst->getPointer();
|
||||
if (pointer->getUses().empty()) {
|
||||
changed = true;
|
||||
SysYIROptUtils::usedelete(inst);
|
||||
iter = instrs.erase(iter);
|
||||
continue;
|
||||
}
|
||||
}else if(inst->isLoad()) {
|
||||
if (iter != instrs.begin()) {
|
||||
auto loadInst = dynamic_cast<LoadInst*>(inst);
|
||||
auto loadPointer = loadInst->getPointer();
|
||||
// TODO:store -> load -> store 模式
|
||||
auto prevIter = std::prev(iter);
|
||||
auto prevInst = prevIter->get();
|
||||
if (prevInst->isStore()) {
|
||||
auto prevStore = dynamic_cast<StoreInst*>(prevInst);
|
||||
auto prevStorePointer = prevStore->getPointer();
|
||||
auto prevStoreValue = prevStore->getOperand(0);
|
||||
// 确保前一个 store 不是数组操作
|
||||
if (prevStore->getIndices().empty()) {
|
||||
// 检查后一条指令是否是 store 同一个值
|
||||
auto nextIter = std::next(iter);
|
||||
if (nextIter != instrs.end()) {
|
||||
auto nextInst = nextIter->get();
|
||||
if (nextInst->isStore()) {
|
||||
auto nextStore = dynamic_cast<StoreInst*>(nextInst);
|
||||
auto nextStorePointer = nextStore->getPointer();
|
||||
auto nextStoreValue = nextStore->getOperand(0);
|
||||
// 确保后一个 store 不是数组操作
|
||||
if (nextStore->getIndices().empty()) {
|
||||
// 判断优化条件:
|
||||
// 1. prevStore 的指针操作数 == load 的指针操作数
|
||||
// 2. nextStore 的值操作数 == load 指令本身
|
||||
if (prevStorePointer == loadPointer &&
|
||||
nextStoreValue == loadInst) {
|
||||
// 可以优化直接把prevStorePointer的值存到nextStorePointer
|
||||
changed = true;
|
||||
nextStore->setOperand(0, prevStoreValue);
|
||||
if(DEBUG){
|
||||
std::cout << "=== Dead Store Load Store Found(now only del Load) ===\n";
|
||||
SysYPrinter::printInst(prevStore);
|
||||
SysYPrinter::printInst(loadInst);
|
||||
SysYPrinter::printInst(nextStore);
|
||||
}
|
||||
SysYIROptUtils::usedelete(loadInst);
|
||||
iter = instrs.erase(iter);
|
||||
// 删除 prevStore 这里是不是可以留给删除无用store处理?
|
||||
// if (prevStore->getUses().empty()) {
|
||||
// usedelete(prevStore);
|
||||
// instrs.erase(prevIter); // 删除 prevStore
|
||||
// }
|
||||
continue; // 跳过 ++iter,因为已经移动迭代器
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace sysy
|
||||
180
src/Dom.cpp
Normal file
180
src/Dom.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
#include "Dom.h"
|
||||
#include <limits> // for std::numeric_limits
|
||||
#include <queue>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 初始化 支配树静态 ID
|
||||
void *DominatorTreeAnalysisPass::ID = (void *)&DominatorTreeAnalysisPass::ID;
|
||||
// ==============================================================
|
||||
// DominatorTree 结果类的实现
|
||||
// ==============================================================
|
||||
|
||||
DominatorTree::DominatorTree(Function *F) : AssociatedFunction(F) {
|
||||
// 构造时可以不计算,在分析遍运行里计算并填充
|
||||
}
|
||||
|
||||
const std::set<BasicBlock *> *DominatorTree::getDominators(BasicBlock *BB) const {
|
||||
auto it = Dominators.find(BB);
|
||||
if (it != Dominators.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BasicBlock *DominatorTree::getImmediateDominator(BasicBlock *BB) const {
|
||||
auto it = IDoms.find(BB);
|
||||
if (it != IDoms.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::set<BasicBlock *> *DominatorTree::getDominanceFrontier(BasicBlock *BB) const {
|
||||
auto it = DominanceFrontiers.find(BB);
|
||||
if (it != DominanceFrontiers.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DominatorTree::computeDominators(Function *F) {
|
||||
// 经典的迭代算法计算支配者集合
|
||||
// TODO: 可以替换为更高效的算法,如 Lengauer-Tarjan 算法
|
||||
BasicBlock *entryBlock = F->getEntryBlock();
|
||||
|
||||
for (const auto &bb_ptr : F->getBasicBlocks()) {
|
||||
BasicBlock *bb = bb_ptr.get();
|
||||
if (bb == entryBlock) {
|
||||
Dominators[bb].insert(bb);
|
||||
} else {
|
||||
for (const auto &all_bb_ptr : F->getBasicBlocks()) {
|
||||
Dominators[bb].insert(all_bb_ptr.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (const auto &bb_ptr : F->getBasicBlocks()) {
|
||||
BasicBlock *bb = bb_ptr.get();
|
||||
if (bb == entryBlock)
|
||||
continue;
|
||||
|
||||
std::set<BasicBlock *> newDom;
|
||||
bool firstPred = true;
|
||||
for (BasicBlock *pred : bb->getPredecessors()) {
|
||||
if (Dominators.count(pred)) {
|
||||
if (firstPred) {
|
||||
newDom = Dominators[pred];
|
||||
firstPred = false;
|
||||
} else {
|
||||
std::set<BasicBlock *> intersection;
|
||||
std::set_intersection(newDom.begin(), newDom.end(), Dominators[pred].begin(), Dominators[pred].end(),
|
||||
std::inserter(intersection, intersection.begin()));
|
||||
newDom = intersection;
|
||||
}
|
||||
}
|
||||
}
|
||||
newDom.insert(bb);
|
||||
|
||||
if (newDom != Dominators[bb]) {
|
||||
Dominators[bb] = newDom;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DominatorTree::computeIDoms(Function *F) {
|
||||
// 采用与之前类似的简化实现。TODO:Lengauer-Tarjan等算法。
|
||||
BasicBlock *entryBlock = F->getEntryBlock();
|
||||
IDoms[entryBlock] = nullptr;
|
||||
|
||||
for (const auto &bb_ptr : F->getBasicBlocks()) {
|
||||
BasicBlock *bb = bb_ptr.get();
|
||||
if (bb == entryBlock)
|
||||
continue;
|
||||
|
||||
BasicBlock *currentIDom = nullptr;
|
||||
const std::set<BasicBlock *> *domsOfBB = getDominators(bb);
|
||||
if (!domsOfBB)
|
||||
continue;
|
||||
|
||||
for (BasicBlock *D : *domsOfBB) {
|
||||
if (D == bb)
|
||||
continue;
|
||||
|
||||
bool isCandidateIDom = true;
|
||||
for (BasicBlock *candidate : *domsOfBB) {
|
||||
if (candidate == bb || candidate == D)
|
||||
continue;
|
||||
const std::set<BasicBlock *> *domsOfCandidate = getDominators(candidate);
|
||||
if (domsOfCandidate && domsOfCandidate->count(D) == 0 && domsOfBB->count(candidate)) {
|
||||
isCandidateIDom = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isCandidateIDom) {
|
||||
currentIDom = D;
|
||||
break;
|
||||
}
|
||||
}
|
||||
IDoms[bb] = currentIDom;
|
||||
}
|
||||
}
|
||||
|
||||
void DominatorTree::computeDominanceFrontiers(Function *F) {
|
||||
// 经典的支配边界计算算法
|
||||
for (const auto &bb_ptr_X : F->getBasicBlocks()) {
|
||||
BasicBlock *X = bb_ptr_X.get();
|
||||
DominanceFrontiers[X].clear();
|
||||
|
||||
for (BasicBlock *Y : X->getSuccessors()) {
|
||||
const std::set<BasicBlock *> *domsOfY = getDominators(Y);
|
||||
if (domsOfY && domsOfY->find(X) == domsOfY->end()) {
|
||||
DominanceFrontiers[X].insert(Y);
|
||||
}
|
||||
}
|
||||
|
||||
const std::set<BasicBlock *> *domsOfX = getDominators(X);
|
||||
if (!domsOfX)
|
||||
continue;
|
||||
for (const auto &bb_ptr_Z : F->getBasicBlocks()) {
|
||||
BasicBlock *Z = bb_ptr_Z.get();
|
||||
if (Z == X)
|
||||
continue;
|
||||
const std::set<BasicBlock *> *domsOfZ = getDominators(Z);
|
||||
if (domsOfZ && domsOfZ->count(X) && Z != X) {
|
||||
|
||||
for (BasicBlock *Y : Z->getSuccessors()) {
|
||||
const std::set<BasicBlock *> *domsOfY = getDominators(Y);
|
||||
if (domsOfY && domsOfY->find(X) == domsOfY->end()) {
|
||||
DominanceFrontiers[X].insert(Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// DominatorTreeAnalysisPass 的实现
|
||||
// ==============================================================
|
||||
|
||||
|
||||
bool DominatorTreeAnalysisPass::runOnFunction(Function* F, AnalysisManager &AM) {
|
||||
CurrentDominatorTree = std::make_unique<DominatorTree>(F);
|
||||
CurrentDominatorTree->computeDominators(F);
|
||||
CurrentDominatorTree->computeIDoms(F);
|
||||
CurrentDominatorTree->computeDominanceFrontiers(F);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<AnalysisResultBase> DominatorTreeAnalysisPass::getResult() {
|
||||
// 返回计算好的 DominatorTree 实例,所有权转移给 AnalysisManager
|
||||
return std::move(CurrentDominatorTree);
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
29
src/IR.cpp
29
src/IR.cpp
@ -105,8 +105,17 @@ FunctionType*FunctionType::get(Type *returnType, const std::vector<Type *> ¶
|
||||
}
|
||||
|
||||
ArrayType *ArrayType::get(Type *elementType, unsigned numElements) {
|
||||
// TODO:可以考虑在这里添加缓存,避免重复创建相同的数组类型
|
||||
return new ArrayType(elementType, numElements);
|
||||
static std::set<std::unique_ptr<ArrayType>> arrayTypes;
|
||||
auto iter = std::find_if(arrayTypes.begin(), arrayTypes.end(), [&](const std::unique_ptr<ArrayType> &type) -> bool {
|
||||
return elementType == type->getElementType() && numElements == type->getNumElements();
|
||||
});
|
||||
if (iter != arrayTypes.end()) {
|
||||
return iter->get();
|
||||
}
|
||||
auto type = new ArrayType(elementType, numElements);
|
||||
assert(type);
|
||||
auto result = arrayTypes.emplace(type);
|
||||
return result.first->get();
|
||||
}
|
||||
|
||||
void Value::replaceAllUsesWith(Value *value) {
|
||||
@ -521,8 +530,14 @@ Function * Function::clone(const std::string &suffix) const {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto ¶m : blocks.front()->getArguments()) {
|
||||
newFunction->getEntryBlock()->insertArgument(dynamic_cast<AllocaInst *>(oldNewValueMap.at(param)));
|
||||
// for (const auto ¶m : blocks.front()->getArguments()) {
|
||||
// newFunction->getEntryBlock()->insertArgument(dynamic_cast<AllocaInst *>(oldNewValueMap.at(param)));
|
||||
// }
|
||||
for (const auto &arg : arguments) {
|
||||
auto newArg = dynamic_cast<Argument *>(oldNewValueMap.at(arg));
|
||||
if (newArg != nullptr) {
|
||||
newFunction->insertArgument(newArg);
|
||||
}
|
||||
}
|
||||
|
||||
return newFunction;
|
||||
@ -638,7 +653,7 @@ Function * CallInst::getCallee() const { return dynamic_cast<Function *>(getOper
|
||||
/**
|
||||
* 获取变量指针
|
||||
*/
|
||||
auto SymbolTable::getVariable(const std::string &name) const -> User * {
|
||||
auto SymbolTable::getVariable(const std::string &name) const -> Value * {
|
||||
auto node = curNode;
|
||||
while (node != nullptr) {
|
||||
auto iter = node->varList.find(name);
|
||||
@ -653,8 +668,8 @@ auto SymbolTable::getVariable(const std::string &name) const -> User * {
|
||||
/**
|
||||
* 添加变量到符号表
|
||||
*/
|
||||
auto SymbolTable::addVariable(const std::string &name, User *variable) -> User * {
|
||||
User *result = nullptr;
|
||||
auto SymbolTable::addVariable(const std::string &name, Value *variable) -> Value * {
|
||||
Value *result = nullptr;
|
||||
if (curNode != nullptr) {
|
||||
std::stringstream ss;
|
||||
auto iter = variableIndex.find(name);
|
||||
|
||||
160
src/Liveness.cpp
Normal file
160
src/Liveness.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#include "Liveness.h"
|
||||
#include <algorithm> // For std::set_union, std::set_difference
|
||||
#include <iostream>
|
||||
#include <queue> // Potentially for worklist, though not strictly needed for the iterative approach below
|
||||
#include <set> // For std::set
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 初始化静态 ID
|
||||
void *LivenessAnalysisPass::ID = (void *)&LivenessAnalysisPass::ID;
|
||||
// ==============================================================
|
||||
// LivenessAnalysisResult 结果类的实现
|
||||
// ==============================================================
|
||||
|
||||
const std::set<Value *> *LivenessAnalysisResult::getLiveIn(BasicBlock *BB) const {
|
||||
auto it = liveInSets.find(BB);
|
||||
if (it != liveInSets.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
// 返回一个空集合,表示未找到或不存在
|
||||
static const std::set<Value *> emptySet;
|
||||
return &emptySet;
|
||||
}
|
||||
|
||||
const std::set<Value *> *LivenessAnalysisResult::getLiveOut(BasicBlock *BB) const {
|
||||
auto it = liveOutSets.find(BB);
|
||||
if (it != liveOutSets.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
static const std::set<Value *> emptySet;
|
||||
return &emptySet;
|
||||
}
|
||||
|
||||
void LivenessAnalysisResult::computeDefUse(BasicBlock *BB, std::set<Value *> &def, std::set<Value *> &use) {
|
||||
def.clear(); // 将持有在 BB 中定义的值
|
||||
use.clear(); // 将持有在 BB 中使用但在其定义之前的值
|
||||
|
||||
// 临时集合,用于跟踪当前基本块中已经定义过的变量
|
||||
std::set<Value *> defined_in_block_so_far;
|
||||
|
||||
// 按照指令在块中的顺序遍历
|
||||
for (const auto &inst_ptr : BB->getInstructions()) {
|
||||
Instruction *inst = inst_ptr.get();
|
||||
|
||||
// 1. 处理指令的操作数 (Use) - 在定义之前的使用
|
||||
for (const auto &use_ptr : inst->getOperands()) { // 修正迭代器类型
|
||||
Value *operand = use_ptr->getValue(); // 从 shared_ptr<Use> 获取 Value*
|
||||
|
||||
// 过滤掉常量和全局变量,因为它们通常不被视为活跃变量
|
||||
ConstantValue *constValue = dynamic_cast<ConstantValue *>(operand);
|
||||
GlobalValue *globalValue = dynamic_cast<GlobalValue *>(operand);
|
||||
if (constValue || globalValue) {
|
||||
continue; // 跳过常量和全局变量
|
||||
}
|
||||
|
||||
// 如果操作数是一个变量(Instruction 或 Argument),并且它在此基本块的当前点之前尚未被定义
|
||||
if (defined_in_block_so_far.find(operand) == defined_in_block_so_far.end()) {
|
||||
use.insert(operand);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 处理指令自身产生的定义 (Def)
|
||||
if (inst->isDefine()) { // 使用 isDefine() 方法
|
||||
// 指令自身定义了一个值。将其添加到块的 def 集合,
|
||||
// 并添加到当前块中已定义的值的临时集合。
|
||||
def.insert(inst); // inst 本身就是被定义的值(例如,虚拟寄存器)
|
||||
defined_in_block_so_far.insert(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LivenessAnalysisResult::computeLiveness(Function *F) {
|
||||
// 每次计算前清空旧结果
|
||||
liveInSets.clear(); // 直接清空 map,不再使用 F 作为键
|
||||
liveOutSets.clear(); // 直接清空 map
|
||||
|
||||
// 初始化所有基本块的 LiveIn 和 LiveOut 集合为空
|
||||
for (const auto &bb_ptr : F->getBasicBlocks()) {
|
||||
BasicBlock *bb = bb_ptr.get();
|
||||
liveInSets[bb] = {}; // 直接以 bb 为键
|
||||
liveOutSets[bb] = {}; // 直接以 bb 为键
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
// TODO : 目前为逆序遍历基本块,考虑反向拓扑序遍历基本块
|
||||
|
||||
// 逆序遍历基本块
|
||||
// std::list<std::unique_ptr<BasicBlock>> basicBlocks(F->getBasicBlocks().begin(), F->getBasicBlocks().end());
|
||||
// std::reverse(basicBlocks.begin(), basicBlocks.end());
|
||||
// 然后遍历 basicBlocks
|
||||
// 创建一个 BasicBlock* 的列表来存储指针,避免拷贝 unique_ptr
|
||||
// Option 1: Using std::vector<BasicBlock*> (preferred for performance with reverse)
|
||||
std::vector<BasicBlock*> basicBlocksPointers;
|
||||
for (const auto& bb_ptr : F->getBasicBlocks()) {
|
||||
basicBlocksPointers.push_back(bb_ptr.get());
|
||||
}
|
||||
std::reverse(basicBlocksPointers.begin(), basicBlocksPointers.end());
|
||||
|
||||
for (auto bb_iter = basicBlocksPointers.begin(); bb_iter != basicBlocksPointers.end(); ++bb_iter) {
|
||||
BasicBlock *bb = *bb_iter; // 获取 BasicBlock 指针
|
||||
if (!bb)
|
||||
continue; // 避免空指针
|
||||
|
||||
std::set<Value *> oldLiveIn = liveInSets[bb];
|
||||
std::set<Value *> oldLiveOut = liveOutSets[bb];
|
||||
|
||||
// 1. 计算 LiveOut(BB) = Union(LiveIn(Succ) for Succ in Successors(BB))
|
||||
std::set<Value *> newLiveOut;
|
||||
for (BasicBlock *succ : bb->getSuccessors()) {
|
||||
const std::set<Value *> *succLiveIn = getLiveIn(succ); // 获取后继的 LiveIn
|
||||
if (succLiveIn) {
|
||||
newLiveOut.insert(succLiveIn->begin(), succLiveIn->end());
|
||||
}
|
||||
}
|
||||
liveOutSets[bb] = newLiveOut;
|
||||
|
||||
// 2. 计算 LiveIn(BB) = Use(BB) Union (LiveOut(BB) - Def(BB))
|
||||
std::set<Value *> defSet, useSet;
|
||||
computeDefUse(bb, defSet, useSet); // 计算当前块的 Def 和 Use
|
||||
|
||||
std::set<Value *> liveOutMinusDef;
|
||||
std::set_difference(newLiveOut.begin(), newLiveOut.end(), defSet.begin(), defSet.end(),
|
||||
std::inserter(liveOutMinusDef, liveOutMinusDef.begin()));
|
||||
|
||||
std::set<Value *> newLiveIn = useSet;
|
||||
newLiveIn.insert(liveOutMinusDef.begin(), liveOutMinusDef.end());
|
||||
liveInSets[bb] = newLiveIn;
|
||||
|
||||
// 检查是否发生变化
|
||||
if (oldLiveIn != newLiveIn || oldLiveOut != newLiveOut) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// LivenessAnalysisPass 的实现
|
||||
// ==============================================================
|
||||
|
||||
bool LivenessAnalysisPass::runOnFunction(Function *F, AnalysisManager &AM) {
|
||||
// 每次运行创建一个新的 LivenessAnalysisResult 对象来存储结果
|
||||
CurrentLivenessResult = std::make_unique<LivenessAnalysisResult>(F);
|
||||
|
||||
// 调用 LivenessAnalysisResult 内部的方法来计算分析结果
|
||||
CurrentLivenessResult->computeLiveness(F);
|
||||
|
||||
// 分析遍通常不修改 IR,所以返回 false
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<AnalysisResultBase> LivenessAnalysisPass::getResult() {
|
||||
// 返回计算好的 LivenessAnalysisResult 实例,所有权转移给 AnalysisManager
|
||||
return std::move(CurrentLivenessResult);
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
515
src/Mem2Reg.cpp
515
src/Mem2Reg.cpp
@ -1,515 +0,0 @@
|
||||
#include "Mem2Reg.h"
|
||||
#include "SysYIRPrinter.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// --- 私有成员函数实现 ---
|
||||
|
||||
// 计算给定定义块集合的迭代支配边界
|
||||
std::unordered_set<BasicBlock*> Mem2Reg::computeIteratedDomFrontiers(const std::unordered_set<BasicBlock*>& blocks) {
|
||||
std::unordered_set<BasicBlock*> result;
|
||||
std::queue<BasicBlock*> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 分析一个 alloca 的所有 uses,填充 allocaDefsBlock 和 allocaUsesBlock
|
||||
void Mem2Reg::allocaAnalysis(AllocaInst* alloca) {
|
||||
allocaDefsBlock[alloca].clear();
|
||||
allocaUsesBlock[alloca].clear();
|
||||
|
||||
for (auto use : alloca->getUses()) {
|
||||
Instruction* userInst = dynamic_cast<Instruction*>(use->getUser());
|
||||
if (!userInst) continue;
|
||||
|
||||
if (StoreInst* store = dynamic_cast<StoreInst*>(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<LoadInst*>(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<PointerType*>(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<Instruction*>(use->getUser());
|
||||
if (!userInst) {
|
||||
// 如果不是指令的 use,比如作为全局变量的初始值等,通常认为逃逸
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LoadInst* load = dynamic_cast<LoadInst*>(userInst)) {
|
||||
// Load 指令结果的类型必须与 alloca 的基类型一致
|
||||
if (load->getType() != allocabaseType) {
|
||||
return false;
|
||||
}
|
||||
} else if (StoreInst* store = dynamic_cast<StoreInst*>(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;
|
||||
}
|
||||
|
||||
// 在迭代支配边界处插入 Phi 指令
|
||||
void Mem2Reg::insertPhiNodes(Function* func) {
|
||||
// 清空上次 Phi 插入的结果
|
||||
phiMap.clear();
|
||||
allPhiInstructions.clear();
|
||||
|
||||
std::unordered_set<BasicBlock*> phiPlacementBlocks; // 存放需要插入 Phi 的块
|
||||
std::queue<BasicBlock*> 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<PointerType*>(alloca->getType())->getBaseType();
|
||||
|
||||
// 在支配边界块的开头插入 Phi 指令
|
||||
pBuilder->setPosition(domFrontierBB->begin());
|
||||
PhiInst* newPhi = pBuilder->createPhiInst(phiType, {}, {}); // 初始入边为空
|
||||
|
||||
allPhiInstructions.push_back(newPhi); // 记录所有 Phi
|
||||
phiPlacementBlocks.insert(domFrontierBB); // 标记已插入
|
||||
|
||||
// 将 Phi 指令映射到它所代表的原始 alloca
|
||||
phiMap[domFrontierBB][newPhi] = alloca;
|
||||
|
||||
// 如果支配边界块本身没有定义该 alloca,则其支配边界也可能需要 Phi
|
||||
// 只有当这个块不是当前alloca的定义块时,才将其加入workQueue,以计算其DF。
|
||||
if (allocaDefsBlock[alloca].find(domFrontierBB) == allocaDefsBlock[alloca].end()) {
|
||||
workQueue.push(domFrontierBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取前驱块在后继块前驱列表中的索引
|
||||
int Mem2Reg::getPredIndex(BasicBlock* pred, BasicBlock* succ) {
|
||||
int index = 0;
|
||||
for (auto* elem : succ->getPredecessors()) {
|
||||
if (elem == pred) {
|
||||
return index;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
// 断言通常在你的 IR 框架中应该确保前驱是存在的
|
||||
// assert(false && "Predecessor not found in successor's predecessor list");
|
||||
return -1; // 应该不会发生
|
||||
}
|
||||
|
||||
// 递归地重命名基本块中的变量并填充 Phi 指令
|
||||
void Mem2Reg::renameBlock(BasicBlock* block,
|
||||
std::unordered_map<AllocaInst*, Value*>& currentIncomings,
|
||||
std::unordered_set<BasicBlock*>& visitedBlocks) {
|
||||
|
||||
// 记录在此块中发生的定义,以便在退出时将它们从栈中弹出
|
||||
std::unordered_map<AllocaInst*, int> 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<AllocaInst*>(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<LoadInst*>(currentInst)) {
|
||||
AllocaInst* originalAlloca = dynamic_cast<AllocaInst*>(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<PointerType*>(originalAlloca->getType())->getBaseType() :
|
||||
originalAlloca->getType());
|
||||
}
|
||||
|
||||
load->replaceAllUsesWith(incomingVal); // 用最新值替换所有 load 的用途
|
||||
SysYIROptUtils::usedelete(currentInst);
|
||||
it = block->getInstructions().erase(it);
|
||||
continue;
|
||||
}
|
||||
} else if (StoreInst* store = dynamic_cast<StoreInst*>(currentInst)) {
|
||||
AllocaInst* originalAlloca = dynamic_cast<AllocaInst*>(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<PhiInst*>(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<PhiInst*>(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<PointerType*>(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<AllocaInst*>(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<Instruction*>(use_ptr->getUser());
|
||||
if (user_inst && (dynamic_cast<LoadInst*>(user_inst) || dynamic_cast<StoreInst*>(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<AllocaInst*, Value*> initialIncomings;
|
||||
std::unordered_set<BasicBlock*> visitedBlocks; // 用于 DFS 遍历,防止循环
|
||||
|
||||
// 初始化 entry block 的 Incomings 状态
|
||||
for (AllocaInst* alloca : currentFunctionAllocas) {
|
||||
initialIncomings[alloca] = UndefinedValue::get(dynamic_cast<PointerType*>(alloca->getType())->getBaseType());
|
||||
}
|
||||
|
||||
// 从入口块开始递归重命名
|
||||
renameBlock(entryBB, initialIncomings, visitedBlocks);
|
||||
|
||||
// 5. 简化 Phi 指令
|
||||
// 由于 renameBlock 可能会删除 Phi,这里复制一份列表以安全迭代
|
||||
std::vector<PhiInst*> 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<PhiInst*> 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<PhiInst*> 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
|
||||
175
src/Pass.cpp
Normal file
175
src/Pass.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
#include "Dom.h"
|
||||
#include "Liveness.h"
|
||||
#include "SysYIRCFGOpt.h"
|
||||
#include "SysYIRPrinter.h"
|
||||
#include "DCE.h"
|
||||
#include "Pass.h"
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
extern int DEBUG; // 全局调试标志
|
||||
namespace sysy {
|
||||
|
||||
// ======================================================================
|
||||
// 封装优化流程的函数:包含Pass注册和迭代运行逻辑
|
||||
// ======================================================================
|
||||
|
||||
void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR, int optLevel) {
|
||||
if (DEBUG) std::cout << "--- Starting Middle-End Optimizations (Level -O" << optLevel << ") ---\n";
|
||||
|
||||
/*
|
||||
中端开发框架基本流程:
|
||||
1) 分析pass
|
||||
1. 实现分析pass并引入Pass.cpp
|
||||
2. 注册分析pass
|
||||
2) 优化pass
|
||||
1. 实现优化pass并引入Pass.cpp
|
||||
2. 注册优化pass
|
||||
3. 添加优化passid
|
||||
*/
|
||||
// 注册分析遍
|
||||
registerAnalysisPass<sysy::DominatorTreeAnalysisPass>();
|
||||
registerAnalysisPass<sysy::LivenessAnalysisPass>();
|
||||
|
||||
// 注册优化遍
|
||||
registerOptimizationPass<SysYDelInstAfterBrPass>();
|
||||
registerOptimizationPass<SysYDelNoPreBLockPass>();
|
||||
registerOptimizationPass<SysYBlockMergePass>();
|
||||
|
||||
registerOptimizationPass<SysYDelEmptyBlockPass>(builderIR);
|
||||
registerOptimizationPass<SysYCondBr2BrPass>(builderIR);
|
||||
registerOptimizationPass<SysYAddReturnPass>(builderIR);
|
||||
|
||||
if (optLevel >= 1) {
|
||||
//经过设计安排优化遍的执行顺序以及执行逻辑
|
||||
if (DEBUG) std::cout << "Applying -O1 optimizations.\n";
|
||||
if (DEBUG) std::cout << "--- Running custom optimization sequence ---\n";
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&SysYDelInstAfterBrPass::ID);
|
||||
this->addPass(&SysYDelNoPreBLockPass::ID);
|
||||
this->addPass(&SysYBlockMergePass::ID);
|
||||
this->addPass(&SysYDelEmptyBlockPass::ID);
|
||||
this->addPass(&SysYCondBr2BrPass::ID);
|
||||
this->addPass(&SysYAddReturnPass::ID);
|
||||
this->run();
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&DCE::ID);
|
||||
this->run();
|
||||
|
||||
if (DEBUG) std::cout << "--- Custom optimization sequence finished ---\n";
|
||||
}
|
||||
|
||||
// 2. 创建遍管理器
|
||||
// 3. 根据优化级别添加不同的优化遍
|
||||
// TODO : 根据 optLevel 添加不同的优化遍
|
||||
// 讨论 是不动点迭代进行优化遍还是手动客制化优化遍的顺序?
|
||||
|
||||
|
||||
if (DEBUG) {
|
||||
std::cout << "=== Final IR After Middle-End Optimizations (Level -O" << optLevel << ") ===\n";
|
||||
SysYPrinter printer(moduleIR);
|
||||
printer.printIR();
|
||||
}
|
||||
}
|
||||
|
||||
void PassManager::clearPasses() {
|
||||
passes.clear();
|
||||
}
|
||||
|
||||
void PassManager::addPass(void *passID) {
|
||||
|
||||
PassRegistry ®istry = PassRegistry::getPassRegistry();
|
||||
std::unique_ptr<Pass> P = registry.createPass(passID);
|
||||
if (!P) {
|
||||
// Error: Pass not found or failed to create
|
||||
return;
|
||||
}
|
||||
|
||||
passes.push_back(std::move(P));
|
||||
}
|
||||
|
||||
// 运行所有注册的遍
|
||||
bool PassManager::run() {
|
||||
bool changed = false;
|
||||
for (const auto &p : passes) {
|
||||
bool passChanged = false; // 记录当前遍是否修改了 IR
|
||||
|
||||
// 处理优化遍的分析依赖和失效
|
||||
if (p->getPassKind() == Pass::PassKind::Optimization) {
|
||||
OptimizationPass *optPass = static_cast<OptimizationPass *>(p.get());
|
||||
std::set<void *> analysisDependencies;
|
||||
std::set<void *> analysisInvalidations;
|
||||
optPass->getAnalysisUsage(analysisDependencies, analysisInvalidations);
|
||||
|
||||
// PassManager 不显式运行分析依赖。
|
||||
// 而是优化遍在 runOnFunction 内部通过 AnalysisManager.getAnalysisResult 按需请求。
|
||||
}
|
||||
|
||||
if (p->getGranularity() == Pass::Granularity::Module) {
|
||||
passChanged = p->runOnModule(pmodule, analysisManager);
|
||||
} else if (p->getGranularity() == Pass::Granularity::Function) {
|
||||
for (auto &funcPair : pmodule->getFunctions()) {
|
||||
Function *F = funcPair.second.get();
|
||||
passChanged = p->runOnFunction(F, analysisManager) || passChanged;
|
||||
|
||||
if (passChanged && p->getPassKind() == Pass::PassKind::Optimization) {
|
||||
OptimizationPass *optPass = static_cast<OptimizationPass *>(p.get());
|
||||
std::set<void *> analysisDependencies;
|
||||
std::set<void *> analysisInvalidations;
|
||||
optPass->getAnalysisUsage(analysisDependencies, analysisInvalidations);
|
||||
for (void *invalidationID : analysisInvalidations) {
|
||||
analysisManager.invalidateAnalysis(invalidationID, F);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p->getGranularity() == Pass::Granularity::BasicBlock) {
|
||||
for (auto &funcPair : pmodule->getFunctions()) {
|
||||
Function *F = funcPair.second.get();
|
||||
for (auto &bbPtr : funcPair.second->getBasicBlocks()) {
|
||||
passChanged = p->runOnBasicBlock(bbPtr.get(), analysisManager) || passChanged;
|
||||
|
||||
if (passChanged && p->getPassKind() == Pass::PassKind::Optimization) {
|
||||
OptimizationPass *optPass = static_cast<OptimizationPass *>(p.get());
|
||||
std::set<void *> analysisDependencies;
|
||||
std::set<void *> analysisInvalidations;
|
||||
optPass->getAnalysisUsage(analysisDependencies, analysisInvalidations);
|
||||
for (void *invalidationID : analysisInvalidations) {
|
||||
analysisManager.invalidateAnalysis(invalidationID, F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
changed = changed || passChanged;
|
||||
}
|
||||
return changed;
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <typename AnalysisPassType> void registerAnalysisPass() {
|
||||
PassRegistry::getPassRegistry().registerPass(&AnalysisPassType::ID,
|
||||
[]() { return std::make_unique<AnalysisPassType>(); });
|
||||
}
|
||||
|
||||
template <typename OptimizationPassType, typename std::enable_if<
|
||||
std::is_constructible<OptimizationPassType, IRBuilder*>::value, int>::type>
|
||||
void registerOptimizationPass(IRBuilder* builder) {
|
||||
PassRegistry::getPassRegistry().registerPass(&OptimizationPassType::ID,
|
||||
[builder]() { return std::make_unique<OptimizationPassType>(builder); });
|
||||
}
|
||||
|
||||
template <typename OptimizationPassType, typename std::enable_if<
|
||||
!std::is_constructible<OptimizationPassType, IRBuilder*>::value, int>::type>
|
||||
void registerOptimizationPass() {
|
||||
PassRegistry::getPassRegistry().registerPass(&OptimizationPassType::ID,
|
||||
[]() { return std::make_unique<OptimizationPassType>(); });
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -18,7 +18,7 @@ bool isMemoryOp(RVOpcodes opcode) {
|
||||
|
||||
RISCv64AsmPrinter::RISCv64AsmPrinter(MachineFunction* mfunc) : MFunc(mfunc) {}
|
||||
|
||||
void RISCv64AsmPrinter::run(std::ostream& os) {
|
||||
void RISCv64AsmPrinter::run(std::ostream& os, bool debug) {
|
||||
OS = &os;
|
||||
|
||||
*OS << ".globl " << MFunc->getName() << "\n";
|
||||
@ -27,10 +27,12 @@ void RISCv64AsmPrinter::run(std::ostream& os) {
|
||||
printPrologue();
|
||||
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
printBasicBlock(mbb.get());
|
||||
printBasicBlock(mbb.get(), debug);
|
||||
}
|
||||
}
|
||||
|
||||
// 在 RISCv64AsmPrinter.cpp 文件中
|
||||
|
||||
void RISCv64AsmPrinter::printPrologue() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
// 序言需要为保存ra和s0预留16字节
|
||||
@ -42,25 +44,7 @@ void RISCv64AsmPrinter::printPrologue() {
|
||||
*OS << " addi sp, sp, -" << aligned_stack_size << "\n";
|
||||
*OS << " sd ra, " << (aligned_stack_size - 8) << "(sp)\n";
|
||||
*OS << " sd s0, " << (aligned_stack_size - 16) << "(sp)\n";
|
||||
*OS << " mv s0, sp\n";
|
||||
}
|
||||
|
||||
// 忠实还原保存函数入口参数的逻辑
|
||||
Function* F = MFunc->getFunc();
|
||||
if (F && F->getEntryBlock()) {
|
||||
int arg_idx = 0;
|
||||
RISCv64ISel* isel = MFunc->getISel();
|
||||
for (AllocaInst* alloca_for_param : F->getEntryBlock()->getArguments()) {
|
||||
if (arg_idx >= 8) break;
|
||||
|
||||
unsigned vreg = isel->getVReg(alloca_for_param);
|
||||
if (frame_info.alloca_offsets.count(vreg)) {
|
||||
int offset = frame_info.alloca_offsets.at(vreg);
|
||||
auto arg_reg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + arg_idx);
|
||||
*OS << " sw " << regToString(arg_reg) << ", " << offset << "(s0)\n";
|
||||
}
|
||||
arg_idx++;
|
||||
}
|
||||
*OS << " addi s0, sp, " << aligned_stack_size << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,24 +57,31 @@ void RISCv64AsmPrinter::printEpilogue() {
|
||||
}
|
||||
}
|
||||
|
||||
void RISCv64AsmPrinter::printBasicBlock(MachineBasicBlock* mbb) {
|
||||
void RISCv64AsmPrinter::printBasicBlock(MachineBasicBlock* mbb, bool debug) {
|
||||
if (!mbb->getName().empty()) {
|
||||
*OS << mbb->getName() << ":\n";
|
||||
}
|
||||
for (auto& instr : mbb->getInstructions()) {
|
||||
printInstruction(instr.get());
|
||||
printInstruction(instr.get(), debug);
|
||||
}
|
||||
}
|
||||
|
||||
void RISCv64AsmPrinter::printInstruction(MachineInstr* instr) {
|
||||
void RISCv64AsmPrinter::printInstruction(MachineInstr* instr, bool debug) {
|
||||
auto opcode = instr->getOpcode();
|
||||
if (opcode == RVOpcodes::RET) {
|
||||
printEpilogue();
|
||||
}
|
||||
if (opcode != RVOpcodes::LABEL) {
|
||||
*OS << " ";
|
||||
|
||||
if (opcode == RVOpcodes::LABEL) {
|
||||
// 标签直接打印,不加缩进
|
||||
printOperand(instr->getOperands()[0].get());
|
||||
*OS << ":\n";
|
||||
return; // 处理完毕,直接返回
|
||||
}
|
||||
|
||||
// 对于所有非标签指令,先打印缩进
|
||||
*OS << " ";
|
||||
|
||||
switch (opcode) {
|
||||
case RVOpcodes::ADD: *OS << "add "; break; case RVOpcodes::ADDI: *OS << "addi "; break;
|
||||
case RVOpcodes::ADDW: *OS << "addw "; break; case RVOpcodes::ADDIW: *OS << "addiw "; break;
|
||||
@ -126,13 +117,27 @@ void RISCv64AsmPrinter::printInstruction(MachineInstr* instr) {
|
||||
case RVOpcodes::SNEZ: *OS << "snez "; break;
|
||||
case RVOpcodes::CALL: *OS << "call "; break;
|
||||
case RVOpcodes::LABEL:
|
||||
printOperand(instr->getOperands()[0].get());
|
||||
*OS << ":";
|
||||
break;
|
||||
case RVOpcodes::FRAME_LOAD:
|
||||
case RVOpcodes::FRAME_STORE:
|
||||
// These should have been eliminated by RegAlloc
|
||||
throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
case RVOpcodes::FRAME_LOAD_W:
|
||||
// It should have been eliminated by RegAlloc
|
||||
if (!debug) throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
*OS << "frame_load_w "; break;
|
||||
case RVOpcodes::FRAME_LOAD_D:
|
||||
// It should have been eliminated by RegAlloc
|
||||
if (!debug) throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
*OS << "frame_load_d "; break;
|
||||
case RVOpcodes::FRAME_STORE_W:
|
||||
// It should have been eliminated by RegAlloc
|
||||
if (!debug) throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
*OS << "frame_store_w "; break;
|
||||
case RVOpcodes::FRAME_STORE_D:
|
||||
// It should have been eliminated by RegAlloc
|
||||
if (!debug) throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
*OS << "frame_store_d "; break;
|
||||
case RVOpcodes::FRAME_ADDR:
|
||||
// It should have been eliminated by RegAlloc
|
||||
if (!debug) throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
*OS << "frame_addr "; break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown opcode in AsmPrinter");
|
||||
}
|
||||
|
||||
@ -16,13 +16,59 @@ std::string RISCv64CodeGen::code_gen() {
|
||||
std::string RISCv64CodeGen::module_gen() {
|
||||
std::stringstream ss;
|
||||
|
||||
// 1. 处理全局变量 (.data段)
|
||||
if (!module->getGlobals().empty()) {
|
||||
ss << ".data\n";
|
||||
for (const auto& global : module->getGlobals()) {
|
||||
// --- [新逻辑] 步骤1:将全局变量分为.data和.bss两组 ---
|
||||
std::vector<GlobalValue*> data_globals;
|
||||
std::vector<GlobalValue*> bss_globals;
|
||||
|
||||
for (const auto& global_ptr : module->getGlobals()) {
|
||||
GlobalValue* global = global_ptr.get();
|
||||
const auto& init_values = global->getInitValues();
|
||||
|
||||
// 判断是否为大型零初始化数组,以便放入.bss段
|
||||
bool is_large_zero_array = false;
|
||||
// 规则:初始化列表只有一项,且该项是值为0的整数,且数量大于一个阈值(例如16)
|
||||
if (init_values.getValues().size() == 1) {
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(init_values.getValues()[0])) {
|
||||
if (const_val->isInt() && const_val->getInt() == 0 && init_values.getNumbers()[0] > 16) {
|
||||
is_large_zero_array = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_large_zero_array) {
|
||||
bss_globals.push_back(global);
|
||||
} else {
|
||||
data_globals.push_back(global);
|
||||
}
|
||||
}
|
||||
|
||||
// --- [新逻辑] 步骤2:生成 .bss 段的代码 ---
|
||||
if (!bss_globals.empty()) {
|
||||
ss << ".bss\n"; // 切换到 .bss 段
|
||||
for (GlobalValue* global : bss_globals) {
|
||||
// 获取数组总大小(元素个数 * 元素大小)
|
||||
// 在SysY中,我们假设元素都是4字节(int或float)
|
||||
unsigned count = global->getInitValues().getNumbers()[0];
|
||||
unsigned total_size = count * 4;
|
||||
|
||||
ss << " .align 3\n"; // 8字节对齐 (2^3)
|
||||
ss << ".globl " << global->getName() << "\n";
|
||||
ss << ".type " << global->getName() << ", @object\n";
|
||||
ss << ".size " << global->getName() << ", " << total_size << "\n";
|
||||
ss << global->getName() << ":\n";
|
||||
// 使用 .space 指令来预留指定大小的零填充空间
|
||||
ss << " .space " << total_size << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// --- [旧逻辑保留] 步骤3:生成 .data 段的代码 ---
|
||||
if (!data_globals.empty()) {
|
||||
ss << ".data\n"; // 切换到 .data 段
|
||||
for (GlobalValue* global : data_globals) {
|
||||
ss << ".globl " << global->getName() << "\n";
|
||||
ss << global->getName() << ":\n";
|
||||
const auto& init_values = global->getInitValues();
|
||||
// 使用您原有的逻辑来处理显式初始化的值
|
||||
for (size_t i = 0; i < init_values.getValues().size(); ++i) {
|
||||
auto val = init_values.getValues()[i];
|
||||
auto count = init_values.getNumbers()[i];
|
||||
@ -41,7 +87,7 @@ std::string RISCv64CodeGen::module_gen() {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 处理函数 (.text段)
|
||||
// --- 处理函数 (.text段) 的逻辑保持不变 ---
|
||||
if (!module->getFunctions().empty()) {
|
||||
ss << ".text\n";
|
||||
for (const auto& func_pair : module->getFunctions()) {
|
||||
@ -61,6 +107,10 @@ std::string RISCv64CodeGen::function_gen(Function* func) {
|
||||
RISCv64ISel isel;
|
||||
std::unique_ptr<MachineFunction> mfunc = isel.runOnFunction(func);
|
||||
|
||||
std::stringstream ss1;
|
||||
RISCv64AsmPrinter printer1(mfunc.get());
|
||||
printer1.run(ss1, true);
|
||||
|
||||
// 阶段 2: 指令调度 (Instruction Scheduling)
|
||||
PreRA_Scheduler scheduler;
|
||||
scheduler.runOnMachineFunction(mfunc.get());
|
||||
@ -69,6 +119,10 @@ std::string RISCv64CodeGen::function_gen(Function* func) {
|
||||
RISCv64RegAlloc reg_alloc(mfunc.get());
|
||||
reg_alloc.run();
|
||||
|
||||
// 阶段 3.5: 处理被调用者保存寄存器
|
||||
CalleeSavedHandler callee_handler;
|
||||
callee_handler.runOnMachineFunction(mfunc.get());
|
||||
|
||||
// 阶段 4: 窥孔优化 (Peephole Optimization)
|
||||
PeepholeOptimizer peephole;
|
||||
peephole.runOnMachineFunction(mfunc.get());
|
||||
@ -81,7 +135,7 @@ std::string RISCv64CodeGen::function_gen(Function* func) {
|
||||
std::stringstream ss;
|
||||
RISCv64AsmPrinter printer(mfunc.get());
|
||||
printer.run(ss);
|
||||
|
||||
if (DEBUG) ss << "\n" << ss1.str(); // 将指令选择阶段的结果也包含在最终输出中
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
@ -4,12 +4,13 @@
|
||||
#include <functional>
|
||||
#include <cmath> // For std::fabs
|
||||
#include <limits> // For std::numeric_limits
|
||||
#include <iostream>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// DAG节点定义 (内部实现)
|
||||
struct RISCv64ISel::DAGNode {
|
||||
enum NodeKind { CONSTANT, LOAD, STORE, BINARY, CALL, RETURN, BRANCH, ALLOCA_ADDR, UNARY, MEMSET };
|
||||
enum NodeKind {ARGUMENT, CONSTANT, LOAD, STORE, BINARY, CALL, RETURN, BRANCH, ALLOCA_ADDR, UNARY, MEMSET, GET_ELEMENT_PTR};
|
||||
NodeKind kind;
|
||||
Value* value = nullptr;
|
||||
std::vector<DAGNode*> operands;
|
||||
@ -50,22 +51,28 @@ std::unique_ptr<MachineFunction> RISCv64ISel::runOnFunction(Function* func) {
|
||||
|
||||
// 指令选择主流程
|
||||
void RISCv64ISel::select() {
|
||||
// 遍历基本块,为它们创建对应的MachineBasicBlock
|
||||
for (const auto& bb_ptr : F->getBasicBlocks()) {
|
||||
auto mbb = std::make_unique<MachineBasicBlock>(bb_ptr->getName(), MFunc.get());
|
||||
bb_map[bb_ptr.get()] = mbb.get();
|
||||
MFunc->addBlock(std::move(mbb));
|
||||
}
|
||||
|
||||
if (F->getEntryBlock()) {
|
||||
for (auto* arg_alloca : F->getEntryBlock()->getArguments()) {
|
||||
getVReg(arg_alloca);
|
||||
// 遍历Argument对象,为它们分配虚拟寄存器。
|
||||
if (F) {
|
||||
for (Argument* arg : F->getArguments()) {
|
||||
// getVReg会为每个代表参数的Argument对象在vreg_map中创建一个条目。
|
||||
// 这样,当后续的store指令使用这个参数时,就能找到对应的vreg。
|
||||
getVReg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历基本块,进行指令选择
|
||||
for (const auto& bb_ptr : F->getBasicBlocks()) {
|
||||
selectBasicBlock(bb_ptr.get());
|
||||
}
|
||||
|
||||
// 链接MachineBasicBlock的前驱和后继
|
||||
for (const auto& bb_ptr : F->getBasicBlocks()) {
|
||||
CurMBB = bb_map.at(bb_ptr.get());
|
||||
for (auto succ : bb_ptr->getSuccessors()) {
|
||||
@ -82,6 +89,10 @@ void RISCv64ISel::selectBasicBlock(BasicBlock* bb) {
|
||||
CurMBB = bb_map.at(bb);
|
||||
auto dag = build_dag(bb);
|
||||
|
||||
if (DEBUG) { // 使用 DEBUG 宏或变量来控制是否打印
|
||||
print_dag(dag, bb->getName());
|
||||
}
|
||||
|
||||
std::map<Value*, DAGNode*> value_to_node;
|
||||
for(const auto& node : dag) {
|
||||
if (node->value) {
|
||||
@ -92,6 +103,10 @@ void RISCv64ISel::selectBasicBlock(BasicBlock* bb) {
|
||||
std::set<DAGNode*> selected_nodes;
|
||||
std::function<void(DAGNode*)> select_recursive =
|
||||
[&](DAGNode* node) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cout << "[DEEPDEBUG] select_recursive: Visiting node with kind: " << node->kind
|
||||
<< " (Value: " << (node->value ? node->value->getName() : "null") << ")" << std::endl;
|
||||
}
|
||||
if (!node || selected_nodes.count(node)) return;
|
||||
for (auto operand : node->operands) {
|
||||
select_recursive(operand);
|
||||
@ -118,46 +133,73 @@ void RISCv64ISel::selectBasicBlock(BasicBlock* bb) {
|
||||
}
|
||||
}
|
||||
|
||||
// 核心函数:为DAG节点选择并生成MachineInstr (忠实移植版)
|
||||
// 核心函数:为DAG节点选择并生成MachineInstr (已修复和增强的完整版本)
|
||||
void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
// 调用者(select_recursive)已经保证了操作数节点会先于当前节点被选择。
|
||||
// 因此,这里我们只处理当前节点。
|
||||
|
||||
switch (node->kind) {
|
||||
// “延迟物化”(Late Materialization)思想。
|
||||
// 这三种节点仅作为标记,不直接生成指令。它们的目的是在DAG中保留类型信息。
|
||||
// 加载其值的责任,被转移给了使用它们的父节点(如STORE, BINARY等)。
|
||||
case DAGNode::ARGUMENT:
|
||||
case DAGNode::CONSTANT:
|
||||
case DAGNode::ALLOCA_ADDR:
|
||||
if (node->value) getVReg(node->value);
|
||||
if (node->value) {
|
||||
// 确保它有一个关联的虚拟寄存器即可,不生成代码。
|
||||
getVReg(node->value);
|
||||
}
|
||||
break;
|
||||
|
||||
case DAGNode::LOAD: {
|
||||
auto dest_vreg = getVReg(node->value);
|
||||
Value* ptr_val = node->operands[0]->value;
|
||||
|
||||
// --- 修改点 ---
|
||||
// 1. 获取加载结果的类型 (即这个LOAD指令自身的类型)
|
||||
Type* loaded_type = node->value->getType();
|
||||
|
||||
// 2. 根据类型选择正确的伪指令或真实指令操作码
|
||||
RVOpcodes frame_opcode = loaded_type->isPointer() ? RVOpcodes::FRAME_LOAD_D : RVOpcodes::FRAME_LOAD_W;
|
||||
RVOpcodes real_opcode = loaded_type->isPointer() ? RVOpcodes::LD : RVOpcodes::LW;
|
||||
|
||||
|
||||
if (auto alloca = dynamic_cast<AllocaInst*>(ptr_val)) {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FRAME_LOAD);
|
||||
// 3. 创建使用新的、区分宽度的伪指令
|
||||
auto instr = std::make_unique<MachineInstr>(frame_opcode);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(getVReg(alloca)));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_val)) {
|
||||
// 对于全局变量,先用 la 加载其地址
|
||||
auto addr_vreg = getNewVReg();
|
||||
auto la = std::make_unique<MachineInstr>(RVOpcodes::LA);
|
||||
la->addOperand(std::make_unique<RegOperand>(addr_vreg));
|
||||
la->addOperand(std::make_unique<LabelOperand>(global->getName()));
|
||||
CurMBB->addInstruction(std::move(la));
|
||||
|
||||
auto lw = std::make_unique<MachineInstr>(RVOpcodes::LW);
|
||||
lw->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
lw->addOperand(std::make_unique<MemOperand>(
|
||||
// 然后根据类型使用 ld 或 lw 加载其值
|
||||
auto load_instr = std::make_unique<MachineInstr>(real_opcode);
|
||||
load_instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
load_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(addr_vreg),
|
||||
std::make_unique<ImmOperand>(0)
|
||||
));
|
||||
CurMBB->addInstruction(std::move(lw));
|
||||
CurMBB->addInstruction(std::move(load_instr));
|
||||
|
||||
} else {
|
||||
// 对于已经在虚拟寄存器中的指针地址,直接通过该地址加载
|
||||
auto ptr_vreg = getVReg(ptr_val);
|
||||
auto lw = std::make_unique<MachineInstr>(RVOpcodes::LW);
|
||||
lw->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
lw->addOperand(std::make_unique<MemOperand>(
|
||||
|
||||
// 根据类型使用 ld 或 lw
|
||||
auto load_instr = std::make_unique<MachineInstr>(real_opcode);
|
||||
load_instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
load_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(ptr_vreg),
|
||||
std::make_unique<ImmOperand>(0)
|
||||
));
|
||||
CurMBB->addInstruction(std::move(lw));
|
||||
CurMBB->addInstruction(std::move(load_instr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -166,6 +208,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
Value* val_to_store = node->operands[0]->value;
|
||||
Value* ptr_val = node->operands[1]->value;
|
||||
|
||||
// 如果要存储的值是一个常量,就在这里生成 `li` 指令加载它
|
||||
if (auto val_const = dynamic_cast<ConstantValue*>(val_to_store)) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(val_const)));
|
||||
@ -174,34 +217,50 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
}
|
||||
auto val_vreg = getVReg(val_to_store);
|
||||
|
||||
// --- 修改点 ---
|
||||
// 1. 获取被存储的值的类型
|
||||
Type* stored_type = val_to_store->getType();
|
||||
|
||||
// 2. 根据类型选择正确的伪指令或真实指令操作码
|
||||
RVOpcodes frame_opcode = stored_type->isPointer() ? RVOpcodes::FRAME_STORE_D : RVOpcodes::FRAME_STORE_W;
|
||||
RVOpcodes real_opcode = stored_type->isPointer() ? RVOpcodes::SD : RVOpcodes::SW;
|
||||
|
||||
if (auto alloca = dynamic_cast<AllocaInst*>(ptr_val)) {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FRAME_STORE);
|
||||
// 3. 创建使用新的、区分宽度的伪指令
|
||||
auto instr = std::make_unique<MachineInstr>(frame_opcode);
|
||||
instr->addOperand(std::make_unique<RegOperand>(val_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(getVReg(alloca)));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_val)) {
|
||||
auto addr_vreg = getNewVReg();
|
||||
// 向全局变量存储
|
||||
auto addr_vreg = getNewVReg();
|
||||
auto la = std::make_unique<MachineInstr>(RVOpcodes::LA);
|
||||
la->addOperand(std::make_unique<RegOperand>(addr_vreg));
|
||||
la->addOperand(std::make_unique<LabelOperand>(global->getName()));
|
||||
CurMBB->addInstruction(std::move(la));
|
||||
|
||||
auto sw = std::make_unique<MachineInstr>(RVOpcodes::SW);
|
||||
sw->addOperand(std::make_unique<RegOperand>(val_vreg));
|
||||
sw->addOperand(std::make_unique<MemOperand>(
|
||||
// 根据类型使用 sd 或 sw
|
||||
auto store_instr = std::make_unique<MachineInstr>(real_opcode);
|
||||
store_instr->addOperand(std::make_unique<RegOperand>(val_vreg));
|
||||
store_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(addr_vreg),
|
||||
std::make_unique<ImmOperand>(0)
|
||||
));
|
||||
CurMBB->addInstruction(std::move(sw));
|
||||
CurMBB->addInstruction(std::move(store_instr));
|
||||
|
||||
} else {
|
||||
// 向一个指针(存储在虚拟寄存器中)指向的地址存储
|
||||
auto ptr_vreg = getVReg(ptr_val);
|
||||
auto sw = std::make_unique<MachineInstr>(RVOpcodes::SW);
|
||||
sw->addOperand(std::make_unique<RegOperand>(val_vreg));
|
||||
sw->addOperand(std::make_unique<MemOperand>(
|
||||
|
||||
// 根据类型使用 sd 或 sw
|
||||
auto store_instr = std::make_unique<MachineInstr>(real_opcode);
|
||||
store_instr->addOperand(std::make_unique<RegOperand>(val_vreg));
|
||||
store_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(ptr_vreg),
|
||||
std::make_unique<ImmOperand>(0)
|
||||
));
|
||||
CurMBB->addInstruction(std::move(sw));
|
||||
CurMBB->addInstruction(std::move(store_instr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -210,37 +269,108 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
auto bin = dynamic_cast<BinaryInst*>(node->value);
|
||||
Value* lhs = bin->getLhs();
|
||||
Value* rhs = bin->getRhs();
|
||||
|
||||
if (bin->getKind() == BinaryInst::kAdd) {
|
||||
Value* base = nullptr;
|
||||
Value* offset = nullptr;
|
||||
|
||||
// [修改] 扩展基地址的判断,使其可以识别 AllocaInst 或 GlobalValue
|
||||
if (dynamic_cast<AllocaInst*>(lhs) || dynamic_cast<GlobalValue*>(lhs)) {
|
||||
base = lhs;
|
||||
offset = rhs;
|
||||
} else if (dynamic_cast<AllocaInst*>(rhs) || dynamic_cast<GlobalValue*>(rhs)) {
|
||||
base = rhs;
|
||||
offset = lhs;
|
||||
}
|
||||
|
||||
// 如果成功匹配到地址计算模式
|
||||
if (base) {
|
||||
// 1. 先为偏移量加载常量(如果它是常量的话)
|
||||
if (auto const_offset = dynamic_cast<ConstantValue*>(offset)) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(const_offset)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_offset->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
|
||||
// 2. [修改] 根据基地址的类型,生成不同的指令来获取基地址
|
||||
auto base_addr_vreg = getNewVReg(); // 创建一个新的临时vreg来存放基地址
|
||||
|
||||
// 情况一:基地址是局部栈变量
|
||||
if (auto alloca_base = dynamic_cast<AllocaInst*>(base)) {
|
||||
auto frame_addr_instr = std::make_unique<MachineInstr>(RVOpcodes::FRAME_ADDR);
|
||||
frame_addr_instr->addOperand(std::make_unique<RegOperand>(base_addr_vreg));
|
||||
frame_addr_instr->addOperand(std::make_unique<RegOperand>(getVReg(alloca_base)));
|
||||
CurMBB->addInstruction(std::move(frame_addr_instr));
|
||||
}
|
||||
// 情况二:基地址是全局变量
|
||||
else if (auto global_base = dynamic_cast<GlobalValue*>(base)) {
|
||||
auto la_instr = std::make_unique<MachineInstr>(RVOpcodes::LA);
|
||||
la_instr->addOperand(std::make_unique<RegOperand>(base_addr_vreg));
|
||||
la_instr->addOperand(std::make_unique<LabelOperand>(global_base->getName()));
|
||||
CurMBB->addInstruction(std::move(la_instr));
|
||||
}
|
||||
|
||||
// 3. 生成真正的add指令,计算最终地址(这部分逻辑保持不变)
|
||||
auto final_addr_vreg = getVReg(bin); // 这是整个二元运算的结果vreg
|
||||
auto offset_vreg = getVReg(offset);
|
||||
auto add_instr = std::make_unique<MachineInstr>(RVOpcodes::ADD); // 指针运算是64位
|
||||
add_instr->addOperand(std::make_unique<RegOperand>(final_addr_vreg));
|
||||
add_instr->addOperand(std::make_unique<RegOperand>(base_addr_vreg));
|
||||
add_instr->addOperand(std::make_unique<RegOperand>(offset_vreg));
|
||||
CurMBB->addInstruction(std::move(add_instr));
|
||||
|
||||
return; // 地址计算处理完毕,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
// [V2优点] 在BINARY节点内部按需加载常量操作数。
|
||||
auto load_val_if_const = [&](Value* val) {
|
||||
if (auto c = dynamic_cast<ConstantValue*>(val)) {
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] selectNode-BINARY: Found constant operand with value " << c->getInt()
|
||||
<< ". Generating LI instruction." << std::endl;
|
||||
}
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(c)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(c->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
};
|
||||
load_val_if_const(lhs);
|
||||
load_val_if_const(rhs);
|
||||
|
||||
auto dest_vreg = getVReg(bin);
|
||||
auto lhs_vreg = getVReg(lhs);
|
||||
auto rhs_vreg = getVReg(rhs);
|
||||
|
||||
if (bin->getKind() == BinaryInst::kAdd) {
|
||||
if (auto rhs_const = dynamic_cast<ConstantValue*>(rhs)) {
|
||||
if (rhs_const->getInt() >= -2048 && rhs_const->getInt() < 2048) {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::ADDIW);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<ImmOperand>(rhs_const->getInt()));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
return;
|
||||
}
|
||||
// 检查是否能应用立即数优化。
|
||||
bool rhs_is_imm_opt = false;
|
||||
if (auto rhs_const = dynamic_cast<ConstantValue*>(rhs)) {
|
||||
if (bin->getKind() == BinaryInst::kAdd && rhs_const->getInt() >= -2048 && rhs_const->getInt() < 2048) {
|
||||
rhs_is_imm_opt = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 仅在不能作为立即数操作数时才需要提前加载。
|
||||
load_val_if_const(lhs);
|
||||
if (!rhs_is_imm_opt) {
|
||||
load_val_if_const(rhs);
|
||||
}
|
||||
|
||||
auto dest_vreg = getVReg(bin);
|
||||
auto lhs_vreg = getVReg(lhs);
|
||||
|
||||
// [V2优点] 融合 ADDIW 优化。
|
||||
if (rhs_is_imm_opt) {
|
||||
auto rhs_const = dynamic_cast<ConstantValue*>(rhs);
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::ADDIW);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<ImmOperand>(rhs_const->getInt()));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
return; // 指令已生成,直接返回。
|
||||
}
|
||||
|
||||
auto rhs_vreg = getVReg(rhs);
|
||||
|
||||
switch (bin->getKind()) {
|
||||
case BinaryInst::kAdd: {
|
||||
// 区分指针运算(64位)和整数运算(32位)。
|
||||
RVOpcodes opcode = (lhs->getType()->isPointer() || rhs->getType()->isPointer()) ? RVOpcodes::ADD : RVOpcodes::ADDW;
|
||||
auto instr = std::make_unique<MachineInstr>(opcode);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
@ -281,7 +411,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case BinaryInst::kICmpEQ: {
|
||||
case BinaryInst::kICmpEQ: { // 等于 (a == b) -> (subw; seqz)
|
||||
auto sub = std::make_unique<MachineInstr>(RVOpcodes::SUBW);
|
||||
sub->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
sub->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
@ -294,7 +424,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(seqz));
|
||||
break;
|
||||
}
|
||||
case BinaryInst::kICmpNE: {
|
||||
case BinaryInst::kICmpNE: { // 不等于 (a != b) -> (subw; snez)
|
||||
auto sub = std::make_unique<MachineInstr>(RVOpcodes::SUBW);
|
||||
sub->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
sub->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
@ -307,7 +437,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(snez));
|
||||
break;
|
||||
}
|
||||
case BinaryInst::kICmpLT: {
|
||||
case BinaryInst::kICmpLT: { // 小于 (a < b) -> slt
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::SLT);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
@ -315,7 +445,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case BinaryInst::kICmpGT: {
|
||||
case BinaryInst::kICmpGT: { // 大于 (a > b) -> (b < a) -> slt
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::SLT);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
@ -323,7 +453,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case BinaryInst::kICmpLE: {
|
||||
case BinaryInst::kICmpLE: { // 小于等于 (a <= b) -> !(b < a) -> (slt; xori)
|
||||
auto slt = std::make_unique<MachineInstr>(RVOpcodes::SLT);
|
||||
slt->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
slt->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
@ -337,7 +467,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(xori));
|
||||
break;
|
||||
}
|
||||
case BinaryInst::kICmpGE: {
|
||||
case BinaryInst::kICmpGE: { // 大于等于 (a >= b) -> !(a < b) -> (slt; xori)
|
||||
auto slt = std::make_unique<MachineInstr>(RVOpcodes::SLT);
|
||||
slt->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
slt->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
@ -363,7 +493,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
auto src_vreg = getVReg(unary->getOperand());
|
||||
|
||||
switch (unary->getKind()) {
|
||||
case UnaryInst::kNeg: {
|
||||
case UnaryInst::kNeg: { // 取负: 0 - src
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::SUBW);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::ZERO));
|
||||
@ -371,7 +501,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case UnaryInst::kNot: {
|
||||
case UnaryInst::kNot: { // 逻辑非: src == 0 ? 1 : 0
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::SEQZ);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
@ -386,7 +516,10 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
|
||||
case DAGNode::CALL: {
|
||||
auto call = dynamic_cast<CallInst*>(node->value);
|
||||
for (size_t i = 0; i < node->operands.size() && i < 8; ++i) {
|
||||
// 处理函数参数,放入a0-a7物理寄存器
|
||||
size_t num_operands = node->operands.size();
|
||||
size_t reg_arg_count = std::min(num_operands, (size_t)8);
|
||||
for (size_t i = 0; i < reg_arg_count; ++i) {
|
||||
DAGNode* arg_node = node->operands[i];
|
||||
auto arg_preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + i);
|
||||
|
||||
@ -405,11 +538,64 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
}
|
||||
}
|
||||
if (num_operands > 8) {
|
||||
size_t stack_arg_count = num_operands - 8;
|
||||
int stack_space = stack_arg_count * 8; // RV64中每个参数槽位8字节
|
||||
|
||||
// 2a. 在栈上分配空间
|
||||
auto alloc_instr = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
alloc_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
alloc_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
alloc_instr->addOperand(std::make_unique<ImmOperand>(-stack_space));
|
||||
CurMBB->addInstruction(std::move(alloc_instr));
|
||||
|
||||
// 2b. 存储每个栈参数
|
||||
for (size_t i = 8; i < num_operands; ++i) {
|
||||
DAGNode* arg_node = node->operands[i];
|
||||
unsigned src_vreg;
|
||||
|
||||
// 准备源寄存器
|
||||
if (arg_node->kind == DAGNode::CONSTANT) {
|
||||
// 如果是常量,先加载到临时寄存器
|
||||
src_vreg = getNewVReg();
|
||||
auto const_val = dynamic_cast<ConstantValue*>(arg_node->value);
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
} else {
|
||||
src_vreg = getVReg(arg_node->value);
|
||||
}
|
||||
|
||||
// 计算在栈上的偏移量
|
||||
int offset = (i - 8) * 8;
|
||||
|
||||
// 生成 sd 指令
|
||||
auto sd_instr = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
sd_instr->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
sd_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::SP),
|
||||
std::make_unique<ImmOperand>(offset)
|
||||
));
|
||||
CurMBB->addInstruction(std::move(sd_instr));
|
||||
}
|
||||
}
|
||||
|
||||
auto call_instr = std::make_unique<MachineInstr>(RVOpcodes::CALL);
|
||||
call_instr->addOperand(std::make_unique<LabelOperand>(call->getCallee()->getName()));
|
||||
CurMBB->addInstruction(std::move(call_instr));
|
||||
|
||||
if (num_operands > 8) {
|
||||
size_t stack_arg_count = num_operands - 8;
|
||||
int stack_space = stack_arg_count * 8;
|
||||
|
||||
auto dealloc_instr = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
dealloc_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
dealloc_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
dealloc_instr->addOperand(std::make_unique<ImmOperand>(stack_space));
|
||||
CurMBB->addInstruction(std::move(dealloc_instr));
|
||||
}
|
||||
// 处理返回值,从a0移动到目标虚拟寄存器
|
||||
if (!call->getType()->isVoid()) {
|
||||
auto mv_instr = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv_instr->addOperand(std::make_unique<RegOperand>(getVReg(call)));
|
||||
@ -423,6 +609,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
auto ret_inst_ir = dynamic_cast<ReturnInst*>(node->value);
|
||||
if (ret_inst_ir && ret_inst_ir->hasReturnValue()) {
|
||||
Value* ret_val = ret_inst_ir->getReturnValue();
|
||||
// [V2优点] 在RETURN节点内加载常量返回值
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(ret_val)) {
|
||||
auto li_instr = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
|
||||
@ -435,36 +622,121 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(mv_instr));
|
||||
}
|
||||
}
|
||||
// [V1设计保留] 函数尾声(epilogue)不由RETURN节点生成,
|
||||
// 而是由后续的AsmPrinter或其它Pass统一处理,这是一种常见且有效的模块化设计。
|
||||
auto ret_mi = std::make_unique<MachineInstr>(RVOpcodes::RET);
|
||||
CurMBB->addInstruction(std::move(ret_mi));
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::BRANCH: {
|
||||
if (auto cond_br = dynamic_cast<CondBrInst*>(node->value)) {
|
||||
// 处理条件分支
|
||||
if (auto cond_br = dynamic_cast<CondBrInst*>(node->value)) {
|
||||
Value* condition = cond_br->getCondition();
|
||||
auto then_bb_name = cond_br->getThenBlock()->getName();
|
||||
auto else_bb_name = cond_br->getElseBlock()->getName();
|
||||
|
||||
// [优化] 检查分支条件是否为编译期常量
|
||||
if (auto const_cond = dynamic_cast<ConstantValue*>(condition)) {
|
||||
// 如果条件是常量,直接生成一个无条件跳转J,而不是BNE
|
||||
if (const_cond->getInt() != 0) { // 条件为 true
|
||||
auto j_instr = std::make_unique<MachineInstr>(RVOpcodes::J);
|
||||
j_instr->addOperand(std::make_unique<LabelOperand>(then_bb_name));
|
||||
CurMBB->addInstruction(std::move(j_instr));
|
||||
} else { // 条件为 false
|
||||
auto j_instr = std::make_unique<MachineInstr>(RVOpcodes::J);
|
||||
j_instr->addOperand(std::make_unique<LabelOperand>(else_bb_name));
|
||||
CurMBB->addInstruction(std::move(j_instr));
|
||||
}
|
||||
}
|
||||
// 如果条件不是常量,则执行标准流程
|
||||
else {
|
||||
// [修复] 为条件变量生成加载指令(如果它是常量的话,尽管上面已经处理了)
|
||||
// 这一步是为了逻辑完整,以防有其他类型的常量没有被捕获
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(condition)) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(const_val)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
|
||||
auto cond_vreg = getVReg(condition);
|
||||
|
||||
// 生成 bne cond, zero, then_label (如果cond不为0,则跳转到then)
|
||||
auto br_instr = std::make_unique<MachineInstr>(RVOpcodes::BNE);
|
||||
br_instr->addOperand(std::make_unique<RegOperand>(getVReg(cond_br->getCondition())));
|
||||
br_instr->addOperand(std::make_unique<RegOperand>(cond_vreg));
|
||||
br_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::ZERO));
|
||||
br_instr->addOperand(std::make_unique<LabelOperand>(cond_br->getThenBlock()->getName()));
|
||||
br_instr->addOperand(std::make_unique<LabelOperand>(then_bb_name));
|
||||
CurMBB->addInstruction(std::move(br_instr));
|
||||
} else if (auto uncond_br = dynamic_cast<UncondBrInst*>(node->value)) {
|
||||
|
||||
// 为else分支生成无条件跳转 (后续Pass可以优化掉不必要的跳转)
|
||||
auto j_instr = std::make_unique<MachineInstr>(RVOpcodes::J);
|
||||
j_instr->addOperand(std::make_unique<LabelOperand>(uncond_br->getBlock()->getName()));
|
||||
j_instr->addOperand(std::make_unique<LabelOperand>(else_bb_name));
|
||||
CurMBB->addInstruction(std::move(j_instr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 处理无条件分支
|
||||
else if (auto uncond_br = dynamic_cast<UncondBrInst*>(node->value)) {
|
||||
auto j_instr = std::make_unique<MachineInstr>(RVOpcodes::J);
|
||||
j_instr->addOperand(std::make_unique<LabelOperand>(uncond_br->getBlock()->getName()));
|
||||
CurMBB->addInstruction(std::move(j_instr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::MEMSET: {
|
||||
// [V1设计保留] Memset的核心展开逻辑在虚拟寄存器层面是正确的,无需修改。
|
||||
// 之前的bug是由于其输入(地址、值、大小)的虚拟寄存器未被正确初始化。
|
||||
// 在修复了CONSTANT/ALLOCA_ADDR的加载问题后,此处的逻辑现在可以正常工作。
|
||||
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] selectNode-MEMSET: Processing MEMSET node." << std::endl;
|
||||
}
|
||||
auto memset = dynamic_cast<MemsetInst*>(node->value);
|
||||
Value* val_to_set = memset->getValue();
|
||||
Value* size_to_set = memset->getSize();
|
||||
Value* ptr_val = memset->getPointer();
|
||||
auto dest_addr_vreg = getVReg(ptr_val);
|
||||
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(val_to_set)) {
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] selectNode-MEMSET: Found constant 'value' operand (" << const_val->getInt() << "). Generating LI." << std::endl;
|
||||
}
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(const_val)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
if (auto const_size = dynamic_cast<ConstantValue*>(size_to_set)) {
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] selectNode-MEMSET: Found constant 'size' operand (" << const_size->getInt() << "). Generating LI." << std::endl;
|
||||
}
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(const_size)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_size->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
if (auto alloca = dynamic_cast<AllocaInst*>(ptr_val)) {
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] selectNode-MEMSET: Found 'pointer' operand is an AllocaInst. Generating FRAME_ADDR." << std::endl;
|
||||
}
|
||||
// 生成新的伪指令来获取栈地址
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FRAME_ADDR);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_addr_vreg)); // 目标虚拟寄存器
|
||||
instr->addOperand(std::make_unique<RegOperand>(getVReg(alloca))); // 源AllocaInst
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
}
|
||||
auto r_dest_addr = getVReg(memset->getPointer());
|
||||
auto r_num_bytes = getVReg(memset->getSize());
|
||||
auto r_value_byte = getVReg(memset->getValue());
|
||||
|
||||
// 为memset内部逻辑创建新的临时虚拟寄存器
|
||||
auto r_counter = getNewVReg();
|
||||
auto r_end_addr = getNewVReg();
|
||||
auto r_current_addr = getNewVReg();
|
||||
auto r_temp_val = getNewVReg();
|
||||
|
||||
// 定义一系列lambda表达式来简化指令创建
|
||||
auto add_instr = [&](RVOpcodes op, unsigned rd, unsigned rs1, unsigned rs2) {
|
||||
auto i = std::make_unique<MachineInstr>(op);
|
||||
i->addOperand(std::make_unique<RegOperand>(rd));
|
||||
@ -503,12 +775,14 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(i));
|
||||
};
|
||||
|
||||
// 生成唯一的循环标签
|
||||
int unique_id = this->local_label_counter++;
|
||||
std::string loop_start_label = MFunc->getName() + "_memset_loop_start_" + std::to_string(unique_id);
|
||||
std::string loop_end_label = MFunc->getName() + "_memset_loop_end_" + std::to_string(unique_id);
|
||||
std::string remainder_label = MFunc->getName() + "_memset_remainder_" + std::to_string(unique_id);
|
||||
std::string done_label = MFunc->getName() + "_memset_done_" + std::to_string(unique_id);
|
||||
|
||||
// 构造64位的填充值
|
||||
addi_instr(RVOpcodes::ANDI, r_temp_val, r_value_byte, 255);
|
||||
addi_instr(RVOpcodes::SLLI, r_value_byte, r_temp_val, 8);
|
||||
add_instr(RVOpcodes::OR, r_temp_val, r_temp_val, r_value_byte);
|
||||
@ -516,6 +790,8 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
add_instr(RVOpcodes::OR, r_temp_val, r_temp_val, r_value_byte);
|
||||
addi_instr(RVOpcodes::SLLI, r_value_byte, r_temp_val, 32);
|
||||
add_instr(RVOpcodes::OR, r_temp_val, r_temp_val, r_value_byte);
|
||||
|
||||
// 计算循环边界
|
||||
add_instr(RVOpcodes::ADD, r_end_addr, r_dest_addr, r_num_bytes);
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(r_current_addr));
|
||||
@ -523,21 +799,128 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
addi_instr(RVOpcodes::ANDI, r_counter, r_num_bytes, -8);
|
||||
add_instr(RVOpcodes::ADD, r_counter, r_dest_addr, r_counter);
|
||||
|
||||
// 8字节主循环
|
||||
label_instr(loop_start_label);
|
||||
branch_instr(RVOpcodes::BGEU, r_current_addr, r_counter, loop_end_label);
|
||||
store_instr(RVOpcodes::SD, r_temp_val, r_current_addr, 0);
|
||||
addi_instr(RVOpcodes::ADDI, r_current_addr, r_current_addr, 8);
|
||||
jump_instr(loop_start_label);
|
||||
|
||||
// 1字节收尾循环
|
||||
label_instr(loop_end_label);
|
||||
label_instr(remainder_label);
|
||||
branch_instr(RVOpcodes::BGEU, r_current_addr, r_end_addr, done_label);
|
||||
store_instr(RVOpcodes::SB, r_temp_val, r_current_addr, 0);
|
||||
addi_instr(RVOpcodes::ADDI, r_current_addr, r_current_addr, 1);
|
||||
jump_instr(remainder_label);
|
||||
|
||||
label_instr(done_label);
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::GET_ELEMENT_PTR: {
|
||||
auto gep = dynamic_cast<GetElementPtrInst*>(node->value);
|
||||
auto result_vreg = getVReg(gep);
|
||||
|
||||
// --- Step 1: 获取基地址 (此部分逻辑正确,保持不变) ---
|
||||
auto base_ptr_node = node->operands[0];
|
||||
auto current_addr_vreg = getNewVReg();
|
||||
|
||||
if (auto alloca_base = dynamic_cast<AllocaInst*>(base_ptr_node->value)) {
|
||||
auto frame_addr_instr = std::make_unique<MachineInstr>(RVOpcodes::FRAME_ADDR);
|
||||
frame_addr_instr->addOperand(std::make_unique<RegOperand>(current_addr_vreg));
|
||||
frame_addr_instr->addOperand(std::make_unique<RegOperand>(getVReg(alloca_base)));
|
||||
CurMBB->addInstruction(std::move(frame_addr_instr));
|
||||
} else if (auto global_base = dynamic_cast<GlobalValue*>(base_ptr_node->value)) {
|
||||
auto la_instr = std::make_unique<MachineInstr>(RVOpcodes::LA);
|
||||
la_instr->addOperand(std::make_unique<RegOperand>(current_addr_vreg));
|
||||
la_instr->addOperand(std::make_unique<LabelOperand>(global_base->getName()));
|
||||
CurMBB->addInstruction(std::move(la_instr));
|
||||
} else {
|
||||
auto base_vreg = getVReg(base_ptr_node->value);
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(current_addr_vreg));
|
||||
mv->addOperand(std::make_unique<RegOperand>(base_vreg));
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
}
|
||||
|
||||
// --- Step 2: [最终权威版] 遵循LLVM GEP语义迭代计算地址 ---
|
||||
|
||||
// 初始被索引的类型,是基指针指向的那个类型 (例如, [2 x i32])
|
||||
Type* current_type = gep->getBasePointer()->getType()->as<PointerType>()->getBaseType();
|
||||
|
||||
// 迭代处理 GEP 的每一个索引
|
||||
for (size_t i = 0; i < gep->getNumIndices(); ++i) {
|
||||
Value* indexValue = gep->getIndex(i);
|
||||
|
||||
// GEP的第一个索引以整个 `current_type` 的大小为步长。
|
||||
// 后续的索引则以 `current_type` 的元素大小为步长。
|
||||
// 这一步是计算地址偏移的关键。
|
||||
unsigned stride = getTypeSizeInBytes(current_type);
|
||||
|
||||
// 如果步长为0(例如对一个void类型或空结构体索引),则不产生任何偏移
|
||||
if (stride != 0) {
|
||||
// --- 为当前索引和步长生成偏移计算指令 ---
|
||||
auto offset_vreg = getNewVReg();
|
||||
auto index_vreg = getVReg(indexValue);
|
||||
|
||||
// 如果索引是常量,先用 LI 指令加载到虚拟寄存器
|
||||
if (auto const_index = dynamic_cast<ConstantValue*>(indexValue)) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(index_vreg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_index->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
|
||||
// 优化:如果步长是1,可以直接移动(MV)作为偏移量,无需乘法
|
||||
if (stride == 1) {
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(offset_vreg));
|
||||
mv->addOperand(std::make_unique<RegOperand>(index_vreg));
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
} else {
|
||||
// 步长不为1,需要生成乘法指令
|
||||
auto size_vreg = getNewVReg();
|
||||
auto li_size = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li_size->addOperand(std::make_unique<RegOperand>(size_vreg));
|
||||
li_size->addOperand(std::make_unique<ImmOperand>(stride));
|
||||
CurMBB->addInstruction(std::move(li_size));
|
||||
|
||||
auto mul = std::make_unique<MachineInstr>(RVOpcodes::MULW);
|
||||
mul->addOperand(std::make_unique<RegOperand>(offset_vreg));
|
||||
mul->addOperand(std::make_unique<RegOperand>(index_vreg));
|
||||
mul->addOperand(std::make_unique<RegOperand>(size_vreg));
|
||||
CurMBB->addInstruction(std::move(mul));
|
||||
}
|
||||
|
||||
// 将计算出的偏移量累加到当前地址上
|
||||
auto add = std::make_unique<MachineInstr>(RVOpcodes::ADD);
|
||||
add->addOperand(std::make_unique<RegOperand>(current_addr_vreg));
|
||||
add->addOperand(std::make_unique<RegOperand>(current_addr_vreg));
|
||||
add->addOperand(std::make_unique<RegOperand>(offset_vreg));
|
||||
CurMBB->addInstruction(std::move(add));
|
||||
}
|
||||
|
||||
// --- 为下一次迭代更新类型:深入一层 ---
|
||||
if (auto array_type = current_type->as<ArrayType>()) {
|
||||
current_type = array_type->getElementType();
|
||||
} else if (auto ptr_type = current_type->as<PointerType>()) {
|
||||
// 这种情况不应该在第二次迭代后发生,但为了逻辑健壮性保留
|
||||
current_type = ptr_type->getBaseType();
|
||||
}
|
||||
// 如果`current_type`已经是i32等基本类型,它会保持不变,
|
||||
// 但下一次循环如果还有索引,`getTypeSizeInBytes(i32)`仍然能正确计算步长。
|
||||
}
|
||||
|
||||
// --- Step 3: 将最终计算出的地址存入GEP的目标虚拟寄存器 (保持不变) ---
|
||||
auto final_mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
final_mv->addOperand(std::make_unique<RegOperand>(result_vreg));
|
||||
final_mv->addOperand(std::make_unique<RegOperand>(current_addr_vreg));
|
||||
CurMBB->addInstruction(std::move(final_mv));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported DAGNode kind in ISel");
|
||||
}
|
||||
@ -560,6 +943,7 @@ RISCv64ISel::DAGNode* RISCv64ISel::create_node(int kind_int, Value* val, std::ma
|
||||
}
|
||||
|
||||
RISCv64ISel::DAGNode* RISCv64ISel::get_operand_node(Value* val_ir, std::map<Value*, DAGNode*>& value_to_node, std::vector<std::unique_ptr<DAGNode>>& nodes_storage) {
|
||||
// 规则1:如果这个Value已经有对应的节点,直接返回
|
||||
if (value_to_node.count(val_ir)) {
|
||||
return value_to_node[val_ir];
|
||||
} else if (dynamic_cast<ConstantValue*>(val_ir)) {
|
||||
@ -568,7 +952,14 @@ RISCv64ISel::DAGNode* RISCv64ISel::get_operand_node(Value* val_ir, std::map<Valu
|
||||
return create_node(DAGNode::CONSTANT, val_ir, value_to_node, nodes_storage);
|
||||
} else if (dynamic_cast<AllocaInst*>(val_ir)) {
|
||||
return create_node(DAGNode::ALLOCA_ADDR, val_ir, value_to_node, nodes_storage);
|
||||
} else if (dynamic_cast<Argument*>(val_ir)) {
|
||||
// Argument 是一个叶子节点,它代表一个在函数入口就可用的值。
|
||||
return create_node(DAGNode::ARGUMENT, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
// 默认行为:如果一个操作数不是上面任何一种叶子节点,
|
||||
// 并且没有在value_to_node中找到(意味着它不是由另一条指令定义的),
|
||||
// 这是一个逻辑问题。但为了保持向前兼容,我们暂时保留旧的LOAD行为,
|
||||
// 尽管在修复Argument后,它不应该再被错误触发。
|
||||
return create_node(DAGNode::LOAD, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
|
||||
@ -590,6 +981,27 @@ std::vector<std::unique_ptr<RISCv64ISel::DAGNode>> RISCv64ISel::build_dag(BasicB
|
||||
memset_node->operands.push_back(get_operand_node(memset->getBegin(), value_to_node, nodes_storage));
|
||||
memset_node->operands.push_back(get_operand_node(memset->getSize(), value_to_node, nodes_storage));
|
||||
memset_node->operands.push_back(get_operand_node(memset->getValue(), value_to_node, nodes_storage));
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] build_dag: Created MEMSET node for: " << memset->getName() << std::endl;
|
||||
for (size_t i = 0; i < memset_node->operands.size(); ++i) {
|
||||
std::cout << " -> Operand " << i << " has kind: " << memset_node->operands[i]->kind << std::endl;
|
||||
}
|
||||
}
|
||||
} else if (auto gep = dynamic_cast<GetElementPtrInst*>(inst)) {
|
||||
// 如果这个GEP指令已经创建过节点,则跳过
|
||||
if(value_to_node.count(gep)) continue;
|
||||
|
||||
// 创建一个新的 GET_ELEMENT_PTR 类型的节点
|
||||
auto gep_node = create_node(DAGNode::GET_ELEMENT_PTR, gep, value_to_node, nodes_storage);
|
||||
|
||||
// 第一个操作数是基指针(即数组本身)
|
||||
gep_node->operands.push_back(get_operand_node(gep->getBasePointer(), value_to_node, nodes_storage));
|
||||
|
||||
// 依次添加所有索引作为后续的操作数
|
||||
for (auto index : gep->getIndices()) {
|
||||
// [修复] 从 Use 对象中获取真正的 Value*
|
||||
gep_node->operands.push_back(get_operand_node(index->getValue(), value_to_node, nodes_storage));
|
||||
}
|
||||
} else if (auto load = dynamic_cast<LoadInst*>(inst)) {
|
||||
auto load_node = create_node(DAGNode::LOAD, load, value_to_node, nodes_storage);
|
||||
load_node->operands.push_back(get_operand_node(load->getPointer(), value_to_node, nodes_storage));
|
||||
@ -632,4 +1044,136 @@ std::vector<std::unique_ptr<RISCv64ISel::DAGNode>> RISCv64ISel::build_dag(BasicB
|
||||
return nodes_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算一个类型在内存中占用的字节数。
|
||||
* @param type 需要计算大小的IR类型。
|
||||
* @return 该类型占用的字节数。
|
||||
*/
|
||||
unsigned RISCv64ISel::getTypeSizeInBytes(Type* type) {
|
||||
if (!type) {
|
||||
assert(false && "Cannot get size of a null type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (type->getKind()) {
|
||||
// 对于SysY语言,基本类型int和float都占用4字节
|
||||
case Type::kInt:
|
||||
case Type::kFloat:
|
||||
return 4;
|
||||
|
||||
// 指针类型在RISC-V 64位架构下占用8字节
|
||||
// 虽然SysY没有'int*'语法,但数组变量在IR层面本身就是指针类型
|
||||
case Type::kPointer:
|
||||
return 8;
|
||||
|
||||
// 数组类型的总大小 = 元素数量 * 单个元素的大小
|
||||
case Type::kArray: {
|
||||
auto arrayType = type->as<ArrayType>();
|
||||
// 递归调用以计算元素大小
|
||||
return arrayType->getNumElements() * getTypeSizeInBytes(arrayType->getElementType());
|
||||
}
|
||||
|
||||
// 其他类型,如Void, Label等不占用栈空间,或者不应该出现在这里
|
||||
default:
|
||||
// 如果遇到未处理的类型,触发断言,方便调试
|
||||
assert(false && "Unsupported type for size calculation.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// [新] 打印DAG图以供调试的辅助函数
|
||||
void RISCv64ISel::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, const std::string& bb_name) {
|
||||
// 检查是否有DEBUG宏或者全局变量,避免在非调试模式下打印
|
||||
// if (!DEBUG) return;
|
||||
|
||||
std::cerr << "=== DAG for Basic Block: " << bb_name << " ===\n";
|
||||
std::set<DAGNode*> visited;
|
||||
|
||||
// 为节点分配临时ID,方便阅读
|
||||
std::map<DAGNode*, int> node_to_id;
|
||||
int current_id = 0;
|
||||
for (const auto& node_ptr : dag) {
|
||||
node_to_id[node_ptr.get()] = current_id++;
|
||||
}
|
||||
|
||||
// 将NodeKind枚举转换为字符串的辅助函数
|
||||
auto get_kind_string = [](DAGNode::NodeKind kind) {
|
||||
switch (kind) {
|
||||
case DAGNode::ARGUMENT: return "ARGUMENT";
|
||||
case DAGNode::CONSTANT: return "CONSTANT";
|
||||
case DAGNode::LOAD: return "LOAD";
|
||||
case DAGNode::STORE: return "STORE";
|
||||
case DAGNode::BINARY: return "BINARY";
|
||||
case DAGNode::CALL: return "CALL";
|
||||
case DAGNode::RETURN: return "RETURN";
|
||||
case DAGNode::BRANCH: return "BRANCH";
|
||||
case DAGNode::ALLOCA_ADDR: return "ALLOCA_ADDR";
|
||||
case DAGNode::UNARY: return "UNARY";
|
||||
case DAGNode::MEMSET: return "MEMSET";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
};
|
||||
|
||||
// 递归打印节点的lambda表达式
|
||||
std::function<void(DAGNode*, int)> print_node =
|
||||
[&](DAGNode* node, int indent) {
|
||||
if (!node) return;
|
||||
|
||||
std::string current_indent(indent, ' ');
|
||||
int node_id = node_to_id.count(node) ? node_to_id[node] : -1;
|
||||
|
||||
std::cerr << current_indent << "Node#" << node_id << ": " << get_kind_string(node->kind);
|
||||
|
||||
// 尝试打印关联的虚拟寄存器
|
||||
if (node->value && vreg_map.count(node->value)) {
|
||||
std::cerr << " (vreg: %vreg" << vreg_map.at(node->value) << ")";
|
||||
}
|
||||
|
||||
// 打印关联的IR Value信息
|
||||
if (node->value) {
|
||||
std::cerr << " [";
|
||||
if (auto inst = dynamic_cast<Instruction*>(node->value)) {
|
||||
std::cerr << inst->getKindString();
|
||||
if (!inst->getName().empty()) {
|
||||
std::cerr << "(" << inst->getName() << ")";
|
||||
}
|
||||
} else if (auto constant = dynamic_cast<ConstantValue*>(node->value)) {
|
||||
std::cerr << "Const(" << constant->getInt() << ")";
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(node->value)) {
|
||||
std::cerr << "Global(" << global->getName() << ")";
|
||||
} else if (auto alloca = dynamic_cast<AllocaInst*>(node->value)) {
|
||||
std::cerr << "Alloca(" << alloca->getName() << ")";
|
||||
}
|
||||
std::cerr << "]";
|
||||
}
|
||||
std::cerr << "\n";
|
||||
|
||||
if (visited.count(node)) {
|
||||
std::cerr << current_indent << " (已打印过子节点)\n";
|
||||
return;
|
||||
}
|
||||
visited.insert(node);
|
||||
|
||||
if (!node->operands.empty()) {
|
||||
std::cerr << current_indent << " Operands:\n";
|
||||
for (auto operand : node->operands) {
|
||||
print_node(operand, indent + 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 从根节点(没有用户的节点,或有副作用的节点)开始打印
|
||||
for (const auto& node_ptr : dag) {
|
||||
if (node_ptr->users.empty() ||
|
||||
node_ptr->kind == DAGNode::STORE ||
|
||||
node_ptr->kind == DAGNode::RETURN ||
|
||||
node_ptr->kind == DAGNode::BRANCH ||
|
||||
node_ptr->kind == DAGNode::MEMSET)
|
||||
{
|
||||
print_node(node_ptr.get(), 0);
|
||||
}
|
||||
}
|
||||
std::cerr << "======================================\n\n";
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
6
src/RISCv64LLIR.cpp
Normal file
6
src/RISCv64LLIR.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "RISCv64LLIR.h"
|
||||
#include <vector>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
}
|
||||
@ -51,4 +51,103 @@ void PostRA_Scheduler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
// std::cout << "Running Post-RA Local Scheduler..." << std::endl;
|
||||
}
|
||||
|
||||
void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
// 【最终方案】: 此 Pass 负责分析、分配栈空间并插入 callee-saved 寄存器的保存/恢复指令。
|
||||
// 它通过与 FrameInfo 协作,确保为 callee-saved 寄存器分配的空间与局部变量/溢出槽的空间不冲突。
|
||||
// 这样做可以使生成的 sd/ld 指令能被后续的优化 Pass (如 PostRA-Scheduler) 处理。
|
||||
|
||||
StackFrameInfo& frame_info = mfunc->getFrameInfo();
|
||||
std::set<PhysicalReg> used_callee_saved;
|
||||
|
||||
// 1. 扫描所有指令,找出被使用的s寄存器 (s1-s11)
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto& instr : mbb->getInstructions()) {
|
||||
for (auto& op : instr->getOperands()) {
|
||||
auto check_and_insert_reg = [&](RegOperand* reg_op) {
|
||||
if (!reg_op->isVirtual()) {
|
||||
PhysicalReg preg = reg_op->getPReg();
|
||||
if (preg >= PhysicalReg::S1 && preg <= PhysicalReg::S11) {
|
||||
used_callee_saved.insert(preg);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
check_and_insert_reg(static_cast<RegOperand*>(op.get()));
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
check_and_insert_reg(static_cast<MemOperand*>(op.get())->getBase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (used_callee_saved.empty()) {
|
||||
frame_info.callee_saved_size = 0; // 确保大小被初始化
|
||||
return; // 无需操作
|
||||
}
|
||||
|
||||
// 2. 计算为 callee-saved 寄存器分配的栈空间
|
||||
// 这里的关键是,偏移的基准点要在局部变量和溢出槽之下。
|
||||
int callee_saved_size = used_callee_saved.size() * 8;
|
||||
frame_info.callee_saved_size = callee_saved_size; // 将大小存入 FrameInfo
|
||||
|
||||
// 3. 计算无冲突的栈偏移
|
||||
// 栈向下增长,所以偏移是负数。
|
||||
// ra/s0 占用 -8 和 -16。局部变量和溢出区在它们之下。callee-saved 区在更下方。
|
||||
// 我们使用相对于 s0 的偏移。s0 将指向栈顶 (sp + total_size)。
|
||||
int base_offset = -16 - frame_info.locals_size - frame_info.spill_size;
|
||||
|
||||
// 为了栈帧布局确定性,对寄存器进行排序
|
||||
std::vector<PhysicalReg> sorted_regs(used_callee_saved.begin(), used_callee_saved.end());
|
||||
std::sort(sorted_regs.begin(), sorted_regs.end());
|
||||
|
||||
// 4. 在函数序言插入保存指令
|
||||
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
|
||||
auto& entry_instrs = entry_block->getInstructions();
|
||||
auto prologue_end = entry_instrs.begin();
|
||||
|
||||
// 找到序言结束的位置(通常是addi s0, sp, size之后,但为了让优化器看到,我们插在更前面)
|
||||
// 合理的位置是在 IR 指令开始之前,即在任何非序言指令(如第一个标签)之前。
|
||||
// 为简单起见,我们直接插入到块的开头,后续重排 pass 会处理。
|
||||
// (更优的实现会寻找一个特定的插入点)
|
||||
|
||||
int current_offset = base_offset;
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
auto sd = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
sd->addOperand(std::make_unique<RegOperand>(reg));
|
||||
sd->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0), // 基址为帧指针 s0
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
// 从头部插入,但要放在函数标签之后
|
||||
entry_instrs.insert(entry_instrs.begin() + 1, std::move(sd));
|
||||
current_offset -= 8;
|
||||
}
|
||||
|
||||
// 5. 【已修复】在函数结尾(ret之前)插入恢复指令,使用反向遍历来避免迭代器失效
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
// 使用手动控制的反向循环
|
||||
for (auto it = mbb->getInstructions().end(); it != mbb->getInstructions().begin(); ) {
|
||||
// 在循环开始时就递减迭代器
|
||||
--it;
|
||||
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
int current_offset_load = base_offset;
|
||||
// 以相同的顺序恢复(从 s1 开始)
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
auto ld = std::make_unique<MachineInstr>(RVOpcodes::LD);
|
||||
ld->addOperand(std::make_unique<RegOperand>(reg));
|
||||
ld->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(current_offset_load)
|
||||
));
|
||||
// 在 'it' (即 RET 指令) 之前插入。
|
||||
// 因为我们是反向遍历,所以这不会影响下一次循环的 'it'。
|
||||
mbb->getInstructions().insert(it, std::move(ld));
|
||||
current_offset_load -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -2,6 +2,8 @@
|
||||
#include "RISCv64ISel.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream> // For DEBUG output
|
||||
#include <cassert> // For assert
|
||||
|
||||
namespace sysy {
|
||||
|
||||
@ -11,133 +13,337 @@ RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) {
|
||||
PhysicalReg::T4, PhysicalReg::T5, PhysicalReg::T6,
|
||||
PhysicalReg::A0, PhysicalReg::A1, PhysicalReg::A2, PhysicalReg::A3,
|
||||
PhysicalReg::A4, PhysicalReg::A5, PhysicalReg::A6, PhysicalReg::A7,
|
||||
PhysicalReg::S0, PhysicalReg::S1, PhysicalReg::S2, PhysicalReg::S3,
|
||||
// PhysicalReg::S0,
|
||||
PhysicalReg::S1, PhysicalReg::S2, PhysicalReg::S3,
|
||||
PhysicalReg::S4, PhysicalReg::S5, PhysicalReg::S6, PhysicalReg::S7,
|
||||
PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11,
|
||||
};
|
||||
|
||||
// 映射物理寄存器到特殊的虚拟寄存器ID,用于干扰图中的物理寄存器节点
|
||||
// 确保这些特殊ID不会与vreg_counter生成的常规虚拟寄存器ID冲突
|
||||
for (PhysicalReg preg : allocable_int_regs) {
|
||||
preg_to_vreg_id_map[preg] = static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID) + static_cast<unsigned>(preg);
|
||||
}
|
||||
}
|
||||
|
||||
// 寄存器分配的主入口点
|
||||
void RISCv64RegAlloc::run() {
|
||||
eliminateFrameIndices();
|
||||
analyzeLiveness();
|
||||
buildInterferenceGraph();
|
||||
colorGraph();
|
||||
rewriteFunction();
|
||||
// 阶段 1: 处理函数调用约定(参数寄存器预着色)
|
||||
handleCallingConvention();
|
||||
// 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移)
|
||||
eliminateFrameIndices();
|
||||
// 阶段 3: 活跃性分析
|
||||
analyzeLiveness();
|
||||
// 阶段 4: 构建干扰图(包含CALL指令对调用者保存寄存器的影响)
|
||||
buildInterferenceGraph();
|
||||
// 阶段 5: 图着色算法分配物理寄存器
|
||||
colorGraph();
|
||||
// 阶段 6: 重写函数(插入溢出/填充代码,替换虚拟寄存器为物理寄存器)
|
||||
rewriteFunction();
|
||||
}
|
||||
|
||||
void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
int current_offset = 0;
|
||||
/**
|
||||
* @brief 处理调用约定,预先为函数参数分配物理寄存器。
|
||||
*/
|
||||
void RISCv64RegAlloc::handleCallingConvention() {
|
||||
Function* F = MFunc->getFunc();
|
||||
RISCv64ISel* isel = MFunc->getISel();
|
||||
|
||||
// 获取函数的Argument对象列表
|
||||
if (F) {
|
||||
auto& args = F->getArguments();
|
||||
// RISC-V RV64G调用约定:前8个整型/指针参数通过 a0-a7 传递
|
||||
int arg_idx = 0;
|
||||
// 遍历 AllocaInst* 列表
|
||||
for (Argument* arg : args) {
|
||||
if (arg_idx >= 8) {
|
||||
break;
|
||||
}
|
||||
// 获取该 Argument 对象对应的虚拟寄存器ID
|
||||
// 通过 MachineFunction -> RISCv64ISel -> vreg_map 来获取
|
||||
const auto& vreg_map_from_isel = MFunc->getISel()->getVRegMap();
|
||||
assert(vreg_map_from_isel.count(arg) && "Argument not found in ISel's vreg_map!");
|
||||
// 1. 获取该 Argument 对象对应的虚拟寄存器
|
||||
unsigned vreg = isel->getVReg(arg);
|
||||
|
||||
// 2. 根据参数索引,确定对应的物理寄存器 (a0, a1, ...)
|
||||
auto preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + arg_idx);
|
||||
|
||||
// 3. 在 color_map 中,将 vreg "预着色" 为对应的物理寄存器
|
||||
color_map[vreg] = preg;
|
||||
|
||||
arg_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 消除帧索引,为局部变量和栈参数分配栈偏移量,并展开伪指令。
|
||||
*/
|
||||
void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
// 初始偏移量,为保存ra和s0留出空间。
|
||||
// 假设序言是 addi sp, sp, -stack_size; sd ra, stack_size-8(sp); sd s0, stack_size-16(sp);
|
||||
int current_offset = 16;
|
||||
|
||||
Function* F = MFunc->getFunc();
|
||||
RISCv64ISel* isel = MFunc->getISel();
|
||||
|
||||
// 在处理局部变量前,首先为栈参数计算偏移量。
|
||||
if (F) {
|
||||
int arg_idx = 0;
|
||||
for (Argument* arg : F->getArguments()) {
|
||||
// 我们只关心第8个索引及之后的参数(即第9个参数开始)
|
||||
if (arg_idx >= 8) {
|
||||
// 计算偏移量:第一个栈参数(idx=8)在0(s0),第二个(idx=9)在8(s0),以此类推。
|
||||
int offset = (arg_idx - 8) * 8;
|
||||
unsigned vreg = isel->getVReg(arg);
|
||||
|
||||
// 将这个vreg和它的栈偏移存入map。
|
||||
// 我们可以复用alloca_offsets,因为它们都代表“vreg到栈偏移”的映射。
|
||||
frame_info.alloca_offsets[vreg] = offset;
|
||||
}
|
||||
arg_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理局部变量
|
||||
// 遍历AllocaInst来计算局部变量所需的总空间
|
||||
for (auto& bb : F->getBasicBlocks()) {
|
||||
for (auto& inst : bb->getInstructions()) {
|
||||
if (auto alloca = dynamic_cast<AllocaInst*>(inst.get())) {
|
||||
int size = 4;
|
||||
if (!alloca->getDims().empty()) {
|
||||
int num_elements = 1;
|
||||
for (const auto& dim_use : alloca->getDims()) {
|
||||
if (auto const_dim = dynamic_cast<ConstantValue*>(dim_use->getValue())) {
|
||||
num_elements *= const_dim->getInt();
|
||||
}
|
||||
}
|
||||
size *= num_elements;
|
||||
}
|
||||
// 获取Alloca指令指向的类型 (例如 alloca i32* 中,获取 i32)
|
||||
Type* allocated_type = alloca->getType()->as<PointerType>()->getBaseType();
|
||||
int size = getTypeSizeInBytes(allocated_type);
|
||||
|
||||
// RISC-V要求栈地址8字节对齐
|
||||
size = (size + 7) & ~7;
|
||||
if (size == 0) size = 8; // 至少分配8字节
|
||||
|
||||
current_offset += size;
|
||||
unsigned alloca_vreg = isel->getVReg(alloca);
|
||||
// 局部变量使用相对于s0的负向偏移
|
||||
frame_info.alloca_offsets[alloca_vreg] = -current_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
frame_info.locals_size = current_offset;
|
||||
|
||||
// 遍历所有机器指令,将伪指令展开为真实指令
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
std::vector<std::unique_ptr<MachineInstr>> new_instructions;
|
||||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||||
if (instr_ptr->getOpcode() == RVOpcodes::FRAME_LOAD) {
|
||||
RVOpcodes opcode = instr_ptr->getOpcode();
|
||||
|
||||
// --- MODIFICATION START: 处理区分宽度的伪指令 ---
|
||||
if (opcode == RVOpcodes::FRAME_LOAD_W || opcode == RVOpcodes::FRAME_LOAD_D) {
|
||||
// 确定要生成的真实加载指令是 lw 还是 ld
|
||||
RVOpcodes real_load_op = (opcode == RVOpcodes::FRAME_LOAD_W) ? RVOpcodes::LW : RVOpcodes::LD;
|
||||
|
||||
auto& operands = instr_ptr->getOperands();
|
||||
unsigned dest_vreg = static_cast<RegOperand*>(operands[0].get())->getVRegNum();
|
||||
unsigned alloca_vreg = static_cast<RegOperand*>(operands[1].get())->getVRegNum();
|
||||
int offset = frame_info.alloca_offsets.at(alloca_vreg);
|
||||
auto addr_vreg = isel->getNewVReg();
|
||||
|
||||
// 展开为: addi addr_vreg, s0, offset
|
||||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
addi->addOperand(std::make_unique<RegOperand>(addr_vreg));
|
||||
addi->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
addi->addOperand(std::make_unique<ImmOperand>(offset));
|
||||
new_instructions.push_back(std::move(addi));
|
||||
|
||||
auto lw = std::make_unique<MachineInstr>(RVOpcodes::LW);
|
||||
lw->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
lw->addOperand(std::make_unique<MemOperand>(
|
||||
// 展开为: lw/ld dest_vreg, 0(addr_vreg)
|
||||
auto load_instr = std::make_unique<MachineInstr>(real_load_op);
|
||||
load_instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
load_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(addr_vreg),
|
||||
std::make_unique<ImmOperand>(0)));
|
||||
new_instructions.push_back(std::move(lw));
|
||||
new_instructions.push_back(std::move(load_instr));
|
||||
|
||||
} else if (opcode == RVOpcodes::FRAME_STORE_W || opcode == RVOpcodes::FRAME_STORE_D) {
|
||||
// 确定要生成的真实存储指令是 sw 还是 sd
|
||||
RVOpcodes real_store_op = (opcode == RVOpcodes::FRAME_STORE_W) ? RVOpcodes::SW : RVOpcodes::SD;
|
||||
|
||||
} else if (instr_ptr->getOpcode() == RVOpcodes::FRAME_STORE) {
|
||||
auto& operands = instr_ptr->getOperands();
|
||||
unsigned src_vreg = static_cast<RegOperand*>(operands[0].get())->getVRegNum();
|
||||
unsigned alloca_vreg = static_cast<RegOperand*>(operands[1].get())->getVRegNum();
|
||||
int offset = frame_info.alloca_offsets.at(alloca_vreg);
|
||||
auto addr_vreg = isel->getNewVReg();
|
||||
|
||||
// 展开为: addi addr_vreg, s0, offset
|
||||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
addi->addOperand(std::make_unique<RegOperand>(addr_vreg));
|
||||
addi->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
addi->addOperand(std::make_unique<ImmOperand>(offset));
|
||||
new_instructions.push_back(std::move(addi));
|
||||
|
||||
auto sw = std::make_unique<MachineInstr>(RVOpcodes::SW);
|
||||
sw->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
sw->addOperand(std::make_unique<MemOperand>(
|
||||
// 展开为: sw/sd src_vreg, 0(addr_vreg)
|
||||
auto store_instr = std::make_unique<MachineInstr>(real_store_op);
|
||||
store_instr->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
store_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(addr_vreg),
|
||||
std::make_unique<ImmOperand>(0)));
|
||||
new_instructions.push_back(std::move(sw));
|
||||
new_instructions.push_back(std::move(store_instr));
|
||||
|
||||
} else if (instr_ptr->getOpcode() == RVOpcodes::FRAME_ADDR) {
|
||||
auto& operands = instr_ptr->getOperands();
|
||||
unsigned dest_vreg = static_cast<RegOperand*>(operands[0].get())->getVRegNum();
|
||||
unsigned alloca_vreg = static_cast<RegOperand*>(operands[1].get())->getVRegNum();
|
||||
int offset = frame_info.alloca_offsets.at(alloca_vreg);
|
||||
|
||||
// 将 `frame_addr rd, rs` 展开为 `addi rd, s0, offset`
|
||||
auto addi = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
addi->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
addi->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
addi->addOperand(std::make_unique<ImmOperand>(offset));
|
||||
new_instructions.push_back(std::move(addi));
|
||||
} else {
|
||||
new_instructions.push_back(std::move(instr_ptr));
|
||||
}
|
||||
// --- MODIFICATION END ---
|
||||
}
|
||||
mbb->getInstructions() = std::move(new_instructions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算给定 MachineInstr 的 Use (读取) 和 Def (写入) 寄存器集合。
|
||||
* 这是活跃性分析的基础。
|
||||
* @param instr 要分析的机器指令。
|
||||
* @param use 存储 Use 寄存器(虚拟寄存器 ID)的集合。
|
||||
* @param def 存储 Def 寄存器(虚拟寄存器 ID)的集合。
|
||||
*/
|
||||
void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& def) {
|
||||
bool is_def = true;
|
||||
bool first_reg_is_def = true; // 默认情况下,指令的第一个寄存器操作数是定义 (def)
|
||||
auto opcode = instr->getOpcode();
|
||||
|
||||
// 预定义def和use规则
|
||||
// 1. 特殊指令的 `is_def` 标志调整
|
||||
// 这些指令的第一个寄存器操作数是源操作数 (use),而不是目标操作数 (def)。
|
||||
if (opcode == RVOpcodes::SW || opcode == RVOpcodes::SD ||
|
||||
opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::RET || opcode == RVOpcodes::J) {
|
||||
is_def = false;
|
||||
first_reg_is_def = false;
|
||||
}
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
// CALL会杀死所有调用者保存寄存器,这是一个简化处理
|
||||
// 同时也使用了传入a0-a7的参数
|
||||
|
||||
// JAL 和 JALR 指令定义 ra (x1)
|
||||
if (opcode == RVOpcodes::JAL || opcode == RVOpcodes::JALR) {
|
||||
// 使用 ra 对应的特殊虚拟寄存器ID
|
||||
def.insert(static_cast<unsigned>(PhysicalReg::RA));
|
||||
first_reg_is_def = false; // JAL/JALR 的第一个操作数是 ra,已经处理为 def
|
||||
}
|
||||
|
||||
// 2. CALL 指令的特殊处理
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
// 1.1 处理返回值 (def)
|
||||
// 约定:如果CALL指令有返回值,IR阶段会将返回值vreg作为指令的第一个操作数。
|
||||
if (!instr->getOperands().empty() && instr->getOperands().front()->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(instr->getOperands().front().get());
|
||||
if (reg_op->isVirtual()) {
|
||||
def.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
|
||||
// 1.2 处理参数 (use)
|
||||
// 参数通常是指令的后续操作数
|
||||
bool first_operand_processed = false; // 用于跳过已作为def处理的返回值
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (!first_operand_processed) { // 如果是第一个操作数
|
||||
first_operand_processed = true;
|
||||
// 如果第一个操作数是返回值(已被加入def),则跳过
|
||||
if (def.count(static_cast<RegOperand*>(op.get())->getVRegNum())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// 否则,该寄存器是 use
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
use.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **重要**: CALL指令隐式定义(杀死)了所有调用者保存的寄存器。
|
||||
// **这部分逻辑不在getInstrUseDef中直接处理**。
|
||||
// 而是通过`buildInterferenceGraph`中添加物理寄存器节点与活跃虚拟寄存器之间的干扰边来完成。
|
||||
// 这样 Liveness Analysis 可以在虚拟寄存器层面进行,而物理寄存器干扰的复杂性则留给干扰图。
|
||||
|
||||
return; // CALL 指令处理完毕,直接返回
|
||||
}
|
||||
|
||||
// 3. 对其他所有指令的通用处理逻辑
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
if (is_def) {
|
||||
if (reg_op->isVirtual()) { // 只有虚拟寄存器才需要处理 Use/Def
|
||||
// 如果是第一个寄存器操作数,且指令类型表明它是定义 (def),则加入 def 集合
|
||||
// 否则,它是 use (读取)
|
||||
if (first_reg_is_def) {
|
||||
def.insert(reg_op->getVRegNum());
|
||||
is_def = false;
|
||||
first_reg_is_def = false; // 确保每条指令只定义一个目标寄存器
|
||||
} else {
|
||||
use.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
// 内存操作数 `offset(base)` 中的 `base` 寄存器是 `use`
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (mem_op->getBase()->isVirtual()) {
|
||||
use.insert(mem_op->getBase()->getVRegNum());
|
||||
}
|
||||
// 对于存储内存指令 (SW, SD),要存储的值(第一个操作数)也是 `use`
|
||||
if ((opcode == RVOpcodes::SW || opcode == RVOpcodes::SD) &&
|
||||
!instr->getOperands().empty() && // 确保有操作数
|
||||
instr->getOperands().front()->getKind() == MachineOperand::KIND_REG) { // 且第一个操作数是寄存器
|
||||
auto src_reg_op = static_cast<RegOperand*>(instr->getOperands().front().get());
|
||||
if (src_reg_op->isVirtual()) {
|
||||
use.insert(src_reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算一个类型在内存中占用的字节数。
|
||||
* @param type 需要计算大小的IR类型。
|
||||
* @return 该类型占用的字节数。
|
||||
*/
|
||||
unsigned RISCv64RegAlloc::getTypeSizeInBytes(Type* type) {
|
||||
if (!type) {
|
||||
assert(false && "Cannot get size of a null type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (type->getKind()) {
|
||||
// 对于SysY语言,基本类型int和float都占用4字节
|
||||
case Type::kInt:
|
||||
case Type::kFloat:
|
||||
return 4;
|
||||
|
||||
// 指针类型在RISC-V 64位架构下占用8字节
|
||||
// 虽然SysY没有'int*'语法,但数组变量在IR层面本身就是指针类型
|
||||
case Type::kPointer:
|
||||
return 8;
|
||||
|
||||
// 数组类型的总大小 = 元素数量 * 单个元素的大小
|
||||
case Type::kArray: {
|
||||
auto arrayType = type->as<ArrayType>();
|
||||
// 递归调用以计算元素大小
|
||||
return arrayType->getNumElements() * getTypeSizeInBytes(arrayType->getElementType());
|
||||
}
|
||||
|
||||
// 其他类型,如Void, Label等不占用栈空间,或者不应该出现在这里
|
||||
default:
|
||||
// 如果遇到未处理的类型,触发断言,方便调试
|
||||
assert(false && "Unsupported type for size calculation.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void RISCv64RegAlloc::analyzeLiveness() {
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
@ -182,6 +388,7 @@ void RISCv64RegAlloc::analyzeLiveness() {
|
||||
|
||||
void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
std::set<unsigned> all_vregs;
|
||||
// 收集所有虚拟寄存器和物理寄存器在干扰图中的节点ID
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
for(auto& instr : mbb->getInstructions()) {
|
||||
LiveSet use, def;
|
||||
@ -190,6 +397,11 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
for(auto d : def) all_vregs.insert(d);
|
||||
}
|
||||
}
|
||||
// 添加所有物理寄存器对应的特殊虚拟寄存器ID到all_vregs,作为干扰图节点
|
||||
for (auto preg : allocable_int_regs) {
|
||||
all_vregs.insert(preg_to_vreg_id_map.at(preg));
|
||||
}
|
||||
|
||||
|
||||
for (auto vreg : all_vregs) { interference_graph[vreg] = {}; }
|
||||
|
||||
@ -199,6 +411,7 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
getInstrUseDef(instr.get(), use, def);
|
||||
const LiveSet& live_out = live_out_map.at(instr.get());
|
||||
|
||||
// 标准干扰图构建:def 与 live_out 中的其他变量干扰
|
||||
for (unsigned d : def) {
|
||||
for (unsigned l : live_out) {
|
||||
if (d != l) {
|
||||
@ -207,6 +420,24 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *** 核心修改点:处理 CALL 指令的隐式 def ***
|
||||
if (instr->getOpcode() == RVOpcodes::CALL) {
|
||||
// CALL 指令会定义(杀死)所有调用者保存的寄存器。
|
||||
// 因此,所有调用者保存的物理寄存器都与 CALL 指令的 live_out 中的所有变量冲突。
|
||||
const std::vector<PhysicalReg>& caller_saved_regs = getCallerSavedIntRegs();
|
||||
for (PhysicalReg cs_reg : caller_saved_regs) {
|
||||
unsigned cs_vreg_id = preg_to_vreg_id_map.at(cs_reg); // 获取物理寄存器对应的特殊vreg ID
|
||||
|
||||
// 将这个物理寄存器节点与 CALL 指令的 live_out 中的所有虚拟寄存器添加干扰边。
|
||||
for (unsigned live_vreg_out : live_out) {
|
||||
if (cs_vreg_id != live_vreg_out) { // 避免自己和自己干扰
|
||||
interference_graph[cs_vreg_id].insert(live_vreg_out);
|
||||
interference_graph[live_vreg_out].insert(cs_vreg_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -214,19 +445,40 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
void RISCv64RegAlloc::colorGraph() {
|
||||
std::vector<unsigned> sorted_vregs;
|
||||
for (auto const& [vreg, neighbors] : interference_graph) {
|
||||
sorted_vregs.push_back(vreg);
|
||||
if (color_map.find(vreg) == color_map.end()) {
|
||||
sorted_vregs.push_back(vreg);
|
||||
}
|
||||
}
|
||||
|
||||
// 排序
|
||||
std::sort(sorted_vregs.begin(), sorted_vregs.end(), [&](unsigned a, unsigned b) {
|
||||
return interference_graph[a].size() > interference_graph[b].size();
|
||||
});
|
||||
|
||||
// 着色
|
||||
for (unsigned vreg : sorted_vregs) {
|
||||
std::set<PhysicalReg> used_colors;
|
||||
for (unsigned neighbor : interference_graph.at(vreg)) {
|
||||
if (color_map.count(neighbor)) {
|
||||
used_colors.insert(color_map.at(neighbor));
|
||||
for (unsigned neighbor_id : interference_graph.at(vreg)) {
|
||||
// --- 修改开始 ---
|
||||
|
||||
// 情况 1: 邻居是一个已经被着色的虚拟寄存器
|
||||
if (color_map.count(neighbor_id)) {
|
||||
used_colors.insert(color_map.at(neighbor_id));
|
||||
}
|
||||
// 情况 2: 邻居本身就是一个代表物理寄存器的节点
|
||||
else if (neighbor_id >= static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID)) {
|
||||
// 需要一个反向映射来从特殊ID找回PhysicalReg
|
||||
// 假设你有这样一个映射 inv_preg_to_vreg_id_map
|
||||
// 或者,你可以重新计算
|
||||
for (auto const& [preg, id] : preg_to_vreg_id_map) {
|
||||
if (id == neighbor_id) {
|
||||
used_colors.insert(preg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 修改结束 ---
|
||||
}
|
||||
|
||||
bool colored = false;
|
||||
@ -246,8 +498,21 @@ void RISCv64RegAlloc::colorGraph() {
|
||||
void RISCv64RegAlloc::rewriteFunction() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
int current_offset = frame_info.locals_size;
|
||||
|
||||
// --- FIX 1: 动态计算溢出槽大小 ---
|
||||
// 根据溢出虚拟寄存器的真实类型,为其在栈上分配正确大小的空间。
|
||||
for (unsigned vreg : spilled_vregs) {
|
||||
current_offset += 4;
|
||||
// 从反向映射中查找 vreg 对应的 IR Value
|
||||
assert(vreg_to_value_map.count(vreg) && "Spilled vreg not found in map!");
|
||||
Value* val = vreg_to_value_map.at(vreg);
|
||||
|
||||
// 使用辅助函数获取类型大小
|
||||
int size = getTypeSizeInBytes(val->getType());
|
||||
|
||||
// 保持栈8字节对齐
|
||||
current_offset += size;
|
||||
current_offset = (current_offset + 7) & ~7;
|
||||
|
||||
frame_info.spill_offsets[vreg] = -current_offset;
|
||||
}
|
||||
frame_info.spill_size = current_offset - frame_info.locals_size;
|
||||
@ -258,10 +523,16 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
LiveSet use, def;
|
||||
getInstrUseDef(instr_ptr.get(), use, def);
|
||||
|
||||
// --- FIX 2: 为溢出的 'use' 操作数插入正确的加载指令 ---
|
||||
for (unsigned vreg : use) {
|
||||
if (spilled_vregs.count(vreg)) {
|
||||
// 同样地,根据 vreg 的类型决定使用 lw 还是 ld
|
||||
assert(vreg_to_value_map.count(vreg));
|
||||
Value* val = vreg_to_value_map.at(vreg);
|
||||
RVOpcodes load_op = val->getType()->isPointer() ? RVOpcodes::LD : RVOpcodes::LW;
|
||||
|
||||
int offset = frame_info.spill_offsets.at(vreg);
|
||||
auto load = std::make_unique<MachineInstr>(RVOpcodes::LW);
|
||||
auto load = std::make_unique<MachineInstr>(load_op);
|
||||
load->addOperand(std::make_unique<RegOperand>(vreg));
|
||||
load->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
@ -273,10 +544,16 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
|
||||
new_instructions.push_back(std::move(instr_ptr));
|
||||
|
||||
// --- FIX 3: 为溢出的 'def' 操作数插入正确的存储指令 ---
|
||||
for (unsigned vreg : def) {
|
||||
if (spilled_vregs.count(vreg)) {
|
||||
// 根据 vreg 的类型决定使用 sw 还是 sd
|
||||
assert(vreg_to_value_map.count(vreg));
|
||||
Value* val = vreg_to_value_map.at(vreg);
|
||||
RVOpcodes store_op = val->getType()->isPointer() ? RVOpcodes::SD : RVOpcodes::SW;
|
||||
|
||||
int offset = frame_info.spill_offsets.at(vreg);
|
||||
auto store = std::make_unique<MachineInstr>(RVOpcodes::SW);
|
||||
auto store = std::make_unique<MachineInstr>(store_op);
|
||||
store->addOperand(std::make_unique<RegOperand>(vreg));
|
||||
store->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
@ -289,29 +566,43 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
mbb->getInstructions() = std::move(new_instructions);
|
||||
}
|
||||
|
||||
// 最后的虚拟寄存器到物理寄存器的替换过程保持不变
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||||
for (auto& op_ptr : instr_ptr->getOperands()) {
|
||||
|
||||
// 情况一:操作数本身就是一个寄存器 (例如 add rd, rs1, rs2 中的所有操作数)
|
||||
if(op_ptr->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op_ptr.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
unsigned vreg = reg_op->getVRegNum();
|
||||
if (color_map.count(vreg)) {
|
||||
reg_op->setPReg(color_map.at(vreg));
|
||||
PhysicalReg preg = color_map.at(vreg);
|
||||
reg_op->setPReg(preg);
|
||||
} else if (spilled_vregs.count(vreg)) {
|
||||
reg_op->setPReg(PhysicalReg::T6); // 溢出统一用t6
|
||||
// 如果vreg被溢出,替换为专用的溢出物理寄存器t6
|
||||
reg_op->setPReg(PhysicalReg::T6);
|
||||
}
|
||||
}
|
||||
} else if (op_ptr->getKind() == MachineOperand::KIND_MEM) {
|
||||
}
|
||||
// 情况二:操作数是一个内存地址 (例如 lw rd, offset(rs1) 中的 offset(rs1))
|
||||
else if (op_ptr->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op_ptr.get());
|
||||
// 获取内存操作数内部的“基址寄存器”
|
||||
auto base_reg_op = mem_op->getBase();
|
||||
|
||||
// 对这个基址寄存器,执行与情况一完全相同的替换逻辑
|
||||
if(base_reg_op->isVirtual()){
|
||||
unsigned vreg = base_reg_op->getVRegNum();
|
||||
if(color_map.count(vreg)) {
|
||||
base_reg_op->setPReg(color_map.at(vreg));
|
||||
} else if (spilled_vregs.count(vreg)) {
|
||||
base_reg_op->setPReg(PhysicalReg::T6);
|
||||
}
|
||||
unsigned vreg = base_reg_op->getVRegNum();
|
||||
if(color_map.count(vreg)) {
|
||||
// 如果基址vreg被成功着色,替换
|
||||
PhysicalReg preg = color_map.at(vreg);
|
||||
base_reg_op->setPReg(preg);
|
||||
|
||||
} else if (spilled_vregs.count(vreg)) {
|
||||
// 如果基址vreg被溢出,替换为t6
|
||||
base_reg_op->setPReg(PhysicalReg::T6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
122
src/Reg2Mem.cpp
122
src/Reg2Mem.cpp
@ -1,122 +0,0 @@
|
||||
#include "Reg2Mem.h"
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* 删除phi节点
|
||||
* 删除phi节点后可能会生成冗余存储代码
|
||||
*/
|
||||
void Reg2Mem::DeletePhiInst(){
|
||||
auto &functions = pModule->getFunctions();
|
||||
for (auto &function : functions) {
|
||||
auto basicBlocks = function.second->getBasicBlocks();
|
||||
for (auto &basicBlock : basicBlocks) {
|
||||
|
||||
for (auto iter = basicBlock->begin(); iter != basicBlock->end();) {
|
||||
auto &instruction = *iter;
|
||||
if (instruction->isPhi()) {
|
||||
auto predBlocks = basicBlock->getPredecessors();
|
||||
// 寻找源和目的
|
||||
// 目的就是phi指令的第一个操作数
|
||||
// 源就是phi指令的后续操作数
|
||||
auto destination = instruction->getOperand(0);
|
||||
int predBlockindex = 0;
|
||||
for (auto &predBlock : predBlocks) {
|
||||
++predBlockindex;
|
||||
// 判断前驱块儿只有一个后继还是多个后继
|
||||
// 如果有多个
|
||||
auto source = instruction->getOperand(predBlockindex);
|
||||
if (source == destination) {
|
||||
continue;
|
||||
}
|
||||
// std::cout << predBlock->getNumSuccessors() << std::endl;
|
||||
if (predBlock->getNumSuccessors() > 1) {
|
||||
// 创建一个basicblock
|
||||
auto newbasicBlock = function.second->addBasicBlock();
|
||||
std::stringstream ss;
|
||||
ss << "phidel.L" << pBuilder->getLabelIndex();
|
||||
newbasicBlock->setName(ss.str());
|
||||
ss.str("");
|
||||
// // 修改前驱后继关系
|
||||
basicBlock->replacePredecessor(predBlock, newbasicBlock);
|
||||
// predBlock = newbasicBlock;
|
||||
newbasicBlock->addPredecessor(predBlock);
|
||||
newbasicBlock->addSuccessor(basicBlock.get());
|
||||
predBlock->removeSuccessor(basicBlock.get());
|
||||
predBlock->addSuccessor(newbasicBlock);
|
||||
// std::cout << "the block name is " << basicBlock->getName() << std::endl;
|
||||
// for (auto pb : basicBlock->getPredecessors()) {
|
||||
// // newbasicBlock->addPredecessor(pb);
|
||||
// std::cout << pb->getName() << std::endl;
|
||||
// }
|
||||
// sysy::BasicBlock::conectBlocks(newbasicBlock, static_cast<BasicBlock *>(basicBlock.get()));
|
||||
// 若后为跳转指令,应该修改跳转指令所到达的位置
|
||||
auto thelastinst = predBlock->end();
|
||||
(--thelastinst);
|
||||
|
||||
if (thelastinst->get()->isConditional() || thelastinst->get()->isUnconditional()) { // 如果是跳转指令
|
||||
auto opnum = thelastinst->get()->getNumOperands();
|
||||
for (size_t i = 0; i < opnum; i++) {
|
||||
if (thelastinst->get()->getOperand(i) == basicBlock.get()) {
|
||||
thelastinst->get()->replaceOperand(i, newbasicBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 在新块中插入store指令
|
||||
pBuilder->setPosition(newbasicBlock, newbasicBlock->end());
|
||||
// pBuilder->createStoreInst(source, destination);
|
||||
if (source->isInt() || source->isFloat()) {
|
||||
pBuilder->createStoreInst(source, destination);
|
||||
} else {
|
||||
auto loadInst = pBuilder->createLoadInst(source);
|
||||
pBuilder->createStoreInst(loadInst, destination);
|
||||
}
|
||||
// pBuilder->createMoveInst(Instruction::kMove, destination->getType(), destination, source,
|
||||
// newbasicBlock);
|
||||
pBuilder->setPosition(newbasicBlock, newbasicBlock->end());
|
||||
pBuilder->createUncondBrInst(basicBlock.get(), {});
|
||||
} else {
|
||||
// 如果前驱块只有一个后继
|
||||
auto thelastinst = predBlock->end();
|
||||
(--thelastinst);
|
||||
// std::cout << predBlock->getName() << std::endl;
|
||||
// std::cout << thelastinst->get() << std::endl;
|
||||
// std::cout << "First point 11 " << std::endl;
|
||||
if (thelastinst->get()->isConditional() || thelastinst->get()->isUnconditional()) {
|
||||
// 在跳转语句前insert st指令
|
||||
pBuilder->setPosition(predBlock, thelastinst);
|
||||
} else {
|
||||
pBuilder->setPosition(predBlock, predBlock->end());
|
||||
}
|
||||
|
||||
if (source->isInt() || source->isFloat()) {
|
||||
pBuilder->createStoreInst(source, destination);
|
||||
} else {
|
||||
auto loadInst = pBuilder->createLoadInst(source);
|
||||
pBuilder->createStoreInst(loadInst, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 删除phi指令
|
||||
auto &instructions = basicBlock->getInstructions();
|
||||
SysYIROptUtils::usedelete(iter->get());
|
||||
iter = instructions.erase(iter);
|
||||
if (basicBlock->getNumInstructions() == 0) {
|
||||
if (basicBlock->getNumSuccessors() == 1) {
|
||||
pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
pBuilder->createUncondBrInst(basicBlock->getSuccessors()[0], {});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -6,14 +6,25 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
#include <queue> // 引入队列,SysYDelNoPreBLock需要
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 定义静态ID
|
||||
void *SysYDelInstAfterBrPass::ID = (void *)&SysYDelInstAfterBrPass::ID;
|
||||
void *SysYDelEmptyBlockPass::ID = (void *)&SysYDelEmptyBlockPass::ID;
|
||||
void *SysYDelNoPreBLockPass::ID = (void *)&SysYDelNoPreBLockPass::ID;
|
||||
void *SysYBlockMergePass::ID = (void *)&SysYBlockMergePass::ID;
|
||||
void *SysYAddReturnPass::ID = (void *)&SysYAddReturnPass::ID;
|
||||
void *SysYCondBr2BrPass::ID = (void *)&SysYCondBr2BrPass::ID;
|
||||
|
||||
|
||||
// ======================================================================
|
||||
// SysYCFGOptUtils: 辅助工具类,包含实际的CFG优化逻辑
|
||||
// ======================================================================
|
||||
|
||||
// 删除br后的无用指令
|
||||
bool SysYCFGOpt::SysYDelInstAfterBr(Function *func) {
|
||||
bool SysYCFGOptUtils::SysYDelInstAfterBr(Function *func) {
|
||||
bool changed = false;
|
||||
|
||||
auto basicBlocks = func->getBasicBlocks();
|
||||
@ -22,11 +33,10 @@ bool SysYCFGOpt::SysYDelInstAfterBr(Function *func) {
|
||||
auto &instructions = basicBlock->getInstructions();
|
||||
auto Branchiter = instructions.end();
|
||||
for (auto iter = instructions.begin(); iter != instructions.end(); ++iter) {
|
||||
if (Branch)
|
||||
SysYIROptUtils::usedelete(iter->get());
|
||||
else if ((*iter)->isTerminator()){
|
||||
if ((*iter)->isTerminator()){
|
||||
Branch = true;
|
||||
Branchiter = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Branchiter != instructions.end()) ++Branchiter;
|
||||
@ -61,8 +71,8 @@ bool SysYCFGOpt::SysYDelInstAfterBr(Function *func) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
// 合并空基本块
|
||||
bool SysYCFGOpt::SysYBlockMerge(Function *func) {
|
||||
// 合并基本块
|
||||
bool SysYCFGOptUtils::SysYBlockMerge(Function *func) {
|
||||
bool changed = false;
|
||||
|
||||
for (auto blockiter = func->getBasicBlocks().begin();
|
||||
@ -75,19 +85,19 @@ bool SysYCFGOpt::SysYBlockMerge(Function *func) {
|
||||
// std::cout << "merge block: " << blockiter->get()->getName() << std::endl;
|
||||
BasicBlock* block = blockiter->get();
|
||||
BasicBlock* nextBlock = blockiter->get()->getSuccessors()[0];
|
||||
auto nextarguments = nextBlock->getArguments();
|
||||
// auto nextarguments = nextBlock->getArguments();
|
||||
// 删除br指令
|
||||
if (block->getNumInstructions() != 0) {
|
||||
auto thelastinstinst = block->end();
|
||||
(--thelastinstinst);
|
||||
if (thelastinstinst->get()->isUnconditional()) {
|
||||
SysYIROptUtils::usedelete(thelastinstinst->get());
|
||||
block->getInstructions().erase(thelastinstinst);
|
||||
thelastinstinst = block->getInstructions().erase(thelastinstinst);
|
||||
} else if (thelastinstinst->get()->isConditional()) {
|
||||
// 如果是条件分支,判断条件是否相同,主要优化相同布尔表达式
|
||||
if (thelastinstinst->get()->getOperand(1)->getName() == thelastinstinst->get()->getOperand(1)->getName()) {
|
||||
SysYIROptUtils::usedelete(thelastinstinst->get());
|
||||
block->getInstructions().erase(thelastinstinst);
|
||||
thelastinstinst = block->getInstructions().erase(thelastinstinst);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,12 +108,6 @@ bool SysYCFGOpt::SysYBlockMerge(Function *func) {
|
||||
block->getInstructions().emplace_back(institer->release());
|
||||
institer = nextBlock->getInstructions().erase(institer);
|
||||
}
|
||||
// 合并参数
|
||||
// TODO:是否需要去重?
|
||||
for (auto &argm : nextarguments) {
|
||||
argm->setParent(block);
|
||||
block->insertArgument(argm);
|
||||
}
|
||||
// 更新前驱后继关系,类似树节点操作
|
||||
block->removeSuccessor(nextBlock);
|
||||
nextBlock->removePredecessor(block);
|
||||
@ -132,7 +136,7 @@ bool SysYCFGOpt::SysYBlockMerge(Function *func) {
|
||||
}
|
||||
|
||||
// 删除无前驱块,兼容SSA后的处理
|
||||
bool SysYCFGOpt::SysYDelNoPreBLock(Function *func) {
|
||||
bool SysYCFGOptUtils::SysYDelNoPreBLock(Function *func) {
|
||||
|
||||
bool changed = false;
|
||||
|
||||
@ -156,29 +160,26 @@ bool SysYCFGOpt::SysYDelNoPreBLock(Function *func) {
|
||||
}
|
||||
|
||||
// 删除不可达基本块指令
|
||||
for (auto blockIter = func->getBasicBlocks().begin(); blockIter != func->getBasicBlocks().end();blockIter++) {
|
||||
if (!blockIter->get()->getreachable())
|
||||
for (auto &iterInst : blockIter->get()->getInstructions())
|
||||
SysYIROptUtils::usedelete(iterInst.get());
|
||||
|
||||
for (auto blockIter = func->getBasicBlocks().begin(); blockIter != func->getBasicBlocks().end(); blockIter++) {
|
||||
if (!blockIter->get()->getreachable()) {
|
||||
for (auto instIter = blockIter->get()->getInstructions().begin();
|
||||
instIter != blockIter->get()->getInstructions().end();) {
|
||||
SysYIROptUtils::usedelete(instIter->get());
|
||||
instIter = blockIter->get()->getInstructions().erase(instIter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto blockIter = func->getBasicBlocks().begin(); blockIter != func->getBasicBlocks().end();) {
|
||||
if (!blockIter->get()->getreachable()) {
|
||||
for (auto succblock : blockIter->get()->getSuccessors()) {
|
||||
int indexphi = 1;
|
||||
for (auto pred : succblock->getPredecessors()) {
|
||||
if (pred == blockIter->get()) {
|
||||
break;
|
||||
}
|
||||
indexphi++;
|
||||
}
|
||||
for (auto &phiinst : succblock->getInstructions()) {
|
||||
if (phiinst->getKind() != Instruction::kPhi) {
|
||||
break;
|
||||
}
|
||||
phiinst->removeOperand(indexphi);
|
||||
if (phiinst->getKind() != Instruction::kPhi) {
|
||||
break;
|
||||
}
|
||||
// 使用 delBlk 方法正确地删除对应于被删除基本块的传入值
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->delBlk(blockIter->get());
|
||||
}
|
||||
}
|
||||
// 删除不可达基本块,注意迭代器不可达问题
|
||||
@ -193,7 +194,7 @@ bool SysYCFGOpt::SysYDelNoPreBLock(Function *func) {
|
||||
}
|
||||
|
||||
// 删除空块
|
||||
bool SysYCFGOpt::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
bool SysYCFGOptUtils::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
bool changed = false;
|
||||
|
||||
// 收集不可达基本块
|
||||
@ -218,24 +219,29 @@ bool SysYCFGOpt::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(onlyPhi)
|
||||
if(onlyPhi && basicBlock->getNumSuccessors() == 1) // 确保有后继且只有一个
|
||||
EmptyBlocks[basicBlock.get()] = basicBlock->getSuccessors().front();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// 更新基本块信息,增加必要指令
|
||||
for (auto &basicBlock : basicBlocks) {
|
||||
// 把空块转换成只有跳转指令的不可达块
|
||||
// 把空块转换成只有跳转指令的不可达块 (这段逻辑在优化遍中可能需要调整,这里是原样保留)
|
||||
// 通常,DelEmptyBlock 应该在BlockMerge之后运行,如果存在完全空块,它会尝试填充一个Br指令。
|
||||
// 但是,它主要目的是重定向跳转。
|
||||
if (distance(basicBlock->begin(), basicBlock->end()) == 0) {
|
||||
if (basicBlock->getNumSuccessors() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (basicBlock->getNumSuccessors() > 1) {
|
||||
assert("");
|
||||
// 如果一个空块有多个后继,说明CFG结构有问题或者需要特殊处理,这里简单assert
|
||||
assert(false && "Empty block with multiple successors found during SysYDelEmptyBlock");
|
||||
}
|
||||
pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
pBuilder->createUncondBrInst(basicBlock->getSuccessors()[0], {});
|
||||
// 这里的逻辑有点问题,如果一个块是空的,且只有一个后继,应该直接跳转到后继。
|
||||
// 如果这个块最终被删除了,那么其前驱也需要重定向。
|
||||
// 这个循环的目的是重定向现有的跳转指令,而不是创建新的。
|
||||
// 所以下面的逻辑才是核心。
|
||||
// pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
// pBuilder->createUncondBrInst(basicBlock->getSuccessors()[0], {});
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -247,50 +253,55 @@ bool SysYCFGOpt::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
BasicBlock* OldBrBlock = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0));
|
||||
BasicBlock *thelastBlockOld = nullptr;
|
||||
// 如果空块链表为多个块
|
||||
while (EmptyBlocks.find(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))) !=
|
||||
EmptyBlocks.end()) {
|
||||
while (EmptyBlocks.count(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0)))) {
|
||||
thelastBlockOld = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0));
|
||||
thelastinst->get()->replaceOperand(0, EmptyBlocks[thelastBlockOld]);
|
||||
}
|
||||
|
||||
basicBlock->removeSuccessor(OldBrBlock);
|
||||
OldBrBlock->removePredecessor(basicBlock.get());
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->addPredecessor(basicBlock.get());
|
||||
// 如果有重定向发生
|
||||
if (thelastBlockOld != nullptr) {
|
||||
basicBlock->removeSuccessor(OldBrBlock);
|
||||
OldBrBlock->removePredecessor(basicBlock.get());
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->addPredecessor(basicBlock.get());
|
||||
changed = true; // 标记IR被修改
|
||||
}
|
||||
|
||||
|
||||
if (thelastBlockOld != nullptr) {
|
||||
int indexphi = 0;
|
||||
for (auto &pred : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getPredecessors()) {
|
||||
if (pred == thelastBlockOld) {
|
||||
break;
|
||||
}
|
||||
indexphi++;
|
||||
}
|
||||
|
||||
// 更新phi指令的操作数
|
||||
// 移除thelastBlockOld对应的phi操作数
|
||||
for (auto &InstInNew : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getInstructions()) {
|
||||
if (InstInNew->isPhi()) {
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->removeOperand(indexphi + 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if (InstInNew->isPhi()) {
|
||||
// 使用 delBlk 方法删除 oldBlock 对应的传入值
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->delBlk(thelastBlockOld);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (thelastinst->get()->getKind() == Instruction::kCondBr) {
|
||||
auto OldThenBlock = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1));
|
||||
auto OldElseBlock = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2));
|
||||
bool thenChanged = false;
|
||||
bool elseChanged = false;
|
||||
|
||||
|
||||
BasicBlock *thelastBlockOld = nullptr;
|
||||
while (EmptyBlocks.find(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1))) !=
|
||||
EmptyBlocks.end()) {
|
||||
while (EmptyBlocks.count(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1)))) {
|
||||
thelastBlockOld = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1));
|
||||
thelastinst->get()->replaceOperand(
|
||||
1, EmptyBlocks[dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1))]);
|
||||
thenChanged = true;
|
||||
}
|
||||
basicBlock->removeSuccessor(OldThenBlock);
|
||||
OldThenBlock->removePredecessor(basicBlock.get());
|
||||
|
||||
if (thenChanged) {
|
||||
basicBlock->removeSuccessor(OldThenBlock);
|
||||
OldThenBlock->removePredecessor(basicBlock.get());
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1))->addPredecessor(basicBlock.get());
|
||||
changed = true; // 标记IR被修改
|
||||
}
|
||||
|
||||
// 处理 then 和 else 分支合并的情况
|
||||
if (dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1)) ==
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))) {
|
||||
@ -299,39 +310,37 @@ bool SysYCFGOpt::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
thelastinst = basicBlock->getInstructions().erase(thelastinst);
|
||||
pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
pBuilder->createUncondBrInst(thebrBlock, {});
|
||||
changed = true; // 标记IR被修改
|
||||
continue;
|
||||
}
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1))->addPredecessor(basicBlock.get());
|
||||
// auto indexInNew = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getPredecessors().
|
||||
|
||||
|
||||
if (thelastBlockOld != nullptr) {
|
||||
int indexphi = 0;
|
||||
for (auto &pred : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1))->getPredecessors()) {
|
||||
if (pred == thelastBlockOld) {
|
||||
break;
|
||||
}
|
||||
indexphi++;
|
||||
}
|
||||
|
||||
for (auto &InstInNew : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1))->getInstructions()) {
|
||||
if (InstInNew->isPhi()) {
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->removeOperand(indexphi + 1);
|
||||
// 使用 delBlk 方法删除 oldBlock 对应的传入值
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->delBlk(thelastBlockOld);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thelastBlockOld = nullptr;
|
||||
while (EmptyBlocks.find(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))) !=
|
||||
EmptyBlocks.end()) {
|
||||
while (EmptyBlocks.count(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2)))) {
|
||||
thelastBlockOld = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2));
|
||||
thelastinst->get()->replaceOperand(
|
||||
2, EmptyBlocks[dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))]);
|
||||
elseChanged = true;
|
||||
}
|
||||
basicBlock->removeSuccessor(OldElseBlock);
|
||||
OldElseBlock->removePredecessor(basicBlock.get());
|
||||
|
||||
if (elseChanged) {
|
||||
basicBlock->removeSuccessor(OldElseBlock);
|
||||
OldElseBlock->removePredecessor(basicBlock.get());
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))->addPredecessor(basicBlock.get());
|
||||
changed = true; // 标记IR被修改
|
||||
}
|
||||
|
||||
// 处理 then 和 else 分支合并的情况
|
||||
if (dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(1)) ==
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))) {
|
||||
@ -340,93 +349,94 @@ bool SysYCFGOpt::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
thelastinst = basicBlock->getInstructions().erase(thelastinst);
|
||||
pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
pBuilder->createUncondBrInst(thebrBlock, {});
|
||||
changed = true; // 标记IR被修改
|
||||
continue;
|
||||
}
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))->addPredecessor(basicBlock.get());
|
||||
|
||||
|
||||
// 如果有重定向发生
|
||||
// 需要更新后继块的前驱关系
|
||||
if (thelastBlockOld != nullptr) {
|
||||
int indexphi = 0;
|
||||
for (auto &pred : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))->getPredecessors()) {
|
||||
if (pred == thelastBlockOld) {
|
||||
break;
|
||||
}
|
||||
indexphi++;
|
||||
}
|
||||
for (auto &InstInNew : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(2))->getInstructions()) {
|
||||
if (InstInNew->isPhi()) {
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->removeOperand(indexphi + 1);
|
||||
// 使用 delBlk 方法删除 oldBlock 对应的传入值
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->delBlk(thelastBlockOld);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// 如果不是终止指令,但有后继 (例如,末尾没有显式终止指令的块)
|
||||
// 这段逻辑可能需要更严谨的CFG检查来确保正确性
|
||||
if (basicBlock->getNumSuccessors() == 1) {
|
||||
pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
pBuilder->createUncondBrInst(basicBlock->getSuccessors()[0], {});
|
||||
auto thelastinst = basicBlock->getInstructions().end();
|
||||
(--thelastinst);
|
||||
auto OldBrBlock = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0));
|
||||
sysy::BasicBlock *thelastBlockOld = nullptr;
|
||||
while (EmptyBlocks.find(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))) !=
|
||||
EmptyBlocks.end()) {
|
||||
thelastBlockOld = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0));
|
||||
// 这里的逻辑似乎是想为没有terminator的块添加一个,但通常这应该在CFG构建阶段完成。
|
||||
// 如果这里仍然执行,确保它符合预期。
|
||||
// pBuilder->setPosition(basicBlock.get(), basicBlock->end());
|
||||
// pBuilder->createUncondBrInst(basicBlock->getSuccessors()[0], {});
|
||||
// auto thelastinst = basicBlock->getInstructions().end();
|
||||
// (--thelastinst);
|
||||
// auto OldBrBlock = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0));
|
||||
// sysy::BasicBlock *thelastBlockOld = nullptr;
|
||||
// while (EmptyBlocks.find(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))) !=
|
||||
// EmptyBlocks.end()) {
|
||||
// thelastBlockOld = dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0));
|
||||
|
||||
thelastinst->get()->replaceOperand(
|
||||
0, EmptyBlocks[dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))]);
|
||||
}
|
||||
// thelastinst->get()->replaceOperand(
|
||||
// 0, EmptyBlocks[dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))]);
|
||||
// }
|
||||
|
||||
basicBlock->removeSuccessor(OldBrBlock);
|
||||
OldBrBlock->removePredecessor(basicBlock.get());
|
||||
basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0)));
|
||||
dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->addPredecessor(basicBlock.get());
|
||||
if (thelastBlockOld != nullptr) {
|
||||
int indexphi = 0;
|
||||
for (auto &pred : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getPredecessors()) {
|
||||
if (pred == thelastBlockOld) {
|
||||
break;
|
||||
}
|
||||
indexphi++;
|
||||
}
|
||||
// basicBlock->removeSuccessor(OldBrBlock);
|
||||
// OldBrBlock->removePredecessor(basicBlock.get());
|
||||
// basicBlock->addSuccessor(dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0)));
|
||||
// dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->addPredecessor(basicBlock.get());
|
||||
// changed = true; // 标记IR被修改
|
||||
// if (thelastBlockOld != nullptr) {
|
||||
// int indexphi = 0;
|
||||
// for (auto &pred : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getPredecessors()) {
|
||||
// if (pred == thelastBlockOld) {
|
||||
// break;
|
||||
// }
|
||||
// indexphi++;
|
||||
// }
|
||||
|
||||
for (auto &InstInNew : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getInstructions()) {
|
||||
if (InstInNew->isPhi()) {
|
||||
dynamic_cast<PhiInst *>(InstInNew.get())->removeOperand(indexphi + 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (auto &InstInNew : dynamic_cast<BasicBlock *>(thelastinst->get()->getOperand(0))->getInstructions()) {
|
||||
// if (InstInNew->isPhi()) {
|
||||
// dynamic_cast<PhiInst *>(InstInNew.get())->removeOperand(indexphi + 1);
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 真正的删除空块
|
||||
for (auto iter = func->getBasicBlocks().begin(); iter != func->getBasicBlocks().end();) {
|
||||
|
||||
if (EmptyBlocks.find(iter->get()) != EmptyBlocks.end()) {
|
||||
if (EmptyBlocks.count(iter->get())) {
|
||||
// EntryBlock跳过
|
||||
if (iter->get() == func->getEntryBlock()) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &iterInst : iter->get()->getInstructions())
|
||||
SysYIROptUtils::usedelete(iterInst.get());
|
||||
for (auto instIter = iter->get()->getInstructions().begin();
|
||||
instIter != iter->get()->getInstructions().end();) {
|
||||
SysYIROptUtils::usedelete(instIter->get()); // 仅删除 use 关系
|
||||
// 显式地从基本块中删除指令并更新迭代器
|
||||
instIter = iter->get()->getInstructions().erase(instIter);
|
||||
}
|
||||
// 删除不可达基本块的phi指令的操作数
|
||||
for (auto &succ : iter->get()->getSuccessors()) {
|
||||
int index = 0;
|
||||
for (auto &pred : succ->getPredecessors()) {
|
||||
if (pred == iter->get()) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
for (auto &instinsucc : succ->getInstructions()) {
|
||||
if (instinsucc->isPhi()) {
|
||||
dynamic_cast<PhiInst *>(instinsucc.get())->removeOperand(index);
|
||||
// iter->get() 就是当前被删除的空基本块,它作为前驱连接到这里的Phi指令
|
||||
dynamic_cast<PhiInst *>(instinsucc.get())->delBlk(iter->get());
|
||||
} else {
|
||||
// Phi 指令通常在基本块的开头,如果不是 Phi 指令就停止检查
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -440,34 +450,35 @@ bool SysYCFGOpt::SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder) {
|
||||
}
|
||||
|
||||
return changed;
|
||||
|
||||
}
|
||||
|
||||
// 如果函数没有返回指令,则添加一个默认返回指令(主要解决void函数没有返回指令的问题)
|
||||
bool SysYCFGOpt::SysYAddReturn(Function *func, IRBuilder* pBuilder) {
|
||||
bool SysYCFGOptUtils::SysYAddReturn(Function *func, IRBuilder* pBuilder) {
|
||||
bool changed = false;
|
||||
auto basicBlocks = func->getBasicBlocks();
|
||||
for (auto &block : basicBlocks) {
|
||||
if (block->getNumSuccessors() == 0) {
|
||||
changed = true;
|
||||
// 如果基本块没有后继块,则添加一个返回指令
|
||||
if (block->getNumInstructions() == 0) {
|
||||
pBuilder->setPosition(block.get(), block->end());
|
||||
pBuilder->createReturnInst();
|
||||
}
|
||||
auto thelastinst = block->getInstructions().end();
|
||||
--thelastinst;
|
||||
if (thelastinst->get()->getKind() != Instruction::kReturn) {
|
||||
// std::cout << "Warning: Function " << func->getName() << " has no return instruction, adding default return." << std::endl;
|
||||
changed = true; // 标记IR被修改
|
||||
} else {
|
||||
auto thelastinst = block->getInstructions().end();
|
||||
--thelastinst;
|
||||
if (thelastinst->get()->getKind() != Instruction::kReturn) {
|
||||
// std::cout << "Warning: Function " << func->getName() << " has no return instruction, adding default return." << std::endl;
|
||||
|
||||
pBuilder->setPosition(block.get(), block->end());
|
||||
// TODO: 如果int float函数缺少返回值是否需要报错
|
||||
if (func->getReturnType()->isInt()) {
|
||||
pBuilder->createReturnInst(ConstantInteger::get(0));
|
||||
} else if (func->getReturnType()->isFloat()) {
|
||||
pBuilder->createReturnInst(ConstantFloating::get(0.0F));
|
||||
} else {
|
||||
pBuilder->createReturnInst();
|
||||
pBuilder->setPosition(block.get(), block->end());
|
||||
// TODO: 如果int float函数缺少返回值是否需要报错
|
||||
if (func->getReturnType()->isInt()) {
|
||||
pBuilder->createReturnInst(ConstantInteger::get(0));
|
||||
} else if (func->getReturnType()->isFloat()) {
|
||||
pBuilder->createReturnInst(ConstantFloating::get(0.0F));
|
||||
} else {
|
||||
pBuilder->createReturnInst();
|
||||
}
|
||||
changed = true; // 标记IR被修改
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -480,7 +491,7 @@ bool SysYCFGOpt::SysYAddReturn(Function *func, IRBuilder* pBuilder) {
|
||||
// 主要针对已知条件值的分支转换为无条件分支
|
||||
// 例如 if (cond) { ... } else { ... } 中的 cond 已经
|
||||
// 确定为 true 或 false 的情况
|
||||
bool SysYCFGOpt::SysYCondBr2Br(Function *func, IRBuilder* pBuilder) {
|
||||
bool SysYCFGOptUtils::SysYCondBr2Br(Function *func, IRBuilder* pBuilder) {
|
||||
bool changed = false;
|
||||
|
||||
for (auto &basicblock : func->getBasicBlocks()) {
|
||||
@ -515,45 +526,41 @@ bool SysYCFGOpt::SysYCondBr2Br(Function *func, IRBuilder* pBuilder) {
|
||||
SysYIROptUtils::usedelete(thelast->get());
|
||||
thelast = basicblock->getInstructions().erase(thelast);
|
||||
if ((constfloat_Use && constfloat == 1.0F) || (constint_Use && constint == 1)) {
|
||||
|
||||
// cond为true或非0
|
||||
pBuilder->setPosition(basicblock.get(), basicblock->end());
|
||||
pBuilder->createUncondBrInst(thenBlock, {});
|
||||
int phiindex = 0;
|
||||
for (auto pred : elseBlock->getPredecessors()) {
|
||||
phiindex++;
|
||||
if (pred == basicblock.get()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新CFG关系
|
||||
basicblock->removeSuccessor(elseBlock);
|
||||
elseBlock->removePredecessor(basicblock.get());
|
||||
|
||||
// 删除elseBlock的phi指令中对应的basicblock.get()的传入值
|
||||
for (auto &phiinst : elseBlock->getInstructions()) {
|
||||
if (phiinst->getKind() != Instruction::kPhi) {
|
||||
break;
|
||||
}
|
||||
phiinst->removeOperand(phiindex);
|
||||
// 使用 delBlk 方法删除 basicblock.get() 对应的传入值
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->delBlk(basicblock.get());
|
||||
}
|
||||
basicblock->removeSuccessor(elseBlock);
|
||||
elseBlock->removePredecessor(basicblock.get());
|
||||
} else {
|
||||
|
||||
} else { // cond为false或0
|
||||
|
||||
pBuilder->setPosition(basicblock.get(), basicblock->end());
|
||||
pBuilder->createUncondBrInst(elseBlock, {});
|
||||
int phiindex = 0;
|
||||
for (auto pred : thenBlock->getPredecessors()) {
|
||||
phiindex++;
|
||||
if (pred == basicblock.get()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新CFG关系
|
||||
basicblock->removeSuccessor(thenBlock);
|
||||
thenBlock->removePredecessor(basicblock.get());
|
||||
|
||||
// 删除thenBlock的phi指令中对应的basicblock.get()的传入值
|
||||
for (auto &phiinst : thenBlock->getInstructions()) {
|
||||
if (phiinst->getKind() != Instruction::kPhi) {
|
||||
break;
|
||||
}
|
||||
phiinst->removeOperand(phiindex);
|
||||
// 使用 delBlk 方法删除 basicblock.get() 对应的传入值
|
||||
dynamic_cast<PhiInst *>(phiinst.get())->delBlk(basicblock.get());
|
||||
}
|
||||
basicblock->removeSuccessor(thenBlock);
|
||||
thenBlock->removePredecessor(basicblock.get());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -562,4 +569,32 @@ bool SysYCFGOpt::SysYCondBr2Br(Function *func, IRBuilder* pBuilder) {
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
// ======================================================================
|
||||
// 独立的CFG优化遍的实现
|
||||
// ======================================================================
|
||||
|
||||
bool SysYDelInstAfterBrPass::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
return SysYCFGOptUtils::SysYDelInstAfterBr(F);
|
||||
}
|
||||
|
||||
bool SysYDelEmptyBlockPass::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
return SysYCFGOptUtils::SysYDelEmptyBlock(F, pBuilder);
|
||||
}
|
||||
|
||||
bool SysYDelNoPreBLockPass::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
return SysYCFGOptUtils::SysYDelNoPreBLock(F);
|
||||
}
|
||||
|
||||
bool SysYBlockMergePass::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
return SysYCFGOptUtils::SysYBlockMerge(F);
|
||||
}
|
||||
|
||||
bool SysYAddReturnPass::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
return SysYCFGOptUtils::SysYAddReturn(F, pBuilder);
|
||||
}
|
||||
|
||||
bool SysYCondBr2BrPass::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
return SysYCFGOptUtils::SysYCondBr2Br(F, pBuilder);
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -40,48 +40,21 @@ Type* SysYIRGenerator::buildArrayType(Type* baseType, const std::vector<Value*>&
|
||||
return currentType;
|
||||
}
|
||||
|
||||
// @brief: 获取 GEP 指令的地址
|
||||
// @param basePointer: GEP 的基指针,已经过适当的加载/处理,类型为 LLVM IR 中的指针类型。
|
||||
// 例如,对于局部数组,它是 AllocaInst;对于参数数组,它是 LoadInst 的结果。
|
||||
// @param indices: 已经包含了所有必要的偏移索引 (包括可能的初始 0 索引,由 visitLValue 准备)。
|
||||
// @return: 计算得到的地址值 (也是一个指针类型)
|
||||
Value* SysYIRGenerator::getGEPAddressInst(Value* basePointer, const std::vector<Value*>& indices) {
|
||||
// 检查 basePointer 是否为指针类型
|
||||
if (!basePointer->getType()->isPointer()) {
|
||||
assert(false && "GEP base pointer must be a pointer type!");
|
||||
}
|
||||
assert(basePointer->getType()->isPointer() && "Base pointer must be a pointer type!");
|
||||
|
||||
// 获取基指针所指向的实际类型 (例如 int* 指向 int, int[2][3]* 指向 int[2][3])
|
||||
Type* currentElementType = basePointer->getType()->as<PointerType>()->getBaseType();
|
||||
|
||||
std::vector<Value*> actualGEPIndices;
|
||||
// GEP 指令的第一个索引通常是0,用于“跳过”基指针指向的聚合类型本身,直接指向其第一个元素。
|
||||
// 例如,对于 AllocaInst 返回的 `int[2][3]*`,第一个 `0` 索引表示从数组的开始而不是指针本身开始索引。
|
||||
actualGEPIndices.push_back(ConstantInteger::get(0));
|
||||
|
||||
// 将用户提供的索引添加到 GEP 操作数中
|
||||
for (Value* index : indices) {
|
||||
actualGEPIndices.push_back(index);
|
||||
}
|
||||
|
||||
// 根据索引链计算最终的元素类型
|
||||
Type* finalTargetType = currentElementType;
|
||||
|
||||
// 遍历用户提供的索引(不包括我们添加的第一个0),逐步确定 GEP 的最终结果类型
|
||||
// 每个索引都“深入”一个维度
|
||||
for (size_t i = 0; i < indices.size(); ++i) { // 这里遍历的是用户提供的索引
|
||||
if (finalTargetType && finalTargetType->isArray()) {
|
||||
finalTargetType = finalTargetType->as<ArrayType>()->getElementType();
|
||||
} else {
|
||||
// 如果索引链还在继续,但当前类型已经不是数组或聚合类型,这通常是一个错误
|
||||
// 或者表示访问的是标量,后续索引无效。此时,finalTargetType 已经是最终的标量类型,不能再深入。
|
||||
// 例如,对 int arr[5]; 访问 arr[i][j] (j 是多余的),这里会停止类型推断。
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// GEP 的结果总是指针类型,指向最终计算出的元素
|
||||
Type* gepResultType = Type::getPointerType(finalTargetType);
|
||||
|
||||
// 创建 GEP 指令。假设 builder.createGetElementPtrInst 的签名为
|
||||
// (Type* resultType, Value* basePointer, const std::vector<Value*>& indices)
|
||||
return builder.createGetElementPtrInst(basePointer, actualGEPIndices);
|
||||
// `indices` 向量现在由调用方(如 visitLValue, visitVarDecl, visitAssignStmt)负责完整准备,
|
||||
// 包括是否需要添加初始的 `0` 索引。
|
||||
// 所以这里直接将其传递给 `builder.createGetElementPtrInst`。
|
||||
return builder.createGetElementPtrInst(basePointer, indices);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief: visit compUnit
|
||||
* @details:
|
||||
@ -146,7 +119,11 @@ std::any SysYIRGenerator::visitGlobalVarDecl(SysYParser::GlobalVarDeclContext *c
|
||||
delete root;
|
||||
}
|
||||
// 创建全局变量,并更新符号表
|
||||
module->createGlobalValue(name, Type::getPointerType(type), dims, values);
|
||||
Type* variableType = type;
|
||||
if (!dims.empty()) { // 如果有维度,说明是数组
|
||||
variableType = buildArrayType(type, dims); // 构建完整的 ArrayType
|
||||
}
|
||||
module->createGlobalValue(name, Type::getPointerType(variableType), dims, values);
|
||||
}
|
||||
return std::any();
|
||||
}
|
||||
@ -193,7 +170,7 @@ std::any SysYIRGenerator::visitVarDecl(SysYParser::VarDeclContext *ctx) {
|
||||
// 对于数组,alloca 的类型将是指针指向数组类型,例如 `int[2][3]*`
|
||||
// 对于标量,alloca 的类型将是指针指向标量类型,例如 `int*`
|
||||
AllocaInst* alloca =
|
||||
builder.createAllocaInst(Type::getPointerType(type), dims, name);
|
||||
builder.createAllocaInst(Type::getPointerType(variableType), {}, name);
|
||||
|
||||
if (varDef->initVal() != nullptr) {
|
||||
ValueCounter values;
|
||||
@ -205,7 +182,7 @@ std::any SysYIRGenerator::visitVarDecl(SysYParser::VarDeclContext *ctx) {
|
||||
builder.createStoreInst(values.getValue(0), alloca);
|
||||
} else { // 数组变量初始化
|
||||
const std::vector<sysy::Value *> &counterValues = values.getValues();
|
||||
|
||||
const std::vector<unsigned> &counterNumbers = values.getNumbers();
|
||||
int numElements = 1;
|
||||
std::vector<int> dimSizes;
|
||||
for (Value *dimVal : dims) {
|
||||
@ -247,23 +224,39 @@ std::any SysYIRGenerator::visitVarDecl(SysYParser::VarDeclContext *ctx) {
|
||||
ConstantInteger::get(0));
|
||||
}
|
||||
else {
|
||||
for (size_t k = 0; k < counterValues.size(); ++k) {
|
||||
std::vector<Value *> currentIndices;
|
||||
int tempLinearIndex = k;
|
||||
|
||||
int linearIndexOffset = 0; // 用于追踪当前处理的线性索引的偏移量
|
||||
for (int k = 0; k < counterValues.size(); ++k) {
|
||||
// 当前 Value 的值和重复次数
|
||||
Value* currentValue = counterValues[k];
|
||||
unsigned currentRepeatNum = counterNumbers[k];
|
||||
|
||||
// 将线性索引转换为多维索引
|
||||
for (int dimIdx = dimSizes.size() - 1; dimIdx >= 0; --dimIdx)
|
||||
{
|
||||
currentIndices.insert(currentIndices.begin(),
|
||||
ConstantInteger::get(static_cast<int>(tempLinearIndex % dimSizes[dimIdx])));
|
||||
tempLinearIndex /= dimSizes[dimIdx];
|
||||
}
|
||||
for (unsigned i = 0; i < currentRepeatNum; ++i) {
|
||||
std::vector<Value *> currentIndices;
|
||||
int tempLinearIndex = linearIndexOffset + i; // 使用偏移量和当前重复次数内的索引
|
||||
|
||||
// 计算元素的地址
|
||||
Value* elementAddress = getGEPAddressInst(alloca, currentIndices);
|
||||
// 生成 store 指令 (假设 createStoreInst 接受 Value* value, Value* pointer)
|
||||
builder.createStoreInst(counterValues[k], elementAddress);
|
||||
// 将线性索引转换为多维索引
|
||||
for (int dimIdx = dimSizes.size() - 1; dimIdx >= 0; --dimIdx) {
|
||||
currentIndices.insert(currentIndices.begin(),
|
||||
ConstantInteger::get(static_cast<int>(tempLinearIndex % dimSizes[dimIdx])));
|
||||
tempLinearIndex /= dimSizes[dimIdx];
|
||||
}
|
||||
|
||||
// 对于局部数组,alloca 本身就是 GEP 的基指针。
|
||||
// GEP 的第一个索引必须是 0,用于“步过”整个数组。
|
||||
std::vector<Value*> gepIndicesForInit;
|
||||
gepIndicesForInit.push_back(ConstantInteger::get(0));
|
||||
gepIndicesForInit.insert(gepIndicesForInit.end(), currentIndices.begin(), currentIndices.end());
|
||||
|
||||
// 计算元素的地址
|
||||
Value* elementAddress = getGEPAddressInst(alloca, gepIndicesForInit);
|
||||
// 生成 store 指令
|
||||
builder.createStoreInst(currentValue, elementAddress);
|
||||
}
|
||||
// 更新线性索引偏移量,以便下一次迭代从正确的位置开始
|
||||
linearIndexOffset += currentRepeatNum;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -342,42 +335,92 @@ std::any SysYIRGenerator::visitFuncDef(SysYParser::FuncDefContext *ctx){
|
||||
module->enterNewScope();
|
||||
|
||||
auto name = ctx->Ident()->getText();
|
||||
std::vector<Type *> paramTypes;
|
||||
std::vector<Type *> paramActualTypes;
|
||||
std::vector<std::string> paramNames;
|
||||
std::vector<std::vector<Value *>> paramDims;
|
||||
|
||||
if (ctx->funcFParams() != nullptr) {
|
||||
auto params = ctx->funcFParams()->funcFParam();
|
||||
for (const auto ¶m : params) {
|
||||
paramTypes.push_back(std::any_cast<Type *>(visitBType(param->bType())));
|
||||
paramNames.push_back(param->Ident()->getText());
|
||||
std::vector<Value *> dims = {};
|
||||
if (!param->LBRACK().empty()) {
|
||||
dims.push_back(ConstantInteger::get(-1)); // 第一个维度不确定
|
||||
Type* baseBType = std::any_cast<Type *>(visitBType(param->bType()));
|
||||
std::string paramName = param->Ident()->getText();
|
||||
|
||||
// 用于收集当前参数的维度信息(如果它是数组)
|
||||
std::vector<Value *> currentParamDims;
|
||||
if (!param->LBRACK().empty()) { // 如果参数声明中有方括号,说明是数组
|
||||
// SysY 数组参数的第一个维度可以是未知的(例如 int arr[] 或 int arr[][10])
|
||||
// 这里的 ConstantInteger::get(-1) 表示未知维度,但对于 LLVM 类型构建,我们主要关注已知维度
|
||||
currentParamDims.push_back(ConstantInteger::get(-1)); // 标记第一个维度为未知
|
||||
for (const auto &exp : param->exp()) {
|
||||
dims.push_back(std::any_cast<Value *>(visitExp(exp)));
|
||||
// 访问表达式以获取维度大小,这些维度必须是常量
|
||||
Value* dimVal = std::any_cast<Value *>(visitExp(exp));
|
||||
// 确保维度是常量整数,否则 buildArrayType 会断言失败
|
||||
assert(dynamic_cast<ConstantInteger*>(dimVal) && "Array dimension in parameter must be a constant integer!");
|
||||
currentParamDims.push_back(dimVal);
|
||||
}
|
||||
}
|
||||
paramDims.emplace_back(dims);
|
||||
|
||||
// 根据解析出的信息,确定参数在 LLVM IR 中的实际类型
|
||||
Type* actualParamType;
|
||||
if (currentParamDims.empty()) { // 情况1:标量参数 (e.g., int x)
|
||||
actualParamType = baseBType; // 实际类型就是基本类型
|
||||
} else { // 情况2&3:数组参数 (e.g., int arr[] 或 int arr[][10])
|
||||
// 数组参数在函数传递时会退化为指针。
|
||||
// 这个指针指向的类型是除第一维外,由后续维度构成的数组类型。
|
||||
|
||||
// 从 currentParamDims 中移除第一个标记未知维度的 -1
|
||||
std::vector<Value*> fixedDimsForTypeBuilding;
|
||||
if (currentParamDims.size() > 1) { // 如果有固定维度 (e.g., int arr[][10])
|
||||
// 复制除第一个 -1 之外的所有维度
|
||||
fixedDimsForTypeBuilding.assign(currentParamDims.begin() + 1, currentParamDims.end());
|
||||
}
|
||||
|
||||
Type* pointedToArrayType = baseBType; // 从基本类型开始构建
|
||||
// 从最内层维度向外层构建数组类型
|
||||
// buildArrayType 期望 dims 是从最外层到最内层,但它内部反向迭代,所以这里直接传入
|
||||
// 例如,对于 int arr[][10],fixedDimsForTypeBuilding 包含 [10],构建出 [10 x i32]
|
||||
if (!fixedDimsForTypeBuilding.empty()) {
|
||||
pointedToArrayType = buildArrayType(baseBType, fixedDimsForTypeBuilding);
|
||||
}
|
||||
|
||||
// 实际参数类型是指向这个构建好的数组类型的指针
|
||||
actualParamType = Type::getPointerType(pointedToArrayType); // e.g., i32* 或 [10 x i32]*
|
||||
}
|
||||
|
||||
paramActualTypes.push_back(actualParamType); // 存储参数的实际 LLVM IR 类型
|
||||
paramNames.push_back(paramName); // 存储参数名称
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Type* returnType = std::any_cast<Type *>(visitFuncType(ctx->funcType()));
|
||||
Type* funcType = Type::getFunctionType(returnType, paramTypes);
|
||||
Type* funcType = Type::getFunctionType(returnType, paramActualTypes);
|
||||
Function* function = module->createFunction(name, funcType);
|
||||
BasicBlock* entry = function->getEntryBlock();
|
||||
builder.setPosition(entry, entry->end());
|
||||
|
||||
for (size_t i = 0; i < paramTypes.size(); ++i) {
|
||||
AllocaInst* alloca = builder.createAllocaInst(Type::getPointerType(paramTypes[i]),
|
||||
paramDims[i], paramNames[i]);
|
||||
entry->insertArgument(alloca);
|
||||
for(int i = 0; i < paramActualTypes.size(); ++i) {
|
||||
Argument* arg = new Argument(paramActualTypes[i], function, i, paramNames[i]);
|
||||
function->insertArgument(arg);
|
||||
|
||||
}
|
||||
|
||||
auto funcArgs = function->getArguments();
|
||||
std::vector<AllocaInst *> allocas;
|
||||
for (int i = 0; i < paramActualTypes.size(); ++i) {
|
||||
AllocaInst *alloca = builder.createAllocaInst(Type::getPointerType(paramActualTypes[i]), {}, paramNames[i]);
|
||||
allocas.push_back(alloca);
|
||||
module->addVariable(paramNames[i], alloca);
|
||||
}
|
||||
|
||||
for(int i = 0; i < paramActualTypes.size(); ++i) {
|
||||
Value *argValue = funcArgs[i];
|
||||
builder.createStoreInst(argValue, allocas[i]);
|
||||
}
|
||||
|
||||
// 在处理函数体之前,创建一个新的基本块作为函数体的实际入口
|
||||
// 这样 entryBB 就可以在完成初始化后跳转到这里
|
||||
BasicBlock* funcBodyEntry = function->addBasicBlock("funcBodyEntry");
|
||||
BasicBlock* funcBodyEntry = function->addBasicBlock("funcBodyEntry_" + name);
|
||||
|
||||
// 从 entryBB 无条件跳转到 funcBodyEntry
|
||||
builder.createUncondBrInst(funcBodyEntry, {});
|
||||
@ -419,60 +462,79 @@ std::any SysYIRGenerator::visitBlockStmt(SysYParser::BlockStmtContext *ctx) {
|
||||
std::any SysYIRGenerator::visitAssignStmt(SysYParser::AssignStmtContext *ctx) {
|
||||
auto lVal = ctx->lValue();
|
||||
std::string name = lVal->Ident()->getText();
|
||||
std::vector<Value *> dims;
|
||||
for (const auto &exp : lVal->exp()) {
|
||||
dims.push_back(std::any_cast<Value *>(visitExp(exp)));
|
||||
}
|
||||
|
||||
auto variable = module->getVariable(name); // 获取 AllocaInst 或 GlobalValue
|
||||
Value* value = std::any_cast<Value *>(visitExp(ctx->exp())); // 右值
|
||||
Value* LValue = nullptr;
|
||||
Value* variable = module->getVariable(name); // 左值
|
||||
|
||||
if (variable == nullptr) {
|
||||
throw std::runtime_error("Variable " + name + " not found in assignment.");
|
||||
vector<Value *> indices;
|
||||
if (lVal->exp().size() > 0) {
|
||||
// 如果有下标,访问表达式获取下标值
|
||||
for (const auto &exp : lVal->exp()) {
|
||||
Value* indexValue = std::any_cast<Value *>(visitExp(exp));
|
||||
indices.push_back(indexValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算最终赋值目标元素的类型
|
||||
// variable 本身应该是一个指针类型 (例如 int* 或 int[2][3]*)
|
||||
if (!variable->getType()->isPointer()) {
|
||||
assert(false && "Variable to be assigned must be a pointer type!");
|
||||
return std::any();
|
||||
if (indices.empty()) {
|
||||
// variable 本身就是指向标量的指针 (e.g., int* %a)
|
||||
if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable)) {
|
||||
LValue = variable;
|
||||
}
|
||||
}
|
||||
Type* targetElementType = variable->getType()->as<PointerType>()->getBaseType(); // 从基指针指向的类型开始
|
||||
|
||||
// 模拟 GEP 路径,根据 dims 确定最终元素的类型
|
||||
for (size_t i = 0; i < dims.size(); ++i) {
|
||||
if (targetElementType && targetElementType->isArray()) {
|
||||
targetElementType = targetElementType->as<ArrayType>()->getElementType();
|
||||
else {
|
||||
// 对于数组或多维数组的左值处理
|
||||
// 需要获取 GEP 地址
|
||||
Value* gepBasePointer = nullptr;
|
||||
std::vector<Value*> gepIndices;
|
||||
if (AllocaInst *alloc = dynamic_cast<AllocaInst *>(variable)) {
|
||||
Type* allocatedType = alloc->getType()->as<PointerType>()->getBaseType();
|
||||
if (allocatedType->isPointer()) {
|
||||
gepBasePointer = builder.createLoadInst(alloc);
|
||||
gepIndices = indices;
|
||||
} else {
|
||||
break; // 如果不是数组类型但还有索引,或者索引超出维度,则停止推断
|
||||
gepBasePointer = alloc;
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), indices.begin(), indices.end());
|
||||
}
|
||||
} else if (GlobalValue *glob = dynamic_cast<GlobalValue *>(variable)) {
|
||||
// 情况 B: 全局变量 (GlobalValue)
|
||||
gepBasePointer = glob;
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), indices.begin(), indices.end());
|
||||
} else if (ConstantVariable *constV = dynamic_cast<ConstantVariable *>(variable)) {
|
||||
gepBasePointer = constV;
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), indices.begin(), indices.end());
|
||||
}
|
||||
// 左值为地址
|
||||
LValue = getGEPAddressInst(gepBasePointer, gepIndices);
|
||||
}
|
||||
|
||||
// 左值右值类型不同处理:根据最终元素类型进行转换
|
||||
if (targetElementType != value->getType()) {
|
||||
ConstantValue * constValue = dynamic_cast<ConstantValue *>(value);
|
||||
Value* RValue = std::any_cast<Value *>(visitExp(ctx->exp())); // 右值
|
||||
|
||||
// 先推断 LValue 的类型
|
||||
// 如果 LValue 是指向数组的指针,则需要根据 indices 获取正确的类型
|
||||
// 如果 LValue 是标量,则直接使用其类型
|
||||
// 注意:LValue 的类型可能是指向数组的指针 (e.g., int(*)[3]) 或者指向标量的指针 (e.g., int*) 也能推断
|
||||
Type* LType = builder.getIndexedType(variable->getType(), indices);
|
||||
Type* RType = RValue->getType();
|
||||
|
||||
if (LType != RType) {
|
||||
ConstantValue * constValue = dynamic_cast<ConstantValue *>(RValue);
|
||||
if (constValue != nullptr) {
|
||||
if (targetElementType == Type::getFloatType()) {
|
||||
value = ConstantFloating::get(static_cast<float>(constValue->getInt()));
|
||||
if (LType == Type::getFloatType()) {
|
||||
RValue = ConstantFloating::get(static_cast<float>(constValue->getFloat()));
|
||||
} else { // 假设如果不是浮点型,就是整型
|
||||
value = ConstantInteger::get(static_cast<int>(constValue->getFloat()));
|
||||
RValue = ConstantInteger::get(static_cast<int>(constValue->getInt()));
|
||||
}
|
||||
} else {
|
||||
if (targetElementType == Type::getFloatType()) {
|
||||
value = builder.createIToFInst(value);
|
||||
if (LType == Type::getFloatType()) {
|
||||
RValue = builder.createIToFInst(RValue);
|
||||
} else { // 假设如果不是浮点型,就是整型
|
||||
value = builder.createFtoIInst(value);
|
||||
RValue = builder.createFtoIInst(RValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算目标地址:如果 dims 为空,就是变量本身地址;否则通过 GEP 计算
|
||||
Value* targetAddress = variable;
|
||||
if (!dims.empty()) {
|
||||
targetAddress = getGEPAddressInst(variable, dims);
|
||||
}
|
||||
|
||||
builder.createStoreInst(value, targetAddress);
|
||||
builder.createStoreInst(RValue, LValue);
|
||||
|
||||
return std::any();
|
||||
}
|
||||
@ -672,30 +734,41 @@ std::any SysYIRGenerator::visitReturnStmt(SysYParser::ReturnStmtContext *ctx) {
|
||||
}
|
||||
|
||||
|
||||
// SysYIRGenerator.cpp (修改部分)
|
||||
// 辅助函数:计算给定类型中嵌套的数组维度数量
|
||||
// 例如:
|
||||
// - 对于 i32* 类型,它指向 i32,维度为 0。
|
||||
// - 对于 [10 x i32]* 类型,它指向 [10 x i32],维度为 1。
|
||||
// - 对于 [20 x [10 x i32]]* 类型,它指向 [20 x [10 x i32]],维度为 2。
|
||||
unsigned SysYIRGenerator::countArrayDimensions(Type* type) {
|
||||
unsigned dims = 0;
|
||||
Type* currentType = type;
|
||||
|
||||
// 如果是指针类型,先获取它指向的基础类型
|
||||
if (currentType->isPointer()) {
|
||||
currentType = currentType->as<PointerType>()->getBaseType();
|
||||
}
|
||||
|
||||
// 递归地计算数组的维度层数
|
||||
while (currentType && currentType->isArray()) {
|
||||
dims++;
|
||||
currentType = currentType->as<ArrayType>()->getElementType();
|
||||
}
|
||||
return dims;
|
||||
}
|
||||
|
||||
std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
|
||||
std::string name = ctx->Ident()->getText();
|
||||
User* variable = module->getVariable(name);
|
||||
Value* variable = module->getVariable(name);
|
||||
|
||||
Value* value = nullptr;
|
||||
if (variable == nullptr) {
|
||||
throw std::runtime_error("Variable " + name + " not found.");
|
||||
}
|
||||
|
||||
std::vector<Value *> dims;
|
||||
for (const auto &exp : ctx->exp()) {
|
||||
dims.push_back(std::any_cast<Value *>(visitExp(exp)));
|
||||
}
|
||||
|
||||
// 1. 获取变量的声明维度数量
|
||||
unsigned declaredNumDims = 0;
|
||||
if (AllocaInst* alloc = dynamic_cast<AllocaInst*>(variable)) {
|
||||
declaredNumDims = alloc->getNumDims();
|
||||
} else if (GlobalValue* glob = dynamic_cast<GlobalValue*>(variable)) {
|
||||
declaredNumDims = glob->getNumDims();
|
||||
} else if (ConstantVariable* constV = dynamic_cast<ConstantVariable*>(variable)) {
|
||||
declaredNumDims = constV->getNumDims();
|
||||
}
|
||||
unsigned declaredNumDims = countArrayDimensions(variable->getType());
|
||||
|
||||
// 2. 处理常量变量 (ConstantVariable) 且所有索引都是常量的情况
|
||||
ConstantVariable* constVar = dynamic_cast<ConstantVariable *>(variable);
|
||||
@ -731,20 +804,56 @@ std::any SysYIRGenerator::visitLValue(SysYParser::LValueContext *ctx) {
|
||||
}
|
||||
} else {
|
||||
// 访问数组元素或子数组(有索引,或变量本身是数组/多维指针)
|
||||
Value* targetAddress = nullptr;
|
||||
|
||||
Value* gepBasePointer = nullptr;
|
||||
std::vector<Value*> gepIndices; // 准备传递给 getGEPAddressInst 的索引列表
|
||||
// GEP 的基指针就是变量本身(它是一个指向内存的指针)
|
||||
if (dynamic_cast<AllocaInst*>(variable) || dynamic_cast<GlobalValue*>(variable) || (constVar != nullptr)) {
|
||||
// 允许对 ConstantVariable (如果它代表全局数组常量) 进行 GEP
|
||||
targetAddress = getGEPAddressInst(variable, dims);
|
||||
if (AllocaInst *alloc = dynamic_cast<AllocaInst *>(variable)) {
|
||||
// 情况 A: 局部变量 (AllocaInst)
|
||||
// 获取 AllocaInst 分配的内存的实际类型。
|
||||
// 例如:对于 `int b[10][20];`,`allocatedType` 是 `[10 x [20 x i32]]`。
|
||||
// 对于 `int b[][20]` 的函数参数,其 AllocaInst 存储的是一个指针,
|
||||
// 此时 `allocatedType` 是 `[20 x i32]*`。
|
||||
Type* allocatedType = alloc->getType()->as<PointerType>()->getBaseType();
|
||||
|
||||
if (allocatedType->isPointer()) {
|
||||
// 如果 AllocaInst 分配的是一个指针类型 (例如,用于存储函数参数的指针,如 int b[][20] 中的 b)
|
||||
// 即 `allocatedType` 是一个指向数组指针的指针 (e.g., [20 x i32]**)
|
||||
// 那么 GEP 的基指针是加载这个指针变量的值。
|
||||
gepBasePointer = builder.createLoadInst(alloc); // 加载出实际的指针值 (e.g., [20 x i32]*)
|
||||
// 对于这种参数指针,用户提供的索引直接作用于它。不需要额外的 0。
|
||||
gepIndices = dims;
|
||||
} else {
|
||||
// 如果 AllocaInst 分配的是实际的数组数据 (例如,int b[10][20] 中的 b)
|
||||
// 那么 AllocaInst 本身就是 GEP 的基指针。
|
||||
// 这里的 `alloc` 是指向数组的指针 (e.g., [10 x [20 x i32]]*)
|
||||
gepBasePointer = alloc; // 类型是 [10 x [20 x i32]]*
|
||||
// 对于这种完整的数组分配,GEP 的第一个索引必须是 0,用于“步过”整个数组。
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
}
|
||||
} else if (GlobalValue *glob = dynamic_cast<GlobalValue *>(variable)) {
|
||||
// 情况 B: 全局变量 (GlobalValue)
|
||||
// GlobalValue 总是指向全局数据的指针。
|
||||
gepBasePointer = glob; // 类型是 [61 x [67 x i32]]*
|
||||
// 对于全局数组,GEP 的第一个索引必须是 0,用于“步过”整个数组。
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
} else if (ConstantVariable *constV = dynamic_cast<ConstantVariable *>(variable)) {
|
||||
// 情况 C: 常量变量 (ConstantVariable),如果它代表全局数组常量
|
||||
// 假设 ConstantVariable 可以直接作为 GEP 的基指针。
|
||||
gepBasePointer = constV;
|
||||
// 对于常量数组,也需要 0 索引来“步过”整个数组。
|
||||
// 这里可以进一步检查 constV->getType()->as<PointerType>()->getBaseType()->isArray()
|
||||
// 但为了简洁,假设所有 ConstantVariable 作为 GEP 基指针时都需要此 0。
|
||||
gepIndices.push_back(ConstantInteger::get(0));
|
||||
gepIndices.insert(gepIndices.end(), dims.begin(), dims.end());
|
||||
} else {
|
||||
// 其他情况(例如尝试对非指针类型或不支持的 LValue 进行 GEP)应报错
|
||||
assert(false && "LValue variable type not supported for GEP or dynamic load.");
|
||||
return static_cast<Value*>(nullptr);
|
||||
assert(false && "LValue variable type not supported for GEP base pointer.");
|
||||
return static_cast<Value *>(nullptr);
|
||||
}
|
||||
|
||||
// 现在 targetAddress 持有元素或子数组的地址。
|
||||
// 需要判断是加载值,还是返回子数组的地址。
|
||||
// 现在调用 getGEPAddressInst,传入正确准备的基指针和索引列表
|
||||
Value *targetAddress = getGEPAddressInst(gepBasePointer, gepIndices);
|
||||
|
||||
// 如果提供的索引数量少于声明的维度数量,则表示访问的是子数组,返回其地址
|
||||
if (dims.size() < declaredNumDims) {
|
||||
@ -796,32 +905,63 @@ std::any SysYIRGenerator::visitCall(SysYParser::CallContext *ctx) {
|
||||
|
||||
std::vector<Value *> args = {};
|
||||
if (funcName == "starttime" || funcName == "stoptime") {
|
||||
// 如果是starttime或stoptime函数
|
||||
// TODO: 这里需要处理starttime和stoptime函数的参数
|
||||
// args.emplace_back()
|
||||
args.emplace_back(
|
||||
ConstantInteger::get(static_cast<int>(ctx->getStart()->getLine())));
|
||||
} else {
|
||||
if (ctx->funcRParams() != nullptr) {
|
||||
args = std::any_cast<std::vector<Value *>>(visitFuncRParams(ctx->funcRParams()));
|
||||
}
|
||||
|
||||
auto params = function->getEntryBlock()->getArguments();
|
||||
for (size_t i = 0; i < args.size(); i++) {
|
||||
// 参数类型转换
|
||||
if (params[i]->getType() != args[i]->getType() &&
|
||||
(params[i]->getNumDims() != 0 ||
|
||||
params[i]->getType()->as<PointerType>()->getBaseType() != args[i]->getType())) {
|
||||
ConstantValue * constValue = dynamic_cast<ConstantValue *>(args[i]);
|
||||
// 获取形参列表。`getArguments()` 返回的是 `Argument*` 的集合,
|
||||
// 每个 `Argument` 代表一个函数形参,其 `getType()` 就是指向形参的类型的指针类型。
|
||||
auto formalParams = function->getArguments();
|
||||
|
||||
// 检查实参和形参数量是否匹配。
|
||||
if (args.size() != formalParams.size()) {
|
||||
std::cerr << "Error: Function call argument count mismatch for function '" << funcName << "'." << std::endl;
|
||||
assert(false && "Function call argument count mismatch!");
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
// 形参的类型 (e.g., i32, float, i32*, [10 x i32]*)
|
||||
Type* formalParamExpectedValueType = formalParams[i]->getType();
|
||||
// 实参的实际类型 (e.g., i32, float, i32*, [67 x i32]*)
|
||||
Type* actualArgType = args[i]->getType();
|
||||
// 如果实参类型与形参类型不匹配,则尝试进行类型转换
|
||||
if (formalParamExpectedValueType != actualArgType) {
|
||||
ConstantValue *constValue = dynamic_cast<ConstantValue *>(args[i]);
|
||||
if (constValue != nullptr) {
|
||||
if (params[i]->getType() == Type::getPointerType(Type::getFloatType())) {
|
||||
args[i] = ConstantInteger::get(static_cast<float>(constValue->getInt()));
|
||||
if (formalParamExpectedValueType->isInt() && actualArgType->isFloat()) {
|
||||
args[i] = ConstantInteger::get(static_cast<int>(constValue->getFloat()));
|
||||
} else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) {
|
||||
args[i] = ConstantFloating::get(static_cast<float>(constValue->getInt()));
|
||||
} else {
|
||||
args[i] = ConstantFloating::get(static_cast<int>(constValue->getFloat()));
|
||||
// 如果是常量但不是简单的 int/float 标量转换,
|
||||
// 或者是指针常量需要 bitcast,则让它进入非常量转换逻辑。
|
||||
// 例如,一个常量数组的地址,需要 bitcast 成另一种指针类型。
|
||||
// 目前不知道样例有没有这种情况,所以这里不做处理。
|
||||
}
|
||||
} else {
|
||||
if (params[i]->getType() == Type::getPointerType(Type::getFloatType())) {
|
||||
args[i] = builder.createIToFInst(args[i]);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// 1. 标量值类型转换 (例如:int_reg 到 float_reg,float_reg 到 int_reg)
|
||||
if (formalParamExpectedValueType->isInt() && actualArgType->isFloat()) {
|
||||
args[i] = builder.createFtoIInst(args[i]);
|
||||
} else if (formalParamExpectedValueType->isFloat() && actualArgType->isInt()) {
|
||||
args[i] = builder.createIToFInst(args[i]);
|
||||
}
|
||||
// 2. 指针类型转换 (例如数组退化:`[N x T]*` 到 `T*`,或兼容指针类型之间) TODO:不清楚有没有这种样例
|
||||
// 这种情况常见于数组参数,实参可能是一个更具体的数组指针类型,
|
||||
// 而形参是其退化后的基础指针类型。LLVM 的 `bitcast` 指令可以用于
|
||||
// 在相同大小的指针类型之间进行转换,这对于数组退化至关重要。
|
||||
// else if (formalParamType->isPointer() && actualArgType->isPointer()) {
|
||||
// 检查指针基类型是否兼容,或者是否是数组退化导致的类型不同。
|
||||
// 使用 bitcast,
|
||||
// args[i] = builder.createBitCastInst(args[i], formalParamType);
|
||||
// }
|
||||
// 3. 其他未预期的类型不匹配
|
||||
// 如果代码执行到这里,说明存在编译器前端未处理的类型不兼容或错误。
|
||||
else {
|
||||
// assert(false && "Unhandled type mismatch for function call argument.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -891,7 +1031,7 @@ std::any SysYIRGenerator::visitFuncRParams(SysYParser::FuncRParamsContext *ctx)
|
||||
std::any SysYIRGenerator::visitMulExp(SysYParser::MulExpContext *ctx) {
|
||||
Value * result = std::any_cast<Value *>(visitUnaryExp(ctx->unaryExp(0)));
|
||||
|
||||
for (size_t i = 1; i < ctx->unaryExp().size(); i++) {
|
||||
for (int i = 1; i < ctx->unaryExp().size(); i++) {
|
||||
auto opNode = dynamic_cast<antlr4::tree::TerminalNode*>(ctx->children[2*i-1]);
|
||||
int opType = opNode->getSymbol()->getType();
|
||||
|
||||
@ -967,7 +1107,7 @@ std::any SysYIRGenerator::visitMulExp(SysYParser::MulExpContext *ctx) {
|
||||
std::any SysYIRGenerator::visitAddExp(SysYParser::AddExpContext *ctx) {
|
||||
Value* result = std::any_cast<Value *>(visitMulExp(ctx->mulExp(0)));
|
||||
|
||||
for (size_t i = 1; i < ctx->mulExp().size(); i++) {
|
||||
for (int i = 1; i < ctx->mulExp().size(); i++) {
|
||||
auto opNode = dynamic_cast<antlr4::tree::TerminalNode*>(ctx->children[2*i-1]);
|
||||
int opType = opNode->getSymbol()->getType();
|
||||
|
||||
@ -1028,7 +1168,7 @@ std::any SysYIRGenerator::visitAddExp(SysYParser::AddExpContext *ctx) {
|
||||
std::any SysYIRGenerator::visitRelExp(SysYParser::RelExpContext *ctx) {
|
||||
Value* result = std::any_cast<Value *>(visitAddExp(ctx->addExp(0)));
|
||||
|
||||
for (size_t i = 1; i < ctx->addExp().size(); i++) {
|
||||
for (int i = 1; i < ctx->addExp().size(); i++) {
|
||||
auto opNode = dynamic_cast<antlr4::tree::TerminalNode*>(ctx->children[2*i-1]);
|
||||
int opType = opNode->getSymbol()->getType();
|
||||
|
||||
@ -1100,7 +1240,7 @@ std::any SysYIRGenerator::visitRelExp(SysYParser::RelExpContext *ctx) {
|
||||
std::any SysYIRGenerator::visitEqExp(SysYParser::EqExpContext *ctx) {
|
||||
Value * result = std::any_cast<Value *>(visitRelExp(ctx->relExp(0)));
|
||||
|
||||
for (size_t i = 1; i < ctx->relExp().size(); i++) {
|
||||
for (int i = 1; i < ctx->relExp().size(); i++) {
|
||||
auto opNode = dynamic_cast<antlr4::tree::TerminalNode*>(ctx->children[2*i-1]);
|
||||
int opType = opNode->getSymbol()->getType();
|
||||
|
||||
@ -1174,7 +1314,7 @@ std::any SysYIRGenerator::visitLAndExp(SysYParser::LAndExpContext *ctx){
|
||||
BasicBlock *falseBlock = builder.getFalseBlock();
|
||||
auto conds = ctx->eqExp();
|
||||
|
||||
for (size_t i = 0; i < conds.size() - 1; i++) {
|
||||
for (int i = 0; i < conds.size() - 1; i++) {
|
||||
|
||||
labelstring << "AND.L" << builder.getLabelIndex();
|
||||
BasicBlock *newtrueBlock = function->addBasicBlock(labelstring.str());
|
||||
@ -1205,7 +1345,7 @@ auto SysYIRGenerator::visitLOrExp(SysYParser::LOrExpContext *ctx) -> std::any {
|
||||
Function *function = curBlock->getParent();
|
||||
auto conds = ctx->lAndExp();
|
||||
|
||||
for (size_t i = 0; i < conds.size() - 1; i++) {
|
||||
for (int i = 0; i < conds.size() - 1; i++) {
|
||||
labelstring << "OR.L" << builder.getLabelIndex();
|
||||
BasicBlock *newFalseBlock = function->addBasicBlock(labelstring.str());
|
||||
labelstring.str("");
|
||||
@ -1222,6 +1362,7 @@ auto SysYIRGenerator::visitLOrExp(SysYParser::LOrExpContext *ctx) -> std::any {
|
||||
return std::any();
|
||||
}
|
||||
|
||||
// attention : 这里的type是数组元素的type
|
||||
void Utils::tree2Array(Type *type, ArrayValueTree *root,
|
||||
const std::vector<Value *> &dims, unsigned numDims,
|
||||
ValueCounter &result, IRBuilder *builder) {
|
||||
@ -1292,11 +1433,13 @@ void Utils::createExternalFunction(
|
||||
auto entry = function->getEntryBlock();
|
||||
pBuilder->setPosition(entry, entry->end());
|
||||
|
||||
for (size_t i = 0; i < paramTypes.size(); ++i) {
|
||||
for (int i = 0; i < paramTypes.size(); ++i) {
|
||||
auto arg = new Argument(paramTypes[i], function, i, paramNames[i]);
|
||||
auto alloca = pBuilder->createAllocaInst(
|
||||
Type::getPointerType(paramTypes[i]), paramDims[i], paramNames[i]);
|
||||
entry->insertArgument(alloca);
|
||||
// pModule->addVariable(paramNames[i], alloca);
|
||||
Type::getPointerType(paramTypes[i]), {}, paramNames[i]);
|
||||
function->insertArgument(arg);
|
||||
auto store = pBuilder->createStoreInst(arg, alloca);
|
||||
pModule->addVariable(paramNames[i], alloca);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -72,6 +72,8 @@ std::string SysYPrinter::getValueName(Value *value) {
|
||||
return std::to_string(constVal->getInt());
|
||||
} else if (auto constVar = dynamic_cast<ConstantVariable*>(value)) {
|
||||
return constVar->getName(); // 假设ConstantVariable有自己的名字或通过getByIndices获取值
|
||||
} else if (auto argVar = dynamic_cast<Argument*>(value)) {
|
||||
return "%" + argVar->getName(); // 假设ArgumentVariable有自己的名字
|
||||
}
|
||||
assert(false && "Unknown value type or unable to get value name");
|
||||
return "";
|
||||
@ -134,7 +136,7 @@ void SysYPrinter::printFunction(Function *function) {
|
||||
|
||||
auto entryBlock = function->getEntryBlock();
|
||||
const auto &args_types = function->getParamTypes();
|
||||
auto &args = entryBlock->getArguments();
|
||||
auto &args = function->getArguments();
|
||||
|
||||
int i = 0;
|
||||
for (const auto &args_type : args_types) {
|
||||
@ -150,9 +152,7 @@ void SysYPrinter::printFunction(Function *function) {
|
||||
for (const auto &blockIter : function->getBasicBlocks()) {
|
||||
// Basic block label
|
||||
BasicBlock* blockPtr = blockIter.get();
|
||||
if (blockPtr == function->getEntryBlock()) {
|
||||
std::cout << "entry:" << std::endl;
|
||||
} else if (!blockPtr->getName().empty()) {
|
||||
if (!blockPtr->getName().empty()) {
|
||||
std::cout << blockPtr->getName() << ":" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
63
src/include/DCE.h
Normal file
63
src/include/DCE.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#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 {
|
||||
public:
|
||||
// 构造函数
|
||||
DCE() : OptimizationPass("DCE", Granularity::Function) {}
|
||||
|
||||
// 静态成员,作为该遍的唯一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
|
||||
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
#include "SysYIRAnalyser.h"
|
||||
#include "SysYIRPrinter.h"
|
||||
#include "SysYIROptUtils.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class DeadCodeElimination {
|
||||
private:
|
||||
Module *pModule;
|
||||
ControlFlowAnalysis *pCFA; // 控制流分析指针
|
||||
ActiveVarAnalysis *pAVA; // 活跃变量分析指针
|
||||
DataFlowAnalysisUtils dataFlowAnalysisUtils; // 数据流分析工具类
|
||||
|
||||
public:
|
||||
explicit DeadCodeElimination(Module *pMoudle,
|
||||
ControlFlowAnalysis *pCFA = nullptr,
|
||||
ActiveVarAnalysis *pAVA = nullptr)
|
||||
: pModule(pMoudle), pCFA(pCFA), pAVA(pAVA), dataFlowAnalysisUtils() {} // 构造函数
|
||||
|
||||
// TODO:根据参数传入的passes来运行不同的死代码删除流程
|
||||
// void runDCEPipeline(const std::vector<std::string>& passes = {
|
||||
// "dead-store", "redundant-load-store", "dead-load", "dead-alloca", "dead-global"
|
||||
// });
|
||||
void runDCEPipeline(); // 运行死代码删除
|
||||
|
||||
void eliminateDeadStores(Function* func, bool& changed); // 消除无用存储
|
||||
void eliminateDeadLoads(Function* func, bool& changed); // 消除无用加载
|
||||
void eliminateDeadAllocas(Function* func, bool& changed); // 消除无用内存分配
|
||||
void eliminateDeadGlobals(bool& changed); // 消除无用全局变量
|
||||
void eliminateDeadIndirectiveAllocas(Function* func, bool& changed); // 消除无用间接内存分配(phi节点)
|
||||
void eliminateDeadRedundantLoadStore(Function* func, bool& changed); // 消除冗余加载和存储
|
||||
};
|
||||
} // namespace sysy
|
||||
52
src/include/Dom.h
Normal file
52
src/include/Dom.h
Normal file
@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pass.h" // 包含 Pass 框架
|
||||
#include "IR.h" // 包含 IR 定义
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 支配树分析结果类 (保持不变)
|
||||
class DominatorTree : public AnalysisResultBase {
|
||||
public:
|
||||
DominatorTree(Function* F);
|
||||
const std::set<BasicBlock*>* getDominators(BasicBlock* BB) const;
|
||||
BasicBlock* getImmediateDominator(BasicBlock* BB) const;
|
||||
const std::set<BasicBlock*>* getDominanceFrontier(BasicBlock* BB) const;
|
||||
const std::map<BasicBlock*, std::set<BasicBlock*>>& getDominatorsMap() const { return Dominators; }
|
||||
const std::map<BasicBlock*, BasicBlock*>& getIDomsMap() const { return IDoms; }
|
||||
const std::map<BasicBlock*, std::set<BasicBlock*>>& getDominanceFrontiersMap() const { return DominanceFrontiers; }
|
||||
void computeDominators(Function* F);
|
||||
void computeIDoms(Function* F);
|
||||
void computeDominanceFrontiers(Function* F);
|
||||
private:
|
||||
Function* AssociatedFunction;
|
||||
std::map<BasicBlock*, std::set<BasicBlock*>> Dominators;
|
||||
std::map<BasicBlock*, BasicBlock*> IDoms;
|
||||
std::map<BasicBlock*, std::set<BasicBlock*>> DominanceFrontiers;
|
||||
};
|
||||
|
||||
|
||||
// 支配树分析遍
|
||||
class DominatorTreeAnalysisPass : public AnalysisPass {
|
||||
public:
|
||||
// 唯一的 Pass ID
|
||||
static void *ID;
|
||||
|
||||
DominatorTreeAnalysisPass() : AnalysisPass("DominatorTreeAnalysis", Pass::Granularity::Function) {}
|
||||
|
||||
// 实现 getPassID
|
||||
void* getPassID() const override { return &ID; }
|
||||
|
||||
bool runOnFunction(Function* F, AnalysisManager &AM) override;
|
||||
|
||||
std::unique_ptr<AnalysisResultBase> getResult() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<DominatorTree> CurrentDominatorTree;
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
101
src/include/IR.h
101
src/include/IR.h
@ -468,7 +468,6 @@ public:
|
||||
|
||||
// --- End of refactored ConstantValue and related classes ---
|
||||
|
||||
|
||||
class Instruction;
|
||||
class Function;
|
||||
class BasicBlock;
|
||||
@ -487,7 +486,6 @@ public:
|
||||
|
||||
using inst_list = std::list<std::unique_ptr<Instruction>>;
|
||||
using iterator = inst_list::iterator;
|
||||
using arg_list = std::vector<AllocaInst *>;
|
||||
using block_list = std::vector<BasicBlock *>;
|
||||
using block_set = std::unordered_set<BasicBlock *>;
|
||||
|
||||
@ -495,7 +493,6 @@ protected:
|
||||
|
||||
Function *parent; ///< 从属的函数
|
||||
inst_list instructions; ///< 拥有的指令序列
|
||||
arg_list arguments; ///< 分配空间后的形式参数列表
|
||||
block_list successors; ///< 前驱列表
|
||||
block_list predecessors; ///< 后继列表
|
||||
bool reachable = false;
|
||||
@ -515,19 +512,25 @@ public:
|
||||
public:
|
||||
|
||||
unsigned getNumInstructions() const { return instructions.size(); }
|
||||
unsigned getNumArguments() const { return arguments.size(); }
|
||||
unsigned getNumPredecessors() const { return predecessors.size(); }
|
||||
unsigned getNumSuccessors() const { return successors.size(); }
|
||||
Function* getParent() const { return parent; }
|
||||
void setParent(Function *func) { parent = func; }
|
||||
inst_list& getInstructions() { return instructions; }
|
||||
arg_list& getArguments() { return arguments; }
|
||||
const block_list& getPredecessors() const { return predecessors; }
|
||||
auto getInstructions_Range() const { return make_range(instructions); }
|
||||
block_list& getPredecessors() { return predecessors; }
|
||||
void clearPredecessors() { predecessors.clear(); }
|
||||
block_list& getSuccessors() { return successors; }
|
||||
void clearSuccessors() { successors.clear(); }
|
||||
iterator begin() { return instructions.begin(); }
|
||||
iterator end() { return instructions.end(); }
|
||||
iterator terminator() { return std::prev(end()); }
|
||||
void insertArgument(AllocaInst *inst) { arguments.push_back(inst); }
|
||||
bool hasSuccessor(BasicBlock *block) const {
|
||||
return std::find(successors.begin(), successors.end(), block) != successors.end();
|
||||
} ///< 判断是否有后继块
|
||||
bool hasPredecessor(BasicBlock *block) const {
|
||||
return std::find(predecessors.begin(), predecessors.end(), block) != predecessors.end();
|
||||
} ///< 判断是否有前驱块
|
||||
void addPredecessor(BasicBlock *block) {
|
||||
if (std::find(predecessors.begin(), predecessors.end(), block) == predecessors.end()) {
|
||||
predecessors.push_back(block);
|
||||
@ -580,6 +583,15 @@ public:
|
||||
next->addPredecessor(prev);
|
||||
}
|
||||
void removeInst(iterator pos) { instructions.erase(pos); }
|
||||
void removeInst(Instruction *inst) {
|
||||
auto pos = std::find_if(instructions.begin(), instructions.end(),
|
||||
[inst](const std::unique_ptr<Instruction> &i) { return i.get() == inst; });
|
||||
if (pos != instructions.end()) {
|
||||
instructions.erase(pos);
|
||||
} else {
|
||||
assert(false && "Instruction not found in BasicBlock");
|
||||
}
|
||||
} ///< 移除指定位置的指令
|
||||
iterator moveInst(iterator sourcePos, iterator targetPos, BasicBlock *block);
|
||||
};
|
||||
|
||||
@ -1047,6 +1059,8 @@ public:
|
||||
}; // class UncondBrInst
|
||||
|
||||
//! Conditional branch
|
||||
// 这里的args是指向条件分支的两个分支的参数列表但是现在弃用了
|
||||
// 通过mem2reg优化后,数据流分析将不会由arguments来传递
|
||||
class CondBrInst : public Instruction {
|
||||
friend class IRBuilder;
|
||||
friend class Function;
|
||||
@ -1072,17 +1086,17 @@ public:
|
||||
BasicBlock* getElseBlock() const {
|
||||
return dynamic_cast<BasicBlock *>(getOperand(2));
|
||||
}
|
||||
auto getThenArguments() const {
|
||||
auto begin = std::next(operand_begin(), 3);
|
||||
auto end = std::next(begin, getThenBlock()->getNumArguments());
|
||||
return make_range(begin, end);
|
||||
}
|
||||
auto getElseArguments() const {
|
||||
auto begin =
|
||||
std::next(operand_begin(), 3 + getThenBlock()->getNumArguments());
|
||||
auto end = operand_end();
|
||||
return make_range(begin, end);
|
||||
}
|
||||
// auto getThenArguments() const {
|
||||
// auto begin = std::next(operand_begin(), 3);
|
||||
// // auto end = std::next(begin, getThenBlock()->getNumArguments());
|
||||
// return make_range(begin, end);
|
||||
// }
|
||||
// auto getElseArguments() const {
|
||||
// auto begin =
|
||||
// std::next(operand_begin(), 3 + getThenBlock()->getNumArguments());
|
||||
// auto end = operand_end();
|
||||
// return make_range(begin, end);
|
||||
// }
|
||||
|
||||
}; // class CondBrInst
|
||||
|
||||
@ -1107,24 +1121,24 @@ public:
|
||||
|
||||
|
||||
class GetElementPtrInst : public Instruction {
|
||||
friend class IRBuilder; // 如果您有IRBuilder来创建指令,需要friend
|
||||
friend class IRBuilder;
|
||||
|
||||
protected:
|
||||
// GEP的构造函数:
|
||||
// resultType: GEP计算出的地址的类型 (通常是指向目标元素类型的指针)
|
||||
// basePointer: 基指针 (第一个操作数)
|
||||
// indices: 索引列表 (后续操作数)
|
||||
GetElementPtrInst(Value *basePointer,
|
||||
const std::vector<Value *> &indices = {},
|
||||
BasicBlock *parent = nullptr, const std::string &name = "")
|
||||
: Instruction(Kind::kGetElementPtr, basePointer->getType(), parent, name) {
|
||||
GetElementPtrInst(Type *resultType,
|
||||
Value *basePointer,
|
||||
const std::vector<Value *> &indices = {},
|
||||
BasicBlock *parent = nullptr, const std::string &name = "")
|
||||
: Instruction(Kind::kGetElementPtr, resultType, parent, name) {
|
||||
assert(basePointer && "GEP base pointer cannot be null!");
|
||||
// TODO : 安全检查
|
||||
assert(basePointer->getType()->isPointer() );
|
||||
addOperand(basePointer); // 第一个操作数是基指针
|
||||
addOperands(indices); // 随后的操作数是索引
|
||||
}
|
||||
|
||||
public:
|
||||
Value* getBasePointer() const { return getOperand(0); }
|
||||
unsigned getNumIndices() const { return getNumOperands() - 1; }
|
||||
@ -1138,7 +1152,7 @@ public:
|
||||
static GetElementPtrInst* create(Type *resultType, Value *basePointer,
|
||||
const std::vector<Value *> &indices = {},
|
||||
BasicBlock *parent = nullptr, const std::string &name = "") {
|
||||
return new GetElementPtrInst(basePointer, indices, parent, name);
|
||||
return new GetElementPtrInst(resultType, basePointer, indices, parent, name);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1225,17 +1239,33 @@ public:
|
||||
class GlobalValue;
|
||||
|
||||
|
||||
class Argument : public Value {
|
||||
protected:
|
||||
Function *func;
|
||||
int index;
|
||||
|
||||
public:
|
||||
Argument(Type *type, Function *func, int index, const std::string &name = "")
|
||||
: Value(type, name), func(func), index(index) {}
|
||||
|
||||
public:
|
||||
Function* getParent() const { return func; }
|
||||
int getIndex() const { return index; }
|
||||
};
|
||||
|
||||
|
||||
class Module;
|
||||
//! Function definitionclass
|
||||
class Function : public Value {
|
||||
friend class Module;
|
||||
protected:
|
||||
Function(Module *parent, Type *type, const std::string &name) : Value(type, name), parent(parent) {
|
||||
blocks.emplace_back(new BasicBlock(this));
|
||||
blocks.emplace_back(new BasicBlock(this, "entry_" + name)); ///< 创建一个入口基本块
|
||||
}
|
||||
|
||||
public:
|
||||
using block_list = std::list<std::unique_ptr<BasicBlock>>;
|
||||
using arg_list = std::vector<Argument *>;
|
||||
enum FunctionAttribute : uint64_t {
|
||||
PlaceHolder = 0x0UL,
|
||||
Pure = 0x1UL << 0,
|
||||
@ -1247,6 +1277,7 @@ public:
|
||||
protected:
|
||||
Module *parent; ///< 函数的父模块
|
||||
block_list blocks; ///< 函数包含的基本块列表
|
||||
arg_list arguments; ///< 函数参数列表
|
||||
FunctionAttribute attribute = PlaceHolder; ///< 函数属性
|
||||
std::set<Function *> callees; ///< 函数调用的函数集合
|
||||
public:
|
||||
@ -1271,6 +1302,16 @@ protected:
|
||||
auto getBasicBlocks() { return make_range(blocks); }
|
||||
block_list& getBasicBlocks_NoRange() { return blocks; }
|
||||
BasicBlock* getEntryBlock() { return blocks.front().get(); }
|
||||
void insertArgument(Argument *arg) { arguments.push_back(arg); }
|
||||
arg_list& getArguments() { return arguments; }
|
||||
unsigned getNumArguments() const { return arguments.size(); }
|
||||
Argument* getArgument(unsigned index) const {
|
||||
assert(index < arguments.size() && "Argument index out of bounds");
|
||||
return arguments[index];
|
||||
} ///< 获取位置为index的参数
|
||||
auto getArgumentsRange() const {
|
||||
return make_range(arguments.begin(), arguments.end());
|
||||
} ///< 获取参数列表的范围
|
||||
void removeBasicBlock(BasicBlock *blockToRemove) {
|
||||
auto is_same_ptr = [blockToRemove](const std::unique_ptr<BasicBlock> &ptr) { return ptr.get() == blockToRemove; };
|
||||
blocks.remove_if(is_same_ptr);
|
||||
@ -1387,7 +1428,7 @@ class ConstantVariable : public User {
|
||||
using SymbolTableNode = struct SymbolTableNode {
|
||||
SymbolTableNode *pNode; ///< 父节点
|
||||
std::vector<SymbolTableNode *> children; ///< 子节点列表
|
||||
std::map<std::string, User *> varList; ///< 变量列表
|
||||
std::map<std::string, Value *> varList; ///< 变量列表
|
||||
};
|
||||
|
||||
|
||||
@ -1402,8 +1443,8 @@ class SymbolTable {
|
||||
public:
|
||||
SymbolTable() = default;
|
||||
|
||||
User* getVariable(const std::string &name) const; ///< 根据名字name以及当前作用域获取变量
|
||||
User* addVariable(const std::string &name, User *variable); ///< 添加变量
|
||||
Value* getVariable(const std::string &name) const; ///< 根据名字name以及当前作用域获取变量
|
||||
Value* addVariable(const std::string &name, Value *variable); ///< 添加变量
|
||||
std::vector<std::unique_ptr<GlobalValue>>& getGlobals(); ///< 获取全局变量列表
|
||||
const std::vector<std::unique_ptr<ConstantVariable>>& getConsts() const; ///< 获取常量列表
|
||||
void enterNewScope(); ///< 进入新的作用域
|
||||
@ -1465,7 +1506,7 @@ class Module {
|
||||
void addVariable(const std::string &name, AllocaInst *variable) {
|
||||
variableTable.addVariable(name, variable);
|
||||
} ///< 添加变量
|
||||
User* getVariable(const std::string &name) {
|
||||
Value* getVariable(const std::string &name) {
|
||||
return variableTable.getVariable(name);
|
||||
} ///< 根据名字name和当前作用域获取变量
|
||||
Function* getFunction(const std::string &name) const {
|
||||
|
||||
@ -294,15 +294,41 @@ class IRBuilder {
|
||||
return inst;
|
||||
} ///< 创建store指令
|
||||
PhiInst * createPhiInst(Type *type, const std::vector<Value*> &vals = {}, const std::vector<BasicBlock*> &blks = {}, const std::string &name = "") {
|
||||
auto predNum = block->getNumPredecessors();
|
||||
auto inst = new PhiInst(type, vals, blks, block, name);
|
||||
assert(inst);
|
||||
block->getInstructions().emplace(block->begin(), inst);
|
||||
return inst;
|
||||
} ///< 创建Phi指令
|
||||
GetElementPtrInst* createGetElementPtrInst(Value *basePointer,
|
||||
const std::vector<Value *> &indices = {},
|
||||
const std::string &name = "") {
|
||||
// GetElementPtrInst* createGetElementPtrInst(Value *basePointer,
|
||||
// const std::vector<Value *> &indices = {},
|
||||
// const std::string &name = "") {
|
||||
// std::string newName;
|
||||
// if (name.empty()) {
|
||||
// std::stringstream ss;
|
||||
// ss << tmpIndex;
|
||||
// newName = ss.str();
|
||||
// tmpIndex++;
|
||||
// } else {
|
||||
// newName = name;
|
||||
// }
|
||||
|
||||
// auto inst = new GetElementPtrInst(basePointer, indices, block, newName);
|
||||
// assert(inst);
|
||||
// block->getInstructions().emplace(position, inst);
|
||||
// return inst;
|
||||
// }
|
||||
/**
|
||||
* @brief 根据 LLVM 设计模式创建 GEP 指令。
|
||||
* 它会自动推断返回类型,无需手动指定。
|
||||
*/
|
||||
GetElementPtrInst *createGetElementPtrInst(Value *basePointer, const std::vector<Value *> &indices,
|
||||
const std::string &name = "") {
|
||||
Type *ResultElementType = getIndexedType(basePointer->getType(), indices);
|
||||
if (!ResultElementType) {
|
||||
assert(false && "Invalid GEP indexing!");
|
||||
return nullptr;
|
||||
}
|
||||
Type *ResultType = PointerType::get(ResultElementType);
|
||||
std::string newName;
|
||||
if (name.empty()) {
|
||||
std::stringstream ss;
|
||||
@ -313,11 +339,57 @@ class IRBuilder {
|
||||
newName = name;
|
||||
}
|
||||
|
||||
auto inst = new GetElementPtrInst(basePointer, indices, block, newName);
|
||||
auto inst = new GetElementPtrInst(ResultType, basePointer, indices, block, newName);
|
||||
assert(inst);
|
||||
block->getInstructions().emplace(position, inst);
|
||||
return inst;
|
||||
}
|
||||
|
||||
static Type *getIndexedType(Type *pointerType, const std::vector<Value *> &indices) {
|
||||
assert(pointerType->isPointer() && "base must be a pointer type!");
|
||||
// GEP 的类型推断从基指针所指向的类型开始。
|
||||
// 例如:
|
||||
// - 如果 pointerType 是 `[20 x [10 x i32]]*`,`currentWalkType` 初始为 `[20 x [10 x i32]]`。
|
||||
// - 如果 pointerType 是 `i32*`,`currentWalkType` 初始为 `i32`。
|
||||
// - 如果 pointerType 是 `i32**`,`currentWalkType` 初始为 `i32*`。
|
||||
Type *currentWalkType = pointerType->as<PointerType>()->getBaseType();
|
||||
|
||||
// 遍历所有索引来深入类型层次结构。
|
||||
// `indices` 向量包含了所有 GEP 索引,包括由 `visitLValue` 等函数添加的初始 `0` 索引。
|
||||
for (int i = 0; i < indices.size(); ++i) {
|
||||
if (currentWalkType->isArray()) {
|
||||
// 情况一:当前遍历类型是 `ArrayType`。
|
||||
// 索引用于选择数组元素,`currentWalkType` 更新为数组的元素类型。
|
||||
currentWalkType = currentWalkType->as<ArrayType>()->getElementType();
|
||||
} else if (currentWalkType->isPointer()) {
|
||||
// 情况二:当前遍历类型是 `PointerType`。
|
||||
// 这意味着我们正在通过一个指针来访问其指向的内存。
|
||||
// 索引用于选择该指针所指向的“数组”的元素。
|
||||
// `currentWalkType` 更新为该指针所指向的基础类型。
|
||||
// 例如:如果 `currentWalkType` 是 `i32*`,它将变为 `i32`。
|
||||
// 如果 `currentWalkType` 是 `[10 x i32]*`,它将变为 `[10 x i32]`。
|
||||
currentWalkType = currentWalkType->as<PointerType>()->getBaseType();
|
||||
} else {
|
||||
// 情况三:当前遍历类型是标量类型 (例如 `i32`, `float` 等非聚合、非指针类型)。
|
||||
//
|
||||
// 如果 `currentWalkType` 是标量,并且当前索引 `i` **不是** `indices` 向量中的最后一个索引,
|
||||
// 这意味着尝试对一个标量类型进行进一步的结构性索引,这是**无效的**。
|
||||
// 例如:`int x; x[0];` 对应的 GEP 链中,`x` 的类型是 `i32`,再加 `[0]` 索引就是错误。
|
||||
//
|
||||
// 如果 `currentWalkType` 是标量,且这是**最后一个索引** (`i == indices.size() - 1`),
|
||||
// 那么 GEP 是合法的,它只是计算一个偏移地址,最终的类型就是这个标量类型。
|
||||
// 此时 `currentWalkType` 保持不变,循环结束。
|
||||
if (i < indices.size() - 1) {
|
||||
assert(false && "Invalid GEP indexing: attempting to index into a non-aggregate/non-pointer type with further indices.");
|
||||
return nullptr; // 返回空指针表示类型推断失败
|
||||
}
|
||||
// 如果是最后一个索引,且当前类型是标量,则类型保持不变,这是合法的。
|
||||
// 循环会自然结束,返回正确的 `currentWalkType`。
|
||||
}
|
||||
}
|
||||
// 所有索引处理完毕后,`currentWalkType` 就是 GEP 指令最终计算出的地址所指向的元素的类型。
|
||||
return currentWalkType;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
72
src/include/Liveness.h
Normal file
72
src/include/Liveness.h
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h" // 包含 IR 定义
|
||||
#include "Pass.h" // 包含 Pass 框架
|
||||
#include <algorithm> // for std::set_union, std::set_difference
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 前向声明
|
||||
class Function;
|
||||
class BasicBlock;
|
||||
class Value;
|
||||
class Instruction;
|
||||
|
||||
// 活跃变量分析结果类
|
||||
// 它将包含 LiveIn 和 LiveOut 集合
|
||||
class LivenessAnalysisResult : public AnalysisResultBase {
|
||||
public:
|
||||
LivenessAnalysisResult(Function *F) : AssociatedFunction(F) {}
|
||||
|
||||
// 获取给定基本块的 LiveIn 集合
|
||||
const std::set<Value *> *getLiveIn(BasicBlock *BB) const;
|
||||
|
||||
// 获取给定基本块的 LiveOut 集合
|
||||
const std::set<Value *> *getLiveOut(BasicBlock *BB) const;
|
||||
|
||||
// 暴露内部数据结构,如果需要更直接的访问
|
||||
const std::map<BasicBlock *, std::set<Value *>> &getLiveInSets() const { return liveInSets; }
|
||||
const std::map<BasicBlock *, std::set<Value *>> &getLiveOutSets() const { return liveOutSets; }
|
||||
|
||||
// 核心计算方法,由 LivenessAnalysisPass 调用
|
||||
void computeLiveness(Function *F);
|
||||
|
||||
private:
|
||||
Function *AssociatedFunction; // 这个活跃变量分析是为哪个函数计算的
|
||||
std::map<BasicBlock *, std::set<Value *>> liveInSets;
|
||||
std::map<BasicBlock *, std::set<Value *>> liveOutSets;
|
||||
|
||||
// 辅助函数:计算基本块的 Def 和 Use 集合
|
||||
// Def: 块内定义,且定义在所有使用之前的值
|
||||
// Use: 块内使用,且使用在所有定义之前的值
|
||||
void computeDefUse(BasicBlock *BB, std::set<Value *> &def, std::set<Value *> &use);
|
||||
};
|
||||
|
||||
// 活跃变量分析遍
|
||||
class LivenessAnalysisPass : public AnalysisPass {
|
||||
public:
|
||||
// 唯一的 Pass ID
|
||||
static void *ID; // LLVM 风格的唯一 ID
|
||||
|
||||
LivenessAnalysisPass() : AnalysisPass("LivenessAnalysis", Pass::Granularity::Function) {}
|
||||
|
||||
// 实现 getPassID
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
// 运行分析并返回结果。现在接受 AnalysisManager& AM 参数
|
||||
bool runOnFunction(Function *F, AnalysisManager &AM) override;
|
||||
|
||||
// 获取分析结果的指针。
|
||||
// 注意:AnalysisManager 将会调用此方法来获取结果并进行缓存。
|
||||
std::unique_ptr<AnalysisResultBase> getResult() override;
|
||||
|
||||
private:
|
||||
// 存储当前分析计算出的 LivenessAnalysisResult 实例
|
||||
// runOnFunction 每次调用都会创建新的 LivenessAnalysisResult 对象
|
||||
std::unique_ptr<LivenessAnalysisResult> CurrentLivenessResult;
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,79 +0,0 @@
|
||||
// 假设 Mem2Reg.h 看起来像这样 (你需要根据实际情况调整)
|
||||
#ifndef SYSY_MEM2REG_H
|
||||
#define SYSY_MEM2REG_H
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <stack>
|
||||
#include <queue> // For computeIteratedDomFrontiers
|
||||
|
||||
// Include your IR and analysis headers
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
#include "SysYIRAnalyser.h"
|
||||
#include "SysYIROptUtils.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class Mem2Reg {
|
||||
private:
|
||||
Module* pModule;
|
||||
IRBuilder* pBuilder;
|
||||
ControlFlowAnalysis* controlFlowAnalysis;
|
||||
ActiveVarAnalysis* activeVarAnalysis;
|
||||
DataFlowAnalysisUtils dataFlowAnalysisUtils; // If this is part of Mem2Reg or an external helper
|
||||
|
||||
public:
|
||||
Mem2Reg(Module* module, IRBuilder* builder, ControlFlowAnalysis* cfa, ActiveVarAnalysis* ava)
|
||||
: pModule(module), pBuilder(builder), controlFlowAnalysis(cfa), activeVarAnalysis(ava) {}
|
||||
// Constructor initializes members
|
||||
void run();
|
||||
|
||||
// --- 新增的私有成员变量和方法,用于SSA转换上下文 ---
|
||||
// 这是核心,用于存储 SSA 转换过程中的状态
|
||||
std::vector<AllocaInst*> currentFunctionAllocas; // 当前函数中所有可提升的 alloca
|
||||
// alloca -> set of BasicBlocks where it's defined (stored into)
|
||||
std::unordered_map<AllocaInst*, std::unordered_set<BasicBlock*>> allocaDefsBlock;
|
||||
// alloca -> set of BasicBlocks where it's used (loaded from)
|
||||
std::unordered_map<AllocaInst*, std::unordered_set<BasicBlock*>> allocaUsesBlock;
|
||||
|
||||
// BasicBlock -> Map of (PhiInst, Original AllocaInst)
|
||||
// 用于在 rename 阶段通过 phi 指令找到它代表的原始 alloca
|
||||
std::unordered_map<BasicBlock*, std::unordered_map<PhiInst*, AllocaInst*>> phiMap;
|
||||
std::vector<PhiInst*> allPhiInstructions; // 收集所有创建的 Phi 指令以便后续简化和清理
|
||||
|
||||
// --- 核心 SSA 转换辅助函数 ---
|
||||
// 计算给定定义块集合的迭代支配边界
|
||||
std::unordered_set<BasicBlock*> computeIteratedDomFrontiers(const std::unordered_set<BasicBlock*>& blocks);
|
||||
|
||||
// 分析一个 alloca 的所有 uses,填充 allocaDefsBlock 和 allocaUsesBlock
|
||||
void allocaAnalysis(AllocaInst* alloca);
|
||||
|
||||
// 判断一个 alloca 是否可以被提升为寄存器 (无地址逃逸,标量类型)
|
||||
bool is_promoted(AllocaInst* alloca);
|
||||
|
||||
// 在迭代支配边界处插入 Phi 指令
|
||||
void insertPhiNodes(Function* func);
|
||||
|
||||
// 递归地重命名基本块中的变量并填充 Phi 指令
|
||||
// 这里的 `count` 和 `stacks` 是临时的,用于 DFS 过程中传递状态
|
||||
void renameBlock(BasicBlock* block,
|
||||
std::unordered_map<AllocaInst*, Value*>& currentIncomings,
|
||||
std::unordered_set<BasicBlock*>& visitedBlocks); // 修改为传递 map 和 set
|
||||
|
||||
// 简化冗余的 Phi 指令 (当所有输入都相同时)
|
||||
void simplifyphi(PhiInst* phi);
|
||||
|
||||
// 获取前驱块在后继块前驱列表中的索引,用于 Phi 指令入边
|
||||
int getPredIndex(BasicBlock* pred, BasicBlock* succ);
|
||||
|
||||
// --- Mem2Reg 的主要工作流函数 ---
|
||||
// 对单个函数执行内存到寄存器的提升
|
||||
bool promoteMemoryToRegisters(Function* func);
|
||||
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // SYSY_MEM2REG_H
|
||||
316
src/include/Pass.h
Normal file
316
src/include/Pass.h
Normal file
@ -0,0 +1,316 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional> // For std::function
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <typeindex> // For std::type_index (although void* ID is more common in LLVM)
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
//前向声明
|
||||
class PassManager;
|
||||
class AnalysisManager;
|
||||
|
||||
// 抽象基类:分析结果
|
||||
class AnalysisResultBase {
|
||||
public:
|
||||
virtual ~AnalysisResultBase() = default;
|
||||
};
|
||||
|
||||
// 抽象基类:Pass
|
||||
class Pass {
|
||||
public:
|
||||
enum class Granularity { Module, Function, BasicBlock };
|
||||
|
||||
enum class PassKind { Analysis, Optimization };
|
||||
|
||||
Pass(const std::string &name, Granularity g, PassKind k) : Name(name), G(g), K(k) {}
|
||||
virtual ~Pass() = default;
|
||||
|
||||
const std::string &getName() const { return Name; }
|
||||
Granularity getGranularity() const { return G; }
|
||||
PassKind getPassKind() const { return K; }
|
||||
|
||||
virtual bool runOnModule(Module *M, AnalysisManager& AM) { return false; }
|
||||
virtual bool runOnFunction(Function *F, AnalysisManager& AM) { return false; }
|
||||
virtual bool runOnBasicBlock(BasicBlock *BB, AnalysisManager& AM) { return false; }
|
||||
|
||||
// 所有 Pass 都必须提供一个唯一的 ID
|
||||
// 这通常是一个静态成员,并在 Pass 类外部定义
|
||||
virtual void *getPassID() const = 0;
|
||||
|
||||
protected:
|
||||
std::string Name;
|
||||
Granularity G;
|
||||
PassKind K;
|
||||
};
|
||||
|
||||
// 抽象基类:分析遍
|
||||
class AnalysisPass : public Pass {
|
||||
public:
|
||||
AnalysisPass(const std::string &name, Granularity g) : Pass(name, g, PassKind::Analysis) {}
|
||||
|
||||
virtual std::unique_ptr<AnalysisResultBase> getResult() = 0;
|
||||
};
|
||||
|
||||
// 抽象基类:优化遍
|
||||
class OptimizationPass : public Pass {
|
||||
public:
|
||||
OptimizationPass(const std::string &name, Granularity g) : Pass(name, g, PassKind::Optimization) {}
|
||||
|
||||
virtual void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const {
|
||||
// 默认不依赖也不修改任何分析
|
||||
}
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// PassRegistry: 全局 Pass 注册表 (单例)
|
||||
// ======================================================================
|
||||
class PassRegistry {
|
||||
public:
|
||||
// Pass 工厂函数类型:返回 Pass 的唯一指针
|
||||
using PassFactory = std::function<std::unique_ptr<Pass>()>;
|
||||
|
||||
// 获取 PassRegistry 实例 (单例模式)
|
||||
static PassRegistry &getPassRegistry() {
|
||||
static PassRegistry instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// 注册一个 Pass
|
||||
// passID 是 Pass 类的唯一静态 ID (例如 MyPass::ID 的地址)
|
||||
// factory 是一个 lambda 或函数指针,用于创建该 Pass 的实例
|
||||
void registerPass(void *passID, PassFactory factory) {
|
||||
if (factories.count(passID)) {
|
||||
// Error: Pass with this ID already registered
|
||||
// You might want to throw an exception or log an error
|
||||
return;
|
||||
}
|
||||
factories[passID] = std::move(factory);
|
||||
}
|
||||
|
||||
// 通过 Pass ID 创建一个 Pass 实例
|
||||
std::unique_ptr<Pass> createPass(void *passID) {
|
||||
auto it = factories.find(passID);
|
||||
if (it == factories.end()) {
|
||||
// Error: Pass with this ID not registered
|
||||
return nullptr;
|
||||
}
|
||||
return it->second(); // 调用工厂函数创建实例
|
||||
}
|
||||
|
||||
private:
|
||||
PassRegistry() = default; // 私有构造函数,实现单例
|
||||
~PassRegistry() = default;
|
||||
PassRegistry(const PassRegistry &) = delete; // 禁用拷贝构造
|
||||
PassRegistry &operator=(const PassRegistry &) = delete; // 禁用赋值操作
|
||||
|
||||
std::map<void *, PassFactory> factories;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// AnalysisManager: 负责管理和提供分析结果
|
||||
// ======================================================================
|
||||
class AnalysisManager {
|
||||
private:
|
||||
Module *pModuleRef; // 指向被分析的Module
|
||||
|
||||
// 缓存不同粒度的分析结果
|
||||
std::map<void *, std::unique_ptr<AnalysisResultBase>> moduleCachedResults;
|
||||
std::map<std::pair<Function *, void *>, std::unique_ptr<AnalysisResultBase>> functionCachedResults;
|
||||
std::map<std::pair<BasicBlock *, void *>, std::unique_ptr<AnalysisResultBase>> basicBlockCachedResults;
|
||||
|
||||
|
||||
public:
|
||||
// 构造函数接收 Module 指针
|
||||
AnalysisManager(Module *M) : pModuleRef(M) {}
|
||||
AnalysisManager() = delete; // 禁止无参构造
|
||||
|
||||
~AnalysisManager() = default;
|
||||
|
||||
// 获取分析结果的通用模板函数
|
||||
// T 是 AnalysisResult 的具体类型,E 是 AnalysisPass 的具体类型
|
||||
// F 和 BB 参数用于提供上下文,根据分析遍的粒度来使用
|
||||
template <typename T, typename E> T *getAnalysisResult(Function *F = nullptr, BasicBlock *BB = nullptr) {
|
||||
void *analysisID = E::ID; // 获取分析遍的唯一 ID
|
||||
|
||||
// 尝试从注册表创建分析遍实例
|
||||
std::unique_ptr<Pass> basePass = PassRegistry::getPassRegistry().createPass(analysisID);
|
||||
if (!basePass) {
|
||||
// Error: Analysis pass not registered
|
||||
std::cerr << "Error: Analysis pass with ID " << analysisID << " not registered.\n";
|
||||
return nullptr;
|
||||
}
|
||||
AnalysisPass *analysisPass = static_cast<AnalysisPass *>(basePass.get());
|
||||
|
||||
// 根据分析遍的粒度处理
|
||||
switch (analysisPass->getGranularity()) {
|
||||
case Pass::Granularity::Module: {
|
||||
// 检查是否已存在有效结果
|
||||
auto it = moduleCachedResults.find(analysisID);
|
||||
if (it != moduleCachedResults.end()) {
|
||||
return static_cast<T *>(it->second.get()); // 返回缓存结果
|
||||
}
|
||||
// 运行模块级分析遍
|
||||
if (!pModuleRef) {
|
||||
std::cerr << "Error: Module reference not set for AnalysisManager to run Module Pass.\n";
|
||||
return nullptr;
|
||||
}
|
||||
analysisPass->runOnModule(pModuleRef, *this);
|
||||
// 获取结果并缓存
|
||||
std::unique_ptr<AnalysisResultBase> result = analysisPass->getResult();
|
||||
T *specificResult = static_cast<T *>(result.get());
|
||||
moduleCachedResults[analysisID] = std::move(result); // 缓存结果
|
||||
return specificResult;
|
||||
}
|
||||
case Pass::Granularity::Function: {
|
||||
// 检查请求的上下文是否正确
|
||||
if (!F) {
|
||||
std::cerr << "Error: Function context required for Function-level Analysis Pass.\n";
|
||||
return nullptr;
|
||||
}
|
||||
// 检查是否已存在有效结果
|
||||
auto it = functionCachedResults.find({F, analysisID});
|
||||
if (it != functionCachedResults.end()) {
|
||||
return static_cast<T *>(it->second.get()); // 返回缓存结果
|
||||
}
|
||||
// 运行函数级分析遍
|
||||
analysisPass->runOnFunction(F, *this);
|
||||
// 获取结果并缓存
|
||||
std::unique_ptr<AnalysisResultBase> result = analysisPass->getResult();
|
||||
T *specificResult = static_cast<T *>(result.get());
|
||||
functionCachedResults[{F, analysisID}] = std::move(result); // 缓存结果
|
||||
return specificResult;
|
||||
}
|
||||
case Pass::Granularity::BasicBlock: {
|
||||
// 检查请求的上下文是否正确
|
||||
if (!BB) {
|
||||
std::cerr << "Error: BasicBlock context required for BasicBlock-level Analysis Pass.\n";
|
||||
return nullptr;
|
||||
}
|
||||
// 检查是否已存在有效结果
|
||||
auto it = basicBlockCachedResults.find({BB, analysisID});
|
||||
if (it != basicBlockCachedResults.end()) {
|
||||
return static_cast<T *>(it->second.get()); // 返回缓存结果
|
||||
}
|
||||
// 运行基本块级分析遍
|
||||
analysisPass->runOnBasicBlock(BB, *this);
|
||||
// 获取结果并缓存
|
||||
std::unique_ptr<AnalysisResultBase> result = analysisPass->getResult();
|
||||
T *specificResult = static_cast<T *>(result.get());
|
||||
basicBlockCachedResults[{BB, analysisID}] = std::move(result); // 缓存结果
|
||||
return specificResult;
|
||||
}
|
||||
}
|
||||
return nullptr; // 不会到达这里
|
||||
}
|
||||
|
||||
// 使所有分析结果失效 (当 IR 被修改时调用)
|
||||
void invalidateAllAnalyses() {
|
||||
moduleCachedResults.clear();
|
||||
functionCachedResults.clear();
|
||||
basicBlockCachedResults.clear();
|
||||
}
|
||||
|
||||
// 使特定分析结果失效
|
||||
// void *analysisID: 要失效的分析的ID
|
||||
// Function *F: 如果是函数级分析,指定函数;如果是模块级或基本块级,则为nullptr (取决于调用方式)
|
||||
// BasicBlock *BB: 如果是基本块级分析,指定基本块;否则为nullptr
|
||||
void invalidateAnalysis(void *analysisID, Function *F = nullptr, BasicBlock *BB = nullptr) {
|
||||
if (BB) {
|
||||
// 使特定基本块的特定分析结果失效
|
||||
basicBlockCachedResults.erase({BB, analysisID});
|
||||
} else if (F) {
|
||||
// 使特定函数的特定分析结果失效 (也可能包含聚合的BasicBlock结果)
|
||||
functionCachedResults.erase({F, analysisID});
|
||||
// 遍历所有属于F的基本块,使其BasicBlockCache失效 (如果该分析是BasicBlock粒度的)
|
||||
// 这需要遍历F的所有基本块,效率较低,更推荐在BasicBlockPass的invalidateAnalysisUsage中精确指定
|
||||
// 或者在Function级别的invalidate时,清空该Function的所有BasicBlock分析
|
||||
// 这里的实现简单地清空该Function下所有该ID的BasicBlock缓存
|
||||
for (auto it = basicBlockCachedResults.begin(); it != basicBlockCachedResults.end(); ) {
|
||||
// 假设BasicBlock::getParent()方法存在,可以获取所属Function
|
||||
if (it->first.second == analysisID /* && it->first.first->getParent() == F */) { // 需要BasicBlock能获取其父函数
|
||||
it = basicBlockCachedResults.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// 使所有函数的特定分析结果失效 (Module级和所有Function/BasicBlock级)
|
||||
moduleCachedResults.erase(analysisID);
|
||||
for (auto it = functionCachedResults.begin(); it != functionCachedResults.end(); ) {
|
||||
if (it->first.second == analysisID) {
|
||||
it = functionCachedResults.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
for (auto it = basicBlockCachedResults.begin(); it != basicBlockCachedResults.end(); ) {
|
||||
if (it->first.second == analysisID) {
|
||||
it = basicBlockCachedResults.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// PassManager:遍管理器
|
||||
// ======================================================================
|
||||
class PassManager {
|
||||
private:
|
||||
std::vector<std::unique_ptr<Pass>> passes;
|
||||
AnalysisManager analysisManager;
|
||||
Module *pmodule;
|
||||
IRBuilder *pBuilder;
|
||||
|
||||
public:
|
||||
PassManager() = default;
|
||||
~PassManager() = default;
|
||||
|
||||
PassManager(Module *module, IRBuilder *builder) : pmodule(module) ,pBuilder(builder), analysisManager(module) {}
|
||||
|
||||
// 运行所有注册的遍
|
||||
bool run();
|
||||
|
||||
// 运行优化管道主要负责注册和运行优化遍
|
||||
// 这里可以根据 optLevel 和 DEBUG 控制不同的优化遍
|
||||
void runOptimizationPipeline(Module* moduleIR, IRBuilder* builder, int optLevel);
|
||||
|
||||
// 添加遍:现在接受 Pass 的 ID,而不是直接的 unique_ptr
|
||||
void addPass(void *passID);
|
||||
|
||||
AnalysisManager &getAnalysisManager() { return analysisManager; }
|
||||
|
||||
void clearPasses();
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
// 辅助宏或函数,用于简化 Pass 的注册
|
||||
// ======================================================================
|
||||
|
||||
// 用于分析遍的注册
|
||||
template <typename AnalysisPassType> void registerAnalysisPass();
|
||||
|
||||
// (1) 针对需要 IRBuilder 参数的优化遍的重载
|
||||
// 这个模板只在 OptimizationPassType 可以通过 IRBuilder* 构造时才有效
|
||||
template <typename OptimizationPassType, typename std::enable_if<
|
||||
std::is_constructible<OptimizationPassType, IRBuilder*>::value, int>::type = 0>
|
||||
void registerOptimizationPass(IRBuilder* builder);
|
||||
|
||||
// (2) 针对不需要 IRBuilder 参数的所有其他优化遍的重载
|
||||
// 这个模板只在 OptimizationPassType 不能通过 IRBuilder* 构造时才有效
|
||||
template <typename OptimizationPassType, typename std::enable_if<
|
||||
!std::is_constructible<OptimizationPassType, IRBuilder*>::value, int>::type = 0>
|
||||
void registerOptimizationPass();
|
||||
|
||||
} // namespace sysy
|
||||
@ -4,20 +4,23 @@
|
||||
#include "RISCv64LLIR.h"
|
||||
#include <iostream>
|
||||
|
||||
extern int DEBUG;
|
||||
extern int DEEPDEBUG;
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class RISCv64AsmPrinter {
|
||||
public:
|
||||
RISCv64AsmPrinter(MachineFunction* mfunc);
|
||||
// 主入口
|
||||
void run(std::ostream& os);
|
||||
void run(std::ostream& os, bool debug = false);
|
||||
|
||||
private:
|
||||
// 打印各个部分
|
||||
void printPrologue();
|
||||
void printEpilogue();
|
||||
void printBasicBlock(MachineBasicBlock* mbb);
|
||||
void printInstruction(MachineInstr* instr);
|
||||
void printBasicBlock(MachineBasicBlock* mbb, bool debug = false);
|
||||
void printInstruction(MachineInstr* instr, bool debug = false);
|
||||
|
||||
// 辅助函数
|
||||
std::string regToString(PhysicalReg reg);
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
#include "IR.h"
|
||||
#include <string>
|
||||
|
||||
extern int DEBUG;
|
||||
extern int DEEPDEBUG;
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// RISCv64CodeGen 现在是一个高层驱动器
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
|
||||
extern int DEBUG;
|
||||
extern int DEEPDEBUG;
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class RISCv64ISel {
|
||||
@ -14,6 +17,8 @@ public:
|
||||
// 公开接口,以便后续模块(如RegAlloc)可以查询或创建vreg
|
||||
unsigned getVReg(Value* val);
|
||||
unsigned getNewVReg() { return vreg_counter++; }
|
||||
// 获取 vreg_map 的公共接口
|
||||
const std::map<Value*, unsigned>& getVRegMap() const { return vreg_map; }
|
||||
|
||||
private:
|
||||
// DAG节点定义,作为ISel的内部实现细节
|
||||
@ -30,6 +35,10 @@ private:
|
||||
std::vector<std::unique_ptr<DAGNode>> build_dag(BasicBlock* bb);
|
||||
DAGNode* get_operand_node(Value* val_ir, std::map<Value*, DAGNode*>&, std::vector<std::unique_ptr<DAGNode>>&);
|
||||
DAGNode* create_node(int kind, Value* val, std::map<Value*, DAGNode*>&, std::vector<std::unique_ptr<DAGNode>>&);
|
||||
// 用于计算类型大小的辅助函数
|
||||
unsigned getTypeSizeInBytes(Type* type);
|
||||
|
||||
void print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, const std::string& bb_name);
|
||||
|
||||
// 状态
|
||||
Function* F; // 当前处理的高层IR函数
|
||||
|
||||
@ -18,8 +18,29 @@ namespace sysy {
|
||||
|
||||
// 物理寄存器定义
|
||||
enum class PhysicalReg {
|
||||
ZERO, RA, SP, GP, TP, T0, T1, T2, S0, S1, A0, A1, A2, A3, A4, A5, A6, A7, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, T3, T4, T5, T6,
|
||||
F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31
|
||||
// --- 特殊功能寄存器 ---
|
||||
ZERO, RA, SP, GP, TP,
|
||||
|
||||
// --- 整数寄存器 (按调用约定分组) ---
|
||||
// 临时寄存器 (调用者保存)
|
||||
T0, T1, T2, T3, T4, T5, T6,
|
||||
|
||||
// 保存寄存器 (被调用者保存)
|
||||
S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11,
|
||||
|
||||
// 参数/返回值寄存器 (调用者保存)
|
||||
A0, A1, A2, A3, A4, A5, A6, A7,
|
||||
|
||||
// --- 浮点寄存器 ---
|
||||
// (保持您原有的 F0-F31 命名)
|
||||
F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11,
|
||||
F12, F13, F14, F15, F16, F17, F18, F19, F20, F21,
|
||||
F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
|
||||
// 用于内部表示物理寄存器在干扰图中的节点ID(一个简单的特殊ID,确保不与vreg_counter冲突)
|
||||
// 假设 vreg_counter 不会达到这么大的值
|
||||
PHYS_REG_START_ID = 10000,
|
||||
PHYS_REG_END_ID = PHYS_REG_START_ID + 32, // 预留足够的空间
|
||||
};
|
||||
|
||||
// RISC-V 指令操作码枚举
|
||||
@ -44,10 +65,16 @@ enum class RVOpcodes {
|
||||
// 特殊标记,非指令
|
||||
LABEL,
|
||||
// 新增伪指令,用于解耦栈帧处理
|
||||
FRAME_LOAD, // 从栈帧加载 (AllocaInst)
|
||||
FRAME_STORE, // 保存到栈帧 (AllocaInst)
|
||||
FRAME_LOAD_W, // 从栈帧加载 32位 Word (对应 lw)
|
||||
FRAME_LOAD_D, // 从栈帧加载 64位 Doubleword (对应 ld)
|
||||
FRAME_STORE_W, // 保存 32位 Word 到栈帧 (对应 sw)
|
||||
FRAME_STORE_D, // 保存 64位 Doubleword 到栈帧 (对应 sd)
|
||||
FRAME_ADDR, // 获取栈帧变量的地址
|
||||
};
|
||||
|
||||
// 定义一个全局辅助函数或常量,提供调用者保存寄存器列表
|
||||
const std::vector<PhysicalReg>& getCallerSavedIntRegs();
|
||||
|
||||
class MachineOperand;
|
||||
class RegOperand;
|
||||
class ImmOperand;
|
||||
@ -168,8 +195,10 @@ struct StackFrameInfo {
|
||||
int locals_size = 0; // 仅为AllocaInst分配的大小
|
||||
int spill_size = 0; // 仅为溢出分配的大小
|
||||
int total_size = 0; // 总大小
|
||||
int callee_saved_size = 0; // 保存寄存器的大小
|
||||
std::map<unsigned, int> alloca_offsets; // <AllocaInst的vreg, 栈偏移>
|
||||
std::map<unsigned, int> spill_offsets; // <溢出vreg, 栈偏移>
|
||||
std::set<PhysicalReg> used_callee_saved_regs; // 使用的保存寄存器
|
||||
};
|
||||
|
||||
// 机器函数
|
||||
@ -195,6 +224,15 @@ private:
|
||||
StackFrameInfo frame_info;
|
||||
};
|
||||
|
||||
inline const std::vector<PhysicalReg>& getCallerSavedIntRegs() {
|
||||
static const std::vector<PhysicalReg> regs = {
|
||||
PhysicalReg::T0, PhysicalReg::T1, PhysicalReg::T2, PhysicalReg::T3,
|
||||
PhysicalReg::T4, PhysicalReg::T5, PhysicalReg::T6,
|
||||
PhysicalReg::A0, PhysicalReg::A1, PhysicalReg::A2, PhysicalReg::A3,
|
||||
PhysicalReg::A4, PhysicalReg::A5, PhysicalReg::A6, PhysicalReg::A7
|
||||
};
|
||||
return regs;
|
||||
}
|
||||
} // namespace sysy
|
||||
|
||||
#endif // RISCV64_LLIR_H
|
||||
@ -6,13 +6,13 @@
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @class Pass
|
||||
* @class BackendPass
|
||||
* @brief 所有优化Pass的抽象基类 (可选,但推荐)
|
||||
* * 定义一个通用的接口,所有优化都应该实现它。
|
||||
*/
|
||||
class Pass {
|
||||
class BackendPass {
|
||||
public:
|
||||
virtual ~Pass() = default;
|
||||
virtual ~BackendPass() = default;
|
||||
virtual void runOnMachineFunction(MachineFunction* mfunc) = 0;
|
||||
};
|
||||
|
||||
@ -25,12 +25,26 @@ public:
|
||||
* * 在虚拟寄存器上进行操作,此时调度自由度最大,
|
||||
* 主要目标是隐藏指令延迟,提高流水线效率。
|
||||
*/
|
||||
class PreRA_Scheduler : public Pass {
|
||||
class PreRA_Scheduler : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class CalleeSavedHandler
|
||||
* @brief 处理被调用者保存寄存器(Callee-Saved Registers)的Pass。
|
||||
* * 这个Pass在寄存器分配之后运行。它的主要职责是:
|
||||
* 1. 扫描整个函数,找出所有被使用的 `s` 系列寄存器。
|
||||
* 2. 在函数序言中插入 `sd` 指令来保存这些寄存器。
|
||||
* 3. 在函数结尾(ret指令前)插入 `ld` 指令来恢复这些寄存器。
|
||||
* 4. 正确计算因保存这些寄存器而需要的额外栈空间,并更新StackFrameInfo。
|
||||
*/
|
||||
class CalleeSavedHandler : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
// --- 寄存器分配后优化 ---
|
||||
|
||||
/**
|
||||
@ -39,7 +53,7 @@ public:
|
||||
* * 在已分配物理寄存器的指令流上,通过一个小的滑动窗口来查找
|
||||
* 并替换掉一些冗余或低效的指令模式。
|
||||
*/
|
||||
class PeepholeOptimizer : public Pass {
|
||||
class PeepholeOptimizer : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
@ -50,7 +64,7 @@ public:
|
||||
* * 主要目标是优化寄存器分配器插入的spill/fill代码(lw/sw),
|
||||
* 尝试将加载指令提前,以隐藏其访存延迟。
|
||||
*/
|
||||
class PostRA_Scheduler : public Pass {
|
||||
class PostRA_Scheduler : public BackendPass {
|
||||
public:
|
||||
void runOnMachineFunction(MachineFunction* mfunc) override;
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define RISCV64_REGALLOC_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "RISCv64ISel.h" // 包含 RISCv64ISel.h 以访问 ISel 和 Value 类型
|
||||
|
||||
namespace sysy {
|
||||
|
||||
@ -34,6 +35,9 @@ private:
|
||||
// 辅助函数,获取指令的Use/Def集合
|
||||
void getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet& def);
|
||||
|
||||
// 辅助函数,处理调用约定
|
||||
void handleCallingConvention();
|
||||
|
||||
MachineFunction* MFunc;
|
||||
|
||||
// 活跃性分析结果
|
||||
@ -49,6 +53,15 @@ private:
|
||||
|
||||
// 可用的物理寄存器池
|
||||
std::vector<PhysicalReg> allocable_int_regs;
|
||||
|
||||
// 存储vreg到IR Value*的反向映射
|
||||
// 这个map将在run()函数开始时被填充,并在rewriteFunction()中使用。
|
||||
std::map<unsigned, Value*> vreg_to_value_map;
|
||||
std::map<PhysicalReg, unsigned> preg_to_vreg_id_map; // 物理寄存器到特殊vreg ID的映射
|
||||
|
||||
// 用于计算类型大小的辅助函数
|
||||
unsigned getTypeSizeInBytes(Type* type);
|
||||
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
#include "SysYIROptUtils.h"
|
||||
|
||||
namespace sysy {
|
||||
/**
|
||||
* Reg2Mem(后端未做phi指令翻译)
|
||||
*/
|
||||
class Reg2Mem {
|
||||
private:
|
||||
Module *pModule;
|
||||
IRBuilder *pBuilder;
|
||||
|
||||
public:
|
||||
Reg2Mem(Module *pMoudle, IRBuilder *pBuilder) : pModule(pMoudle), pBuilder(pBuilder) {}
|
||||
|
||||
void DeletePhiInst();
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
@ -17,44 +18,84 @@ namespace sysy {
|
||||
// - 合并连续的跳转指令(Jump Threading)在合并不可达块中似乎已经实现了
|
||||
// - 基本块重排序(Block Reordering),提升局部性
|
||||
|
||||
class SysYCFGOpt {
|
||||
private:
|
||||
Module *pModule;
|
||||
IRBuilder *pBuilder;
|
||||
|
||||
public:
|
||||
SysYCFGOpt(Module *pMoudle, IRBuilder *pBuilder) : pModule(pMoudle), pBuilder(pBuilder) {}
|
||||
|
||||
void SysYOptimizateAfterIR(){
|
||||
|
||||
auto &functions = pModule->getFunctions();
|
||||
for (auto &function : functions) {
|
||||
bool changed = false;
|
||||
while(changed){
|
||||
changed = false;
|
||||
changed |= SysYCondBr2Br(function.second.get(), pBuilder);
|
||||
// 删除br后面的无用指令
|
||||
changed |= SysYDelInstAfterBr(function.second.get());
|
||||
// 合并空基本块
|
||||
changed |= SysYBlockMerge(function.second.get());
|
||||
// 删除无前驱块
|
||||
changed |= SysYDelNoPreBLock(function.second.get());
|
||||
// 删除空块
|
||||
changed |= SysYDelEmptyBlock(function.second.get(), pBuilder);
|
||||
// 添加return指令
|
||||
changed |= SysYAddReturn(function.second.get(), pBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助工具类,包含实际的CFG优化逻辑
|
||||
// 这些方法可以被独立的Pass调用
|
||||
class SysYCFGOptUtils {
|
||||
public:
|
||||
static bool SysYDelInstAfterBr(Function *func); // 删除br后面的指令
|
||||
static bool SysYDelEmptyBlock(Function *func, IRBuilder* pBuilder); // 空块删除
|
||||
static bool SysYDelNoPreBLock(Function *func); // 删除无前驱块(不可达块)
|
||||
static bool SysYBlockMerge(Function *func); // 合并基本块(主要针对嵌套if while的exit块,
|
||||
// 也可以修改IR生成实现回填机制
|
||||
static bool SysYAddReturn(Function *func, IRBuilder* pBuilder); // 添加return指令(主要针对Void函数)
|
||||
static bool SysYCondBr2Br(Function *func, IRBuilder* pBuilder); // 条件分支(已知cond的值)转换为无条件分支
|
||||
static bool SysYBlockMerge(Function *func); // 合并基本块
|
||||
static bool SysYAddReturn(Function *func, IRBuilder* pBuilder); // 添加return指令
|
||||
static bool SysYCondBr2Br(Function *func, IRBuilder* pBuilder); // 条件分支转换为无条件分支
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
// ======================================================================
|
||||
// 独立的CFG优化遍
|
||||
// ======================================================================
|
||||
|
||||
class SysYDelInstAfterBrPass : public OptimizationPass {
|
||||
public:
|
||||
static void *ID; // 唯一ID
|
||||
SysYDelInstAfterBrPass() : OptimizationPass("SysYDelInstAfterBrPass", Granularity::Function) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override {
|
||||
// 这个优化可能改变CFG结构,使一些CFG相关的分析失效
|
||||
// 可以在这里指定哪些分析会失效,例如支配树、活跃变量等
|
||||
// analysisInvalidations.insert(DominatorTreeAnalysisPass::ID); // 示例
|
||||
}
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
class SysYDelEmptyBlockPass : public OptimizationPass {
|
||||
private:
|
||||
IRBuilder *pBuilder;
|
||||
public:
|
||||
static void *ID;
|
||||
SysYDelEmptyBlockPass(IRBuilder *builder) : OptimizationPass("SysYDelEmptyBlockPass", Granularity::Function), pBuilder(builder) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override {};
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
class SysYDelNoPreBLockPass : public OptimizationPass {
|
||||
public:
|
||||
static void *ID;
|
||||
SysYDelNoPreBLockPass() : OptimizationPass("SysYDelNoPreBLockPass", Granularity::Function) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override {};
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
class SysYBlockMergePass : public OptimizationPass {
|
||||
public:
|
||||
static void *ID;
|
||||
SysYBlockMergePass() : OptimizationPass("SysYBlockMergePass", Granularity::Function) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override {};
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
class SysYAddReturnPass : public OptimizationPass {
|
||||
private:
|
||||
IRBuilder *pBuilder;
|
||||
public:
|
||||
static void *ID;
|
||||
SysYAddReturnPass(IRBuilder *builder) : OptimizationPass("SysYAddReturnPass", Granularity::Function), pBuilder(builder) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override {};
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
class SysYCondBr2BrPass : public OptimizationPass {
|
||||
private:
|
||||
IRBuilder *pBuilder;
|
||||
public:
|
||||
static void *ID;
|
||||
SysYCondBr2BrPass(IRBuilder *builder) : OptimizationPass("SysYCondBr2BrPass", Granularity::Function), pBuilder(builder) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override {};
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -139,6 +139,8 @@ public:
|
||||
// 构建数组类型
|
||||
Type* buildArrayType(Type* baseType, const std::vector<Value*>& dims);
|
||||
|
||||
unsigned countArrayDimensions(Type* type);
|
||||
|
||||
}; // class SysYIRGenerator
|
||||
|
||||
} // namespace sysy
|
||||
@ -10,7 +10,7 @@ namespace sysy {
|
||||
class SysYIROptUtils{
|
||||
|
||||
public:
|
||||
// 删除use关系
|
||||
// 仅仅删除use关系
|
||||
static void usedelete(Instruction *instr) {
|
||||
for (auto &use : instr->getOperands()) {
|
||||
Value* val = use->getValue();
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 前置声明
|
||||
class FunctionPass;
|
||||
class ModulePass;
|
||||
class AnalysisPass;
|
||||
class PassManager;
|
||||
|
||||
// 抽象基类 Pass
|
||||
class Pass {
|
||||
public:
|
||||
enum PassKind {
|
||||
PK_Function,
|
||||
PK_Module,
|
||||
PK_Analysis
|
||||
};
|
||||
|
||||
Pass(PassKind kind, const std::string& name) : Kind(kind), Name(name) {}
|
||||
virtual ~Pass() = default;
|
||||
|
||||
PassKind getPassKind() const { return Kind; }
|
||||
const std::string& getPassName() const { return Name; }
|
||||
|
||||
// 每个Pass需要实现此方法来执行其逻辑
|
||||
// 具体的run方法将根据Pass类型在FunctionPass和ModulePass中定义
|
||||
protected:
|
||||
PassKind Kind;
|
||||
std::string Name;
|
||||
};
|
||||
|
||||
// 针对函数的优化遍
|
||||
class FunctionPass : public Pass {
|
||||
public:
|
||||
FunctionPass(const std::string& name) : Pass(PK_Function, name) {}
|
||||
// 真正的优化逻辑将在此方法中实现
|
||||
virtual bool runOnFunction(Function& F) = 0;
|
||||
};
|
||||
|
||||
// 针对模块的优化遍
|
||||
class ModulePass : public Pass {
|
||||
public:
|
||||
ModulePass(const std::string& name) : Pass(PK_Module, name) {}
|
||||
// 真正的优化逻辑将在此方法中实现
|
||||
virtual bool runOnModule(Module& M) = 0;
|
||||
};
|
||||
|
||||
// 分析遍
|
||||
class AnalysisPass : public Pass {
|
||||
public:
|
||||
AnalysisPass(const std::string& name) : Pass(PK_Analysis, name) {}
|
||||
// 分析遍通常需要一个模块或函数作为输入,并计算出分析结果
|
||||
// 具体分析结果的存储和访问方式需要设计
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -13,13 +13,10 @@ using namespace antlr4;
|
||||
|
||||
#include "SysYIRGenerator.h"
|
||||
#include "SysYIRPrinter.h"
|
||||
#include "SysYIRCFGOpt.h"
|
||||
#include "SysYIRCFGOpt.h" // 包含 CFG 优化
|
||||
#include "RISCv64Backend.h"
|
||||
// #include "SysYIRAnalyser.h"
|
||||
// #include "DeadCodeElimination.h"
|
||||
#include "Pass.h" // 包含新的 Pass 框架
|
||||
#include "AddressCalculationExpansion.h"
|
||||
// #include "Mem2Reg.h"
|
||||
// #include "Reg2Mem.h"
|
||||
|
||||
using namespace sysy;
|
||||
|
||||
@ -131,19 +128,20 @@ int main(int argc, char **argv) {
|
||||
if (argStopAfter == "ird") {
|
||||
DEBUG = 1; // 这里可能需要更精细地控制 DEBUG 的开启时机和范围
|
||||
}
|
||||
// 默认优化 pass (在所有优化级别都会执行)
|
||||
SysYCFGOpt cfgopt(moduleIR, builder);
|
||||
cfgopt.SysYOptimizateAfterIR();
|
||||
|
||||
// ControlFlowAnalysis cfa(moduleIR);
|
||||
// cfa.init();
|
||||
// ActiveVarAnalysis ava;
|
||||
// ava.init(moduleIR);
|
||||
|
||||
|
||||
if (DEBUG) {
|
||||
cout << "=== After CFA & AVA (Default) ===\n";
|
||||
cout << "=== Init IR ===\n";
|
||||
SysYPrinter(moduleIR).printIR(); // 临时打印器用于调试
|
||||
}
|
||||
|
||||
// 创建 Pass 管理器并运行优化管道
|
||||
PassManager passManager(moduleIR, builder); // 创建 Pass 管理器
|
||||
// 好像都不用传递module和builder了,因为 PassManager 初始化了
|
||||
passManager.runOptimizationPipeline(moduleIR, builder, optLevel);
|
||||
|
||||
|
||||
|
||||
|
||||
AddressCalculationExpansion ace(moduleIR, builder);
|
||||
if (ace.run()) {
|
||||
if (DEBUG) cout << "AddressCalculationExpansion made changes.\n";
|
||||
@ -218,7 +216,7 @@ int main(int argc, char **argv) {
|
||||
// 设置 DEBUG 模式(如果指定了 'asmd')
|
||||
if (argStopAfter == "asmd") {
|
||||
DEBUG = 1;
|
||||
// DEEPDEBUG = 1;
|
||||
DEEPDEBUG = 1;
|
||||
}
|
||||
sysy::RISCv64CodeGen codegen(moduleIR); // 传入优化后的 moduleIR
|
||||
string asmCode = codegen.code_gen();
|
||||
|
||||
227
test_script/runit-riscv64-single.sh
Normal file
227
test_script/runit-riscv64-single.sh
Normal file
@ -0,0 +1,227 @@
|
||||
#!/bin/bash
|
||||
|
||||
# runit-riscv64-single.sh - 用于在 RISC-V 虚拟机内部测试单个或少量 .s 文件的脚本
|
||||
# 模仿 runit-riscv64.sh 的功能,但以具体文件路径作为输入。
|
||||
|
||||
# --- 配置区 ---
|
||||
# 假设此脚本位于项目根目录 (例如 /home/ubuntu/debug)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
LIB_DIR="${SCRIPT_DIR}/lib"
|
||||
TMP_DIR="${SCRIPT_DIR}/tmp" # 临时可执行文件将存放在这里
|
||||
TESTDATA_DIR="${SCRIPT_DIR}/testdata" # 用于查找 .in/.out 文件
|
||||
|
||||
# 定义编译器
|
||||
GCC_NATIVE="gcc" # VM 内部的原生 gcc
|
||||
|
||||
# --- 初始化变量 ---
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # 程序自动化执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
S_FILES=() # 存储用户提供的 .s 文件列表
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST=""
|
||||
|
||||
# --- 函数定义 ---
|
||||
show_help() {
|
||||
echo "用法: $0 [文件1.s] [文件2.s] ... [选项]"
|
||||
echo "在 VM 内部编译并测试指定的 .s 文件。"
|
||||
echo ""
|
||||
echo "如果找到对应的 .in/.out 文件,则进行自动化测试。否则,进入交互模式。"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -ct N 设置 gcc 编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -t N 设置程序自动化执行超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
echo " -h, --help 显示此帮助信息并退出。"
|
||||
}
|
||||
|
||||
# 显示文件内容并根据行数截断的函数
|
||||
display_file_content() {
|
||||
local file_path="$1"
|
||||
local title="$2"
|
||||
local max_lines="$3"
|
||||
|
||||
if [ ! -f "$file_path" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "$title"
|
||||
local line_count
|
||||
line_count=$(wc -l < "$file_path")
|
||||
|
||||
if [ "$line_count" -gt "$max_lines" ]; then
|
||||
head -n "$max_lines" "$file_path"
|
||||
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
|
||||
else
|
||||
cat "$file_path"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 参数解析 ---
|
||||
# 从参数中分离出 .s 文件和选项
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-ct|-t|-ml|--max-lines)
|
||||
# 选项和其值将在下一个循环中处理
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
# 检查是否是带值的选项
|
||||
if ! [[ ${args_processed+x} ]]; then
|
||||
args_processed=true # 标记已处理过参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-ct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift; else echo "错误: -ct 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
-t) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift; else echo "错误: -t 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
-ml|--max-lines) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
*.s) S_FILES+=("$1") ;;
|
||||
*) if ! [[ "$1" =~ ^[0-9]+$ ]]; then echo "未知选项或无效文件: $1"; show_help; exit 1; fi ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
fi
|
||||
;;
|
||||
*.s)
|
||||
if [[ -f "$arg" ]]; then
|
||||
S_FILES+=("$arg")
|
||||
else
|
||||
echo "警告: 文件不存在,已忽略: $arg"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- 主逻辑开始 ---
|
||||
if [ ${#S_FILES[@]} -eq 0 ]; then
|
||||
echo "错误: 未提供任何 .s 文件作为输入。"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${TMP_DIR}"
|
||||
TOTAL_CASES=${#S_FILES[@]}
|
||||
|
||||
echo "SysY VM 内单例测试运行器启动..."
|
||||
echo "超时设置: gcc=${GCC_TIMEOUT}s, 运行=${EXEC_TIMEOUT}s"
|
||||
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
|
||||
echo ""
|
||||
|
||||
for s_file in "${S_FILES[@]}"; do
|
||||
is_passed=1
|
||||
|
||||
# 从 .s 文件名反向推导原始测试用例路径
|
||||
base_name_from_s_file=$(basename "$s_file" .s)
|
||||
original_test_name_underscored=$(echo "$base_name_from_s_file" | sed 's/_sysyc_riscv64$//')
|
||||
category=$(echo "$original_test_name_underscored" | cut -d'_' -f1)
|
||||
test_file_base=$(echo "$original_test_name_underscored" | cut -d'_' -f2-)
|
||||
original_relative_path="${category}/${test_file_base}"
|
||||
|
||||
executable_file="${TMP_DIR}/${base_name_from_s_file}"
|
||||
input_file="${TESTDATA_DIR}/${original_relative_path}.in"
|
||||
output_reference_file="${TESTDATA_DIR}/${original_relative_path}.out"
|
||||
output_actual_file="${TMP_DIR}/${base_name_from_s_file}.actual_out"
|
||||
|
||||
echo "======================================================================"
|
||||
echo "正在处理: ${s_file}"
|
||||
echo " (关联测试用例: ${original_relative_path}.sy)"
|
||||
|
||||
# 步骤 1: GCC 编译
|
||||
echo " 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
|
||||
timeout -s KILL ${GCC_TIMEOUT} "${GCC_NATIVE}" "${s_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static -g
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译失败或超时。\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
# 步骤 2: 执行与测试
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
# 检查是自动化测试还是交互模式
|
||||
if [ -f "${input_file}" ] || [ -f "${output_reference_file}" ]; then
|
||||
# --- 自动化测试模式 ---
|
||||
echo " 检测到 .in/.out 文件,进入自动化测试模式..."
|
||||
echo " 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
|
||||
|
||||
exec_cmd="\"${executable_file}\""
|
||||
[ -f "${input_file}" ] && exec_cmd+=" < \"${input_file}\""
|
||||
exec_cmd+=" > \"${output_actual_file}\""
|
||||
|
||||
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
|
||||
ACTUAL_RETURN_CODE=$?
|
||||
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时。\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
|
||||
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
|
||||
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name_from_s_file}.expected_stdout"
|
||||
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
|
||||
if [ "$ACTUAL_RETURN_CODE" -ne "$EXPECTED_RETURN_CODE" ]; then echo -e "\e[31m 返回码测试失败: 期望 ${EXPECTED_RETURN_CODE}, 实际 ${ACTUAL_RETURN_CODE}\e[0m"; is_passed=0; fi
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"; is_passed=0
|
||||
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m----------------\e[0m"
|
||||
fi
|
||||
else
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${output_reference_file}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 标准输出测试成功。\e[0m"
|
||||
else
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"; is_passed=0
|
||||
display_file_content "${output_reference_file}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m----------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# --- 交互模式 ---
|
||||
echo -e "\e[33m"
|
||||
echo " **********************************************************"
|
||||
echo " ** 未找到 .in 或 .out 文件,进入交互模式。 **"
|
||||
echo " ** 程序即将运行,你可以直接在终端中输入。 **"
|
||||
echo " ** 按下 Ctrl+D (EOF) 或以其他方式结束程序以继续。 **"
|
||||
echo " **********************************************************"
|
||||
echo -e "\e[0m"
|
||||
"${executable_file}"
|
||||
INTERACTIVE_RET_CODE=$?
|
||||
echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE}\e[0m"
|
||||
echo " 注意: 交互模式的结果未经验证。"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo -e "\e[32m状态: 通过\e[0m"
|
||||
((PASSED_CASES++))
|
||||
else
|
||||
echo -e "\e[31m状态: 失败\e[0m"
|
||||
FAILED_CASES_LIST+="${original_relative_path}.sy\n"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- 打印最终总结 ---
|
||||
echo "======================================================================"
|
||||
echo "所有测试完成"
|
||||
echo "测试通过率: [${PASSED_CASES}/${TOTAL_CASES}]"
|
||||
|
||||
if [ -n "$FAILED_CASES_LIST" ]; then
|
||||
echo ""
|
||||
echo -e "\e[31m未通过的测例:\e[0m"
|
||||
echo -e "${FAILED_CASES_LIST}"
|
||||
fi
|
||||
|
||||
echo "======================================================================"
|
||||
|
||||
if [ "$PASSED_CASES" -eq "$TOTAL_CASES" ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
@ -1,16 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# run_vm_tests.sh - 用于在 RISC-V 虚拟机内部汇编、链接和测试 SysY 程序的脚本
|
||||
# runit-riscv64.sh - 用于在 RISC-V 虚拟机内部汇编、链接和测试 SysY 程序的脚本
|
||||
# 此脚本应位于您的项目根目录 (例如 /home/ubuntu/debug)
|
||||
# 假设当前运行环境已经是 RISC-V 64 位架构,可以直接执行编译后的程序。
|
||||
# 脚本的目录结构应该为:
|
||||
# .
|
||||
# ├── runit.sh
|
||||
# ├── lib
|
||||
# │ └── libsysy_riscv.a
|
||||
# └── testdata
|
||||
# ├── functional
|
||||
# └── performance
|
||||
|
||||
# 定义相对于脚本位置的目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
@ -22,30 +14,47 @@ TESTDATA_DIR="${SCRIPT_DIR}/testdata"
|
||||
GCC_NATIVE="gcc" # VM 内部的 gcc
|
||||
|
||||
# --- 新增功能: 初始化变量 ---
|
||||
TIMEOUT_SECONDS=5 # 默认运行时超时时间为 5 秒
|
||||
COMPILE_TIMEOUT_SECONDS=10 # 默认编译超时时间为 10 秒
|
||||
GCC_TIMEOUT=10 # 默认 gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # 默认运行时超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
TOTAL_CASES=0
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST="" # 用于存储未通过的测例列表
|
||||
|
||||
# 显示帮助信息的函数
|
||||
show_help() {
|
||||
echo "用法: $0 [选项]"
|
||||
echo "此脚本用于在 RISC-V 虚拟机内部,对之前生成的 .s 汇编文件进行汇编、链接和测试。"
|
||||
echo "假设当前运行环境已经是 RISC-V 64 位架构,可以直接执行编译后的程序。"
|
||||
echo "测试会按文件名升序进行。"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -c, --clean 清理 'tmp' 目录下的所有生成文件。"
|
||||
echo " -t, --timeout N 设置每个测试用例的运行时超时为 N 秒 (默认: 5)。"
|
||||
echo " -ct, --compile-timeout M 设置 gcc 编译的超时时间为 M 秒 (默认: 10)。"
|
||||
echo " -ct M 设置 gcc 编译的超时时间为 M 秒 (默认: 10)。"
|
||||
echo " -t N 设置每个测试用例的运行时超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
echo " -h, --help 显示此帮助信息并退出。"
|
||||
echo ""
|
||||
echo "执行步骤:"
|
||||
echo "1. 遍历 'tmp/' 目录下的所有 .s 汇编文件。"
|
||||
echo "2. 在指定的超时时间内使用 VM 内部的 gcc 将 .s 文件汇编并链接为可执行文件。"
|
||||
echo "3. 在指定的超时时间内运行编译后的可执行文件。"
|
||||
echo "4. 根据对应的 .out 文件内容进行返回值和/或标准输出的比较。"
|
||||
echo "5. 输出比较时会忽略行尾多余的换行符。"
|
||||
echo "6. 所有测试结束后,报告总通过率。"
|
||||
}
|
||||
|
||||
# 显示文件内容并根据行数截断的函数
|
||||
display_file_content() {
|
||||
local file_path="$1"
|
||||
local title="$2"
|
||||
local max_lines="$3"
|
||||
|
||||
if [ ! -f "$file_path" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "$title"
|
||||
local line_count
|
||||
line_count=$(wc -l < "$file_path")
|
||||
|
||||
if [ "$line_count" -gt "$max_lines" ]; then
|
||||
head -n "$max_lines" "$file_path"
|
||||
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
|
||||
else
|
||||
cat "$file_path"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理临时文件的函数
|
||||
@ -69,23 +78,14 @@ while [[ "$#" -gt 0 ]]; do
|
||||
clean_tmp
|
||||
exit 0
|
||||
;;
|
||||
-t|--timeout)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
|
||||
TIMEOUT_SECONDS="$2"
|
||||
shift # 移过参数值
|
||||
else
|
||||
echo "错误: --timeout 需要一个正整数参数。" >&2
|
||||
exit 1
|
||||
fi
|
||||
-t)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift; else echo "错误: -t 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-ct|--compile-timeout)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
|
||||
COMPILE_TIMEOUT_SECONDS="$2"
|
||||
shift # 移过参数值
|
||||
else
|
||||
echo "错误: --compile-timeout 需要一个正整数参数。" >&2
|
||||
exit 1
|
||||
fi
|
||||
-ct)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift; else echo "错误: -ct 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-ml|--max-lines)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
@ -101,32 +101,27 @@ while [[ "$#" -gt 0 ]]; do
|
||||
done
|
||||
|
||||
echo "SysY VM 内部测试运行器启动..."
|
||||
echo "编译超时设置为: ${COMPILE_TIMEOUT_SECONDS} 秒"
|
||||
echo "运行时超时设置为: ${TIMEOUT_SECONDS} 秒"
|
||||
echo "GCC 编译超时设置为: ${GCC_TIMEOUT} 秒"
|
||||
echo "运行时超时设置为: ${EXEC_TIMEOUT} 秒"
|
||||
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
|
||||
echo "汇编文件目录: ${TMP_DIR}"
|
||||
echo "库文件目录: ${LIB_DIR}"
|
||||
echo "测试数据目录: ${TESTDATA_DIR}"
|
||||
echo ""
|
||||
|
||||
# 查找 tmp 目录下的所有 .s 汇编文件
|
||||
s_files=$(find "${TMP_DIR}" -maxdepth 1 -name "*.s")
|
||||
# 查找 tmp 目录下的所有 .s 汇编文件并排序
|
||||
s_files=$(find "${TMP_DIR}" -maxdepth 1 -name "*.s" | sort -V)
|
||||
TOTAL_CASES=$(echo "$s_files" | wc -w)
|
||||
|
||||
# 遍历找到的每个 .s 文件
|
||||
echo "$s_files" | while read s_file; do
|
||||
# --- 新增功能: 初始化用例通过状态 ---
|
||||
# 使用 here-string (<<<) 避免子 shell 问题
|
||||
while IFS= read -r s_file; do
|
||||
is_passed=1 # 1 表示通过, 0 表示失败
|
||||
|
||||
# 从 .s 文件名中提取原始的测试用例名称部分
|
||||
base_name_from_s_file=$(basename "$s_file" .s)
|
||||
original_test_name_underscored=$(echo "$base_name_from_s_file" | sed 's/_sysyc_riscv64$//')
|
||||
|
||||
# 将 `original_test_name_underscored` 分割成类别和文件名
|
||||
category=$(echo "$original_test_name_underscored" | cut -d'_' -f1)
|
||||
test_file_base=$(echo "$original_test_name_underscored" | cut -d'_' -f2-)
|
||||
original_relative_path="${category}/${test_file_base}"
|
||||
|
||||
# 定义可执行文件、输入文件、参考输出文件和实际输出文件的路径
|
||||
executable_file="${TMP_DIR}/${base_name_from_s_file}"
|
||||
input_file="${TESTDATA_DIR}/${original_relative_path}.in"
|
||||
output_reference_file="${TESTDATA_DIR}/${original_relative_path}.out"
|
||||
@ -136,42 +131,43 @@ echo "$s_files" | while read s_file; do
|
||||
echo " 对应的测试用例路径: ${original_relative_path}"
|
||||
|
||||
# 步骤 1: 使用 VM 内部的 gcc 编译 .s 到可执行文件
|
||||
echo " 使用 gcc 汇编并链接 (超时 ${COMPILE_TIMEOUT_SECONDS}s)..."
|
||||
# --- 修改点: 为 gcc 增加 timeout ---
|
||||
timeout ${COMPILE_TIMEOUT_SECONDS} "${GCC_NATIVE}" "${s_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static -g
|
||||
echo " 使用 gcc 汇编并链接 (超时 ${GCC_TIMEOUT}s)..."
|
||||
timeout -s KILL ${GCC_TIMEOUT} "${GCC_NATIVE}" "${s_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static -g
|
||||
GCC_STATUS=$?
|
||||
if [ $GCC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译/链接 ${s_file} 超时 (超过 ${COMPILE_TIMEOUT_SECONDS} 秒)\e[0m"
|
||||
echo -e "\e[31m错误: GCC 编译/链接 ${s_file} 超时\e[0m"
|
||||
is_passed=0
|
||||
elif [ $GCC_STATUS -ne 0 ]; then
|
||||
echo -e "\e[31m错误: GCC 汇编/链接 ${s_file} 失败,退出码: ${GCC_STATUS}\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
fi
|
||||
|
||||
# 步骤 2: 只有当编译成功时才执行
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo " 生成的可执行文件: ${executable_file}"
|
||||
echo " 正在执行 (超时 ${TIMEOUT_SECONDS}s): \"${executable_file}\""
|
||||
echo " 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
|
||||
|
||||
# 步骤 2: 执行编译后的文件并比较/报告结果
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
|
||||
|
||||
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
|
||||
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name_from_s_file}.expected_stdout"
|
||||
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
|
||||
echo " 检测到 .out 文件同时包含标准输出和期望的返回码。"
|
||||
echo " 期望返回码: ${EXPECTED_RETURN_CODE}"
|
||||
exec_cmd="\"${executable_file}\""
|
||||
if [ -f "${input_file}" ]; then
|
||||
exec_cmd+=" < \"${input_file}\""
|
||||
fi
|
||||
exec_cmd+=" > \"${output_actual_file}\""
|
||||
|
||||
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
|
||||
ACTUAL_RETURN_CODE=$?
|
||||
|
||||
if [ -f "${input_file}" ]; then
|
||||
timeout ${TIMEOUT_SECONDS} "${executable_file}" < "${input_file}" > "${output_actual_file}"
|
||||
else
|
||||
timeout ${TIMEOUT_SECONDS} "${executable_file}" > "${output_actual_file}"
|
||||
fi
|
||||
ACTUAL_RETURN_CODE=$?
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时: ${original_relative_path}.sy 运行超过 ${EXEC_TIMEOUT} 秒\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
|
||||
|
||||
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
|
||||
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name_from_s_file}.expected_stdout"
|
||||
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
|
||||
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时: ${original_relative_path}.sy 运行超过 ${TIMEOUT_SECONDS} 秒\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq "$EXPECTED_RETURN_CODE" ]; then
|
||||
echo -e "\e[32m 返回码测试成功: (${ACTUAL_RETURN_CODE}) 与期望值 (${EXPECTED_RETURN_CODE}) 匹配\e[0m"
|
||||
else
|
||||
@ -179,65 +175,51 @@ echo "$s_files" | while read s_file; do
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
if diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 标准输出测试成功\e[0m"
|
||||
else
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
echo -e "\e[31m 标准输出测试失败\e[0m"
|
||||
echo " 差异:"
|
||||
diff "${output_actual_file}" "${EXPECTED_STDOUT_FILE}"
|
||||
is_passed=0
|
||||
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m------------------------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " 检测到 .out 文件为纯标准输出参考。"
|
||||
if [ -f "${input_file}" ]; then
|
||||
timeout ${TIMEOUT_SECONDS} "${executable_file}" < "${input_file}" > "${output_actual_file}"
|
||||
else
|
||||
timeout ${TIMEOUT_SECONDS} "${executable_file}" > "${output_actual_file}"
|
||||
fi
|
||||
EXEC_STATUS=$?
|
||||
|
||||
if [ $EXEC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时: ${original_relative_path}.sy 运行超过 ${TIMEOUT_SECONDS} 秒\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
if [ $EXEC_STATUS -ne 0 ]; then
|
||||
echo -e "\e[33m警告: 程序以非零状态 ${EXEC_STATUS} 退出 (纯输出比较模式)。\e[0m"
|
||||
if [ $ACTUAL_RETURN_CODE -ne 0 ]; then
|
||||
echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m"
|
||||
fi
|
||||
if diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${output_reference_file}") >/dev/null 2>&1; then
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${output_reference_file}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 成功: 输出与参考输出匹配\e[0m"
|
||||
else
|
||||
echo -e "\e[31m 失败: 输出不匹配\e[0m"
|
||||
echo " 差异:"
|
||||
diff "${output_actual_file}" "${output_reference_file}"
|
||||
is_passed=0
|
||||
display_file_content "${output_reference_file}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m------------------------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " 未找到 .out 文件。正在运行并报告返回码。"
|
||||
timeout ${TIMEOUT_SECONDS} "${executable_file}"
|
||||
EXEC_STATUS=$?
|
||||
if [ $EXEC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时: ${original_relative_path}.sy 运行超过 ${TIMEOUT_SECONDS} 秒\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
echo " ${original_relative_path}.sy 的返回码: ${EXEC_STATUS}"
|
||||
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- 新增功能: 更新通过用例计数 ---
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
((PASSED_CASES++))
|
||||
else
|
||||
FAILED_CASES_LIST+="${original_relative_path}.sy\n"
|
||||
fi
|
||||
echo "" # 为测试用例之间添加一个空行
|
||||
done
|
||||
echo ""
|
||||
done <<< "$s_files"
|
||||
|
||||
# --- 新增功能: 打印最终总结 ---
|
||||
echo "========================================"
|
||||
echo "测试完成"
|
||||
echo "测试通过率: [${PASSED_CASES}/${TOTAL_CASES}]"
|
||||
|
||||
if [ -n "$FAILED_CASES_LIST" ]; then
|
||||
echo ""
|
||||
echo -e "\e[31m未通过的测例:\e[0m"
|
||||
echo -e "${FAILED_CASES_LIST}"
|
||||
fi
|
||||
|
||||
echo "========================================"
|
||||
|
||||
if [ "$PASSED_CASES" -eq "$TOTAL_CASES" ]; then
|
||||
|
||||
270
test_script/runit-single.sh
Normal file
270
test_script/runit-single.sh
Normal file
@ -0,0 +1,270 @@
|
||||
#!/bin/bash
|
||||
|
||||
# runit-single.sh - 用于编译和测试单个或少量 SysY 程序的脚本
|
||||
# 模仿 runit.sh 的功能,但以具体文件路径作为输入。
|
||||
|
||||
# --- 配置区 ---
|
||||
# 请根据你的环境修改这些路径
|
||||
# 假设此脚本位于你的项目根目录或一个脚本目录中
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
||||
# 默认寻找项目根目录下的 build 和 lib
|
||||
BUILD_BIN_DIR="${SCRIPT_DIR}/../build/bin"
|
||||
LIB_DIR="${SCRIPT_DIR}/../lib"
|
||||
# 临时文件会存储在脚本所在目录的 tmp 子目录中
|
||||
TMP_DIR="${SCRIPT_DIR}/tmp"
|
||||
|
||||
# 定义编译器和模拟器
|
||||
SYSYC="${BUILD_BIN_DIR}/sysyc"
|
||||
GCC_RISCV64="riscv64-linux-gnu-gcc"
|
||||
QEMU_RISCV64="qemu-riscv64"
|
||||
|
||||
# --- 初始化变量 ---
|
||||
EXECUTE_MODE=false
|
||||
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒)
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # qemu 自动化执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
SY_FILES=() # 存储用户提供的 .sy 文件列表
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST=""
|
||||
|
||||
# --- 函数定义 ---
|
||||
show_help() {
|
||||
echo "用法: $0 [文件1.sy] [文件2.sy] ... [选项]"
|
||||
echo "编译并测试指定的 .sy 文件。"
|
||||
echo ""
|
||||
echo "如果找到对应的 .in/.out 文件,则进行自动化测试。否则,进入交互模式。"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -e, --executable 编译为可执行文件并运行测试 (必须)。"
|
||||
echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -et N 设置 qemu 自动化执行超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
echo " -h, --help 显示此帮助信息并退出。"
|
||||
}
|
||||
|
||||
# --- 新增功能: 显示文件内容并根据行数截断 ---
|
||||
display_file_content() {
|
||||
local file_path="$1"
|
||||
local title="$2"
|
||||
local max_lines="$3"
|
||||
|
||||
if [ ! -f "$file_path" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "$title"
|
||||
local line_count
|
||||
line_count=$(wc -l < "$file_path")
|
||||
|
||||
if [ "$line_count" -gt "$max_lines" ]; then
|
||||
head -n "$max_lines" "$file_path"
|
||||
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
|
||||
else
|
||||
cat "$file_path"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# --- 参数解析 ---
|
||||
# 从参数中分离出 .sy 文件和选项
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-e|--executable)
|
||||
EXECUTE_MODE=true
|
||||
;;
|
||||
-sct|-gct|-et|-ml|--max-lines)
|
||||
# 选项和其值将在下一个循环中处理
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
# 检查是否是带值的选项
|
||||
if ! [[ ${args_processed+x} ]]; then
|
||||
args_processed=true # 标记已处理过参数
|
||||
# 重新处理所有参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-sct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
-gct) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
-et) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
-ml|--max-lines) if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi ;;
|
||||
*.sy) SY_FILES+=("$1") ;;
|
||||
-e|--executable) ;; # 已在外部处理
|
||||
*) if ! [[ "$1" =~ ^[0-9]+$ ]]; then echo "未知选项或无效文件: $1"; show_help; exit 1; fi ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
fi
|
||||
;;
|
||||
*.sy)
|
||||
if [[ -f "$arg" ]]; then
|
||||
SY_FILES+=("$arg")
|
||||
else
|
||||
echo "警告: 文件不存在,已忽略: $arg"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- 主逻辑开始 ---
|
||||
if ! ${EXECUTE_MODE}; then
|
||||
echo "错误: 请提供 -e 或 --executable 选项来运行测试。"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${#SY_FILES[@]} -eq 0 ]; then
|
||||
echo "错误: 未提供任何 .sy 文件作为输入。"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "${TMP_DIR}"
|
||||
TOTAL_CASES=${#SY_FILES[@]}
|
||||
|
||||
echo "SysY 单例测试运行器启动..."
|
||||
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
|
||||
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
|
||||
echo ""
|
||||
|
||||
for sy_file in "${SY_FILES[@]}"; do
|
||||
is_passed=1
|
||||
base_name=$(basename "${sy_file}" .sy)
|
||||
source_dir=$(dirname "${sy_file}")
|
||||
|
||||
ir_file="${TMP_DIR}/${base_name}_sysyc_riscv64.ll"
|
||||
assembly_file="${TMP_DIR}/${base_name}.s"
|
||||
executable_file="${TMP_DIR}/${base_name}"
|
||||
input_file="${source_dir}/${base_name}.in"
|
||||
output_reference_file="${source_dir}/${base_name}.out"
|
||||
output_actual_file="${TMP_DIR}/${base_name}.actual_out"
|
||||
|
||||
echo "======================================================================"
|
||||
echo "正在处理: ${sy_file}"
|
||||
|
||||
# 步骤 1: sysyc 编译
|
||||
echo " 使用 sysyc 编译 (超时 ${SYSYC_TIMEOUT}s)..."
|
||||
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -s ir "${sy_file}" > "${ir_file}"
|
||||
SYSYC_STATUS=$?
|
||||
if [ $SYSYC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m错误: SysY 编译 ${sy_file} IR超时\e[0m"
|
||||
is_passed=0
|
||||
elif [ $SYSYC_STATUS -ne 0 ]; then
|
||||
echo -e "\e[31m错误: SysY 编译 ${sy_file} IR失败,退出码: ${SYSYC_STATUS}\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file}"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[31m错误: SysY 编译失败或超时。\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
# 步骤 2: GCC 编译
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo " 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
|
||||
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译失败或超时。\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
fi
|
||||
|
||||
# 步骤 3: 执行与测试
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
# 检查是自动化测试还是交互模式
|
||||
if [ -f "${input_file}" ] || [ -f "${output_reference_file}" ]; then
|
||||
# --- 自动化测试模式 ---
|
||||
echo " 检测到 .in/.out 文件,进入自动化测试模式..."
|
||||
echo " 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
|
||||
|
||||
exec_cmd="${QEMU_RISCV64} \"${executable_file}\""
|
||||
[ -f "${input_file}" ] && exec_cmd+=" < \"${input_file}\""
|
||||
exec_cmd+=" > \"${output_actual_file}\""
|
||||
|
||||
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
|
||||
ACTUAL_RETURN_CODE=$?
|
||||
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时。\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
# 此处逻辑与 runit.sh 相同
|
||||
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
|
||||
if [[ "$LAST_LINE_TRIMMED" =~ ^[-+]?[0-9]+$ ]]; then
|
||||
EXPECTED_RETURN_CODE="$LAST_LINE_TRIMMED"
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${base_name}.expected_stdout"
|
||||
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
|
||||
if [ "$ACTUAL_RETURN_CODE" -ne "$EXPECTED_RETURN_CODE" ]; then echo -e "\e[31m 返回码测试失败: 期望 ${EXPECTED_RETURN_CODE}, 实际 ${ACTUAL_RETURN_CODE}\e[0m"; is_passed=0; fi
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"
|
||||
is_passed=0
|
||||
# --- 本次修改点: 使用新函数显示输出 ---
|
||||
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m----------------\e[0m"
|
||||
fi
|
||||
else
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${output_reference_file}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 标准输出测试成功。\e[0m"
|
||||
else
|
||||
echo -e "\e[31m 标准输出测试失败。\e[0m"
|
||||
is_passed=0
|
||||
# --- 本次修改点: 使用新函数显示输出 ---
|
||||
display_file_content "${output_reference_file}" " \e[36m--- 期望输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m--- 实际输出 ---\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m----------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# --- 交互模式 ---
|
||||
echo -e "\e[33m"
|
||||
echo " **********************************************************"
|
||||
echo " ** 未找到 .in 或 .out 文件,进入交互模式。 **"
|
||||
echo " ** 程序即将运行,你可以直接在终端中输入。 **"
|
||||
echo " ** 按下 Ctrl+D (EOF) 或以其他方式结束程序以继续。 **"
|
||||
echo " **********************************************************"
|
||||
echo -e "\e[0m"
|
||||
"${QEMU_RISCV64}" "${executable_file}"
|
||||
INTERACTIVE_RET_CODE=$?
|
||||
echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE}\e[0m"
|
||||
# 交互模式无法自动判断对错,默认算通过,但会提示
|
||||
echo " 注意: 交互模式的结果未经验证。"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo -e "\e[32m状态: 通过\e[0m"
|
||||
((PASSED_CASES++))
|
||||
else
|
||||
echo -e "\e[31m状态: 失败\e[0m"
|
||||
FAILED_CASES_LIST+="${sy_file}\n"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- 打印最终总结 ---
|
||||
echo "======================================================================"
|
||||
echo "所有测试完成"
|
||||
echo "测试通过率: [${PASSED_CASES}/${TOTAL_CASES}]"
|
||||
|
||||
if [ -n "$FAILED_CASES_LIST" ]; then
|
||||
echo ""
|
||||
echo -e "\e[31m未通过的测例:\e[0m"
|
||||
echo -e "${FAILED_CASES_LIST}"
|
||||
fi
|
||||
|
||||
echo "======================================================================"
|
||||
|
||||
if [ "$PASSED_CASES" -eq "$TOTAL_CASES" ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
@ -21,6 +21,7 @@ EXECUTE_MODE=false
|
||||
SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒)
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # qemu 执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
TOTAL_CASES=0
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST="" # 用于存储未通过的测例列表
|
||||
@ -36,9 +37,32 @@ show_help() {
|
||||
echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -et N 设置 qemu 执行超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
echo " -h, --help 显示此帮助信息并退出。"
|
||||
}
|
||||
|
||||
# 显示文件内容并根据行数截断的函数
|
||||
display_file_content() {
|
||||
local file_path="$1"
|
||||
local title="$2"
|
||||
local max_lines="$3"
|
||||
|
||||
if [ ! -f "$file_path" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "$title"
|
||||
local line_count
|
||||
line_count=$(wc -l < "$file_path")
|
||||
|
||||
if [ "$line_count" -gt "$max_lines" ]; then
|
||||
head -n "$max_lines" "$file_path"
|
||||
echo -e "\e[33m[... 输出已截断,共 ${line_count} 行 ...]\e[0m"
|
||||
else
|
||||
cat "$file_path"
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理临时文件的函数
|
||||
clean_tmp() {
|
||||
echo "正在清理临时目录: ${TMP_DIR}"
|
||||
@ -67,6 +91,9 @@ while [[ "$#" -gt 0 ]]; do
|
||||
-et)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-ml|--max-lines)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
@ -86,6 +113,7 @@ echo "临时目录: ${TMP_DIR}"
|
||||
echo "执行模式: ${EXECUTE_MODE}"
|
||||
if ${EXECUTE_MODE}; then
|
||||
echo "超时设置: sysyc=${SYSYC_TIMEOUT}s, gcc=${GCC_TIMEOUT}s, qemu=${EXEC_TIMEOUT}s"
|
||||
echo "失败输出最大行数: ${MAX_OUTPUT_LINES}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
@ -93,8 +121,7 @@ echo ""
|
||||
sy_files=$(find "${TESTDATA_DIR}" -name "*.sy" | sort -V)
|
||||
TOTAL_CASES=$(echo "$sy_files" | wc -w)
|
||||
|
||||
# --- 本次修复: 使用 here-string (<<<) 代替管道 (|) 来避免子 shell 问题 ---
|
||||
# 这样可以确保循环内的 PASSED_CASES 变量修改在循环结束后依然有效
|
||||
# --- 修复: 使用 here-string (<<<) 代替管道 (|) 来避免子 shell 问题 ---
|
||||
while IFS= read -r sy_file; do
|
||||
is_passed=1 # 1 表示通过, 0 表示失败
|
||||
|
||||
@ -111,7 +138,7 @@ while IFS= read -r sy_file; do
|
||||
|
||||
# 步骤 1: 使用 sysyc 编译 .sy 到 .s
|
||||
echo " 使用 sysyc 编译 (超时 ${SYSYC_TIMEOUT}s)..."
|
||||
timeout ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file}"
|
||||
timeout -s KILL ${SYSYC_TIMEOUT} "${SYSYC}" -S "${sy_file}" -o "${assembly_file}"
|
||||
SYSYC_STATUS=$?
|
||||
if [ $SYSYC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m错误: SysY 编译 ${sy_file} 超时\e[0m"
|
||||
@ -125,7 +152,7 @@ while IFS= read -r sy_file; do
|
||||
if ${EXECUTE_MODE} && [ "$is_passed" -eq 1 ]; then
|
||||
# 步骤 2: 使用 riscv64-linux-gnu-gcc 编译 .s 到可执行文件
|
||||
echo " 使用 gcc 编译 (超时 ${GCC_TIMEOUT}s)..."
|
||||
timeout ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static
|
||||
timeout -s KILL ${GCC_TIMEOUT} "${GCC_RISCV64}" "${assembly_file}" -o "${executable_file}" -L"${LIB_DIR}" -lsysy_riscv -static
|
||||
GCC_STATUS=$?
|
||||
if [ $GCC_STATUS -eq 124 ]; then
|
||||
echo -e "\e[31m错误: GCC 编译 ${assembly_file} 超时\e[0m"
|
||||
@ -136,11 +163,9 @@ while IFS= read -r sy_file; do
|
||||
fi
|
||||
elif ! ${EXECUTE_MODE}; then
|
||||
echo " 跳过执行模式。仅生成汇编文件。"
|
||||
# 如果只编译不执行,只要编译成功就算通过
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
((PASSED_CASES++))
|
||||
else
|
||||
# --- 本次修改点 ---
|
||||
FAILED_CASES_LIST+="${relative_path_no_ext}.sy\n"
|
||||
fi
|
||||
echo ""
|
||||
@ -151,22 +176,19 @@ while IFS= read -r sy_file; do
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
echo " 正在执行 (超时 ${EXEC_TIMEOUT}s)..."
|
||||
|
||||
# 准备执行命令
|
||||
exec_cmd="${QEMU_RISCV64} \"${executable_file}\""
|
||||
if [ -f "${input_file}" ]; then
|
||||
exec_cmd+=" < \"${input_file}\""
|
||||
fi
|
||||
exec_cmd+=" > \"${output_actual_file}\""
|
||||
|
||||
# 执行并捕获返回码
|
||||
eval "timeout ${EXEC_TIMEOUT} ${exec_cmd}"
|
||||
eval "timeout -s KILL ${EXEC_TIMEOUT} ${exec_cmd}"
|
||||
ACTUAL_RETURN_CODE=$?
|
||||
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq 124 ]; then
|
||||
echo -e "\e[31m 执行超时: ${sy_file} 运行超过 ${EXEC_TIMEOUT} 秒\e[0m"
|
||||
is_passed=0
|
||||
else
|
||||
# 检查是否存在 .out 文件以进行比较
|
||||
if [ -f "${output_reference_file}" ]; then
|
||||
LAST_LINE_TRIMMED=$(tail -n 1 "${output_reference_file}" | tr -d '[:space:]')
|
||||
|
||||
@ -175,70 +197,54 @@ while IFS= read -r sy_file; do
|
||||
EXPECTED_STDOUT_FILE="${TMP_DIR}/${output_base_name}_sysyc_riscv64.expected_stdout"
|
||||
head -n -1 "${output_reference_file}" > "${EXPECTED_STDOUT_FILE}"
|
||||
|
||||
# 比较返回码
|
||||
if [ "$ACTUAL_RETURN_CODE" -eq "$EXPECTED_RETURN_CODE" ]; then
|
||||
echo -e "\e[32m 返回码测试成功: (${ACTUAL_RETURN_CODE}) 与期望值 (${EXPECTED_RETURN_CODE}) 匹配\e[0m"
|
||||
else
|
||||
echo -e "\e[31m 返回码测试失败: 期望: ${EXPECTED_RETURN_CODE}, 实际: ${ACTUAL_RETURN_CODE}\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
# 比较标准输出
|
||||
if diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 标准输出测试成功\e[0m"
|
||||
else
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
echo -e "\e[31m 标准输出测试失败\e[0m"
|
||||
is_passed=0
|
||||
echo -e " \e[36m---------- 期望输出 ----------\e[0m"
|
||||
cat "${EXPECTED_STDOUT_FILE}"
|
||||
echo -e " \e[36m---------- 实际输出 ----------\e[0m"
|
||||
cat "${output_actual_file}"
|
||||
display_file_content "${EXPECTED_STDOUT_FILE}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m------------------------------\e[0m"
|
||||
fi
|
||||
else
|
||||
# 纯标准输出比较
|
||||
if [ $ACTUAL_RETURN_CODE -ne 0 ]; then
|
||||
echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m"
|
||||
fi
|
||||
if diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${output_reference_file}") >/dev/null 2>&1; then
|
||||
if ! diff -q <(sed ':a;N;$!ba;s/\n*$//' "${output_actual_file}") <(sed ':a;N;$!ba;s/\n*$//' "${output_reference_file}") >/dev/null 2>&1; then
|
||||
echo -e "\e[32m 成功: 输出与参考输出匹配\e[0m"
|
||||
else
|
||||
echo -e "\e[31m 失败: 输出不匹配\e[0m"
|
||||
is_passed=0
|
||||
echo -e " \e[36m---------- 期望输出 ----------\e[0m"
|
||||
cat "${output_reference_file}"
|
||||
echo -e " \e[36m---------- 实际输出 ----------\e[0m"
|
||||
cat "${output_actual_file}"
|
||||
display_file_content "${output_reference_file}" " \e[36m---------- 期望输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
display_file_content "${output_actual_file}" " \e[36m---------- 实际输出 ----------\e[0m" "${MAX_OUTPUT_LINES}"
|
||||
echo -e " \e[36m------------------------------\e[0m"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# 没有 .out 文件,只报告返回码
|
||||
echo " 无参考输出文件。程序返回码: ${ACTUAL_RETURN_CODE}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 更新通过用例计数
|
||||
# --- 本次修改点 ---
|
||||
if [ "$is_passed" -eq 1 ]; then
|
||||
((PASSED_CASES++))
|
||||
else
|
||||
# 将失败的用例名称添加到列表中
|
||||
FAILED_CASES_LIST+="${relative_path_no_ext}.sy\n"
|
||||
fi
|
||||
echo "" # 添加空行以提高可读性
|
||||
echo ""
|
||||
done <<< "$sy_files"
|
||||
|
||||
# --- 新增功能: 打印最终总结 ---
|
||||
echo "========================================"
|
||||
echo "测试完成"
|
||||
echo "测试通过率: [${PASSED_CASES}/${TOTAL_CASES}]"
|
||||
|
||||
# --- 本次修改点: 打印未通过的测例列表 ---
|
||||
if [ -n "$FAILED_CASES_LIST" ]; then
|
||||
echo ""
|
||||
echo -e "\e[31m未通过的测例:\e[0m"
|
||||
# 使用 -e 来解释换行符 \n
|
||||
echo -e "${FAILED_CASES_LIST}"
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user