Compare commits
1 Commits
constPropa
...
deploy-202
| Author | SHA1 | Date | |
|---|---|---|---|
| ed8fc32a23 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -36,7 +36,6 @@ doxygen
|
||||
|
||||
!/testdata/functional/*.out
|
||||
!/testdata/h_functional/*.out
|
||||
!/testdata/performance/*.out
|
||||
build/
|
||||
.antlr
|
||||
.vscode/
|
||||
|
||||
@ -3,26 +3,4 @@
|
||||
| 名称 | 优化级别 | 开发进度 |
|
||||
| ------------ | ------------ | ---------- |
|
||||
| CFG优化 | 函数级 | 已完成 |
|
||||
| DCE | 函数级 | 待正确性测试 |
|
||||
| Mem2Reg | 函数级 | 待正确性测试 |
|
||||
| Reg2Mem | 函数级 | 待正确性测试 |
|
||||
|
||||
|
||||
# 部分优化遍的说明
|
||||
|
||||
## Mem2Reg
|
||||
|
||||
Mem2Reg 遍的主要目标是将那些不必要的、只用于局部标量变量的内存分配 (alloca 指令) 消除,并将这些变量的值转换为 SSA 形式。这有助于减少内存访问,提高代码效率,并为后续的优化创造更好的条件。
|
||||
|
||||
## Reg2Mem
|
||||
|
||||
我们的Reg2Mem 遍的主要目标是作为 Mem2Reg 的一种逆操作,但更具体是解决后端无法识别 PhiInst 指令的问题。主要的速录是将函数参数和 PhiInst 指令的结果从 SSA 形式转换回内存形式,通过插入 alloca、load 和 store 指令来实现。其他非 Phi 的指令结果将保持 SSA 形式。
|
||||
|
||||
|
||||
# 后续优化可能涉及的改动
|
||||
|
||||
## 1)将所有的alloca集中到entryblock中
|
||||
|
||||
好处:优化友好性,方便mem2reg提升
|
||||
目前没有实现这个机制,如果想要实现首先解决同一函数不同域的同名变量命名区分
|
||||
需要保证符号表能正确维护域中的局部变量
|
||||
| DCE | 函数级 | 待测试 |
|
||||
Binary file not shown.
171
src/AddressCalculationExpansion.cpp
Normal file
171
src/AddressCalculationExpansion.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
#include "AddressCalculationExpansion.h"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
|
||||
extern int DEBUG;
|
||||
|
||||
namespace sysy {
|
||||
|
||||
bool AddressCalculationExpansion::run() {
|
||||
bool changed = false;
|
||||
|
||||
for (auto& funcPair : pModule->getFunctions()) {
|
||||
Function* func = funcPair.second.get();
|
||||
for (auto& bb_ptr : func->getBasicBlocks()) {
|
||||
BasicBlock* bb = bb_ptr.get();
|
||||
for (auto it = bb->getInstructions().begin(); it != bb->getInstructions().end(); ) {
|
||||
Instruction* inst = it->get();
|
||||
|
||||
Value* basePointer = nullptr;
|
||||
Value* valueToStore = nullptr;
|
||||
size_t firstIndexOperandIdx = 0;
|
||||
size_t numBaseOperands = 0;
|
||||
|
||||
if (inst->isLoad()) {
|
||||
numBaseOperands = 1;
|
||||
basePointer = inst->getOperand(0);
|
||||
firstIndexOperandIdx = 1;
|
||||
} else if (inst->isStore()) {
|
||||
numBaseOperands = 2;
|
||||
valueToStore = inst->getOperand(0);
|
||||
basePointer = inst->getOperand(1);
|
||||
firstIndexOperandIdx = 2;
|
||||
} else {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inst->getNumOperands() <= numBaseOperands) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<int> dims;
|
||||
if (AllocaInst* allocaInst = dynamic_cast<AllocaInst*>(basePointer)) {
|
||||
for (const auto& use_ptr : allocaInst->getDims()) {
|
||||
Value* dimValue = use_ptr->getValue();
|
||||
if (ConstantValue* constVal = dynamic_cast<ConstantValue*>(dimValue)) {
|
||||
dims.push_back(constVal->getInt());
|
||||
} else {
|
||||
std::cerr << "Warning: AllocaInst dimension is not a constant integer. Skipping GEP expansion for: ";
|
||||
SysYPrinter::printValue(allocaInst);
|
||||
std::cerr << "\n";
|
||||
dims.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (GlobalValue* globalValue = dynamic_cast<GlobalValue*>(basePointer)) {
|
||||
// 遍历 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);
|
||||
std::cerr << " in instruction ";
|
||||
SysYPrinter::printInst(inst);
|
||||
std::cerr << "\n";
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dims.empty() && (inst->getNumOperands() > numBaseOperands)) {
|
||||
if (DEBUG) {
|
||||
std::cerr << "ACE Warning: Could not get valid array dimensions for ";
|
||||
SysYPrinter::printValue(basePointer);
|
||||
std::cerr << " in instruction ";
|
||||
SysYPrinter::printInst(inst);
|
||||
std::cerr << " (expected dimensions for indices, but got none).\n";
|
||||
}
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<Value*> indexOperands;
|
||||
for (size_t i = firstIndexOperandIdx; i < inst->getNumOperands(); ++i) {
|
||||
indexOperands.push_back(inst->getOperand(i));
|
||||
}
|
||||
|
||||
if (AllocaInst* allocaInst = dynamic_cast<AllocaInst*>(basePointer)) {
|
||||
if (allocaInst->getNumDims() != indexOperands.size()) {
|
||||
if (DEBUG) {
|
||||
std::cerr << "ACE Warning: Index count (" << indexOperands.size() << ") does not match AllocaInst dimensions (" << allocaInst->getNumDims() << ") for instruction ";
|
||||
SysYPrinter::printInst(inst);
|
||||
std::cerr << "\n";
|
||||
}
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Value* totalOffset = ConstantInteger::get(0);
|
||||
pBuilder->setPosition(bb, it);
|
||||
|
||||
for (size_t i = 0; i < indexOperands.size(); ++i) {
|
||||
Value* index = indexOperands[i];
|
||||
int stride = calculateStride(dims, i);
|
||||
Value* strideConst = ConstantInteger::get(stride);
|
||||
Type* intType = Type::getIntType();
|
||||
BinaryInst* currentDimOffsetInst = pBuilder->createBinaryInst(Instruction::kMul, intType, index, strideConst);
|
||||
BinaryInst* newTotalOffsetInst = pBuilder->createBinaryInst(Instruction::kAdd, intType, totalOffset, currentDimOffsetInst);
|
||||
totalOffset = newTotalOffsetInst;
|
||||
}
|
||||
|
||||
// 计算有效地址:effective_address = basePointer + totalOffset
|
||||
Value* effective_address = pBuilder->createBinaryInst(Instruction::kAdd, basePointer->getType(), basePointer, totalOffset);
|
||||
|
||||
// 创建新的 LoadInst 或 StoreInst,indices 为空
|
||||
Instruction* newInst = nullptr;
|
||||
if (inst->isLoad()) {
|
||||
newInst = pBuilder->createLoadInst(effective_address, {});
|
||||
inst->replaceAllUsesWith(newInst);
|
||||
} else { // StoreInst
|
||||
newInst = pBuilder->createStoreInst(valueToStore, effective_address, {});
|
||||
}
|
||||
|
||||
Instruction* oldInst = it->get();
|
||||
++it;
|
||||
|
||||
for (size_t i = 0; i < oldInst->getNumOperands(); ++i) {
|
||||
Value* operandValue = oldInst->getOperand(i);
|
||||
if (operandValue) {
|
||||
for (auto use_it = operandValue->getUses().begin(); use_it != operandValue->getUses().end(); ++use_it) {
|
||||
if ((*use_it)->getUser() == oldInst && (*use_it)->getIndex() == i) {
|
||||
operandValue->removeUse(*use_it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bb->getInstructions().erase(std::prev(it));
|
||||
changed = true;
|
||||
|
||||
if (DEBUG) {
|
||||
std::cerr << "ACE: Computed effective address:\n";
|
||||
SysYPrinter::printInst(dynamic_cast<Instruction*>(effective_address));
|
||||
std::cerr << "ACE: New Load/Store instruction:\n";
|
||||
SysYPrinter::printInst(newInst);
|
||||
std::cerr << "--------------------------------\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,24 +1,58 @@
|
||||
# src/CMakeLists.txt
|
||||
# add_subdirectory 命令会负责遍历子目录并查找其内部的 CMakeLists.txt 文件
|
||||
add_subdirectory(frontend)
|
||||
add_subdirectory(midend)
|
||||
add_subdirectory(backend/RISCv64)
|
||||
# 移除 ANTLR 代码生成相关配置
|
||||
# list(APPEND CMAKE_MODULE_PATH "${ANTLR_RUNTIME}/cmake")
|
||||
# include(FindANTLR)
|
||||
# antlr_target(SysYGen SysY.g4
|
||||
# LEXER PARSER
|
||||
# OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
# VISITOR
|
||||
# )
|
||||
|
||||
# 构建 sysyc 可执行文件,链接各个模块的库
|
||||
# 移除 SysYParser 库的构建(如果不需要独立库)
|
||||
# add_library(SysYParser SHARED ${ANTLR_SysYGen_CXX_OUTPUTS})
|
||||
# target_include_directories(SysYParser PUBLIC ${ANTLR_RUNTIME}/runtime/src)
|
||||
# target_link_libraries(SysYParser PUBLIC antlr4_shared)
|
||||
|
||||
# 构建 sysyc 可执行文件,使用手动提供的 SysYLexer.cpp、SysYParser.cpp 等文件
|
||||
add_executable(sysyc
|
||||
sysyc.cpp
|
||||
sysyc.cpp
|
||||
SysYLexer.cpp # 手动提供的文件
|
||||
SysYParser.cpp # 手动提供的文件
|
||||
SysYVisitor.cpp # 手动提供的文件
|
||||
IR.cpp
|
||||
SysYIRGenerator.cpp
|
||||
SysYIRPrinter.cpp
|
||||
SysYIRCFGOpt.cpp
|
||||
Pass.cpp
|
||||
Dom.cpp
|
||||
Liveness.cpp
|
||||
DCE.cpp
|
||||
AddressCalculationExpansion.cpp
|
||||
# Mem2Reg.cpp
|
||||
# Reg2Mem.cpp
|
||||
RISCv64Backend.cpp
|
||||
RISCv64ISel.cpp
|
||||
RISCv64RegAlloc.cpp
|
||||
RISCv64AsmPrinter.cpp
|
||||
RISCv64Peephole.cpp
|
||||
PreRA_Scheduler.cpp
|
||||
PostRA_Scheduler.cpp
|
||||
CalleeSavedHandler.cpp
|
||||
RISCv64LLIR.cpp
|
||||
)
|
||||
|
||||
# 链接各个模块的库
|
||||
target_link_libraries(sysyc PRIVATE
|
||||
frontend_lib
|
||||
midend_lib
|
||||
riscv64_backend_lib
|
||||
antlr4_shared
|
||||
)
|
||||
|
||||
# 设置 include 路径,包含项目顶层 include 目录
|
||||
# 设置 include 路径,包含 ANTLR 运行时库和项目头文件
|
||||
target_include_directories(sysyc PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include # 项目头文件目录
|
||||
${ANTLR_RUNTIME}/runtime/src # ANTLR运行时库头文件
|
||||
)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include # 项目头文件目录
|
||||
${ANTLR_RUNTIME}/runtime/src # ANTLR 运行时库头文件
|
||||
)
|
||||
|
||||
# 保留 ANTLR 运行时库的链接
|
||||
target_link_libraries(sysyc PRIVATE antlr4_shared)
|
||||
|
||||
# 保留其他编译选项
|
||||
target_compile_options(sysyc PRIVATE -frtti)
|
||||
|
||||
# 可选:线程支持(如果需要,取消注释)
|
||||
# set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
# find_package(Threads REQUIRED)
|
||||
# target_link_libraries(sysyc PRIVATE Threads::Threads)
|
||||
123
src/CalleeSavedHandler.cpp
Normal file
123
src/CalleeSavedHandler.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "CalleeSavedHandler.h"
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char CalleeSavedHandler::ID = 0;
|
||||
|
||||
bool CalleeSavedHandler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// This pass works on MachineFunction level, not IR level
|
||||
return false;
|
||||
}
|
||||
|
||||
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().begin(); it != mbb->getInstructions().end(); ++it) {
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
// 1. 创建一个临时vector来存储所有需要插入的恢复指令
|
||||
std::vector<std::unique_ptr<MachineInstr>> restore_instrs;
|
||||
|
||||
int current_offset_load = base_offset;
|
||||
// 以相同的顺序(例如 s1, s2, ...)创建恢复指令
|
||||
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)
|
||||
));
|
||||
restore_instrs.push_back(std::move(ld));
|
||||
current_offset_load -= 8;
|
||||
}
|
||||
|
||||
// 2. 使用 make_move_iterator 一次性将所有恢复指令插入到 RET 指令之前
|
||||
// 这可以高效地转移指令的所有权,并且只让迭代器失效一次。
|
||||
if (!restore_instrs.empty()) {
|
||||
mbb->getInstructions().insert(it,
|
||||
std::make_move_iterator(restore_instrs.begin()),
|
||||
std::make_move_iterator(restore_instrs.end())
|
||||
);
|
||||
}
|
||||
|
||||
// 找到了RET并处理完毕后,就可以跳出内层循环,继续寻找下一个基本块
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -38,14 +38,6 @@ const std::set<BasicBlock *> *DominatorTree::getDominanceFrontier(BasicBlock *BB
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::set<BasicBlock*>* DominatorTree::getDominatorTreeChildren(BasicBlock* BB) const {
|
||||
auto it = DominatorTreeChildren.find(BB);
|
||||
if (it != DominatorTreeChildren.end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DominatorTree::computeDominators(Function *F) {
|
||||
// 经典的迭代算法计算支配者集合
|
||||
// TODO: 可以替换为更高效的算法,如 Lengauer-Tarjan 算法
|
||||
@ -167,19 +159,6 @@ void DominatorTree::computeDominanceFrontiers(Function *F) {
|
||||
}
|
||||
}
|
||||
|
||||
void DominatorTree::computeDominatorTreeChildren(Function *F) {
|
||||
for (auto &bb_ptr : F->getBasicBlocks()) {
|
||||
BasicBlock *B = bb_ptr.get();
|
||||
auto it = getImmediateDominator(B);
|
||||
if (it != nullptr) {
|
||||
BasicBlock *A = it;
|
||||
if (A) {
|
||||
DominatorTreeChildren[A].insert(B);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================================================
|
||||
// DominatorTreeAnalysisPass 的实现
|
||||
// ==============================================================
|
||||
@ -190,7 +169,6 @@ bool DominatorTreeAnalysisPass::runOnFunction(Function* F, AnalysisManager &AM)
|
||||
CurrentDominatorTree->computeDominators(F);
|
||||
CurrentDominatorTree->computeIDoms(F);
|
||||
CurrentDominatorTree->computeDominanceFrontiers(F);
|
||||
CurrentDominatorTree->computeDominatorTreeChildren(F);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -652,7 +652,6 @@ Function * CallInst::getCallee() const { return dynamic_cast<Function *>(getOper
|
||||
|
||||
/**
|
||||
* 获取变量指针
|
||||
* 如果在当前作用域或父作用域中找到变量,则返回该变量的指针,否则返回nullptr
|
||||
*/
|
||||
auto SymbolTable::getVariable(const std::string &name) const -> Value * {
|
||||
auto node = curNode;
|
||||
@ -689,7 +688,7 @@ auto SymbolTable::addVariable(const std::string &name, Value *variable) -> Value
|
||||
if (global != nullptr) {
|
||||
globals.emplace_back(global);
|
||||
} else if (constvar != nullptr) {
|
||||
globalconsts.emplace_back(constvar);
|
||||
consts.emplace_back(constvar);
|
||||
}
|
||||
|
||||
result = variable;
|
||||
@ -704,7 +703,7 @@ auto SymbolTable::getGlobals() -> std::vector<std::unique_ptr<GlobalValue>> & {
|
||||
/**
|
||||
* 获取常量
|
||||
*/
|
||||
auto SymbolTable::getConsts() const -> const std::vector<std::unique_ptr<ConstantVariable>> & { return globalconsts; }
|
||||
auto SymbolTable::getConsts() const -> const std::vector<std::unique_ptr<ConstantVariable>> & { return consts; }
|
||||
/**
|
||||
* 进入新的作用域
|
||||
*/
|
||||
@ -3,9 +3,6 @@
|
||||
#include "SysYIRCFGOpt.h"
|
||||
#include "SysYIRPrinter.h"
|
||||
#include "DCE.h"
|
||||
#include "Mem2Reg.h"
|
||||
#include "Reg2Mem.h"
|
||||
#include "ConstPropagation.h"
|
||||
#include "Pass.h"
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
@ -47,10 +44,6 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR
|
||||
registerOptimizationPass<SysYCondBr2BrPass>(builderIR);
|
||||
registerOptimizationPass<SysYAddReturnPass>(builderIR);
|
||||
|
||||
registerOptimizationPass<DCE>();
|
||||
registerOptimizationPass<Mem2Reg>(builderIR);
|
||||
registerOptimizationPass<Reg2Mem>(builderIR);
|
||||
|
||||
if (optLevel >= 1) {
|
||||
//经过设计安排优化遍的执行顺序以及执行逻辑
|
||||
if (DEBUG) std::cout << "Applying -O1 optimizations.\n";
|
||||
@ -65,39 +58,10 @@ void PassManager::runOptimizationPipeline(Module* moduleIR, IRBuilder* builderIR
|
||||
this->addPass(&SysYAddReturnPass::ID);
|
||||
this->run();
|
||||
|
||||
if(DEBUG) {
|
||||
std::cout << "=== IR After CFGOpt Optimizations ===\n";
|
||||
printPasses();
|
||||
}
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&DCE::ID);
|
||||
this->run();
|
||||
|
||||
if(DEBUG) {
|
||||
std::cout << "=== IR After DCE Optimizations ===\n";
|
||||
printPasses();
|
||||
}
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&Mem2Reg::ID);
|
||||
this->addPass(&ConstPropagation::ID);
|
||||
this->run();
|
||||
|
||||
if(DEBUG) {
|
||||
std::cout << "=== IR After Mem2Reg Optimizations ===\n";
|
||||
printPasses();
|
||||
}
|
||||
|
||||
this->clearPasses();
|
||||
this->addPass(&Reg2Mem::ID);
|
||||
this->run();
|
||||
|
||||
if(DEBUG) {
|
||||
std::cout << "=== IR After Reg2Mem Optimizations ===\n";
|
||||
printPasses();
|
||||
}
|
||||
|
||||
if (DEBUG) std::cout << "--- Custom optimization sequence finished ---\n";
|
||||
}
|
||||
|
||||
@ -188,20 +152,6 @@ bool PassManager::run() {
|
||||
|
||||
}
|
||||
|
||||
void PassManager::printPasses() const {
|
||||
std::cout << "Registered Passes:\n";
|
||||
for (const auto &p : passes) {
|
||||
std::cout << " - " << p->getName() << " (Granularity: "
|
||||
<< static_cast<int>(p->getGranularity())
|
||||
<< ", Kind: " << static_cast<int>(p->getPassKind()) << ")\n";
|
||||
}
|
||||
std::cout << "Total Passes: " << passes.size() << "\n";
|
||||
if (pmodule) {
|
||||
SysYPrinter printer(pmodule);
|
||||
std::cout << "Module IR:\n";
|
||||
printer.printIR();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename AnalysisPassType> void registerAnalysisPass() {
|
||||
PassRegistry::getPassRegistry().registerPass(&AnalysisPassType::ID,
|
||||
383
src/PostRA_Scheduler.cpp
Normal file
383
src/PostRA_Scheduler.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
#include "PostRA_Scheduler.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#define MAX_SCHEDULING_BLOCK_SIZE 10000 // 限制调度块大小,避免过大导致性能问题
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PostRA_Scheduler::ID = 0;
|
||||
|
||||
// 检查指令是否是加载指令 (LW, LD)
|
||||
bool isLoadInstr(MachineInstr* instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::LW || opcode == RVOpcodes::LD ||
|
||||
opcode == RVOpcodes::LH || opcode == RVOpcodes::LB ||
|
||||
opcode == RVOpcodes::LHU || opcode == RVOpcodes::LBU ||
|
||||
opcode == RVOpcodes::LWU;
|
||||
}
|
||||
|
||||
// 检查指令是否是存储指令 (SW, SD)
|
||||
bool isStoreInstr(MachineInstr* instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::SW || opcode == RVOpcodes::SD ||
|
||||
opcode == RVOpcodes::SH || opcode == RVOpcodes::SB;
|
||||
}
|
||||
|
||||
// 检查指令是否为控制流指令
|
||||
bool isControlFlowInstr(MachineInstr* instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::RET || opcode == RVOpcodes::J ||
|
||||
opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::CALL;
|
||||
}
|
||||
|
||||
// 获取指令定义的寄存器 - 修复版本
|
||||
std::set<PhysicalReg> getDefinedRegisters(MachineInstr* instr) {
|
||||
std::set<PhysicalReg> defined_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// 特殊处理CALL指令
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
// CALL指令可能定义返回值寄存器
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 存储指令不定义寄存器
|
||||
if (isStoreInstr(instr)) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 分支指令不定义寄存器
|
||||
if (opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::J || opcode == RVOpcodes::RET) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 对于其他指令,第一个寄存器操作数通常是定义的
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 获取指令使用的寄存器 - 修复版本
|
||||
std::set<PhysicalReg> getUsedRegisters(MachineInstr* instr) {
|
||||
std::set<PhysicalReg> used_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// 特殊处理CALL指令
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
bool first_reg_skipped = false;
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (!first_reg_skipped) {
|
||||
first_reg_skipped = true;
|
||||
continue; // 跳过返回值寄存器
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于存储指令,所有寄存器操作数都是使用的
|
||||
if (isStoreInstr(instr)) {
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于分支指令,所有寄存器操作数都是使用的
|
||||
if (opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU) {
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于其他指令,除了第一个寄存器操作数(通常是定义),其余都是使用的
|
||||
bool first_reg = true;
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (first_reg) {
|
||||
first_reg = false;
|
||||
continue; // 跳过第一个寄存器(定义)
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 获取内存访问的基址和偏移
|
||||
struct MemoryAccess {
|
||||
PhysicalReg base_reg;
|
||||
int64_t offset;
|
||||
bool valid;
|
||||
|
||||
MemoryAccess() : valid(false) {}
|
||||
MemoryAccess(PhysicalReg base, int64_t off) : base_reg(base), offset(off), valid(true) {}
|
||||
};
|
||||
|
||||
MemoryAccess getMemoryAccess(MachineInstr* instr) {
|
||||
if (!isLoadInstr(instr) && !isStoreInstr(instr)) {
|
||||
return MemoryAccess();
|
||||
}
|
||||
|
||||
// 查找内存操作数
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
return MemoryAccess(mem_op->getBase()->getPReg(), mem_op->getOffset()->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryAccess();
|
||||
}
|
||||
|
||||
// 检查内存依赖 - 加强版本
|
||||
bool hasMemoryDependency(MachineInstr* instr1, MachineInstr* instr2) {
|
||||
// 如果都不是内存指令,没有内存依赖
|
||||
if (!isLoadInstr(instr1) && !isStoreInstr(instr1) &&
|
||||
!isLoadInstr(instr2) && !isStoreInstr(instr2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MemoryAccess mem1 = getMemoryAccess(instr1);
|
||||
MemoryAccess mem2 = getMemoryAccess(instr2);
|
||||
|
||||
if (!mem1.valid || !mem2.valid) {
|
||||
// 如果无法确定内存访问模式,保守地认为存在依赖
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果访问相同的内存位置
|
||||
if (mem1.base_reg == mem2.base_reg && mem1.offset == mem2.offset) {
|
||||
// Store->Load: RAW依赖
|
||||
// Load->Store: WAR依赖
|
||||
// Store->Store: WAW依赖
|
||||
return isStoreInstr(instr1) || isStoreInstr(instr2);
|
||||
}
|
||||
|
||||
// 不同内存位置通常没有依赖,但为了安全起见,
|
||||
// 如果涉及store指令,我们需要更保守
|
||||
if (isStoreInstr(instr1) && isLoadInstr(instr2)) {
|
||||
// 保守处理:不同store和load之间可能有别名
|
||||
return false; // 这里可以根据需要调整策略
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查两个指令之间是否存在依赖关系 - 修复版本
|
||||
bool hasDependency(MachineInstr* instr1, MachineInstr* instr2) {
|
||||
// 检查RAW依赖:instr1定义的寄存器是否被instr2使用
|
||||
auto defined_regs1 = getDefinedRegisters(instr1);
|
||||
auto used_regs2 = getUsedRegisters(instr2);
|
||||
|
||||
for (const auto& reg : defined_regs1) {
|
||||
if (used_regs2.find(reg) != used_regs2.end()) {
|
||||
return true; // RAW依赖 - instr2读取instr1写入的值
|
||||
}
|
||||
}
|
||||
|
||||
// 检查WAR依赖:instr1使用的寄存器是否被instr2定义
|
||||
auto used_regs1 = getUsedRegisters(instr1);
|
||||
auto defined_regs2 = getDefinedRegisters(instr2);
|
||||
|
||||
for (const auto& reg : used_regs1) {
|
||||
if (defined_regs2.find(reg) != defined_regs2.end()) {
|
||||
return true; // WAR依赖 - instr2覆盖instr1需要的值
|
||||
}
|
||||
}
|
||||
|
||||
// 检查WAW依赖:两个指令定义相同寄存器
|
||||
for (const auto& reg : defined_regs1) {
|
||||
if (defined_regs2.find(reg) != defined_regs2.end()) {
|
||||
return true; // WAW依赖 - 两条指令写入同一寄存器
|
||||
}
|
||||
}
|
||||
|
||||
// 检查内存依赖
|
||||
if (hasMemoryDependency(instr1, instr2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否可以安全地将instr1和instr2交换位置
|
||||
bool canSwapInstructions(MachineInstr* instr1, MachineInstr* instr2) {
|
||||
// 不能移动控制流指令
|
||||
if (isControlFlowInstr(instr1) || isControlFlowInstr(instr2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查双向依赖关系
|
||||
return !hasDependency(instr1, instr2) && !hasDependency(instr2, instr1);
|
||||
}
|
||||
|
||||
// 新增:验证调度结果的正确性
|
||||
void validateSchedule(const std::vector<MachineInstr*>& instr_list) {
|
||||
for (int i = 0; i < (int)instr_list.size(); i++) {
|
||||
for (int j = i + 1; j < (int)instr_list.size(); j++) {
|
||||
MachineInstr* earlier = instr_list[i];
|
||||
MachineInstr* later = instr_list[j];
|
||||
|
||||
// 检查是否存在被违反的依赖关系
|
||||
auto defined_regs = getDefinedRegisters(earlier);
|
||||
auto used_regs = getUsedRegisters(later);
|
||||
|
||||
// 检查RAW依赖
|
||||
for (const auto& reg : defined_regs) {
|
||||
if (used_regs.find(reg) != used_regs.end()) {
|
||||
// 这是正常的依赖关系,earlier应该在later之前
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查内存依赖
|
||||
if (hasMemoryDependency(earlier, later)) {
|
||||
MemoryAccess mem1 = getMemoryAccess(earlier);
|
||||
MemoryAccess mem2 = getMemoryAccess(later);
|
||||
|
||||
if (mem1.valid && mem2.valid &&
|
||||
mem1.base_reg == mem2.base_reg && mem1.offset == mem2.offset) {
|
||||
if (isStoreInstr(earlier) && isLoadInstr(later)) {
|
||||
// Store->Load依赖,顺序正确
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在基本块内对指令进行调度优化 - 完全重写版本
|
||||
void scheduleBlock(MachineBasicBlock* mbb) {
|
||||
auto& instructions = mbb->getInstructions();
|
||||
if (instructions.size() <= 1) return;
|
||||
if (instructions.size() > MAX_SCHEDULING_BLOCK_SIZE) {
|
||||
return; // 跳过超大块,防止卡住
|
||||
}
|
||||
|
||||
std::vector<MachineInstr*> instr_list;
|
||||
for (auto& instr : instructions) {
|
||||
instr_list.push_back(instr.get());
|
||||
}
|
||||
|
||||
// 使用更严格的调度策略,避免破坏依赖关系
|
||||
bool changed = true;
|
||||
int max_iterations = 10; // 限制迭代次数避免死循环
|
||||
int iteration = 0;
|
||||
|
||||
while (changed && iteration < max_iterations) {
|
||||
changed = false;
|
||||
iteration++;
|
||||
|
||||
for (int i = 0; i < (int)instr_list.size() - 1; i++) {
|
||||
MachineInstr* instr1 = instr_list[i];
|
||||
MachineInstr* instr2 = instr_list[i + 1];
|
||||
|
||||
// 只进行非常保守的优化
|
||||
bool should_swap = false;
|
||||
|
||||
// 策略1: 将load指令提前,减少load-use延迟
|
||||
if (isLoadInstr(instr2) && !isLoadInstr(instr1) && !isStoreInstr(instr1)) {
|
||||
should_swap = canSwapInstructions(instr1, instr2);
|
||||
}
|
||||
// 策略2: 将非关键store指令延后,为其他指令让路
|
||||
else if (isStoreInstr(instr1) && !isLoadInstr(instr2) && !isStoreInstr(instr2)) {
|
||||
should_swap = canSwapInstructions(instr1, instr2);
|
||||
}
|
||||
|
||||
if (should_swap) {
|
||||
std::swap(instr_list[i], instr_list[i + 1]);
|
||||
changed = true;
|
||||
|
||||
// 调试输出
|
||||
// std::cout << "Swapped instructions at positions " << i << " and " << (i+1) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证调度结果的正确性
|
||||
validateSchedule(instr_list);
|
||||
|
||||
// 将调度后的指令顺序写回
|
||||
std::map<MachineInstr*, std::unique_ptr<MachineInstr>> instr_map;
|
||||
for (auto& instr : instructions) {
|
||||
instr_map[instr.get()] = std::move(instr);
|
||||
}
|
||||
|
||||
instructions.clear();
|
||||
for (auto instr : instr_list) {
|
||||
instructions.push_back(std::move(instr_map[instr]));
|
||||
}
|
||||
}
|
||||
|
||||
bool PostRA_Scheduler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// 这个函数在IR级别运行,但我们需要在机器指令级别运行
|
||||
// 所以我们返回false,表示没有对IR进行修改
|
||||
return false;
|
||||
}
|
||||
|
||||
void PostRA_Scheduler::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
// std::cout << "Running Post-RA Local Scheduler... " << std::endl;
|
||||
|
||||
// 遍历每个机器基本块
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
scheduleBlock(mbb.get());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
36
src/PreRA_Scheduler.cpp
Normal file
36
src/PreRA_Scheduler.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "PreRA_Scheduler.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PreRA_Scheduler::ID = 0;
|
||||
|
||||
bool PreRA_Scheduler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// TODO: 在此实现寄存器分配前的指令调度。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 对每个基本块内的MachineInstr列表进行重排。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 分析每个基本块内指令的数据依赖关系,构建依赖图(DAG)。
|
||||
// 2.
|
||||
// 根据目标处理器的流水线特性(指令延迟等),使用列表调度等算法对指令进行重排。
|
||||
// 3. 此时操作的是虚拟寄存器,只存在真依赖,调度自由度最大。
|
||||
//
|
||||
// std::cout << "Running Pre-RA Instruction Scheduler..." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PreRA_Scheduler::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
// TODO: 在此实现寄存器分配前的指令调度。
|
||||
// 遍历mfunc中的每一个MachineBasicBlock。
|
||||
// 对每个基本块内的MachineInstr列表进行重排。
|
||||
//
|
||||
// 实现思路:
|
||||
// 1. 分析每个基本块内指令的数据依赖关系,构建依赖图(DAG)。
|
||||
// 2.
|
||||
// 根据目标处理器的流水线特性(指令延迟等),使用列表调度等算法对指令进行重排。
|
||||
// 3. 此时操作的是虚拟寄存器,只存在真依赖,调度自由度最大。
|
||||
//
|
||||
// std::cout << "Running Pre-RA Instruction Scheduler..." << std::endl;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -7,15 +7,9 @@ namespace sysy {
|
||||
// 检查是否为内存加载/存储指令,以处理特殊的打印格式
|
||||
bool isMemoryOp(RVOpcodes opcode) {
|
||||
switch (opcode) {
|
||||
// --- 整数加载/存储 (原有逻辑) ---
|
||||
case RVOpcodes::LB: case RVOpcodes::LH: case RVOpcodes::LW: case RVOpcodes::LD:
|
||||
case RVOpcodes::LBU: case RVOpcodes::LHU: case RVOpcodes::LWU:
|
||||
case RVOpcodes::SB: case RVOpcodes::SH: case RVOpcodes::SW: case RVOpcodes::SD:
|
||||
case RVOpcodes::FLW:
|
||||
case RVOpcodes::FSW:
|
||||
// 如果未来支持双精度,也在这里添加FLD/FSD
|
||||
// case RVOpcodes::FLD:
|
||||
// case RVOpcodes::FSD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -28,12 +22,52 @@ void RISCv64AsmPrinter::run(std::ostream& os, bool debug) {
|
||||
OS = &os;
|
||||
|
||||
*OS << ".globl " << MFunc->getName() << "\n";
|
||||
*OS << MFunc->getName() << ":\n";
|
||||
|
||||
printPrologue();
|
||||
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
printBasicBlock(mbb.get(), debug);
|
||||
}
|
||||
}
|
||||
|
||||
// 在 RISCv64AsmPrinter.cpp 文件中
|
||||
|
||||
void RISCv64AsmPrinter::printPrologue() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
// 计算总栈帧大小。
|
||||
// 包含三部分:局部变量区、寄存器溢出区、以及为被调用者保存(callee-saved)寄存器预留的区域。
|
||||
// 最后再加上为保存 ra 和 s0 固定的16字节。
|
||||
int total_stack_size = frame_info.locals_size +
|
||||
frame_info.spill_size +
|
||||
frame_info.callee_saved_size +
|
||||
16;
|
||||
|
||||
// 保持栈指针16字节对齐
|
||||
int aligned_stack_size = (total_stack_size + 15) & ~15;
|
||||
frame_info.total_size = aligned_stack_size; // 更新最终的栈大小
|
||||
|
||||
// 只有在需要分配栈空间时才生成指令
|
||||
if (aligned_stack_size > 0) {
|
||||
// 1. 一次性分配整个栈帧
|
||||
*OS << " addi sp, sp, -" << aligned_stack_size << "\n";
|
||||
// 2. 在新的栈顶附近保存 ra 和 s0
|
||||
*OS << " sd ra, " << (aligned_stack_size - 8) << "(sp)\n";
|
||||
*OS << " sd s0, " << (aligned_stack_size - 16) << "(sp)\n";
|
||||
// 3. 设置新的帧指针 s0,使其指向栈帧的底部(高地址)
|
||||
*OS << " addi s0, sp, " << aligned_stack_size << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void RISCv64AsmPrinter::printEpilogue() {
|
||||
int aligned_stack_size = MFunc->getFrameInfo().total_size;
|
||||
if (aligned_stack_size > 0) {
|
||||
*OS << " ld ra, " << (aligned_stack_size - 8) << "(sp)\n";
|
||||
*OS << " ld s0, " << (aligned_stack_size - 16) << "(sp)\n";
|
||||
*OS << " addi sp, sp, " << aligned_stack_size << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void RISCv64AsmPrinter::printBasicBlock(MachineBasicBlock* mbb, bool debug) {
|
||||
if (!mbb->getName().empty()) {
|
||||
*OS << mbb->getName() << ":\n";
|
||||
@ -45,6 +79,9 @@ void RISCv64AsmPrinter::printBasicBlock(MachineBasicBlock* mbb, bool debug) {
|
||||
|
||||
void RISCv64AsmPrinter::printInstruction(MachineInstr* instr, bool debug) {
|
||||
auto opcode = instr->getOpcode();
|
||||
if (opcode == RVOpcodes::RET) {
|
||||
printEpilogue();
|
||||
}
|
||||
|
||||
if (opcode == RVOpcodes::LABEL) {
|
||||
// 标签直接打印,不加缩进
|
||||
@ -79,9 +116,7 @@ void RISCv64AsmPrinter::printInstruction(MachineInstr* instr, bool debug) {
|
||||
case RVOpcodes::LHU: *OS << "lhu "; break; case RVOpcodes::LBU: *OS << "lbu "; break;
|
||||
case RVOpcodes::SW: *OS << "sw "; break; case RVOpcodes::SH: *OS << "sh "; break;
|
||||
case RVOpcodes::SB: *OS << "sb "; break; case RVOpcodes::LD: *OS << "ld "; break;
|
||||
case RVOpcodes::SD: *OS << "sd "; break; case RVOpcodes::FLW: *OS << "flw "; break;
|
||||
case RVOpcodes::FSW: *OS << "fsw "; break; case RVOpcodes::FLD: *OS << "fld "; break;
|
||||
case RVOpcodes::FSD: *OS << "fsd "; break;
|
||||
case RVOpcodes::SD: *OS << "sd "; break;
|
||||
case RVOpcodes::J: *OS << "j "; break; case RVOpcodes::JAL: *OS << "jal "; break;
|
||||
case RVOpcodes::JALR: *OS << "jalr "; break; case RVOpcodes::RET: *OS << "ret"; break;
|
||||
case RVOpcodes::BEQ: *OS << "beq "; break; case RVOpcodes::BNE: *OS << "bne "; break;
|
||||
@ -90,20 +125,7 @@ void RISCv64AsmPrinter::printInstruction(MachineInstr* instr, bool debug) {
|
||||
case RVOpcodes::LI: *OS << "li "; break; case RVOpcodes::LA: *OS << "la "; break;
|
||||
case RVOpcodes::MV: *OS << "mv "; break; case RVOpcodes::NEG: *OS << "neg "; break;
|
||||
case RVOpcodes::NEGW: *OS << "negw "; break; case RVOpcodes::SEQZ: *OS << "seqz "; break;
|
||||
case RVOpcodes::SNEZ: *OS << "snez "; break;
|
||||
case RVOpcodes::FADD_S: *OS << "fadd.s "; break;
|
||||
case RVOpcodes::FSUB_S: *OS << "fsub.s "; break;
|
||||
case RVOpcodes::FMUL_S: *OS << "fmul.s "; break;
|
||||
case RVOpcodes::FDIV_S: *OS << "fdiv.s "; break;
|
||||
case RVOpcodes::FNEG_S: *OS << "fneg.s "; break;
|
||||
case RVOpcodes::FEQ_S: *OS << "feq.s "; break;
|
||||
case RVOpcodes::FLT_S: *OS << "flt.s "; break;
|
||||
case RVOpcodes::FLE_S: *OS << "fle.s "; break;
|
||||
case RVOpcodes::FCVT_S_W: *OS << "fcvt.s.w "; break;
|
||||
case RVOpcodes::FCVT_W_S: *OS << "fcvt.w.s "; break;
|
||||
case RVOpcodes::FMV_S: *OS << "fmv.s "; break;
|
||||
case RVOpcodes::FMV_W_X: *OS << "fmv.w.x "; break;
|
||||
case RVOpcodes::FMV_X_W: *OS << "fmv.x.w "; break;
|
||||
case RVOpcodes::SNEZ: *OS << "snez "; break;
|
||||
case RVOpcodes::CALL: { // [核心修改] 为CALL指令添加特殊处理逻辑
|
||||
*OS << "call ";
|
||||
// 遍历所有操作数,只寻找并打印函数名标签
|
||||
@ -138,15 +160,6 @@ void RISCv64AsmPrinter::printInstruction(MachineInstr* instr, bool debug) {
|
||||
// It should have been eliminated by RegAlloc
|
||||
if (!debug) throw std::runtime_error("FRAME pseudo-instruction not eliminated before AsmPrinter");
|
||||
*OS << "frame_addr "; break;
|
||||
case RVOpcodes::FRAME_LOAD_F:
|
||||
if (!debug) throw std::runtime_error("FRAME_LOAD_F not eliminated before AsmPrinter");
|
||||
*OS << "frame_load_f "; break;
|
||||
case RVOpcodes::FRAME_STORE_F:
|
||||
if (!debug) throw std::runtime_error("FRAME_STORE_F not eliminated before AsmPrinter");
|
||||
*OS << "frame_store_f "; break;
|
||||
case RVOpcodes::PSEUDO_KEEPALIVE:
|
||||
if (!debug) throw std::runtime_error("PSEUDO_KEEPALIVE not eliminated before AsmPrinter");
|
||||
*OS << "keepalive "; break;
|
||||
default:
|
||||
throw std::runtime_error("Unknown opcode in AsmPrinter");
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
#include "RISCv64ISel.h"
|
||||
#include "RISCv64RegAlloc.h"
|
||||
#include "RISCv64AsmPrinter.h"
|
||||
#include "RISCv64Passes.h"
|
||||
#include "RISCv64Passes.h" // 包含优化Pass的头文件
|
||||
#include <sstream>
|
||||
|
||||
namespace sysy {
|
||||
@ -12,28 +12,11 @@ std::string RISCv64CodeGen::code_gen() {
|
||||
return module_gen();
|
||||
}
|
||||
|
||||
void printInitializer(std::stringstream& ss, const ValueCounter& init_values) {
|
||||
for (size_t i = 0; i < init_values.getValues().size(); ++i) {
|
||||
auto val = init_values.getValues()[i];
|
||||
auto count = init_values.getNumbers()[i];
|
||||
if (auto constant = dynamic_cast<ConstantValue*>(val)) {
|
||||
for (unsigned j = 0; j < count; ++j) {
|
||||
if (constant->isInt()) {
|
||||
ss << " .word " << constant->getInt() << "\n";
|
||||
} else {
|
||||
float f = constant->getFloat();
|
||||
uint32_t float_bits = *(uint32_t*)&f;
|
||||
ss << " .word " << float_bits << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 模块级代码生成
|
||||
std::string RISCv64CodeGen::module_gen() {
|
||||
std::stringstream ss;
|
||||
|
||||
// --- 步骤1:将全局变量(GlobalValue)分为.data和.bss两组 ---
|
||||
// --- [新逻辑] 步骤1:将全局变量分为.data和.bss两组 ---
|
||||
std::vector<GlobalValue*> data_globals;
|
||||
std::vector<GlobalValue*> bss_globals;
|
||||
|
||||
@ -43,6 +26,7 @@ std::string RISCv64CodeGen::module_gen() {
|
||||
|
||||
// 判断是否为大型零初始化数组,以便放入.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) {
|
||||
@ -58,40 +42,48 @@ std::string RISCv64CodeGen::module_gen() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- 步骤2:生成 .bss 段的代码 (这部分不变) ---
|
||||
// --- [新逻辑] 步骤2:生成 .bss 段的代码 ---
|
||||
if (!bss_globals.empty()) {
|
||||
ss << ".bss\n";
|
||||
ss << ".bss\n"; // 切换到 .bss 段
|
||||
for (GlobalValue* global : bss_globals) {
|
||||
// 获取数组总大小(元素个数 * 元素大小)
|
||||
// 在SysY中,我们假设元素都是4字节(int或float)
|
||||
unsigned count = global->getInitValues().getNumbers()[0];
|
||||
unsigned total_size = count * 4; // 假设元素都是4字节
|
||||
unsigned total_size = count * 4;
|
||||
|
||||
ss << " .align 3\n";
|
||||
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 段的代码 ---
|
||||
// 我们需要检查 data_globals 和 常量列表是否都为空
|
||||
if (!data_globals.empty() || !module->getConsts().empty()) {
|
||||
ss << ".data\n";
|
||||
|
||||
// a. 先处理普通的全局变量 (GlobalValue)
|
||||
// --- [旧逻辑保留] 步骤3:生成 .data 段的代码 ---
|
||||
if (!data_globals.empty()) {
|
||||
ss << ".data\n"; // 切换到 .data 段
|
||||
for (GlobalValue* global : data_globals) {
|
||||
ss << ".globl " << global->getName() << "\n";
|
||||
ss << global->getName() << ":\n";
|
||||
printInitializer(ss, global->getInitValues());
|
||||
}
|
||||
|
||||
// b. [新增] 再处理全局常量 (ConstantVariable)
|
||||
for (const auto& const_ptr : module->getConsts()) {
|
||||
ConstantVariable* cnst = const_ptr.get();
|
||||
ss << ".globl " << cnst->getName() << "\n";
|
||||
ss << cnst->getName() << ":\n";
|
||||
printInitializer(ss, cnst->getInitValues());
|
||||
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];
|
||||
if (auto constant = dynamic_cast<ConstantValue*>(val)) {
|
||||
for (unsigned j = 0; j < count; ++j) {
|
||||
if (constant->isInt()) {
|
||||
ss << " .word " << constant->getInt() << "\n";
|
||||
} else {
|
||||
float f = constant->getFloat();
|
||||
uint32_t float_bits = *(uint32_t*)&f;
|
||||
ss << " .word " << float_bits << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +99,7 @@ std::string RISCv64CodeGen::module_gen() {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// function_gen 现在是包含具体优化名称的、完整的处理流水线
|
||||
std::string RISCv64CodeGen::function_gen(Function* func) {
|
||||
// === 完整的后端处理流水线 ===
|
||||
|
||||
@ -114,7 +107,6 @@ 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);
|
||||
@ -127,7 +119,7 @@ std::string RISCv64CodeGen::function_gen(Function* func) {
|
||||
RISCv64RegAlloc reg_alloc(mfunc.get());
|
||||
reg_alloc.run();
|
||||
|
||||
// 阶段 3.1: 处理被调用者保存寄存器
|
||||
// 阶段 3.5: 处理被调用者保存寄存器
|
||||
CalleeSavedHandler callee_handler;
|
||||
callee_handler.runOnMachineFunction(mfunc.get());
|
||||
|
||||
@ -139,14 +131,6 @@ std::string RISCv64CodeGen::function_gen(Function* func) {
|
||||
PostRA_Scheduler local_scheduler;
|
||||
local_scheduler.runOnMachineFunction(mfunc.get());
|
||||
|
||||
// 阶段 3.2: 插入序言和尾声
|
||||
PrologueEpilogueInsertionPass pei_pass;
|
||||
pei_pass.runOnMachineFunction(mfunc.get());
|
||||
|
||||
// 阶段 3.3: 清理产生的大立即数
|
||||
LegalizeImmediatesPass legalizer;
|
||||
legalizer.runOnMachineFunction(mfunc.get());
|
||||
|
||||
// 阶段 6: 代码发射 (Code Emission)
|
||||
std::stringstream ss;
|
||||
RISCv64AsmPrinter printer(mfunc.get());
|
||||
@ -10,23 +10,7 @@ namespace sysy {
|
||||
|
||||
// DAG节点定义 (内部实现)
|
||||
struct RISCv64ISel::DAGNode {
|
||||
enum NodeKind {
|
||||
ARGUMENT,
|
||||
CONSTANT, // 整数或地址常量
|
||||
LOAD,
|
||||
STORE,
|
||||
BINARY, // 整数二元运算
|
||||
CALL,
|
||||
RETURN,
|
||||
BRANCH,
|
||||
ALLOCA_ADDR,
|
||||
UNARY, // 整数一元运算
|
||||
MEMSET,
|
||||
GET_ELEMENT_PTR,
|
||||
FP_CONSTANT, // 浮点常量
|
||||
FBINARY, // 浮点二元运算 (如 FADD, FSUB, FCMP)
|
||||
FUNARY, // 浮点一元运算 (如 FCVT, FNEG)
|
||||
};
|
||||
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;
|
||||
@ -45,20 +29,11 @@ unsigned RISCv64ISel::getVReg(Value* val) {
|
||||
if (vreg_counter == 0) {
|
||||
vreg_counter = 1; // vreg 0 保留
|
||||
}
|
||||
unsigned new_vreg = vreg_counter++;
|
||||
vreg_map[val] = new_vreg;
|
||||
vreg_to_value_map[new_vreg] = val;
|
||||
vreg_type_map[new_vreg] = val->getType();
|
||||
vreg_map[val] = vreg_counter++;
|
||||
}
|
||||
return vreg_map.at(val);
|
||||
}
|
||||
|
||||
unsigned RISCv64ISel::getNewVReg(Type* type) {
|
||||
unsigned new_vreg = vreg_counter++;
|
||||
vreg_type_map[new_vreg] = type; // 记录这个新vreg的类型
|
||||
return new_vreg;
|
||||
}
|
||||
|
||||
// 主入口函数
|
||||
std::unique_ptr<MachineFunction> RISCv64ISel::runOnFunction(Function* func) {
|
||||
F = func;
|
||||
@ -77,18 +52,8 @@ std::unique_ptr<MachineFunction> RISCv64ISel::runOnFunction(Function* func) {
|
||||
// 指令选择主流程
|
||||
void RISCv64ISel::select() {
|
||||
// 遍历基本块,为它们创建对应的MachineBasicBlock
|
||||
bool is_first_block = true;
|
||||
for (const auto& bb_ptr : F->getBasicBlocks()) {
|
||||
std::string mbb_name;
|
||||
if (is_first_block) {
|
||||
// 对于函数的第一个基本块,其标签必须与函数名完全相同
|
||||
mbb_name = F->getName();
|
||||
is_first_block = false;
|
||||
} else {
|
||||
// 对于后续的基本块,继续使用它们在IR中的原始名称
|
||||
mbb_name = bb_ptr->getName();
|
||||
}
|
||||
auto mbb = std::make_unique<MachineBasicBlock>(mbb_name, MFunc.get());
|
||||
auto mbb = std::make_unique<MachineBasicBlock>(bb_ptr->getName(), MFunc.get());
|
||||
bb_map[bb_ptr.get()] = mbb.get();
|
||||
MFunc->addBlock(std::move(mbb));
|
||||
}
|
||||
@ -152,48 +117,20 @@ void RISCv64ISel::selectBasicBlock(BasicBlock* bb) {
|
||||
|
||||
for (const auto& inst_ptr : bb->getInstructions()) {
|
||||
DAGNode* node_to_select = nullptr;
|
||||
auto it = value_to_node.find(inst_ptr.get());
|
||||
if (it != value_to_node.end()) {
|
||||
node_to_select = it->second;
|
||||
if (value_to_node.count(inst_ptr.get())) {
|
||||
node_to_select = value_to_node.at(inst_ptr.get());
|
||||
} else {
|
||||
for(const auto& node : dag) {
|
||||
if(node->value == inst_ptr.get()) {
|
||||
node_to_select = node.get();
|
||||
break;
|
||||
for(const auto& node : dag) {
|
||||
if(node->value == inst_ptr.get()) {
|
||||
node_to_select = node.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node_to_select) {
|
||||
if(node_to_select) {
|
||||
select_recursive(node_to_select);
|
||||
}
|
||||
}
|
||||
|
||||
if (CurMBB == MFunc->getBlocks().front().get()) { // 只对入口块操作
|
||||
auto keepalive = std::make_unique<MachineInstr>(RVOpcodes::PSEUDO_KEEPALIVE);
|
||||
for (Argument* arg : F->getArguments()) {
|
||||
keepalive->addOperand(std::make_unique<RegOperand>(getVReg(arg)));
|
||||
}
|
||||
|
||||
auto& instrs = CurMBB->getInstructions();
|
||||
auto insert_pos = instrs.end();
|
||||
|
||||
// 关键:检查基本块是否以一个“终止指令”结尾
|
||||
if (!instrs.empty()) {
|
||||
RVOpcodes last_op = instrs.back()->getOpcode();
|
||||
// 扩充了判断条件,涵盖所有可能的终止指令
|
||||
if (last_op == RVOpcodes::J || last_op == RVOpcodes::RET ||
|
||||
last_op == RVOpcodes::BEQ || last_op == RVOpcodes::BNE ||
|
||||
last_op == RVOpcodes::BLT || last_op == RVOpcodes::BGE ||
|
||||
last_op == RVOpcodes::BLTU || last_op == RVOpcodes::BGEU)
|
||||
{
|
||||
// 如果是,插入点就在这个终止指令之前
|
||||
insert_pos = std::prev(instrs.end());
|
||||
}
|
||||
}
|
||||
|
||||
// 在计算出的正确位置插入伪指令
|
||||
instrs.insert(insert_pos, std::move(keepalive));
|
||||
}
|
||||
}
|
||||
|
||||
// 核心函数:为DAG节点选择并生成MachineInstr (已修复和增强的完整版本)
|
||||
@ -214,52 +151,18 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
}
|
||||
break;
|
||||
|
||||
case DAGNode::FP_CONSTANT: {
|
||||
// RISC-V没有直接加载浮点立即数的指令
|
||||
// 标准做法是:1. 将浮点数的32位二进制表示加载到一个整数寄存器
|
||||
// 2. 使用 fmv.w.x 指令将位模式从整数寄存器移动到浮点寄存器
|
||||
auto const_val = dynamic_cast<ConstantValue*>(node->value);
|
||||
auto float_vreg = getVReg(const_val);
|
||||
auto temp_int_vreg = getNewVReg(Type::getIntType()); // 临时整数虚拟寄存器
|
||||
|
||||
float f_val = const_val->getFloat();
|
||||
// 使用 reinterpret_cast 获取浮点数的32位二进制表示
|
||||
uint32_t float_bits = *reinterpret_cast<uint32_t*>(&f_val);
|
||||
|
||||
// 1. li temp_int_vreg, float_bits
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(temp_int_vreg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(float_bits));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
|
||||
// 2. fmv.w.x float_vreg, temp_int_vreg
|
||||
auto fmv = std::make_unique<MachineInstr>(RVOpcodes::FMV_W_X);
|
||||
fmv->addOperand(std::make_unique<RegOperand>(float_vreg));
|
||||
fmv->addOperand(std::make_unique<RegOperand>(temp_int_vreg));
|
||||
CurMBB->addInstruction(std::move(fmv));
|
||||
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;
|
||||
RVOpcodes real_opcode;
|
||||
if (loaded_type->isPointer()) {
|
||||
frame_opcode = RVOpcodes::FRAME_LOAD_D;
|
||||
real_opcode = RVOpcodes::LD;
|
||||
} else if (loaded_type->isFloat()) {
|
||||
frame_opcode = RVOpcodes::FRAME_LOAD_F;
|
||||
real_opcode = RVOpcodes::FLW;
|
||||
} else { // 默认为整数
|
||||
frame_opcode = RVOpcodes::FRAME_LOAD_W;
|
||||
real_opcode = RVOpcodes::LW;
|
||||
}
|
||||
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)) {
|
||||
// 3. 创建使用新的、区分宽度的伪指令
|
||||
@ -270,7 +173,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_val)) {
|
||||
// 对于全局变量,先用 la 加载其地址
|
||||
auto addr_vreg = getNewVReg(Type::getPointerType(global->getType()));
|
||||
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()));
|
||||
@ -307,51 +210,20 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
|
||||
// 如果要存储的值是一个常量,就在这里生成 `li` 指令加载它
|
||||
if (auto val_const = dynamic_cast<ConstantValue*>(val_to_store)) {
|
||||
// 区分整数常量和浮点常量
|
||||
if (val_const->isInt()) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(val_const)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(val_const->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
} else if (val_const->isFloat()) {
|
||||
// 先将浮点数的位模式加载到整数vreg,再用fmv.w.x移到浮点vreg
|
||||
auto temp_int_vreg = getNewVReg(Type::getIntType());
|
||||
auto float_vreg = getVReg(val_const);
|
||||
|
||||
float f_val = val_const->getFloat();
|
||||
uint32_t float_bits = *reinterpret_cast<uint32_t*>(&f_val);
|
||||
|
||||
// 1. li temp_int_vreg, float_bits
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(temp_int_vreg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(float_bits));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
|
||||
// 2. fmv.w.x float_vreg, temp_int_vreg
|
||||
auto fmv = std::make_unique<MachineInstr>(RVOpcodes::FMV_W_X);
|
||||
fmv->addOperand(std::make_unique<RegOperand>(float_vreg));
|
||||
fmv->addOperand(std::make_unique<RegOperand>(temp_int_vreg));
|
||||
CurMBB->addInstruction(std::move(fmv));
|
||||
}
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(getVReg(val_const)));
|
||||
li->addOperand(std::make_unique<ImmOperand>(val_const->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
auto val_vreg = getVReg(val_to_store);
|
||||
|
||||
// --- 修改点 ---
|
||||
// 1. 获取被存储的值的类型
|
||||
Type* stored_type = val_to_store->getType();
|
||||
|
||||
// 2. 根据类型选择正确的伪指令或真实指令操作码
|
||||
RVOpcodes frame_opcode;
|
||||
RVOpcodes real_opcode;
|
||||
if (stored_type->isPointer()) {
|
||||
frame_opcode = RVOpcodes::FRAME_STORE_D;
|
||||
real_opcode = RVOpcodes::SD;
|
||||
} else if (stored_type->isFloat()) {
|
||||
frame_opcode = RVOpcodes::FRAME_STORE_F;
|
||||
real_opcode = RVOpcodes::FSW;
|
||||
} else { // 默认为整数
|
||||
frame_opcode = RVOpcodes::FRAME_STORE_W;
|
||||
real_opcode = RVOpcodes::SW;
|
||||
}
|
||||
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)) {
|
||||
// 3. 创建使用新的、区分宽度的伪指令
|
||||
@ -362,7 +234,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
|
||||
} else if (auto global = dynamic_cast<GlobalValue*>(ptr_val)) {
|
||||
// 向全局变量存储
|
||||
auto addr_vreg = getNewVReg(Type::getIntType());
|
||||
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()));
|
||||
@ -422,7 +294,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
}
|
||||
|
||||
// 2. [修改] 根据基地址的类型,生成不同的指令来获取基地址
|
||||
auto base_addr_vreg = getNewVReg(Type::getIntType()); // 创建一个新的临时vreg来存放基地址
|
||||
auto base_addr_vreg = getNewVReg(); // 创建一个新的临时vreg来存放基地址
|
||||
|
||||
// 情况一:基地址是局部栈变量
|
||||
if (auto alloca_base = dynamic_cast<AllocaInst*>(base)) {
|
||||
@ -615,109 +487,6 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::FBINARY: {
|
||||
auto bin = dynamic_cast<BinaryInst*>(node->value);
|
||||
auto dest_vreg = getVReg(bin);
|
||||
auto lhs_vreg = getVReg(bin->getLhs());
|
||||
auto rhs_vreg = getVReg(bin->getRhs());
|
||||
|
||||
switch (bin->getKind()) {
|
||||
case Instruction::kFAdd: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FADD_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFSub: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FSUB_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFMul: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FMUL_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFDiv: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FDIV_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
|
||||
// --- 浮点比较指令 ---
|
||||
// 注意:比较结果(0或1)写入的是一个通用整数寄存器(dest_vreg)
|
||||
case Instruction::kFCmpEQ: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FEQ_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFCmpLT: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FLT_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFCmpLE: {
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FLE_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
// --- 通过交换操作数或组合指令实现其余比较 ---
|
||||
case Instruction::kFCmpGT: { // a > b 等价于 b < a
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FLT_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg)); // 操作数交换
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFCmpGE: { // a >= b 等价于 b <= a
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FLE_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(rhs_vreg)); // 操作数交换
|
||||
instr->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFCmpNE: { // a != b 等价于 !(a == b)
|
||||
// 1. 先用 feq.s 比较,结果存入 dest_vreg
|
||||
auto feq = std::make_unique<MachineInstr>(RVOpcodes::FEQ_S);
|
||||
feq->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
feq->addOperand(std::make_unique<RegOperand>(lhs_vreg));
|
||||
feq->addOperand(std::make_unique<RegOperand>(rhs_vreg));
|
||||
CurMBB->addInstruction(std::move(feq));
|
||||
// 2. 再用 seqz 对结果取反 (如果相等(1),则变0;如果不等(0),则变1)
|
||||
auto seqz = std::make_unique<MachineInstr>(RVOpcodes::SEQZ);
|
||||
seqz->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
seqz->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
CurMBB->addInstruction(std::move(seqz));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unsupported float binary instruction in ISel");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::UNARY: {
|
||||
auto unary = dynamic_cast<UnaryInst*>(node->value);
|
||||
auto dest_vreg = getVReg(unary);
|
||||
@ -745,245 +514,109 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::FUNARY: {
|
||||
auto unary = dynamic_cast<UnaryInst*>(node->value);
|
||||
auto dest_vreg = getVReg(unary);
|
||||
auto src_vreg = getVReg(unary->getOperand());
|
||||
|
||||
switch (unary->getKind()) {
|
||||
case Instruction::kItoF: { // 整数 to 浮点
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FCVT_S_W);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg)); // 目标是浮点vreg
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg)); // 源是整数vreg
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFtoI: { // 浮点 to 整数
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FCVT_W_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg)); // 目标是整数vreg
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg)); // 源是浮点vreg
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kFNeg: { // 浮点取负
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FNEG_S);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
// --- 处理位传送指令 ---
|
||||
case Instruction::kBitItoF: { // 整数位模式 -> 浮点寄存器
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FMV_W_X);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg)); // 目标是浮点vreg
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg)); // 源是整数vreg
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
case Instruction::kBitFtoI: { // 浮点位模式 -> 整数寄存器
|
||||
auto instr = std::make_unique<MachineInstr>(RVOpcodes::FMV_X_W);
|
||||
instr->addOperand(std::make_unique<RegOperand>(dest_vreg)); // 目标是整数vreg
|
||||
instr->addOperand(std::make_unique<RegOperand>(src_vreg)); // 源是浮点vreg
|
||||
CurMBB->addInstruction(std::move(instr));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Unsupported float unary instruction in ISel");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DAGNode::CALL: {
|
||||
auto call = dynamic_cast<CallInst*>(node->value);
|
||||
// 处理函数参数,放入a0-a7物理寄存器
|
||||
size_t num_operands = node->operands.size();
|
||||
|
||||
// --- 步骤 1: 分配寄存器参数和栈参数 ---
|
||||
// 根据RISC-V调用约定,前8个整数/指针参数通过a0-a7传递,
|
||||
// 前8个浮点参数通过fa0-fa7传递 (物理寄存器 f10-f17)。其余参数通过栈传递。
|
||||
|
||||
int int_reg_idx = 0; // a0-a7 的索引
|
||||
int fp_reg_idx = 0; // fa0-fa7 的索引
|
||||
|
||||
// 用于存储需要通过栈传递的参数
|
||||
std::vector<DAGNode*> stack_args;
|
||||
|
||||
for (size_t i = 0; i < num_operands; ++i) {
|
||||
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];
|
||||
Value* arg_val = arg_node->value;
|
||||
Type* arg_type = arg_val->getType();
|
||||
auto arg_preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + i);
|
||||
|
||||
// 判断参数是浮点类型还是整型/指针类型
|
||||
if (arg_type->isFloat()) {
|
||||
if (fp_reg_idx < 8) {
|
||||
// --- 处理浮点寄存器参数 (fa0-fa7, 对应物理寄存器 F10-F17) ---
|
||||
auto arg_preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::F10) + fp_reg_idx);
|
||||
fp_reg_idx++;
|
||||
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(arg_val)) {
|
||||
// 如果是浮点常量,需要先物化
|
||||
// 1. 获取其32位二进制表示
|
||||
float f_val = const_val->getFloat();
|
||||
uint32_t float_bits = *reinterpret_cast<uint32_t*>(&f_val);
|
||||
// 2. 将位模式加载到一个临时整数寄存器 (使用t0)
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(PhysicalReg::T0));
|
||||
li->addOperand(std::make_unique<ImmOperand>(float_bits));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
// 3. 使用fmv.w.x将位模式从整数寄存器移动到目标浮点参数寄存器
|
||||
auto fmv_wx = std::make_unique<MachineInstr>(RVOpcodes::FMV_W_X);
|
||||
fmv_wx->addOperand(std::make_unique<RegOperand>(arg_preg));
|
||||
fmv_wx->addOperand(std::make_unique<RegOperand>(PhysicalReg::T0));
|
||||
CurMBB->addInstruction(std::move(fmv_wx));
|
||||
} else {
|
||||
// 如果已经是虚拟寄存器,直接用 fmv.s 移动
|
||||
auto src_vreg = getVReg(arg_val);
|
||||
auto fmv_s = std::make_unique<MachineInstr>(RVOpcodes::FMV_S);
|
||||
fmv_s->addOperand(std::make_unique<RegOperand>(arg_preg));
|
||||
fmv_s->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
CurMBB->addInstruction(std::move(fmv_s));
|
||||
}
|
||||
} else {
|
||||
// 浮点寄存器已用完,放到栈上传递
|
||||
stack_args.push_back(arg_node);
|
||||
}
|
||||
} else { // 整数或指针参数
|
||||
if (int_reg_idx < 8) {
|
||||
// --- 处理整数/指针寄存器参数 (a0-a7) ---
|
||||
auto arg_preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + int_reg_idx);
|
||||
int_reg_idx++;
|
||||
|
||||
if (arg_node->kind == DAGNode::CONSTANT) {
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(arg_val)) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(arg_preg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
} else {
|
||||
auto src_vreg = getVReg(arg_val);
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(arg_preg));
|
||||
mv->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
}
|
||||
} else {
|
||||
// 整数寄存器已用完,放到栈上传递
|
||||
stack_args.push_back(arg_node);
|
||||
if (arg_node->kind == DAGNode::CONSTANT) {
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(arg_node->value)) {
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(arg_preg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
}
|
||||
} else {
|
||||
auto src_vreg = getVReg(arg_node->value);
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(arg_preg));
|
||||
mv->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
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字节
|
||||
|
||||
// --- 步骤 2: 处理所有栈参数 ---
|
||||
int stack_space = 0;
|
||||
if (!stack_args.empty()) {
|
||||
// 计算栈参数所需的总空间,RV64中每个槽位为8字节
|
||||
stack_space = stack_args.size() * 8;
|
||||
// 根据ABI,为call分配的栈空间需要16字节对齐
|
||||
if (stack_space % 16 != 0) {
|
||||
stack_space += 16 - (stack_space % 16);
|
||||
}
|
||||
|
||||
// 在栈上分配空间
|
||||
if (stack_space > 0) {
|
||||
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));
|
||||
}
|
||||
|
||||
// 将每个参数存储到栈上对应的位置
|
||||
for (size_t i = 0; i < stack_args.size(); ++i) {
|
||||
DAGNode* arg_node = stack_args[i];
|
||||
Value* arg_val = arg_node->value;
|
||||
Type* arg_type = arg_val->getType();
|
||||
int offset = i * 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;
|
||||
// 如果是常量,先加载到临时vreg
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(arg_val)) {
|
||||
src_vreg = getNewVReg(arg_type);
|
||||
if(arg_type->isFloat()) {
|
||||
auto temp_int_vreg = getNewVReg(Type::getIntType());
|
||||
float f_val = const_val->getFloat();
|
||||
uint32_t float_bits = *reinterpret_cast<uint32_t*>(&f_val);
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(temp_int_vreg));
|
||||
li->addOperand(std::make_unique<ImmOperand>(float_bits));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
auto fmv_wx = std::make_unique<MachineInstr>(RVOpcodes::FMV_W_X);
|
||||
fmv_wx->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
fmv_wx->addOperand(std::make_unique<RegOperand>(temp_int_vreg));
|
||||
CurMBB->addInstruction(std::move(fmv_wx));
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
|
||||
// 准备源寄存器
|
||||
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_val);
|
||||
src_vreg = getVReg(arg_node->value);
|
||||
}
|
||||
|
||||
// 根据类型选择 fsw (浮点) 或 sd (整型/指针) 存储指令
|
||||
std::unique_ptr<MachineInstr> store_instr;
|
||||
if (arg_type->isFloat()) {
|
||||
store_instr = std::make_unique<MachineInstr>(RVOpcodes::FSW);
|
||||
} else {
|
||||
store_instr = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
}
|
||||
store_instr->addOperand(std::make_unique<RegOperand>(src_vreg));
|
||||
store_instr->addOperand(std::make_unique<MemOperand>(
|
||||
// 计算在栈上的偏移量
|
||||
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(store_instr));
|
||||
CurMBB->addInstruction(std::move(sd_instr));
|
||||
}
|
||||
}
|
||||
|
||||
// --- 步骤 3: 生成CALL指令 ---
|
||||
|
||||
auto call_instr = std::make_unique<MachineInstr>(RVOpcodes::CALL);
|
||||
// [协议] 如果函数有返回值,将它的目标虚拟寄存器作为第一个操作数
|
||||
if (!call->getType()->isVoid()) {
|
||||
unsigned dest_vreg = getVReg(call);
|
||||
call_instr->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
}
|
||||
|
||||
// 将函数名标签作为后续操作数
|
||||
call_instr->addOperand(std::make_unique<LabelOperand>(call->getCallee()->getName()));
|
||||
|
||||
// 将所有参数的虚拟寄存器也作为后续操作数,供getInstrUseDef分析
|
||||
for (size_t i = 0; i < num_operands; ++i) {
|
||||
if (node->operands[i]->kind != DAGNode::CONSTANT && node->operands[i]->kind != DAGNode::FP_CONSTANT) {
|
||||
if (node->operands[i]->kind != DAGNode::CONSTANT) { // 常量参数已直接加载,无需作为use
|
||||
call_instr->addOperand(std::make_unique<RegOperand>(getVReg(node->operands[i]->value)));
|
||||
}
|
||||
}
|
||||
CurMBB->addInstruction(std::move(call_instr));
|
||||
|
||||
// --- 步骤 4: 处理返回值 ---
|
||||
if (!call->getType()->isVoid()) {
|
||||
unsigned dest_vreg = getVReg(call);
|
||||
if (call->getType()->isFloat()) {
|
||||
// 浮点返回值在 fa0 (物理寄存器 F10)
|
||||
auto fmv_s = std::make_unique<MachineInstr>(RVOpcodes::FMV_S);
|
||||
fmv_s->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
fmv_s->addOperand(std::make_unique<RegOperand>(PhysicalReg::F10)); // fa0
|
||||
CurMBB->addInstruction(std::move(fmv_s));
|
||||
} else {
|
||||
// 整数/指针返回值在 a0
|
||||
auto mv = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv->addOperand(std::make_unique<RegOperand>(dest_vreg));
|
||||
mv->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
}
|
||||
}
|
||||
CurMBB->addInstruction(std::move(call_instr));
|
||||
|
||||
// --- 步骤 5: 回收为栈参数分配的空间 ---
|
||||
if (stack_space > 0) {
|
||||
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)));
|
||||
// mv_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
|
||||
// CurMBB->addInstruction(std::move(mv_instr));
|
||||
// }
|
||||
break;
|
||||
}
|
||||
|
||||
@ -991,47 +624,17 @@ 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();
|
||||
Type* ret_type = ret_val->getType();
|
||||
|
||||
if (ret_type->isFloat()) {
|
||||
// --- 处理浮点返回值 ---
|
||||
// 返回值需要被放入 fa0 (物理寄存器 F10)
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(ret_val)) {
|
||||
// 如果是浮点常量,需要先物化到fa0
|
||||
float f_val = const_val->getFloat();
|
||||
uint32_t float_bits = *reinterpret_cast<uint32_t*>(&f_val);
|
||||
// 1. 加载位模式到临时整数寄存器 (t0)
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(PhysicalReg::T0));
|
||||
li->addOperand(std::make_unique<ImmOperand>(float_bits));
|
||||
CurMBB->addInstruction(std::move(li));
|
||||
// 2. 将位模式从 t0 移动到 fa0
|
||||
auto fmv_wx = std::make_unique<MachineInstr>(RVOpcodes::FMV_W_X);
|
||||
fmv_wx->addOperand(std::make_unique<RegOperand>(PhysicalReg::F10)); // fa0
|
||||
fmv_wx->addOperand(std::make_unique<RegOperand>(PhysicalReg::T0));
|
||||
CurMBB->addInstruction(std::move(fmv_wx));
|
||||
} else {
|
||||
// 如果是vreg,直接用 fmv.s 移动到 fa0
|
||||
auto fmv_s = std::make_unique<MachineInstr>(RVOpcodes::FMV_S);
|
||||
fmv_s->addOperand(std::make_unique<RegOperand>(PhysicalReg::F10)); // fa0
|
||||
fmv_s->addOperand(std::make_unique<RegOperand>(getVReg(ret_val)));
|
||||
CurMBB->addInstruction(std::move(fmv_s));
|
||||
}
|
||||
// [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));
|
||||
li_instr->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li_instr));
|
||||
} else {
|
||||
// --- 处理整数/指针返回值 ---
|
||||
// 返回值需要被放入 a0
|
||||
// [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));
|
||||
li_instr->addOperand(std::make_unique<ImmOperand>(const_val->getInt()));
|
||||
CurMBB->addInstruction(std::move(li_instr));
|
||||
} else {
|
||||
auto mv_instr = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
|
||||
mv_instr->addOperand(std::make_unique<RegOperand>(getVReg(ret_val)));
|
||||
CurMBB->addInstruction(std::move(mv_instr));
|
||||
}
|
||||
auto mv_instr = std::make_unique<MachineInstr>(RVOpcodes::MV);
|
||||
mv_instr->addOperand(std::make_unique<RegOperand>(PhysicalReg::A0));
|
||||
mv_instr->addOperand(std::make_unique<RegOperand>(getVReg(ret_val)));
|
||||
CurMBB->addInstruction(std::move(mv_instr));
|
||||
}
|
||||
}
|
||||
// [V1设计保留] 函数尾声(epilogue)不由RETURN节点生成,
|
||||
@ -1249,11 +852,6 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
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 if (auto const_global_base = dynamic_cast<ConstantVariable*>(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>(const_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);
|
||||
@ -1262,7 +860,7 @@ void RISCv64ISel::selectNode(DAGNode* node) {
|
||||
CurMBB->addInstruction(std::move(mv));
|
||||
}
|
||||
|
||||
// --- Step 2: 遵循LLVM GEP语义迭代计算地址 ---
|
||||
// --- Step 2: [最终权威版] 遵循LLVM GEP语义迭代计算地址 ---
|
||||
|
||||
// 初始被索引的类型,是基指针指向的那个类型 (例如, [2 x i32])
|
||||
Type* current_type = gep->getBasePointer()->getType()->as<PointerType>()->getBaseType();
|
||||
@ -1359,54 +957,25 @@ RISCv64ISel::DAGNode* RISCv64ISel::create_node(int kind_int, Value* val, std::ma
|
||||
return raw_node_ptr;
|
||||
}
|
||||
|
||||
RISCv64ISel::DAGNode* RISCv64ISel::get_operand_node(
|
||||
Value* val_ir,
|
||||
std::map<Value*, DAGNode*>& value_to_node,
|
||||
std::vector<std::unique_ptr<DAGNode>>& nodes_storage
|
||||
) {
|
||||
// 空指针错误处理
|
||||
if (val_ir == nullptr) {
|
||||
throw std::runtime_error("get_operand_node received a null Value.");
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
if (auto const_val = dynamic_cast<ConstantValue*>(val_ir)) {
|
||||
if (const_val->isInt()) {
|
||||
return create_node(DAGNode::CONSTANT, val_ir, value_to_node, nodes_storage);
|
||||
} else {
|
||||
// 为浮点常量创建新的FP_CONSTANT节点
|
||||
return create_node(DAGNode::FP_CONSTANT, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
}
|
||||
if (dynamic_cast<GlobalValue*>(val_ir)) {
|
||||
} else if (dynamic_cast<ConstantValue*>(val_ir)) {
|
||||
return create_node(DAGNode::CONSTANT, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
if (dynamic_cast<AllocaInst*>(val_ir)) {
|
||||
} else if (dynamic_cast<GlobalValue*>(val_ir)) {
|
||||
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);
|
||||
}
|
||||
if (dynamic_cast<Argument*>(val_ir)) {
|
||||
} else if (dynamic_cast<Argument*>(val_ir)) {
|
||||
// Argument 是一个叶子节点,它代表一个在函数入口就可用的值。
|
||||
return create_node(DAGNode::ARGUMENT, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
if (dynamic_cast<ConstantVariable*>(val_ir)) {
|
||||
// 全局常量数组和全局变量类似,在指令选择层面都表现为一个常量地址
|
||||
// 因此也为它创建一个 CONSTANT 类型的节点
|
||||
return create_node(DAGNode::CONSTANT, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
// 如果代码执行到这里,意味着val_ir不是上述任何一种叶子节点,
|
||||
// 且它也不是在当前基本块中定义的(否则它会在value_to_node中被找到)。
|
||||
// 这说明它是一个来自前驱块的Live-In值。
|
||||
|
||||
// 我们将其视为一个与函数参数(Argument)类似的“块输入值”,并为它创建一个ARGUMENT节点。
|
||||
// 这样,后续的指令选择逻辑就知道这个值是直接可用的,无需在当前块内计算。
|
||||
if (dynamic_cast<Instruction*>(val_ir)) {
|
||||
return create_node(DAGNode::ARGUMENT, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
|
||||
// 如果一个Value不是任何已知类型,也不是指令,那说明出现了未处理的情况,抛出异常。
|
||||
throw std::runtime_error("Unhandled Value type in get_operand_node for value named: " + val_ir->getName());
|
||||
// 默认行为:如果一个操作数不是上面任何一种叶子节点,
|
||||
// 并且没有在value_to_node中找到(意味着它不是由另一条指令定义的),
|
||||
// 这是一个逻辑问题。但为了保持向前兼容,我们暂时保留旧的LOAD行为,
|
||||
// 尽管在修复Argument后,它不应该再被错误触发。
|
||||
return create_node(DAGNode::LOAD, val_ir, value_to_node, nodes_storage);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<RISCv64ISel::DAGNode>> RISCv64ISel::build_dag(BasicBlock* bb) {
|
||||
@ -1453,17 +1022,6 @@ std::vector<std::unique_ptr<RISCv64ISel::DAGNode>> RISCv64ISel::build_dag(BasicB
|
||||
load_node->operands.push_back(get_operand_node(load->getPointer(), value_to_node, nodes_storage));
|
||||
} else if (auto bin = dynamic_cast<BinaryInst*>(inst)) {
|
||||
if(value_to_node.count(bin)) continue;
|
||||
if (bin->getKind() == Instruction::kFSub) {
|
||||
if (auto const_lhs = dynamic_cast<ConstantValue*>(bin->getLhs())) {
|
||||
// 使用isZero()来判断浮点数0.0,比直接比较更健壮
|
||||
if (const_lhs->isZero()) {
|
||||
// 这是一个浮点取负操作,创建 FUNARY 节点
|
||||
auto funary_node = create_node(DAGNode::FUNARY, bin, value_to_node, nodes_storage);
|
||||
funary_node->operands.push_back(get_operand_node(bin->getRhs(), value_to_node, nodes_storage));
|
||||
continue; // 处理完毕,跳到下一条指令
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bin->getKind() == BinaryInst::kSub) {
|
||||
if (auto const_lhs = dynamic_cast<ConstantValue*>(bin->getLhs())) {
|
||||
if (const_lhs->getInt() == 0) {
|
||||
@ -1473,24 +1031,13 @@ std::vector<std::unique_ptr<RISCv64ISel::DAGNode>> RISCv64ISel::build_dag(BasicB
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bin->getKind() >= Instruction::kFAdd) { // 假设浮点指令枚举值更大
|
||||
auto fbin_node = create_node(DAGNode::FBINARY, bin, value_to_node, nodes_storage);
|
||||
fbin_node->operands.push_back(get_operand_node(bin->getLhs(), value_to_node, nodes_storage));
|
||||
fbin_node->operands.push_back(get_operand_node(bin->getRhs(), value_to_node, nodes_storage));
|
||||
} else {
|
||||
auto bin_node = create_node(DAGNode::BINARY, bin, value_to_node, nodes_storage);
|
||||
bin_node->operands.push_back(get_operand_node(bin->getLhs(), value_to_node, nodes_storage));
|
||||
bin_node->operands.push_back(get_operand_node(bin->getRhs(), value_to_node, nodes_storage));
|
||||
}
|
||||
auto bin_node = create_node(DAGNode::BINARY, bin, value_to_node, nodes_storage);
|
||||
bin_node->operands.push_back(get_operand_node(bin->getLhs(), value_to_node, nodes_storage));
|
||||
bin_node->operands.push_back(get_operand_node(bin->getRhs(), value_to_node, nodes_storage));
|
||||
} else if (auto un = dynamic_cast<UnaryInst*>(inst)) {
|
||||
if(value_to_node.count(un)) continue;
|
||||
if (un->getKind() >= Instruction::kFNeg) {
|
||||
auto funary_node = create_node(DAGNode::FUNARY, un, value_to_node, nodes_storage);
|
||||
funary_node->operands.push_back(get_operand_node(un->getOperand(), value_to_node, nodes_storage));
|
||||
} else {
|
||||
auto unary_node = create_node(DAGNode::UNARY, un, value_to_node, nodes_storage);
|
||||
unary_node->operands.push_back(get_operand_node(un->getOperand(), value_to_node, nodes_storage));
|
||||
}
|
||||
auto unary_node = create_node(DAGNode::UNARY, un, value_to_node, nodes_storage);
|
||||
unary_node->operands.push_back(get_operand_node(un->getOperand(), value_to_node, nodes_storage));
|
||||
} else if (auto call = dynamic_cast<CallInst*>(inst)) {
|
||||
if(value_to_node.count(call)) continue;
|
||||
auto call_node = create_node(DAGNode::CALL, call, value_to_node, nodes_storage);
|
||||
@ -1578,7 +1125,6 @@ void RISCv64ISel::print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, co
|
||||
case DAGNode::ALLOCA_ADDR: return "ALLOCA_ADDR";
|
||||
case DAGNode::UNARY: return "UNARY";
|
||||
case DAGNode::MEMSET: return "MEMSET";
|
||||
case DAGNode::GET_ELEMENT_PTR: return "GET_ELEMENT_PTR";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
#include "Peephole.h"
|
||||
#include "RISCv64Peephole.h"
|
||||
#include <functional>
|
||||
|
||||
namespace sysy {
|
||||
@ -1,7 +1,6 @@
|
||||
#include "RISCv64RegAlloc.h"
|
||||
#include "RISCv64ISel.h"
|
||||
#include "RISCv64AsmPrinter.h" // For DEBUG output
|
||||
#include "LegalizeImmediates.h"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <iostream> // For DEBUG output
|
||||
@ -10,10 +9,9 @@
|
||||
namespace sysy {
|
||||
|
||||
RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) {
|
||||
// 1. 初始化可分配的整数寄存器池
|
||||
allocable_int_regs = {
|
||||
PhysicalReg::T0, PhysicalReg::T1, PhysicalReg::T2, PhysicalReg::T3,
|
||||
PhysicalReg::T4, /*PhysicalReg::T5,*/ PhysicalReg::T6, // T5是大立即数传送寄存器
|
||||
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,
|
||||
@ -21,45 +19,19 @@ RISCv64RegAlloc::RISCv64RegAlloc(MachineFunction* mfunc) : MFunc(mfunc) {
|
||||
PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11,
|
||||
};
|
||||
|
||||
// 2. 初始化可分配的浮点寄存器池
|
||||
allocable_fp_regs = {
|
||||
// 浮点临时寄存器 ft0-ft11
|
||||
PhysicalReg::F0, PhysicalReg::F1, PhysicalReg::F2, PhysicalReg::F3,
|
||||
PhysicalReg::F4, PhysicalReg::F5, PhysicalReg::F6, PhysicalReg::F7,
|
||||
PhysicalReg::F28, PhysicalReg::F29, PhysicalReg::F30, PhysicalReg::F31,
|
||||
// 浮点参数/返回值寄存器 fa0-fa7
|
||||
PhysicalReg::F10, PhysicalReg::F11, PhysicalReg::F12, PhysicalReg::F13,
|
||||
PhysicalReg::F14, PhysicalReg::F15, PhysicalReg::F16, PhysicalReg::F17,
|
||||
// 浮点保存寄存器 fs0-fs11
|
||||
PhysicalReg::F8, PhysicalReg::F9,
|
||||
PhysicalReg::F18, PhysicalReg::F19, PhysicalReg::F20, PhysicalReg::F21,
|
||||
PhysicalReg::F22, PhysicalReg::F23, PhysicalReg::F24, PhysicalReg::F25,
|
||||
PhysicalReg::F26, PhysicalReg::F27
|
||||
};
|
||||
|
||||
// 3. 映射所有物理寄存器(包括整数、浮点和特殊寄存器)到特殊的虚拟寄存器ID
|
||||
// 这是为了让活跃性分析和干扰图构建能够统一处理所有类型的寄存器
|
||||
for (int i = 0; i < static_cast<int>(PhysicalReg::PHYS_REG_START_ID); ++i) {
|
||||
auto preg = static_cast<PhysicalReg>(i);
|
||||
preg_to_vreg_id_map[preg] = static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID) + i;
|
||||
// 映射物理寄存器到特殊的虚拟寄存器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() {
|
||||
// --- 在所有流程开始前,构建完整的vreg到Value的反向映射 ---
|
||||
const auto& vreg_map_from_isel = MFunc->getISel()->getVRegMap();
|
||||
for (const auto& pair : vreg_map_from_isel) {
|
||||
Value* val = pair.first;
|
||||
unsigned vreg = pair.second;
|
||||
vreg_to_value_map[vreg] = val;
|
||||
}
|
||||
// 阶段 1: 处理函数调用约定(参数寄存器预着色)
|
||||
handleCallingConvention();
|
||||
// 阶段 2: 消除帧索引(为局部变量和栈参数分配栈偏移)
|
||||
eliminateFrameIndices();
|
||||
|
||||
// 调试输出当前的LLIR状态
|
||||
{ // 使用大括号创建一个局部作用域,避免printer变量泄露
|
||||
if (DEBUG) {
|
||||
std::cerr << "\n===== LLIR after eliminateFrameIndices for function: "
|
||||
@ -74,7 +46,6 @@ void RISCv64RegAlloc::run() {
|
||||
std::cerr << "===== End of LLIR =====\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 阶段 3: 活跃性分析
|
||||
analyzeLiveness();
|
||||
// 阶段 4: 构建干扰图(包含CALL指令对调用者保存寄存器的影响)
|
||||
@ -82,10 +53,7 @@ void RISCv64RegAlloc::run() {
|
||||
// 阶段 5: 图着色算法分配物理寄存器
|
||||
colorGraph();
|
||||
// 阶段 6: 重写函数(插入溢出/填充代码,替换虚拟寄存器为物理寄存器)
|
||||
rewriteFunction();
|
||||
|
||||
// 将最终的寄存器分配结果保存到MachineFunction的帧信息中,供后续Pass使用
|
||||
MFunc->getFrameInfo().vreg_to_preg_map = this->color_map;
|
||||
rewriteFunction();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,38 +67,35 @@ void RISCv64RegAlloc::handleCallingConvention() {
|
||||
RISCv64ISel* isel = MFunc->getISel();
|
||||
|
||||
// --- 部分1:处理函数传入参数的预着色 ---
|
||||
// 获取函数的Argument对象列表
|
||||
if (F) {
|
||||
auto& args = F->getArguments();
|
||||
|
||||
// [修改] 为整数参数和浮点参数分别维护索引
|
||||
int int_arg_idx = 0;
|
||||
int float_arg_idx = 0;
|
||||
|
||||
// RISC-V RV64G调用约定:前8个整型/指针参数通过 a0-a7 传递
|
||||
int arg_idx = 0;
|
||||
// 遍历 Argument* 列表
|
||||
for (Argument* arg : args) {
|
||||
// [修改] 根据参数类型决定使用哪个寄存器池和索引
|
||||
if (arg->getType()->isFloat()) {
|
||||
// --- 处理浮点参数 ---
|
||||
if (float_arg_idx >= 8) continue; // fa0-fa7
|
||||
|
||||
unsigned vreg = isel->getVReg(arg);
|
||||
// 浮点参数使用 fa10-fa17 (在RISC-V ABI中对应F10-F17)
|
||||
auto preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::F10) + float_arg_idx);
|
||||
color_map[vreg] = preg;
|
||||
float_arg_idx++;
|
||||
|
||||
} else {
|
||||
// --- 处理整数/指针参数 (原有逻辑) ---
|
||||
if (int_arg_idx >= 8) continue; // a0-a7
|
||||
|
||||
unsigned vreg = isel->getVReg(arg);
|
||||
auto preg = static_cast<PhysicalReg>(static_cast<int>(PhysicalReg::A0) + int_arg_idx);
|
||||
color_map[vreg] = preg;
|
||||
int_arg_idx++;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 部分2:为CALL指令的返回值预着色 ---
|
||||
// // --- 部分2:[新逻辑] 遍历所有指令,为CALL指令的返回值预着色为 a0 ---
|
||||
// // 这是为了强制寄存器分配器知道,call的结果物理上出现在a0寄存器。
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
for (auto& instr : mbb->getInstructions()) {
|
||||
if (instr->getOpcode() == RVOpcodes::CALL) {
|
||||
@ -141,17 +106,11 @@ void RISCv64RegAlloc::handleCallingConvention() {
|
||||
auto reg_op = static_cast<RegOperand*>(instr->getOperands().front().get());
|
||||
if (reg_op->isVirtual()) {
|
||||
unsigned ret_vreg = reg_op->getVRegNum();
|
||||
|
||||
// [修改] 检查返回值的类型,预着色到 a0 或 fa0
|
||||
assert(MFunc->getISel()->getVRegValueMap().count(ret_vreg) && "Return vreg not found in value map!");
|
||||
Value* ret_val = MFunc->getISel()->getVRegValueMap().at(ret_vreg);
|
||||
|
||||
if (ret_val->getType()->isFloat()) {
|
||||
// 浮点返回值预着色到 fa0 (F10)
|
||||
color_map[ret_vreg] = PhysicalReg::F10;
|
||||
} else {
|
||||
// 整数/指针返回值预着色到 a0
|
||||
color_map[ret_vreg] = PhysicalReg::A0;
|
||||
// 强制将这个虚拟寄存器预着色为 a0
|
||||
color_map[ret_vreg] = PhysicalReg::A0;
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] Pre-coloring vreg" << ret_vreg
|
||||
<< " to a0 for CALL instruction." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,6 +124,10 @@ void RISCv64RegAlloc::handleCallingConvention() {
|
||||
*/
|
||||
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();
|
||||
|
||||
@ -186,15 +149,12 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
}
|
||||
}
|
||||
|
||||
// [关键修改] 为局部变量分配空间时,起始点必须考虑为ra, s0以及所有callee-saved寄存器预留的空间。
|
||||
// 布局顺序为: [s0/ra, 16字节] -> [callee-saved, callee_saved_size字节] -> [局部变量...]
|
||||
int local_var_offset = 16 + frame_info.callee_saved_size;
|
||||
int locals_start_offset = local_var_offset; // 记录局部变量区域的起始点,用于计算总大小
|
||||
|
||||
// 处理局部变量 (AllocaInst)
|
||||
// 处理局部变量
|
||||
// 遍历AllocaInst来计算局部变量所需的总空间
|
||||
for (auto& bb : F->getBasicBlocks()) {
|
||||
for (auto& inst : bb->getInstructions()) {
|
||||
if (auto alloca = dynamic_cast<AllocaInst*>(inst.get())) {
|
||||
// 获取Alloca指令指向的类型 (例如 alloca i32* 中,获取 i32)
|
||||
Type* allocated_type = alloca->getType()->as<PointerType>()->getBaseType();
|
||||
int size = getTypeSizeInBytes(allocated_type);
|
||||
|
||||
@ -202,17 +162,14 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
size = (size + 7) & ~7;
|
||||
if (size == 0) size = 8; // 至少分配8字节
|
||||
|
||||
local_var_offset += size;
|
||||
current_offset += size;
|
||||
unsigned alloca_vreg = isel->getVReg(alloca);
|
||||
// 局部变量使用相对于s0的负向偏移
|
||||
frame_info.alloca_offsets[alloca_vreg] = -local_var_offset;
|
||||
frame_info.alloca_offsets[alloca_vreg] = -current_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [修复] 正确计算并设置locals_size
|
||||
// 它只应该包含由AllocaInst分配的局部变量的总大小。
|
||||
frame_info.locals_size = local_var_offset - locals_start_offset;
|
||||
frame_info.locals_size = current_offset;
|
||||
|
||||
// 遍历所有机器指令,将伪指令展开为真实指令
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
@ -246,30 +203,6 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
std::make_unique<ImmOperand>(0)));
|
||||
new_instructions.push_back(std::move(load_instr));
|
||||
|
||||
} else if (opcode == RVOpcodes::FRAME_LOAD_F) {
|
||||
// 展开浮点加载伪指令
|
||||
RVOpcodes real_load_op = RVOpcodes::FLW; // 对应的真实指令是 flw
|
||||
|
||||
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));
|
||||
|
||||
// 展开为: flw 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(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;
|
||||
@ -295,30 +228,6 @@ void RISCv64RegAlloc::eliminateFrameIndices() {
|
||||
std::make_unique<ImmOperand>(0)));
|
||||
new_instructions.push_back(std::move(store_instr));
|
||||
|
||||
} else if (opcode == RVOpcodes::FRAME_STORE_F) {
|
||||
// 展开浮点存储伪指令
|
||||
RVOpcodes real_store_op = RVOpcodes::FSW; // 对应的真实指令是 fsw
|
||||
|
||||
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));
|
||||
|
||||
// 展开为: fsw 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(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();
|
||||
@ -351,21 +260,9 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
bool first_reg_is_def = true; // 默认情况下,指令的第一个寄存器操作数是定义 (def)
|
||||
auto opcode = instr->getOpcode();
|
||||
|
||||
if (opcode == RVOpcodes::PSEUDO_KEEPALIVE) {
|
||||
for (auto& op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(op.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
use.insert(reg_op->getVRegNum()); // 它的所有操作数都是 "use"
|
||||
}
|
||||
}
|
||||
}
|
||||
return; // 处理完毕
|
||||
}
|
||||
|
||||
// 1. 特殊指令的 `is_def` 标志调整
|
||||
// 这些指令的第一个寄存器操作数是源操作数 (use),而不是目标操作数 (def)。
|
||||
if (opcode == RVOpcodes::SW || opcode == RVOpcodes::SD || opcode == RVOpcodes::FSW ||
|
||||
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 ||
|
||||
@ -398,15 +295,13 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
}
|
||||
} else { // [修复] CALL指令也可能定义物理寄存器(如a0)
|
||||
if (first_reg_operand_is_def) {
|
||||
auto it = preg_to_vreg_id_map.find(reg_op->getPReg());
|
||||
if (it != preg_to_vreg_id_map.end()) {
|
||||
def.insert(it->second);
|
||||
}
|
||||
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
|
||||
def.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
|
||||
}
|
||||
first_reg_operand_is_def = false;
|
||||
} else {
|
||||
auto it = preg_to_vreg_id_map.find(reg_op->getPReg());
|
||||
if (it != preg_to_vreg_id_map.end()) {
|
||||
use.insert(it->second);
|
||||
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
|
||||
use.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -414,27 +309,6 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
}
|
||||
return; // CALL 指令处理完毕
|
||||
}
|
||||
|
||||
// 2.1 浮点比较指令添加特殊规则
|
||||
// 它们的源操作数是浮点寄存器,但目标操作数是整数寄存器
|
||||
if (opcode == RVOpcodes::FEQ_S || opcode == RVOpcodes::FLT_S || opcode == RVOpcodes::FLE_S) {
|
||||
auto& operands = instr->getOperands();
|
||||
// Def: 第一个操作数 (整数vreg)
|
||||
if (operands[0]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(operands[0].get());
|
||||
if(reg_op->isVirtual()) def.insert(reg_op->getVRegNum());
|
||||
}
|
||||
// Use: 第二、三个操作数 (浮点vreg)
|
||||
if (operands[1]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(operands[1].get());
|
||||
if(reg_op->isVirtual()) use.insert(reg_op->getVRegNum());
|
||||
}
|
||||
if (operands[2]->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand*>(operands[2].get());
|
||||
if(reg_op->isVirtual()) use.insert(reg_op->getVRegNum());
|
||||
}
|
||||
return; // 处理完毕
|
||||
}
|
||||
|
||||
// 3. 对其他所有指令的通用处理逻辑 [已重构和修复]
|
||||
for (const auto& op : instr->getOperands()) {
|
||||
@ -446,10 +320,9 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
if (reg_op->isVirtual()) {
|
||||
def.insert(reg_op->getVRegNum());
|
||||
} else { // 物理寄存器也可以是 Def
|
||||
auto it = preg_to_vreg_id_map.find(reg_op->getPReg());
|
||||
if (it != preg_to_vreg_id_map.end()) {
|
||||
def.insert(it->second);
|
||||
}
|
||||
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
|
||||
def.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
|
||||
}
|
||||
}
|
||||
first_reg_is_def = false; // **关键**:处理完第一个寄存器后,立即更新标志
|
||||
} else {
|
||||
@ -457,37 +330,34 @@ void RISCv64RegAlloc::getInstrUseDef(MachineInstr* instr, LiveSet& use, LiveSet&
|
||||
if (reg_op->isVirtual()) {
|
||||
use.insert(reg_op->getVRegNum());
|
||||
} else { // 物理寄存器也可以是 Use
|
||||
auto it = preg_to_vreg_id_map.find(reg_op->getPReg());
|
||||
if (it != preg_to_vreg_id_map.end()) {
|
||||
use.insert(it->second);
|
||||
if (preg_to_vreg_id_map.count(reg_op->getPReg())) {
|
||||
use.insert(preg_to_vreg_id_map.at(reg_op->getPReg()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
// 内存操作数的处理逻辑看起来是正确的
|
||||
// [保持不变] 内存操作数的处理逻辑看起来是正确的
|
||||
auto mem_op = static_cast<MemOperand*>(op.get());
|
||||
auto base_reg = mem_op->getBase();
|
||||
if (base_reg->isVirtual()) {
|
||||
use.insert(base_reg->getVRegNum());
|
||||
} else {
|
||||
PhysicalReg preg = base_reg->getPReg();
|
||||
auto it = preg_to_vreg_id_map.find(preg);
|
||||
if (it != preg_to_vreg_id_map.end()) {
|
||||
use.insert(it->second);
|
||||
if (preg_to_vreg_id_map.count(preg)) {
|
||||
use.insert(preg_to_vreg_id_map.at(preg));
|
||||
}
|
||||
}
|
||||
|
||||
// 对于存储内存指令 (SW, SD),要存储的值(第一个操作数)也是 `use`
|
||||
if ((opcode == RVOpcodes::SW || opcode == RVOpcodes::SD || opcode == RVOpcodes::FSW) &&
|
||||
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());
|
||||
} else {
|
||||
auto it = preg_to_vreg_id_map.find(src_reg_op->getPReg());
|
||||
if (it != preg_to_vreg_id_map.end()) {
|
||||
use.insert(it->second);
|
||||
if (preg_to_vreg_id_map.count(src_reg_op->getPReg())) {
|
||||
use.insert(preg_to_vreg_id_map.at(src_reg_op->getPReg()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -691,22 +561,6 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
}
|
||||
}
|
||||
|
||||
// 所有在某一点上同时活跃的寄存器(即live_out集合中的所有成员),
|
||||
// 它们之间必须两两互相干扰。
|
||||
// 这会根据我们修正后的 liveness 信息,在所有参数vreg之间构建一个完全图(clique)。
|
||||
std::vector<unsigned> live_out_vec(live_out.begin(), live_out.end());
|
||||
for (size_t i = 0; i < live_out_vec.size(); ++i) {
|
||||
for (size_t j = i + 1; j < live_out_vec.size(); ++j) {
|
||||
unsigned u = live_out_vec[i];
|
||||
unsigned v = live_out_vec[j];
|
||||
if (DEEPDEBUG && interference_graph[u].find(v) == interference_graph[u].end()) {
|
||||
std::cerr << " Edge (Live-Live): %vreg" << u << " <-> %vreg" << v << "\n";
|
||||
}
|
||||
interference_graph[u].insert(v);
|
||||
interference_graph[v].insert(u);
|
||||
}
|
||||
}
|
||||
|
||||
// 在非move指令中,def 与 use 互相干扰
|
||||
if (instr->getOpcode() != RVOpcodes::MV) {
|
||||
for (unsigned d : def) {
|
||||
@ -736,53 +590,19 @@ void RISCv64RegAlloc::buildInterferenceGraph() {
|
||||
}
|
||||
// CALL 指令会定义(杀死)所有调用者保存的寄存器。
|
||||
// 因此,所有调用者保存的物理寄存器都与 CALL 指令的 live_out 中的所有变量冲突。
|
||||
|
||||
// 辅助函数,用于判断一个vreg是整数类型还是浮点类型
|
||||
auto is_fp_vreg = [&](unsigned vreg) {
|
||||
if (vreg_to_value_map.count(vreg)) {
|
||||
return vreg_to_value_map.at(vreg)->getType()->isFloat();
|
||||
}
|
||||
// 对于ISel创建的、没有直接IR Value对应的临时vreg,
|
||||
// 默认其为整数类型。这是一个合理的兜底策略。
|
||||
return false;
|
||||
};
|
||||
|
||||
// --- 处理整数寄存器干扰 ---
|
||||
const std::vector<PhysicalReg>& caller_saved_int_regs = getCallerSavedIntRegs();
|
||||
for (PhysicalReg cs_reg : caller_saved_int_regs) {
|
||||
// 确保物理寄存器在映射表中,我们已在构造函数中保证了这一点
|
||||
unsigned cs_vreg_id = preg_to_vreg_id_map.at(cs_reg);
|
||||
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) {
|
||||
// 只为整数vreg添加与整数preg的干扰
|
||||
if (!is_fp_vreg(live_vreg_out)) {
|
||||
if (cs_vreg_id != live_vreg_out) {
|
||||
if (DEEPDEBUG && interference_graph[cs_vreg_id].find(live_vreg_out) == interference_graph[cs_vreg_id].end()) {
|
||||
std::cerr << " Edge (CALL, Int): preg(" << static_cast<int>(cs_reg) << ") <-> %vreg" << live_vreg_out << "\n";
|
||||
}
|
||||
interference_graph[cs_vreg_id].insert(live_vreg_out);
|
||||
interference_graph[live_vreg_out].insert(cs_vreg_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 处理浮点寄存器干扰 ---
|
||||
const std::vector<PhysicalReg>& caller_saved_fp_regs = getCallerSavedFpRegs();
|
||||
for (PhysicalReg cs_reg : caller_saved_fp_regs) {
|
||||
unsigned cs_vreg_id = preg_to_vreg_id_map.at(cs_reg);
|
||||
|
||||
for (unsigned live_vreg_out : live_out) {
|
||||
// 只为浮点vreg添加与浮点preg的干扰
|
||||
if (is_fp_vreg(live_vreg_out)) {
|
||||
if (cs_vreg_id != live_vreg_out) {
|
||||
// 添加与整数版本一致的调试代码
|
||||
if (DEEPDEBUG && interference_graph[cs_vreg_id].find(live_vreg_out) == interference_graph[cs_vreg_id].end()) {
|
||||
std::cerr << " Edge (CALL, FP): preg(" << static_cast<int>(cs_reg) << ") <-> %vreg" << live_vreg_out << "\n";
|
||||
}
|
||||
interference_graph[cs_vreg_id].insert(live_vreg_out);
|
||||
interference_graph[live_vreg_out].insert(cs_vreg_id);
|
||||
if (cs_vreg_id != live_vreg_out) { // 避免自己和自己干扰
|
||||
// [新增调试逻辑] 打印添加的干扰边及其原因
|
||||
if (DEEPDEBUG && interference_graph[cs_vreg_id].find(live_vreg_out) == interference_graph[cs_vreg_id].end()) {
|
||||
std::cerr << " Edge (CALL) : preg(" << static_cast<int>(cs_reg) << ") <-> %vreg" << live_vreg_out << "\n";
|
||||
}
|
||||
interference_graph[cs_vreg_id].insert(live_vreg_out);
|
||||
interference_graph[live_vreg_out].insert(cs_vreg_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -806,70 +626,34 @@ void RISCv64RegAlloc::colorGraph() {
|
||||
return interference_graph[a].size() > interference_graph[b].size();
|
||||
});
|
||||
|
||||
// [调试] 辅助函数,用于判断一个vreg是整数还是浮点类型,并打印详细诊断信息
|
||||
auto is_fp_vreg = [&](unsigned vreg) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cout << " [Debug is_fp_vreg] Checking vreg" << vreg << ": ";
|
||||
}
|
||||
if (vreg_to_value_map.count(vreg)) {
|
||||
Value* val = vreg_to_value_map.at(vreg);
|
||||
bool is_float = val->getType()->isFloat();
|
||||
if (DEEPDEBUG) {
|
||||
std::cout << "Found in map. Value is '" << val->getName()
|
||||
<< "', Type is " << (is_float ? "FLOAT" : "INT")
|
||||
<< ". Returning " << (is_float ? "true" : "false") << ".\n";
|
||||
}
|
||||
return is_float;
|
||||
}
|
||||
|
||||
if (DEEPDEBUG) {
|
||||
std::cout << "NOT found in vreg_to_value_map. Defaulting to INT. Returning false.\n";
|
||||
}
|
||||
// 对于ISel创建的、没有直接IR Value对应的临时vreg,默认其为整数类型。
|
||||
return false;
|
||||
};
|
||||
|
||||
// 着色
|
||||
for (unsigned vreg : sorted_vregs) {
|
||||
std::set<PhysicalReg> used_colors;
|
||||
for (unsigned neighbor_id : interference_graph.at(vreg)) {
|
||||
// 收集邻居颜色的逻辑保持不变
|
||||
// --- 关键改进 (来自 rec 分支) ---
|
||||
|
||||
// 情况 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 neighbor_preg = static_cast<PhysicalReg>(neighbor_id - static_cast<unsigned>(PhysicalReg::PHYS_REG_START_ID));
|
||||
used_colors.insert(neighbor_preg);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_float = is_fp_vreg(vreg);
|
||||
const auto& allocable_regs = is_float ? allocable_fp_regs : allocable_int_regs;
|
||||
|
||||
// [调试] 打印着色决策过程
|
||||
if (DEBUG) {
|
||||
std::cout << "[DEBUG] Coloring %vreg" << vreg
|
||||
<< ": Type is " << (is_float ? "FLOAT" : "INT")
|
||||
<< ", choosing from " << (is_float ? "Float" : "Integer") << " pool.\n";
|
||||
}
|
||||
|
||||
bool colored = false;
|
||||
for (PhysicalReg preg : allocable_regs) {
|
||||
for (PhysicalReg preg : allocable_int_regs) {
|
||||
if (used_colors.find(preg) == used_colors.end()) {
|
||||
color_map[vreg] = preg;
|
||||
colored = true;
|
||||
if (DEBUG) {
|
||||
RISCv64AsmPrinter p(MFunc); // For regToString
|
||||
std::cout << " -> Assigned to physical register: " << p.regToString(preg) << "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!colored) {
|
||||
spilled_vregs.insert(vreg);
|
||||
if (DEBUG) {
|
||||
std::cout << " -> FAILED to color. Spilling.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -878,7 +662,7 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
StackFrameInfo& frame_info = MFunc->getFrameInfo();
|
||||
int current_offset = frame_info.locals_size;
|
||||
|
||||
// --- 动态计算溢出槽大小 ---
|
||||
// --- FIX 1: 动态计算溢出槽大小 ---
|
||||
// 根据溢出虚拟寄存器的真实类型,为其在栈上分配正确大小的空间。
|
||||
for (unsigned vreg : spilled_vregs) {
|
||||
// 从反向映射中查找 vreg 对应的 IR Value
|
||||
@ -896,40 +680,23 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
}
|
||||
frame_info.spill_size = current_offset - frame_info.locals_size;
|
||||
|
||||
// 定义专用的溢出寄存器
|
||||
const PhysicalReg INT_SPILL_REG = PhysicalReg::T6; // t6
|
||||
const PhysicalReg FP_SPILL_REG = PhysicalReg::F7; // ft7
|
||||
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
std::vector<std::unique_ptr<MachineInstr>> new_instructions;
|
||||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||||
LiveSet use, def;
|
||||
getInstrUseDef(instr_ptr.get(), use, def);
|
||||
|
||||
// --- 为溢出的 'use' 操作数插入正确的加载指令 ---
|
||||
// --- 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);
|
||||
|
||||
// 根据vreg类型决定加载指令(lw/ld/flw)和目标物理寄存器(t6/ft7)
|
||||
RVOpcodes load_op;
|
||||
PhysicalReg target_preg;
|
||||
if (val->getType()->isFloat()) {
|
||||
load_op = RVOpcodes::FLW;
|
||||
target_preg = FP_SPILL_REG;
|
||||
} else if (val->getType()->isPointer()) {
|
||||
load_op = RVOpcodes::LD;
|
||||
target_preg = INT_SPILL_REG;
|
||||
} else {
|
||||
load_op = RVOpcodes::LW;
|
||||
target_preg = INT_SPILL_REG;
|
||||
}
|
||||
RVOpcodes load_op = val->getType()->isPointer() ? RVOpcodes::LD : RVOpcodes::LW;
|
||||
|
||||
int offset = frame_info.spill_offsets.at(vreg);
|
||||
auto load = std::make_unique<MachineInstr>(load_op);
|
||||
load->addOperand(std::make_unique<RegOperand>(target_preg)); // 加载到专用溢出寄存器
|
||||
load->addOperand(std::make_unique<RegOperand>(vreg));
|
||||
load->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(offset)
|
||||
@ -940,29 +707,17 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
|
||||
new_instructions.push_back(std::move(instr_ptr));
|
||||
|
||||
// --- 为溢出的 'def' 操作数插入正确的存储指令 ---
|
||||
// --- 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);
|
||||
|
||||
// 根据vreg类型决定存储指令(sw/sd/fsw)和源物理寄存器(t6/ft7)
|
||||
RVOpcodes store_op;
|
||||
PhysicalReg src_preg;
|
||||
if (val->getType()->isFloat()) {
|
||||
store_op = RVOpcodes::FSW;
|
||||
src_preg = FP_SPILL_REG;
|
||||
} else if (val->getType()->isPointer()) {
|
||||
store_op = RVOpcodes::SD;
|
||||
src_preg = INT_SPILL_REG;
|
||||
} else {
|
||||
store_op = RVOpcodes::SW;
|
||||
src_preg = INT_SPILL_REG;
|
||||
}
|
||||
RVOpcodes store_op = val->getType()->isPointer() ? RVOpcodes::SD : RVOpcodes::SW;
|
||||
|
||||
int offset = frame_info.spill_offsets.at(vreg);
|
||||
auto store = std::make_unique<MachineInstr>(store_op);
|
||||
store->addOperand(std::make_unique<RegOperand>(src_preg)); // 从专用溢出寄存器存储
|
||||
store->addOperand(std::make_unique<RegOperand>(vreg));
|
||||
store->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(offset)
|
||||
@ -978,29 +733,40 @@ void RISCv64RegAlloc::rewriteFunction() {
|
||||
for (auto& mbb : MFunc->getBlocks()) {
|
||||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||||
for (auto& op_ptr : instr_ptr->getOperands()) {
|
||||
// 定义一个处理寄存器操作数的 lambda 函数
|
||||
auto process_reg_op = [&](RegOperand* reg_op) {
|
||||
|
||||
// 情况一:操作数本身就是一个寄存器 (例如 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)) {
|
||||
// 根据vreg类型,替换为对应的专用溢出寄存器
|
||||
assert(vreg_to_value_map.count(vreg));
|
||||
Value* val = vreg_to_value_map.at(vreg);
|
||||
if (val->getType()->isFloat()) {
|
||||
reg_op->setPReg(FP_SPILL_REG);
|
||||
} else {
|
||||
reg_op->setPReg(INT_SPILL_REG);
|
||||
}
|
||||
// 如果vreg被溢出,替换为专用的溢出物理寄存器t6
|
||||
reg_op->setPReg(PhysicalReg::T6);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 情况二:操作数是一个内存地址 (例如 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)) {
|
||||
// 如果基址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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(op_ptr->getKind() == MachineOperand::KIND_REG) {
|
||||
process_reg_op(static_cast<RegOperand*>(op_ptr.get()));
|
||||
} else if (op_ptr->getKind() == MachineOperand::KIND_MEM) {
|
||||
process_reg_op(static_cast<MemOperand*>(op_ptr.get())->getBase());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,7 @@
|
||||
#include "SysYIRPrinter.h"
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "IR.h" // 确保IR.h包含了ArrayType、GetElementPtrInst等的定义
|
||||
|
||||
@ -16,7 +13,6 @@ void SysYPrinter::printIR() {
|
||||
//TODO: Print target datalayout and triple (minimal required by LLVM)
|
||||
|
||||
printGlobalVariable();
|
||||
printGlobalConstant();
|
||||
|
||||
for (const auto &iter : functions) {
|
||||
if (iter.second->getName() == "main") {
|
||||
@ -64,21 +60,16 @@ std::string SysYPrinter::getValueName(Value *value) {
|
||||
} else if (auto constInt = dynamic_cast<ConstantInteger*>(value)) { // 优先匹配具体的常量类型
|
||||
return std::to_string(constInt->getInt());
|
||||
} else if (auto constFloat = dynamic_cast<ConstantFloating*>(value)) { // 优先匹配具体的常量类型
|
||||
std::ostringstream oss;
|
||||
oss << std::scientific << std::setprecision(std::numeric_limits<float>::max_digits10) << constFloat->getFloat();
|
||||
return oss.str();
|
||||
return std::to_string(constFloat->getFloat());
|
||||
} else if (auto constUndef = dynamic_cast<UndefinedValue*>(value)) { // 如果有Undef类型
|
||||
return "undef";
|
||||
} else if (auto constVal = dynamic_cast<ConstantValue*>(value)) { // fallback for generic ConstantValue
|
||||
// 这里的逻辑可能需要根据你ConstantValue的实际设计调整
|
||||
// 确保它能处理所有可能的ConstantValue
|
||||
if (auto constInt = dynamic_cast<ConstantInteger*>(value)) { // 优先匹配具体的常量类型
|
||||
return std::to_string(constInt->getInt());
|
||||
} else if (auto constFloat = dynamic_cast<ConstantFloating*>(value)) { // 优先匹配具体的常量类型
|
||||
std::ostringstream oss;
|
||||
oss << std::scientific << std::setprecision(std::numeric_limits<float>::max_digits10) << constFloat->getFloat();
|
||||
return oss.str();
|
||||
if (constVal->getType()->isFloat()) {
|
||||
return std::to_string(constVal->getFloat());
|
||||
}
|
||||
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)) {
|
||||
@ -88,15 +79,6 @@ std::string SysYPrinter::getValueName(Value *value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string SysYPrinter::getBlockName(BasicBlock *block) {
|
||||
static int blockId = 0; // 用于生成唯一的基本块ID
|
||||
if (block->getName().empty()) {
|
||||
return "bb" + std::to_string(blockId++); // 如果没有名字,生成一个唯一的基本块ID
|
||||
} else {
|
||||
return block->getName();
|
||||
}
|
||||
}
|
||||
|
||||
void SysYPrinter::printType(Type *type) {
|
||||
std::cout << getTypeString(type);
|
||||
}
|
||||
@ -146,52 +128,6 @@ void SysYPrinter::printGlobalVariable() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SysYPrinter::printGlobalConstant() {
|
||||
auto &globalConstants = pModule->getConsts();
|
||||
|
||||
for (const auto &globalConstant : globalConstants) {
|
||||
std::cout << "@" << globalConstant->getName() << " = global constant ";
|
||||
|
||||
// 全局变量的类型是一个指针,指向其基类型 (可能是 ArrayType 或 Integer/FloatType)
|
||||
auto globalVarBaseType = dynamic_cast<PointerType *>(globalConstant->getType())->getBaseType();
|
||||
printType(globalVarBaseType); // 打印全局变量的实际类型 (例如 i32 或 [10 x i32])
|
||||
|
||||
std::cout << " ";
|
||||
|
||||
// 检查是否是数组类型 (通过检查 globalVarBaseType 是否是 ArrayType)
|
||||
if (globalVarBaseType->isArray()) {
|
||||
// 数组初始化器
|
||||
std::cout << "["; // LLVM IR 数组初始化器格式: [type value, type value, ...]
|
||||
auto values = globalConstant->getInitValues(); // 假设 getInitValues() 返回一个 ValueCounter
|
||||
const std::vector<sysy::Value *> &counterValues = values.getValues(); // 获取所有值
|
||||
|
||||
for (size_t i = 0; i < counterValues.size(); i++) {
|
||||
if (i > 0) std::cout << ", ";
|
||||
// 打印元素类型,这个元素类型应该是数组的最终元素类型,例如 i32 或 float
|
||||
// 可以从 globalVarBaseType 逐层剥离得到最终元素类型,但这里简化为直接从值获取
|
||||
printType(counterValues[i]->getType());
|
||||
std::cout << " ";
|
||||
printValue(counterValues[i]);
|
||||
}
|
||||
std::cout << "]";
|
||||
} else {
|
||||
// 标量初始化器
|
||||
// 假设标量全局变量的初始化值通过 getByIndex(0) 获取
|
||||
Value* initVal = globalConstant->getByIndex(0);
|
||||
printType(initVal->getType()); // 打印标量值的类型
|
||||
std::cout << " ";
|
||||
printValue(initVal); // 打印标量值
|
||||
}
|
||||
|
||||
std::cout << ", align 4" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SysYPrinter::printBlock(BasicBlock *block) {
|
||||
std::cout << getBlockName(block);
|
||||
}
|
||||
|
||||
void SysYPrinter::printFunction(Function *function) {
|
||||
// Function signature
|
||||
std::cout << "define ";
|
||||
@ -415,8 +351,7 @@ void SysYPrinter::printInst(Instruction *pInst) {
|
||||
|
||||
// AllocaInst 的类型现在应该是一个 PointerType,指向正确的 ArrayType 或 ScalarType
|
||||
// 例如:alloca i32, align 4 或者 alloca [10 x i32], align 4
|
||||
// auto allocatedType = dynamic_cast<PointerType *>(allocaInst->getType())->getBaseType();
|
||||
auto allocatedType = allocaInst->getAllocatedType();
|
||||
auto allocatedType = dynamic_cast<PointerType *>(allocaInst->getType())->getBaseType();
|
||||
printType(allocatedType);
|
||||
|
||||
// 仍然打印维度信息,如果存在的话
|
||||
@ -531,13 +466,13 @@ void SysYPrinter::printInst(Instruction *pInst) {
|
||||
// 如果你的 PhiInst 存储方式是 getIncomingValues() 和 getIncomingBlocks(),请相应调整
|
||||
// LLVM IR 格式: phi type [value1, block1], [value2, block2]
|
||||
bool firstPair = true;
|
||||
for (unsigned i = 0; i < phiInst->getNumIncomingValues(); ++i) { // 遍历成对的操作数
|
||||
for (unsigned i = 0; i < phiInst->getNumOperands() / 2; ++i) { // 遍历成对的操作数
|
||||
if (!firstPair) std::cout << ", ";
|
||||
firstPair = false;
|
||||
std::cout << "[ ";
|
||||
printValue(phiInst->getValue(i));
|
||||
printValue(phiInst->getOperand(i * 2)); // value
|
||||
std::cout << ", %";
|
||||
printBlock(phiInst->getBlock(i));
|
||||
printValue(phiInst->getOperand(i * 2 + 1)); // block
|
||||
std::cout << " ]";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
@ -1,23 +0,0 @@
|
||||
# src/backend/RISCv64/CMakeLists.txt
|
||||
add_library(riscv64_backend_lib STATIC
|
||||
RISCv64AsmPrinter.cpp
|
||||
RISCv64Backend.cpp
|
||||
RISCv64ISel.cpp
|
||||
RISCv64LLIR.cpp
|
||||
RISCv64RegAlloc.cpp
|
||||
Handler/CalleeSavedHandler.cpp
|
||||
Handler/LegalizeImmediates.cpp
|
||||
Handler/PrologueEpilogueInsertion.cpp
|
||||
Optimize/Peephole.cpp
|
||||
Optimize/PostRA_Scheduler.cpp
|
||||
Optimize/PreRA_Scheduler.cpp
|
||||
)
|
||||
|
||||
# 包含后端模块所需的头文件路径
|
||||
target_include_directories(riscv64_backend_lib PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include/backend/RISCv64 # 后端顶层头文件
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include/backend/RISCv64/Handler # 增加 Handler 头文件路径
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include/backend/RISCv64/Optimize # 增加 Optimize 头文件路径
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include/midend # 增加 midend 头文件路径 (已存在)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../include/midend/Pass # 增加 midend 头文件路径 (已存在)
|
||||
)
|
||||
@ -1,133 +0,0 @@
|
||||
#include "CalleeSavedHandler.h"
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char CalleeSavedHandler::ID = 0;
|
||||
|
||||
// 辅助函数,用于判断一个物理寄存器是否为浮点寄存器
|
||||
static bool is_fp_reg(PhysicalReg reg) {
|
||||
return reg >= PhysicalReg::F0 && reg <= PhysicalReg::F31;
|
||||
}
|
||||
|
||||
bool CalleeSavedHandler::runOnFunction(Function *F, AnalysisManager& AM) {
|
||||
// This pass works on MachineFunction level, not IR level
|
||||
return false;
|
||||
}
|
||||
|
||||
void CalleeSavedHandler::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
StackFrameInfo& frame_info = mfunc->getFrameInfo();
|
||||
|
||||
std::set<PhysicalReg> used_callee_saved;
|
||||
|
||||
// 1. 扫描所有指令,找出被使用的callee-saved寄存器
|
||||
// 这个Pass在RegAlloc之后运行,所以可以访问到物理寄存器
|
||||
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 && !reg_op->isVirtual()) {
|
||||
PhysicalReg preg = reg_op->getPReg();
|
||||
|
||||
// 检查整数 s1-s11
|
||||
if (preg >= PhysicalReg::S1 && preg <= PhysicalReg::S11) {
|
||||
used_callee_saved.insert(preg);
|
||||
}
|
||||
// 检查浮点 fs0-fs11 (f8,f9,f18-f27)
|
||||
else if ((preg >= PhysicalReg::F8 && preg <= PhysicalReg::F9) || (preg >= PhysicalReg::F18 && preg <= PhysicalReg::F27)) {
|
||||
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. 计算并更新 frame_info
|
||||
frame_info.callee_saved_size = used_callee_saved.size() * 8;
|
||||
|
||||
// 为了布局确定性和恢复顺序一致,对寄存器排序
|
||||
std::vector<PhysicalReg> sorted_regs(used_callee_saved.begin(), used_callee_saved.end());
|
||||
std::sort(sorted_regs.begin(), sorted_regs.end());
|
||||
|
||||
// 3. 在函数序言中插入保存指令
|
||||
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
|
||||
auto& entry_instrs = entry_block->getInstructions();
|
||||
// 插入点在函数入口标签之后,或者就是最开始
|
||||
auto insert_pos = entry_instrs.begin();
|
||||
if (!entry_instrs.empty() && entry_instrs.front()->getOpcode() == RVOpcodes::LABEL) {
|
||||
insert_pos = std::next(insert_pos);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<MachineInstr>> save_instrs;
|
||||
// [关键] 从局部变量区域之后开始分配空间
|
||||
int current_offset = - (16 + frame_info.locals_size);
|
||||
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
current_offset -= 8;
|
||||
RVOpcodes save_op = is_fp_reg(reg) ? RVOpcodes::FSD : RVOpcodes::SD;
|
||||
|
||||
auto save_instr = std::make_unique<MachineInstr>(save_op);
|
||||
save_instr->addOperand(std::make_unique<RegOperand>(reg));
|
||||
save_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0), // 基址为帧指针 s0
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
save_instrs.push_back(std::move(save_instr));
|
||||
}
|
||||
|
||||
if (!save_instrs.empty()) {
|
||||
entry_instrs.insert(insert_pos,
|
||||
std::make_move_iterator(save_instrs.begin()),
|
||||
std::make_move_iterator(save_instrs.end()));
|
||||
}
|
||||
|
||||
// 4. 在函数结尾(ret之前)插入恢复指令
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
std::vector<std::unique_ptr<MachineInstr>> restore_instrs;
|
||||
// [关键] 使用与保存时完全相同的逻辑来计算偏移量
|
||||
current_offset = - (16 + frame_info.locals_size);
|
||||
|
||||
for (PhysicalReg reg : sorted_regs) {
|
||||
current_offset -= 8;
|
||||
RVOpcodes restore_op = is_fp_reg(reg) ? RVOpcodes::FLD : RVOpcodes::LD;
|
||||
|
||||
auto restore_instr = std::make_unique<MachineInstr>(restore_op);
|
||||
restore_instr->addOperand(std::make_unique<RegOperand>(reg));
|
||||
restore_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(current_offset)
|
||||
));
|
||||
restore_instrs.push_back(std::move(restore_instr));
|
||||
}
|
||||
|
||||
if (!restore_instrs.empty()) {
|
||||
mbb->getInstructions().insert(it,
|
||||
std::make_move_iterator(restore_instrs.begin()),
|
||||
std::make_move_iterator(restore_instrs.end()));
|
||||
}
|
||||
goto next_block_label;
|
||||
}
|
||||
}
|
||||
next_block_label:;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,171 +0,0 @@
|
||||
#include "LegalizeImmediates.h"
|
||||
#include "RISCv64ISel.h" // 需要包含它以调用 getNewVReg()
|
||||
#include "RISCv64AsmPrinter.h"
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
// 声明外部调试控制变量
|
||||
extern int DEBUG;
|
||||
extern int DEEPDEBUG;
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char LegalizeImmediatesPass::ID = 0;
|
||||
|
||||
// 辅助函数:检查一个立即数是否在RISC-V的12位有符号范围内
|
||||
static bool isLegalImmediate(int64_t imm) {
|
||||
return imm >= -2048 && imm <= 2047;
|
||||
}
|
||||
|
||||
void LegalizeImmediatesPass::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
if (DEBUG) {
|
||||
std::cerr << "===== Running Legalize Immediates Pass on function: " << mfunc->getName() << " =====\n";
|
||||
}
|
||||
|
||||
// 定义我们保留的、用于暂存的物理寄存器
|
||||
const PhysicalReg TEMP_REG = PhysicalReg::T5;
|
||||
|
||||
// 创建一个临时的AsmPrinter用于打印指令,方便调试
|
||||
RISCv64AsmPrinter temp_printer(mfunc);
|
||||
if (DEEPDEBUG) {
|
||||
temp_printer.setStream(std::cerr);
|
||||
}
|
||||
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << "--- Processing Basic Block: " << mbb->getName() << " ---\n";
|
||||
}
|
||||
// 创建一个新的指令列表,用于存放合法化后的指令
|
||||
std::vector<std::unique_ptr<MachineInstr>> new_instructions;
|
||||
|
||||
for (auto& instr_ptr : mbb->getInstructions()) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << " Checking: ";
|
||||
// 打印指令时末尾会带换行符,所以这里不用 std::endl
|
||||
temp_printer.printInstruction(instr_ptr.get(), true);
|
||||
}
|
||||
|
||||
bool legalized = false; // 标记当前指令是否已被展开处理
|
||||
|
||||
switch (instr_ptr->getOpcode()) {
|
||||
case RVOpcodes::ADDI:
|
||||
case RVOpcodes::ADDIW: {
|
||||
auto& operands = instr_ptr->getOperands();
|
||||
// 确保操作数足够多,以防万一
|
||||
if (operands.size() < 3) break;
|
||||
auto imm_op = static_cast<ImmOperand*>(operands.back().get());
|
||||
|
||||
if (!isLegalImmediate(imm_op->getValue())) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << " >> ILLEGAL immediate (" << imm_op->getValue() << "). Expanding...\n";
|
||||
}
|
||||
// 立即数超出范围,需要展开
|
||||
auto rd_op = std::make_unique<RegOperand>(*static_cast<RegOperand*>(operands[0].get()));
|
||||
auto rs1_op = std::make_unique<RegOperand>(*static_cast<RegOperand*>(operands[1].get()));
|
||||
|
||||
// 1. li t5, immediate
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(TEMP_REG));
|
||||
li->addOperand(std::make_unique<ImmOperand>(imm_op->getValue()));
|
||||
|
||||
// 2. add/addw rd, rs1, t5
|
||||
auto new_op = (instr_ptr->getOpcode() == RVOpcodes::ADDI) ? RVOpcodes::ADD : RVOpcodes::ADDW;
|
||||
auto add = std::make_unique<MachineInstr>(new_op);
|
||||
add->addOperand(std::move(rd_op));
|
||||
add->addOperand(std::move(rs1_op));
|
||||
add->addOperand(std::make_unique<RegOperand>(TEMP_REG));
|
||||
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << " New sequence:\n ";
|
||||
temp_printer.printInstruction(li.get(), true);
|
||||
std::cerr << " ";
|
||||
temp_printer.printInstruction(add.get(), true);
|
||||
}
|
||||
|
||||
new_instructions.push_back(std::move(li));
|
||||
new_instructions.push_back(std::move(add));
|
||||
|
||||
legalized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 处理所有内存加载/存储指令
|
||||
case RVOpcodes::LB: case RVOpcodes::LH: case RVOpcodes::LW: case RVOpcodes::LD:
|
||||
case RVOpcodes::LBU: case RVOpcodes::LHU: case RVOpcodes::LWU:
|
||||
case RVOpcodes::SB: case RVOpcodes::SH: case RVOpcodes::SW: case RVOpcodes::SD:
|
||||
case RVOpcodes::FLW: case RVOpcodes::FSW: {
|
||||
auto& operands = instr_ptr->getOperands();
|
||||
auto mem_op = static_cast<MemOperand*>(operands.back().get());
|
||||
auto offset_op = mem_op->getOffset();
|
||||
|
||||
if (!isLegalImmediate(offset_op->getValue())) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << " >> ILLEGAL immediate offset (" << offset_op->getValue() << "). Expanding...\n";
|
||||
}
|
||||
// 偏移量超出范围,需要展开
|
||||
auto data_reg_op = std::make_unique<RegOperand>(*static_cast<RegOperand*>(operands[0].get()));
|
||||
auto base_reg_op = std::make_unique<RegOperand>(*mem_op->getBase());
|
||||
|
||||
// 1. li t5, offset
|
||||
auto li = std::make_unique<MachineInstr>(RVOpcodes::LI);
|
||||
li->addOperand(std::make_unique<RegOperand>(TEMP_REG));
|
||||
li->addOperand(std::make_unique<ImmOperand>(offset_op->getValue()));
|
||||
|
||||
// 2. add t5, base_reg, t5 (计算最终地址,结果也放在t5)
|
||||
auto add = std::make_unique<MachineInstr>(RVOpcodes::ADD);
|
||||
add->addOperand(std::make_unique<RegOperand>(TEMP_REG));
|
||||
add->addOperand(std::move(base_reg_op));
|
||||
add->addOperand(std::make_unique<RegOperand>(TEMP_REG));
|
||||
|
||||
// 3. lw/sw data_reg, 0(t5)
|
||||
auto mem_instr = std::make_unique<MachineInstr>(instr_ptr->getOpcode());
|
||||
mem_instr->addOperand(std::move(data_reg_op));
|
||||
mem_instr->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(TEMP_REG),
|
||||
std::make_unique<ImmOperand>(0)
|
||||
));
|
||||
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << " New sequence:\n ";
|
||||
temp_printer.printInstruction(li.get(), true);
|
||||
std::cerr << " ";
|
||||
temp_printer.printInstruction(add.get(), true);
|
||||
std::cerr << " ";
|
||||
temp_printer.printInstruction(mem_instr.get(), true);
|
||||
}
|
||||
|
||||
new_instructions.push_back(std::move(li));
|
||||
new_instructions.push_back(std::move(add));
|
||||
new_instructions.push_back(std::move(mem_instr));
|
||||
|
||||
legalized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// 其他指令不需要处理
|
||||
break;
|
||||
}
|
||||
|
||||
if (!legalized) {
|
||||
if (DEEPDEBUG) {
|
||||
std::cerr << " -- Immediate is legal. Skipping.\n";
|
||||
}
|
||||
// 如果当前指令不需要合法化,直接将其移动到新列表中
|
||||
new_instructions.push_back(std::move(instr_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
// 用新的、已合法化的指令列表替换旧的列表
|
||||
mbb->getInstructions() = std::move(new_instructions);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
std::cerr << "===== Finished Legalize Immediates Pass =====\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,172 +0,0 @@
|
||||
#include "PrologueEpilogueInsertion.h"
|
||||
#include "RISCv64ISel.h"
|
||||
#include "RISCv64RegAlloc.h" // 需要访问RegAlloc的结果
|
||||
#include <algorithm>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PrologueEpilogueInsertionPass::ID = 0;
|
||||
|
||||
void PrologueEpilogueInsertionPass::runOnMachineFunction(MachineFunction* mfunc) {
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
auto& instrs = mbb->getInstructions();
|
||||
|
||||
// 使用标准的 Erase-Remove Idiom 来删除满足条件的元素
|
||||
instrs.erase(
|
||||
std::remove_if(instrs.begin(), instrs.end(),
|
||||
[](const std::unique_ptr<MachineInstr>& instr) {
|
||||
return instr->getOpcode() == RVOpcodes::PSEUDO_KEEPALIVE;
|
||||
}
|
||||
),
|
||||
instrs.end()
|
||||
);
|
||||
}
|
||||
|
||||
StackFrameInfo& frame_info = mfunc->getFrameInfo();
|
||||
Function* F = mfunc->getFunc();
|
||||
RISCv64ISel* isel = mfunc->getISel();
|
||||
|
||||
// [关键] 获取寄存器分配的结果 (vreg -> preg 的映射)
|
||||
// RegAlloc Pass 必须已经运行过
|
||||
auto& vreg_to_preg_map = frame_info.vreg_to_preg_map;
|
||||
|
||||
// 完全遵循 AsmPrinter 中的计算逻辑
|
||||
int total_stack_size = frame_info.locals_size +
|
||||
frame_info.spill_size +
|
||||
frame_info.callee_saved_size +
|
||||
16; // 为 ra 和 s0 固定的16字节
|
||||
|
||||
int aligned_stack_size = (total_stack_size + 15) & ~15;
|
||||
frame_info.total_size = aligned_stack_size;
|
||||
|
||||
// 只有在需要分配栈空间时才生成指令
|
||||
if (aligned_stack_size > 0) {
|
||||
// --- 1. 插入序言 ---
|
||||
MachineBasicBlock* entry_block = mfunc->getBlocks().front().get();
|
||||
auto& entry_instrs = entry_block->getInstructions();
|
||||
|
||||
std::vector<std::unique_ptr<MachineInstr>> prologue_instrs;
|
||||
|
||||
// 1. addi sp, sp, -aligned_stack_size
|
||||
auto alloc_stack = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
alloc_stack->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
alloc_stack->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
alloc_stack->addOperand(std::make_unique<ImmOperand>(-aligned_stack_size));
|
||||
prologue_instrs.push_back(std::move(alloc_stack));
|
||||
|
||||
// 2. sd ra, (aligned_stack_size - 8)(sp)
|
||||
auto save_ra = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
save_ra->addOperand(std::make_unique<RegOperand>(PhysicalReg::RA));
|
||||
save_ra->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::SP),
|
||||
std::make_unique<ImmOperand>(aligned_stack_size - 8)
|
||||
));
|
||||
prologue_instrs.push_back(std::move(save_ra));
|
||||
|
||||
// 3. sd s0, (aligned_stack_size - 16)(sp)
|
||||
auto save_fp = std::make_unique<MachineInstr>(RVOpcodes::SD);
|
||||
save_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
save_fp->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::SP),
|
||||
std::make_unique<ImmOperand>(aligned_stack_size - 16)
|
||||
));
|
||||
prologue_instrs.push_back(std::move(save_fp));
|
||||
|
||||
// 4. addi s0, sp, aligned_stack_size
|
||||
auto set_fp = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
set_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
set_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
set_fp->addOperand(std::make_unique<ImmOperand>(aligned_stack_size));
|
||||
prologue_instrs.push_back(std::move(set_fp));
|
||||
|
||||
// --- 在s0设置完毕后,使用物理寄存器加载栈参数 ---
|
||||
if (F && isel) {
|
||||
int arg_idx = 0;
|
||||
for (Argument* arg : F->getArguments()) {
|
||||
if (arg_idx >= 8) {
|
||||
unsigned vreg = isel->getVReg(arg);
|
||||
|
||||
if (frame_info.alloca_offsets.count(vreg) && vreg_to_preg_map.count(vreg)) {
|
||||
int offset = frame_info.alloca_offsets.at(vreg);
|
||||
PhysicalReg dest_preg = vreg_to_preg_map.at(vreg);
|
||||
Type* arg_type = arg->getType();
|
||||
|
||||
if (arg_type->isFloat()) {
|
||||
auto load_arg = std::make_unique<MachineInstr>(RVOpcodes::FLW);
|
||||
load_arg->addOperand(std::make_unique<RegOperand>(dest_preg));
|
||||
load_arg->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(offset)
|
||||
));
|
||||
prologue_instrs.push_back(std::move(load_arg));
|
||||
} else {
|
||||
RVOpcodes load_op = arg_type->isPointer() ? RVOpcodes::LD : RVOpcodes::LW;
|
||||
auto load_arg = std::make_unique<MachineInstr>(load_op);
|
||||
load_arg->addOperand(std::make_unique<RegOperand>(dest_preg));
|
||||
load_arg->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::S0),
|
||||
std::make_unique<ImmOperand>(offset)
|
||||
));
|
||||
prologue_instrs.push_back(std::move(load_arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
arg_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
// 确定插入点
|
||||
auto insert_pos = entry_instrs.begin();
|
||||
|
||||
// 一次性将所有序言指令插入
|
||||
if (!prologue_instrs.empty()) {
|
||||
entry_instrs.insert(insert_pos,
|
||||
std::make_move_iterator(prologue_instrs.begin()),
|
||||
std::make_move_iterator(prologue_instrs.end()));
|
||||
}
|
||||
|
||||
// --- 2. 插入尾声 (此部分逻辑保持不变) ---
|
||||
for (auto& mbb : mfunc->getBlocks()) {
|
||||
for (auto it = mbb->getInstructions().begin(); it != mbb->getInstructions().end(); ++it) {
|
||||
if ((*it)->getOpcode() == RVOpcodes::RET) {
|
||||
std::vector<std::unique_ptr<MachineInstr>> epilogue_instrs;
|
||||
|
||||
// 1. ld ra
|
||||
auto restore_ra = std::make_unique<MachineInstr>(RVOpcodes::LD);
|
||||
restore_ra->addOperand(std::make_unique<RegOperand>(PhysicalReg::RA));
|
||||
restore_ra->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::SP),
|
||||
std::make_unique<ImmOperand>(aligned_stack_size - 8)
|
||||
));
|
||||
epilogue_instrs.push_back(std::move(restore_ra));
|
||||
|
||||
// 2. ld s0
|
||||
auto restore_fp = std::make_unique<MachineInstr>(RVOpcodes::LD);
|
||||
restore_fp->addOperand(std::make_unique<RegOperand>(PhysicalReg::S0));
|
||||
restore_fp->addOperand(std::make_unique<MemOperand>(
|
||||
std::make_unique<RegOperand>(PhysicalReg::SP),
|
||||
std::make_unique<ImmOperand>(aligned_stack_size - 16)
|
||||
));
|
||||
epilogue_instrs.push_back(std::move(restore_fp));
|
||||
|
||||
// 3. addi sp, sp, aligned_stack_size
|
||||
auto dealloc_stack = std::make_unique<MachineInstr>(RVOpcodes::ADDI);
|
||||
dealloc_stack->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
dealloc_stack->addOperand(std::make_unique<RegOperand>(PhysicalReg::SP));
|
||||
dealloc_stack->addOperand(std::make_unique<ImmOperand>(aligned_stack_size));
|
||||
epilogue_instrs.push_back(std::move(dealloc_stack));
|
||||
|
||||
if (!epilogue_instrs.empty()) {
|
||||
mbb->getInstructions().insert(it,
|
||||
std::make_move_iterator(epilogue_instrs.begin()),
|
||||
std::make_move_iterator(epilogue_instrs.end()));
|
||||
}
|
||||
goto next_block;
|
||||
}
|
||||
}
|
||||
next_block:;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,416 +0,0 @@
|
||||
#include "PostRA_Scheduler.h"
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#define MAX_SCHEDULING_BLOCK_SIZE 10000 // 限制调度块大小,避免过大导致性能问题
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PostRA_Scheduler::ID = 0;
|
||||
|
||||
// 检查指令是否是加载指令 (LW, LD)
|
||||
bool isLoadInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::LW || opcode == RVOpcodes::LD ||
|
||||
opcode == RVOpcodes::LH || opcode == RVOpcodes::LB ||
|
||||
opcode == RVOpcodes::LHU || opcode == RVOpcodes::LBU ||
|
||||
opcode == RVOpcodes::LWU;
|
||||
}
|
||||
|
||||
// 检查指令是否是存储指令 (SW, SD)
|
||||
bool isStoreInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::SW || opcode == RVOpcodes::SD ||
|
||||
opcode == RVOpcodes::SH || opcode == RVOpcodes::SB;
|
||||
}
|
||||
|
||||
// 检查指令是否为控制流指令
|
||||
bool isControlFlowInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::RET || opcode == RVOpcodes::J ||
|
||||
opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::CALL;
|
||||
}
|
||||
|
||||
// 预计算指令信息的缓存
|
||||
static std::unordered_map<MachineInstr *, InstrRegInfo> instr_info_cache;
|
||||
|
||||
// 获取指令定义的寄存器 - 优化版本
|
||||
std::unordered_set<PhysicalReg> getDefinedRegisters(MachineInstr *instr) {
|
||||
std::unordered_set<PhysicalReg> defined_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// 特殊处理CALL指令
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
// CALL指令可能定义返回值寄存器
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 存储指令不定义寄存器
|
||||
if (isStoreInstr(instr)) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 分支指令不定义寄存器
|
||||
if (opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU ||
|
||||
opcode == RVOpcodes::J || opcode == RVOpcodes::RET) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 对于其他指令,第一个寄存器操作数通常是定义的
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 获取指令使用的寄存器 - 优化版本
|
||||
std::unordered_set<PhysicalReg> getUsedRegisters(MachineInstr *instr) {
|
||||
std::unordered_set<PhysicalReg> used_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// 特殊处理CALL指令
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
bool first_reg_skipped = false;
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (!first_reg_skipped) {
|
||||
first_reg_skipped = true;
|
||||
continue; // 跳过返回值寄存器
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于存储指令,所有寄存器操作数都是使用的
|
||||
if (isStoreInstr(instr)) {
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand *>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于分支指令,所有寄存器操作数都是使用的
|
||||
if (opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU) {
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 对于其他指令,除了第一个寄存器操作数(通常是定义),其余都是使用的
|
||||
bool first_reg = true;
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (first_reg) {
|
||||
first_reg = false;
|
||||
continue; // 跳过第一个寄存器(定义)
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (!reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getPReg());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand *>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getPReg());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 获取内存访问的基址和偏移
|
||||
|
||||
MemoryAccess getMemoryAccess(MachineInstr *instr) {
|
||||
if (!isLoadInstr(instr) && !isStoreInstr(instr)) {
|
||||
return MemoryAccess();
|
||||
}
|
||||
|
||||
// 查找内存操作数
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand *>(op.get());
|
||||
if (!mem_op->getBase()->isVirtual()) {
|
||||
return MemoryAccess(mem_op->getBase()->getPReg(),
|
||||
mem_op->getOffset()->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryAccess();
|
||||
}
|
||||
|
||||
// 预计算指令信息
|
||||
InstrRegInfo &getInstrInfo(MachineInstr *instr) {
|
||||
auto it = instr_info_cache.find(instr);
|
||||
if (it != instr_info_cache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
InstrRegInfo &info = instr_info_cache[instr];
|
||||
info.defined_regs = getDefinedRegisters(instr);
|
||||
info.used_regs = getUsedRegisters(instr);
|
||||
info.is_load = isLoadInstr(instr);
|
||||
info.is_store = isStoreInstr(instr);
|
||||
info.is_control_flow = isControlFlowInstr(instr);
|
||||
info.mem_access = getMemoryAccess(instr);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// 检查内存依赖 - 优化版本
|
||||
bool hasMemoryDependency(const InstrRegInfo &info1, const InstrRegInfo &info2) {
|
||||
// 如果都不是内存指令,没有内存依赖
|
||||
if (!info1.is_load && !info1.is_store && !info2.is_load && !info2.is_store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const MemoryAccess &mem1 = info1.mem_access;
|
||||
const MemoryAccess &mem2 = info2.mem_access;
|
||||
|
||||
if (!mem1.valid || !mem2.valid) {
|
||||
// 如果无法确定内存访问模式,保守地认为存在依赖
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果访问相同的内存位置
|
||||
if (mem1.base_reg == mem2.base_reg && mem1.offset == mem2.offset) {
|
||||
// Store->Load: RAW依赖
|
||||
// Load->Store: WAR依赖
|
||||
// Store->Store: WAW依赖
|
||||
return info1.is_store || info2.is_store;
|
||||
}
|
||||
|
||||
// 不同内存位置通常没有依赖,但为了安全起见,
|
||||
// 如果涉及store指令,我们需要更保守
|
||||
if (info1.is_store && info2.is_load) {
|
||||
// 保守处理:不同store和load之间可能有别名
|
||||
return false; // 这里可以根据需要调整策略
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查两个指令之间是否存在依赖关系 - 优化版本
|
||||
bool hasDependency(MachineInstr *instr1, MachineInstr *instr2) {
|
||||
const InstrRegInfo &info1 = getInstrInfo(instr1);
|
||||
const InstrRegInfo &info2 = getInstrInfo(instr2);
|
||||
|
||||
// 检查RAW依赖:instr1定义的寄存器是否被instr2使用
|
||||
for (const auto ® : info1.defined_regs) {
|
||||
if (info2.used_regs.find(reg) != info2.used_regs.end()) {
|
||||
return true; // RAW依赖 - instr2读取instr1写入的值
|
||||
}
|
||||
}
|
||||
|
||||
// 检查WAR依赖:instr1使用的寄存器是否被instr2定义
|
||||
for (const auto ® : info1.used_regs) {
|
||||
if (info2.defined_regs.find(reg) != info2.defined_regs.end()) {
|
||||
return true; // WAR依赖 - instr2覆盖instr1需要的值
|
||||
}
|
||||
}
|
||||
|
||||
// 检查WAW依赖:两个指令定义相同寄存器
|
||||
for (const auto ® : info1.defined_regs) {
|
||||
if (info2.defined_regs.find(reg) != info2.defined_regs.end()) {
|
||||
return true; // WAW依赖 - 两条指令写入同一寄存器
|
||||
}
|
||||
}
|
||||
|
||||
// 检查内存依赖
|
||||
if (hasMemoryDependency(info1, info2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否可以安全地将instr1和instr2交换位置 - 优化版本
|
||||
bool canSwapInstructions(MachineInstr *instr1, MachineInstr *instr2) {
|
||||
const InstrRegInfo &info1 = getInstrInfo(instr1);
|
||||
const InstrRegInfo &info2 = getInstrInfo(instr2);
|
||||
|
||||
// 不能移动控制流指令
|
||||
if (info1.is_control_flow || info2.is_control_flow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查双向依赖关系
|
||||
return !hasDependency(instr1, instr2) && !hasDependency(instr2, instr1);
|
||||
}
|
||||
|
||||
// 新增:验证调度结果的正确性 - 优化版本
|
||||
void validateSchedule(const std::vector<MachineInstr *> &instr_list) {
|
||||
for (int i = 0; i < (int)instr_list.size(); i++) {
|
||||
for (int j = i + 1; j < (int)instr_list.size(); j++) {
|
||||
MachineInstr *earlier = instr_list[i];
|
||||
MachineInstr *later = instr_list[j];
|
||||
|
||||
const InstrRegInfo &info_earlier = getInstrInfo(earlier);
|
||||
const InstrRegInfo &info_later = getInstrInfo(later);
|
||||
|
||||
// 检查是否存在被违反的依赖关系
|
||||
// 检查RAW依赖
|
||||
for (const auto ® : info_earlier.defined_regs) {
|
||||
if (info_later.used_regs.find(reg) != info_later.used_regs.end()) {
|
||||
// 这是正常的依赖关系,earlier应该在later之前
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查内存依赖
|
||||
if (hasMemoryDependency(info_earlier, info_later)) {
|
||||
const MemoryAccess &mem1 = info_earlier.mem_access;
|
||||
const MemoryAccess &mem2 = info_later.mem_access;
|
||||
|
||||
if (mem1.valid && mem2.valid && mem1.base_reg == mem2.base_reg &&
|
||||
mem1.offset == mem2.offset) {
|
||||
if (info_earlier.is_store && info_later.is_load) {
|
||||
// Store->Load依赖,顺序正确
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在基本块内对指令进行调度优化 - 优化版本
|
||||
void scheduleBlock(MachineBasicBlock *mbb) {
|
||||
auto &instructions = mbb->getInstructions();
|
||||
if (instructions.size() <= 1)
|
||||
return;
|
||||
if (instructions.size() > MAX_SCHEDULING_BLOCK_SIZE) {
|
||||
return; // 跳过超大块,防止卡住
|
||||
}
|
||||
|
||||
// 清理缓存,避免无效指针
|
||||
instr_info_cache.clear();
|
||||
|
||||
std::vector<MachineInstr *> instr_list;
|
||||
instr_list.reserve(instructions.size()); // 预分配容量
|
||||
for (auto &instr : instructions) {
|
||||
instr_list.push_back(instr.get());
|
||||
}
|
||||
|
||||
// 预计算所有指令的信息
|
||||
for (auto *instr : instr_list) {
|
||||
getInstrInfo(instr);
|
||||
}
|
||||
|
||||
// 使用更严格的调度策略,避免破坏依赖关系
|
||||
bool changed = true;
|
||||
int max_iterations = 10; // 限制迭代次数避免死循环
|
||||
int iteration = 0;
|
||||
|
||||
while (changed && iteration < max_iterations) {
|
||||
changed = false;
|
||||
iteration++;
|
||||
|
||||
for (int i = 0; i < (int)instr_list.size() - 1; i++) {
|
||||
MachineInstr *instr1 = instr_list[i];
|
||||
MachineInstr *instr2 = instr_list[i + 1];
|
||||
|
||||
const InstrRegInfo &info1 = getInstrInfo(instr1);
|
||||
const InstrRegInfo &info2 = getInstrInfo(instr2);
|
||||
|
||||
// 只进行非常保守的优化
|
||||
bool should_swap = false;
|
||||
|
||||
// 策略1: 将load指令提前,减少load-use延迟
|
||||
if (info2.is_load && !info1.is_load && !info1.is_store) {
|
||||
should_swap = canSwapInstructions(instr1, instr2);
|
||||
}
|
||||
// 策略2: 将非关键store指令延后,为其他指令让路
|
||||
else if (info1.is_store && !info2.is_load && !info2.is_store) {
|
||||
should_swap = canSwapInstructions(instr1, instr2);
|
||||
}
|
||||
|
||||
if (should_swap) {
|
||||
std::swap(instr_list[i], instr_list[i + 1]);
|
||||
changed = true;
|
||||
|
||||
// 调试输出
|
||||
// std::cout << "Swapped instructions at positions " << i << " and " <<
|
||||
// (i+1) << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证调度结果的正确性
|
||||
validateSchedule(instr_list);
|
||||
|
||||
// 将调度后的指令顺序写回
|
||||
std::unordered_map<MachineInstr *, std::unique_ptr<MachineInstr>> instr_map;
|
||||
instr_map.reserve(instructions.size()); // 预分配容量
|
||||
for (auto &instr : instructions) {
|
||||
instr_map[instr.get()] = std::move(instr);
|
||||
}
|
||||
|
||||
instructions.clear();
|
||||
instructions.reserve(instr_list.size()); // 预分配容量
|
||||
for (auto instr : instr_list) {
|
||||
instructions.push_back(std::move(instr_map[instr]));
|
||||
}
|
||||
}
|
||||
|
||||
bool PostRA_Scheduler::runOnFunction(Function *F, AnalysisManager &AM) {
|
||||
// 这个函数在IR级别运行,但我们需要在机器指令级别运行
|
||||
// 所以我们返回false,表示没有对IR进行修改
|
||||
return false;
|
||||
}
|
||||
|
||||
void PostRA_Scheduler::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
// std::cout << "Running Post-RA Local Scheduler... " << std::endl;
|
||||
|
||||
// 遍历每个机器基本块
|
||||
for (auto &mbb : mfunc->getBlocks()) {
|
||||
scheduleBlock(mbb.get());
|
||||
}
|
||||
|
||||
// 清理全局缓存
|
||||
instr_info_cache.clear();
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,466 +0,0 @@
|
||||
#include "PreRA_Scheduler.h"
|
||||
#include "RISCv64LLIR.h"
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#define MAX_SCHEDULING_BLOCK_SIZE 1000 // 严格限制调度块大小
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char PreRA_Scheduler::ID = 0;
|
||||
|
||||
// 检查指令是否是加载指令 (LW, LD)
|
||||
static bool isLoadInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::LW || opcode == RVOpcodes::LD ||
|
||||
opcode == RVOpcodes::LH || opcode == RVOpcodes::LB ||
|
||||
opcode == RVOpcodes::LHU || opcode == RVOpcodes::LBU ||
|
||||
opcode == RVOpcodes::LWU;
|
||||
}
|
||||
|
||||
// 检查指令是否是存储指令 (SW, SD)
|
||||
static bool isStoreInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::SW || opcode == RVOpcodes::SD ||
|
||||
opcode == RVOpcodes::SH || opcode == RVOpcodes::SB;
|
||||
}
|
||||
|
||||
// 检查指令是否为分支指令
|
||||
static bool isBranchInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::BEQ || opcode == RVOpcodes::BNE ||
|
||||
opcode == RVOpcodes::BLT || opcode == RVOpcodes::BGE ||
|
||||
opcode == RVOpcodes::BLTU || opcode == RVOpcodes::BGEU;
|
||||
}
|
||||
|
||||
// 检查指令是否为跳转指令
|
||||
static bool isJumpInstr(MachineInstr *instr) {
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
return opcode == RVOpcodes::J;
|
||||
}
|
||||
|
||||
// 检查指令是否为返回指令
|
||||
static bool isReturnInstr(MachineInstr *instr) {
|
||||
return instr->getOpcode() == RVOpcodes::RET;
|
||||
}
|
||||
|
||||
// 检查指令是否为调用指令
|
||||
static bool isCallInstr(MachineInstr *instr) {
|
||||
return instr->getOpcode() == RVOpcodes::CALL;
|
||||
}
|
||||
|
||||
// 检查指令是否为块终结指令(必须保持在块尾)
|
||||
static bool isTerminatorInstr(MachineInstr *instr) {
|
||||
return isBranchInstr(instr) || isJumpInstr(instr) || isReturnInstr(instr);
|
||||
}
|
||||
|
||||
// 检查指令是否有副作用(需要谨慎处理)
|
||||
static bool hasSideEffect(MachineInstr *instr) {
|
||||
return isStoreInstr(instr) || isCallInstr(instr) || isTerminatorInstr(instr);
|
||||
}
|
||||
|
||||
// 检查指令是否涉及内存操作
|
||||
static bool hasMemoryAccess(MachineInstr *instr) {
|
||||
return isLoadInstr(instr) || isStoreInstr(instr);
|
||||
}
|
||||
|
||||
// 获取内存访问位置信息
|
||||
struct MemoryLocation {
|
||||
unsigned base_reg;
|
||||
int64_t offset;
|
||||
bool is_valid;
|
||||
|
||||
MemoryLocation() : base_reg(0), offset(0), is_valid(false) {}
|
||||
MemoryLocation(unsigned base, int64_t off)
|
||||
: base_reg(base), offset(off), is_valid(true) {}
|
||||
|
||||
bool operator==(const MemoryLocation &other) const {
|
||||
return is_valid && other.is_valid && base_reg == other.base_reg &&
|
||||
offset == other.offset;
|
||||
}
|
||||
};
|
||||
|
||||
// 缓存指令分析信息
|
||||
struct InstrInfo {
|
||||
std::unordered_set<unsigned> defined_regs;
|
||||
std::unordered_set<unsigned> used_regs;
|
||||
MemoryLocation mem_location;
|
||||
bool is_load;
|
||||
bool is_store;
|
||||
bool is_terminator;
|
||||
bool is_call;
|
||||
bool has_side_effect;
|
||||
bool has_memory_access;
|
||||
|
||||
InstrInfo() : is_load(false), is_store(false), is_terminator(false),
|
||||
is_call(false), has_side_effect(false), has_memory_access(false) {}
|
||||
};
|
||||
|
||||
// 指令信息缓存
|
||||
static std::unordered_map<MachineInstr*, InstrInfo> instr_info_cache;
|
||||
|
||||
// 获取指令定义的虚拟寄存器 - 优化版本
|
||||
static std::unordered_set<unsigned> getDefinedVirtualRegisters(MachineInstr *instr) {
|
||||
std::unordered_set<unsigned> defined_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// CALL指令可能定义返回值寄存器
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 存储指令和终结指令不定义寄存器
|
||||
if (isStoreInstr(instr) || isTerminatorInstr(instr)) {
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 其他指令的第一个操作数通常是目标寄存器
|
||||
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()) {
|
||||
defined_regs.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
|
||||
return defined_regs;
|
||||
}
|
||||
|
||||
// 获取指令使用的虚拟寄存器 - 优化版本
|
||||
static std::unordered_set<unsigned> getUsedVirtualRegisters(MachineInstr *instr) {
|
||||
std::unordered_set<unsigned> used_regs;
|
||||
RVOpcodes opcode = instr->getOpcode();
|
||||
|
||||
// CALL指令:跳过第一个操作数(返回值),其余为参数
|
||||
if (opcode == RVOpcodes::CALL) {
|
||||
bool first_reg_skipped = false;
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (!first_reg_skipped) {
|
||||
first_reg_skipped = true;
|
||||
continue;
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getVRegNum());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 存储指令和终结指令:所有操作数都是使用的
|
||||
if (isStoreInstr(instr) || isTerminatorInstr(instr)) {
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getVRegNum());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand *>(op.get());
|
||||
if (mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getVRegNum());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 其他指令:跳过第一个操作数(目标寄存器),其余为源操作数
|
||||
bool first_reg = true;
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_REG) {
|
||||
if (first_reg) {
|
||||
first_reg = false;
|
||||
continue;
|
||||
}
|
||||
auto reg_op = static_cast<RegOperand *>(op.get());
|
||||
if (reg_op->isVirtual()) {
|
||||
used_regs.insert(reg_op->getVRegNum());
|
||||
}
|
||||
} else if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand *>(op.get());
|
||||
if (mem_op->getBase()->isVirtual()) {
|
||||
used_regs.insert(mem_op->getBase()->getVRegNum());
|
||||
}
|
||||
}
|
||||
}
|
||||
return used_regs;
|
||||
}
|
||||
|
||||
// 获取内存访问位置
|
||||
static MemoryLocation getMemoryLocation(MachineInstr *instr) {
|
||||
if (!isLoadInstr(instr) && !isStoreInstr(instr)) {
|
||||
return MemoryLocation();
|
||||
}
|
||||
|
||||
for (const auto &op : instr->getOperands()) {
|
||||
if (op->getKind() == MachineOperand::KIND_MEM) {
|
||||
auto mem_op = static_cast<MemOperand *>(op.get());
|
||||
if (mem_op->getBase()->isVirtual()) {
|
||||
return MemoryLocation(mem_op->getBase()->getVRegNum(),
|
||||
mem_op->getOffset()->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryLocation();
|
||||
}
|
||||
|
||||
// 预计算并缓存指令信息
|
||||
static const InstrInfo& getInstrInfo(MachineInstr *instr) {
|
||||
auto it = instr_info_cache.find(instr);
|
||||
if (it != instr_info_cache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
InstrInfo& info = instr_info_cache[instr];
|
||||
info.defined_regs = getDefinedVirtualRegisters(instr);
|
||||
info.used_regs = getUsedVirtualRegisters(instr);
|
||||
info.mem_location = getMemoryLocation(instr);
|
||||
info.is_load = isLoadInstr(instr);
|
||||
info.is_store = isStoreInstr(instr);
|
||||
info.is_terminator = isTerminatorInstr(instr);
|
||||
info.is_call = isCallInstr(instr);
|
||||
info.has_side_effect = hasSideEffect(instr);
|
||||
info.has_memory_access = hasMemoryAccess(instr);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// 检查两个内存位置是否可能别名
|
||||
static bool mayAlias(const MemoryLocation &loc1, const MemoryLocation &loc2) {
|
||||
if (!loc1.is_valid || !loc2.is_valid) {
|
||||
return true; // 保守处理:未知位置可能别名
|
||||
}
|
||||
|
||||
// 不同基址寄存器,保守假设可能别名
|
||||
if (loc1.base_reg != loc2.base_reg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 相同基址寄存器,检查偏移
|
||||
return loc1.offset == loc2.offset;
|
||||
}
|
||||
|
||||
// 检查两个指令之间是否存在数据依赖 - 优化版本
|
||||
static bool hasDataDependency(MachineInstr *first, MachineInstr *second) {
|
||||
const InstrInfo& info_first = getInstrInfo(first);
|
||||
const InstrInfo& info_second = getInstrInfo(second);
|
||||
|
||||
// RAW依赖: second读取first写入的寄存器
|
||||
for (const auto ® : info_first.defined_regs) {
|
||||
if (info_second.used_regs.find(reg) != info_second.used_regs.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// WAR依赖: second写入first读取的寄存器
|
||||
for (const auto ® : info_first.used_regs) {
|
||||
if (info_second.defined_regs.find(reg) != info_second.defined_regs.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// WAW依赖: 两个指令写入同一寄存器
|
||||
for (const auto ® : info_first.defined_regs) {
|
||||
if (info_second.defined_regs.find(reg) != info_second.defined_regs.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查两个指令之间是否存在内存依赖 - 优化版本
|
||||
static bool hasMemoryDependency(MachineInstr *first, MachineInstr *second) {
|
||||
const InstrInfo& info_first = getInstrInfo(first);
|
||||
const InstrInfo& info_second = getInstrInfo(second);
|
||||
|
||||
if (!info_first.has_memory_access || !info_second.has_memory_access) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果至少有一个是存储指令,需要检查别名
|
||||
if (info_first.is_store || info_second.is_store) {
|
||||
return mayAlias(info_first.mem_location, info_second.mem_location);
|
||||
}
|
||||
|
||||
return false; // 两个加载指令之间没有依赖
|
||||
}
|
||||
|
||||
// 检查两个指令之间是否存在控制依赖 - 优化版本
|
||||
static bool hasControlDependency(MachineInstr *first, MachineInstr *second) {
|
||||
const InstrInfo& info_first = getInstrInfo(first);
|
||||
const InstrInfo& info_second = getInstrInfo(second);
|
||||
|
||||
// 终结指令与任何其他指令都有控制依赖
|
||||
if (info_first.is_terminator) {
|
||||
return true; // first是终结指令,second不能移动到first之前
|
||||
}
|
||||
|
||||
if (info_second.is_terminator) {
|
||||
return false; // second是终结指令,可以保持在后面
|
||||
}
|
||||
|
||||
// CALL指令具有控制副作用,但可以参与有限的调度
|
||||
if (info_first.is_call || info_second.is_call) {
|
||||
// CALL指令之间保持顺序
|
||||
if (info_first.is_call && info_second.is_call) {
|
||||
return true;
|
||||
}
|
||||
// 其他情况允许调度(通过数据依赖控制)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 综合检查两个指令是否可以交换 - 优化版本
|
||||
static bool canSwapInstructions(MachineInstr *first, MachineInstr *second) {
|
||||
// 检查所有类型的依赖
|
||||
if (hasDataDependency(first, second) || hasDataDependency(second, first)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasMemoryDependency(first, second)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasControlDependency(first, second) ||
|
||||
hasControlDependency(second, first)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 找到基本块中的调度边界 - 优化版本
|
||||
static std::vector<size_t>
|
||||
findSchedulingBoundaries(const std::vector<MachineInstr *> &instrs) {
|
||||
std::vector<size_t> boundaries;
|
||||
boundaries.reserve(instrs.size() / 10); // 预估边界数量
|
||||
boundaries.push_back(0); // 起始边界
|
||||
|
||||
for (size_t i = 0; i < instrs.size(); i++) {
|
||||
const InstrInfo& info = getInstrInfo(instrs[i]);
|
||||
// 终结指令前后都是边界
|
||||
if (info.is_terminator) {
|
||||
if (i > 0)
|
||||
boundaries.push_back(i);
|
||||
if (i + 1 < instrs.size())
|
||||
boundaries.push_back(i + 1);
|
||||
}
|
||||
// 跳转目标标签也可能是边界(这里简化处理)
|
||||
}
|
||||
|
||||
boundaries.push_back(instrs.size()); // 结束边界
|
||||
|
||||
// 去重并排序
|
||||
std::sort(boundaries.begin(), boundaries.end());
|
||||
boundaries.erase(std::unique(boundaries.begin(), boundaries.end()),
|
||||
boundaries.end());
|
||||
|
||||
return boundaries;
|
||||
}
|
||||
|
||||
// 在单个调度区域内进行指令调度 - 优化版本
|
||||
static void scheduleRegion(std::vector<MachineInstr *> &instrs, size_t start,
|
||||
size_t end) {
|
||||
if (end - start <= 1) {
|
||||
return; // 区域太小,无需调度
|
||||
}
|
||||
|
||||
// 保守的调度策略:
|
||||
// 1. 只对小规模区域进行调度
|
||||
// 2. 优先将加载指令向前调度,以隐藏内存延迟
|
||||
// 3. 确保不破坏数据依赖和内存依赖
|
||||
|
||||
// 简单的调度算法:只尝试将加载指令尽可能前移
|
||||
for (size_t i = start + 1; i < end; i++) {
|
||||
const InstrInfo& info = getInstrInfo(instrs[i]);
|
||||
if (info.is_load) {
|
||||
// 尝试将加载指令向前移动
|
||||
for (size_t j = i; j > start; j--) {
|
||||
// 检查是否可以与前一条指令交换
|
||||
if (canSwapInstructions(instrs[j - 1], instrs[j])) {
|
||||
std::swap(instrs[j - 1], instrs[j]);
|
||||
} else {
|
||||
// 一旦遇到依赖关系就停止移动
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scheduleBlock(MachineBasicBlock *mbb) {
|
||||
auto &instructions = mbb->getInstructions();
|
||||
if (instructions.size() <= 1 ||
|
||||
instructions.size() > MAX_SCHEDULING_BLOCK_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理缓存,避免无效指针
|
||||
instr_info_cache.clear();
|
||||
|
||||
// 构建指令列表
|
||||
std::vector<MachineInstr *> instr_list;
|
||||
instr_list.reserve(instructions.size()); // 预分配容量
|
||||
for (auto &instr : instructions) {
|
||||
instr_list.push_back(instr.get());
|
||||
}
|
||||
|
||||
// 预计算所有指令信息
|
||||
for (auto* instr : instr_list) {
|
||||
getInstrInfo(instr);
|
||||
}
|
||||
|
||||
// 找到调度边界
|
||||
std::vector<size_t> boundaries = findSchedulingBoundaries(instr_list);
|
||||
|
||||
// 在每个调度区域内进行局部调度
|
||||
for (size_t i = 0; i < boundaries.size() - 1; i++) {
|
||||
size_t region_start = boundaries[i];
|
||||
size_t region_end = boundaries[i + 1];
|
||||
scheduleRegion(instr_list, region_start, region_end);
|
||||
}
|
||||
|
||||
// 重建指令序列
|
||||
std::unordered_map<MachineInstr *, std::unique_ptr<MachineInstr>> instr_map;
|
||||
instr_map.reserve(instructions.size()); // 预分配容量
|
||||
for (auto &instr : instructions) {
|
||||
instr_map[instr.get()] = std::move(instr);
|
||||
}
|
||||
|
||||
instructions.clear();
|
||||
instructions.reserve(instr_list.size()); // 预分配容量
|
||||
for (auto *instr : instr_list) {
|
||||
instructions.push_back(std::move(instr_map[instr]));
|
||||
}
|
||||
}
|
||||
|
||||
bool PreRA_Scheduler::runOnFunction(Function *F, AnalysisManager &AM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void PreRA_Scheduler::runOnMachineFunction(MachineFunction *mfunc) {
|
||||
for (auto &mbb : mfunc->getBlocks()) {
|
||||
scheduleBlock(mbb.get());
|
||||
}
|
||||
|
||||
// 清理全局缓存
|
||||
instr_info_cache.clear();
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,17 +0,0 @@
|
||||
# src/frontend/CMakeLists.txt
|
||||
add_library(frontend_lib STATIC
|
||||
SysYBaseVisitor.cpp
|
||||
SysY.g4
|
||||
SysYLexer.cpp
|
||||
SysYParser.cpp
|
||||
SysYVisitor.cpp
|
||||
)
|
||||
|
||||
# 包含前端模块所需的头文件路径
|
||||
target_include_directories(frontend_lib PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include/frontend # 前端头文件
|
||||
${ANTLR_RUNTIME}/runtime/src # ANTLR 运行时头文件
|
||||
)
|
||||
|
||||
# 链接 ANTLR 运行时库
|
||||
target_link_libraries(frontend_lib PRIVATE antlr4_shared)
|
||||
59
src/include/AddressCalculationExpansion.h
Normal file
59
src/include/AddressCalculationExpansion.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h" // 假设IR.h包含了Module, Function, BasicBlock, Instruction, Value, IRBuilder, Type等定义
|
||||
#include "IRBuilder.h" // 需要IRBuilder来创建新指令
|
||||
#include "SysYIRPrinter.h" // 新增: 用于调试输出
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <list> // 用于迭代和修改指令列表
|
||||
#include <algorithm> // for std::reverse (if needed, although not used in final version)
|
||||
#include <iostream> // MODIFICATION: 用于警告输出
|
||||
|
||||
namespace sysy {
|
||||
|
||||
/**
|
||||
* @brief AddressCalculationExpansion Pass
|
||||
*
|
||||
* 这是一个IR优化Pass,用于将LoadInst和StoreInst中包含的多维数组索引
|
||||
* 显式地转换为IR中的BinaryInst(乘法和加法)序列,并生成带有线性偏移量的
|
||||
* LoadInst/StoreInst。
|
||||
*
|
||||
* 目的:确保在寄存器分配之前,所有中间地址计算的结果都有明确的IR指令和对应的虚拟寄存器,
|
||||
* 从而避免在后端DAG构建时临时创建值而导致寄存器分配缺失的问题。
|
||||
*
|
||||
* SysY语言特性:
|
||||
* - 无指针类型(所有数组访问的基地址是alloca或global的AllocaType/ArrayType)
|
||||
* - 数据类型只有int和float,且都占用4字节。
|
||||
* - LoadInst和StoreInst直接接受多个索引作为额外操作数。
|
||||
*/
|
||||
class AddressCalculationExpansion {
|
||||
private:
|
||||
Module* pModule;
|
||||
IRBuilder* pBuilder; // 用于在IR中插入新指令
|
||||
|
||||
// 数组元素的固定大小,根据SysY特性,int和float都是4字节
|
||||
static const int ELEMENT_SIZE = 4;
|
||||
|
||||
// 辅助函数:根据数组的维度信息和当前索引的维度,计算该索引的步长(字节数)
|
||||
// dims: 包含所有维度大小的vector,例如 {2, 3, 4}
|
||||
// currentDimIndex: 当前正在处理的索引在 dims 中的位置 (0, 1, 2...)
|
||||
int calculateStride(const std::vector<int>& dims, size_t currentDimIndex) {
|
||||
int stride = ELEMENT_SIZE; // 最内层元素大小 (4字节)
|
||||
// 乘以当前维度之后的所有维度的大小
|
||||
for (size_t i = currentDimIndex + 1; i < dims.size(); ++i) {
|
||||
stride *= dims[i];
|
||||
}
|
||||
return stride;
|
||||
}
|
||||
|
||||
public:
|
||||
AddressCalculationExpansion(Module* module, IRBuilder* builder)
|
||||
: pModule(module), pBuilder(builder) {}
|
||||
|
||||
// 运行此Pass
|
||||
bool run();
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -16,20 +16,17 @@ public:
|
||||
const std::set<BasicBlock*>* getDominators(BasicBlock* BB) const;
|
||||
BasicBlock* getImmediateDominator(BasicBlock* BB) const;
|
||||
const std::set<BasicBlock*>* getDominanceFrontier(BasicBlock* BB) const;
|
||||
const std::set<BasicBlock*>* getDominatorTreeChildren(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);
|
||||
void computeDominatorTreeChildren(Function* F);
|
||||
private:
|
||||
Function* AssociatedFunction;
|
||||
std::map<BasicBlock*, std::set<BasicBlock*>> Dominators;
|
||||
std::map<BasicBlock*, BasicBlock*> IDoms;
|
||||
std::map<BasicBlock*, std::set<BasicBlock*>> DominanceFrontiers;
|
||||
std::map<BasicBlock*, std::set<BasicBlock*>> DominatorTreeChildren;
|
||||
};
|
||||
|
||||
|
||||
@ -359,25 +359,12 @@ public:
|
||||
|
||||
// Helper methods to access constant values with appropriate casting
|
||||
int getInt() const {
|
||||
auto val = getVal();
|
||||
if (std::holds_alternative<int>(val)) {
|
||||
return std::get<int>(val);
|
||||
} else if (std::holds_alternative<float>(val)) {
|
||||
return static_cast<int>(std::get<float>(val));
|
||||
}
|
||||
// Handle other possible types if needed
|
||||
return 0; // Default fallback
|
||||
assert(getType()->isInt() && "Calling getInt() on non-integer type");
|
||||
return std::get<int>(getVal());
|
||||
}
|
||||
|
||||
float getFloat() const {
|
||||
auto val = getVal();
|
||||
if (std::holds_alternative<float>(val)) {
|
||||
return std::get<float>(val);
|
||||
} else if (std::holds_alternative<int>(val)) {
|
||||
return static_cast<float>(std::get<int>(val));
|
||||
}
|
||||
// Handle other possible types if needed
|
||||
return 0.0f; // Default fallback
|
||||
assert(getType()->isFloat() && "Calling getFloat() on non-float type");
|
||||
return std::get<float>(getVal());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -538,10 +525,6 @@ public:
|
||||
iterator begin() { return instructions.begin(); }
|
||||
iterator end() { return instructions.end(); }
|
||||
iterator terminator() { return std::prev(end()); }
|
||||
iterator findInstIterator(Instruction *inst) {
|
||||
return std::find_if(instructions.begin(), instructions.end(),
|
||||
[inst](const std::unique_ptr<Instruction> &i) { return i.get() == inst; });
|
||||
} ///< 查找指定指令的迭代器
|
||||
bool hasSuccessor(BasicBlock *block) const {
|
||||
return std::find(successors.begin(), successors.end(), block) != successors.end();
|
||||
} ///< 判断是否有后继块
|
||||
@ -1129,10 +1112,7 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
//! 获取分配的类型
|
||||
Type* getAllocatedType() const {
|
||||
return getType()->as<PointerType>()->getBaseType();
|
||||
} ///< 获取分配的类型
|
||||
|
||||
int getNumDims() const { return getNumOperands(); }
|
||||
auto getDims() const { return getOperands(); }
|
||||
Value* getDim(int index) { return getOperand(index); }
|
||||
@ -1351,7 +1331,7 @@ protected:
|
||||
};
|
||||
|
||||
//! Global value declared at file scope
|
||||
class GlobalValue : public Value {
|
||||
class GlobalValue : public User {
|
||||
friend class Module;
|
||||
|
||||
protected:
|
||||
@ -1363,10 +1343,9 @@ protected:
|
||||
GlobalValue(Module *parent, Type *type, const std::string &name,
|
||||
const std::vector<Value *> &dims = {},
|
||||
ValueCounter init = {})
|
||||
: Value(type, name), parent(parent) {
|
||||
: User(type, name), parent(parent) {
|
||||
assert(type->isPointer());
|
||||
// addOperands(dims);
|
||||
// 维度信息已经被记录到Type中,dim只是为了方便初始化
|
||||
addOperands(dims);
|
||||
numDims = dims.size();
|
||||
if (init.size() == 0) {
|
||||
unsigned num = 1;
|
||||
@ -1386,34 +1365,20 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
// unsigned getNumDims() const { return numDims; } ///< 获取维度数量
|
||||
// Value* getDim(unsigned index) const { return getOperand(index); } ///< 获取位置为index的维度
|
||||
// auto getDims() const { return getOperands(); } ///< 获取维度列表
|
||||
unsigned getNumIndices() const {
|
||||
return numDims;
|
||||
} ///< 获取维度数量
|
||||
unsigned getIndex(unsigned index) const {
|
||||
assert(index < getNumIndices() && "Index out of bounds for GlobalValue!");
|
||||
Type *GlobalValueType = getType()->as<PointerType>()->getBaseType();
|
||||
for (unsigned i = 0; i < index; i++) {
|
||||
GlobalValueType = GlobalValueType->as<ArrayType>()->getElementType();
|
||||
}
|
||||
return GlobalValueType->as<ArrayType>()->getNumElements();
|
||||
} ///< 获取维度大小(从第0个开始)
|
||||
unsigned getNumDims() const { return numDims; } ///< 获取维度数量
|
||||
Value* getDim(unsigned index) const { return getOperand(index); } ///< 获取位置为index的维度
|
||||
auto getDims() const { return getOperands(); } ///< 获取维度列表
|
||||
Value* getByIndex(unsigned index) const {
|
||||
return initValues.getValue(index);
|
||||
} ///< 通过一维偏移量index获取初始值
|
||||
Value* getByIndices(const std::vector<Value *> &indices) const {
|
||||
Value* getByIndices(const std::vector<Value *> &indices) const {
|
||||
int index = 0;
|
||||
Type *GlobalValueType = getType()->as<PointerType>()->getBaseType();
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
// Ensure dims[i] and indices[i] are ConstantInteger and retrieve their values correctly
|
||||
// GlobalValueType->as<ArrayType>()->getNumElements();
|
||||
auto dim_val = GlobalValueType->as<ArrayType>()->getNumElements();
|
||||
auto dim_val = dynamic_cast<ConstantInteger*>(getDim(i));
|
||||
auto idx_val = dynamic_cast<ConstantInteger*>(indices[i]);
|
||||
assert(dim_val && idx_val && "Dims and indices must be constant integers");
|
||||
index = dim_val * index + idx_val->getInt();
|
||||
GlobalValueType = GlobalValueType->as<ArrayType>()->getElementType();
|
||||
index = dim_val->getInt() * index + idx_val->getInt();
|
||||
}
|
||||
return getByIndex(index);
|
||||
} ///< 通过多维索引indices获取初始值
|
||||
@ -1421,7 +1386,7 @@ public:
|
||||
}; // class GlobalValue
|
||||
|
||||
|
||||
class ConstantVariable : public Value {
|
||||
class ConstantVariable : public User {
|
||||
friend class Module;
|
||||
|
||||
protected:
|
||||
@ -1432,45 +1397,31 @@ class ConstantVariable : public Value {
|
||||
protected:
|
||||
ConstantVariable(Module *parent, Type *type, const std::string &name, const ValueCounter &init,
|
||||
const std::vector<Value *> &dims = {})
|
||||
: Value(type, name), parent(parent) {
|
||||
: User(type, name), parent(parent) {
|
||||
assert(type->isPointer());
|
||||
numDims = dims.size();
|
||||
initValues = init;
|
||||
// addOperands(dims); 同GlobalValue,维度信息已经被记录到Type中,dim只是为了方便初始化
|
||||
addOperands(dims);
|
||||
}
|
||||
|
||||
public:
|
||||
unsigned getNumIndices() const {
|
||||
return numDims;
|
||||
} ///< 获取索引数量
|
||||
unsigned getIndex(unsigned index) const {
|
||||
assert(index < getNumIndices() && "Index out of bounds for ConstantVariable!");
|
||||
Type *ConstantVariableType = getType()->as<PointerType>()->getBaseType();
|
||||
for (unsigned i = 0; i < index; i++) {
|
||||
ConstantVariableType = ConstantVariableType->as<ArrayType>()->getElementType();
|
||||
}
|
||||
return ConstantVariableType->as<ArrayType>()->getNumElements();
|
||||
} ///< 获取索引个数(从第0个开始)
|
||||
Value* getByIndex(unsigned index) const { return initValues.getValue(index); } ///< 通过一维位置index获取值
|
||||
Value* getByIndices(const std::vector<Value *> &indices) const {
|
||||
int index = 0;
|
||||
// 计算偏移量
|
||||
Type *ConstantVariableType = getType()->as<PointerType>()->getBaseType();
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
// Ensure dims[i] and indices[i] are ConstantInteger and retrieve their values correctly
|
||||
// ConstantVariableType->as<ArrayType>()->getNumElements();
|
||||
auto dim_val = ConstantVariableType->as<ArrayType>()->getNumElements();
|
||||
auto dim_val = dynamic_cast<ConstantInteger*>(getDim(i));
|
||||
auto idx_val = dynamic_cast<ConstantInteger*>(indices[i]);
|
||||
assert(dim_val && idx_val && "Dims and indices must be constant integers");
|
||||
index = dim_val * index + idx_val->getInt();
|
||||
ConstantVariableType = ConstantVariableType->as<ArrayType>()->getElementType();
|
||||
index = dim_val->getInt() * index + idx_val->getInt();
|
||||
}
|
||||
|
||||
return getByIndex(index);
|
||||
} ///< 通过多维索引indices获取初始值
|
||||
// unsigned getNumDims() const { return numDims; } ///< 获取维度数量
|
||||
// Value* getDim(unsigned index) const { return getOperand(index); } ///< 获取位置为index的维度
|
||||
// auto getDims() const { return getOperands(); } ///< 获取维度列表
|
||||
unsigned getNumDims() const { return numDims; } ///< 获取维度数量
|
||||
Value* getDim(unsigned index) const { return getOperand(index); } ///< 获取位置为index的维度
|
||||
auto getDims() const { return getOperands(); } ///< 获取维度列表
|
||||
const ValueCounter& getInitValues() const { return initValues; } ///< 获取初始值
|
||||
};
|
||||
|
||||
@ -1486,7 +1437,7 @@ class SymbolTable {
|
||||
SymbolTableNode *curNode{}; ///< 当前所在的作用域(符号表节点)
|
||||
std::map<std::string, unsigned> variableIndex; ///< 变量命名索引表
|
||||
std::vector<std::unique_ptr<GlobalValue>> globals; ///< 全局变量列表
|
||||
std::vector<std::unique_ptr<ConstantVariable>> globalconsts; ///< 全局常量列表
|
||||
std::vector<std::unique_ptr<ConstantVariable>> consts; ///< 常量列表
|
||||
std::vector<std::unique_ptr<SymbolTableNode>> nodeList; ///< 符号表节点列表
|
||||
|
||||
public:
|
||||
@ -1495,7 +1446,7 @@ class SymbolTable {
|
||||
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; ///< 获取全局常量列表
|
||||
const std::vector<std::unique_ptr<ConstantVariable>>& getConsts() const; ///< 获取常量列表
|
||||
void enterNewScope(); ///< 进入新的作用域
|
||||
void leaveScope(); ///< 离开作用域
|
||||
bool isInGlobalScope() const; ///< 是否位于全局作用域
|
||||
@ -126,7 +126,7 @@ class IRBuilder {
|
||||
UnaryInst * createFNotInst(Value *operand, const std::string &name = "") {
|
||||
return createUnaryInst(Instruction::kFNot, Type::getIntType(), operand, name);
|
||||
} ///< 创建浮点取非指令
|
||||
UnaryInst * createItoFInst(Value *operand, const std::string &name = "") {
|
||||
UnaryInst * createIToFInst(Value *operand, const std::string &name = "") {
|
||||
return createUnaryInst(Instruction::kItoF, Type::getFloatType(), operand, name);
|
||||
} ///< 创建整型转浮点指令
|
||||
UnaryInst * createBitItoFInst(Value *operand, const std::string &name = "") {
|
||||
@ -294,16 +294,7 @@ class IRBuilder {
|
||||
return inst;
|
||||
} ///< 创建store指令
|
||||
PhiInst * createPhiInst(Type *type, const std::vector<Value*> &vals = {}, const std::vector<BasicBlock*> &blks = {}, 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 PhiInst(type, vals, blks, block, newName);
|
||||
auto inst = new PhiInst(type, vals, blks, block, name);
|
||||
assert(inst);
|
||||
block->getInstructions().emplace(block->begin(), inst);
|
||||
return inst;
|
||||
@ -11,8 +11,6 @@
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h"
|
||||
|
||||
extern int DEBUG; // 全局调试标志
|
||||
|
||||
namespace sysy {
|
||||
|
||||
//前向声明
|
||||
@ -151,9 +149,6 @@ public:
|
||||
}
|
||||
AnalysisPass *analysisPass = static_cast<AnalysisPass *>(basePass.get());
|
||||
|
||||
if(DEBUG){
|
||||
std::cout << "Running Analysis Pass: " << analysisPass->getName() << "\n";
|
||||
}
|
||||
// 根据分析遍的粒度处理
|
||||
switch (analysisPass->getGranularity()) {
|
||||
case Pass::Granularity::Module: {
|
||||
@ -297,9 +292,6 @@ public:
|
||||
AnalysisManager &getAnalysisManager() { return analysisManager; }
|
||||
|
||||
void clearPasses();
|
||||
|
||||
// 输出pass列表并打印IR信息供观察优化遍效果
|
||||
void printPasses() const;
|
||||
};
|
||||
|
||||
// ======================================================================
|
||||
@ -12,26 +12,6 @@ namespace sysy {
|
||||
* * 主要目标是优化寄存器分配器插入的spill/fill代码(lw/sw),
|
||||
* 尝试将加载指令提前,以隐藏其访存延迟。
|
||||
*/
|
||||
struct MemoryAccess {
|
||||
PhysicalReg base_reg;
|
||||
int64_t offset;
|
||||
bool valid;
|
||||
|
||||
MemoryAccess() : valid(false) {}
|
||||
MemoryAccess(PhysicalReg base, int64_t off) : base_reg(base), offset(off), valid(true) {}
|
||||
};
|
||||
|
||||
struct InstrRegInfo {
|
||||
std::unordered_set<PhysicalReg> defined_regs;
|
||||
std::unordered_set<PhysicalReg> used_regs;
|
||||
bool is_load;
|
||||
bool is_store;
|
||||
bool is_control_flow;
|
||||
MemoryAccess mem_access;
|
||||
|
||||
InstrRegInfo() : is_load(false), is_store(false), is_control_flow(false) {}
|
||||
};
|
||||
|
||||
class PostRA_Scheduler : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
@ -18,12 +18,14 @@ public:
|
||||
void printInstruction(MachineInstr* instr, bool debug = false);
|
||||
// 辅助函数
|
||||
void setStream(std::ostream& os) { OS = &os; }
|
||||
// 辅助函数
|
||||
std::string regToString(PhysicalReg reg);
|
||||
private:
|
||||
// 打印各个部分
|
||||
void printPrologue();
|
||||
void printEpilogue();
|
||||
void printBasicBlock(MachineBasicBlock* mbb, bool debug = false);
|
||||
|
||||
// 辅助函数
|
||||
std::string regToString(PhysicalReg reg);
|
||||
void printOperand(MachineOperand* op);
|
||||
|
||||
MachineFunction* MFunc;
|
||||
@ -17,11 +17,8 @@ public:
|
||||
// 公开接口,以便后续模块(如RegAlloc)可以查询或创建vreg
|
||||
unsigned getVReg(Value* val);
|
||||
unsigned getNewVReg() { return vreg_counter++; }
|
||||
unsigned getNewVReg(Type* type);
|
||||
// 获取 vreg_map 的公共接口
|
||||
const std::map<Value*, unsigned>& getVRegMap() const { return vreg_map; }
|
||||
const std::map<unsigned, Value*>& getVRegValueMap() const { return vreg_to_value_map; }
|
||||
const std::map<unsigned, Type*>& getVRegTypeMap() const { return vreg_type_map; }
|
||||
|
||||
private:
|
||||
// DAG节点定义,作为ISel的内部实现细节
|
||||
@ -41,7 +38,6 @@ private:
|
||||
// 用于计算类型大小的辅助函数
|
||||
unsigned getTypeSizeInBytes(Type* type);
|
||||
|
||||
// 打印DAG图以供调试
|
||||
void print_dag(const std::vector<std::unique_ptr<DAGNode>>& dag, const std::string& bb_name);
|
||||
|
||||
// 状态
|
||||
@ -51,8 +47,6 @@ private:
|
||||
|
||||
// 映射关系
|
||||
std::map<Value*, unsigned> vreg_map;
|
||||
std::map<unsigned, Value*> vreg_to_value_map;
|
||||
std::map<unsigned, Type*> vreg_type_map;
|
||||
std::map<const BasicBlock*, MachineBasicBlock*> bb_map;
|
||||
|
||||
unsigned vreg_counter;
|
||||
@ -32,6 +32,7 @@ enum class PhysicalReg {
|
||||
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,
|
||||
@ -63,98 +64,16 @@ enum class RVOpcodes {
|
||||
CALL,
|
||||
// 特殊标记,非指令
|
||||
LABEL,
|
||||
|
||||
// 浮点指令 (RISC-V 'F' 扩展)
|
||||
// 浮点加载与存储
|
||||
FLW, // flw rd, offset(rs1)
|
||||
FSW, // fsw rs2, offset(rs1)
|
||||
FLD, // fld rd, offset(rs1)
|
||||
FSD, // fsd rs2, offset(rs1)
|
||||
|
||||
// 浮点算术运算 (单精度)
|
||||
FADD_S, // fadd.s rd, rs1, rs2
|
||||
FSUB_S, // fsub.s rd, rs1, rs2
|
||||
FMUL_S, // fmul.s rd, rs1, rs2
|
||||
FDIV_S, // fdiv.s rd, rs1, rs2
|
||||
|
||||
// 浮点比较 (单精度)
|
||||
FEQ_S, // feq.s rd, rs1, rs2 (结果写入整数寄存器rd)
|
||||
FLT_S, // flt.s rd, rs1, rs2 (less than)
|
||||
FLE_S, // fle.s rd, rs1, rs2 (less than or equal)
|
||||
|
||||
// 浮点转换
|
||||
FCVT_S_W, // fcvt.s.w rd, rs1 (有符号整数 -> 单精度浮点)
|
||||
FCVT_W_S, // fcvt.w.s rd, rs1 (单精度浮点 -> 有符号整数)
|
||||
|
||||
// 浮点传送/移动
|
||||
FMV_S, // fmv.s rd, rs1 (浮点寄存器之间)
|
||||
FMV_W_X, // fmv.w.x rd, rs1 (整数寄存器位模式 -> 浮点寄存器)
|
||||
FMV_X_W, // fmv.x.w rd, rs1 (浮点寄存器位模式 -> 整数寄存器)
|
||||
FNEG_S, // fneg.s rd, rs (浮点取负)
|
||||
|
||||
// 伪指令
|
||||
// 新增伪指令,用于解耦栈帧处理
|
||||
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_LOAD_F, // 从栈帧加载单精度浮点数
|
||||
FRAME_STORE_F, // 将单精度浮点数存入栈帧
|
||||
FRAME_ADDR, // 获取栈帧变量的地址
|
||||
PSEUDO_KEEPALIVE, // 保持寄存器活跃,防止优化器删除
|
||||
};
|
||||
|
||||
inline bool isGPR(PhysicalReg reg) {
|
||||
return reg >= PhysicalReg::ZERO && reg <= PhysicalReg::T6;
|
||||
}
|
||||
|
||||
// 判断一个物理寄存器是否是浮点寄存器 (FPR)
|
||||
inline bool isFPR(PhysicalReg reg) {
|
||||
return reg >= PhysicalReg::F0 && reg <= PhysicalReg::F31;
|
||||
}
|
||||
|
||||
// 获取所有调用者保存的整数寄存器 (t0-t6, a0-a7)
|
||||
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;
|
||||
}
|
||||
|
||||
// 获取所有被调用者保存的整数寄存器 (s0-s11)
|
||||
inline const std::vector<PhysicalReg>& getCalleeSavedIntRegs() {
|
||||
static const std::vector<PhysicalReg> regs = {
|
||||
PhysicalReg::S0, PhysicalReg::S1, PhysicalReg::S2, PhysicalReg::S3,
|
||||
PhysicalReg::S4, PhysicalReg::S5, PhysicalReg::S6, PhysicalReg::S7,
|
||||
PhysicalReg::S8, PhysicalReg::S9, PhysicalReg::S10, PhysicalReg::S11
|
||||
};
|
||||
return regs;
|
||||
}
|
||||
|
||||
// 获取所有调用者保存的浮点寄存器 (ft0-ft11, fa0-fa7)
|
||||
inline const std::vector<PhysicalReg>& getCallerSavedFpRegs() {
|
||||
static const std::vector<PhysicalReg> regs = {
|
||||
PhysicalReg::F0, PhysicalReg::F1, PhysicalReg::F2, PhysicalReg::F3,
|
||||
PhysicalReg::F4, PhysicalReg::F5, PhysicalReg::F6, PhysicalReg::F7,
|
||||
PhysicalReg::F8, PhysicalReg::F9, PhysicalReg::F10, PhysicalReg::F11, // ft0-ft11 和 fa0-fa7 在标准ABI中重叠
|
||||
PhysicalReg::F12, PhysicalReg::F13, PhysicalReg::F14, PhysicalReg::F15,
|
||||
PhysicalReg::F16, PhysicalReg::F17
|
||||
};
|
||||
return regs;
|
||||
}
|
||||
|
||||
// 获取所有被调用者保存的浮点寄存器 (fs0-fs11)
|
||||
inline const std::vector<PhysicalReg>& getCalleeSavedFpRegs() {
|
||||
static const std::vector<PhysicalReg> regs = {
|
||||
PhysicalReg::F18, PhysicalReg::F19, PhysicalReg::F20, PhysicalReg::F21,
|
||||
PhysicalReg::F22, PhysicalReg::F23, PhysicalReg::F24, PhysicalReg::F25,
|
||||
PhysicalReg::F26, PhysicalReg::F27, PhysicalReg::F28, PhysicalReg::F29,
|
||||
PhysicalReg::F30, PhysicalReg::F31
|
||||
};
|
||||
return regs;
|
||||
}
|
||||
// 定义一个全局辅助函数或常量,提供调用者保存寄存器列表
|
||||
const std::vector<PhysicalReg>& getCallerSavedIntRegs();
|
||||
|
||||
class MachineOperand;
|
||||
class RegOperand;
|
||||
@ -280,8 +199,6 @@ struct StackFrameInfo {
|
||||
std::map<unsigned, int> alloca_offsets; // <AllocaInst的vreg, 栈偏移>
|
||||
std::map<unsigned, int> spill_offsets; // <溢出vreg, 栈偏移>
|
||||
std::set<PhysicalReg> used_callee_saved_regs; // 使用的保存寄存器
|
||||
std::map<unsigned, PhysicalReg> vreg_to_preg_map;
|
||||
std::vector<PhysicalReg> callee_saved_regs; // 用于存储需要保存的被调用者保存寄存器列表
|
||||
};
|
||||
|
||||
// 机器函数
|
||||
@ -307,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
|
||||
@ -2,12 +2,10 @@
|
||||
#define RISCV64_PASSES_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Peephole.h"
|
||||
#include "RISCv64Peephole.h"
|
||||
#include "PreRA_Scheduler.h"
|
||||
#include "PostRA_Scheduler.h"
|
||||
#include "CalleeSavedHandler.h"
|
||||
#include "LegalizeImmediates.h"
|
||||
#include "PrologueEpilogueInsertion.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
@ -56,7 +56,6 @@ private:
|
||||
|
||||
// 可用的物理寄存器池
|
||||
std::vector<PhysicalReg> allocable_int_regs;
|
||||
std::vector<PhysicalReg> allocable_fp_regs;
|
||||
|
||||
// 存储vreg到IR Value*的反向映射
|
||||
// 这个map将在run()函数开始时被填充,并在rewriteFunction()中使用。
|
||||
465
src/include/SysYIRAnalyser.h
Normal file
465
src/include/SysYIRAnalyser.h
Normal file
@ -0,0 +1,465 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 前向声明
|
||||
|
||||
class Loop;
|
||||
// 基本块分析信息类
|
||||
class BlockAnalysisInfo {
|
||||
|
||||
public:
|
||||
using block_list = std::vector<BasicBlock*>;
|
||||
using block_set = std::unordered_set<BasicBlock*>;
|
||||
|
||||
protected:
|
||||
// 支配树相关
|
||||
int domdepth = 0; ///< 支配节点所在深度
|
||||
BasicBlock* idom = nullptr; ///< 直接支配结点
|
||||
block_list sdoms; ///< 支配树后继
|
||||
block_set dominants; ///< 必经结点集合
|
||||
block_set dominant_frontiers; ///< 支配边界
|
||||
|
||||
// 后续添加循环分析相关
|
||||
// Loop* loopbelong = nullptr; ///< 所属循环
|
||||
// int loopdepth = 0; ///< 循环深度
|
||||
|
||||
public:
|
||||
// getterface
|
||||
const int getDomDepth() const { return domdepth; }
|
||||
const BasicBlock* getIdom() const { return idom; }
|
||||
const block_list& getSdoms() const { return sdoms; }
|
||||
const block_set& getDominants() const { return dominants; }
|
||||
const block_set& getDomFrontiers() const { return dominant_frontiers; }
|
||||
|
||||
// 支配树操作
|
||||
void setDomDepth(int depth) { domdepth = depth; }
|
||||
void setIdom(BasicBlock* block) { idom = block; }
|
||||
void addSdoms(BasicBlock* block) { sdoms.push_back(block); }
|
||||
void clearSdoms() { sdoms.clear(); }
|
||||
void removeSdoms(BasicBlock* block) {
|
||||
sdoms.erase(std::remove(sdoms.begin(), sdoms.end(), block), sdoms.end());
|
||||
}
|
||||
void addDominants(BasicBlock* block) { dominants.emplace(block); }
|
||||
void addDominants(const block_set& blocks) { dominants.insert(blocks.begin(), blocks.end()); }
|
||||
void setDominants(BasicBlock* block) {
|
||||
dominants.clear();
|
||||
addDominants(block);
|
||||
}
|
||||
void setDominants(const block_set& doms) {
|
||||
dominants = doms;
|
||||
}
|
||||
void setDomFrontiers(const block_set& df) {
|
||||
dominant_frontiers = df;
|
||||
}
|
||||
|
||||
// TODO:循环分析操作方法
|
||||
|
||||
// 清空所有分析信息
|
||||
void clear() {
|
||||
domdepth = -1;
|
||||
idom = nullptr;
|
||||
sdoms.clear();
|
||||
dominants.clear();
|
||||
dominant_frontiers.clear();
|
||||
// loopbelong = nullptr;
|
||||
// loopdepth = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 函数分析信息类
|
||||
class FunctionAnalysisInfo {
|
||||
|
||||
|
||||
public:
|
||||
// 函数属性
|
||||
enum FunctionAttribute : uint64_t {
|
||||
PlaceHolder = 0x0UL,
|
||||
Pure = 0x1UL << 0,
|
||||
SelfRecursive = 0x1UL << 1,
|
||||
SideEffect = 0x1UL << 2,
|
||||
NoPureCauseMemRead = 0x1UL << 3
|
||||
};
|
||||
|
||||
// 数据结构
|
||||
using Loop_list = std::list<std::unique_ptr<Loop>>;
|
||||
using block_loop_map = std::unordered_map<BasicBlock*, Loop*>;
|
||||
using value_block_map = std::unordered_map<Value*, BasicBlock*>;
|
||||
using value_block_count_map = std::unordered_map<Value*, std::unordered_map<BasicBlock*, int>>;
|
||||
|
||||
// 分析数据
|
||||
FunctionAttribute attribute = PlaceHolder; ///< 函数属性
|
||||
std::set<Function*> callees; ///< 函数调用集合
|
||||
Loop_list loops; ///< 所有循环
|
||||
Loop_list topLoops; ///< 顶层循环
|
||||
// block_loop_map basicblock2Loop; ///< 基本块到循环映射
|
||||
std::list<std::unique_ptr<AllocaInst>> indirectAllocas; ///< 间接分配内存
|
||||
|
||||
// 值定义/使用信息
|
||||
value_block_map value2AllocBlocks; ///< 值分配位置映射
|
||||
value_block_count_map value2DefBlocks; ///< 值定义位置映射
|
||||
value_block_count_map value2UseBlocks; ///< 值使用位置映射
|
||||
|
||||
// 函数属性操作
|
||||
FunctionAttribute getAttribute() const { return attribute; }
|
||||
void setAttribute(FunctionAttribute attr) { attribute = static_cast<FunctionAttribute>(attribute | attr); }
|
||||
void clearAttribute() { attribute = PlaceHolder; }
|
||||
|
||||
// 调用关系操作
|
||||
void addCallee(Function* callee) { callees.insert(callee); }
|
||||
void removeCallee(Function* callee) { callees.erase(callee); }
|
||||
void clearCallees() { callees.clear(); }
|
||||
|
||||
|
||||
// 值-块映射操作
|
||||
BasicBlock* getAllocBlockByValue(Value* value) {
|
||||
auto it = value2AllocBlocks.find(value);
|
||||
return it != value2AllocBlocks.end() ? it->second : nullptr;
|
||||
}
|
||||
std::unordered_set<BasicBlock *> getDefBlocksByValue(Value *value) {
|
||||
std::unordered_set<BasicBlock *> blocks;
|
||||
if (value2DefBlocks.count(value) > 0) {
|
||||
for (const auto &pair : value2DefBlocks[value]) {
|
||||
blocks.insert(pair.first);
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
std::unordered_set<BasicBlock *> getUseBlocksByValue(Value *value) {
|
||||
std::unordered_set<BasicBlock *> blocks;
|
||||
if (value2UseBlocks.count(value) > 0) {
|
||||
for (const auto &pair : value2UseBlocks[value]) {
|
||||
blocks.insert(pair.first);
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
// 值定义/使用操作
|
||||
void addValue2AllocBlocks(Value* value, BasicBlock* block) { value2AllocBlocks[value] = block; }
|
||||
void addValue2DefBlocks(Value* value, BasicBlock* block) { ++value2DefBlocks[value][block]; }
|
||||
void addValue2UseBlocks(Value* value, BasicBlock* block) { ++value2UseBlocks[value][block]; }
|
||||
|
||||
|
||||
// 获取值定义/使用信息
|
||||
std::unordered_map<Value *, BasicBlock *>& getValue2AllocBlocks() {
|
||||
return value2AllocBlocks;
|
||||
}
|
||||
std::unordered_map<Value *, std::unordered_map<BasicBlock *, int>>& getValue2DefBlocks() {
|
||||
return value2DefBlocks;
|
||||
}
|
||||
std::unordered_map<Value *, std::unordered_map<BasicBlock *, int>>& getValue2UseBlocks() {
|
||||
return value2UseBlocks;
|
||||
}
|
||||
std::unordered_set<Value *> getValuesOfDefBlock() {
|
||||
std::unordered_set<Value *> values;
|
||||
for (const auto &pair : value2DefBlocks) {
|
||||
values.insert(pair.first);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// 删除信息操作
|
||||
void removeValue2AllocBlock(Value *value) { value2AllocBlocks.erase(value); }
|
||||
bool removeValue2DefBlock(Value *value, BasicBlock *block) {
|
||||
bool changed = false;
|
||||
if (--value2DefBlocks[value][block] == 0) {
|
||||
value2DefBlocks[value].erase(block);
|
||||
if (value2DefBlocks[value].empty()) {
|
||||
value2DefBlocks.erase(value);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
bool removeValue2UseBlock(Value *value, BasicBlock *block) {
|
||||
bool changed = false;
|
||||
if (--value2UseBlocks[value][block] == 0) {
|
||||
value2UseBlocks[value].erase(block);
|
||||
if (value2UseBlocks[value].empty()) {
|
||||
value2UseBlocks.erase(value);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
// 间接分配操作
|
||||
void addIndirectAlloca(AllocaInst* alloca) { indirectAllocas.emplace_back(alloca); }
|
||||
std::list<std::unique_ptr<AllocaInst>>& getIndirectAllocas() { return indirectAllocas; }
|
||||
|
||||
// TODO:循环分析操作
|
||||
|
||||
// 清空所有分析信息
|
||||
void clear() {
|
||||
attribute = PlaceHolder;
|
||||
callees.clear();
|
||||
loops.clear();
|
||||
topLoops.clear();
|
||||
// basicblock2Loop.clear();
|
||||
indirectAllocas.clear();
|
||||
value2AllocBlocks.clear();
|
||||
value2DefBlocks.clear();
|
||||
value2UseBlocks.clear();
|
||||
}
|
||||
};
|
||||
// 循环类 - 未实现优化
|
||||
class Loop {
|
||||
public:
|
||||
using block_list = std::vector<BasicBlock *>;
|
||||
using block_set = std::unordered_set<BasicBlock *>;
|
||||
using Loop_list = std::vector<Loop *>;
|
||||
|
||||
protected:
|
||||
Function *parent; // 所属函数
|
||||
block_list blocksInLoop; // 循环内的基本块
|
||||
BasicBlock *preheaderBlock = nullptr; // 前驱块
|
||||
BasicBlock *headerBlock = nullptr; // 循环头
|
||||
block_list latchBlock; // 回边块
|
||||
block_set exitingBlocks; // 退出块
|
||||
block_set exitBlocks; // 退出目标块
|
||||
Loop *parentloop = nullptr; // 父循环
|
||||
Loop_list subLoops; // 子循环
|
||||
size_t loopID; // 循环ID
|
||||
unsigned loopDepth; // 循环深度
|
||||
|
||||
Instruction *indCondVar = nullptr; // 循环条件变量
|
||||
Instruction::Kind IcmpKind; // 比较类型
|
||||
Value *indEnd = nullptr; // 循环结束值
|
||||
AllocaInst *IndPhi = nullptr; // 循环变量
|
||||
|
||||
ConstantValue *indBegin = nullptr; // 循环起始值
|
||||
ConstantValue *indStep = nullptr; // 循环步长
|
||||
|
||||
std::set<GlobalValue *> GlobalValuechange; // 循环内改变的全局变量
|
||||
|
||||
int StepType = 0; // 循环步长类型
|
||||
bool parallelable = false; // 是否可并行
|
||||
|
||||
public:
|
||||
explicit Loop(BasicBlock *header, const std::string &name = "")
|
||||
: headerBlock(header) {
|
||||
blocksInLoop.push_back(header);
|
||||
}
|
||||
|
||||
void setloopID() {
|
||||
static unsigned loopCount = 0;
|
||||
loopCount = loopCount + 1;
|
||||
loopID = loopCount;
|
||||
}
|
||||
ConstantValue* getindBegin() { return indBegin; }
|
||||
ConstantValue* getindStep() { return indStep; }
|
||||
void setindBegin(ConstantValue *indBegin2set) { indBegin = indBegin2set; }
|
||||
void setindStep(ConstantValue *indStep2set) { indStep = indStep2set; }
|
||||
void setStepType(int StepType2Set) { StepType = StepType2Set; }
|
||||
int getStepType() { return StepType; }
|
||||
size_t getLoopID() { return loopID; }
|
||||
|
||||
BasicBlock* getHeader() const { return headerBlock; }
|
||||
BasicBlock* getPreheaderBlock() const { return preheaderBlock; }
|
||||
block_list& getLatchBlocks() { return latchBlock; }
|
||||
block_set& getExitingBlocks() { return exitingBlocks; }
|
||||
block_set& getExitBlocks() { return exitBlocks; }
|
||||
Loop* getParentLoop() const { return parentloop; }
|
||||
void setParentLoop(Loop *parent) { parentloop = parent; }
|
||||
void addBasicBlock(BasicBlock *bb) { blocksInLoop.push_back(bb); }
|
||||
void addSubLoop(Loop *loop) { subLoops.push_back(loop); }
|
||||
void setLoopDepth(unsigned depth) { loopDepth = depth; }
|
||||
block_list& getBasicBlocks() { return blocksInLoop; }
|
||||
Loop_list& getSubLoops() { return subLoops; }
|
||||
unsigned getLoopDepth() const { return loopDepth; }
|
||||
|
||||
bool isLoopContainsBasicBlock(BasicBlock *bb) const {
|
||||
return std::find(blocksInLoop.begin(), blocksInLoop.end(), bb) != blocksInLoop.end();
|
||||
}
|
||||
|
||||
void addExitingBlock(BasicBlock *bb) { exitingBlocks.insert(bb); }
|
||||
void addExitBlock(BasicBlock *bb) { exitBlocks.insert(bb); }
|
||||
void addLatchBlock(BasicBlock *bb) { latchBlock.push_back(bb); }
|
||||
void setPreheaderBlock(BasicBlock *bb) { preheaderBlock = bb; }
|
||||
|
||||
void setIndexCondInstr(Instruction *instr) { indCondVar = instr; }
|
||||
void setIcmpKind(Instruction::Kind kind) { IcmpKind = kind; }
|
||||
Instruction::Kind getIcmpKind() const { return IcmpKind; }
|
||||
|
||||
bool isSimpleLoopInvariant(Value *value) ;
|
||||
|
||||
void setIndEnd(Value *value) { indEnd = value; }
|
||||
void setIndPhi(AllocaInst *phi) { IndPhi = phi; }
|
||||
Value* getIndEnd() const { return indEnd; }
|
||||
AllocaInst* getIndPhi() const { return IndPhi; }
|
||||
Instruction* getIndCondVar() const { return indCondVar; }
|
||||
|
||||
void addGlobalValuechange(GlobalValue *globalvaluechange2add) {
|
||||
GlobalValuechange.insert(globalvaluechange2add);
|
||||
}
|
||||
std::set<GlobalValue *>& getGlobalValuechange() {
|
||||
return GlobalValuechange;
|
||||
}
|
||||
|
||||
void setParallelable(bool flag) { parallelable = flag; }
|
||||
bool isParallelable() const { return parallelable; }
|
||||
};
|
||||
|
||||
// 控制流分析类
|
||||
class ControlFlowAnalysis {
|
||||
private:
|
||||
Module *pModule; ///< 模块
|
||||
std::unordered_map<BasicBlock*, BlockAnalysisInfo*> blockAnalysisInfo; // 基本块分析信息表
|
||||
std::unordered_map<Function*, FunctionAnalysisInfo*> functionAnalysisInfo; // 函数分析信息
|
||||
|
||||
public:
|
||||
explicit ControlFlowAnalysis(Module *pMoudle) : pModule(pMoudle) {}
|
||||
|
||||
// 获取基本块分析信息
|
||||
BlockAnalysisInfo* getBlockAnalysisInfo(BasicBlock *block) {
|
||||
auto it = blockAnalysisInfo.find(block);
|
||||
if (it != blockAnalysisInfo.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr; // 如果未找到,返回nullptr
|
||||
}
|
||||
FunctionAnalysisInfo* getFunctionAnalysisInfo(Function *func) {
|
||||
auto it = functionAnalysisInfo.find(func);
|
||||
if (it != functionAnalysisInfo.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr; // 如果未找到,返回nullptr
|
||||
}
|
||||
|
||||
void init(); // 初始化分析器
|
||||
void computeDomNode(); // 计算必经结点
|
||||
void computeDomTree(); // 构造支配树
|
||||
// std::unordered_set<BasicBlock *> computeDomFrontier(BasicBlock *block) ; // 计算单个块的支配边界(弃用)
|
||||
void computeDomFrontierAllBlk(); // 计算所有块的支配边界
|
||||
void runControlFlowAnalysis(); // 运行控制流分析(主要是支配树和支配边界)
|
||||
void clear(){
|
||||
for (auto &pair : blockAnalysisInfo) {
|
||||
delete pair.second; // 清理基本块分析信息
|
||||
}
|
||||
blockAnalysisInfo.clear();
|
||||
|
||||
for (auto &pair : functionAnalysisInfo) {
|
||||
delete pair.second; // 清理函数分析信息
|
||||
}
|
||||
functionAnalysisInfo.clear();
|
||||
} // 清空分析结果
|
||||
~ControlFlowAnalysis() {
|
||||
clear(); // 析构时清理所有分析信息
|
||||
}
|
||||
|
||||
private:
|
||||
void intersectOP4Dom(std::unordered_set<BasicBlock *> &dom, const std::unordered_set<BasicBlock *> &other); // 交集运算,
|
||||
BasicBlock* findCommonDominator(BasicBlock *a, BasicBlock *b); // 查找两个基本块的共同支配结点
|
||||
};
|
||||
|
||||
// 数据流分析类
|
||||
// 该类为抽象类,具体的数据流分析器需要继承此类
|
||||
// 因为每个数据流分析器的分析动作都不一样,所以需要继承并实现analyze方法
|
||||
class DataFlowAnalysis {
|
||||
public:
|
||||
virtual ~DataFlowAnalysis() = default;
|
||||
|
||||
public:
|
||||
virtual void init(Module *pModule) {} ///< 分析器初始化
|
||||
virtual auto analyze(Module *pModule, BasicBlock *block) -> bool { return true; } ///< 分析动作,若完成则返回true;
|
||||
virtual void clear() {} ///< 清空
|
||||
};
|
||||
|
||||
// 数据流分析工具类
|
||||
// 该类用于管理多个数据流分析器,提供统一的前向与后向分析接口
|
||||
class DataFlowAnalysisUtils {
|
||||
private:
|
||||
std::vector<DataFlowAnalysis *> forwardAnalysisList; ///< 前向分析器列表
|
||||
std::vector<DataFlowAnalysis *> backwardAnalysisList; ///< 后向分析器列表
|
||||
|
||||
public:
|
||||
DataFlowAnalysisUtils() = default;
|
||||
~DataFlowAnalysisUtils() {
|
||||
clear(); // 析构时清理所有分析器
|
||||
}
|
||||
// 统一添加接口
|
||||
void addAnalyzers(
|
||||
std::vector<DataFlowAnalysis *> forwardList,
|
||||
std::vector<DataFlowAnalysis *> backwardList = {})
|
||||
{
|
||||
forwardAnalysisList.insert(
|
||||
forwardAnalysisList.end(),
|
||||
forwardList.begin(),
|
||||
forwardList.end());
|
||||
|
||||
backwardAnalysisList.insert(
|
||||
backwardAnalysisList.end(),
|
||||
backwardList.begin(),
|
||||
backwardList.end());
|
||||
}
|
||||
|
||||
// 单独添加接口
|
||||
void addForwardAnalyzer(DataFlowAnalysis *analyzer) {
|
||||
forwardAnalysisList.push_back(analyzer);
|
||||
}
|
||||
|
||||
void addBackwardAnalyzer(DataFlowAnalysis *analyzer) {
|
||||
backwardAnalysisList.push_back(analyzer);
|
||||
}
|
||||
|
||||
// 设置分析器列表
|
||||
void setAnalyzers(
|
||||
std::vector<DataFlowAnalysis *> forwardList,
|
||||
std::vector<DataFlowAnalysis *> backwardList)
|
||||
{
|
||||
forwardAnalysisList = std::move(forwardList);
|
||||
backwardAnalysisList = std::move(backwardList);
|
||||
}
|
||||
|
||||
// 清空列表
|
||||
void clear() {
|
||||
forwardAnalysisList.clear();
|
||||
backwardAnalysisList.clear();
|
||||
}
|
||||
|
||||
// 访问器
|
||||
const auto& getForwardAnalyzers() const { return forwardAnalysisList; }
|
||||
const auto& getBackwardAnalyzers() const { return backwardAnalysisList; }
|
||||
|
||||
public:
|
||||
void forwardAnalyze(Module *pModule); ///< 执行前向分析
|
||||
void backwardAnalyze(Module *pModule); ///< 执行后向分析
|
||||
};
|
||||
|
||||
// 活跃变量分析类
|
||||
// 提供def - use分析
|
||||
// 未兼容数组变量但是考虑了维度的use信息
|
||||
class ActiveVarAnalysis : public DataFlowAnalysis {
|
||||
private:
|
||||
std::map<BasicBlock *, std::vector<std::set<User *>>> activeTable; ///< 活跃信息表,存储每个基本块内的的活跃变量信息
|
||||
|
||||
public:
|
||||
ActiveVarAnalysis() = default;
|
||||
~ActiveVarAnalysis() override = default;
|
||||
|
||||
public:
|
||||
static std::set<User*> getUsedSet(Instruction *inst);
|
||||
static User* getDefine(Instruction *inst);
|
||||
|
||||
public:
|
||||
void init(Module *pModule) override;
|
||||
bool analyze(Module *pModule, BasicBlock *block) override;
|
||||
// 外部活跃信息表访问器
|
||||
const std::map<BasicBlock *, std::vector<std::set<User *>>> &getActiveTable() const;
|
||||
void clear() override {
|
||||
activeTable.clear(); // 清空活跃信息表
|
||||
}
|
||||
};
|
||||
|
||||
// 分析管理器 后续实现
|
||||
// class AnalysisManager {
|
||||
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace sysy
|
||||
@ -59,35 +59,6 @@ private:
|
||||
std::unique_ptr<Module> module;
|
||||
IRBuilder builder;
|
||||
|
||||
using ValueOrOperator = std::variant<Value*, int>;
|
||||
std::vector<ValueOrOperator> BinaryExpStack; ///< 用于存储二元表达式的中缀表达式
|
||||
std::vector<int> BinaryExpLenStack; ///< 用于存储该层次的二元表达式的长度
|
||||
// 下面是用于后缀表达式的计算的数据结构
|
||||
std::vector<ValueOrOperator> BinaryRPNStack; ///< 用于存储二元表达式的后缀表达式
|
||||
std::vector<int> BinaryOpStack; ///< 用于存储二元表达式中缀表达式转换到后缀表达式的操作符栈
|
||||
std::vector<Value *> BinaryValueStack; ///< 用于存储后缀表达式计算的操作数栈
|
||||
|
||||
// 约定操作符:
|
||||
// 1: 'ADD', 2: 'SUB', 3: 'MUL', 4: 'DIV', 5: '%', 6: 'PLUS', 7: 'NEG', 8: 'NOT', 9: 'LPAREN', 10: 'RPAREN'
|
||||
// 这里的操作符是为了方便后缀表达式的计算而设计
|
||||
// 其中,'ADD', 'SUB', 'MUL', 'DIV', '%'
|
||||
// 分别对应加法、减法、乘法、除法和取模
|
||||
// 'PLUS' 和 'NEG' 分别对应一元加法和一元减法
|
||||
// 'NOT' 对应逻辑非
|
||||
// 'LPAREN' 和 'RPAREN' 分别对应左括号和右括号
|
||||
enum BinaryOp {
|
||||
ADD = 1, SUB = 2, MUL = 3, DIV = 4, MOD = 5, PLUS = 6, NEG = 7, NOT = 8, LPAREN = 9, RPAREN = 10,
|
||||
};
|
||||
int getOperatorPrecedence(int op) {
|
||||
switch (op) {
|
||||
case MUL: case DIV: case MOD: return 2;
|
||||
case ADD: case SUB: return 1;
|
||||
case PLUS: case NEG: case NOT: return 3;
|
||||
case LPAREN: case RPAREN: return 0; // Parentheses have lowest precedence for stack logic
|
||||
default: return -1; // Unknown operator
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
SysYIRGenerator() = default;
|
||||
|
||||
@ -126,7 +97,7 @@ public:
|
||||
std::any visitBlockStmt(SysYParser::BlockStmtContext* ctx) override;
|
||||
// std::any visitStmt(SysYParser::StmtContext *ctx) override;
|
||||
std::any visitAssignStmt(SysYParser::AssignStmtContext *ctx) override;
|
||||
std::any visitExpStmt(SysYParser::ExpStmtContext *ctx) override;
|
||||
// std::any visitExpStmt(SysYParser::ExpStmtContext *ctx) override;
|
||||
// std::any visitBlkStmt(SysYParser::BlkStmtContext *ctx) override;
|
||||
std::any visitIfStmt(SysYParser::IfStmtContext *ctx) override;
|
||||
std::any visitWhileStmt(SysYParser::WhileStmtContext *ctx) override;
|
||||
@ -160,13 +131,8 @@ public:
|
||||
std::any visitLAndExp(SysYParser::LAndExpContext *ctx) override;
|
||||
std::any visitLOrExp(SysYParser::LOrExpContext *ctx) override;
|
||||
|
||||
std::any visitConstExp(SysYParser::ConstExpContext *ctx) override;
|
||||
// std::any visitConstExp(SysYParser::ConstExpContext *ctx) override;
|
||||
|
||||
bool isRightAssociative(int op);
|
||||
Value* promoteType(Value* value, Type* targetType);
|
||||
Value* computeExp(SysYParser::ExpContext *ctx, Type* targetType = nullptr);
|
||||
Value* computeAddExp(SysYParser::AddExpContext *ctx, Type* targetType = nullptr);
|
||||
void compute();
|
||||
public:
|
||||
// 获取GEP指令的地址
|
||||
Value* getGEPAddressInst(Value* basePointer, const std::vector<Value*>& indices);
|
||||
@ -175,7 +141,6 @@ public:
|
||||
|
||||
unsigned countArrayDimensions(Type* type);
|
||||
|
||||
|
||||
}; // class SysYIRGenerator
|
||||
|
||||
} // namespace sysy
|
||||
58
src/include/SysYIRPassManager.h
Normal file
58
src/include/SysYIRPassManager.h
Normal file
@ -0,0 +1,58 @@
|
||||
// PassManager.h
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <typeindex> // For std::type_index
|
||||
#include <unordered_map>
|
||||
#include "SysYIRPass.h"
|
||||
#include "IR.h" // 假设你的IR.h定义了Module, Function等
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class PassManager {
|
||||
public:
|
||||
PassManager() = default;
|
||||
|
||||
// 添加一个FunctionPass
|
||||
void addPass(std::unique_ptr<FunctionPass> pass) {
|
||||
functionPasses.push_back(std::move(pass));
|
||||
}
|
||||
|
||||
// 添加一个ModulePass
|
||||
void addPass(std::unique_ptr<ModulePass> pass) {
|
||||
modulePasses.push_back(std::move(pass));
|
||||
}
|
||||
|
||||
// 添加一个AnalysisPass
|
||||
template<typename T, typename... Args>
|
||||
T* addAnalysisPass(Args&&... args) {
|
||||
static_assert(std::is_base_of<AnalysisPass, T>::value, "T must derive from AnalysisPass");
|
||||
auto analysis = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
T* rawPtr = analysis.get();
|
||||
analysisPasses[std::type_index(typeid(T))] = std::move(analysis);
|
||||
return rawPtr;
|
||||
}
|
||||
|
||||
// 获取分析结果(用于其他Pass访问)
|
||||
template<typename T>
|
||||
T* getAnalysis() {
|
||||
static_assert(std::is_base_of<AnalysisPass, T>::value, "T must derive from AnalysisPass");
|
||||
auto it = analysisPasses.find(std::type_index(typeid(T)));
|
||||
if (it != analysisPasses.end()) {
|
||||
return static_cast<T*>(it->second.get());
|
||||
}
|
||||
return nullptr; // 或者抛出异常
|
||||
}
|
||||
|
||||
// 运行所有注册的遍
|
||||
void run(Module& M);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<FunctionPass>> functionPasses;
|
||||
std::vector<std::unique_ptr<ModulePass>> modulePasses;
|
||||
std::unordered_map<std::type_index, std::unique_ptr<AnalysisPass>> analysisPasses;
|
||||
// 未来可以添加AnalysisPass的缓存机制
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -15,7 +15,6 @@ public:
|
||||
public:
|
||||
void printIR();
|
||||
void printGlobalVariable();
|
||||
void printGlobalConstant();
|
||||
|
||||
|
||||
public:
|
||||
@ -23,8 +22,6 @@ public:
|
||||
static void printInst(Instruction *pInst);
|
||||
static void printType(Type *type);
|
||||
static void printValue(Value *value);
|
||||
static void printBlock(BasicBlock *block);
|
||||
static std::string getBlockName(BasicBlock *block);
|
||||
static std::string getOperandName(Value *operand);
|
||||
static std::string getTypeString(Type *type);
|
||||
static std::string getValueName(Value *value);
|
||||
@ -1,36 +0,0 @@
|
||||
#ifndef SYSY_LEGALIZE_IMMEDIATES_H
|
||||
#define SYSY_LEGALIZE_IMMEDIATES_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// MachineFunction 的前向声明在这里是可选的,因为 RISCv64LLIR.h 已经定义了它
|
||||
// class MachineFunction;
|
||||
|
||||
/**
|
||||
* @class LegalizeImmediatesPass
|
||||
* @brief 一个用于“合法化”机器指令的Pass。
|
||||
*
|
||||
* 这个Pass的主要职责是遍历所有机器指令,查找那些包含了超出
|
||||
* 目标架构(RISC-V)编码范围的大立即数(immediate)的指令,
|
||||
* 并将它们展开成一个等价的、只包含合法立即数的指令序列。
|
||||
*
|
||||
* 它在指令选择之后、寄存器分配之前运行,确保进入后续阶段的
|
||||
* 所有指令都符合硬件约束。
|
||||
*/
|
||||
class LegalizeImmediatesPass : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
LegalizeImmediatesPass() : Pass("legalize-immediates", Granularity::Function, PassKind::Optimization) {}
|
||||
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
void runOnMachineFunction(MachineFunction* mfunc);
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // SYSY_LEGALIZE_IMMEDIATES_H
|
||||
@ -1,35 +0,0 @@
|
||||
#ifndef SYSY_PROLOGUE_EPILOGUE_INSERTION_H
|
||||
#define SYSY_PROLOGUE_EPILOGUE_INSERTION_H
|
||||
|
||||
#include "RISCv64LLIR.h"
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class MachineFunction;
|
||||
|
||||
/**
|
||||
* @class PrologueEpilogueInsertionPass
|
||||
* @brief 在函数中插入序言和尾声的机器指令。
|
||||
*
|
||||
* 这个Pass在所有栈帧大小计算完毕后(包括局部变量、溢出槽、被调用者保存寄存器),
|
||||
* 在寄存器分配之后运行。它的职责是:
|
||||
* 1. 根据 StackFrameInfo 中的最终栈大小,生成用于分配和释放栈帧的指令 (addi sp, sp, +/-size)。
|
||||
* 2. 生成用于保存和恢复返回地址(ra)和旧帧指针(s0)的指令。
|
||||
* 3. 将这些指令作为 MachineInstr 对象插入到 MachineFunction 的入口块和所有返回块中。
|
||||
* 4. 这个Pass可能会生成带有大立即数的指令,需要后续的 LegalizeImmediatesPass 来处理。
|
||||
*/
|
||||
class PrologueEpilogueInsertionPass : public Pass {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
PrologueEpilogueInsertionPass() : Pass("prologue-epilogue-insertion", Granularity::Function, PassKind::Optimization) {}
|
||||
|
||||
void *getPassID() const override { return &ID; }
|
||||
|
||||
void runOnMachineFunction(MachineFunction* mfunc);
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
|
||||
#endif // SYSY_PROLOGUE_EPILOGUE_INSERTION_H
|
||||
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pass.h"
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class ConstPropagation : public OptimizationPass {
|
||||
public:
|
||||
ConstPropagation() : OptimizationPass("ConstPropagation", Granularity::Function) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
static char ID;
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,118 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pass.h" // 包含Pass的基类定义
|
||||
#include "IR.h" // 包含IR相关的定义,如Instruction, Function, BasicBlock, AllocaInst, LoadInst, StoreInst, PhiInst等
|
||||
#include "Dom.h" // 假设支配树分析的头文件,提供 DominatorTreeAnalysisResult
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <stack> // 用于变量重命名阶段的SSA值栈
|
||||
|
||||
namespace sysy {
|
||||
|
||||
// 前向声明分析结果类,确保在需要时可以引用
|
||||
class DominatorTree;
|
||||
|
||||
// Mem2RegContext 类,封装 mem2reg 遍的核心逻辑和状态
|
||||
// 这样可以避免静态变量在多线程或多次运行时的冲突,并保持代码的模块化
|
||||
class Mem2RegContext {
|
||||
public:
|
||||
|
||||
Mem2RegContext(IRBuilder *builder) : builder(builder) {}
|
||||
// 运行 mem2reg 优化的主要方法
|
||||
// func: 当前要优化的函数
|
||||
// tp: 分析管理器,用于获取支配树等分析结果
|
||||
void run(Function* func, AnalysisManager* tp);
|
||||
|
||||
private:
|
||||
IRBuilder *builder; // IR 构建器,用于插入指令
|
||||
// 存储所有需要被提升的 AllocaInst
|
||||
std::vector<AllocaInst*> promotableAllocas;
|
||||
|
||||
// 存储每个 AllocaInst 对应的 Phi 指令列表
|
||||
// 键是 AllocaInst,值是该 AllocaInst 在各个基本块中插入的 Phi 指令的列表
|
||||
// (实际上,一个 AllocaInst 在一个基本块中只会有一个 Phi)
|
||||
std::unordered_map<AllocaInst*, std::unordered_map<BasicBlock*, PhiInst*>> allocaToPhiMap;
|
||||
|
||||
// 存储每个 AllocaInst 对应的当前活跃 SSA 值栈
|
||||
// 用于在变量重命名阶段追踪每个 AllocaInst 在不同控制流路径上的最新值
|
||||
std::unordered_map<AllocaInst*, std::stack<Value*>> allocaToValueStackMap;
|
||||
|
||||
// 辅助映射,存储每个 AllocaInst 的所有 store 指令
|
||||
std::unordered_map<AllocaInst*, std::unordered_set<StoreInst*>> allocaToStoresMap;
|
||||
|
||||
// 辅助映射,存储每个 AllocaInst 对应的定义基本块(包含 store 指令的块)
|
||||
std::unordered_map<AllocaInst*, std::unordered_set<BasicBlock*>> allocaToDefBlocksMap;
|
||||
|
||||
// 支配树分析结果,用于 Phi 插入和变量重命名
|
||||
DominatorTree* dt;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段1: 识别可提升的 AllocaInst
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// 判断一个 AllocaInst 是否可以被提升到寄存器
|
||||
// alloca: 要检查的 AllocaInst
|
||||
// 返回值: 如果可以提升,则为 true,否则为 false
|
||||
bool isPromotableAlloca(AllocaInst* alloca);
|
||||
|
||||
// 收集所有对给定 AllocaInst 进行存储的 StoreInst
|
||||
// alloca: 目标 AllocaInst
|
||||
void collectStores(AllocaInst* alloca);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段2: 插入 Phi 指令 (Phi Insertion)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// 为给定的 AllocaInst 插入必要的 Phi 指令
|
||||
// alloca: 目标 AllocaInst
|
||||
// defBlocks: 包含对该 AllocaInst 进行 store 操作的基本块集合
|
||||
void insertPhis(AllocaInst* alloca, const std::unordered_set<BasicBlock*>& defBlocks);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段3: 变量重命名 (Variable Renaming)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// 对支配树进行深度优先遍历,重命名变量并替换 load/store 指令
|
||||
// alloca: 当前正在处理的 AllocaInst
|
||||
// currentBB: 当前正在遍历的基本块
|
||||
// dt: 支配树分析结果
|
||||
// valueStack: 存储当前 AllocaInst 在当前路径上可见的 SSA 值栈
|
||||
void renameVariables(AllocaInst* alloca, BasicBlock* currentBB);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段4: 清理
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// 删除所有原始的 AllocaInst、LoadInst 和 StoreInst
|
||||
void cleanup();
|
||||
};
|
||||
|
||||
// Mem2Reg 优化遍类,继承自 OptimizationPass
|
||||
// 粒度为 Function,表示它在每个函数上独立运行
|
||||
class Mem2Reg : public OptimizationPass {
|
||||
private:
|
||||
IRBuilder *builder;
|
||||
|
||||
public:
|
||||
// 构造函数
|
||||
Mem2Reg(IRBuilder *builder) : OptimizationPass("Mem2Reg", Granularity::Function), builder(builder) {}
|
||||
|
||||
// 静态成员,作为该遍的唯一ID
|
||||
static void *ID;
|
||||
|
||||
// 运行在函数上的优化逻辑
|
||||
// F: 当前要优化的函数
|
||||
// AM: 分析管理器,用于获取支配树等分析结果,或使分析结果失效
|
||||
// 返回值: 如果IR被修改,则为true,否则为false
|
||||
bool runOnFunction(Function *F, AnalysisManager& AM) override;
|
||||
|
||||
// 声明该遍的分析依赖和失效信息
|
||||
// analysisDependencies: 该遍运行前需要哪些分析结果
|
||||
// analysisInvalidations: 该遍运行后会使哪些分析结果失效
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override;
|
||||
void *getPassID() const override { return &ID; }
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "IR.h"
|
||||
#include "IRBuilder.h" // 你的 IR Builder
|
||||
#include "Liveness.h"
|
||||
#include "Dom.h"
|
||||
#include "Pass.h" // 你的 Pass 框架基类
|
||||
#include <iostream> // 调试用
|
||||
#include <map> // 用于 Value 到 AllocaInst 的映射
|
||||
#include <set> // 可能用于其他辅助集合
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
class Reg2MemContext {
|
||||
public:
|
||||
Reg2MemContext(IRBuilder *b) : builder(b) {}
|
||||
|
||||
// 运行 Reg2Mem 优化
|
||||
void run(Function *func);
|
||||
|
||||
private:
|
||||
IRBuilder *builder; // IR 构建器
|
||||
|
||||
// 存储 SSA Value 到对应的 AllocaInst 的映射
|
||||
// 只有那些需要被"溢出"到内存的 SSA 值才会被记录在这里
|
||||
std::map<Value *, AllocaInst *> valueToAllocaMap;
|
||||
|
||||
// 辅助函数:
|
||||
// 1. 识别并为 SSA Value 分配 AllocaInst
|
||||
void allocateMemoryForSSAValues(Function *func);
|
||||
|
||||
// 2. 将 SSA 值的使用替换为 Load/Store
|
||||
void insertLoadsAndStores(Function *func);
|
||||
|
||||
// 3. 处理 Phi 指令,将其转换为 Load/Store
|
||||
void rewritePhis(Function *func);
|
||||
|
||||
// 4. 清理 (例如,可能删除不再需要的 Phi 指令)
|
||||
void cleanup(Function *func);
|
||||
|
||||
// 判断一个 Value 是否是 AllocaInst 可以为其分配内存的目标
|
||||
// 通常指非指针类型的Instruction结果和Argument
|
||||
bool isPromotableToMemory(Value *val);
|
||||
};
|
||||
|
||||
class Reg2Mem : public OptimizationPass {
|
||||
private:
|
||||
IRBuilder *builder; ///< IR构建器,用于插入指令
|
||||
public:
|
||||
static void *ID; ///< Pass的唯一标识符
|
||||
Reg2Mem(IRBuilder* builder) : OptimizationPass("Reg2Mem", Pass::Granularity::Function), builder(builder) {}
|
||||
bool runOnFunction(Function *F, AnalysisManager &AM) override;
|
||||
void getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const override;
|
||||
void *getPassID() const override { return &ID; } ///< 获取 Pass ID
|
||||
};
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,24 +0,0 @@
|
||||
# src/midend/CMakeLists.txt
|
||||
add_library(midend_lib STATIC
|
||||
IR.cpp
|
||||
SysYIRGenerator.cpp
|
||||
SysYIRPrinter.cpp
|
||||
Pass/Pass.cpp
|
||||
Pass/Analysis/Dom.cpp
|
||||
Pass/Analysis/Liveness.cpp
|
||||
Pass/Optimize/DCE.cpp
|
||||
Pass/Optimize/Mem2Reg.cpp
|
||||
Pass/Optimize/Reg2Mem.cpp
|
||||
Pass/Optimize/SysYIRCFGOpt.cpp
|
||||
Pass/Optimize/ConstPropagation.cpp
|
||||
)
|
||||
|
||||
# 包含中端模块所需的头文件路径
|
||||
target_include_directories(midend_lib PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include/midend # 中端顶层头文件
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include/midend/Pass # 增加 Pass 头文件路径
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include/midend/Pass/Analysis # 增加 Pass/Analysis 头文件路径
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include/midend/Pass/Optimize # 增加 Pass/Optimize 头文件路径
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../include/frontend # 增加 frontend 头文件路径 (已存在)
|
||||
${ANTLR_RUNTIME}/runtime/src # ANTLR运行时库头文件
|
||||
)
|
||||
@ -1,241 +0,0 @@
|
||||
#include "Pass/Optimize/ConstPropagation.h"
|
||||
#include "IR.h"
|
||||
#include "Pass.h"
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
|
||||
namespace sysy {
|
||||
|
||||
char ConstPropagation::ID = 0;
|
||||
|
||||
bool ConstPropagation::runOnFunction(Function *func, AnalysisManager &am) {
|
||||
bool changed = false;
|
||||
bool localChanged = true;
|
||||
|
||||
while (localChanged) {
|
||||
localChanged = false;
|
||||
|
||||
for (auto &bb : func->getBasicBlocks()) {
|
||||
for (auto instIter = bb->getInstructions().begin();
|
||||
instIter != bb->getInstructions().end();) {
|
||||
auto &inst = *instIter;
|
||||
bool shouldAdvanceIter = true;
|
||||
|
||||
// 处理二元运算指令
|
||||
if (auto *binaryInst = dynamic_cast<BinaryInst *>(inst.get())) {
|
||||
auto *lhs = binaryInst->getLhs();
|
||||
auto *rhs = binaryInst->getRhs();
|
||||
|
||||
auto *lhsConst = dynamic_cast<ConstantValue *>(lhs);
|
||||
auto *rhsConst = dynamic_cast<ConstantValue *>(rhs);
|
||||
|
||||
if (lhsConst && rhsConst) {
|
||||
ConstantValue *newConst = nullptr;
|
||||
|
||||
try {
|
||||
if (lhs->isInt() && rhs->isInt()) {
|
||||
int l = lhsConst->getInt();
|
||||
int r = rhsConst->getInt();
|
||||
int result;
|
||||
bool validOperation = true;
|
||||
|
||||
switch (binaryInst->getKind()) {
|
||||
case Instruction::kAdd:
|
||||
// 检查加法溢出
|
||||
if ((r > 0 && l > INT_MAX - r) || (r < 0 && l < INT_MIN - r)) {
|
||||
validOperation = false;
|
||||
} else {
|
||||
result = l + r;
|
||||
}
|
||||
break;
|
||||
case Instruction::kSub:
|
||||
// 检查减法溢出
|
||||
if ((r < 0 && l > INT_MAX + r) || (r > 0 && l < INT_MIN + r)) {
|
||||
validOperation = false;
|
||||
} else {
|
||||
result = l - r;
|
||||
}
|
||||
break;
|
||||
case Instruction::kMul:
|
||||
// 检查乘法溢出
|
||||
if (l != 0 && r != 0 &&
|
||||
(std::abs(l) > INT_MAX / std::abs(r))) {
|
||||
validOperation = false;
|
||||
} else {
|
||||
result = l * r;
|
||||
}
|
||||
break;
|
||||
case Instruction::kDiv:
|
||||
if (r == 0) {
|
||||
validOperation = false;
|
||||
} else {
|
||||
result = l / r;
|
||||
}
|
||||
break;
|
||||
case Instruction::kRem:
|
||||
if (r == 0) {
|
||||
validOperation = false;
|
||||
} else {
|
||||
result = l % r;
|
||||
}
|
||||
break;
|
||||
case Instruction::kICmpEQ: result = (l == r) ? 1 : 0; break;
|
||||
case Instruction::kICmpNE: result = (l != r) ? 1 : 0; break;
|
||||
case Instruction::kICmpLT: result = (l < r) ? 1 : 0; break;
|
||||
case Instruction::kICmpGT: result = (l > r) ? 1 : 0; break;
|
||||
case Instruction::kICmpLE: result = (l <= r) ? 1 : 0; break;
|
||||
case Instruction::kICmpGE: result = (l >= r) ? 1 : 0; break;
|
||||
case Instruction::kAnd: result = (l && r) ? 1 : 0; break;
|
||||
case Instruction::kOr: result = (l || r) ? 1 : 0; break;
|
||||
default:
|
||||
validOperation = false;
|
||||
}
|
||||
|
||||
if (validOperation) {
|
||||
if (binaryInst->isCmp() || binaryInst->getKind() == Instruction::kAnd ||
|
||||
binaryInst->getKind() == Instruction::kOr) {
|
||||
newConst = ConstantInteger::get(Type::getIntType(), result);
|
||||
} else {
|
||||
newConst = ConstantInteger::get(result);
|
||||
}
|
||||
}
|
||||
} else if (lhs->isFloat() && rhs->isFloat()) {
|
||||
float l = lhsConst->getFloat();
|
||||
float r = rhsConst->getFloat();
|
||||
bool validOperation = true;
|
||||
|
||||
switch (binaryInst->getKind()) {
|
||||
case Instruction::kFAdd: {
|
||||
float result = l + r;
|
||||
if (std::isfinite(result)) {
|
||||
newConst = ConstantFloating::get(result);
|
||||
} else {
|
||||
validOperation = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Instruction::kFSub: {
|
||||
float result = l - r;
|
||||
if (std::isfinite(result)) {
|
||||
newConst = ConstantFloating::get(result);
|
||||
} else {
|
||||
validOperation = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Instruction::kFMul: {
|
||||
float result = l * r;
|
||||
if (std::isfinite(result)) {
|
||||
newConst = ConstantFloating::get(result);
|
||||
} else {
|
||||
validOperation = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Instruction::kFDiv: {
|
||||
if (std::abs(r) < std::numeric_limits<float>::epsilon()) {
|
||||
validOperation = false;
|
||||
} else {
|
||||
float result = l / r;
|
||||
if (std::isfinite(result)) {
|
||||
newConst = ConstantFloating::get(result);
|
||||
} else {
|
||||
validOperation = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Instruction::kFCmpEQ:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (l == r) ? 1 : 0);
|
||||
break;
|
||||
case Instruction::kFCmpNE:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (l != r) ? 1 : 0);
|
||||
break;
|
||||
case Instruction::kFCmpLT:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (l < r) ? 1 : 0);
|
||||
break;
|
||||
case Instruction::kFCmpGT:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (l > r) ? 1 : 0);
|
||||
break;
|
||||
case Instruction::kFCmpLE:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (l <= r) ? 1 : 0);
|
||||
break;
|
||||
case Instruction::kFCmpGE:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (l >= r) ? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
validOperation = false;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// 捕获可能的异常,跳过优化
|
||||
newConst = nullptr;
|
||||
}
|
||||
|
||||
if (newConst) {
|
||||
binaryInst->replaceAllUsesWith(newConst);
|
||||
instIter = bb->getInstructions().erase(instIter);
|
||||
shouldAdvanceIter = false;
|
||||
localChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理一元运算指令
|
||||
else if (auto *unaryInst = dynamic_cast<UnaryInst *>(inst.get())) {
|
||||
auto *operand = unaryInst->getOperand();
|
||||
auto *operandConst = dynamic_cast<ConstantValue *>(operand);
|
||||
|
||||
if (operandConst) {
|
||||
ConstantValue *newConst = nullptr;
|
||||
|
||||
if (operand->isInt()) {
|
||||
int val = operandConst->getInt();
|
||||
|
||||
switch (unaryInst->getKind()) {
|
||||
case Instruction::kNeg:
|
||||
if (val != INT_MIN) { // 避免溢出
|
||||
newConst = ConstantInteger::get(-val);
|
||||
}
|
||||
break;
|
||||
case Instruction::kNot:
|
||||
newConst = ConstantInteger::get(Type::getIntType(), (!val) ? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (operand->isFloat()) {
|
||||
float val = operandConst->getFloat();
|
||||
|
||||
switch (unaryInst->getKind()) {
|
||||
case Instruction::kFNeg:
|
||||
newConst = ConstantFloating::get(-val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newConst) {
|
||||
unaryInst->replaceAllUsesWith(newConst);
|
||||
instIter = bb->getInstructions().erase(instIter);
|
||||
shouldAdvanceIter = false;
|
||||
localChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldAdvanceIter) {
|
||||
++instIter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (localChanged) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,388 +0,0 @@
|
||||
#include "Mem2Reg.h" // 包含 Mem2Reg 遍的头文件
|
||||
#include "Dom.h" // 包含支配树分析的头文件
|
||||
#include "Liveness.h"
|
||||
#include "IR.h" // 包含 IR 相关的定义
|
||||
#include "SysYIROptUtils.h"
|
||||
#include <cassert> // 用于断言
|
||||
#include <iostream> // 用于调试输出
|
||||
|
||||
namespace sysy {
|
||||
|
||||
void *Mem2Reg::ID = (void *)&Mem2Reg::ID;
|
||||
|
||||
void Mem2RegContext::run(Function *func, AnalysisManager *AM) {
|
||||
if (func->getBasicBlocks().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空所有状态,确保每次运行都是新的状态
|
||||
promotableAllocas.clear();
|
||||
allocaToPhiMap.clear();
|
||||
allocaToValueStackMap.clear();
|
||||
allocaToStoresMap.clear();
|
||||
allocaToDefBlocksMap.clear();
|
||||
|
||||
// 获取支配树分析结果
|
||||
dt = AM->getAnalysisResult<DominatorTree, DominatorTreeAnalysisPass>(func);
|
||||
assert(dt && "DominatorTreeAnalysisResult not available for Mem2Reg!");
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段1: 识别可提升的 AllocaInst 并收集其 Store 指令
|
||||
// --------------------------------------------------------------------
|
||||
// 遍历函数入口块?中的所有指令,寻找 AllocaInst
|
||||
// 必须是要入口块的吗
|
||||
for (auto &inst : func->getEntryBlock()->getInstructions_Range()) {
|
||||
Value *allocainst = inst.get();
|
||||
if (auto alloca = dynamic_cast<AllocaInst *>(allocainst)) {
|
||||
if (isPromotableAlloca(alloca)) {
|
||||
promotableAllocas.push_back(alloca);
|
||||
collectStores(alloca); // 收集所有对该 alloca 的 store
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段2: 插入 Phi 指令
|
||||
// --------------------------------------------------------------------
|
||||
for (auto alloca : promotableAllocas) {
|
||||
// 为每个可提升的 alloca 插入 Phi 指令
|
||||
insertPhis(alloca, allocaToDefBlocksMap[alloca]);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段3: 变量重命名
|
||||
// --------------------------------------------------------------------
|
||||
// 为每个可提升的 alloca 初始化其值栈
|
||||
for (auto alloca : promotableAllocas) {
|
||||
// 初始值通常是 undef 或 null,取决于 IR 类型系统
|
||||
UndefinedValue *undefValue = UndefinedValue::get(alloca->getType()->as<PointerType>()->getBaseType());
|
||||
allocaToValueStackMap[alloca].push(undefValue); // 压入一个初始的“未定义”值
|
||||
}
|
||||
|
||||
// 从入口基本块开始,对支配树进行 DFS 遍历,进行变量重命名
|
||||
renameVariables(nullptr, func->getEntryBlock()); // 第一个参数 alloca 在这里不使用,因为是递归入口点
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 阶段4: 清理
|
||||
// --------------------------------------------------------------------
|
||||
cleanup();
|
||||
}
|
||||
|
||||
// 判断一个 AllocaInst 是否可以被提升到寄存器
|
||||
bool Mem2RegContext::isPromotableAlloca(AllocaInst *alloca) {
|
||||
// 1. 必须是标量类型(非数组、非结构体)sysy不支持结构体
|
||||
if (alloca->getType()->as<PointerType>()->getBaseType()->isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 其所有用途都必须是 LoadInst 或 StoreInst
|
||||
// (或 GetElementPtrInst,但 GEP 的结果也必须只被 Load/Store 使用)
|
||||
for (auto use : alloca->getUses()) {
|
||||
auto user = use->getUser();
|
||||
if (!user)
|
||||
return false; // 用户无效
|
||||
|
||||
if (dynamic_cast<LoadInst *>(user)) {
|
||||
// OK
|
||||
} else if (dynamic_cast<StoreInst *>(user)) {
|
||||
// OK
|
||||
} else if (auto gep = dynamic_cast<GetElementPtrInst *>(user)) {
|
||||
// 如果是 GetElementPtrInst (GEP)
|
||||
// 需要判断这个 GEP 是否代表了数组元素的访问,而非简单的指针操作
|
||||
// LLVM 的 mem2reg 通常不提升用于数组元素访问的 alloca。
|
||||
// 启发式判断:
|
||||
// 如果 GEP 有多个索引(例如 `getelementptr i32, i32* %ptr, i32 0, i32 %idx`),
|
||||
// 或者第一个索引(对于指针类型)不是常量 0,则很可能是数组访问。
|
||||
// 对于 `alloca i32* %a.param` (对应 `int a[]` 参数),其 `allocatedType()` 是 `i32*`。
|
||||
// 访问 `a[i]` 会生成类似 `getelementptr i32, i32* %a.param, i32 %i` 的 GEP。
|
||||
// 这种 GEP 有两个操作数:基指针和索引。
|
||||
|
||||
// 检查 GEP 的操作数数量和索引值
|
||||
// GEP 的操作数通常是:<base_pointer>, <index_1>, <index_2>, ...
|
||||
// 对于一个 `i32*` 类型的 `alloca`,如果它被 GEP 使用,那么 GEP 的第一个索引通常是 `0`
|
||||
// (表示解引用指针本身),后续索引才是数组元素的索引。
|
||||
// 如果 GEP 的操作数数量大于 2 (即 `base_ptr` 和 `index_0` 之外还有其他索引),
|
||||
// 或者 `index_0` 不是常量 0,则它可能是一个复杂的数组访问。
|
||||
// 假设 `gep->getNumOperands()` 和 `gep->getOperand(idx)->getValue()`
|
||||
// 假设 `ConstantInt` 类用于表示常量整数值
|
||||
if (gep->getNumOperands() > 2) { // 如果有超过一个索引(除了基指针的第一个隐式索引)
|
||||
// std::cerr << "Mem2Reg: Not promotable (GEP with multiple indices): " << alloca->name() << std::endl;
|
||||
return false; // 复杂 GEP,通常表示数组或结构体字段访问
|
||||
}
|
||||
if (gep->getNumOperands() == 2) { // 只有基指针和一个索引
|
||||
Value *firstIndexVal = gep->getOperand(1); // 获取第一个索引值
|
||||
if (auto constInt = dynamic_cast<ConstantInteger *>(firstIndexVal)) {
|
||||
if (constInt->getInt() != 0) {
|
||||
// std::cerr << "Mem2Reg: Not promotable (GEP with non-zero first index): " << alloca->name() << std::endl;
|
||||
return false; // 索引不是0,表示访问数组的非第一个元素
|
||||
}
|
||||
} else {
|
||||
// std::cerr << "Mem2Reg: Not promotable (GEP with non-constant first index): " << alloca->name() <<
|
||||
// std::endl;
|
||||
return false; // 索引不是常量,表示动态数组访问
|
||||
}
|
||||
}
|
||||
|
||||
// 此外,GEP 的结果也必须只被 LoadInst 或 StoreInst 使用
|
||||
for (auto gep_use : gep->getUses()) {
|
||||
auto gep_user = gep_use->getUser();
|
||||
if (!gep_user) {
|
||||
// std::cerr << "Mem2Reg: Not promotable (GEP result null user): " << alloca->name() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!dynamic_cast<LoadInst *>(gep_user) && !dynamic_cast<StoreInst *>(gep_user)) {
|
||||
// std::cerr << "Mem2Reg: Not promotable (GEP result used by non-load/store): " << alloca->name() <<
|
||||
// std::endl;
|
||||
return false; // GEP 结果被其他指令使用,地址逃逸或复杂用途
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 其他类型的用户,如 CallInst (如果地址逃逸),则不能提升
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 3. 不能是 volatile 内存访问 (假设 AllocaInst 有 isVolatile() 方法)
|
||||
// if (alloca->isVolatile()) return false; // 如果有这样的属性
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 收集所有对给定 AllocaInst 进行存储的 StoreInst
|
||||
void Mem2RegContext::collectStores(AllocaInst *alloca) {
|
||||
// 遍历 alloca 的所有用途
|
||||
for (auto use : alloca->getUses()) {
|
||||
auto user = use->getUser();
|
||||
if (!user)
|
||||
continue;
|
||||
|
||||
if (auto storeInst = dynamic_cast<StoreInst *>(user)) {
|
||||
allocaToStoresMap[alloca].insert(storeInst);
|
||||
allocaToDefBlocksMap[alloca].insert(storeInst->getParent());
|
||||
} else if (auto gep = dynamic_cast<GetElementPtrInst *>(user)) {
|
||||
// 如果是 GEP,递归收集其下游的 store
|
||||
for (auto gep_use : gep->getUses()) {
|
||||
if (auto gep_store = dynamic_cast<StoreInst *>(gep_use->getUser())) {
|
||||
allocaToStoresMap[alloca].insert(gep_store);
|
||||
allocaToDefBlocksMap[alloca].insert(gep_store->getParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 为给定的 AllocaInst 插入必要的 Phi 指令
|
||||
void Mem2RegContext::insertPhis(AllocaInst *alloca, const std::unordered_set<BasicBlock *> &defBlocks) {
|
||||
std::queue<BasicBlock *> workQueue;
|
||||
std::unordered_set<BasicBlock *> phiHasBeenInserted; // 记录已插入 Phi 的基本块
|
||||
|
||||
// 将所有定义块加入工作队列
|
||||
for (auto bb : defBlocks) {
|
||||
workQueue.push(bb);
|
||||
}
|
||||
|
||||
while (!workQueue.empty()) {
|
||||
BasicBlock *currentDefBlock = workQueue.front();
|
||||
workQueue.pop();
|
||||
|
||||
// 遍历当前定义块的支配边界 (Dominance Frontier)
|
||||
const std::set<BasicBlock *> *frontierBlocks = dt->getDominanceFrontier(currentDefBlock);
|
||||
for (auto frontierBlock : *frontierBlocks) {
|
||||
// 如果该支配边界块还没有为当前 alloca 插入 Phi 指令
|
||||
if (phiHasBeenInserted.find(frontierBlock) == phiHasBeenInserted.end()) {
|
||||
// 在支配边界块的开头插入一个新的 Phi 指令
|
||||
// Phi 指令的类型与 alloca 的类型指向的类型相同
|
||||
|
||||
builder->setPosition(frontierBlock, frontierBlock->begin()); // 设置插入位置为基本块开头
|
||||
PhiInst *phiInst = builder->createPhiInst(alloca->getAllocatedType(), {}, {}, "");
|
||||
|
||||
allocaToPhiMap[alloca][frontierBlock] = phiInst; // 记录 Phi 指令
|
||||
|
||||
phiHasBeenInserted.insert(frontierBlock); // 标记已插入 Phi
|
||||
|
||||
// 如果这个支配边界块本身也是一个定义块(即使没有 store,但插入了 Phi),
|
||||
// 那么它的支配边界也可能需要插入 Phi
|
||||
// 例如一个xx型的cfg,如果在第一个交叉处插入phi节点,那么第二个交叉处可能也需要插入phi
|
||||
workQueue.push(frontierBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对支配树进行深度优先遍历,重命名变量并替换 load/store 指令
|
||||
void Mem2RegContext::renameVariables(AllocaInst *currentAlloca, BasicBlock *currentBB) {
|
||||
// 维护一个局部栈,用于存储当前基本块中为 Phi 和 Store 创建的 SSA 值,以便在退出时弹出
|
||||
std::stack<Value *> localStackPushed;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 处理当前基本块的指令
|
||||
// --------------------------------------------------------------------
|
||||
for (auto instIter = currentBB->getInstructions().begin(); instIter != currentBB->getInstructions().end();) {
|
||||
Instruction *inst = instIter->get();
|
||||
bool instDeleted = false;
|
||||
|
||||
// 处理 Phi 指令 (如果是当前 alloca 的 Phi)
|
||||
if (auto phiInst = dynamic_cast<PhiInst *>(inst)) {
|
||||
// 检查这个 Phi 是否是为某个可提升的 alloca 插入的
|
||||
for (auto alloca : promotableAllocas) {
|
||||
if (allocaToPhiMap[alloca].count(currentBB) && allocaToPhiMap[alloca][currentBB] == phiInst) {
|
||||
// 为 Phi 指令的输出创建一个新的 SSA 值,并压入值栈
|
||||
allocaToValueStackMap[alloca].push(phiInst);
|
||||
localStackPushed.push(phiInst); // 记录以便弹出
|
||||
break; // 找到对应的 alloca,处理下一个指令
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理 LoadInst
|
||||
else if (auto loadInst = dynamic_cast<LoadInst *>(inst)) {
|
||||
// 检查这个 LoadInst 是否是为某个可提升的 alloca
|
||||
for (auto alloca : promotableAllocas) {
|
||||
if (loadInst->getPointer() == alloca) {
|
||||
// loadInst->getPointer() 返回 AllocaInst*
|
||||
// 将 LoadInst 的所有用途替换为当前 alloca 值栈顶部的 SSA 值
|
||||
assert(!allocaToValueStackMap[alloca].empty() && "Value stack empty for alloca during load replacement!");
|
||||
loadInst->replaceAllUsesWith(allocaToValueStackMap[alloca].top());
|
||||
// instIter = currentBB->force_delete_inst(loadInst); // 删除 LoadInst
|
||||
SysYIROptUtils::usedelete(loadInst); // 仅删除 use 关系
|
||||
instIter = currentBB->getInstructions().erase(instIter); // 删除 LoadInst
|
||||
instDeleted = true;
|
||||
// std::cerr << "Mem2Reg: Replaced load " << loadInst->name() << " with SSA value." << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理 StoreInst
|
||||
else if (auto storeInst = dynamic_cast<StoreInst *>(inst)) {
|
||||
// 检查这个 StoreInst 是否是为某个可提升的 alloca
|
||||
for (auto alloca : promotableAllocas) {
|
||||
if (storeInst->getPointer() == alloca) {
|
||||
// 假设 storeInst->getPointer() 返回 AllocaInst*
|
||||
// 将 StoreInst 存储的值作为新的 SSA 值,压入值栈
|
||||
allocaToValueStackMap[alloca].push(storeInst->getValue());
|
||||
localStackPushed.push(storeInst->getValue()); // 记录以便弹出
|
||||
SysYIROptUtils::usedelete(storeInst);
|
||||
instIter = currentBB->getInstructions().erase(instIter); // 删除 StoreInst
|
||||
instDeleted = true;
|
||||
// std::cerr << "Mem2Reg: Replaced store to " << storeInst->ptr()->name() << " with SSA value." << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!instDeleted) {
|
||||
++instIter; // 如果指令没有被删除,移动到下一个
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 处理后继基本块的 Phi 指令参数
|
||||
// --------------------------------------------------------------------
|
||||
for (auto successorBB : currentBB->getSuccessors()) {
|
||||
if (!successorBB)
|
||||
continue;
|
||||
for (auto alloca : promotableAllocas) {
|
||||
// 如果后继基本块包含为当前 alloca 插入的 Phi 指令
|
||||
if (allocaToPhiMap[alloca].count(successorBB)) {
|
||||
auto phiInst = allocaToPhiMap[alloca][successorBB];
|
||||
// 为 Phi 指令添加来自当前基本块的参数
|
||||
// 参数值是当前 alloca 值栈顶部的 SSA 值
|
||||
assert(!allocaToValueStackMap[alloca].empty() && "Value stack empty for alloca when setting phi operand!");
|
||||
phiInst->addIncoming(allocaToValueStackMap[alloca].top(), currentBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 递归访问支配树的子节点
|
||||
// --------------------------------------------------------------------
|
||||
const std::set<BasicBlock *> *dominatedBlocks = dt->getDominatorTreeChildren(currentBB);
|
||||
if(dominatedBlocks){
|
||||
for (auto dominatedBB : *dominatedBlocks) {
|
||||
if (dominatedBB) {
|
||||
std::cout << "Mem2Reg: Recursively renaming variables in dominated block: " << dominatedBB->getName() << std::endl;
|
||||
renameVariables(currentAlloca, dominatedBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// 退出基本块时,弹出在此块中压入值栈的 SSA 值
|
||||
// --------------------------------------------------------------------
|
||||
while (!localStackPushed.empty()) {
|
||||
Value *val = localStackPushed.top();
|
||||
localStackPushed.pop();
|
||||
// 找到是哪个 alloca 对应的栈
|
||||
for (auto alloca : promotableAllocas) {
|
||||
if (!allocaToValueStackMap[alloca].empty() && allocaToValueStackMap[alloca].top() == val) {
|
||||
allocaToValueStackMap[alloca].pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除所有原始的 AllocaInst、LoadInst 和 StoreInst
|
||||
void Mem2RegContext::cleanup() {
|
||||
for (auto alloca : promotableAllocas) {
|
||||
if (alloca && alloca->getParent()) {
|
||||
// 删除 alloca 指令本身
|
||||
SysYIROptUtils::usedelete(alloca);
|
||||
alloca->getParent()->removeInst(alloca); // 从基本块中删除 alloca
|
||||
|
||||
// std::cerr << "Mem2Reg: Deleted alloca " << alloca->name() << std::endl;
|
||||
}
|
||||
}
|
||||
// LoadInst 和 StoreInst 已经在 renameVariables 阶段被删除了
|
||||
}
|
||||
|
||||
// Mem2Reg 遍的 runOnFunction 方法实现
|
||||
bool Mem2Reg::runOnFunction(Function *F, AnalysisManager &AM) {
|
||||
// 记录初始的指令数量,用于判断优化是否发生了改变
|
||||
size_t initial_inst_count = 0;
|
||||
for (auto &bb : F->getBasicBlocks()) {
|
||||
initial_inst_count += bb->getInstructions().size();
|
||||
}
|
||||
|
||||
Mem2RegContext ctx(builder);
|
||||
ctx.run(F, &AM); // 运行 Mem2Reg 优化
|
||||
|
||||
// 运行优化后,再次计算指令数量
|
||||
size_t final_inst_count = 0;
|
||||
for (auto &bb : F->getBasicBlocks()) {
|
||||
final_inst_count += bb->getInstructions().size();
|
||||
}
|
||||
|
||||
// 如果指令数量发生变化(通常是减少,因为 load/store 被删除,phi 被添加),说明 IR 被修改了
|
||||
// TODO:不保险,后续修改为更精确的判断
|
||||
// 直接在添加和删除指令时维护changed值
|
||||
bool changed = (initial_inst_count != final_inst_count);
|
||||
|
||||
// 如果 IR 被修改,则使相关的分析结果失效
|
||||
if (changed) {
|
||||
// Mem2Reg 会显著改变 IR 结构,特别是数据流和控制流(通过 Phi)。
|
||||
// 这会使几乎所有数据流分析和部分控制流分析失效。
|
||||
// AM.invalidateAnalysis(&DominatorTreeAnalysisPass::ID, F); // 支配树可能间接改变(如果基本块被删除)
|
||||
// AM.invalidateAnalysis(&LivenessAnalysisPass::ID, F); // 活跃性分析肯定失效
|
||||
// AM.invalidateAnalysis(&LoopInfoAnalysisPass::ID, F); // 循环信息可能失效
|
||||
// AM.invalidateAnalysis(&SideEffectInfoAnalysisPass::ID); // 副作用分析可能失效(如果 Alloca/Load/Store
|
||||
// 被替换为寄存器)
|
||||
// ... 其他数据流分析,如到达定义、可用表达式等,也应失效
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
// 声明Mem2Reg遍的分析依赖和失效信息
|
||||
void Mem2Reg::getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const {
|
||||
// Mem2Reg 强烈依赖于支配树分析来插入 Phi 指令
|
||||
analysisDependencies.insert(&DominatorTreeAnalysisPass::ID); // 假设 DominatorTreeAnalysisPass 的 ID
|
||||
|
||||
// Mem2Reg 会删除 Alloca/Load/Store 指令,插入 Phi 指令,这会大幅改变 IR 结构。
|
||||
// 因此,它会使许多分析结果失效。
|
||||
analysisInvalidations.insert(&DominatorTreeAnalysisPass::ID); // 支配树可能受影响
|
||||
analysisInvalidations.insert(&LivenessAnalysisPass::ID); // 活跃性分析肯定失效
|
||||
// analysisInvalidations.insert(&LoopInfoAnalysisPass::ID); // 循环信息可能失效
|
||||
// analysisInvalidations.insert(&SideEffectInfoAnalysisPass::ID); // 副作用分析可能失效
|
||||
// 其他所有依赖于数据流或 IR 结构的分析都可能失效。
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -1,289 +0,0 @@
|
||||
#include "Reg2Mem.h"
|
||||
#include "SysYIROptUtils.h"
|
||||
#include "SysYIRPrinter.h"
|
||||
|
||||
extern int DEBUG; // 全局调试标志
|
||||
|
||||
namespace sysy {
|
||||
|
||||
void *Reg2Mem::ID = (void *)&Reg2Mem::ID;
|
||||
|
||||
void Reg2MemContext::run(Function *func) {
|
||||
if (func->getBasicBlocks().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空状态,确保每次运行都是新的
|
||||
valueToAllocaMap.clear();
|
||||
|
||||
// 阶段1: 识别并为 SSA Value 分配 AllocaInst
|
||||
allocateMemoryForSSAValues(func);
|
||||
|
||||
// 阶段2: 将 Phi 指令转换为 Load/Store 逻辑 (此阶段需要先于通用 Load/Store 插入)
|
||||
// 这样做是因为 Phi 指令的特殊性,它需要在前驱块的末尾插入 Store
|
||||
// 如果先处理通用 Load/Store,可能无法正确处理 Phi 的复杂性
|
||||
rewritePhis(func); // Phi 指令可能在 rewritePhis 中被删除或标记删除
|
||||
|
||||
// 阶段3: 将其他 SSA Value 的使用替换为 Load/Store
|
||||
insertLoadsAndStores(func);
|
||||
|
||||
// 阶段4: 清理(删除不再需要的 Phi 指令)
|
||||
cleanup(func);
|
||||
}
|
||||
|
||||
bool Reg2MemContext::isPromotableToMemory(Value *val) {
|
||||
// 参数和指令结果是 SSA 值
|
||||
if(DEBUG){
|
||||
// if(val->getName() == ""){
|
||||
// assert(false && "Value name should not be empty in Reg2MemContext::isPromotableToMemory");
|
||||
// }
|
||||
// std::cout << "Checking if value is promotable to memory: " << val->getName() << std::endl;
|
||||
}
|
||||
// if (dynamic_cast<Argument *>(val) || dynamic_cast<Instruction *>(val)) {
|
||||
// // 如果值已经是指针类型,则通常不为其分配额外的内存,因为它已经是一个地址。
|
||||
// // (除非我们想将其值也存储起来,这通常不用于 Reg2Mem)
|
||||
// // // Reg2Mem 关注的是将非指针值从寄存器语义转换为内存语义。
|
||||
// if (val->getType()->isPointer()) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
// 1. 如果是 Argument,则可以提升到内存
|
||||
if (dynamic_cast<Argument *>(val)) {
|
||||
// 参数类型(i32, i32* 等)都可以为其分配内存
|
||||
// 因为它们在 Mem2Reg 逆操作中,被认为是从寄存器分配到内存
|
||||
return true;
|
||||
}
|
||||
if (dynamic_cast<PhiInst *>(val)) {
|
||||
// Phi 指令的结果也是一个 SSA 值,需要将其转换为 Load/Store
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Reg2MemContext::allocateMemoryForSSAValues(Function *func) {
|
||||
// AllocaInst 必须在函数的入口基本块中
|
||||
BasicBlock *entryBlock = func->getEntryBlock();
|
||||
if (!entryBlock) {
|
||||
return; // 函数可能没有入口块 (例如声明)
|
||||
}
|
||||
|
||||
// 1. 为函数参数分配内存
|
||||
builder->setPosition(entryBlock, entryBlock->begin()); // 确保在入口块的开始位置插入
|
||||
for (auto arg : func->getArguments()) {
|
||||
// 默认情况下,将所有参数是提升到内存
|
||||
if (isPromotableToMemory(arg)) {
|
||||
// 参数的类型就是 AllocaInst 需要分配的类型
|
||||
AllocaInst *alloca = builder->createAllocaInst(Type::getPointerType(arg->getType()), {}, arg->getName() + ".reg2mem");
|
||||
// 将参数值 store 到 alloca 中 (这是 Mem2Reg 逆转的关键一步)
|
||||
valueToAllocaMap[arg] = alloca;
|
||||
|
||||
// 确保 alloca 位于入口块的顶部,但在所有参数的 store 指令之前
|
||||
// 通常 alloca 都在 entry block 的最开始
|
||||
// 这里我们只是创建,并让 builder 决定插入位置 (通常在当前插入点)
|
||||
// 如果需要严格控制顺序,可能需要手动 insert 到 instruction list
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 为指令结果分配内存
|
||||
// 遍历所有基本块和指令,找出所有需要分配 Alloca 的指令结果
|
||||
for (auto &bb : func->getBasicBlocks()) {
|
||||
for (auto &inst : bb->getInstructions_Range()) {
|
||||
// SysYPrinter::printInst(inst.get());
|
||||
// 只有有结果的指令才可能需要分配内存
|
||||
// (例如 BinaryInst, CallInst, LoadInst, PhiInst 等)
|
||||
// StoreInst, BranchInst, ReturnInst 等没有结果的指令不需要
|
||||
|
||||
if (dynamic_cast<AllocaInst*>(inst.get()) || inst.get()->getType()->isVoid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPromotableToMemory(inst.get())) {
|
||||
// 为指令的结果分配内存
|
||||
// AllocaInst 应该在入口块,而不是当前指令所在块
|
||||
// 这里我们只是创建,并稍后调整其位置
|
||||
// 通常的做法是在循环结束后统一将 alloca 放到 entryBlock 的顶部
|
||||
AllocaInst *alloca = builder->createAllocaInst(Type::getPointerType(inst.get()->getType()), {}, inst.get()->getName() + ".reg2mem");
|
||||
valueToAllocaMap[inst.get()] = alloca;
|
||||
}
|
||||
}
|
||||
}
|
||||
Instruction *firstNonAlloca = nullptr;
|
||||
for (auto instIter = entryBlock->getInstructions().begin(); instIter != entryBlock->getInstructions().end(); instIter++) {
|
||||
if (!dynamic_cast<AllocaInst*>(instIter->get())) {
|
||||
firstNonAlloca = instIter->get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstNonAlloca) {
|
||||
builder->setPosition(entryBlock, entryBlock->findInstIterator(firstNonAlloca));
|
||||
} else { // 如果 entryBlock 只有 AllocaInst 或为空,则设置到 terminator 前
|
||||
builder->setPosition(entryBlock, entryBlock->terminator());
|
||||
}
|
||||
|
||||
// 插入所有参数的初始 Store 指令
|
||||
for (auto arg : func->getArguments()) {
|
||||
if (valueToAllocaMap.count(arg)) { // 检查是否为其分配了 alloca
|
||||
builder->createStoreInst(arg, valueToAllocaMap[arg]);
|
||||
}
|
||||
}
|
||||
|
||||
builder->setPosition(entryBlock, entryBlock->terminator());
|
||||
}
|
||||
|
||||
void Reg2MemContext::rewritePhis(Function *func) {
|
||||
std::vector<PhiInst *> phisToErase; // 收集要删除的 Phi
|
||||
|
||||
// 遍历所有基本块和其中的指令,查找 Phi 指令
|
||||
for (auto &bb : func->getBasicBlocks()) {
|
||||
// auto insts = bb->getInstructions(); // 复制一份,因为要修改
|
||||
for (auto instIter = bb->getInstructions().begin(); instIter != bb->getInstructions().end(); instIter++) {
|
||||
Instruction *inst = instIter->get();
|
||||
if (auto phiInst = dynamic_cast<PhiInst *>(inst)) {
|
||||
// 检查 Phi 指令是否是需要处理的 SSA 值
|
||||
if (valueToAllocaMap.count(phiInst)) {
|
||||
AllocaInst *alloca = valueToAllocaMap[phiInst];
|
||||
|
||||
// 1. 为 Phi 指令的每个入边,在前驱块的末尾插入 Store 指令
|
||||
// PhiInst 假设有 getIncomingValues() 和 getIncomingBlocks()
|
||||
for (unsigned i = 0; i < phiInst->getNumIncomingValues(); ++i) { // 假设 PhiInst 是通过操作数来管理入边的
|
||||
Value *incomingValue = phiInst->getValue(i); // 获取入值
|
||||
BasicBlock *incomingBlock = phiInst->getBlock(i); // 获取对应的入块
|
||||
|
||||
// 在入块的跳转指令之前插入 StoreInst
|
||||
// 需要找到 incomingBlock 的终结指令 (Terminator Instruction)
|
||||
// 并将 StoreInst 插入到它前面
|
||||
if (incomingBlock->terminator()->get()->isTerminator()) {
|
||||
builder->setPosition(incomingBlock, incomingBlock->terminator());
|
||||
} else {
|
||||
// 如果没有终结指令,插入到末尾
|
||||
builder->setPosition(incomingBlock, incomingBlock->end());
|
||||
}
|
||||
builder->createStoreInst(incomingValue, alloca);
|
||||
}
|
||||
|
||||
// 2. 在当前 Phi 所在基本块的开头,插入 Load 指令
|
||||
// 将 Load 指令插入到 Phi 指令之后,因为 Phi 指令即将被删除
|
||||
builder->setPosition(bb.get(), bb.get()->findInstIterator(phiInst));
|
||||
LoadInst *newLoad = builder->createLoadInst(alloca);
|
||||
|
||||
// 3. 将 Phi 指令的所有用途替换为新的 Load 指令
|
||||
phiInst->replaceAllUsesWith(newLoad);
|
||||
|
||||
// 标记 Phi 指令待删除
|
||||
phisToErase.push_back(phiInst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 实际删除 Phi 指令
|
||||
for (auto phi : phisToErase) {
|
||||
if (phi && phi->getParent()) {
|
||||
SysYIROptUtils::usedelete(phi); // 清理 use-def 链
|
||||
phi->getParent()->removeInst(phi); // 从基本块中删除
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reg2MemContext::insertLoadsAndStores(Function *func) {
|
||||
// 收集所有需要替换的 uses,避免在迭代时修改 use 链表
|
||||
std::vector<std::pair<Use *, LoadInst *>> usesToReplace;
|
||||
std::vector<Instruction *> instsToStore; // 收集需要插入 Store 的指令
|
||||
|
||||
// 遍历所有基本块和指令
|
||||
for (auto &bb : func->getBasicBlocks()) {
|
||||
for (auto instIter = bb->getInstructions().begin(); instIter != bb->getInstructions().end(); instIter++) {
|
||||
Instruction *inst = instIter->get();
|
||||
|
||||
// 如果指令有结果且我们为其分配了 alloca (Phi 已在 rewritePhis 处理)
|
||||
// 并且其类型不是 void
|
||||
if (!inst->getType()->isVoid() && valueToAllocaMap.count(inst)) {
|
||||
// 在指令之后插入 Store 指令
|
||||
// StoreInst 应该插入到当前指令之后
|
||||
builder->setPosition(bb.get(), bb.get()->findInstIterator(inst));
|
||||
builder->createStoreInst(inst, valueToAllocaMap[inst]);
|
||||
}
|
||||
|
||||
// 处理指令的操作数:如果操作数是一个 SSA 值,且为其分配了 alloca
|
||||
// (并且这个操作数不是 Phi Inst 的 incoming value,因为 Phi 的 incoming value 已经在 rewritePhis 中处理了)
|
||||
// 注意:Phi Inst 的操作数是特殊的,它们表示来自不同前驱块的值。
|
||||
// 这里的处理主要是针对非 Phi 指令的操作数。
|
||||
for (auto use = inst->getUses().begin(); use != inst->getUses().end(); ++use) {
|
||||
// 如果当前 use 的 Value 是一个 Instruction 或 Argument
|
||||
Value *operand = use->get()->getValue();
|
||||
if (isPromotableToMemory(operand) && valueToAllocaMap.count(operand)) {
|
||||
// 确保这个 operand 不是一个即将被删除的 Phi 指令
|
||||
// (在 rewritePhis 阶段,Phi 已经被处理并可能被标记删除)
|
||||
// 或者检查 use 的 user 不是 PhiInst
|
||||
if (dynamic_cast<PhiInst *>(inst)) {
|
||||
continue; // Phi 的操作数已在 rewritePhis 中处理
|
||||
}
|
||||
|
||||
AllocaInst *alloca = valueToAllocaMap[operand];
|
||||
|
||||
// 在使用点之前插入 Load 指令
|
||||
// LoadInst 应该插入到使用它的指令之前
|
||||
builder->setPosition(bb.get(), bb.get()->findInstIterator(inst));
|
||||
LoadInst *newLoad = builder->createLoadInst(alloca);
|
||||
|
||||
// 记录要替换的 use
|
||||
usesToReplace.push_back({use->get(), newLoad});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行所有替换操作
|
||||
for (auto &pair : usesToReplace) {
|
||||
pair.first->setValue(pair.second); // 替换 use 的 Value
|
||||
}
|
||||
}
|
||||
|
||||
void Reg2MemContext::cleanup(Function *func) {
|
||||
// 此时,所有原始的 Phi 指令应该已经被删除。
|
||||
// 如果有其他需要删除的临时指令,可以在这里处理。
|
||||
// 通常,Reg2Mem 的清理比 Mem2Reg 简单,因为主要是在插入指令。
|
||||
// 这里可以作为一个占位符,以防未来有其他清理需求。
|
||||
}
|
||||
|
||||
bool Reg2Mem::runOnFunction(Function *F, AnalysisManager &AM) {
|
||||
// 记录初始指令数量
|
||||
size_t initial_inst_count = 0;
|
||||
for (auto &bb : F->getBasicBlocks()) {
|
||||
initial_inst_count += bb->getInstructions().size();
|
||||
}
|
||||
|
||||
Reg2MemContext ctx(builder); // 假设 builder 是一个全局或可访问的 IRBuilder 实例
|
||||
ctx.run(F);
|
||||
|
||||
// 记录最终指令数量
|
||||
size_t final_inst_count = 0;
|
||||
for (auto &bb : F->getBasicBlocks()) {
|
||||
final_inst_count += bb->getInstructions().size();
|
||||
}
|
||||
// TODO: 添加更精确的变化检测逻辑,例如在run函数中维护changed状态
|
||||
bool changed = (initial_inst_count != final_inst_count); // 粗略判断是否改变
|
||||
|
||||
if (changed) {
|
||||
// Reg2Mem 会显著改变 IR 结构,特别是数据流。
|
||||
// 它会插入大量的 Load/Store 指令,改变 Value 的来源。
|
||||
// 这会使几乎所有数据流分析失效。
|
||||
// 例如:
|
||||
// AM.invalidateAnalysis(&DominatorTreeAnalysisPass::ID, F); // 如果基本块结构改变,可能失效
|
||||
// AM.invalidateAnalysis(&LivenessAnalysisPass::ID, F); // 活跃性分析肯定失效
|
||||
// AM.invalidateAnalysis(&DCEPass::ID, F); // 可能产生新的死代码
|
||||
// ... 其他所有数据流分析
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void Reg2Mem::getAnalysisUsage(std::set<void *> &analysisDependencies, std::set<void *> &analysisInvalidations) const {
|
||||
// Reg2Mem 通常不需要特定的分析作为依赖,因为它主要是一个转换。
|
||||
// 但它会使许多分析失效。
|
||||
analysisInvalidations.insert(&LivenessAnalysisPass::ID); // 例如
|
||||
analysisInvalidations.insert(&DominatorTreeAnalysisPass::ID);
|
||||
}
|
||||
|
||||
} // namespace sysy
|
||||
@ -16,6 +16,7 @@ using namespace antlr4;
|
||||
#include "SysYIRCFGOpt.h" // 包含 CFG 优化
|
||||
#include "RISCv64Backend.h"
|
||||
#include "Pass.h" // 包含新的 Pass 框架
|
||||
#include "AddressCalculationExpansion.h"
|
||||
|
||||
using namespace sysy;
|
||||
|
||||
@ -138,6 +139,69 @@ int main(int argc, char **argv) {
|
||||
// 好像都不用传递module和builder了,因为 PassManager 初始化了
|
||||
passManager.runOptimizationPipeline(moduleIR, builder, optLevel);
|
||||
|
||||
|
||||
|
||||
|
||||
AddressCalculationExpansion ace(moduleIR, builder);
|
||||
if (ace.run()) {
|
||||
if (DEBUG) cout << "AddressCalculationExpansion made changes.\n";
|
||||
// 如果 ACE 改变了IR,并且 DEBUG 模式开启,可以考虑打印IR
|
||||
if (DEBUG) {
|
||||
cout << "=== After AddressCalculationExpansion ===\n";
|
||||
SysYPrinter(moduleIR).printIR();
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) cout << "AddressCalculationExpansion made no changes.\n";
|
||||
}
|
||||
|
||||
|
||||
// 根据优化级别,执行额外的优化 pass
|
||||
if (optLevel >= 1) {
|
||||
if (DEBUG) cout << "Applying additional -O" << optLevel << " optimizations...\n";
|
||||
// 放置 -O1 及其以上级别要启用的额外优化 pass
|
||||
// 例如:
|
||||
// MyNewOptimizationPass newOpt(moduleIR, builder);
|
||||
// newOpt.run();
|
||||
|
||||
// 占位符注释,替换为你的具体优化 pass
|
||||
// cout << "--- Additional Pass: MyCustomOpt1 ---" << endl;
|
||||
// MyCustomOpt1 opt1_pass(moduleIR, builder);
|
||||
// opt1_pass.run();
|
||||
|
||||
// cout << "--- Additional Pass: MyCustomOpt2 ---" << endl;
|
||||
// MyCustomOpt2 opt2_pass(moduleIR, builder, &cfa); // 假设需要CFA
|
||||
// opt2_pass.run();
|
||||
// ... 更多 -O1 特有的优化
|
||||
// DeadCodeElimination dce(moduleIR, &cfa, &ava);
|
||||
// dce.runDCEPipeline();
|
||||
// if (DEBUG) {
|
||||
// cout << "=== After 1st DCE (Default) ===\n";
|
||||
// SysYPrinter(moduleIR).printIR();
|
||||
// }
|
||||
|
||||
// Mem2Reg mem2reg(moduleIR, builder, &cfa, &ava);
|
||||
// mem2reg.mem2regPipeline();
|
||||
// if (DEBUG) {
|
||||
// cout << "=== After Mem2Reg (Default) ===\n";
|
||||
// SysYPrinter(moduleIR).printIR();
|
||||
// }
|
||||
|
||||
// Reg2Mem reg2mem(moduleIR, builder);
|
||||
// reg2mem.DeletePhiInst();
|
||||
// if (DEBUG) {
|
||||
// cout << "=== After Reg2Mem (Default) ===\n";
|
||||
// SysYPrinter(moduleIR).printIR();
|
||||
// }
|
||||
|
||||
// dce.runDCEPipeline(); // 第二次 DCE (默认)
|
||||
// if (DEBUG) {
|
||||
// cout << "=== After 2nd DCE (Default) ===\n";
|
||||
// SysYPrinter(moduleIR).printIR();
|
||||
// }
|
||||
} else {
|
||||
if (DEBUG) cout << "No additional middle-end optimizations applied for -O" << optLevel << ".\n";
|
||||
}
|
||||
|
||||
// 5. 根据 argStopAfter 决定后续操作
|
||||
// a) 如果指定停止在 IR 阶段,则打印最终 IR 并退出
|
||||
if (argStopAfter == "ir" || argStopAfter == "ird") {
|
||||
@ -154,7 +218,6 @@ int main(int argc, char **argv) {
|
||||
DEBUG = 1;
|
||||
DEEPDEBUG = 1;
|
||||
}
|
||||
|
||||
sysy::RISCv64CodeGen codegen(moduleIR); // 传入优化后的 moduleIR
|
||||
string asmCode = codegen.code_gen();
|
||||
|
||||
|
||||
8
test/01_add.sy
Normal file
8
test/01_add.sy
Normal file
@ -0,0 +1,8 @@
|
||||
//test add
|
||||
|
||||
int main(){
|
||||
int a, b;
|
||||
a = 10;
|
||||
b = 2;
|
||||
return a + b;
|
||||
}
|
||||
14
test/10_test.sy
Normal file
14
test/10_test.sy
Normal file
@ -0,0 +1,14 @@
|
||||
//test file for backend lab
|
||||
|
||||
int main() {
|
||||
const int a = 1;
|
||||
const int b = 2;
|
||||
int c;
|
||||
|
||||
if (a != b)
|
||||
c = b - a + 20; // 21 <- this
|
||||
else
|
||||
c = a * b + b + b + 10; // 16
|
||||
|
||||
return c;
|
||||
}
|
||||
13
test/11_add2.sy
Normal file
13
test/11_add2.sy
Normal file
@ -0,0 +1,13 @@
|
||||
//test add
|
||||
|
||||
int mul(int x, int y) {
|
||||
return x * y;
|
||||
}
|
||||
|
||||
int main(){
|
||||
int a, b;
|
||||
a = 10;
|
||||
b = 3;
|
||||
a = mul(a, b); //60
|
||||
return a + b; //66
|
||||
}
|
||||
20
test/20_test_licm_sr.sy
Normal file
20
test/20_test_licm_sr.sy
Normal file
@ -0,0 +1,20 @@
|
||||
//test file for loop-invariant code motion (licm) and strength reduction (sr is optional)
|
||||
|
||||
int main(){
|
||||
const int a = 1;
|
||||
const int b = 2;
|
||||
int c, d, f;
|
||||
|
||||
int i = 0;
|
||||
while(i < 100){
|
||||
c = a + b;
|
||||
d = c * 2;
|
||||
|
||||
if(i > 50){
|
||||
f = i * d;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
18
test/21_test_cse.sy
Normal file
18
test/21_test_cse.sy
Normal file
@ -0,0 +1,18 @@
|
||||
//test file for common subexpression eliminiation (cse)
|
||||
int main(){
|
||||
|
||||
int a = 1;
|
||||
int b = 2;
|
||||
int c, d, e, f;
|
||||
|
||||
c = a + b;
|
||||
|
||||
if(c > 0){
|
||||
b = 3;
|
||||
d = a + b;
|
||||
}
|
||||
|
||||
e = a + b;
|
||||
|
||||
return e;
|
||||
}
|
||||
15
test/22_test_dce.sy
Normal file
15
test/22_test_dce.sy
Normal file
@ -0,0 +1,15 @@
|
||||
//test file for dead code eliminiation (dce)
|
||||
int main(){
|
||||
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int a[100];
|
||||
|
||||
while(j < 100){
|
||||
a[j] = j;
|
||||
i = i * 2;
|
||||
j = j + 1;
|
||||
}
|
||||
|
||||
return a[j-1];
|
||||
}
|
||||
11
test/23_test_vn.sy
Normal file
11
test/23_test_vn.sy
Normal file
@ -0,0 +1,11 @@
|
||||
//test file for value numbering (vn)
|
||||
int main(){
|
||||
|
||||
int a, b, c, d;
|
||||
a = 1;
|
||||
b = a + 1;
|
||||
c = a;
|
||||
d = c + 1;
|
||||
|
||||
return d;
|
||||
}
|
||||
14
test/24_test_cp_cf.sy
Normal file
14
test/24_test_cp_cf.sy
Normal file
@ -0,0 +1,14 @@
|
||||
//test file for constant propogation (cp) and constant folding (cf)
|
||||
int main(){
|
||||
|
||||
int b = 5;
|
||||
int c = 4 * b;
|
||||
int d, g;
|
||||
|
||||
if(c > 8){
|
||||
d = b + c;
|
||||
}
|
||||
g = c * d;
|
||||
|
||||
return g;
|
||||
}
|
||||
94
test/30_test_reg_alloc.sy
Normal file
94
test/30_test_reg_alloc.sy
Normal file
@ -0,0 +1,94 @@
|
||||
int a1 = 1;
|
||||
int a2 = 2;
|
||||
int a3 = 3;
|
||||
int a4 = 4;
|
||||
int a5 = 5;
|
||||
int a6 = 6;
|
||||
int a7 = 7;
|
||||
int a8 = 8;
|
||||
int a9 = 9;
|
||||
int a10 = 10;
|
||||
int a11 = 11;
|
||||
int a12 = 12;
|
||||
int a13 = 13;
|
||||
int a14 = 14;
|
||||
int a15 = 15;
|
||||
int a16 = 16;
|
||||
int a17 = 1;
|
||||
int a18 = 2;
|
||||
int a19 = 3;
|
||||
int a20 = 4;
|
||||
int a21 = 5;
|
||||
int a22 = 6;
|
||||
int a23 = 7;
|
||||
int a24 = 8;
|
||||
int a25 = 9;
|
||||
int a26 = 10;
|
||||
int a27 = 11;
|
||||
int a28 = 12;
|
||||
int a29 = 13;
|
||||
int a30 = 14;
|
||||
int a31 = 15;
|
||||
int a32 = 16;
|
||||
|
||||
int func(int a, int b){
|
||||
int i;
|
||||
i = a + b;
|
||||
|
||||
int c1;int c2;int c3;int c4;
|
||||
int d1;int d2;int d3;int d4;
|
||||
int e1;int e2;int e3;int e4;
|
||||
int f1;int f2;int f3;int f4;
|
||||
int g1;int g2;int g3;int g4;
|
||||
int h1;int h2;int h3;int h4;
|
||||
int i1;int i2;int i3;int i4;
|
||||
int j1;int j2;int j3;int j4;
|
||||
int k1;int k2;int k3;int k4;
|
||||
c1 = getint();c2 = getint();c3 = getint();c4 = getint();
|
||||
d1 = 1 + c1 + a1;d2 = 2 + c2 + a2;d3 = 3 + c3 + a3;d4 = 4 + c4 + a4;
|
||||
e1 = 1 + d1 + a5;e2 = 2 + d2 + a6;e3 = 3 + d3 + a7;e4 = 4 + d4 + a8;
|
||||
f1 = 1 + e1 + a9;f2 = 2 + e2 + a10;f3 = 3 + e3 + a11;f4 = 4 + e4 + a12;
|
||||
g1 = 1 + f1 + a13;g2 = 2 + f2 + a14;g3 = 3 + f3 + a15;g4 = 4 + f4 + a16;
|
||||
h1 = 1 + g1 + a17;h2 = 2 + g2 + a18;h3 = 3 + g3 + a19;h4 = 4 + g4 + a20;
|
||||
i1 = 1 + h1 + a21;i2 = 2 + h2 + a22;i3 = 3 + h3 + a23;i4 = 4 + h4 + a24;
|
||||
j1 = 1 + i1 + a25;j2 = 2 + i2 + a26;j3 = 3 + i3 + a27;j4 = 4 + i4 + a28;
|
||||
k1 = 1 + j1 + a29;k2 = 2 + j2 + a30;k3 = 3 + j3 + a31;k4 = 4 + j4 + a32;
|
||||
|
||||
i = a - b + 10;
|
||||
k1 = 1 + j1 + a29;k2 = 2 + j2 + a30;k3 = 3 + j3 + a31;k4 = 4 + j4 + a32;
|
||||
j1 = 1 + i1 + a25;j2 = 2 + i2 + a26;j3 = 3 + i3 + a27;j4 = 4 + i4 + a28;
|
||||
i1 = 1 + h1 + a21;i2 = 2 + h2 + a22;i3 = 3 + h3 + a23;i4 = 4 + h4 + a24;
|
||||
h1 = 1 + g1 + a17;h2 = 2 + g2 + a18;h3 = 3 + g3 + a19;h4 = 4 + g4 + a20;
|
||||
g1 = 1 + f1 + a13;g2 = 2 + f2 + a14;g3 = 3 + f3 + a15;g4 = 4 + f4 + a16;
|
||||
f1 = 1 + e1 + a9;f2 = 2 + e2 + a10;f3 = 3 + e3 + a11;f4 = 4 + e4 + a12;
|
||||
e1 = 1 + d1 + a5;e2 = 2 + d2 + a6;e3 = 3 + d3 + a7;e4 = 4 + d4 + a8;
|
||||
d1 = 1 + c1 + a1;d2 = 2 + c2 + a2;d3 = 3 + c3 + a3;d4 = 4 + c4 + a4;
|
||||
d1 = 1 + c1 + a1;d2 = 2 + c2 + a2;d3 = 3 + c3 + a3;d4 = 4 + c4 + a4;
|
||||
return i + c1 + c2 + c3 + c4
|
||||
- d1 - d2 - d3 - d4
|
||||
+ e1 + e2 + e3 + e4
|
||||
- f1 - f2 - f3 - f4
|
||||
+ g1 + g2 + g3 + g4
|
||||
- h1 - h2 - h3 - h4
|
||||
+ i1 + i2 + i3 + i4
|
||||
- j1 - j2 - j3 - j4
|
||||
+ k1 + k2 + k3 + k4
|
||||
+ a1 - a2 + a3 - a4
|
||||
+ a5 - a6 + a7 - a8
|
||||
+ a9 - a10 + a11 - a12
|
||||
+ a13 - a14 + a15 - a16
|
||||
+ a17 - a18 + a19 - a20
|
||||
+ a21 - a22 + a23 - a24
|
||||
+ a25 - a26 + a27 - a28
|
||||
+ a29 - a30 + a31 - a32;
|
||||
}
|
||||
|
||||
int main(){
|
||||
int a;
|
||||
int b;
|
||||
a = getint();
|
||||
b = a + 2 * 9;
|
||||
a = func(a, b);
|
||||
putint(a);
|
||||
return a;
|
||||
}
|
||||
7
test/bug1.sy
Normal file
7
test/bug1.sy
Normal file
@ -0,0 +1,7 @@
|
||||
// bug1: getint(;
|
||||
|
||||
int main() {
|
||||
int a;
|
||||
a = getint(;
|
||||
return 0;
|
||||
}
|
||||
31
test/format-ref.sy
Normal file
31
test/format-ref.sy
Normal file
@ -0,0 +1,31 @@
|
||||
int get_one(int a) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int deepWhileBr(int a, int b) {
|
||||
int c;
|
||||
c = a + b;
|
||||
while (c < 75) {
|
||||
int d;
|
||||
d = 42;
|
||||
if (c < 100) {
|
||||
c = c + d;
|
||||
if (c > 99) {
|
||||
int e;
|
||||
e = d * 2;
|
||||
if (get_one(0) == 1) {
|
||||
c = e * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (c);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int p;
|
||||
p = 2;
|
||||
p = deepWhileBr(p, p);
|
||||
putint(p);
|
||||
return 0;
|
||||
}
|
||||
30
test/format-test.sy
Normal file
30
test/format-test.sy
Normal file
@ -0,0 +1,30 @@
|
||||
int get_one(int a)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int deepWhileBr(int a,int b){
|
||||
int c;
|
||||
c = a + b;
|
||||
while(c<75) {
|
||||
int d; d=42;
|
||||
if (c<100) {
|
||||
c =c+d;
|
||||
if (c > 99) {
|
||||
int e;
|
||||
e = d*2;
|
||||
if (get_one(0)==1) c=e * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (c);
|
||||
}
|
||||
int main() {
|
||||
int p;
|
||||
p = 2;
|
||||
p = deepWhileBr(p, p);
|
||||
putint(p);
|
||||
return 0;
|
||||
}
|
||||
1
test/funcrparams.sy
Normal file
1
test/funcrparams.sy
Normal file
@ -0,0 +1 @@
|
||||
1,0xa , 011, "hellow",1.1
|
||||
3
test_script/clean.sh
Normal file
3
test_script/clean.sh
Normal file
@ -0,0 +1,3 @@
|
||||
rm -rf tmp/*
|
||||
rm -rf *.s *.ll *clang *sysyc
|
||||
rm -rf *_riscv32
|
||||
49
test_script/exe-riscv32.sh
Normal file
49
test_script/exe-riscv32.sh
Normal file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义输入目录
|
||||
input_dir="./tmp"
|
||||
|
||||
# 获取tmp目录下的所有符合条件的可执行文件,并按前缀数字升序排序
|
||||
executable_files=$(ls "$input_dir" | grep -E '^[0-9]+_.*' | grep -E '_gcc_riscv32$|_sysyc_riscv32$' | sort -t '_' -k1,1n)
|
||||
|
||||
# 用于存储前缀数字和返回值
|
||||
declare -A gcc_results
|
||||
declare -A sysyc_results
|
||||
|
||||
# 遍历所有符合条件的可执行文件
|
||||
for file in $executable_files; do
|
||||
# 提取文件名前缀和后缀
|
||||
prefix=$(echo "$file" | cut -d '_' -f 1)
|
||||
suffix=$(echo "$file" | cut -d '_' -f 2)
|
||||
|
||||
# 检查是否已经处理过该前缀的两个文件
|
||||
if [[ ${gcc_results["$prefix"]} && ${sysyc_results["$prefix"]} ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# 执行可执行文件并捕获返回值
|
||||
echo "Executing: $file"
|
||||
qemu-riscv32 "$input_dir/$file"
|
||||
ret_code=$?
|
||||
|
||||
# 明确记录返回值
|
||||
echo "Return code for $file: $ret_code"
|
||||
|
||||
# 根据后缀存储返回值
|
||||
if [[ "$suffix" == "gcc" ]]; then
|
||||
gcc_results["$prefix"]=$ret_code
|
||||
elif [[ "$suffix" == "sysyc" ]]; then
|
||||
sysyc_results["$prefix"]=$ret_code
|
||||
fi
|
||||
|
||||
# 如果同一个前缀的两个文件都已执行,比较它们的返回值
|
||||
if [[ ${gcc_results["$prefix"]} && ${sysyc_results["$prefix"]} ]]; then
|
||||
gcc_ret=${gcc_results["$prefix"]}
|
||||
sysyc_ret=${sysyc_results["$prefix"]}
|
||||
if [[ "$gcc_ret" -ne "$sysyc_ret" ]]; then
|
||||
echo -e "\e[31mWARNING: Return codes differ for prefix $prefix: _gcc=$gcc_ret, _sysyc=$sysyc_ret\e[0m"
|
||||
else
|
||||
echo "Return codes match for prefix $prefix: $gcc_ret"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
49
test_script/exe.sh
Normal file
49
test_script/exe.sh
Normal file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义输入目录
|
||||
input_dir="."
|
||||
|
||||
# 获取当前目录下的所有符合条件的可执行文件,并按前缀数字升序排序
|
||||
executable_files=$(ls "$input_dir" | grep -E '^[0-9]+_.*' | grep -E '_clang$|_sysyc$' | sort -t '_' -k1,1n)
|
||||
|
||||
# 用于存储前缀数字和返回值
|
||||
declare -A clang_results
|
||||
declare -A sysyc_results
|
||||
|
||||
# 遍历所有符合条件的可执行文件
|
||||
for file in $executable_files; do
|
||||
# 提取文件名前缀和后缀
|
||||
prefix=$(echo "$file" | cut -d '_' -f 1)
|
||||
suffix=$(echo "$file" | cut -d '_' -f 2)
|
||||
|
||||
# 检查是否已经处理过该前缀的两个文件
|
||||
if [[ ${clang_results["$prefix"]} && ${sysyc_results["$prefix"]} ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# 执行可执行文件并捕获返回值
|
||||
echo "Executing: $file"
|
||||
"./$file"
|
||||
ret_code=$?
|
||||
|
||||
# 明确记录返回值
|
||||
echo "Return code for $file: $ret_code"
|
||||
|
||||
# 根据后缀存储返回值
|
||||
if [[ "$suffix" == "clang" ]]; then
|
||||
clang_results["$prefix"]=$ret_code
|
||||
elif [[ "$suffix" == "sysyc" ]]; then
|
||||
sysyc_results["$prefix"]=$ret_code
|
||||
fi
|
||||
|
||||
# 如果同一个前缀的两个文件都已执行,比较它们的返回值
|
||||
if [[ ${clang_results["$prefix"]} && ${sysyc_results["$prefix"]} ]]; then
|
||||
clang_ret=${clang_results["$prefix"]}
|
||||
sysyc_ret=${sysyc_results["$prefix"]}
|
||||
if [[ "$clang_ret" -ne "$sysyc_ret" ]]; then
|
||||
echo -e "\e[31mWARNING: Return codes differ for prefix $prefix: _clang=$clang_ret, _sysyc=$sysyc_ret\e[0m"
|
||||
else
|
||||
echo "Return codes match for prefix $prefix: $clang_ret"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
57
test_script/gcc-riscv32.sh
Normal file
57
test_script/gcc-riscv32.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义输入和输出路径
|
||||
input_dir="../test/"
|
||||
output_dir="./tmp"
|
||||
|
||||
# 默认不生成可执行文件
|
||||
generate_executable=false
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--executable|-e)
|
||||
generate_executable=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown parameter: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 确保输出目录存在
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# 遍历输入路径中的所有 .sy 文件
|
||||
for sy_file in "$input_dir"*.sy; do
|
||||
# 获取文件名(不带路径和扩展名)
|
||||
base_name=$(basename "$sy_file" .sy)
|
||||
|
||||
# 定义输出文件路径
|
||||
output_file="${output_dir}/${base_name}_gcc_riscv32.s"
|
||||
|
||||
# 使用 gcc 编译 .sy 文件为 .ll 文件
|
||||
riscv32-unknown-elf-gcc -x c -S "$sy_file" -o "$output_file"
|
||||
|
||||
# 检查是否成功
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Compiled $sy_file -> $output_file"
|
||||
else
|
||||
echo "Failed to compile $sy_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
# 如果指定了 --executable 或 -e 参数,则进一步编译为可执行文件
|
||||
if $generate_executable; then
|
||||
executable_file="${output_dir}/${base_name}_gcc_riscv32"
|
||||
riscv32-unknown-elf-gcc "$output_file" -o "$executable_file"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Generated executable: $executable_file"
|
||||
else
|
||||
echo "Failed to generate executable from $output_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
57
test_script/ll.sh
Normal file
57
test_script/ll.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义输入和输出路径
|
||||
input_dir="../test/"
|
||||
output_dir="./"
|
||||
|
||||
# 默认不生成可执行文件
|
||||
generate_executable=false
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--executable|-e)
|
||||
generate_executable=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown parameter: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 确保输出目录存在
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# 遍历输入路径中的所有 .sy 文件
|
||||
for sy_file in "$input_dir"*.sy; do
|
||||
# 获取文件名(不带路径和扩展名)
|
||||
base_name=$(basename "$sy_file" .sy)
|
||||
|
||||
# 定义输出文件路径
|
||||
output_file="${base_name}_clang.ll"
|
||||
|
||||
# 使用 clang 编译 .sy 文件为 .ll 文件
|
||||
clang -x c -S -emit-llvm "$sy_file" -o "$output_file"
|
||||
|
||||
# 检查是否成功
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Compiled $sy_file -> $output_file"
|
||||
else
|
||||
echo "Failed to compile $sy_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
# 如果指定了 --executable 或 -e 参数,则进一步编译为可执行文件
|
||||
if $generate_executable; then
|
||||
executable_file="${base_name}_clang"
|
||||
clang "$output_file" -o "$executable_file"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Generated executable: $executable_file"
|
||||
else
|
||||
echo "Failed to generate executable from $output_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@ -14,7 +14,6 @@ TESTDATA_DIR="${SCRIPT_DIR}/testdata" # 用于查找 .in/.out 文件
|
||||
GCC_NATIVE="gcc" # VM 内部的原生 gcc
|
||||
|
||||
# --- 初始化变量 ---
|
||||
CLEAN_MODE=false
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # 程序自动化执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
@ -30,7 +29,6 @@ show_help() {
|
||||
echo "如果找到对应的 .in/.out 文件,则进行自动化测试。否则,进入交互模式。"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -c, --clean 清理 tmp 临时目录下的所有文件。"
|
||||
echo " -ct N 设置 gcc 编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -t N 设置程序自动化执行超时为 N 秒 (默认: 5)。"
|
||||
echo " -ml N, --max-lines N 当输出对比失败时,最多显示 N 行内容 (默认: 50)。"
|
||||
@ -59,24 +57,10 @@ display_file_content() {
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 新增功能: 清理临时文件的函数 ---
|
||||
clean_tmp() {
|
||||
echo "正在清理临时目录: ${TMP_DIR}"
|
||||
if [ -d "${TMP_DIR}" ]; then
|
||||
rm -rf "${TMP_DIR}"/* 2>/dev/null
|
||||
echo "清理完成。"
|
||||
else
|
||||
echo "临时目录 ${TMP_DIR} 不存在,无需清理。"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 参数解析 ---
|
||||
# 从参数中分离出 .s 文件和选项
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-c|--clean)
|
||||
CLEAN_MODE=true
|
||||
;;
|
||||
-ct|-t|-ml|--max-lines)
|
||||
# 选项和其值将在下一个循环中处理
|
||||
;;
|
||||
@ -90,7 +74,6 @@ for arg in "$@"; do
|
||||
args_processed=true # 标记已处理过参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-c|--clean) ;; # 已在外部处理
|
||||
-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 ;;
|
||||
@ -112,14 +95,6 @@ for arg in "$@"; do
|
||||
done
|
||||
|
||||
# --- 主逻辑开始 ---
|
||||
if ${CLEAN_MODE}; then
|
||||
clean_tmp
|
||||
# 如果只提供了 -c 选项,则退出
|
||||
if [ ${#S_FILES[@]} -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ${#S_FILES[@]} -eq 0 ]; then
|
||||
echo "错误: 未提供任何 .s 文件作为输入。"
|
||||
show_help
|
||||
@ -187,17 +162,14 @@ for s_file in "${S_FILES[@]}"; do
|
||||
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
|
||||
|
||||
# --- 本次修改点: 使用 tr 删除所有空白字符后再比较 ---
|
||||
if ! diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
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
|
||||
# --- 本次修改点: 使用 tr 删除所有空白字符后再比较 ---
|
||||
if diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${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
|
||||
@ -175,8 +175,7 @@ while IFS= read -r s_file; do
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
# --- 本次修改点: 使用 tr 删除所有空白字符后再比较 ---
|
||||
if ! diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
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}"
|
||||
@ -187,9 +186,7 @@ while IFS= read -r s_file; do
|
||||
if [ $ACTUAL_RETURN_CODE -ne 0 ]; then
|
||||
echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m"
|
||||
fi
|
||||
|
||||
# --- 本次修改点: 使用 tr 删除所有空白字符后再比较 ---
|
||||
if diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${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"
|
||||
@ -68,60 +68,66 @@ display_file_content() {
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 本次修改点: 整个参数解析逻辑被重写 ---
|
||||
# 使用标准的 while 循环来健壮地处理任意顺序的参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-e|--executable)
|
||||
EXECUTE_MODE=true
|
||||
shift # 消耗选项
|
||||
;;
|
||||
-c|--clean)
|
||||
CLEAN_MODE=true
|
||||
shift # 消耗选项
|
||||
;;
|
||||
-sct)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift 2; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-gct)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then GCC_TIMEOUT="$2"; shift 2; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-et)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then EXEC_TIMEOUT="$2"; shift 2; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-ml|--max-lines)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift 2; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-*) # 未知选项
|
||||
echo "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
*) # 其他参数被视为文件路径
|
||||
if [[ -f "$1" && "$1" == *.sy ]]; then
|
||||
SY_FILES+=("$1")
|
||||
else
|
||||
echo "警告: 无效文件或不是 .sy 文件,已忽略: $1"
|
||||
fi
|
||||
shift # 消耗文件参数
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- 参数解析 ---
|
||||
# 从参数中分离出 .sy 文件和选项
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-e|--executable)
|
||||
EXECUTE_MODE=true
|
||||
;;
|
||||
-c|--clean)
|
||||
CLEAN_MODE=true
|
||||
shift
|
||||
;;
|
||||
-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 ${CLEAN_MODE}; then
|
||||
echo "检测到 -c/--clean 选项,正在清空 ${TMP_DIR}..."
|
||||
if [ -d "${TMP_DIR}" ]; then
|
||||
# 使用 * 而不是 . 来确保只删除内容,不删除目录本身
|
||||
# 忽略 rm 可能因目录为空而报告的错误
|
||||
rm -rf "${TMP_DIR}"/* 2>/dev/null
|
||||
echo "清理完成。"
|
||||
else
|
||||
echo "临时目录 ${TMP_DIR} 不存在,无需清理。"
|
||||
fi
|
||||
|
||||
# 如果只提供了 -c 选项而没有其他 .sy 文件,则在清理后退出
|
||||
if [ ${#SY_FILES[@]} -eq 0 ] && ! ${EXECUTE_MODE}; then
|
||||
exit 0
|
||||
fi
|
||||
@ -212,26 +218,28 @@ for sy_file in "${SY_FILES[@]}"; do
|
||||
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 <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
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 <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${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
|
||||
# --- 本次修改点: 使用新函数显示输出 ---
|
||||
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"
|
||||
@ -253,6 +261,7 @@ for sy_file in "${SY_FILES[@]}"; do
|
||||
"${QEMU_RISCV64}" "${executable_file}"
|
||||
INTERACTIVE_RET_CODE=$?
|
||||
echo -e "\e[33m\n 交互模式执行完毕,程序返回码: ${INTERACTIVE_RET_CODE}\e[0m"
|
||||
# 交互模式无法自动判断对错,默认算通过,但会提示
|
||||
echo " 注意: 交互模式的结果未经验证。"
|
||||
fi
|
||||
fi
|
||||
@ -22,7 +22,6 @@ SYSYC_TIMEOUT=10 # sysyc 编译超时 (秒)
|
||||
GCC_TIMEOUT=10 # gcc 编译超时 (秒)
|
||||
EXEC_TIMEOUT=5 # qemu 执行超时 (秒)
|
||||
MAX_OUTPUT_LINES=50 # 对比失败时显示的最大行数
|
||||
TEST_SETS=() # 用于存储要运行的测试集
|
||||
TOTAL_CASES=0
|
||||
PASSED_CASES=0
|
||||
FAILED_CASES_LIST="" # 用于存储未通过的测例列表
|
||||
@ -35,7 +34,6 @@ show_help() {
|
||||
echo "选项:"
|
||||
echo " -e, --executable 编译为可执行文件并运行测试。"
|
||||
echo " -c, --clean 清理 'tmp' 目录下的所有生成文件。"
|
||||
echo " -set [f|h|p|all]... 指定要运行的测试集 (functional, h_functional, performance)。可多选,默认为 all。"
|
||||
echo " -sct N 设置 sysyc 编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -gct N 设置 gcc 交叉编译超时为 N 秒 (默认: 10)。"
|
||||
echo " -et N 设置 qemu 执行超时为 N 秒 (默认: 5)。"
|
||||
@ -79,31 +77,22 @@ while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-e|--executable)
|
||||
EXECUTE_MODE=true
|
||||
shift
|
||||
;;
|
||||
-c|--clean)
|
||||
clean_tmp
|
||||
exit 0
|
||||
;;
|
||||
-set)
|
||||
shift # 移过 '-set'
|
||||
# 消耗所有后续参数直到遇到下一个选项
|
||||
while [[ "$#" -gt 0 && ! "$1" =~ ^- ]]; do
|
||||
TEST_SETS+=("$1")
|
||||
shift
|
||||
done
|
||||
;;
|
||||
-sct)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then SYSYC_TIMEOUT="$2"; shift 2; else echo "错误: -sct 需要一个正整数参数。" >&2; exit 1; fi
|
||||
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 2; else echo "错误: -gct 需要一个正整数参数。" >&2; exit 1; fi
|
||||
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 2; else echo "错误: -et 需要一个正整数参数。" >&2; exit 1; fi
|
||||
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 2; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then MAX_OUTPUT_LINES="$2"; shift; else echo "错误: --max-lines 需要一个正整数参数。" >&2; exit 1; fi
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
@ -115,37 +104,11 @@ while [[ "$#" -gt 0 ]]; do
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# --- 本次修改点: 根据 -set 参数构建查找路径 ---
|
||||
declare -A SET_MAP
|
||||
SET_MAP[f]="functional"
|
||||
SET_MAP[h]="h_functional"
|
||||
SET_MAP[p]="performance"
|
||||
|
||||
SEARCH_PATHS=()
|
||||
|
||||
# 如果未指定测试集,或指定了 'all',则搜索所有目录
|
||||
if [ ${#TEST_SETS[@]} -eq 0 ] || [[ " ${TEST_SETS[@]} " =~ " all " ]]; then
|
||||
SEARCH_PATHS+=("${TESTDATA_DIR}")
|
||||
else
|
||||
for set in "${TEST_SETS[@]}"; do
|
||||
if [[ -v SET_MAP[$set] ]]; then
|
||||
SEARCH_PATHS+=("${TESTDATA_DIR}/${SET_MAP[$set]}")
|
||||
else
|
||||
echo -e "\e[33m警告: 未知的测试集 '$set',已忽略。\e[0m"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 如果没有有效的搜索路径,则退出
|
||||
if [ ${#SEARCH_PATHS[@]} -eq 0 ]; then
|
||||
echo -e "\e[31m错误: 没有找到有效的测试集目录,测试中止。\e[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "SysY 测试运行器启动..."
|
||||
echo "输入目录: ${SEARCH_PATHS[@]}"
|
||||
echo "输入目录: ${TESTDATA_DIR}"
|
||||
echo "临时目录: ${TMP_DIR}"
|
||||
echo "执行模式: ${EXECUTE_MODE}"
|
||||
if ${EXECUTE_MODE}; then
|
||||
@ -154,12 +117,8 @@ if ${EXECUTE_MODE}; then
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 使用构建好的路径查找 .sy 文件并排序
|
||||
sy_files=$(find "${SEARCH_PATHS[@]}" -name "*.sy" | sort -V)
|
||||
if [ -z "$sy_files" ]; then
|
||||
echo "在指定目录中未找到任何 .sy 文件。"
|
||||
exit 0
|
||||
fi
|
||||
# --- 修改点: 查找所有 .sy 文件并按文件名前缀数字排序 ---
|
||||
sy_files=$(find "${TESTDATA_DIR}" -name "*.sy" | sort -V)
|
||||
TOTAL_CASES=$(echo "$sy_files" | wc -w)
|
||||
|
||||
# --- 修复: 使用 here-string (<<<) 代替管道 (|) 来避免子 shell 问题 ---
|
||||
@ -244,8 +203,7 @@ while IFS= read -r sy_file; do
|
||||
echo -e "\e[31m 返回码测试失败: 期望: ${EXPECTED_RETURN_CODE}, 实际: ${ACTUAL_RETURN_CODE}\e[0m"
|
||||
is_passed=0
|
||||
fi
|
||||
|
||||
if ! diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${EXPECTED_STDOUT_FILE}") >/dev/null 2>&1; then
|
||||
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}"
|
||||
@ -256,8 +214,7 @@ while IFS= read -r sy_file; do
|
||||
if [ $ACTUAL_RETURN_CODE -ne 0 ]; then
|
||||
echo -e "\e[33m警告: 程序以非零状态 ${ACTUAL_RETURN_CODE} 退出 (纯输出比较模式)。\e[0m"
|
||||
fi
|
||||
|
||||
if diff -q <(tr -d '[:space:]' < "${output_actual_file}") <(tr -d '[:space:]' < "${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"
|
||||
57
test_script/sysy-riscv32.sh
Normal file
57
test_script/sysy-riscv32.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义输入和输出路径
|
||||
input_dir="../test/"
|
||||
output_dir="./tmp"
|
||||
|
||||
# 默认不生成可执行文件
|
||||
generate_executable=false
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--executable|-e)
|
||||
generate_executable=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown parameter: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 确保输出目录存在
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# 遍历输入路径中的所有 .sy 文件
|
||||
for sy_file in "$input_dir"*.sy; do
|
||||
# 获取文件名(不带路径和扩展名)
|
||||
base_name=$(basename "$sy_file" .sy)
|
||||
|
||||
# 定义输出文件路径
|
||||
output_file="${output_dir}/${base_name}_sysyc_riscv32.s"
|
||||
|
||||
# 使用 sysyc 编译 .sy 文件为 .s 文件
|
||||
../build/bin/sysyc -s asm "$sy_file" > "$output_file"
|
||||
|
||||
# 检查是否成功
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Compiled $sy_file -> $output_file"
|
||||
else
|
||||
echo "Failed to compile $sy_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
# 如果指定了 --executable 或 -e 参数,则进一步编译为可执行文件
|
||||
if $generate_executable; then
|
||||
executable_file="${output_dir}/${base_name}_sysyc_riscv32"
|
||||
riscv32-unknown-elf-gcc "$output_file" -o "$executable_file"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Generated executable: $executable_file"
|
||||
else
|
||||
echo "Failed to generate executable from $output_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
57
test_script/sysyll.sh
Normal file
57
test_script/sysyll.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义输入和输出路径
|
||||
input_dir="../test/"
|
||||
output_dir="./"
|
||||
|
||||
# 默认不生成可执行文件
|
||||
generate_executable=false
|
||||
|
||||
# 解析命令行参数
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--executable|-e)
|
||||
generate_executable=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown parameter: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 确保输出目录存在
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# 遍历输入路径中的所有 .sy 文件
|
||||
for sy_file in "$input_dir"*.sy; do
|
||||
# 获取文件名(不带路径和扩展名)
|
||||
base_name=$(basename "$sy_file" .sy)
|
||||
|
||||
# 定义输出文件路径
|
||||
output_file="${base_name}_sysyc.ll"
|
||||
|
||||
# 使用 sysyc 编译 .sy 文件为 .ll 文件
|
||||
../build/bin/sysyc -s ir "$sy_file" > "$output_file"
|
||||
|
||||
# 检查是否成功
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Compiled $sy_file -> $output_file"
|
||||
else
|
||||
echo "Failed to compile $sy_file"
|
||||
continue
|
||||
fi
|
||||
|
||||
# 如果指定了 --executable 或 -e 参数,则进一步编译为可执行文件
|
||||
if $generate_executable; then
|
||||
executable_file="${base_name}_sysyc"
|
||||
clang "$output_file" -o "$executable_file"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Generated executable: $executable_file"
|
||||
else
|
||||
echo "Failed to generate executable from $output_file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
3
test_script/wrapper.sh
Normal file
3
test_script/wrapper.sh
Normal file
@ -0,0 +1,3 @@
|
||||
sh ./gcc-riscv32.sh -e
|
||||
sh ./sysy-riscv32.sh -e
|
||||
sh ./exe-riscv32.sh
|
||||
200
testdata/performance/01_mm1.in
vendored
200
testdata/performance/01_mm1.in
vendored
@ -1,200 +0,0 @@
|
||||
100
|
||||
-4 4 -60 -32 -67 65 7 32 -77 98 -88 85 13 -67 -44 -13 31 62 -12 53 7 -90 -58 3 -5 10 -72 22 -41 76 -12 80 27 -64 4 55 -21 1 -42 42 -55 -93 -34 -90 41 -96 -98 55 36 -19 16 -30 80 -32 -63 -28 67 -95 99 99 -96 -52 -9 -52 18 27 19 14 27 -94 -100 -53 -48 13 85 -72 -69 78 39 79 28 85 -19 22 9 23 -85 46 65 68 -89 53 17 -39 14 -73 -76 87 -56 -27
|
||||
-51 -55 1 -10 88 -22 4 17 -24 89 -26 -81 -33 93 -89 74 29 -80 -73 -37 92 -6 -22 -23 -99 49 41 -99 15 -32 35 2 20 22 58 -89 61 59 -54 31 20 -87 93 -3 49 -72 -90 -10 77 -92 84 -72 52 74 -35 98 44 -33 89 -82 -88 51 -49 27 26 -87 -72 -57 82 -19 -26 -17 -30 32 3 60 90 -24 30 -81 70 -39 100 13 -59 -56 6 -26 45 73 49 -44 -41 70 46 -63 -53 82 -10 -46
|
||||
62 10 73 29 -95 93 -27 31 -16 83 -43 6 70 -64 -46 -80 79 -81 84 79 -85 -57 -78 -63 -11 83 -81 37 -49 90 65 -48 39 68 -31 52 85 15 -32 51 -30 -56 57 -92 91 3 -60 51 66 -33 6 -92 62 50 16 -67 -44 69 90 24 -94 31 -9 -75 -33 50 33 -99 88 -78 -98 -23 72 -16 -87 -19 -14 -71 -52 -98 -94 -88 82 -13 68 85 73 -93 19 -54 -64 -75 -62 99 24 -35 61 -80 -61 -12
|
||||
87 -50 45 -11 -8 -93 -66 -96 36 13 -100 78 -25 55 -63 46 81 -33 -83 -8 19 -7 -14 -4 16 -31 -88 2 -84 68 -85 2 -46 66 82 -49 -86 99 -17 27 -51 -83 -96 -92 89 4 -25 47 -52 -25 -63 -65 96 -20 -71 -82 -43 38 -95 -61 -40 -1 -82 -18 -98 -72 -56 62 -84 72 33 -47 97 99 -39 78 -17 -48 -79 87 6 -75 88 -48 -57 9 -3 -5 65 -50 11 -53 56 -75 -51 87 70 -33 -11 18
|
||||
-99 -46 -5 15 13 -56 -82 69 -48 3 6 89 -17 94 -32 64 -85 25 -18 -82 66 92 8 36 71 32 12 -21 -53 -52 -75 -41 50 21 -47 12 -6 8 -13 -49 62 40 -11 91 -9 19 55 -95 -48 -72 -26 11 -81 -38 -49 -12 -93 69 -68 28 -69 26 94 1 -99 -25 -68 -40 46 -11 -92 -38 -21 -77 -33 -91 39 4 34 57 50 -13 9 -69 45 -76 -72 29 -81 -36 36 -92 -48 27 -45 -69 -74 55 85 51
|
||||
66 93 7 57 92 94 -84 89 -67 30 -10 -83 -6 -51 81 -22 -62 -24 30 -57 -90 61 58 94 39 -16 8 -66 -56 54 -53 12 44 -82 -55 34 8 34 -32 -67 51 89 -47 52 34 67 -3 -81 34 -53 72 -75 50 9 -93 38 -28 -3 46 31 -92 -93 67 -12 -14 -17 -33 10 -15 -88 -48 -65 0 -15 81 25 -42 75 -95 34 90 -6 -90 -35 -68 70 -15 36 19 -94 -57 8 -24 36 -41 59 -29 41 87 62
|
||||
-68 48 -9 1 1 10 -42 10 35 85 32 26 -35 75 -37 3 -99 39 74 99 76 94 11 -14 -29 96 -17 -48 46 9 -1 25 65 41 21 91 51 49 -84 64 23 -34 13 61 35 61 99 -29 -21 12 63 28 51 17 -23 100 29 -47 32 -14 -48 -20 -63 45 -40 91 23 -42 80 36 -27 -76 36 89 2 -14 91 89 -87 -97 28 -34 3 -100 98 65 22 -100 -25 66 -53 75 -62 85 -38 -45 -49 51 -93 -59
|
||||
-54 -32 -2 -81 60 35 14 14 -4 21 52 4 32 65 -6 60 84 6 -72 -72 70 -63 4 98 -4 8 -23 -31 -2 -6 78 56 -99 -5 47 -60 0 15 56 -48 18 35 35 1 -9 91 24 -46 -30 83 56 -98 39 -1 59 -53 30 69 -79 -42 24 91 71 -97 -35 3 -25 84 22 82 -46 68 62 23 71 -79 16 -73 39 55 -12 17 42 -66 -60 73 -36 -64 -68 39 -58 95 -72 10 -8 -9 -14 51 -44 -71
|
||||
5 38 24 -38 36 -59 21 43 93 -8 93 41 -11 8 93 8 90 76 54 -90 -98 77 22 33 44 -34 25 -71 -70 87 44 56 -98 -61 -79 25 33 -62 72 8 -13 -15 43 75 74 79 -23 52 -58 -9 27 -7 -27 -53 -76 -92 -95 81 -99 63 78 9 -43 -82 -80 -8 71 18 -40 -62 -57 90 -10 20 -44 -27 64 50 -27 -95 -76 94 42 -54 77 67 -29 -27 -77 40 83 15 7 30 -45 81 -86 -55 -85 -78
|
||||
-52 59 21 35 0 -9 95 34 -35 -47 -100 14 -50 10 19 78 64 30 76 96 47 22 -83 -29 0 11 -9 -28 -42 -31 56 36 -10 -26 53 93 -50 94 67 -5 17 -99 -77 -83 55 4 93 -46 -54 4 97 -16 30 8 45 -40 -88 -19 -36 -79 -13 -96 34 -59 58 77 86 -75 -27 -28 76 4 -45 -40 27 -59 -73 -54 -17 -27 72 62 24 -51 19 35 99 23 -33 -95 78 6 74 -71 -97 74 -72 14 62 98
|
||||
62 72 3 50 100 -58 77 -20 -89 -88 -87 -69 19 -29 -25 -29 14 -80 61 -36 -29 81 55 -73 46 62 6 69 11 69 -73 -100 62 18 -71 -85 -54 -73 5 18 98 -44 77 -50 -3 92 -5 87 25 -42 -92 -27 77 -57 -78 40 21 -83 -10 -9 51 50 31 -33 -67 5 60 -71 77 -1 10 -63 -57 -27 73 -91 69 97 -77 38 24 -53 -38 -2 85 -62 25 -64 -83 -23 33 22 -10 87 82 -53 81 79 -31 42
|
||||
71 -46 -29 75 34 -22 26 -27 -70 -60 44 52 -24 45 -37 -82 -64 -6 82 -48 97 92 -73 33 -2 83 51 -6 -23 -35 -86 -44 11 -13 -10 -14 30 2 -46 47 -53 4 39 70 -94 32 76 -97 -1 100 -12 -70 -84 -85 -54 56 15 45 -4 -98 13 59 -36 -9 42 -92 11 -60 61 92 -38 32 35 -49 23 48 84 67 30 9 71 17 38 -44 -62 49 -94 57 66 -38 52 78 38 -66 6 0 13 -69 40 25
|
||||
33 -84 -44 -25 -36 28 -92 -24 5 30 72 2 24 2 -27 90 1 -30 -97 -87 95 9 -82 -42 43 -85 13 48 1 33 34 -80 22 19 30 20 -70 51 92 -27 -41 -83 20 -88 -28 36 98 8 -53 -29 -76 14 -23 4 -45 63 -69 -99 77 -35 -12 73 59 6 -4 -13 -5 -64 15 89 -72 -37 -83 -91 87 -31 -33 -66 67 0 -84 -87 49 18 -69 -79 -7 -97 21 -74 -3 49 -26 -87 45 80 -19 -83 90 -93
|
||||
-84 -10 -13 -85 42 -23 -35 84 80 -78 -4 28 40 -4 77 43 33 10 -64 30 15 61 41 -66 32 -43 72 -36 38 63 79 -4 -30 -73 66 -55 31 29 97 15 -46 -75 -25 58 -83 83 -85 7 -4 -92 -72 73 32 61 55 -57 -86 -32 -13 64 21 76 -19 -55 31 -45 -28 -44 -30 -28 9 -35 82 50 -74 10 1 -25 78 -89 41 -38 41 -64 78 82 -16 -28 78 -53 47 10 29 5 -52 94 -88 0 -29 -52
|
||||
-86 -69 80 -49 -19 100 45 -25 25 -37 46 -51 -42 84 65 80 -20 89 -80 -74 42 13 81 73 43 65 67 -20 -23 75 -100 -24 89 96 -53 33 66 42 29 -62 73 -9 3 1 28 -70 35 -29 82 0 -13 -41 -22 82 90 -34 60 -21 90 -97 -55 13 41 -57 -46 88 61 -73 -85 96 48 9 -89 20 66 -70 16 -64 -72 -73 -47 13 -99 -55 -91 10 -22 -33 -60 -4 -81 -69 -55 -68 -94 28 -42 -20 -14 27
|
||||
-40 10 -49 94 73 26 -90 97 2 2 -83 28 55 32 -82 -60 11 -87 -32 -100 11 -57 4 91 36 -46 8 55 -1 -1 25 -5 19 81 -47 12 -77 18 -99 21 -83 -69 -82 11 39 35 1 80 24 -53 -58 -15 -85 -66 -81 -96 97 -61 -77 6 88 91 76 -45 5 78 -36 -87 -68 54 20 -70 -48 -90 55 82 -85 48 24 -66 -19 28 19 62 30 -15 -75 -39 97 14 62 -30 -33 91 -82 81 33 81 8 -67
|
||||
10 76 83 39 -88 -59 64 -63 92 84 94 -70 44 31 97 -95 43 43 -53 32 -61 -63 57 -6 57 7 -99 -54 45 69 -52 12 68 -88 -75 67 -93 19 -56 -82 -45 69 87 97 94 -50 -50 -26 94 32 96 -71 -47 -11 -17 1 -60 96 74 0 30 -78 53 -63 -34 -6 70 -77 -17 -34 -43 49 27 -26 -39 -13 62 87 84 -48 14 38 -50 47 -2 33 55 85 -42 -56 -3 -51 8 8 89 96 -93 65 81 -93
|
||||
4 43 52 21 -61 26 17 -12 -14 -78 2 -48 59 -80 69 73 -30 -12 99 36 -33 -89 38 -80 -93 -36 -97 -29 50 9 -56 59 -3 94 46 35 96 -25 56 -28 -10 -68 -55 43 -74 30 -73 16 54 -80 14 12 81 60 -57 -76 36 22 26 29 6 92 -45 82 -19 -36 -61 50 -50 89 -67 -10 -88 98 -59 -69 8 -63 -34 -72 -35 78 -2 -85 24 35 -13 65 29 -5 54 87 20 -82 -90 29 90 94 46 -86
|
||||
80 49 30 54 -91 -10 73 -18 51 46 -94 34 59 46 -17 -42 -29 31 0 22 85 -67 61 15 -31 -10 78 32 -76 -86 67 -13 20 -20 -40 -22 33 9 81 -62 87 -39 76 -55 -15 3 89 71 76 63 -53 75 -99 -21 -20 70 22 -77 98 63 -20 -77 7 -29 43 62 -80 77 40 -10 56 87 100 -27 39 -63 5 7 -97 -11 61 19 -74 95 65 -96 58 -49 -46 -84 16 34 -33 -46 47 -44 56 33 0 -61
|
||||
-71 -3 51 -3 16 21 13 -95 17 97 -59 -77 -15 -79 -88 -98 -92 2 47 -9 -6 39 -47 89 18 34 9 75 84 82 -65 21 -70 -6 -13 -7 -50 85 19 -31 -73 -99 -88 79 19 63 4 -99 -68 65 -49 94 -58 -52 83 59 -39 31 -24 81 96 -83 4 -17 -39 46 -1 -19 -75 -16 2 13 -76 31 -80 97 -46 -3 -71 66 60 16 89 -71 15 69 -25 45 -75 -16 -76 -83 -28 46 -79 94 3 -61 84 -94
|
||||
11 -56 -91 74 34 -2 -46 -95 8 -31 29 -48 -73 -18 34 72 -44 48 -44 -57 21 -93 -81 -11 -51 -70 -97 4 -81 20 4 -27 -57 -25 -83 -51 -31 41 12 -68 -4 -8 -96 35 62 -2 -29 -52 61 62 20 70 26 23 91 6 52 -63 -57 65 -77 27 76 -62 -46 61 -50 55 11 71 -31 -66 -45 -87 -51 2 -44 25 62 -19 -58 74 -54 -32 68 -13 -32 -2 3 23 -92 -28 -19 -49 20 40 89 28 36 32
|
||||
64 -66 -61 -66 93 48 16 86 20 95 8 -75 50 -54 -94 -11 -26 32 -5 41 -97 -33 68 14 58 38 -70 85 67 -18 94 8 11 28 33 -67 85 47 32 -36 -95 47 25 9 -64 -20 -23 -62 -81 30 -86 -42 47 27 40 30 -44 -84 59 -68 5 -18 47 66 48 -1 -33 56 60 -27 -60 19 23 -96 57 -25 21 -96 63 82 -69 -73 35 89 58 -69 67 -84 21 18 96 47 82 6 31 -38 37 -94 72 69
|
||||
-57 82 74 86 -39 -19 82 -16 61 41 -20 -82 -96 -55 -37 35 -31 51 -66 -21 55 -84 -39 58 -58 35 -55 56 58 -23 -48 92 -21 91 20 81 -14 68 -60 -21 -53 29 -76 42 -26 -55 60 -27 49 70 48 -61 82 -78 51 47 -73 40 10 -29 -27 9 64 -91 38 8 -26 -43 -1 -46 78 50 -53 -25 94 -54 -17 66 81 34 -94 67 48 -96 -24 -55 -92 31 -21 81 23 -45 -83 -8 -89 86 -13 -78 -41 -65
|
||||
-86 -9 -20 -45 -27 -29 38 89 -62 100 53 21 -17 49 -78 60 -52 65 90 68 31 33 9 64 47 -31 75 26 -74 -97 95 -81 -66 98 -40 -47 -95 79 -30 24 21 58 11 63 -73 -41 32 2 -48 44 20 56 15 34 13 -10 4 -62 -8 -40 -79 34 -95 -94 22 80 87 -59 -9 -35 -74 -44 -37 -35 46 61 53 -98 -2 -30 12 60 -94 -100 -91 -61 -96 -37 49 75 -73 43 -36 17 -31 55 -43 -90 -62 46
|
||||
-46 -41 82 -55 -13 -22 80 51 -18 64 -72 43 30 -72 -28 -68 89 -13 79 -81 57 42 -87 -72 63 1 41 61 -57 43 3 -41 -18 51 85 -79 -45 76 -93 -46 18 -100 -74 -97 -58 29 49 52 -37 -20 57 -6 -50 88 75 59 39 -59 -34 -71 -80 -38 -75 48 -73 69 -2 -52 46 -95 8 -21 7 -84 10 54 -28 -60 91 77 -71 -75 39 1 -5 2 29 47 -1 -93 93 -40 -81 -27 -98 95 -35 -51 -96 31
|
||||
40 29 -82 16 84 -40 79 -89 -38 8 82 2 -89 42 -36 -67 52 62 2 38 55 -36 -58 99 32 94 67 -88 59 -95 -72 50 38 69 -95 83 -19 -62 54 49 -96 -96 -79 -19 94 -25 -6 -24 39 17 -33 35 -84 -14 -35 -10 7 -36 67 -91 1 75 50 53 65 49 -95 3 81 95 87 84 -98 95 -15 -87 39 78 -92 28 -68 85 83 58 -88 -84 -52 58 -48 20 -15 -44 24 -86 -88 -71 -90 21 -50 39
|
||||
40 71 -99 100 -31 -99 0 -77 26 -28 88 72 -53 73 97 34 -76 56 -7 -14 56 58 -88 -37 98 98 -19 -29 74 44 -61 -8 11 -51 -52 80 -45 -61 68 -96 95 -52 -33 63 -76 34 -70 -15 -77 0 -38 54 4 -12 84 -9 -36 -60 81 67 -83 -88 38 -38 36 38 42 35 -99 -75 -56 61 -98 -44 40 -33 78 43 -12 96 -16 49 25 79 -56 100 30 89 -65 -76 1 87 33 38 -95 51 85 3 89 57
|
||||
98 -15 86 -57 -86 34 -14 -76 -32 44 -50 98 5 51 57 -82 80 11 77 21 -64 71 96 -93 -43 56 84 49 -86 18 74 55 65 -69 -26 55 55 -8 59 -5 -45 43 70 -39 46 -44 -2 -15 35 -33 -66 -56 -15 -21 60 -11 41 -44 -7 -21 -8 2 -18 -75 27 -68 72 -2 45 -11 3 36 -44 23 89 61 81 -65 -21 78 66 51 -94 -59 -76 -52 -42 67 30 -49 51 -91 -63 -53 -87 -7 -2 57 -40 60
|
||||
57 -30 -45 60 7 53 58 69 -74 -38 -11 -20 -62 23 -79 53 -18 -97 -28 25 -79 27 -64 8 4 -58 62 -21 -12 -4 -63 9 71 -30 -38 -72 -10 -20 -98 -43 -2 99 -43 52 -2 -78 -17 29 -76 -81 -94 -61 75 -71 -74 9 79 -31 15 91 -98 -34 85 -12 -65 -72 -37 3 32 -44 15 -4 -32 85 -10 -21 -41 67 20 -66 -66 -53 -20 7 77 60 0 -36 -2 -20 -27 31 50 50 -41 12 39 -12 74 -96
|
||||
14 2 48 2 41 57 -22 -39 -21 -2 -89 27 -94 -43 -76 67 -66 80 41 -54 30 -37 -27 -49 -74 -57 9 -15 64 -89 1 -95 -22 -41 88 -33 83 -6 -63 45 -86 71 96 -96 5 0 -37 6 62 -84 -38 -77 -70 -49 -82 -27 19 -5 1 90 94 -24 -88 6 -25 -50 11 -61 81 84 -79 45 88 -98 93 -20 11 -57 25 -61 -36 57 6 77 77 24 -1 -90 11 35 -72 -22 46 49 19 -33 -37 -84 -11 -4
|
||||
-100 45 60 -33 58 22 86 80 53 -35 51 73 -50 -64 62 43 42 10 57 -9 -15 39 -82 73 20 -57 -18 -5 11 -76 -29 -78 83 -75 86 36 5 -62 85 70 -85 65 -5 13 78 29 -3 84 -39 -37 -88 -46 -1 100 -5 -90 44 70 -8 30 -29 25 72 -11 30 -31 52 -38 71 -10 77 -80 -13 44 25 -58 -51 29 -28 -8 90 36 -5 -40 -77 98 50 67 22 53 74 89 -70 32 -52 84 85 73 -10 -13
|
||||
-73 50 -92 27 -18 95 -97 5 88 3 89 -32 -14 -66 -27 -53 46 18 -80 41 87 -45 91 -41 -42 24 17 -76 53 85 47 -10 5 62 -8 -44 29 -75 -2 -29 43 -44 26 -6 17 -20 84 20 -99 38 -19 72 -53 23 47 -20 10 -88 72 -100 64 -86 -44 -39 56 -45 -7 20 1 32 -36 -94 37 -9 -50 -39 40 -66 46 -40 93 22 -63 -31 -53 93 -86 64 -35 77 18 -59 -2 50 -47 -9 -62 77 92 2
|
||||
95 76 70 64 -41 12 -61 -99 56 -63 88 88 57 -33 36 -35 58 -85 -85 86 -74 -29 -33 23 58 28 -52 61 -64 -58 6 -46 98 -37 46 82 -20 66 -95 27 17 -97 -49 -25 35 54 -57 77 3 25 23 58 2 -45 -26 -63 -98 8 38 1 -38 29 34 -76 73 84 63 -44 75 63 18 -88 17 -57 -26 53 18 43 49 -49 -10 -10 -35 94 10 -88 -37 66 -46 20 43 -65 55 81 -62 -47 -25 -64 84 -24
|
||||
99 -85 -67 -29 -53 88 -94 -36 -79 13 -12 -19 24 35 -100 94 -59 89 -90 -81 -84 87 -11 -67 -64 -14 97 43 35 79 90 -47 44 -16 -59 14 72 37 -82 -34 16 67 -35 3 55 -49 63 -49 -14 -7 -92 -94 56 -38 -76 -92 -82 61 -98 -85 25 25 59 11 -35 -69 85 -92 98 -15 45 59 -56 77 28 11 77 48 49 -15 -64 66 -53 -27 82 17 -93 -12 47 -6 85 -93 46 -82 38 -68 77 -6 -81 -40
|
||||
-52 31 -10 -68 44 -1 24 -81 -6 70 25 -90 78 -33 76 -98 43 -63 35 49 15 50 11 46 -54 84 19 -55 91 -89 58 -44 -67 90 -94 58 -21 22 33 -26 -55 38 -98 58 83 -59 -55 94 -27 20 -84 2 87 19 -11 -2 -60 -56 -4 29 -29 75 42 51 -66 46 16 -33 -37 -83 86 87 1 67 14 -31 32 -52 -84 -38 -73 -14 92 -64 54 61 -14 -77 46 -74 83 81 -95 -68 -47 55 -27 38 96 -33
|
||||
-24 -30 27 -99 32 -93 100 -50 -99 -94 -48 8 30 -20 -57 -4 46 -73 79 55 -99 48 -66 65 -89 -77 81 -77 -39 -94 -49 66 5 53 -16 -3 93 -51 85 95 -17 67 62 -67 99 -34 15 60 -12 -75 94 -80 55 -72 -29 52 87 -53 -51 -47 93 -16 10 49 40 -28 31 10 70 -16 14 -4 80 19 70 41 44 81 -1 60 -50 -91 49 -69 81 2 1 75 -91 26 -91 79 25 74 12 82 77 67 -79 -27
|
||||
8 -85 51 83 -39 -20 -73 -46 23 -21 -31 -86 56 22 18 20 -42 22 -81 3 -15 -3 -24 -62 -82 19 -92 -81 77 -35 93 61 -39 92 -29 81 78 18 -57 -50 -65 74 11 -39 11 37 7 12 -65 -3 -83 16 -90 -2 44 100 -34 18 -99 -2 77 86 -64 -28 -37 -10 88 -35 -15 -6 48 27 14 -40 73 -32 -41 77 87 -66 -89 99 -48 15 -38 78 -94 -15 -59 -77 5 -45 -43 -56 -61 90 -18 -61 -15 -6
|
||||
-9 -66 -59 -52 -38 84 -51 -60 -20 8 13 -86 -50 47 90 -100 52 34 -52 -94 50 20 20 79 16 77 -87 14 30 -84 1 83 -7 7 43 -46 -23 100 67 58 -75 -19 -69 -4 54 -17 61 -72 73 -76 -75 77 -75 93 24 -98 53 -56 38 37 -97 97 11 -9 83 -43 -82 80 73 -51 -9 -51 47 73 -81 -93 23 11 29 -22 87 -77 78 -84 -29 94 -17 90 -22 80 -77 18 2 -54 49 25 29 24 -54 64
|
||||
-96 -93 62 -23 17 -67 -55 69 -39 30 31 -33 -70 -98 91 76 98 -71 18 49 66 -84 51 -76 -36 -19 -54 -86 -28 -34 -77 75 2 19 31 -65 -12 98 29 15 -57 86 -49 57 -66 -36 -7 -49 54 62 -86 84 96 -25 84 84 94 -23 1 -99 -55 33 28 45 83 58 11 7 -88 -23 -75 40 -20 40 52 -34 -28 -24 -80 56 87 20 68 69 46 -3 -87 -5 -15 67 28 100 -18 -65 67 11 -18 -78 16 69
|
||||
-20 73 -53 20 -4 75 -7 -24 -36 -36 -29 -33 -37 72 56 36 -19 60 61 18 63 64 -20 67 -33 -23 -56 66 -81 -17 75 94 5 91 33 73 -75 6 38 9 -37 -56 -33 -16 69 33 12 -87 -9 -23 -87 2 24 -91 -18 -56 -31 -24 27 -56 15 -12 -25 -85 -48 2 48 29 -76 -89 -84 -38 -63 54 -61 32 42 -41 67 66 -68 32 -55 77 -8 -46 6 -41 -20 -67 33 -13 -74 27 66 -7 34 77 -100 32
|
||||
-64 -27 -17 95 43 -16 -64 23 99 97 32 -81 -27 17 -2 -4 -32 -17 36 25 -83 -76 22 -21 44 73 -16 76 -40 29 100 9 66 39 -86 62 -66 -71 15 -4 -69 -27 43 99 -57 -48 89 81 -94 -2 -50 6 6 42 55 62 50 14 -91 38 25 98 94 77 -94 23 16 -67 75 -54 48 -26 2 -43 36 -69 61 82 19 -100 -48 19 -23 34 0 18 -72 77 -13 66 -18 39 31 -81 -89 -65 0 -20 44 23
|
||||
60 74 -67 81 -6 40 -38 3 53 -56 -2 98 -2 51 -50 -77 -12 -84 -43 78 -30 44 24 42 -36 49 -58 -35 30 -92 -81 31 -5 98 -3 -10 -9 -80 -79 80 22 -39 77 35 -21 0 -87 38 48 -76 -47 64 14 89 92 -49 -18 -49 11 -99 -17 30 19 4 -83 6 -5 -90 -61 -26 63 76 72 84 51 7 42 -26 56 85 23 39 34 -86 -42 71 -68 -56 37 80 -50 88 -86 -85 -60 -6 -31 -37 83 83
|
||||
13 43 96 3 92 -23 89 -64 -8 44 42 -58 48 81 -71 55 63 25 -9 49 -84 -95 53 -72 44 -3 23 -61 24 -51 -37 -58 26 -26 46 -54 68 -70 53 -13 55 -100 34 81 22 -85 66 -52 85 54 60 -100 15 -39 74 26 33 -53 -99 38 9 -45 10 -12 31 -59 -69 9 -87 -86 -73 -56 86 -36 89 94 -85 -4 -74 66 85 62 -48 -42 89 -65 78 -38 -39 -99 13 -86 -11 35 -84 92 -24 99 -87 -13
|
||||
22 -12 -58 19 -92 -88 -51 39 6 -21 -8 38 19 57 -35 66 28 -46 46 29 45 57 48 33 97 -91 53 87 -39 87 -85 -83 61 2 -75 47 32 88 83 32 -16 41 -93 30 -14 43 75 -11 23 -26 -42 63 -94 78 -89 -78 -30 -5 -70 -89 -97 31 25 -66 7 5 -60 61 -7 80 52 -6 -59 64 28 42 -45 -23 -92 1 65 -73 -80 -67 -3 22 91 -91 0 -1 -89 85 -73 -5 15 -26 34 36 -74 45
|
||||
-66 46 79 -58 -93 -13 -88 -19 -72 -19 81 -1 -44 8 -4 -77 43 11 -68 -56 -52 -18 40 17 -33 -4 78 84 14 60 -2 84 94 -44 35 27 -7 51 0 48 84 60 70 -6 1 83 -74 -46 43 -32 -56 7 -7 26 -96 96 -56 61 -54 2 -59 -88 11 -30 -34 -40 -34 -16 73 91 75 -61 -17 69 19 -91 -2 13 -49 -7 65 32 61 -5 78 -35 81 43 -62 80 18 65 66 76 82 79 -77 -33 64 -28
|
||||
30 22 96 -64 -67 46 -94 94 -1 -16 44 18 -45 -19 -1 11 -69 -50 -42 -84 -81 96 -95 -75 0 97 73 -90 -8 -19 9 54 75 69 15 -32 54 -69 11 -89 -82 14 93 37 -87 39 -49 -52 -8 62 -69 5 12 -29 -61 43 22 3 14 -91 15 86 -65 -1 36 -55 -35 42 -53 -11 41 64 -41 78 -82 18 27 -53 -100 -72 57 26 19 -25 85 40 -97 78 -91 96 80 -2 65 -77 -41 90 95 -55 74 25
|
||||
-75 50 -52 -40 48 -92 -23 29 3 -82 76 49 49 -26 70 -41 -29 69 -20 -55 -89 28 -21 61 28 -70 7 97 -40 -29 31 -39 64 -46 -75 -76 -51 99 -1 -73 -61 22 99 -7 51 -70 -17 -44 -75 23 -81 54 74 42 -13 35 38 91 11 -100 65 38 63 78 -71 -45 64 37 -29 75 19 30 -86 -3 71 26 -77 15 97 -25 13 34 17 90 -74 16 -33 97 84 -56 74 64 23 1 -78 39 -3 -93 -36 29
|
||||
-31 23 -83 -90 88 50 19 41 -32 -40 39 18 69 16 -95 74 67 -85 -13 -21 -17 21 53 13 82 30 72 -91 78 -27 -31 72 -76 -55 65 28 22 5 50 70 1 97 66 85 -80 -65 -45 43 -19 -92 -57 95 7 -93 99 -47 78 36 86 31 -76 -11 44 44 88 -13 -79 -4 30 94 -52 71 -1 -38 69 17 21 96 22 87 98 -29 30 9 -37 -44 -78 -15 67 -87 -62 -29 -42 -61 -93 14 -87 50 -7 38
|
||||
3 14 -49 52 51 -29 -86 -9 -55 -77 -73 47 28 23 -60 56 75 58 -12 -96 71 -22 62 -8 24 -2 23 -21 -46 -33 50 -93 33 39 -59 26 -57 62 63 24 36 16 27 -79 -62 -24 -56 -78 34 -72 18 51 30 56 31 35 -58 65 45 60 -51 -6 -23 100 48 85 21 99 96 -83 58 -24 -39 66 -7 17 -75 -83 -45 -21 79 54 62 96 32 -76 92 68 52 53 17 -28 89 70 90 -16 75 -85 100 75
|
||||
-83 -28 -73 -11 -53 -58 47 52 -93 -35 -6 -95 25 -22 84 98 -46 33 47 11 -74 -30 -8 15 -60 -85 -99 77 -46 -88 -25 14 -56 -72 2 -28 82 50 89 47 -23 -40 -39 19 56 -47 92 -20 -73 -39 -39 29 2 -12 85 20 54 54 -37 36 -65 17 88 -33 86 73 -60 -87 79 -15 -21 -69 -79 97 30 -54 -34 -85 36 48 -55 28 -28 -6 36 -89 -56 26 98 -45 99 6 -42 -76 44 91 70 -69 34 -25
|
||||
89 50 61 59 -34 -91 -90 -1 -51 -4 57 21 -46 -59 78 7 100 82 -74 4 48 65 20 96 96 -35 40 46 -14 79 80 -35 -47 -14 41 41 -16 47 98 -88 -97 45 17 74 51 38 -64 97 -77 87 -26 47 -58 -68 -96 12 99 53 -45 10 3 -61 99 37 15 -85 -23 78 -53 -48 -69 41 -24 2 42 -64 51 -17 -1 70 -31 -68 60 -78 -66 -66 20 23 -71 -32 -62 72 76 21 63 -9 49 -2 -50 -17
|
||||
43 -79 89 18 -99 93 -77 73 -75 73 -91 59 -78 -71 -96 -18 -54 -35 49 41 -77 11 -49 62 44 -49 6 -40 61 -52 -74 -99 30 -42 39 69 -86 9 -76 18 60 35 8 -78 67 87 38 -77 76 -75 34 46 61 4 -55 14 -24 -55 5 47 34 84 -97 94 -67 -50 -81 7 9 68 -52 -83 -9 92 1 79 -93 -11 18 17 -3 -41 54 -12 98 -47 -6 -60 87 -97 100 3 76 -6 23 -51 80 76 -10 64
|
||||
8 34 90 46 -38 -78 13 52 -79 28 -93 -62 76 21 -31 20 -61 -100 -46 31 -83 41 44 -25 -48 69 96 95 -56 43 28 -98 56 -98 62 -46 -68 -59 20 -83 -81 89 29 21 -96 68 57 -38 -98 84 34 -50 29 -30 -64 25 -19 32 -97 31 66 -22 -37 -83 11 -15 85 12 31 -51 -30 -60 80 46 -54 -56 -97 71 51 -94 -55 -96 81 -14 -16 42 16 -57 -14 83 42 24 24 -12 13 -32 -50 -23 35 -78
|
||||
39 -2 -75 -24 74 14 -97 27 58 44 -55 8 63 45 26 -47 -89 -4 -25 -69 13 64 -62 93 -32 -42 50 76 -51 18 41 29 -80 -86 -14 -58 35 -5 -32 -60 -100 8 -1 -92 90 63 3 -41 -39 29 -74 44 61 9 -81 -30 7 47 78 55 -7 -69 -93 38 4 5 -81 -66 11 -67 -72 39 -23 -75 -53 19 -30 -58 -10 37 24 31 -13 93 28 -35 40 22 -47 -39 -86 -51 54 -45 41 -30 -34 85 -16 -71
|
||||
-67 89 71 -84 12 93 94 46 -41 78 25 91 -83 -16 70 -93 -12 2 69 -56 -2 100 3 -45 3 -79 -99 -23 -51 -29 -42 -87 82 -93 -27 77 -10 0 -67 62 66 60 -19 -6 -35 73 46 -37 65 26 -28 99 -71 -94 -2 27 11 89 5 -97 -53 84 6 -44 -24 54 -71 24 86 -4 -49 15 91 -86 71 -78 -67 -99 91 -11 100 -10 -96 44 -16 39 -69 -69 -86 16 -26 -75 -50 46 62 -21 -52 -31 52 56
|
||||
1 -15 43 96 78 -44 71 49 -93 81 -82 -73 -52 -90 -1 27 -47 68 77 -67 -71 33 68 -14 51 24 -66 -53 50 -29 -75 47 68 98 -36 89 -15 -98 33 53 -88 42 32 82 55 -28 -80 30 71 64 -5 18 -63 7 -43 85 -43 12 80 9 -36 -7 -2 -85 82 5 97 46 -10 100 81 -45 33 -75 -56 19 50 -25 -30 -31 -5 52 -12 93 3 48 -69 2 -28 99 81 27 -39 -16 9 -58 69 -85 -87 -43
|
||||
-53 -36 -3 60 -4 -23 25 -32 -42 3 -86 16 32 -11 -20 -23 17 74 60 -34 32 50 -61 19 96 -73 -14 -80 10 -19 45 -48 -54 -24 2 -36 80 73 -94 72 62 100 -21 86 76 -26 39 -57 59 -5 -32 -52 64 -36 -41 69 5 92 90 58 -63 -4 -90 74 81 -64 97 -29 93 40 -71 13 86 76 -11 -96 24 68 23 -36 -68 -30 -77 64 -21 26 22 -60 -91 96 -91 27 -41 99 -20 -35 -13 -17 42 -13
|
||||
23 46 -70 89 -90 45 39 -21 -46 41 85 -76 73 -7 -69 -80 63 95 -51 0 -48 93 54 26 -24 30 -88 98 -58 10 28 -26 76 25 11 55 -13 -51 -55 -5 -91 -20 -80 27 -91 16 74 -83 -17 90 -35 26 82 -87 -59 -87 35 -83 93 -88 -20 -57 8 -81 39 36 -73 -40 88 84 -40 65 100 -59 -5 -35 57 17 -57 -50 33 13 77 -14 64 18 -76 -62 -73 -15 8 87 -69 41 -47 86 -22 20 80 -96
|
||||
-51 8 -19 41 49 -21 -34 -39 50 -48 -47 -54 -46 55 57 75 -42 -47 -83 23 54 24 72 15 -78 28 38 77 59 38 -67 -98 65 -34 -56 66 -46 -79 69 41 18 -77 -63 43 100 -94 88 44 -27 21 -89 36 56 -26 3 -92 -29 71 -72 -70 73 -79 -38 90 23 49 -83 -36 68 69 -41 -29 33 89 -80 -9 42 34 -90 3 2 -42 -24 77 12 59 -91 -5 2 66 -30 -13 -53 -9 88 92 60 49 -45 75
|
||||
-40 29 31 -75 73 -16 31 -25 100 48 0 -23 -48 58 -88 13 -20 -88 7 -33 89 55 77 30 70 41 -45 -84 28 -51 4 -19 -11 60 -3 18 30 94 11 -91 -34 30 -100 -16 72 36 -73 8 26 -55 -65 -4 -60 88 56 47 31 -10 39 30 19 -20 38 -52 -66 -36 -21 28 39 -63 92 -46 39 55 4 74 30 25 -89 74 21 -52 -76 -48 98 -93 -47 15 62 -58 -95 -16 43 -91 22 -23 -46 94 8 -47
|
||||
16 80 -32 51 27 -54 -45 7 -38 32 -95 2 95 -46 79 -58 -18 -8 -100 36 -13 75 11 85 86 -46 -58 -45 -48 -94 -18 -67 1 94 79 -7 41 -17 56 -76 1 -95 -68 44 91 -63 -99 -57 43 -85 -3 -62 43 -44 -95 12 61 44 -57 -93 25 10 100 76 -90 39 -21 -90 42 67 20 86 -45 -31 -54 31 -20 -23 -80 2 26 -85 18 -1 -1 44 -21 -37 -42 -49 -38 -41 10 -37 -93 -1 -91 94 -30 -78
|
||||
-63 -84 96 -66 67 -30 -100 51 99 46 -71 -9 -29 -89 -32 -66 -76 3 46 -19 46 -21 6 5 57 96 -16 -77 -26 -15 35 -95 62 43 24 79 34 69 -62 -25 83 -28 -52 95 62 -9 59 43 41 -24 2 -64 21 -47 -92 51 63 7 45 3 70 -92 -67 75 63 25 69 -25 -1 -27 -94 85 -69 62 5 -4 65 18 38 13 4 35 71 -13 40 -98 73 38 50 38 -30 71 16 34 36 63 -7 21 -80 74
|
||||
44 -17 43 -74 42 61 94 -77 65 22 100 -23 7 67 -81 47 70 -6 74 -62 -41 17 -6 -81 -98 -9 44 46 95 25 -35 80 -58 82 -15 97 -77 -72 99 -50 29 24 -10 67 39 32 -18 87 18 -37 -88 88 -58 32 57 -71 -15 72 12 -82 3 81 88 51 -79 78 -23 -35 -5 57 86 -27 -22 82 -82 0 94 79 -95 -91 7 -31 34 -3 -56 -17 6 57 50 -63 -54 -26 -41 -94 48 -3 75 -36 90 49
|
||||
-69 -32 6 90 56 -4 -96 88 -28 -76 -34 61 -17 -42 -79 -83 -70 -99 8 30 -65 72 -24 -27 11 18 -83 -23 22 68 80 -4 23 -72 -23 73 -9 31 -76 -64 -6 -86 77 -50 94 -48 43 93 -79 17 -88 84 27 -81 -88 -4 -41 -27 97 17 -17 49 71 44 -69 34 100 35 -47 48 -91 -63 -44 -4 26 77 -83 -25 2 45 71 -3 -30 69 -17 44 -54 -31 -63 9 -3 51 68 -86 1 -85 41 -2 -94 4
|
||||
83 -20 -36 -31 -4 43 55 2 -25 -93 -42 81 64 66 65 95 94 -56 -60 4 -27 -57 4 19 63 -34 -66 68 -11 53 67 -8 -35 -87 -94 21 -97 75 83 -99 14 -87 -72 15 55 73 64 -54 82 -58 -70 47 -81 -75 -36 -14 -92 -4 29 98 -49 3 -34 -61 -71 15 -77 -89 47 -18 19 66 27 62 56 9 41 50 -12 19 -35 -19 -57 51 -41 -5 69 -81 14 -51 -17 70 64 65 -47 100 -84 63 -46 -15
|
||||
-26 10 86 -64 90 -46 -5 -73 99 -67 -33 58 -49 40 39 78 91 -73 9 62 47 -57 -72 88 93 94 -48 -12 -11 87 -12 -66 34 -7 -82 -72 49 -5 -17 -26 -2 -42 100 6 -27 0 -67 78 -4 2 -44 100 -95 -50 -35 -86 -89 31 -28 29 52 84 73 -15 47 -36 73 -45 -89 -55 -35 -99 -27 -80 -72 -27 82 79 88 40 -90 4 43 -13 86 49 -31 -54 -57 45 -23 55 39 -24 -16 -51 -38 -11 6 -87
|
||||
46 -65 -68 17 -13 32 -51 -51 -35 -89 87 -87 57 93 -81 -75 36 84 -53 83 67 52 53 -58 -10 -88 -70 -50 19 8 2 57 91 -71 98 -22 -82 75 -25 -62 -47 -84 35 -66 67 -12 14 67 -58 88 58 2 100 29 -79 -56 45 -80 -31 83 28 -95 -92 45 88 71 -20 -30 -64 -37 -51 -60 15 -74 1 -97 10 59 -43 49 46 64 83 -48 63 50 -92 40 70 -7 32 59 -65 -95 -57 49 52 -96 -5 60
|
||||
-16 -59 -40 -66 66 32 9 17 43 5 -67 54 40 -31 61 0 61 -37 77 15 -13 76 -68 -90 39 95 -51 14 -20 38 85 75 17 61 56 1 -38 -52 -67 -32 -100 -48 -22 -98 96 77 -96 9 -33 -99 96 38 -98 0 12 49 -33 -5 -87 -44 14 97 -51 -22 75 16 -33 12 -57 -19 -60 -71 -32 -43 16 36 3 -38 67 -53 -31 7 -51 -14 -4 41 59 -82 -81 76 -8 -63 84 56 -52 -29 35 -85 69 55
|
||||
75 -13 -85 30 -85 -98 64 92 63 -79 1 62 -82 -82 -38 14 56 -16 91 -14 -68 47 -89 -54 -67 -45 -42 -65 -27 73 -91 -47 29 97 74 13 46 -31 -99 -17 -32 -1 5 -99 -58 -66 57 9 91 -61 96 6 -33 -15 -89 62 7 -7 -74 68 68 -16 39 -11 95 -27 21 -42 -28 90 -78 40 8 80 -59 -88 -45 -87 83 53 85 -28 32 53 -53 -98 -91 -25 33 22 57 -87 44 82 -88 40 -79 -29 -77 97
|
||||
-26 14 -1 -62 -7 59 -86 -99 -35 33 -59 61 5 49 66 45 38 -15 99 -27 73 -39 -13 30 -73 -55 48 -87 -68 -91 -10 2 -75 25 -53 -61 -56 11 -30 80 83 -79 -62 -32 82 43 -53 -87 -88 44 -29 17 60 16 100 -68 -60 74 32 -66 5 -56 -73 -48 7 -46 57 50 29 -9 85 64 32 -30 -87 -30 62 30 -64 -33 -36 68 -52 -51 -28 38 -18 66 -3 -78 -79 -64 -73 42 -84 52 85 -88 -82 0
|
||||
-79 -38 -21 98 73 -29 12 -19 93 15 88 27 57 31 -43 85 -17 3 76 -96 63 48 -61 -18 40 -27 65 52 55 -26 -56 -32 -98 38 41 87 -97 64 32 -5 -53 -14 -34 -49 -32 77 -67 -48 -26 61 -88 50 -35 96 35 -31 -64 -7 -46 -13 -17 1 27 -72 48 -50 -29 24 -83 -67 59 15 -82 14 -55 -65 -27 0 7 -37 88 -52 -12 -29 -41 -55 -24 33 0 73 -46 -29 -37 -16 -7 -23 24 -67 34 98
|
||||
-23 -35 28 0 -17 93 55 -86 42 -79 -21 -65 37 95 42 -96 -48 12 54 100 -23 -70 42 42 27 69 -49 49 84 -43 -45 100 -11 -75 -36 6 61 -88 -3 -26 76 -49 -4 71 -78 -16 -85 24 82 -98 -30 -77 -29 -90 49 -40 62 50 81 -17 -96 -92 29 100 67 -84 -43 -9 65 -88 -71 -55 -21 -88 87 30 -17 -52 50 -4 -60 -100 -86 -68 55 35 8 -66 31 40 -87 -80 62 -100 76 -62 99 42 45 -73
|
||||
-70 15 -75 37 15 29 -53 -7 -58 87 -77 33 75 -23 95 -43 -83 40 33 -95 74 -90 25 12 90 47 84 52 -90 -20 44 -9 58 -82 50 -99 94 90 -54 -42 -24 -74 -37 92 -13 -71 -72 -5 -96 -25 33 -28 -34 -19 -62 -94 -76 49 12 50 -1 -7 -19 90 30 -9 -22 -52 -97 -15 -18 63 73 -17 -62 13 79 19 76 81 23 -90 50 -54 -88 60 91 89 -66 22 2 -95 -50 -15 80 41 94 -34 -5 -24
|
||||
66 -63 76 -84 48 92 -47 62 -39 23 -87 -54 36 -51 -14 -69 82 19 90 15 -41 97 10 -56 44 97 -97 -38 -26 12 -18 -64 65 -10 82 -42 48 -20 24 18 69 -86 -44 -81 -48 4 79 70 83 50 -14 59 -68 29 -85 22 17 83 25 60 -77 -96 21 17 -91 -77 77 33 8 11 -71 -55 -60 -55 -40 97 96 67 -57 100 66 23 66 -77 -16 56 -48 -9 -65 86 6 48 -4 92 15 -59 65 74 69 -70
|
||||
12 66 -24 -83 -33 -98 -95 72 0 40 -86 29 -14 -25 -24 -100 76 -92 -29 58 63 -92 80 1 -63 -40 -9 -99 38 -4 -75 29 66 -52 -74 81 66 -98 -8 -73 45 -23 -94 0 98 -45 -81 43 83 -73 30 -12 -7 -44 -24 12 42 -57 -87 -25 68 -67 -100 -69 -87 -64 -28 -55 -42 -30 19 31 90 -17 58 -20 -54 26 14 -30 -72 -75 -3 -88 -17 9 -14 -47 54 -86 -19 -48 -12 69 5 -13 -4 -27 -25 -82
|
||||
29 56 -37 21 17 13 -8 -39 83 0 -14 -62 16 68 -2 86 -51 35 -88 26 -4 -59 68 -69 -72 48 31 32 -82 56 17 94 12 71 74 -34 -70 92 14 -69 -45 -1 83 -63 -13 -88 -1 -91 64 -40 -65 53 -82 -54 -93 44 38 -80 46 -51 14 76 -58 -10 69 4 87 84 46 -30 78 57 93 -82 -11 -59 4 85 15 38 66 47 -87 86 62 44 31 -11 -37 -97 27 71 -96 -40 52 27 -16 38 10 62
|
||||
11 74 -7 12 -72 -56 54 -92 -86 -36 -31 33 -48 -88 -91 95 86 -98 3 -1 99 29 -96 23 -92 94 -64 94 28 66 -11 -1 -96 -43 93 82 71 -31 -87 67 68 58 62 97 -31 82 -6 14 18 -36 -33 -59 -30 -6 6 54 -64 -47 -8 -71 -93 90 11 46 46 -75 -98 12 -19 -38 88 0 67 -62 72 38 -41 29 2 -31 46 22 -71 -60 -54 38 -56 -24 72 -91 -57 38 20 73 14 86 -38 -87 -3 42
|
||||
38 -43 -25 -45 -24 -8 11 -32 44 -89 -40 97 -97 77 -85 -32 -69 15 -74 -15 49 88 -67 36 50 11 -88 55 -80 61 91 12 56 -65 -100 -4 -70 -75 7 47 -28 -29 -57 20 -11 -33 -89 -85 13 -97 -6 -56 -2 -91 -91 33 -88 -45 20 94 -20 81 76 9 -83 -98 39 -33 36 -7 -2 65 -78 59 36 -9 14 -15 -27 95 83 99 30 -69 -87 -25 -87 -50 -15 52 54 -4 -74 -66 -78 -19 -91 15 -30 -14
|
||||
-29 9 100 -12 -89 97 5 -12 -84 31 3 -76 -66 -75 -95 -52 66 76 -43 92 72 -83 23 24 41 38 -5 -60 -95 -38 -33 95 61 -75 95 -31 88 -75 5 -67 72 3 -40 -17 56 33 80 63 -31 0 -79 34 -21 14 87 -59 47 -57 37 81 43 -11 10 -18 45 -90 -72 69 -62 -94 84 58 43 -90 -27 79 59 -56 -3 -37 59 -16 -45 -31 43 55 -7 -18 10 -61 66 5 -52 -25 -39 -14 -89 -82 36 68
|
||||
-31 96 93 -100 32 76 100 16 52 -87 55 -94 15 2 -34 63 -96 13 -67 6 47 79 -48 44 -96 -19 69 20 11 58 50 -75 -93 13 18 82 87 85 18 38 19 -88 56 -80 18 98 -65 -89 99 -92 -93 24 -23 -18 95 -21 9 60 -73 26 88 44 78 -69 -73 0 36 -75 93 -13 16 -61 40 63 -37 14 -47 19 10 -98 76 -12 67 -64 2 -10 88 -26 -79 21 -73 98 48 -80 -50 -85 -53 -89 -56 100
|
||||
1 48 29 -19 35 48 84 -6 89 -83 51 45 15 74 56 21 -65 -92 100 -30 -16 -35 -24 -50 46 16 -78 -17 -32 7 -28 99 -76 -14 -69 -46 6 -93 -9 -2 -70 -3 95 -42 9 -67 73 60 98 98 64 33 -27 -64 8 -1 90 -68 -92 -14 -83 50 -16 98 86 -63 -41 62 -15 58 -68 -62 92 -79 -22 -63 -66 41 -34 74 -59 -16 -25 -49 -65 97 100 -43 14 -61 -29 -36 -69 94 -13 -16 61 32 29 45
|
||||
-66 -16 0 -15 18 -48 -57 -19 49 -74 96 70 -6 6 87 3 16 21 -19 91 36 -83 -5 -74 79 0 5 81 14 -16 7 0 14 -3 84 -31 44 -20 62 -89 -51 30 13 54 -4 -27 -1 -48 35 -14 -76 43 -49 74 21 29 -67 60 64 8 -68 57 83 -35 86 -90 -22 -81 -42 93 65 -23 -55 -70 -58 96 76 17 4 16 -89 46 -83 85 36 45 13 -54 95 74 -87 21 47 41 -84 32 -6 -78 97 1 2 6 62 -28 -62 -72 12 27 74 5 -78 -94 81 82 21 54 66 88 -86 -90 -49 -41 22 29 -96 12 -4 -46 75 -58 -73 50 -11 43 72 44 -71 48 53 -81 -28 -91 -96 78 51 5 -61 44 50 52 23 -31 -98 -70 75 40 -43 -86 -50 -10 -57 21 -28 67 42 -38 -75 -95 -69 -31 9 15 -14 -86 -54 40 -22 -57 -32 -89 -94 -100 61 37 53 84 -43 -92 7 -71 -36 -25 -35 -23 -70 -97 74 4 -82 43
|
||||
-66 -93 13 -55 -3 46 47 28 -39 -9 84 41 80 0 -35 32 69 -32 30 70 16 -52 62 13 42 -75 -96 -81 26 -89 18 3 -18 12 94 -61 84 68 -36 -75 -29 -62 54 6 -95 73 -61 25 28 -87 45 -49 83 71 -94 12 12 80 -31 -51 -11 -100 25 -23 55 -66 84 -15 19 -3 70 -81 -24 -65 85 -76 10 70 -33 -19 -31 -77 -4 37 28 64 -32 23 -28 96 38 86 -5 48 -30 -33 -57 89 30 64
|
||||
-9 -79 -30 36 -96 7 -42 27 -46 -63 -76 -84 48 -36 64 -76 5 -96 -44 31 44 -77 37 -1 17 -66 67 80 21 67 -60 61 68 -79 20 -73 74 60 -4 -7 -95 -90 -10 24 10 92 -18 -89 -98 -65 95 -96 -93 89 79 -76 -47 47 -86 -83 70 -45 87 -12 -32 50 -34 -76 -94 75 -95 49 -62 64 -31 -11 -94 -75 -14 -84 -35 -35 26 -72 -91 22 25 22 -21 34 -16 -11 92 -7 -90 -85 -1 -99 94 94
|
||||
22 -42 -66 -11 -30 -81 96 65 -70 -42 -9 -63 89 72 -51 100 -67 27 -58 50 -34 50 70 41 84 -67 12 -76 -25 75 -33 91 53 -43 -81 -33 57 100 -16 66 77 -63 32 -45 -5 36 61 66 90 -23 -46 -57 59 -12 -6 75 4 13 28 -65 -43 95 63 -87 6 -28 67 10 67 1 94 -99 17 -49 26 34 -37 -12 60 -78 57 21 0 -89 94 17 -99 -94 -85 6 0 -75 -54 51 -38 -74 48 40 -44 -27
|
||||
-62 90 42 4 -84 -93 -18 42 55 -70 -44 5 55 -17 -90 -96 -27 34 2 87 -24 -67 -43 73 25 58 100 39 84 -47 -86 12 -23 -19 100 90 -27 73 21 47 -67 63 -36 -18 79 99 3 -13 -43 -54 -47 -31 27 -50 -81 -37 -84 10 -12 -51 1 -48 -24 7 63 -83 33 35 -95 57 -31 29 20 -28 -60 11 25 84 -80 -3 66 -81 84 -6 34 -89 54 26 -11 -92 2 53 92 -89 92 -89 31 55 21 8
|
||||
34 -64 -14 -76 -76 50 2 99 -92 50 -47 -38 96 -74 32 -53 15 12 49 -35 27 -7 -87 27 90 -58 -35 40 3 -79 3 -40 -52 27 -39 3 39 100 -57 47 77 3 -70 93 -8 86 62 52 86 -23 96 -35 -70 -67 95 38 75 99 -58 -70 15 19 27 65 -10 -52 72 41 52 18 14 -38 32 47 -41 -67 -84 5 -98 -16 -60 -28 37 -39 -26 -34 10 74 -83 53 -36 -40 19 -71 83 -50 64 84 87 16
|
||||
35 -6 -97 74 80 15 -32 -100 92 -100 -92 -72 -64 -61 20 -93 -75 2 38 39 29 51 -13 -61 3 76 -88 0 60 31 8 -33 -93 3 49 22 -21 -80 -91 -58 -1 41 -93 -45 -22 -99 14 -63 95 40 39 -100 99 6 -80 16 94 -34 -70 -47 -37 -36 -99 -71 10 65 -43 -88 -19 28 -50 61 -36 -15 99 73 3 -67 -30 -40 23 74 -28 48 -54 -50 -45 -4 60 50 97 100 3 -83 -37 -85 -74 -44 -59 -60
|
||||
30 59 -2 -55 -26 -83 -93 80 -53 -28 56 84 -83 16 -18 -25 -24 -66 -75 22 48 100 64 -59 -43 -38 54 -97 83 59 -99 -74 97 -2 -76 96 -50 -70 0 59 72 7 -12 17 20 -80 82 -35 -55 74 -33 -85 85 45 83 77 -90 -98 -23 24 -58 -27 56 -13 14 18 -71 27 84 -14 -11 -43 -18 -87 50 -54 -20 91 -28 -52 -22 10 77 -94 -77 -32 -7 -66 57 57 -34 -42 -43 2 -48 -23 2 98 -52 -92
|
||||
27 78 90 -36 -98 22 67 68 87 4 -50 -46 15 25 -17 25 9 15 -3 -41 35 -7 -91 -28 66 92 36 -14 -94 46 43 11 21 -46 -53 22 30 -81 21 -43 -4 -77 -57 79 -45 -14 -36 37 92 31 69 23 -14 -10 -30 -67 24 -16 -16 63 -61 31 -83 -9 74 37 77 25 -57 -71 92 -22 6 -100 38 49 10 98 -94 12 11 -94 -84 94 -56 100 -34 -30 75 87 -79 98 8 -7 4 25 98 84 -98 -56
|
||||
40 41 -74 33 48 -11 -47 -1 -21 -19 74 -53 0 72 -37 -76 -37 16 -69 97 -55 -99 -52 -47 27 -96 84 29 -7 -42 -49 -72 59 -82 73 41 -74 -75 70 84 38 -23 -89 35 19 68 -12 -12 77 87 44 71 -57 -4 22 -83 37 19 35 -26 -20 -24 53 -28 31 16 53 -62 62 10 90 -2 33 12 62 54 -39 83 10 8 -3 -17 30 91 -6 -49 41 -25 11 -18 -56 72 -27 9 -90 -71 -24 83 27 -47
|
||||
50 -42 61 10 50 78 48 42 -16 -48 -89 -90 -67 -82 86 27 -90 -98 2 62 -13 46 -64 -28 4 -79 18 -52 38 47 1 -15 4 -70 -48 -45 56 -89 29 -11 -89 -78 -69 59 64 54 45 21 91 -67 58 56 -67 -22 43 1 13 55 51 -63 -42 -8 46 -47 -30 -90 52 38 22 -10 22 -69 -22 -66 -49 -4 -71 -10 31 6 7 80 -49 82 87 -52 68 9 35 -66 76 99 30 40 -79 -53 5 13 -34 -41
|
||||
-26 -100 -32 -87 64 -56 -11 71 -39 -48 -9 -59 -76 -36 -67 33 99 -34 54 -67 100 39 -15 27 -91 -58 -87 82 29 22 38 -35 100 81 84 -2 -48 21 -69 56 -26 71 -4 67 4 3 57 55 -61 -63 -36 -32 0 -21 87 32 -93 74 -11 -100 -1 -27 38 -74 -62 -99 24 -38 13 81 -47 -55 21 -59 24 -53 -61 13 -10 52 9 35 -29 61 97 -69 92 -98 -97 -47 33 -71 73 -55 -65 -78 -4 -84 -7 -78
|
||||
10 -39 -94 -64 8 73 61 -19 -63 63 39 61 99 -36 -88 1 21 69 46 11 -58 -80 4 52 55 31 -84 -75 -92 35 59 -11 59 21 -62 34 42 71 53 13 -88 66 -85 65 -34 -86 11 -24 -57 -45 -79 29 21 22 -27 -94 -49 -42 47 -85 44 86 41 38 6 25 -16 54 93 54 30 38 -72 -50 -92 -35 6 12 -82 -4 46 -40 -48 53 64 -38 -95 -35 21 63 14 15 36 27 -48 -51 -20 -98 47 50
|
||||
-16 25 95 -5 -85 -71 -94 23 -27 65 44 66 -17 -60 -49 25 90 -19 99 63 -53 -39 50 -38 57 0 -29 -58 -66 -91 -89 29 -44 -7 -74 8 -67 -89 92 18 -23 4 -23 79 45 -53 92 61 -15 -10 13 59 17 0 96 -74 -8 -84 49 78 82 63 74 33 88 84 -49 -11 52 -88 71 -13 -90 -74 49 55 50 18 -40 -50 87 -4 42 -45 -98 77 43 -71 -92 88 -43 -27 -44 64 -97 27 -86 60 16 9
|
||||
-14 -86 18 70 -12 92 -37 -24 84 17 25 46 74 -23 -99 46 -6 83 -81 33 64 -40 -76 -69 83 -67 25 -96 -27 76 -24 -10 -32 25 -82 -29 78 9 99 82 -55 66 -52 71 -89 -44 8 -56 -76 24 67 -45 -10 15 -33 -33 77 -82 26 -53 97 63 -68 -98 -97 78 2 -15 -99 61 -70 47 -89 -39 48 52 61 -46 -9 -49 -54 -51 62 -86 -92 -84 58 69 -75 23 -25 44 -18 -77 -57 6 -49 -47 29 -36
|
||||
-83 86 -29 -91 -64 -82 50 -45 -67 -68 42 -38 73 -29 62 -44 -19 22 67 -80 -30 24 36 -25 -11 55 -31 98 12 72 -88 -65 29 89 -41 28 -97 15 49 -100 41 57 -96 -11 16 32 61 80 -15 -48 -14 23 4 -85 40 -35 18 45 -44 -60 -93 -43 24 71 -65 75 -25 82 94 82 -54 44 -59 87 -89 54 81 6 -10 -92 98 26 17 -55 14 86 -76 96 -23 84 73 51 -15 -95 90 7 70 -17 90 -17
|
||||
-60 -25 33 -16 -74 -22 7 95 41 -72 -39 21 93 -44 -13 -15 -79 -83 -76 -70 86 -90 -90 46 -3 50 -41 -74 -74 20 -24 -12 -94 -64 59 62 85 -31 -84 94 65 -89 -72 -12 -69 -41 31 49 -53 -22 34 88 63 -36 -1 -95 -100 -93 -53 -75 44 64 -20 5 26 -19 0 80 88 94 -13 -72 96 92 75 -66 18 99 4 35 66 -56 7 43 1 -94 82 94 22 31 42 15 -16 -22 -37 14 76 45 -75 -45
|
||||
-65 2 -8 90 75 36 -52 -29 72 34 26 -86 -6 55 74 52 79 -15 95 -70 62 -76 34 -20 68 28 -23 6 -41 20 -67 -1 100 11 33 63 4 -95 -85 -17 33 -59 -15 2 90 49 18 -75 86 -67 50 51 52 57 50 11 -44 -99 -76 -1 5 -61 38 -29 -54 -4 -6 -40 -88 -69 -16 49 -20 -28 -85 -61 58 -2 -84 93 -47 63 94 0 -80 -67 13 -14 -8 -14 83 24 43 -61 -65 -34 -82 44 -64 77
|
||||
35 -79 89 23 74 74 94 -52 87 -40 -90 -32 3 -14 95 62 -9 3 97 40 -53 -37 5 91 15 69 -91 -71 -46 -63 -83 46 85 -24 -16 -22 -39 31 31 19 -75 12 36 -12 65 78 14 -14 -52 4 -65 26 -63 -24 -56 92 -34 51 -93 -42 3 -16 99 48 -36 -17 -30 -1 96 -95 -73 -78 -92 -12 -19 -75 43 -37 52 -52 -59 -9 -14 -34 25 -78 -59 36 -91 -23 20 -94 -81 48 -20 -28 14 -19 32 22
|
||||
42 49 78 51 -49 -87 19 -36 -60 36 63 83 -20 -25 23 -72 84 58 60 11 -55 -22 -37 -15 44 96 29 72 -4 75 -93 -55 52 -27 66 -30 -10 -61 74 -50 51 -19 -70 -9 86 -78 -7 79 44 -55 65 56 18 -10 31 -56 37 77 45 -30 62 68 15 23 -68 10 66 96 89 -85 -21 95 4 -55 96 -29 22 77 42 89 74 -91 -40 96 -1 86 -9 28 36 -30 -77 84 96 13 -83 -47 -9 10 42 -2
|
||||
-15 -94 -62 69 97 -86 -16 72 -96 56 74 -75 -83 57 -23 -58 18 62 -19 69 69 23 -6 -21 70 56 31 -97 -64 71 -94 -84 -48 0 -49 -92 -77 74 -44 -91 -65 96 77 -14 45 -86 30 88 78 91 77 37 -74 10 -40 98 55 -57 -86 84 -50 -79 -15 52 10 -7 -70 59 55 6 18 -67 12 6 -89 8 76 -90 -93 73 -63 -89 100 19 -31 -66 -11 -67 -76 95 54 -72 -35 -18 -70 30 -62 -60 14 9
|
||||
14 14 6 49 88 -46 -94 85 -52 -59 -55 -1 -66 -58 50 -87 16 -72 -53 -18 -54 -84 0 10 -21 62 -67 43 -94 -40 -43 92 -81 80 -41 -97 17 -33 -83 37 48 40 -94 32 7 -19 -23 64 -70 -76 -84 95 -57 48 3 -40 -86 -44 7 67 72 72 98 -97 -24 -98 -70 -59 -26 -3 -11 29 12 4 26 98 1 65 90 -93 28 -5 49 -91 32 65 -49 -79 11 -24 7 -73 -49 -49 -36 28 -89 -46 -12 -50
|
||||
-63 -14 -98 -86 16 47 18 -11 -48 -2 88 -86 15 71 -32 -27 99 -41 -44 -20 -76 82 -86 81 -80 -57 76 57 84 -83 1 54 44 53 -99 -71 44 -3 -90 30 43 -5 88 33 -73 -84 35 74 55 -93 81 27 -45 85 79 81 87 55 -32 -77 25 -76 -50 80 -69 44 81 -62 53 31 29 -32 42 -10 9 15 -1 65 12 45 -19 17 25 42 -69 -65 -4 32 -84 -42 57 40 81 75 -18 -55 63 -93 10 30
|
||||
28 98 -39 -12 -89 34 82 -4 69 38 -48 40 85 6 -40 9 -53 99 47 -73 25 -85 68 -62 53 13 -65 32 1 15 -63 -40 93 -82 -59 -57 57 -44 -30 92 -80 -38 -78 -13 31 -3 17 -38 57 -17 -8 -74 54 45 -78 82 12 -55 5 -65 43 -74 -33 15 -5 -63 17 26 -7 45 -2 1 49 -54 -4 88 -86 -28 -77 -70 67 82 41 5 -57 23 42 -40 60 5 77 16 -75 93 84 25 75 -53 31 -13
|
||||
-54 40 64 77 -14 -95 -10 -85 -59 -8 1 28 79 83 -97 56 -4 -42 -83 -14 37 -75 -67 -32 91 -2 96 -29 100 95 -61 -80 -8 94 49 -14 -24 47 11 -74 1 26 -31 -14 -14 -62 -87 58 54 37 -73 73 -79 88 58 41 -56 94 11 70 -4 -97 -85 -52 96 -7 -29 -63 -80 47 50 -25 -79 -69 55 42 -17 -9 43 36 2 93 96 25 -95 49 -35 85 68 92 -16 -92 -28 74 -37 -73 34 -97 -49 -8
|
||||
9 53 18 -90 -76 -81 12 -55 63 -10 -40 -79 21 18 -84 -49 -58 -30 55 -21 -94 -43 -64 14 18 -84 -42 -96 25 -33 -83 -58 -21 44 34 28 -65 37 20 -100 -33 -56 -48 -20 -31 -44 78 -23 79 78 85 18 -10 37 -22 26 77 83 16 43 41 -21 -75 -48 51 -54 -12 68 66 9 55 42 13 -49 31 34 -47 -100 52 48 90 39 64 -32 -63 27 21 -86 -97 48 3 -28 -62 -37 8 -91 25 -4 -59 79
|
||||
-44 -99 49 84 -20 94 -39 -16 -40 -44 41 -75 -97 -1 -66 -20 33 12 42 -66 25 -58 80 62 -68 30 -39 -25 28 95 33 100 12 -49 50 58 100 -66 9 -81 -73 42 28 96 71 -48 59 -68 -67 -15 38 -36 -14 -66 -72 -72 66 -49 87 -28 8 20 -10 -8 97 -55 -88 -43 15 73 -16 0 68 -18 -16 -61 -7 -65 41 58 -21 -5 36 67 0 11 -31 -100 32 13 -34 70 -36 -76 7 -12 90 85 -49 -100
|
||||
4 68 -66 -98 -81 -61 82 -84 -33 -17 44 -72 51 22 69 -9 37 -66 2 -74 41 -39 -63 -10 -51 -27 44 35 5 17 21 -98 97 52 -91 40 47 7 -75 48 58 -93 91 69 7 -63 -8 81 40 21 90 -50 -96 51 70 -34 2 44 36 21 -45 -13 -22 -99 -39 46 48 -57 -33 25 29 76 77 -41 -39 -76 39 98 -65 -19 59 -25 73 53 17 62 41 84 -40 65 21 -1 -26 -21 -28 61 86 27 -61 -18
|
||||
62 85 6 82 87 -24 34 -71 11 31 96 72 11 79 100 -54 100 66 -1 -14 71 69 -20 79 -84 -43 86 96 -34 15 -28 18 30 -45 -19 97 12 -62 34 -84 36 -2 -60 -55 -77 44 -18 -37 97 1 -72 91 -14 32 -4 -31 -93 -29 16 43 99 25 97 23 -31 77 37 90 50 -6 11 83 -91 50 59 67 -54 -88 -37 -73 -92 23 51 27 62 -82 -38 -65 6 91 -61 -78 -60 -61 -7 -50 94 -13 -97 -67
|
||||
56 64 79 -87 13 44 95 46 35 -13 -3 48 -61 -100 -27 -54 3 -79 89 -12 -4 41 16 -40 -50 -96 56 -4 6 -68 53 13 -20 -5 20 36 -64 -38 59 2 82 -51 89 -27 -77 -77 -29 -33 76 -32 32 84 -28 68 64 59 94 34 -13 82 -42 -77 -91 23 -20 73 100 70 14 61 100 -65 -47 -95 -12 16 84 -25 50 70 35 -95 -86 -98 -37 51 30 5 -11 37 -86 30 57 99 96 94 -47 -11 90 25
|
||||
26 40 32 -89 96 -17 78 -88 62 -79 98 -22 44 -50 44 51 15 44 31 -37 -46 -62 11 -70 -64 -24 12 59 29 88 92 -94 -60 36 67 -31 72 -14 91 -15 -62 -76 -29 -14 -1 14 -92 93 -92 -2 -29 -36 0 38 80 -66 22 -98 -72 44 -68 100 41 0 -4 -78 61 -85 96 -89 -18 -36 -81 -7 -97 64 76 94 -26 -80 -33 24 84 -8 92 55 -79 -14 -21 -54 46 76 -90 17 42 45 -27 98 -65 10
|
||||
71 -42 99 87 1 83 61 11 40 79 69 42 66 -68 -46 10 -79 -42 55 -47 87 -64 -81 8 32 16 -78 9 38 -34 17 76 -11 42 -97 44 20 57 -72 -91 53 -95 -56 -14 -72 -41 5 -51 -10 -36 36 -25 -62 84 98 65 40 -50 -7 -5 -81 26 8 -42 -45 91 -69 -90 6 -13 -99 -73 -56 21 28 20 -64 88 8 90 22 -31 68 -42 -74 56 -71 21 -72 56 94 -27 52 -83 79 64 -22 -89 -94 37
|
||||
-45 -69 6 -54 -81 75 24 -84 74 34 -66 51 -92 14 -99 -73 -45 85 28 -84 -31 -1 63 -66 -9 -86 -17 24 65 82 -68 -100 -55 -31 -58 30 57 -49 -86 -7 -44 -28 81 -6 95 -44 54 47 -39 22 98 -85 4 50 -33 -100 -38 -51 -25 32 3 5 -97 4 -74 -95 -41 73 95 43 96 76 -74 -77 -71 87 -86 94 -7 -72 -67 -83 -60 -54 83 74 -10 68 12 -51 -91 24 55 3 -77 -85 -85 -94 -93 -63
|
||||
53 -95 -83 -62 7 -14 15 79 -97 -47 -94 -57 -96 45 80 90 -59 -4 -33 -48 14 38 -1 25 10 -83 62 97 53 -44 -85 62 -56 47 38 -43 46 39 -55 -60 -87 63 4 -38 -13 -44 14 -23 -12 -84 -48 -65 -40 4 43 45 10 -2 6 44 -21 50 48 67 -89 12 60 -18 -95 -61 20 30 7 79 -12 -60 -38 -74 -20 34 11 -74 75 48 -48 16 -10 -11 -38 66 72 61 16 86 97 -42 8 52 -39 -60
|
||||
-49 -88 -47 -57 -8 -95 -1 14 27 0 47 36 -95 -59 14 12 -44 -28 -71 -88 -40 64 -59 85 -42 -72 66 -78 2 -52 67 -97 31 84 70 72 85 3 -93 8 98 99 -83 17 82 -36 41 -44 65 39 93 -14 -34 57 27 97 34 63 -23 -22 77 -87 -26 60 -26 62 -77 -70 67 -5 -11 10 25 -54 39 -68 -86 23 46 -19 20 -81 97 -81 99 -68 -45 -13 89 23 34 67 53 54 -5 -74 56 59 60 10
|
||||
47 -57 82 -48 58 -15 -88 58 22 -15 56 57 -57 7 -72 -43 -26 19 -54 76 -16 96 -35 84 3 -11 19 -63 -38 -95 -65 84 8 17 -87 90 62 -8 36 79 92 77 -61 35 9 87 -95 59 -43 71 -14 79 -35 -21 49 59 -91 -79 -9 53 -58 91 -93 -11 -11 -65 36 -37 -74 67 -83 -51 17 -80 -80 -61 -90 -50 -3 -47 10 -5 -87 -87 -47 20 -9 -38 41 -38 25 -77 48 57 11 -87 19 76 -68 33
|
||||
-22 73 -67 -98 78 31 48 20 67 86 -81 -58 -66 -3 -66 -8 -32 26 -65 86 -36 79 -55 47 60 91 31 99 -84 48 -2 -94 -62 -65 -48 95 60 87 -79 -95 100 3 49 70 -76 33 -64 -61 -47 73 57 -97 -71 -10 6 -7 -63 30 95 -91 -78 50 26 59 63 5 -30 95 36 48 -60 -27 -37 72 -88 -63 -75 -91 84 -80 -57 -34 57 42 -42 -41 -89 -99 -59 41 -28 65 -41 -30 30 69 -16 95 -46 -100
|
||||
59 22 -18 -36 -46 -27 -9 74 -58 95 -48 59 -83 50 -43 -75 -57 65 -55 92 60 67 -1 73 -71 -34 96 17 -78 27 -99 45 14 54 16 -84 -95 16 98 -7 25 -10 -14 -47 8 -3 86 28 -58 -54 20 -88 -40 12 -48 -20 -80 -46 -19 -62 -86 32 -13 -8 -27 78 65 68 95 18 -19 99 48 83 64 94 72 31 95 51 50 -2 -95 -91 36 0 -13 -81 25 32 -73 40 37 28 -17 -29 56 74 55 -29
|
||||
-34 -73 -34 91 -34 -46 -81 76 -49 79 49 54 62 -66 -61 99 46 -31 43 10 -18 -17 -78 22 -74 -79 27 -7 -46 -4 44 -74 36 6 84 97 -30 -70 3 55 -41 0 27 -84 -79 -75 -75 -11 -64 -61 -5 2 -28 -90 -89 -53 -14 34 5 13 88 -32 -65 -47 -39 -75 -39 -18 -66 -100 82 -96 24 100 17 -90 -30 -15 59 42 25 -85 46 60 -26 38 -1 -78 -64 -15 -89 53 -48 -98 26 54 -31 12 -77 0
|
||||
-55 60 62 1 -10 55 -84 68 -66 12 62 -7 0 10 -86 99 -33 54 31 37 58 -99 5 30 48 89 5 28 -66 -38 -10 -62 -55 -43 61 76 -41 -59 40 -51 -83 -41 -19 26 -74 63 -15 -22 52 -47 -81 100 82 53 -85 92 -12 -43 21 -62 -89 -33 18 45 89 46 -74 24 57 -66 61 56 -30 92 97 92 23 -72 88 -13 -3 -44 96 -93 -21 44 -25 77 33 -24 -63 -50 -83 71 95 -87 -71 78 -39 99
|
||||
2 18 -93 -15 24 73 40 59 83 67 15 -35 15 -35 16 69 5 -28 61 90 -87 -31 58 47 39 10 74 38 39 -66 -91 23 60 79 84 -32 -12 15 85 73 30 8 -99 -41 -66 -94 57 -8 -57 76 59 49 40 53 -28 -51 -12 -31 -16 -37 -12 -43 34 -17 -95 68 -21 -23 65 -61 95 26 -18 -86 71 59 -29 70 -28 31 0 13 56 18 14 15 -54 62 -92 -79 84 55 45 15 11 -52 -17 64 -93 -6
|
||||
68 -52 21 -12 -71 95 0 90 12 -11 4 14 -13 -94 15 88 -100 -98 -81 -91 95 -1 33 -7 90 -37 83 -33 11 5 -40 -41 76 -74 90 75 52 -56 11 39 -88 -94 60 84 51 18 -29 93 -84 53 71 90 -86 73 -98 -19 -44 76 -10 -97 -50 74 24 62 -72 -89 7 -48 7 28 57 -20 -87 -91 27 89 -72 56 32 18 100 23 79 -96 10 24 -11 -84 78 -74 9 -82 -44 24 -18 23 50 -9 75 -24
|
||||
24 -56 54 29 72 -100 35 17 -32 95 44 -30 8 -89 33 -85 -100 77 -100 13 67 -82 98 -28 16 94 -64 -97 83 -12 -24 9 -20 -69 -11 -8 -85 -93 41 0 96 -69 35 96 -12 3 -87 100 30 88 77 9 69 77 -50 70 70 43 -66 -84 -62 29 -10 -96 41 -57 24 -41 81 81 59 -37 -100 95 21 -84 25 29 37 -52 -25 62 60 -9 47 56 -85 -83 18 -38 -68 26 -56 -23 42 25 21 -66 43 -34
|
||||
-54 -61 23 -83 58 6 24 -100 97 -72 -35 -64 42 47 -58 22 -77 9 4 78 -2 41 76 74 82 -11 -7 92 32 -94 -72 -54 -80 9 45 21 -47 66 -36 -81 35 12 22 -3 -76 87 23 -8 7 -32 57 44 28 -46 -27 -50 -3 96 -84 64 -1 -65 58 -70 -76 2 -30 -91 -73 -23 -78 -21 -30 -37 -28 -77 59 -20 -51 -58 18 -94 95 23 8 98 -8 -63 -33 -21 13 14 93 -85 -45 -70 54 -58 2 10
|
||||
16 62 -12 -41 -53 33 77 -68 93 20 73 80 -54 47 -38 18 -54 -57 7 -90 55 -26 -27 63 -10 34 44 52 -88 60 -56 -42 0 19 53 57 90 -71 56 -51 95 -50 91 39 59 -26 6 79 -70 32 43 -39 -78 -17 -56 87 5 47 6 -93 51 -4 -36 -51 50 30 53 -27 61 -47 -46 -23 33 42 -99 89 97 -79 -25 -8 2 -30 7 85 89 65 -73 56 -100 -11 -81 -8 82 98 1 -32 54 54 0 -32
|
||||
-51 -42 -94 -99 5 -33 93 46 7 75 -97 16 -93 82 12 70 26 11 65 50 -71 -46 -38 -51 19 -2 1 -3 -90 98 -38 -86 80 26 42 -24 -30 63 95 -11 -25 -56 -23 58 -65 -46 -46 41 25 87 54 -60 -58 75 -52 18 13 9 58 57 37 35 96 4 -16 -66 -16 -94 17 -52 85 -71 -82 18 46 -88 78 60 -37 -58 65 43 38 68 -83 -53 73 81 -91 -3 23 -96 94 72 9 18 -93 29 42 82
|
||||
92 -20 -91 -8 88 9 13 60 80 -61 -95 -73 22 95 -45 69 -87 91 -21 23 74 36 36 84 44 98 5 -85 14 -84 -88 15 -11 35 -39 -32 -19 58 -12 -18 29 -100 11 -27 24 0 70 21 -66 -53 -81 44 -59 -60 11 74 21 84 48 25 -5 80 99 -11 -5 -97 -19 75 -5 -5 -68 30 90 -52 -24 -65 -28 65 5 -39 89 31 88 53 -79 -8 20 -54 47 7 52 -13 -86 4 -98 -94 50 20 82 76
|
||||
75 -2 30 -36 3 -40 92 -57 70 13 90 61 -63 -8 25 -39 -91 -14 -8 3 -45 5 70 39 -67 88 -38 -6 -43 -29 -69 -39 -33 -80 -46 -74 13 84 -48 43 23 -69 -94 60 -28 2 93 -13 54 -26 -92 -39 -45 61 -35 -51 -91 53 70 -99 53 42 97 85 -98 62 -3 53 46 8 -70 -63 -4 10 46 98 95 -62 -73 78 -34 -76 -91 -94 -79 33 95 9 -48 -23 -91 -6 -84 77 -44 -23 -7 -29 74 82
|
||||
59 -74 30 -17 -10 -93 -75 -25 -60 4 -52 -61 89 89 47 -100 57 17 26 -27 -73 -100 20 22 93 18 67 -17 46 6 -49 2 96 97 70 75 65 -90 -83 -64 -69 -20 93 94 -58 -49 36 57 10 88 36 -28 -80 2 -88 70 -79 -82 97 -40 38 71 52 78 -45 74 -38 6 75 -7 -49 -94 75 -93 76 -87 -85 56 33 78 -67 -42 20 -15 1 13 -39 34 78 -94 -22 -81 30 -18 51 -51 78 -26 52 86
|
||||
59 0 -66 -33 -24 -2 23 -19 -37 31 -16 12 -6 45 -7 -50 -20 27 -84 33 88 58 -86 -40 60 14 -17 61 14 77 -3 78 84 59 66 44 -82 -40 97 51 -87 21 49 -32 -88 -46 -52 -54 68 63 -15 3 1 -7 23 31 -61 82 -6 31 81 1 -18 32 45 56 -54 42 -64 -59 95 3 -71 -54 -95 -63 -79 2 84 -98 70 12 85 79 -69 -68 -4 100 -26 -27 -21 -83 -52 50 -37 37 -20 -16 92 -54
|
||||
-75 -78 88 -68 83 -3 75 -12 65 61 70 -34 99 45 -1 47 -35 58 -21 -92 -5 -22 -76 87 -8 65 51 96 -98 3 -56 17 63 -47 -58 -47 -17 12 0 -65 -89 -24 69 -82 -38 -33 -87 -78 -72 26 -79 -74 71 -81 -95 61 -50 2 -29 -84 55 -46 33 68 86 -15 -80 59 39 -64 -74 100 -38 37 63 -64 40 -65 64 -55 88 68 94 90 87 -3 -27 -62 82 -15 21 -13 -34 -17 -24 -48 70 -76 -89 92
|
||||
-84 -82 -2 85 70 76 91 92 -24 -39 31 81 68 -36 -95 -24 -99 -2 -77 -71 -83 60 64 -100 11 32 -59 43 -53 19 85 -74 -13 75 -59 30 68 -44 58 -91 -86 61 -51 8 34 67 -13 -98 -91 46 -71 -5 39 5 -61 72 76 37 -51 84 -55 34 -53 -82 -47 7 11 64 -82 -60 -39 16 55 -2 26 81 19 9 -7 98 65 66 -30 -49 94 75 10 40 -64 40 1 15 75 18 -73 42 94 -85 -64 84
|
||||
-97 77 61 10 -39 99 -12 70 85 3 -11 61 98 -11 76 4 27 28 61 -10 -55 39 50 26 4 13 -22 2 -87 -10 -41 52 -85 21 -35 99 13 -95 -72 -8 22 29 90 95 -93 74 46 37 -81 51 86 -26 -95 74 51 -33 -77 -95 -60 81 53 75 -77 80 69 -25 6 95 19 -8 54 -87 81 28 69 19 -90 62 21 84 -16 11 92 -19 -28 -4 51 8 40 45 -43 59 -66 9 67 5 10 90 -58 -8
|
||||
-8 16 70 -22 -40 12 2 -68 -93 21 -92 -72 -79 -91 -79 87 20 -44 62 -96 29 36 17 46 -92 -77 27 90 -99 -83 93 -53 42 -73 26 3 -63 15 73 -10 61 18 3 68 -5 -62 37 11 -81 0 54 40 -38 80 18 -37 43 45 27 44 -75 76 1 -60 -94 89 -11 -3 52 -88 -36 12 -35 -15 70 -1 100 -93 54 44 -81 -99 -1 -97 42 58 46 -31 63 -96 -94 98 2 11 -36 24 7 2 80 -89
|
||||
-57 -90 -41 17 -47 -55 -11 -79 -63 90 22 -84 73 92 82 -98 -40 -23 -82 -14 46 -66 -49 -55 -78 -33 -55 86 67 -36 -43 -83 39 44 96 42 -61 94 -93 70 -56 -13 -5 -11 -4 -40 81 13 -36 -61 66 30 22 82 66 -79 35 10 -10 3 41 -94 71 98 -86 -15 25 -98 74 2 -82 99 33 -73 -70 -50 -23 99 -38 -38 -32 -22 -10 13 16 35 -86 45 23 -70 97 59 90 43 -60 12 65 -97 98 50
|
||||
-4 -55 -24 -54 58 8 -17 -9 4 -89 -53 13 9 -31 -15 -91 73 63 -93 -42 -39 39 -41 -5 -83 -46 35 -5 -7 25 39 61 69 -67 -14 22 6 80 -12 -51 -92 57 9 -53 -82 17 55 -22 -62 33 5 -84 -56 -70 -38 -33 -33 88 34 0 -75 -10 -45 -59 -9 50 -100 76 38 -83 46 57 83 80 -14 93 29 40 28 -89 -68 -76 -85 62 94 -91 94 68 49 100 -73 -32 42 29 -67 -71 -83 50 25 28
|
||||
82 60 5 -70 31 -95 49 66 -62 2 -6 39 44 90 -72 19 -68 -55 90 -73 32 23 35 44 50 43 -27 5 -61 36 -4 -1 -4 83 -75 54 -44 22 73 60 50 85 6 90 96 -72 -50 72 79 17 -15 -99 90 97 85 -77 96 -23 -59 2 -44 -28 -94 -78 36 -83 -21 84 -73 53 -11 -34 -80 -94 -50 17 -50 -58 -42 66 70 -55 82 -57 -7 -25 8 31 92 -45 20 26 3 -72 -63 65 -98 -78 65 -62
|
||||
74 78 -5 -68 35 12 -2 -40 -83 -12 31 63 -46 77 -70 88 5 24 48 -70 -88 -87 -87 77 55 67 10 9 -70 6 51 -79 67 18 8 -95 -46 -68 38 27 47 50 24 5 47 -100 -81 -14 71 61 -47 -79 46 -29 -98 16 -10 49 -50 37 -47 -30 81 35 58 90 -91 65 -81 85 -63 -57 -63 91 59 -54 -66 -89 -76 -22 -38 86 -50 0 -19 -24 7 -39 53 79 73 14 -66 33 9 41 3 42 -65 97
|
||||
-14 0 -79 -23 -11 85 0 -71 -83 -72 -79 -34 0 42 81 72 -39 -44 -23 64 58 100 89 -33 -40 -14 -65 -68 27 79 81 -93 -10 50 -30 -62 -18 28 -53 66 60 -14 -70 -13 26 -67 -55 -20 -56 10 1 81 -28 -74 41 46 38 97 8 82 -72 -89 23 62 72 -42 71 24 -10 77 -7 83 15 -69 49 85 88 93 -7 20 -11 -27 -16 -98 -53 40 18 88 -32 6 -91 -24 -36 -93 -37 7 -53 100 36 -20
|
||||
-25 -88 36 -36 70 83 33 -7 -22 46 -23 79 -65 13 -87 -37 43 3 -81 44 38 -99 97 23 -3 -70 -60 -92 54 -25 63 -56 -60 -22 64 97 8 80 20 -46 -27 -36 45 37 99 -48 42 83 -9 -92 -81 94 79 76 69 10 -49 39 -64 -11 -51 49 -67 98 5 85 -57 25 -21 26 -70 39 58 -51 21 -62 -99 17 -44 -91 49 -17 -29 22 -4 -95 -38 91 82 64 -63 20 -7 -32 -12 -58 31 24 -35 -95
|
||||
47 -23 18 1 89 22 67 90 53 63 -32 69 61 29 25 55 71 -44 -25 89 -39 -32 95 35 75 -19 -24 37 13 -94 93 71 4 -91 29 100 48 -1 75 -6 -62 -51 -20 -12 -37 -18 91 51 12 94 -14 80 -47 -87 -13 -78 -13 -89 -62 -22 86 96 43 -64 -26 -24 -75 -71 6 -70 8 98 -64 33 93 11 -83 -70 -43 -70 100 -98 93 -32 -2 -93 -37 -80 39 90 -3 38 -64 -97 -91 74 14 -52 -49 13
|
||||
90 -56 -88 -55 -1 -81 -37 -50 72 18 -97 -80 4 -62 59 91 31 -72 40 35 -8 40 35 96 -82 46 -74 8 -79 45 -5 -72 -20 -43 -15 74 -98 -61 -74 -44 6 -79 44 -69 58 24 77 -23 78 -7 -44 77 -36 -48 -64 25 -25 -79 -94 -41 -73 -7 53 39 -83 80 -30 -25 57 46 -98 26 71 49 68 -36 -90 2 14 84 -48 27 -16 -32 -28 -4 94 -27 -38 65 -86 29 -82 -61 75 33 68 -84 -24 -76
|
||||
-19 -89 -82 -30 17 -57 37 -90 -79 19 12 -39 30 73 25 -35 -63 -37 10 89 34 40 -25 79 71 94 59 -26 22 84 87 -36 32 79 -47 1 -74 37 70 -53 -22 30 42 77 -1 -38 80 -75 -95 48 57 -35 -98 -86 81 54 -91 -29 -7 54 28 27 -79 -28 58 21 76 -32 -67 -74 25 17 84 3 -14 -52 -73 70 67 -47 7 89 -39 33 21 55 32 -16 -88 88 -80 -40 12 -96 97 -29 -68 10 90 -91
|
||||
4 58 -88 -68 -20 69 -20 47 74 -93 0 -30 60 -99 -94 -27 -43 20 81 51 19 -85 -34 -48 -52 -84 -14 -89 -27 -26 -4 91 80 -74 -45 -66 -100 -70 0 68 96 -10 47 23 33 70 -10 16 -95 -97 46 -78 -34 -64 43 -20 0 24 -78 -95 65 23 70 78 -19 -26 -11 -13 -57 -59 -31 75 64 74 31 -8 36 -62 -47 -61 -56 -75 84 -89 -24 -21 -4 16 93 79 -46 34 100 68 8 -59 63 -10 -53 41
|
||||
-35 19 -2 -12 -23 21 -84 -34 56 91 -30 -95 -82 -60 -94 14 61 60 -53 85 -79 -18 -1 96 -5 -41 73 40 -94 70 1 -2 9 33 -65 -48 -67 61 -64 -91 -37 58 -8 43 -13 93 23 -55 -61 79 -3 85 10 -79 -13 -30 -43 57 62 -83 -68 83 -22 98 -84 -21 -41 1 -27 65 5 92 -47 72 77 100 32 -41 -35 -90 37 -6 -77 -89 -24 -79 -77 93 -70 -91 -80 31 -80 66 -67 85 50 5 -89 -77
|
||||
83 -35 -83 53 45 92 -14 2 86 67 79 13 -30 -62 -58 -43 -77 -87 71 -16 -55 -92 -64 67 89 53 -12 91 -98 24 -13 -82 90 98 -55 -50 -46 94 56 82 -74 -77 -66 -3 -66 -14 -87 -17 -68 -98 -88 21 11 -50 -53 -31 -58 -57 88 -55 34 19 94 71 -31 -16 -29 -31 -53 87 37 -48 6 -11 34 34 98 9 93 59 -7 25 -76 89 -55 29 -60 68 58 -37 -85 67 59 97 82 46 44 -72 42 23
|
||||
-15 -69 71 95 72 -39 -87 49 -93 -58 73 -65 -55 -12 -16 -51 99 36 -23 56 44 0 19 -42 -27 -1 -56 -85 -75 8 -72 -58 62 -78 30 15 -65 84 32 57 -58 -29 -62 -9 -28 -57 -18 -92 -47 -54 36 -17 -62 -82 -80 9 7 -20 74 23 89 94 74 -27 98 -87 -15 0 -1 20 -36 -41 -94 79 -87 49 27 17 -68 21 -97 -19 -11 80 -69 47 19 72 -14 74 63 53 -9 -2 91 -31 13 -38 -26 -59
|
||||
-26 19 91 41 -41 -6 83 -72 -49 -11 60 -70 39 74 -62 39 54 49 45 -49 -26 0 -69 32 -92 -25 -67 -91 -63 28 85 -58 -80 32 -82 -68 28 4 -66 58 74 90 -39 -65 -81 -59 31 96 -64 -16 60 -16 -88 70 -82 -26 -69 14 -12 57 89 -37 46 53 -59 -91 -59 -10 -26 90 72 -41 -1 -12 47 43 -83 -3 0 73 -30 -21 -78 97 94 -28 -33 64 -71 8 75 93 -49 -94 33 94 -83 -56 95 88
|
||||
16 16 38 45 -5 99 -93 4 -74 -72 -33 56 -43 -60 69 81 -9 46 -58 -87 44 89 -5 58 33 -73 -27 -82 81 -6 -65 -3 93 -83 2 71 37 79 2 4 49 -10 18 -30 -20 23 -31 -79 54 90 32 -9 -94 -97 58 62 70 88 -38 78 -67 -18 53 -96 -33 -29 -33 43 35 -82 -71 -77 50 -22 -38 90 -48 93 -27 50 31 40 -2 54 -23 58 -38 92 -92 -63 -22 -41 -83 89 7 -43 5 -64 -64 -69
|
||||
-25 60 -97 -77 -40 -35 69 -54 59 -33 30 54 -88 37 -4 81 22 34 -89 35 54 81 13 -92 -98 -58 -80 29 65 93 63 67 -69 73 75 42 -24 29 45 32 44 13 -28 -84 40 -63 -80 28 -78 -40 -49 65 16 94 -88 68 -36 -49 -39 93 -44 68 -21 71 48 -10 -64 -91 85 12 -61 -64 -28 -73 -23 41 46 -89 54 97 11 -13 -34 1 -91 69 -15 90 -48 35 97 77 24 -2 -70 -2 -79 33 12 97
|
||||
74 -42 -46 97 59 -68 73 -69 -56 50 24 62 82 -87 44 27 -64 59 9 5 -69 69 90 73 10 -48 91 -6 -85 57 -40 47 -84 -29 -25 -52 19 96 81 -83 -79 78 -91 -49 90 68 7 91 66 26 28 -41 -85 -61 -57 47 80 -48 86 -27 1 75 88 41 46 89 0 73 13 37 -32 88 -86 -28 -59 84 -68 54 36 -36 5 -5 32 1 -18 18 32 -32 -13 0 -87 -24 49 -11 53 63 29 -38 -92 23
|
||||
19 -61 -93 93 44 64 93 21 4 91 80 86 -64 -53 -64 -1 85 -2 -36 91 38 -6 -47 -20 -66 36 25 -49 -89 -66 -58 66 -24 -77 73 -84 -32 -27 -70 7 74 -79 -44 -11 -15 57 -87 41 5 -75 66 -17 35 34 -65 71 8 85 97 16 -46 -40 88 53 7 56 57 72 6 62 33 43 -63 -63 48 -36 80 -49 80 -96 -87 62 97 2 -81 -2 -71 87 -10 0 -86 25 4 34 -90 -23 47 -71 -39 -14
|
||||
72 50 85 -62 -31 -72 81 -29 15 -20 -45 -76 -29 -22 85 -33 -85 45 -78 -8 -81 -15 33 -85 94 21 14 56 -98 -57 -15 47 57 -48 76 -31 -31 -25 100 73 -53 -90 -83 52 41 21 82 74 -21 92 -99 21 22 -57 33 -11 -53 -66 -58 -45 -74 89 93 -56 -37 -66 47 -29 44 93 -23 69 -51 26 38 94 30 -40 -77 -38 -53 -23 76 -46 -98 -56 12 -14 61 -67 61 -63 34 -45 -80 -76 67 -97 -54 -86
|
||||
48 -90 100 -62 46 -24 -15 89 45 -85 -4 -34 -10 26 36 -77 84 -66 -60 -74 -47 -93 -72 -7 34 71 78 -22 -3 -70 24 -27 34 23 29 -16 19 -47 -63 -46 11 -28 55 -14 -46 90 88 -7 -26 -22 49 -46 -54 52 86 -33 -53 38 99 68 75 13 -15 -96 -21 -22 5 31 9 71 -52 -20 -63 -15 -85 20 -88 -59 36 67 -55 -68 -74 73 -54 100 14 -7 -7 92 85 85 -20 -12 -19 98 91 33 -45 -77
|
||||
61 2 47 -79 44 41 -46 -97 -74 -39 -64 47 -16 -67 69 33 -72 86 -80 27 -9 -37 84 88 65 35 -3 -69 -43 -7 19 -51 -99 -71 -82 -20 -30 -8 0 -34 -41 10 -84 -16 23 83 -33 87 -9 -82 -49 90 -78 -77 -47 -54 78 -24 -44 -10 -6 -54 55 85 74 -34 99 -100 -22 -70 -96 -76 67 28 83 -55 45 -71 -68 -13 13 -39 -21 -49 44 13 -28 78 -9 -20 -66 4 -6 95 56 -61 41 11 90 7
|
||||
-75 28 -39 45 58 10 -79 -81 44 53 12 -43 46 14 32 -95 -83 -3 49 -55 41 41 -32 14 91 -62 71 -42 60 19 -83 73 -5 -63 80 38 54 53 -20 -55 -2 47 -54 -7 24 20 -49 84 6 54 -53 87 -23 -22 -64 -66 26 -11 -13 58 70 8 -29 72 64 58 -45 -76 43 -53 -32 41 69 -9 -78 52 -3 -81 12 -34 49 42 97 -33 -53 53 41 32 -70 -18 76 13 96 -10 -26 -19 -79 -81 -3 -76
|
||||
-73 7 -36 85 -13 -62 32 -81 9 71 -80 -43 21 100 90 89 43 -56 60 -22 -71 16 91 -24 -32 -34 30 -42 -7 -88 -35 58 95 -75 -18 -68 -64 -13 -24 66 65 66 -15 74 -51 -14 99 14 -33 -71 49 20 -28 -38 -45 47 22 10 -56 13 -25 59 -92 -62 -75 41 -89 -9 61 -95 -58 -91 -100 -68 -75 34 -70 -27 84 49 -18 -94 0 16 2 48 -12 37 -62 59 18 71 -46 95 60 51 -2 -99 -56 93
|
||||
1 -52 27 18 40 39 19 13 -19 14 -31 59 50 -51 -100 -33 -95 61 -62 39 -73 -37 62 50 -27 -42 -16 66 -15 51 -32 46 -85 -5 -90 13 62 76 -100 -79 58 71 66 -25 -9 52 -27 73 -26 23 57 -72 -71 -96 75 43 29 13 59 22 -46 60 94 91 -85 51 23 -20 54 -31 91 -53 12 -57 -84 74 -71 38 99 82 -64 -51 57 -87 -50 -71 -79 -49 54 -39 -84 -45 -89 96 88 -77 25 41 70 94
|
||||
0 20 84 48 0 13 13 34 -10 -88 22 35 -82 -80 38 62 -26 99 -43 84 -90 -13 37 46 3 -44 -86 -29 89 -8 59 49 -30 -34 72 0 59 21 90 -95 62 -69 -47 66 15 99 84 -5 -38 99 -48 -42 -83 -1 -9 -41 -30 30 -87 63 -37 -59 3 88 -82 53 -4 2 -42 -17 62 49 -39 -54 12 -18 83 -81 -29 7 57 20 -58 12 -31 31 5 19 10 90 50 88 59 -13 -94 51 49 63 73 96
|
||||
12 38 32 -7 -85 -5 -26 62 11 60 -39 61 100 73 2 -40 -12 51 -93 76 58 96 -88 17 -75 10 -16 43 -42 -16 -37 -75 31 -24 19 32 -15 69 91 -2 58 31 2 -75 -15 -59 -1 -13 31 93 58 9 -98 -20 45 -14 84 -17 22 99 95 -91 4 25 42 -10 -57 -68 -88 40 -55 43 78 20 55 -79 44 69 -61 82 -34 -30 56 9 -81 77 31 -2 -43 -96 72 -98 -71 80 59 -29 -60 92 -57 -36
|
||||
96 24 -68 91 -14 79 60 43 6 77 76 -60 -82 5 18 -27 92 56 64 -24 43 -31 -79 30 -79 75 66 99 26 24 -90 -61 43 -50 62 77 -57 -2 29 -78 88 -29 -38 -46 86 -74 -77 81 41 -36 -85 -95 33 -79 75 54 -38 37 69 -56 -87 -34 0 25 23 -37 13 -38 79 -7 -32 13 85 70 38 -27 30 18 -98 48 54 58 -39 -55 37 -40 66 89 -19 -92 19 -99 -57 38 -19 -33 18 96 0 -4
|
||||
-63 71 82 33 57 81 7 -92 -8 53 -93 42 -23 -56 -38 -35 89 -37 87 -15 19 -37 36 66 -17 -93 -19 -15 91 -34 -40 -44 6 -59 -17 -90 11 81 -48 97 -91 -80 87 24 94 -5 57 -67 77 90 14 78 -97 -5 17 95 -55 74 -2 -22 -76 56 35 -26 66 -85 -88 77 -14 -26 15 9 11 -48 -22 36 50 -11 -72 -69 69 62 28 -67 83 -69 54 -60 -31 -65 -32 53 -45 58 36 -67 -90 -66 10 -27
|
||||
-31 -91 -20 73 68 -5 -87 87 35 100 -32 -46 41 -69 1 -43 -72 11 -59 59 44 97 -10 29 46 84 87 -34 -27 48 -68 100 -6 -1 88 -22 15 73 -64 100 -38 -88 -93 96 29 -99 95 51 -49 35 29 -59 89 46 25 53 79 14 34 42 93 -33 29 22 96 -33 -34 -53 -67 46 -3 -64 68 98 -25 11 -24 -86 -88 84 4 73 -2 79 -51 -90 63 -51 18 -73 0 -3 10 -10 42 92 -9 -1 73 -38
|
||||
71 42 31 8 64 -34 -58 -23 87 -15 39 -7 79 -84 -16 -26 17 61 12 -25 -2 70 48 -41 76 83 5 49 95 -57 90 74 54 31 94 97 -79 -18 -20 10 -68 32 46 -49 19 89 33 65 -25 -48 -49 55 -79 29 21 -7 1 90 14 -84 82 27 -48 24 29 -70 58 -77 100 69 53 50 42 94 -2 44 -24 -30 84 -29 84 -57 -31 90 69 -77 -28 91 -67 76 90 -86 -58 61 12 74 -63 86 39 39
|
||||
0 50 -13 49 44 70 -57 -62 -88 6 -21 -23 -43 -59 46 81 78 -91 -9 16 -21 -73 52 -49 52 30 -20 38 52 -13 -92 -31 46 78 91 -77 14 -16 10 2 9 8 -74 -43 -74 6 -54 -44 -28 -48 92 -74 -47 -53 36 -43 -94 93 28 -90 -68 24 91 -4 -54 17 -68 -25 -76 -87 7 57 8 -27 10 -65 68 -94 96 53 61 81 -29 67 61 57 -29 -69 36 65 -10 -81 43 29 -43 73 56 73 71 -58
|
||||
40 51 -89 -32 76 -17 -17 -34 -67 66 -12 61 29 14 -51 -38 94 17 49 -63 86 -49 49 4 28 -22 9 -40 -2 -68 -80 -27 -25 40 39 -71 -84 -32 79 -81 73 49 -96 -35 46 43 -59 5 83 67 -43 80 48 85 -32 -79 -47 71 92 -7 -90 -26 47 -33 -6 94 70 -64 85 -99 92 -62 -23 44 -7 77 7 89 -99 -96 84 -19 -59 -71 -65 29 -23 47 28 -95 43 -17 -18 -86 -1 79 87 -41 49 50
|
||||
-30 -84 -19 8 33 72 -96 96 6 46 4 -23 38 -37 71 74 -15 77 -89 34 -76 -73 97 -50 -3 -87 -61 17 -38 31 -80 77 22 -43 -67 49 -12 -51 -45 -8 38 71 64 -62 50 89 55 90 16 -22 -42 -80 -73 -60 57 9 -87 29 -81 57 26 -95 8 43 -19 -38 -81 -61 -34 -68 -1 29 84 26 96 83 68 5 -98 47 -19 -85 -18 55 -18 89 -16 21 99 30 56 -90 -7 84 10 77 -73 2 -70 -15
|
||||
-13 91 -54 8 -60 -34 27 73 95 -48 -31 -93 -78 66 26 18 -10 -11 -65 -100 -98 68 33 -46 51 -5 35 29 -32 -87 41 64 -92 -12 90 0 3 60 53 -40 -9 60 -98 -32 -26 20 23 53 -55 -58 94 13 98 31 7 30 -30 -71 27 1 63 88 78 36 -54 4 -96 21 -42 98 -64 76 77 89 66 89 37 54 46 78 24 -66 68 -70 79 -13 61 52 30 -91 -22 61 60 36 -76 1 -94 40 -11 -2
|
||||
53 81 36 -69 -41 -50 75 12 75 -54 -13 78 -14 -72 -61 -13 -43 99 -15 88 -77 -40 98 -83 81 -90 -59 -43 -20 88 -16 50 -78 40 94 -61 59 32 31 -65 97 81 36 -60 70 -20 41 61 3 6 -24 -93 -33 -88 -56 -63 -27 -96 -62 -83 -92 94 91 -35 -57 -12 -7 -53 4 1 57 68 37 46 -12 -65 -80 53 23 22 -34 -97 75 -81 85 -72 12 74 77 -84 -85 -70 -49 21 78 -29 -59 -18 46 3
|
||||
-91 -2 83 -100 -5 39 -87 -6 -71 89 -20 60 5 4 73 -4 -3 26 -35 4 52 92 22 8 -63 -40 -91 68 -76 -74 -95 -73 -96 37 37 -91 88 -50 -73 30 -27 -97 -38 26 77 -14 -59 -29 -93 42 0 -66 -52 51 -75 -25 75 -30 -25 -27 -68 -58 79 6 -41 41 32 -49 -90 98 -68 70 -64 -97 -67 34 -100 -67 80 -21 77 -48 -85 13 26 12 -89 85 -89 75 42 -34 -28 24 31 97 88 94 -58 -64
|
||||
-68 -97 -59 -27 -37 -79 42 -85 -4 -26 1 -98 26 44 18 -99 -40 1 62 91 -97 -68 72 55 -44 55 36 73 55 -91 100 93 22 73 93 14 40 -2 52 73 78 59 23 37 -54 27 -70 -88 16 41 91 0 -18 17 -78 50 50 10 -62 17 92 18 71 24 -87 -98 42 84 11 57 45 86 -80 26 -33 66 67 52 65 3 55 -99 -62 59 51 5 13 93 40 -29 61 -7 15 -11 -6 100 67 -1 -75 -44
|
||||
94 -53 6 -99 29 -70 -24 -12 97 40 91 34 55 -34 -1 1 96 87 -12 50 9 25 56 -56 57 15 -81 -24 47 -53 49 40 88 39 11 -57 -27 43 -59 22 -90 90 -21 -91 -70 -4 69 -47 -88 -16 -36 -9 -81 49 -3 69 -91 -64 84 -54 97 32 35 93 61 78 7 -53 10 -75 -17 -10 -65 -67 78 -79 37 -55 2 -16 -65 -7 7 32 17 29 -77 -5 -66 -64 38 -84 90 -36 -28 -13 26 68 63 47
|
||||
25 -82 -9 -25 42 -83 9 59 36 -5 89 55 -28 -11 38 -95 88 -68 -42 -41 -93 -75 93 31 17 -17 -65 84 1 -33 -99 36 -2 63 -67 34 50 78 -51 10 4 -6 61 5 92 83 -41 -38 90 -6 -31 -72 81 31 -13 -14 -21 13 100 93 -66 22 39 -74 -23 44 -19 -96 -78 37 89 -10 42 -46 61 -58 100 49 10 79 36 62 -46 -8 86 -22 74 -30 31 47 81 -88 -1 69 -78 -94 84 -44 -35 90
|
||||
2 35 -42 45 -99 37 -2 -85 -56 29 -25 3 24 21 -13 -22 81 92 -57 -10 70 -44 16 -67 -99 -72 -14 54 90 -59 99 59 -53 -63 12 16 44 -57 36 16 22 38 38 -2 56 24 -51 96 25 -55 -12 16 18 70 18 -51 98 63 -26 -77 62 -35 -44 25 -70 -21 57 61 4 90 -77 86 -55 -29 -87 -6 -92 96 -25 -49 -92 1 23 46 -6 48 -35 -20 -9 68 -50 -70 -24 34 -54 27 -10 -56 -39 33
|
||||
-99 40 -29 -73 -63 3 -94 -80 -62 -78 -10 80 -54 54 -19 12 75 64 20 -10 9 -11 -93 -92 31 -11 -70 -29 34 90 13 60 -80 -88 86 -45 -74 56 -62 92 87 70 -1 26 8 7 -94 1 5 -46 -61 -94 80 15 -78 16 60 -80 0 33 -48 57 98 -15 87 81 90 -68 -42 -53 26 39 -64 -21 -67 20 -66 80 -5 -43 16 -4 61 37 18 -95 -74 -77 -60 -95 -35 73 37 46 -23 -41 -34 -60 -60 57
|
||||
-1 50 97 -70 -50 -95 -27 -79 -46 68 -89 10 -42 -53 -28 -50 -26 20 -94 88 36 -96 -40 -50 -89 9 88 -73 -63 87 28 1 -59 35 -14 -33 -17 100 75 8 82 -45 54 -100 -23 -88 45 -34 54 -53 22 -76 38 8 13 40 75 57 90 14 46 70 89 -17 -65 8 -89 90 -12 -6 86 -10 -95 -80 60 -90 -7 8 -37 -74 -4 -12 9 49 86 90 28 59 60 100 17 -97 -70 -23 -39 -11 -11 -71 -37 -24
|
||||
45 72 78 -2 12 -32 16 59 63 72 -85 52 -88 50 -91 35 94 6 79 -71 28 100 11 -2 62 6 -92 22 -46 -11 22 -13 3 71 1 -28 73 -21 -6 -38 -44 56 -69 -27 -96 -88 37 64 -36 -69 26 -91 -97 -77 93 -93 -19 26 -20 -72 -95 -97 8 -67 -71 -78 64 74 -57 -69 99 66 95 -30 -95 21 22 -4 -11 37 -89 75 16 -98 81 17 -82 41 35 90 -40 -10 11 95 31 84 4 -72 4 51
|
||||
9 86 91 -15 73 -64 12 -88 -51 -77 -93 100 -99 20 -84 28 -76 40 -46 -59 96 17 -22 57 75 -14 -2 92 82 -13 -50 85 -50 62 10 -7 59 -65 81 54 48 -54 40 -90 87 13 82 -76 -31 -46 60 -96 71 -99 16 67 56 61 70 7 -63 -5 0 81 68 -51 -24 68 13 73 6 -8 42 72 -60 -90 -89 -88 -12 -43 -85 3 26 -50 -67 -3 -59 -69 40 -3 26 80 -11 -11 1 99 -65 -14 84 -79
|
||||
65 88 56 65 -49 -20 -77 71 30 -64 91 -32 -56 88 -5 -67 -49 19 -54 32 85 -41 -52 -21 84 -83 -53 -62 -24 22 -42 0 -57 67 79 -53 96 69 71 40 -29 -73 -47 56 -15 67 -29 38 -18 46 -18 -34 53 -28 -39 53 16 16 85 88 -6 52 1 27 34 -34 47 12 90 -45 49 79 3 -49 -61 75 -89 -89 -34 -62 -13 -35 -68 69 63 -11 10 -72 33 -1 28 41 63 -62 84 74 99 -49 24 -41
|
||||
-45 -79 -35 -64 -7 -56 32 -90 34 -22 -26 72 14 71 -57 91 88 -61 42 10 87 -95 -44 -71 18 35 -73 13 23 -3 -41 78 96 75 72 74 22 3 -8 -94 89 -100 -76 -93 87 15 27 39 30 -90 -8 -2 3 -70 50 -96 -64 0 13 64 39 84 -100 -67 44 -24 -100 36 -45 34 -48 -22 -9 66 -56 -57 -63 95 92 -33 60 96 15 -5 7 -40 43 24 78 -21 3 -92 68 -46 -97 36 -37 10 9 -84
|
||||
56 -69 24 -13 -95 90 -82 -7 -44 89 93 -84 -47 27 -72 65 -33 -28 98 -99 -95 12 88 73 57 69 -9 77 95 -51 -42 -78 -42 20 78 98 -58 50 71 73 64 -72 -39 -13 10 99 59 38 -18 -81 -54 49 -36 -54 -53 85 -99 -69 0 -1 6 37 -56 11 -2 -28 67 15 98 -15 17 -48 84 20 42 17 -73 9 -36 85 -64 -42 -68 67 -46 26 -4 49 13 -21 -17 -51 -23 -12 -18 -46 31 9 88 -44
|
||||
75 -27 12 -48 -48 59 10 -72 20 -17 92 88 -4 -44 53 36 67 8 28 23 -1 92 93 32 69 -52 23 65 26 -62 51 28 63 -47 -82 -47 70 -73 -40 94 -28 -32 -48 19 -2 94 -26 60 2 -26 -91 43 28 -3 -50 -58 57 25 -42 85 -28 93 -46 -27 61 7 77 100 -43 34 -31 -26 68 -99 -59 28 -4 -23 43 -99 -69 -44 34 -12 -47 -22 -56 56 -13 99 16 -89 42 -12 84 -83 -56 -47 -48 9
|
||||
16 15 -29 -22 -98 -45 51 69 -71 -44 97 -27 -85 48 74 -49 -32 96 -82 -12 -39 14 10 -85 -30 26 -45 -69 94 -1 35 48 31 22 84 34 -41 40 -75 80 -9 -39 7 23 -48 100 -69 -78 49 -51 43 -65 12 68 90 -17 -40 -7 -20 -74 -3 -71 42 22 -85 4 10 -12 -60 100 81 81 -53 22 51 26 -41 26 40 -37 31 64 -32 70 85 62 -8 53 -99 -80 16 38 39 85 53 76 -27 92 -72 -72
|
||||
61 19 59 -56 -48 25 93 -46 -83 83 19 49 -41 -78 -69 25 94 55 76 -72 -25 68 -16 -93 82 0 17 -45 54 35 -48 -59 -7 -12 19 4 -13 59 41 -69 -72 57 69 -7 -79 -55 -66 -79 100 -6 -21 38 -10 -18 -2 34 89 -64 45 -13 9 1 -98 41 64 90 29 -40 11 -74 -25 -46 24 35 57 60 55 1 -43 -23 -13 48 -56 -88 41 -74 71 35 27 35 -18 31 20 -21 90 -88 74 -53 -26 55
|
||||
5 -93 -11 -35 35 28 8 -81 -68 44 -33 18 38 -3 -40 -7 40 -33 55 -34 85 -91 2 96 26 5 -19 13 -31 -74 -46 -86 -79 -61 14 -94 -32 47 39 83 -70 -51 77 -52 -37 68 -77 -87 -63 -44 -53 41 16 17 -10 -33 100 -8 36 24 -10 -8 -93 -96 0 91 -25 40 65 -69 38 -32 8 -80 42 -92 26 -79 42 -26 -86 -3 -77 -23 62 -22 -84 -97 67 14 49 95 84 97 16 5 88 50 -46 -4
|
||||
98 82 17 78 -96 97 68 23 89 53 13 39 69 -82 76 -83 94 -83 98 26 -42 -14 78 -39 -51 75 -68 -95 -50 -56 -98 87 -97 63 -29 -81 -15 71 -100 -30 -56 -87 82 -2 73 69 37 -76 -2 62 -36 91 -73 -20 -33 43 -80 13 79 29 16 83 -20 -91 -52 45 -62 -65 82 50 -78 98 0 -78 12 85 20 81 -33 52 -61 84 -77 49 -29 17 24 0 80 -39 -31 -62 62 -5 -63 -77 86 96 -10 -38
|
||||
-22 -2 58 -28 22 -34 94 9 -88 14 80 -26 -55 35 -81 -10 -93 -61 -53 -68 -26 -1 -92 -84 30 8 36 -65 54 -79 99 -11 -28 82 -26 21 51 79 -94 69 87 -8 30 22 -29 20 92 -11 -37 -3 -52 18 -20 -53 41 -37 34 97 43 92 -6 -42 72 94 43 97 79 -8 -55 80 -67 -34 54 -51 21 -64 38 -49 88 -46 -100 20 -71 56 -2 -74 -73 -7 -97 -75 9 4 58 -76 -61 -12 -99 -38 12 67
|
||||
41 -95 -21 84 76 94 94 11 -84 19 -49 -74 -76 34 -91 -100 13 -78 -21 40 -77 62 -41 24 -26 -57 -68 22 89 27 -23 -100 -58 -90 -10 79 71 -22 43 -87 91 -76 -35 93 94 -47 -92 -8 -62 51 60 60 -91 77 70 32 -70 26 80 -76 43 -14 89 -77 99 16 -17 1 87 52 98 95 -52 6 -19 83 -33 22 7 -54 -77 35 -55 -18 4 68 94 74 50 5 21 -34 13 -84 1 31 -80 38 0 -56
|
||||
-59 3 -84 87 42 -26 -49 16 -47 93 -5 41 64 36 -8 -70 55 -100 56 33 51 -65 79 -14 -65 -15 -98 90 35 56 78 -60 -63 -75 -92 -25 -15 21 -36 -11 14 -70 84 -59 17 74 46 -2 86 72 -96 -24 41 29 -4 -38 -25 80 -84 2 26 14 34 78 26 80 -19 -14 -51 15 -63 75 17 76 13 -27 16 -68 90 44 100 40 62 37 -13 -93 -73 25 33 -81 -89 -21 -55 -86 97 57 54 -29 28 79
|
||||
98 -51 -55 40 4 -99 -97 14 -13 -67 0 49 -63 -66 75 42 9 20 85 15 92 76 -85 -43 79 -18 41 -11 46 -48 -32 -43 98 57 -39 -64 36 51 -78 40 -59 16 -92 2 73 -46 22 -69 45 -50 89 -46 26 37 -72 -52 -88 19 -76 82 -87 -49 56 61 75 45 46 -3 -74 -84 39 21 43 -13 -5 -93 -23 48 45 66 50 54 31 -96 8 97 22 3 -23 61 -32 -15 -32 89 39 -95 55 -19 -3 21
|
||||
-21 98 -68 -64 43 -59 -91 -33 37 36 7 -43 42 -27 100 57 32 30 5 -10 -53 -79 100 96 -58 -31 43 63 95 -76 -62 -56 -62 3 -7 77 58 -21 -67 -80 -75 -94 15 76 -32 -6 37 -14 -83 -54 -96 77 -27 8 -72 -15 17 -75 89 -33 89 15 20 9 85 72 -26 6 71 -100 50 54 60 -39 -41 62 -39 -75 -61 73 -56 8 -10 9 -81 -38 -47 -94 2 21 -4 72 90 -69 90 -84 -41 10 -13 -26
|
||||
44 -70 15 -23 15 81 71 -61 -23 -85 -46 17 -32 -42 -94 -3 87 -83 -81 74 -15 -50 -61 -13 69 72 -49 6 -16 -31 37 18 -51 -61 -64 -23 60 -27 -96 60 -38 -32 -90 -28 94 47 -33 -24 -3 -89 -84 -56 -31 67 -87 96 -31 73 -11 71 -57 -42 -91 -84 -33 -39 -75 69 13 11 -16 96 -75 95 -75 -64 71 6 -66 69 85 -67 -7 -72 -19 34 54 91 44 -74 50 88 -74 -15 9 26 -60 81 -96 98
|
||||
-16 -15 -55 -29 -82 -32 -94 -80 63 82 84 -9 -80 -44 -34 -21 -73 -66 -17 90 50 77 73 -31 -58 58 11 61 -41 95 -70 70 -90 -14 5 -54 46 64 -48 -9 -71 33 19 -6 -24 39 93 -40 -15 26 40 86 -32 81 -7 -2 57 -47 -50 -52 63 -57 7 -73 26 35 96 40 98 -91 95 84 -11 91 -80 31 4 -92 -70 95 -22 35 -52 56 -88 -28 93 -32 44 -66 41 -32 -71 18 -49 -50 30 -87 85 19
|
||||
-17 82 -49 -59 74 -26 -77 -42 -37 58 -5 -73 -28 50 90 81 88 66 98 -59 -21 98 99 -19 -33 47 -81 96 88 -70 -3 -44 -13 -94 33 -64 -93 40 96 29 62 -55 100 11 -82 -53 -4 79 -80 -48 69 55 55 -39 -37 -2 -76 33 39 54 17 75 89 -76 -96 -68 95 13 -4 -57 44 92 64 45 -75 73 28 -69 -62 -64 0 -11 -23 23 -78 40 58 -38 -64 -44 21 71 31 100 -22 4 -69 -49 -98 59
|
||||
86 45 -25 48 -25 69 67 -16 83 53 46 56 -8 -53 -91 -36 -52 -97 -49 -27 95 -11 -32 -27 28 -95 45 27 -99 81 -79 84 -96 49 -55 90 74 91 70 -37 46 -61 58 -52 -94 -13 -28 -15 -8 -35 -91 -54 -4 36 45 77 59 -21 -9 -90 -29 79 -50 -29 3 2 -55 -60 -58 68 -16 99 16 75 92 -22 88 -8 73 8 -24 27 -19 -19 95 -79 19 -91 15 46 -16 -70 -20 13 97 -68 90 -13 75 -35
|
||||
23 -31 37 -45 55 39 -56 81 11 1 -9 -79 -56 7 31 -87 12 -88 -42 99 -21 -35 -5 -36 18 -89 39 -13 33 -64 23 -33 -69 -38 -33 -24 65 -60 -32 -47 18 64 26 -37 -78 79 -93 37 -31 -5 -99 -99 38 38 -2 15 -32 2 87 0 22 6 33 54 -87 -74 -61 59 65 72 96 84 80 -86 -39 -47 -7 96 2 -100 99 -72 93 -76 -10 35 60 -63 -16 29 53 17 20 -14 42 -39 92 56 11 37
|
||||
-27 -24 -21 95 62 78 98 69 -61 54 -29 85 49 52 -49 42 85 -90 -50 83 -35 -62 -51 -78 -8 -10 96 51 -34 -21 24 42 1 2 6 81 -48 100 43 44 -91 62 15 11 -83 60 -57 -73 -52 -59 35 88 81 32 27 -50 69 -33 -41 -63 87 13 56 21 -26 18 61 -20 25 -89 -11 -76 -41 53 56 47 -68 40 46 87 65 -44 -13 -43 -100 -39 100 87 -58 26 -28 34 15 -72 9 87 -65 3 33 56
|
||||
-11 52 -32 -59 -92 25 11 34 -66 -45 -96 22 29 31 -10 -36 76 3 70 -83 89 64 11 -21 -40 22 80 77 37 91 9 -92 -40 42 -25 -18 90 47 -42 -63 19 43 -19 62 32 65 53 92 49 -60 -94 -1 -70 97 19 64 69 -77 -45 -42 -82 45 -70 -57 -83 19 -59 42 47 76 16 70 -57 -1 -29 -60 45 39 21 71 -59 -53 13 78 21 28 54 97 86 75 28 -11 -89 -92 42 45 31 23 -63 -32
|
||||
2
testdata/performance/01_mm1.out
vendored
2
testdata/performance/01_mm1.out
vendored
@ -1,2 +0,0 @@
|
||||
1151854256
|
||||
0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user