From 881c2a9723fda06c84ca86c436d9d6c6aab168f5 Mon Sep 17 00:00:00 2001 From: Lixuanwang Date: Mon, 4 Aug 2025 19:28:15 +0800 Subject: [PATCH] =?UTF-8?q?[backend]=E5=BC=BA=E5=8C=96=E4=BA=86=E7=BA=BF?= =?UTF-8?q?=E6=80=A7=E6=89=AB=E6=8F=8F=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/RISCv64/RISCv64LinearScan.cpp | 302 +++++++++++------- src/include/backend/RISCv64/RISCv64LLIR.h | 2 + .../backend/RISCv64/RISCv64LinearScan.h | 25 +- 3 files changed, 206 insertions(+), 123 deletions(-) diff --git a/src/backend/RISCv64/RISCv64LinearScan.cpp b/src/backend/RISCv64/RISCv64LinearScan.cpp index 628c512..43e253b 100644 --- a/src/backend/RISCv64/RISCv64LinearScan.cpp +++ b/src/backend/RISCv64/RISCv64LinearScan.cpp @@ -89,22 +89,27 @@ void RISCv64LinearScan::linearizeBlocks() { } } -// 步骤 1.2: 计算活跃区间 +// RISCv64LinearScan.cpp + void RISCv64LinearScan::computeLiveIntervals() { instr_numbering.clear(); live_intervals.clear(); unhandled.clear(); - // a. 对所有指令进行线性编号 + // a. 对所有指令进行线性编号,并记录CALL指令的位置 int num = 0; + std::set call_locations; for (auto* mbb : linear_order_blocks) { for (auto& instr : mbb->getInstructions()) { instr_numbering[instr.get()] = num; + if (instr->getOpcode() == RVOpcodes::CALL) { + call_locations.insert(num); + } num += 2; // 指令编号间隔为2,方便在溢出重写时插入指令 } } - // b. (新逻辑) 遍历所有指令,记录每个vreg首次和末次出现的位置 + // b. 遍历所有指令,记录每个vreg首次和末次出现的位置 std::map> vreg_ranges; // vreg -> {first_instr_num, last_instr_num} for (auto* mbb : linear_order_blocks) { @@ -114,28 +119,31 @@ void RISCv64LinearScan::computeLiveIntervals() { std::set use, def; getInstrUseDef(instr, use, def); - // 合并use和def集合,获取本条指令涉及的所有vreg auto all_vregs = use; all_vregs.insert(def.begin(), def.end()); - // 更新每个vreg的区间边界 for (unsigned vreg : all_vregs) { if (vreg_ranges.find(vreg) == vreg_ranges.end()) { - // 第一次见到这个vreg,记录首次和末次位置 vreg_ranges[vreg] = {instr_num, instr_num}; } else { - // 更新末次位置 vreg_ranges[vreg].second = std::max(vreg_ranges[vreg].second, instr_num); } } } } - // c. 根据记录的边界,创建LiveInterval对象 + // c. 根据记录的边界,创建LiveInterval对象,并检查是否跨越CALL for (auto const& [vreg, range] : vreg_ranges) { live_intervals.emplace(vreg, LiveInterval(vreg)); - live_intervals.at(vreg).start = range.first; - live_intervals.at(vreg).end = range.second; + auto& interval = live_intervals.at(vreg); + interval.start = range.first; + interval.end = range.second; + + // 检查此区间是否跨越了任何CALL指令 + auto it = call_locations.lower_bound(interval.start); + if (it != call_locations.end() && *it < interval.end) { + interval.crosses_call = true; + } } // d. 将所有计算出的活跃区间放入 unhandled 列表 @@ -147,62 +155,106 @@ void RISCv64LinearScan::computeLiveIntervals() { }); } -// 步骤 2: 线性扫描主算法 +// RISCv64LinearScan.cpp + +// 在类的定义中添加一个辅助函数来判断寄存器类型 +bool isCalleeSaved(PhysicalReg preg) { + if (preg >= PhysicalReg::S1 && preg <= PhysicalReg::S11) return true; + if (preg == PhysicalReg::S0) return true; // s0 通常也作为被调用者保存 + // 浮点寄存器 + if (preg >= PhysicalReg::F8 && preg <= PhysicalReg::F9) return true; + if (preg >= PhysicalReg::F18 && preg <= PhysicalReg::F27) return true; + return false; +} + +// 线性扫描主算法 bool RISCv64LinearScan::linearScan() { - // 初始化/重置状态 active.clear(); spilled_vregs.clear(); vreg_to_preg_map.clear(); - free_int_regs.clear(); - free_fp_regs.clear(); - free_int_regs.insert(allocable_int_regs.begin(), allocable_int_regs.end()); - free_fp_regs.insert(allocable_fp_regs.begin(), allocable_fp_regs.end()); - // 1. 将ABI参数的vreg直接分配给其固定的物理寄存器 + // 将寄存器池分为调用者保存和被调用者保存两类 + std::set free_caller_int_regs, free_callee_int_regs; + std::set free_caller_fp_regs, free_callee_fp_regs; + + for (auto preg : allocable_int_regs) { + if (isCalleeSaved(preg)) free_callee_int_regs.insert(preg); + else free_caller_int_regs.insert(preg); + } + for (auto preg : allocable_fp_regs) { + if (isCalleeSaved(preg)) free_callee_fp_regs.insert(preg); + else free_caller_fp_regs.insert(preg); + } + + // 预处理ABI参数寄存器 vreg_to_preg_map.insert(abi_vreg_map.begin(), abi_vreg_map.end()); - - // 2. 从unhandled列表中移除这些ABI参数区间,并将其加入active列表 std::vector normal_unhandled; for(LiveInterval* interval : unhandled) { if(abi_vreg_map.count(interval->vreg)) { - // 这是一个ABI参数区间,它已经被“预分配” active.push_back(interval); - // 从空闲池中移除对应的物理寄存器 PhysicalReg preg = abi_vreg_map.at(interval->vreg); if (isFPVReg(interval->vreg)) { - free_fp_regs.erase(preg); + if(isCalleeSaved(preg)) free_callee_fp_regs.erase(preg); else free_caller_fp_regs.erase(preg); } else { - free_int_regs.erase(preg); + if(isCalleeSaved(preg)) free_callee_int_regs.erase(preg); else free_caller_int_regs.erase(preg); } } else { - // 这是一个普通区间,留待后续处理 normal_unhandled.push_back(interval); } } - unhandled = normal_unhandled; // 更新unhandled列表,只包含普通区间 - - // 3. 对active列表(现在只包含ABI参数)按结束点排序 - std::sort(active.begin(), active.end(), [](const LiveInterval* a, const LiveInterval* b){ - return a->end < b->end; - }); + unhandled = normal_unhandled; + std::sort(active.begin(), active.end(), [](const LiveInterval* a, const LiveInterval* b){ return a->end < b->end; }); + // 主循环 for (LiveInterval* current : unhandled) { - // a. 检查并释放 active 列表中已经结束的区间 + // a. 释放active列表中已结束的区间 std::vector new_active; for (LiveInterval* active_interval : active) { if (active_interval->end < current->start) { - // 此区间已结束,释放其物理寄存器 PhysicalReg preg = vreg_to_preg_map.at(active_interval->vreg); - if (isFPVReg(active_interval->vreg)) free_fp_regs.insert(preg); - else free_int_regs.insert(preg); + if (isFPVReg(active_interval->vreg)) { + if(isCalleeSaved(preg)) free_callee_fp_regs.insert(preg); else free_caller_fp_regs.insert(preg); + } else { + if(isCalleeSaved(preg)) free_callee_int_regs.insert(preg); else free_caller_int_regs.insert(preg); + } } else { new_active.push_back(active_interval); } } active = new_active; - // b. 尝试为当前区间分配寄存器 - chooseRegForInterval(current); + // b. 约束化地为当前区间分配寄存器 + bool is_fp = isFPVReg(current->vreg); + auto& free_caller = is_fp ? free_caller_fp_regs : free_caller_int_regs; + auto& free_callee = is_fp ? free_callee_fp_regs : free_callee_int_regs; + + PhysicalReg allocated_preg = PhysicalReg::INVALID; + + if (current->crosses_call) { + // 跨调用区间:必须使用被调用者保存寄存器 + if (!free_callee.empty()) { + allocated_preg = *free_callee.begin(); + free_callee.erase(allocated_preg); + } + } else { + // 非跨调用区间:优先使用调用者保存寄存器 + if (!free_caller.empty()) { + allocated_preg = *free_caller.begin(); + free_caller.erase(allocated_preg); + } else if (!free_callee.empty()) { + allocated_preg = *free_callee.begin(); + free_callee.erase(allocated_preg); + } + } + + if (allocated_preg != PhysicalReg::INVALID) { + vreg_to_preg_map[current->vreg] = allocated_preg; + active.push_back(current); + std::sort(active.begin(), active.end(), [](const LiveInterval* a, const LiveInterval* b){ return a->end < b->end; }); + } else { + // c. 没有可用寄存器,需要溢出 + spillAtInterval(current); + } } return !spilled_vregs.empty(); } @@ -228,33 +280,29 @@ void RISCv64LinearScan::chooseRegForInterval(LiveInterval* current) { } void RISCv64LinearScan::spillAtInterval(LiveInterval* current) { - // 启发式:比较当前区间和 active 列表中结束点最晚的区间 - LiveInterval* spill_candidate = active.back(); // active已按end排序,最后一个就是结束最晚的 - - if (spill_candidate->end > current->end) { - // active中的区间结束得更晚,溢出它 - PhysicalReg preg = vreg_to_preg_map.at(spill_candidate->vreg); - - // 将被溢出区间的物理寄存器分配给当前区间 - vreg_to_preg_map[current->vreg] = preg; - - // 更新 active 列表 - active.pop_back(); // 移除被溢出的 - active.push_back(current); // 加入当前的 - std::sort(active.begin(), active.end(), [](const LiveInterval* a, const LiveInterval* b){ - return a->end < b->end; - }); + LiveInterval* spill_candidate = nullptr; + // 启发式溢出: + // 如果current需要callee-saved,则从active中找一个占用callee-saved且结束最晚的区间比较 + // 否则,找active中结束最晚的区间 + // 这里简化处理:总是找active中结束最晚的区间 + auto last_active = active.back(); + if (last_active->end > current->end) { + // 溢出active中的区间 + spill_candidate = last_active; + PhysicalReg preg = vreg_to_preg_map.at(spill_candidate->vreg); + vreg_to_preg_map[current->vreg] = preg; // 把换出的寄存器给current + // 更新active列表 + active.pop_back(); + active.push_back(current); + std::sort(active.begin(), active.end(), [](const LiveInterval* a, const LiveInterval* b){ return a->end < b->end; }); spilled_vregs.insert(spill_candidate->vreg); - if(DEBUG) std::cerr << " Spilling vreg" << spill_candidate->vreg << " to make room for vreg" << current->vreg << "\n"; } else { - // 当前区间结束得更晚,直接溢出当前区间 + // 溢出当前区间 spilled_vregs.insert(current->vreg); - if(DEBUG) std::cerr << " Spilling current vreg" << current->vreg << "\n"; } } - // 步骤 3: 重写程序,插入溢出代码 void RISCv64LinearScan::rewriteProgram() { StackFrameInfo& frame_info = MFunc->getFrameInfo(); @@ -276,15 +324,20 @@ void RISCv64LinearScan::rewriteProgram() { for (auto it = instrs.begin(); it != instrs.end(); ++it) { auto& instr = *it; - std::set use, def; - getInstrUseDef(instr.get(), use, def); + std::set use_vregs, def_vregs; + getInstrUseDef(instr.get(), use_vregs, def_vregs); - // 为每个溢出的 use 创建 load - for (unsigned old_vreg : use) { - if (spilled_vregs.count(old_vreg)) { + // 建立溢出vreg到新临时vreg的映射 + std::map use_remap; + std::map def_remap; + + // 1. 为所有溢出的USE创建LOAD指令和映射 + for (unsigned old_vreg : use_vregs) { + if (spilled_vregs.count(old_vreg) && use_remap.find(old_vreg) == use_remap.end()) { Type* type = vreg_type_map.at(old_vreg); unsigned new_temp_vreg = ISel->getNewVReg(type); - + use_remap[old_vreg] = new_temp_vreg; + RVOpcodes load_op = isFPVReg(old_vreg) ? RVOpcodes::FLW : (type->isPointer() ? RVOpcodes::LD : RVOpcodes::LW); auto load = std::make_unique(load_op); load->addOperand(std::make_unique(new_temp_vreg)); @@ -293,52 +346,74 @@ void RISCv64LinearScan::rewriteProgram() { std::make_unique(frame_info.spill_offsets.at(old_vreg)) )); new_instrs.push_back(std::move(load)); + } + } - // 替换原指令中的 use - for(auto& op : instr->getOperands()) { - if(op->getKind() == MachineOperand::KIND_REG) { - auto reg_op = static_cast(op.get()); - if(reg_op->isVirtual() && reg_op->getVRegNum() == old_vreg) { - reg_op->setVRegNum(new_temp_vreg); - } - } else if(op->getKind() == MachineOperand::KIND_MEM) { - auto mem_op = static_cast(op.get()); - auto base_reg = mem_op->getBase(); - if(base_reg->isVirtual() && base_reg->getVRegNum() == old_vreg) { - base_reg->setVRegNum(new_temp_vreg); - } - } - } + // 2. 为所有溢出的DEF创建映射 + for (unsigned old_vreg : def_vregs) { + if (spilled_vregs.count(old_vreg) && def_remap.find(old_vreg) == def_remap.end()) { + Type* type = vreg_type_map.at(old_vreg); + unsigned new_temp_vreg = ISel->getNewVReg(type); + def_remap[old_vreg] = new_temp_vreg; } } - new_instrs.push_back(std::move(instr)); + // 3. 基于角色精确地替换原指令中的操作数 + auto opcode = instr->getOpcode(); + auto& operands = instr->getOperands(); - // 为每个溢出的 def 创建 store - for (unsigned old_vreg : def) { - if (spilled_vregs.count(old_vreg)) { - Type* type = vreg_type_map.at(old_vreg); - unsigned new_temp_vreg = ISel->getNewVReg(type); - - // 替换原指令中的 def - for(auto& op : new_instrs.back()->getOperands()) { - if(op->getKind() == MachineOperand::KIND_REG) { - auto reg_op = static_cast(op.get()); - if(reg_op->isVirtual() && reg_op->getVRegNum() == old_vreg) { - reg_op->setVRegNum(new_temp_vreg); - } + auto replace_reg_op = [](RegOperand* reg_op, const std::map& remap) { + if (reg_op->isVirtual() && remap.count(reg_op->getVRegNum())) { + reg_op->setVRegNum(remap.at(reg_op->getVRegNum())); + } + }; + + if (op_info.count(opcode)) { + const auto& info = op_info.at(opcode); + // 替换 Defs + for (int idx : info.first) { + if (idx < operands.size() && operands[idx]->getKind() == MachineOperand::KIND_REG) { + replace_reg_op(static_cast(operands[idx].get()), def_remap); + } + } + // 替换 Uses + for (int idx : info.second) { + if (idx < operands.size()) { + if (operands[idx]->getKind() == MachineOperand::KIND_REG) { + replace_reg_op(static_cast(operands[idx].get()), use_remap); + } else if (operands[idx]->getKind() == MachineOperand::KIND_MEM) { + replace_reg_op(static_cast(operands[idx].get())->getBase(), use_remap); } } - - RVOpcodes store_op = isFPVReg(old_vreg) ? RVOpcodes::FSW : (type->isPointer() ? RVOpcodes::SD : RVOpcodes::SW); - auto store = std::make_unique(store_op); - store->addOperand(std::make_unique(new_temp_vreg)); - store->addOperand(std::make_unique( - std::make_unique(PhysicalReg::S0), - std::make_unique(frame_info.spill_offsets.at(old_vreg)) - )); - new_instrs.push_back(std::move(store)); } + } else if (opcode == RVOpcodes::CALL) { + // 特殊处理 CALL 指令 + if (!operands.empty() && operands[0]->getKind() == MachineOperand::KIND_REG) { + replace_reg_op(static_cast(operands[0].get()), def_remap); + } + for (size_t i = 1; i < operands.size(); ++i) { + if (operands[i]->getKind() == MachineOperand::KIND_REG) { + replace_reg_op(static_cast(operands[i].get()), use_remap); + } + } + } + + // 4. 将修改后的指令放入新列表 + new_instrs.push_back(std::move(instr)); + + // 5. 为所有溢出的DEF创建STORE指令 + for(const auto& pair : def_remap) { + unsigned old_vreg = pair.first; + unsigned new_temp_vreg = pair.second; + Type* type = vreg_type_map.at(old_vreg); + RVOpcodes store_op = isFPVReg(old_vreg) ? RVOpcodes::FSW : (type->isPointer() ? RVOpcodes::SD : RVOpcodes::SW); + auto store = std::make_unique(store_op); + store->addOperand(std::make_unique(new_temp_vreg)); + store->addOperand(std::make_unique( + std::make_unique(PhysicalReg::S0), + std::make_unique(frame_info.spill_offsets.at(old_vreg)) + )); + new_instrs.push_back(std::move(store)); } } instrs = std::move(new_instrs); @@ -382,33 +457,12 @@ void RISCv64LinearScan::applyAllocation() { } } -// 辅助函数: 获取指令的use/def集合 (仅虚拟寄存器) void RISCv64LinearScan::getInstrUseDef(const MachineInstr* instr, std::set& use, std::set& def) { // 这个函数与图着色版本中的 getInstrUseDef 逻辑完全相同,此处直接复用 auto opcode = instr->getOpcode(); const auto& operands = instr->getOperands(); - static const std::map, std::vector>> op_info = { - {RVOpcodes::ADD, {{0}, {1, 2}}}, {RVOpcodes::SUB, {{0}, {1, 2}}}, {RVOpcodes::MUL, {{0}, {1, 2}}}, - {RVOpcodes::DIV, {{0}, {1, 2}}}, {RVOpcodes::REM, {{0}, {1, 2}}}, {RVOpcodes::ADDW, {{0}, {1, 2}}}, - {RVOpcodes::SUBW, {{0}, {1, 2}}}, {RVOpcodes::MULW, {{0}, {1, 2}}}, {RVOpcodes::DIVW, {{0}, {1, 2}}}, - {RVOpcodes::REMW, {{0}, {1, 2}}}, {RVOpcodes::SLT, {{0}, {1, 2}}}, {RVOpcodes::SLTU, {{0}, {1, 2}}}, - {RVOpcodes::ADDI, {{0}, {1}}}, {RVOpcodes::ADDIW, {{0}, {1}}}, {RVOpcodes::XORI, {{0}, {1}}}, - {RVOpcodes::SLTI, {{0}, {1}}}, {RVOpcodes::SLTIU, {{0}, {1}}}, {RVOpcodes::LB, {{0}, {}}}, - {RVOpcodes::LH, {{0}, {}}}, {RVOpcodes::LW, {{0}, {}}}, {RVOpcodes::LD, {{0}, {}}}, - {RVOpcodes::LBU, {{0}, {}}}, {RVOpcodes::LHU, {{0}, {}}}, {RVOpcodes::LWU, {{0}, {}}}, - {RVOpcodes::FLW, {{0}, {}}}, {RVOpcodes::FLD, {{0}, {}}}, {RVOpcodes::SB, {{}, {0, 1}}}, - {RVOpcodes::SH, {{}, {0, 1}}}, {RVOpcodes::SW, {{}, {0, 1}}}, {RVOpcodes::SD, {{}, {0, 1}}}, - {RVOpcodes::FSW, {{}, {0, 1}}}, {RVOpcodes::FSD, {{}, {0, 1}}}, {RVOpcodes::BEQ, {{}, {0, 1}}}, - {RVOpcodes::BNE, {{}, {0, 1}}}, {RVOpcodes::BLT, {{}, {0, 1}}}, {RVOpcodes::BGE, {{}, {0, 1}}}, - {RVOpcodes::JALR, {{0}, {1}}}, {RVOpcodes::LI, {{0}, {}}}, {RVOpcodes::LA, {{0}, {}}}, - {RVOpcodes::MV, {{0}, {1}}}, {RVOpcodes::SEQZ, {{0}, {1}}}, {RVOpcodes::SNEZ, {{0}, {1}}}, - {RVOpcodes::RET, {{}, {}}}, {RVOpcodes::FADD_S, {{0}, {1, 2}}}, {RVOpcodes::FSUB_S, {{0}, {1, 2}}}, - {RVOpcodes::FMUL_S, {{0}, {1, 2}}}, {RVOpcodes::FDIV_S, {{0}, {1, 2}}}, {RVOpcodes::FEQ_S, {{0}, {1, 2}}}, - {RVOpcodes::FLT_S, {{0}, {1, 2}}}, {RVOpcodes::FLE_S, {{0}, {1, 2}}}, {RVOpcodes::FCVT_S_W, {{0}, {1}}}, - {RVOpcodes::FCVT_W_S, {{0}, {1}}}, {RVOpcodes::FMV_S, {{0}, {1}}}, {RVOpcodes::FMV_W_X, {{0}, {1}}}, - {RVOpcodes::FMV_X_W, {{0}, {1}}}, {RVOpcodes::FNEG_S, {{0}, {1}}} - }; + // op_info 的定义已被移到函数外部的命名空间中 auto get_vreg_id_if_virtual = [&](const MachineOperand* op, std::set& s) { if (op->getKind() == MachineOperand::KIND_REG) { @@ -425,9 +479,13 @@ void RISCv64LinearScan::getInstrUseDef(const MachineInstr* instr, std::setgetKind() == MachineOperand::KIND_MEM) get_vreg_id_if_virtual(op.get(), use); } else if (opcode == RVOpcodes::CALL) { + // CALL指令的特殊处理 + // 第一个操作数(如果有)是def(返回值) if (!operands.empty() && operands[0]->getKind() == MachineOperand::KIND_REG) get_vreg_id_if_virtual(operands[0].get(), def); + // 后续的寄存器操作数是use(参数) for (size_t i = 1; i < operands.size(); ++i) if (operands[i]->getKind() == MachineOperand::KIND_REG) get_vreg_id_if_virtual(operands[i].get(), use); } } diff --git a/src/include/backend/RISCv64/RISCv64LLIR.h b/src/include/backend/RISCv64/RISCv64LLIR.h index b2111ff..5d4a15f 100644 --- a/src/include/backend/RISCv64/RISCv64LLIR.h +++ b/src/include/backend/RISCv64/RISCv64LLIR.h @@ -41,6 +41,8 @@ enum class PhysicalReg { // 假设 vreg_counter 不会达到这么大的值 PHYS_REG_START_ID = 1000000, PHYS_REG_END_ID = PHYS_REG_START_ID + 320, // 预留足够的空间 + + INVALID, ///< 无效寄存器标记 }; // RISC-V 指令操作码枚举 diff --git a/src/include/backend/RISCv64/RISCv64LinearScan.h b/src/include/backend/RISCv64/RISCv64LinearScan.h index 23e2d1b..96cf5f6 100644 --- a/src/include/backend/RISCv64/RISCv64LinearScan.h +++ b/src/include/backend/RISCv64/RISCv64LinearScan.h @@ -23,7 +23,8 @@ struct LiveInterval { unsigned vreg = 0; int start = -1; int end = -1; - + bool crosses_call = false; + LiveInterval(unsigned vreg) : vreg(vreg) {} // 用于排序,按起始点从小到大 @@ -76,6 +77,28 @@ private: const std::map& vreg_type_map; }; +static const std::map, std::vector>> op_info = { + {RVOpcodes::ADD, {{0}, {1, 2}}}, {RVOpcodes::SUB, {{0}, {1, 2}}}, {RVOpcodes::MUL, {{0}, {1, 2}}}, + {RVOpcodes::DIV, {{0}, {1, 2}}}, {RVOpcodes::REM, {{0}, {1, 2}}}, {RVOpcodes::ADDW, {{0}, {1, 2}}}, + {RVOpcodes::SUBW, {{0}, {1, 2}}}, {RVOpcodes::MULW, {{0}, {1, 2}}}, {RVOpcodes::DIVW, {{0}, {1, 2}}}, + {RVOpcodes::REMW, {{0}, {1, 2}}}, {RVOpcodes::SLT, {{0}, {1, 2}}}, {RVOpcodes::SLTU, {{0}, {1, 2}}}, + {RVOpcodes::ADDI, {{0}, {1}}}, {RVOpcodes::ADDIW, {{0}, {1}}}, {RVOpcodes::XORI, {{0}, {1}}}, + {RVOpcodes::SLTI, {{0}, {1}}}, {RVOpcodes::SLTIU, {{0}, {1}}}, {RVOpcodes::LB, {{0}, {}}}, + {RVOpcodes::LH, {{0}, {}}}, {RVOpcodes::LW, {{0}, {}}}, {RVOpcodes::LD, {{0}, {}}}, + {RVOpcodes::LBU, {{0}, {}}}, {RVOpcodes::LHU, {{0}, {}}}, {RVOpcodes::LWU, {{0}, {}}}, + {RVOpcodes::FLW, {{0}, {}}}, {RVOpcodes::FLD, {{0}, {}}}, {RVOpcodes::SB, {{}, {0, 1}}}, + {RVOpcodes::SH, {{}, {0, 1}}}, {RVOpcodes::SW, {{}, {0, 1}}}, {RVOpcodes::SD, {{}, {0, 1}}}, + {RVOpcodes::FSW, {{}, {0, 1}}}, {RVOpcodes::FSD, {{}, {0, 1}}}, {RVOpcodes::BEQ, {{}, {0, 1}}}, + {RVOpcodes::BNE, {{}, {0, 1}}}, {RVOpcodes::BLT, {{}, {0, 1}}}, {RVOpcodes::BGE, {{}, {0, 1}}}, + {RVOpcodes::JALR, {{0}, {1}}}, {RVOpcodes::LI, {{0}, {}}}, {RVOpcodes::LA, {{0}, {}}}, + {RVOpcodes::MV, {{0}, {1}}}, {RVOpcodes::SEQZ, {{0}, {1}}}, {RVOpcodes::SNEZ, {{0}, {1}}}, + {RVOpcodes::RET, {{}, {}}}, {RVOpcodes::FADD_S, {{0}, {1, 2}}}, {RVOpcodes::FSUB_S, {{0}, {1, 2}}}, + {RVOpcodes::FMUL_S, {{0}, {1, 2}}}, {RVOpcodes::FDIV_S, {{0}, {1, 2}}}, {RVOpcodes::FEQ_S, {{0}, {1, 2}}}, + {RVOpcodes::FLT_S, {{0}, {1, 2}}}, {RVOpcodes::FLE_S, {{0}, {1, 2}}}, {RVOpcodes::FCVT_S_W, {{0}, {1}}}, + {RVOpcodes::FCVT_W_S, {{0}, {1}}}, {RVOpcodes::FMV_S, {{0}, {1}}}, {RVOpcodes::FMV_W_X, {{0}, {1}}}, + {RVOpcodes::FMV_X_W, {{0}, {1}}}, {RVOpcodes::FNEG_S, {{0}, {1}}} +}; + } // namespace sysy #endif // RISCV64_LINEARSCAN_H \ No newline at end of file