| 1 | //===-- EmulateInstructionRISCV.cpp ---------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "EmulateInstructionRISCV.h" |
| 10 | #include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h" |
| 11 | #include "Plugins/Process/Utility/lldb-riscv-register-enums.h" |
| 12 | #include "RISCVCInstructions.h" |
| 13 | #include "RISCVInstructions.h" |
| 14 | |
| 15 | #include "lldb/Core/Address.h" |
| 16 | #include "lldb/Core/PluginManager.h" |
| 17 | #include "lldb/Interpreter/OptionValueArray.h" |
| 18 | #include "lldb/Interpreter/OptionValueDictionary.h" |
| 19 | #include "lldb/Symbol/UnwindPlan.h" |
| 20 | #include "lldb/Utility/ArchSpec.h" |
| 21 | #include "lldb/Utility/LLDBLog.h" |
| 22 | #include "lldb/Utility/Stream.h" |
| 23 | |
| 24 | #include "llvm/ADT/STLExtras.h" |
| 25 | #include "llvm/Support/MathExtras.h" |
| 26 | #include <optional> |
| 27 | |
| 28 | using namespace llvm; |
| 29 | using namespace lldb; |
| 30 | using namespace lldb_private; |
| 31 | |
| 32 | LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionRISCV, InstructionRISCV) |
| 33 | |
| 34 | namespace lldb_private { |
| 35 | |
| 36 | /// Returns all values wrapped in Optional, or std::nullopt if any of the values |
| 37 | /// is std::nullopt. |
| 38 | template <typename... Ts> |
| 39 | static std::optional<std::tuple<Ts...>> zipOpt(std::optional<Ts> &&...ts) { |
| 40 | if ((ts.has_value() && ...)) |
| 41 | return std::optional<std::tuple<Ts...>>(std::make_tuple(std::move(*ts)...)); |
| 42 | else |
| 43 | return std::nullopt; |
| 44 | } |
| 45 | |
| 46 | // The funct3 is the type of compare in B<CMP> instructions. |
| 47 | // funct3 means "3-bits function selector", which RISC-V ISA uses as minor |
| 48 | // opcode. It reuses the major opcode encoding space. |
| 49 | constexpr uint32_t BEQ = 0b000; |
| 50 | constexpr uint32_t BNE = 0b001; |
| 51 | constexpr uint32_t BLT = 0b100; |
| 52 | constexpr uint32_t BGE = 0b101; |
| 53 | constexpr uint32_t BLTU = 0b110; |
| 54 | constexpr uint32_t BGEU = 0b111; |
| 55 | |
| 56 | // used in decoder |
| 57 | constexpr int32_t SignExt(uint32_t imm) { return int32_t(imm); } |
| 58 | |
| 59 | // used in executor |
| 60 | template <typename T> |
| 61 | constexpr std::enable_if_t<sizeof(T) <= 4, uint64_t> SextW(T value) { |
| 62 | return uint64_t(int64_t(int32_t(value))); |
| 63 | } |
| 64 | |
| 65 | // used in executor |
| 66 | template <typename T> constexpr uint64_t ZextD(T value) { |
| 67 | return uint64_t(value); |
| 68 | } |
| 69 | |
| 70 | constexpr uint32_t DecodeJImm(uint32_t inst) { |
| 71 | return (uint64_t(int64_t(int32_t(inst & 0x80000000)) >> 11)) // imm[20] |
| 72 | | (inst & 0xff000) // imm[19:12] |
| 73 | | ((inst >> 9) & 0x800) // imm[11] |
| 74 | | ((inst >> 20) & 0x7fe); // imm[10:1] |
| 75 | } |
| 76 | |
| 77 | constexpr uint32_t DecodeIImm(uint32_t inst) { |
| 78 | return int64_t(int32_t(inst)) >> 20; // imm[11:0] |
| 79 | } |
| 80 | |
| 81 | constexpr uint32_t DecodeBImm(uint32_t inst) { |
| 82 | return (uint64_t(int64_t(int32_t(inst & 0x80000000)) >> 19)) // imm[12] |
| 83 | | ((inst & 0x80) << 4) // imm[11] |
| 84 | | ((inst >> 20) & 0x7e0) // imm[10:5] |
| 85 | | ((inst >> 7) & 0x1e); // imm[4:1] |
| 86 | } |
| 87 | |
| 88 | constexpr uint32_t DecodeSImm(uint32_t inst) { |
| 89 | return (uint64_t(int64_t(int32_t(inst & 0xFE000000)) >> 20)) // imm[11:5] |
| 90 | | ((inst & 0xF80) >> 7); // imm[4:0] |
| 91 | } |
| 92 | |
| 93 | constexpr uint32_t DecodeUImm(uint32_t inst) { |
| 94 | return SextW(value: inst & 0xFFFFF000); // imm[31:12] |
| 95 | } |
| 96 | |
| 97 | static uint32_t GPREncodingToLLDB(uint32_t reg_encode) { |
| 98 | if (reg_encode == 0) |
| 99 | return gpr_x0_riscv; |
| 100 | if (reg_encode >= 1 && reg_encode <= 31) |
| 101 | return gpr_x1_riscv + reg_encode - 1; |
| 102 | return LLDB_INVALID_REGNUM; |
| 103 | } |
| 104 | |
| 105 | static uint32_t FPREncodingToLLDB(uint32_t reg_encode) { |
| 106 | if (reg_encode <= 31) |
| 107 | return fpr_f0_riscv + reg_encode; |
| 108 | return LLDB_INVALID_REGNUM; |
| 109 | } |
| 110 | |
| 111 | bool Rd::Write(EmulateInstructionRISCV &emulator, uint64_t value) { |
| 112 | uint32_t lldb_reg = GPREncodingToLLDB(reg_encode: rd); |
| 113 | EmulateInstruction::Context ctx; |
| 114 | ctx.type = EmulateInstruction::eContextRegisterStore; |
| 115 | ctx.SetNoArgs(); |
| 116 | RegisterValue registerValue; |
| 117 | registerValue.SetUInt64(uint: value); |
| 118 | return emulator.WriteRegister(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: lldb_reg, |
| 119 | reg_value: registerValue); |
| 120 | } |
| 121 | |
| 122 | bool Rd::WriteAPFloat(EmulateInstructionRISCV &emulator, APFloat value) { |
| 123 | uint32_t lldb_reg = FPREncodingToLLDB(reg_encode: rd); |
| 124 | EmulateInstruction::Context ctx; |
| 125 | ctx.type = EmulateInstruction::eContextRegisterStore; |
| 126 | ctx.SetNoArgs(); |
| 127 | RegisterValue registerValue; |
| 128 | registerValue.SetUInt64(uint: value.bitcastToAPInt().getZExtValue()); |
| 129 | return emulator.WriteRegister(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: lldb_reg, |
| 130 | reg_value: registerValue); |
| 131 | } |
| 132 | |
| 133 | std::optional<uint64_t> Rs::Read(EmulateInstructionRISCV &emulator) { |
| 134 | uint32_t lldbReg = GPREncodingToLLDB(reg_encode: rs); |
| 135 | RegisterValue value; |
| 136 | return emulator.ReadRegister(reg_kind: eRegisterKindLLDB, reg_num: lldbReg, reg_value&: value) |
| 137 | ? std::optional<uint64_t>(value.GetAsUInt64()) |
| 138 | : std::nullopt; |
| 139 | } |
| 140 | |
| 141 | std::optional<int32_t> Rs::ReadI32(EmulateInstructionRISCV &emulator) { |
| 142 | return transformOptional( |
| 143 | O: Read(emulator), F: [](uint64_t value) { return int32_t(uint32_t(value)); }); |
| 144 | } |
| 145 | |
| 146 | std::optional<int64_t> Rs::ReadI64(EmulateInstructionRISCV &emulator) { |
| 147 | return transformOptional(O: Read(emulator), |
| 148 | F: [](uint64_t value) { return int64_t(value); }); |
| 149 | } |
| 150 | |
| 151 | std::optional<uint32_t> Rs::ReadU32(EmulateInstructionRISCV &emulator) { |
| 152 | return transformOptional(O: Read(emulator), |
| 153 | F: [](uint64_t value) { return uint32_t(value); }); |
| 154 | } |
| 155 | |
| 156 | std::optional<APFloat> Rs::ReadAPFloat(EmulateInstructionRISCV &emulator, |
| 157 | bool isDouble) { |
| 158 | uint32_t lldbReg = FPREncodingToLLDB(reg_encode: rs); |
| 159 | RegisterValue value; |
| 160 | if (!emulator.ReadRegister(reg_kind: eRegisterKindLLDB, reg_num: lldbReg, reg_value&: value)) |
| 161 | return std::nullopt; |
| 162 | uint64_t bits = value.GetAsUInt64(); |
| 163 | APInt api(64, bits, false); |
| 164 | return APFloat(isDouble ? APFloat(api.bitsToDouble()) |
| 165 | : APFloat(api.bitsToFloat())); |
| 166 | } |
| 167 | |
| 168 | static bool CompareB(uint64_t rs1, uint64_t rs2, uint32_t funct3) { |
| 169 | switch (funct3) { |
| 170 | case BEQ: |
| 171 | return rs1 == rs2; |
| 172 | case BNE: |
| 173 | return rs1 != rs2; |
| 174 | case BLT: |
| 175 | return int64_t(rs1) < int64_t(rs2); |
| 176 | case BGE: |
| 177 | return int64_t(rs1) >= int64_t(rs2); |
| 178 | case BLTU: |
| 179 | return rs1 < rs2; |
| 180 | case BGEU: |
| 181 | return rs1 >= rs2; |
| 182 | default: |
| 183 | llvm_unreachable("unexpected funct3" ); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | template <typename T> |
| 188 | constexpr bool is_load = |
| 189 | std::is_same_v<T, LB> || std::is_same_v<T, LH> || std::is_same_v<T, LW> || |
| 190 | std::is_same_v<T, LD> || std::is_same_v<T, LBU> || std::is_same_v<T, LHU> || |
| 191 | std::is_same_v<T, LWU>; |
| 192 | |
| 193 | template <typename T> |
| 194 | constexpr bool is_store = std::is_same_v<T, SB> || std::is_same_v<T, SH> || |
| 195 | std::is_same_v<T, SW> || std::is_same_v<T, SD>; |
| 196 | |
| 197 | template <typename T> |
| 198 | constexpr bool is_amo_add = |
| 199 | std::is_same_v<T, AMOADD_W> || std::is_same_v<T, AMOADD_D>; |
| 200 | |
| 201 | template <typename T> |
| 202 | constexpr bool is_amo_bit_op = |
| 203 | std::is_same_v<T, AMOXOR_W> || std::is_same_v<T, AMOXOR_D> || |
| 204 | std::is_same_v<T, AMOAND_W> || std::is_same_v<T, AMOAND_D> || |
| 205 | std::is_same_v<T, AMOOR_W> || std::is_same_v<T, AMOOR_D>; |
| 206 | |
| 207 | template <typename T> |
| 208 | constexpr bool is_amo_swap = |
| 209 | std::is_same_v<T, AMOSWAP_W> || std::is_same_v<T, AMOSWAP_D>; |
| 210 | |
| 211 | template <typename T> |
| 212 | constexpr bool is_amo_cmp = |
| 213 | std::is_same_v<T, AMOMIN_W> || std::is_same_v<T, AMOMIN_D> || |
| 214 | std::is_same_v<T, AMOMAX_W> || std::is_same_v<T, AMOMAX_D> || |
| 215 | std::is_same_v<T, AMOMINU_W> || std::is_same_v<T, AMOMINU_D> || |
| 216 | std::is_same_v<T, AMOMAXU_W> || std::is_same_v<T, AMOMAXU_D>; |
| 217 | |
| 218 | template <typename I> |
| 219 | static std::enable_if_t<is_load<I> || is_store<I>, std::optional<uint64_t>> |
| 220 | LoadStoreAddr(EmulateInstructionRISCV &emulator, I inst) { |
| 221 | return transformOptional(inst.rs1.Read(emulator), [&](uint64_t rs1) { |
| 222 | return rs1 + uint64_t(SignExt(inst.imm)); |
| 223 | }); |
| 224 | } |
| 225 | |
| 226 | // Read T from memory, then load its sign-extended value m_emu to register. |
| 227 | template <typename I, typename T, typename E> |
| 228 | static std::enable_if_t<is_load<I>, bool> |
| 229 | Load(EmulateInstructionRISCV &emulator, I inst, uint64_t (*extend)(E)) { |
| 230 | auto addr = LoadStoreAddr(emulator, inst); |
| 231 | if (!addr) |
| 232 | return false; |
| 233 | return transformOptional( |
| 234 | emulator.ReadMem<T>(*addr), |
| 235 | [&](T t) { return inst.rd.Write(emulator, extend(E(t))); }) |
| 236 | .value_or(false); |
| 237 | } |
| 238 | |
| 239 | template <typename I, typename T> |
| 240 | static std::enable_if_t<is_store<I>, bool> |
| 241 | Store(EmulateInstructionRISCV &emulator, I inst) { |
| 242 | auto addr = LoadStoreAddr(emulator, inst); |
| 243 | if (!addr) |
| 244 | return false; |
| 245 | return transformOptional( |
| 246 | inst.rs2.Read(emulator), |
| 247 | [&](uint64_t rs2) { return emulator.WriteMem<T>(*addr, rs2); }) |
| 248 | .value_or(false); |
| 249 | } |
| 250 | |
| 251 | template <typename I> |
| 252 | static std::enable_if_t<is_amo_add<I> || is_amo_bit_op<I> || is_amo_swap<I> || |
| 253 | is_amo_cmp<I>, |
| 254 | std::optional<uint64_t>> |
| 255 | AtomicAddr(EmulateInstructionRISCV &emulator, I inst, unsigned int align) { |
| 256 | return transformOptional(inst.rs1.Read(emulator), |
| 257 | [&](uint64_t rs1) { |
| 258 | return rs1 % align == 0 |
| 259 | ? std::optional<uint64_t>(rs1) |
| 260 | : std::nullopt; |
| 261 | }) |
| 262 | .value_or(std::nullopt); |
| 263 | } |
| 264 | |
| 265 | template <typename I, typename T> |
| 266 | static std::enable_if_t<is_amo_swap<I>, bool> |
| 267 | AtomicSwap(EmulateInstructionRISCV &emulator, I inst, int align, |
| 268 | uint64_t (*extend)(T)) { |
| 269 | auto addr = AtomicAddr(emulator, inst, align); |
| 270 | if (!addr) |
| 271 | return false; |
| 272 | return transformOptional( |
| 273 | zipOpt(emulator.ReadMem<T>(*addr), inst.rs2.Read(emulator)), |
| 274 | [&](auto &&tup) { |
| 275 | auto [tmp, rs2] = tup; |
| 276 | return emulator.WriteMem<T>(*addr, T(rs2)) && |
| 277 | inst.rd.Write(emulator, extend(tmp)); |
| 278 | }) |
| 279 | .value_or(false); |
| 280 | } |
| 281 | |
| 282 | template <typename I, typename T> |
| 283 | static std::enable_if_t<is_amo_add<I>, bool> |
| 284 | AtomicADD(EmulateInstructionRISCV &emulator, I inst, int align, |
| 285 | uint64_t (*extend)(T)) { |
| 286 | auto addr = AtomicAddr(emulator, inst, align); |
| 287 | if (!addr) |
| 288 | return false; |
| 289 | return transformOptional( |
| 290 | zipOpt(emulator.ReadMem<T>(*addr), inst.rs2.Read(emulator)), |
| 291 | [&](auto &&tup) { |
| 292 | auto [tmp, rs2] = tup; |
| 293 | return emulator.WriteMem<T>(*addr, T(tmp + rs2)) && |
| 294 | inst.rd.Write(emulator, extend(tmp)); |
| 295 | }) |
| 296 | .value_or(false); |
| 297 | } |
| 298 | |
| 299 | template <typename I, typename T> |
| 300 | static std::enable_if_t<is_amo_bit_op<I>, bool> |
| 301 | AtomicBitOperate(EmulateInstructionRISCV &emulator, I inst, int align, |
| 302 | uint64_t (*extend)(T), T (*operate)(T, T)) { |
| 303 | auto addr = AtomicAddr(emulator, inst, align); |
| 304 | if (!addr) |
| 305 | return false; |
| 306 | return transformOptional( |
| 307 | zipOpt(emulator.ReadMem<T>(*addr), inst.rs2.Read(emulator)), |
| 308 | [&](auto &&tup) { |
| 309 | auto [value, rs2] = tup; |
| 310 | return emulator.WriteMem<T>(*addr, operate(value, T(rs2))) && |
| 311 | inst.rd.Write(emulator, extend(value)); |
| 312 | }) |
| 313 | .value_or(false); |
| 314 | } |
| 315 | |
| 316 | template <typename I, typename T> |
| 317 | static std::enable_if_t<is_amo_cmp<I>, bool> |
| 318 | AtomicCmp(EmulateInstructionRISCV &emulator, I inst, int align, |
| 319 | uint64_t (*extend)(T), T (*cmp)(T, T)) { |
| 320 | auto addr = AtomicAddr(emulator, inst, align); |
| 321 | if (!addr) |
| 322 | return false; |
| 323 | return transformOptional( |
| 324 | zipOpt(emulator.ReadMem<T>(*addr), inst.rs2.Read(emulator)), |
| 325 | [&](auto &&tup) { |
| 326 | auto [value, rs2] = tup; |
| 327 | return emulator.WriteMem<T>(*addr, cmp(value, T(rs2))) && |
| 328 | inst.rd.Write(emulator, extend(value)); |
| 329 | }) |
| 330 | .value_or(false); |
| 331 | } |
| 332 | |
| 333 | bool AtomicSequence(EmulateInstructionRISCV &emulator) { |
| 334 | // The atomic sequence is always 4 instructions long: |
| 335 | // example: |
| 336 | // 110cc: 100427af lr.w a5,(s0) |
| 337 | // 110d0: 00079663 bnez a5,110dc |
| 338 | // 110d4: 1ce426af sc.w.aq a3,a4,(s0) |
| 339 | // 110d8: fe069ae3 bnez a3,110cc |
| 340 | // 110dc: ........ <next instruction> |
| 341 | const auto pc = emulator.ReadPC(); |
| 342 | if (!pc) |
| 343 | return false; |
| 344 | auto current_pc = *pc; |
| 345 | const auto entry_pc = current_pc; |
| 346 | |
| 347 | // The first instruction should be LR.W or LR.D |
| 348 | auto inst = emulator.ReadInstructionAt(addr: current_pc); |
| 349 | if (!inst || (!std::holds_alternative<LR_W>(v: inst->decoded) && |
| 350 | !std::holds_alternative<LR_D>(v: inst->decoded))) |
| 351 | return false; |
| 352 | |
| 353 | // The second instruction should be BNE to exit address |
| 354 | inst = emulator.ReadInstructionAt(addr: current_pc += 4); |
| 355 | if (!inst || !std::holds_alternative<B>(v: inst->decoded)) |
| 356 | return false; |
| 357 | auto bne_exit = std::get<B>(v&: inst->decoded); |
| 358 | if (bne_exit.funct3 != BNE) |
| 359 | return false; |
| 360 | // save the exit address to check later |
| 361 | const auto exit_pc = current_pc + SextW(value: bne_exit.imm); |
| 362 | |
| 363 | // The third instruction should be SC.W or SC.D |
| 364 | inst = emulator.ReadInstructionAt(addr: current_pc += 4); |
| 365 | if (!inst || (!std::holds_alternative<SC_W>(v: inst->decoded) && |
| 366 | !std::holds_alternative<SC_D>(v: inst->decoded))) |
| 367 | return false; |
| 368 | |
| 369 | // The fourth instruction should be BNE to entry address |
| 370 | inst = emulator.ReadInstructionAt(addr: current_pc += 4); |
| 371 | if (!inst || !std::holds_alternative<B>(v: inst->decoded)) |
| 372 | return false; |
| 373 | auto bne_start = std::get<B>(v&: inst->decoded); |
| 374 | if (bne_start.funct3 != BNE) |
| 375 | return false; |
| 376 | if (entry_pc != current_pc + SextW(value: bne_start.imm)) |
| 377 | return false; |
| 378 | |
| 379 | current_pc += 4; |
| 380 | // check the exit address and jump to it |
| 381 | return exit_pc == current_pc && emulator.WritePC(pc: current_pc); |
| 382 | } |
| 383 | |
| 384 | template <typename T> static RISCVInst DecodeUType(uint32_t inst) { |
| 385 | return T{Rd{.rd: DecodeRD(inst)}, DecodeUImm(inst)}; |
| 386 | } |
| 387 | |
| 388 | template <typename T> static RISCVInst DecodeJType(uint32_t inst) { |
| 389 | return T{Rd{.rd: DecodeRD(inst)}, DecodeJImm(inst)}; |
| 390 | } |
| 391 | |
| 392 | template <typename T> static RISCVInst DecodeIType(uint32_t inst) { |
| 393 | return T{Rd{.rd: DecodeRD(inst)}, Rs{.rs: DecodeRS1(inst)}, DecodeIImm(inst)}; |
| 394 | } |
| 395 | |
| 396 | template <typename T> static RISCVInst DecodeBType(uint32_t inst) { |
| 397 | return T{Rs{.rs: DecodeRS1(inst)}, Rs{.rs: DecodeRS2(inst)}, DecodeBImm(inst), |
| 398 | DecodeFunct3(inst)}; |
| 399 | } |
| 400 | |
| 401 | template <typename T> static RISCVInst DecodeSType(uint32_t inst) { |
| 402 | return T{Rs{.rs: DecodeRS1(inst)}, Rs{.rs: DecodeRS2(inst)}, DecodeSImm(inst)}; |
| 403 | } |
| 404 | |
| 405 | template <typename T> static RISCVInst DecodeRType(uint32_t inst) { |
| 406 | return T{Rd{.rd: DecodeRD(inst)}, Rs{.rs: DecodeRS1(inst)}, Rs{.rs: DecodeRS2(inst)}}; |
| 407 | } |
| 408 | |
| 409 | template <typename T> static RISCVInst DecodeRShamtType(uint32_t inst) { |
| 410 | return T{Rd{.rd: DecodeRD(inst)}, Rs{.rs: DecodeRS1(inst)}, DecodeRS2(inst)}; |
| 411 | } |
| 412 | |
| 413 | template <typename T> static RISCVInst DecodeRRS1Type(uint32_t inst) { |
| 414 | return T{Rd{.rd: DecodeRD(inst)}, Rs{.rs: DecodeRS1(inst)}}; |
| 415 | } |
| 416 | |
| 417 | template <typename T> static RISCVInst DecodeR4Type(uint32_t inst) { |
| 418 | return T{Rd{.rd: DecodeRD(inst)}, Rs{.rs: DecodeRS1(inst)}, Rs{.rs: DecodeRS2(inst)}, |
| 419 | Rs{.rs: DecodeRS3(inst)}, DecodeRM(inst)}; |
| 420 | } |
| 421 | |
| 422 | static const InstrPattern PATTERNS[] = { |
| 423 | // RV32I & RV64I (The base integer ISA) // |
| 424 | {.name: "LUI" , .type_mask: 0x7F, .eigen: 0x37, .decode: DecodeUType<LUI>}, |
| 425 | {.name: "AUIPC" , .type_mask: 0x7F, .eigen: 0x17, .decode: DecodeUType<AUIPC>}, |
| 426 | {.name: "JAL" , .type_mask: 0x7F, .eigen: 0x6F, .decode: DecodeJType<JAL>}, |
| 427 | {.name: "JALR" , .type_mask: 0x707F, .eigen: 0x67, .decode: DecodeIType<JALR>}, |
| 428 | {.name: "B" , .type_mask: 0x7F, .eigen: 0x63, .decode: DecodeBType<B>}, |
| 429 | {.name: "LB" , .type_mask: 0x707F, .eigen: 0x3, .decode: DecodeIType<LB>}, |
| 430 | {.name: "LH" , .type_mask: 0x707F, .eigen: 0x1003, .decode: DecodeIType<LH>}, |
| 431 | {.name: "LW" , .type_mask: 0x707F, .eigen: 0x2003, .decode: DecodeIType<LW>}, |
| 432 | {.name: "LBU" , .type_mask: 0x707F, .eigen: 0x4003, .decode: DecodeIType<LBU>}, |
| 433 | {.name: "LHU" , .type_mask: 0x707F, .eigen: 0x5003, .decode: DecodeIType<LHU>}, |
| 434 | {.name: "SB" , .type_mask: 0x707F, .eigen: 0x23, .decode: DecodeSType<SB>}, |
| 435 | {.name: "SH" , .type_mask: 0x707F, .eigen: 0x1023, .decode: DecodeSType<SH>}, |
| 436 | {.name: "SW" , .type_mask: 0x707F, .eigen: 0x2023, .decode: DecodeSType<SW>}, |
| 437 | {.name: "ADDI" , .type_mask: 0x707F, .eigen: 0x13, .decode: DecodeIType<ADDI>}, |
| 438 | {.name: "SLTI" , .type_mask: 0x707F, .eigen: 0x2013, .decode: DecodeIType<SLTI>}, |
| 439 | {.name: "SLTIU" , .type_mask: 0x707F, .eigen: 0x3013, .decode: DecodeIType<SLTIU>}, |
| 440 | {.name: "XORI" , .type_mask: 0x707F, .eigen: 0x4013, .decode: DecodeIType<XORI>}, |
| 441 | {.name: "ORI" , .type_mask: 0x707F, .eigen: 0x6013, .decode: DecodeIType<ORI>}, |
| 442 | {.name: "ANDI" , .type_mask: 0x707F, .eigen: 0x7013, .decode: DecodeIType<ANDI>}, |
| 443 | {.name: "SLLI" , .type_mask: 0xF800707F, .eigen: 0x1013, .decode: DecodeRShamtType<SLLI>}, |
| 444 | {.name: "SRLI" , .type_mask: 0xF800707F, .eigen: 0x5013, .decode: DecodeRShamtType<SRLI>}, |
| 445 | {.name: "SRAI" , .type_mask: 0xF800707F, .eigen: 0x40005013, .decode: DecodeRShamtType<SRAI>}, |
| 446 | {.name: "ADD" , .type_mask: 0xFE00707F, .eigen: 0x33, .decode: DecodeRType<ADD>}, |
| 447 | {.name: "SUB" , .type_mask: 0xFE00707F, .eigen: 0x40000033, .decode: DecodeRType<SUB>}, |
| 448 | {.name: "SLL" , .type_mask: 0xFE00707F, .eigen: 0x1033, .decode: DecodeRType<SLL>}, |
| 449 | {.name: "SLT" , .type_mask: 0xFE00707F, .eigen: 0x2033, .decode: DecodeRType<SLT>}, |
| 450 | {.name: "SLTU" , .type_mask: 0xFE00707F, .eigen: 0x3033, .decode: DecodeRType<SLTU>}, |
| 451 | {.name: "XOR" , .type_mask: 0xFE00707F, .eigen: 0x4033, .decode: DecodeRType<XOR>}, |
| 452 | {.name: "SRL" , .type_mask: 0xFE00707F, .eigen: 0x5033, .decode: DecodeRType<SRL>}, |
| 453 | {.name: "SRA" , .type_mask: 0xFE00707F, .eigen: 0x40005033, .decode: DecodeRType<SRA>}, |
| 454 | {.name: "OR" , .type_mask: 0xFE00707F, .eigen: 0x6033, .decode: DecodeRType<OR>}, |
| 455 | {.name: "AND" , .type_mask: 0xFE00707F, .eigen: 0x7033, .decode: DecodeRType<AND>}, |
| 456 | {.name: "LWU" , .type_mask: 0x707F, .eigen: 0x6003, .decode: DecodeIType<LWU>}, |
| 457 | {.name: "LD" , .type_mask: 0x707F, .eigen: 0x3003, .decode: DecodeIType<LD>}, |
| 458 | {.name: "SD" , .type_mask: 0x707F, .eigen: 0x3023, .decode: DecodeSType<SD>}, |
| 459 | {.name: "ADDIW" , .type_mask: 0x707F, .eigen: 0x1B, .decode: DecodeIType<ADDIW>}, |
| 460 | {.name: "SLLIW" , .type_mask: 0xFE00707F, .eigen: 0x101B, .decode: DecodeRShamtType<SLLIW>}, |
| 461 | {.name: "SRLIW" , .type_mask: 0xFE00707F, .eigen: 0x501B, .decode: DecodeRShamtType<SRLIW>}, |
| 462 | {.name: "SRAIW" , .type_mask: 0xFE00707F, .eigen: 0x4000501B, .decode: DecodeRShamtType<SRAIW>}, |
| 463 | {.name: "ADDW" , .type_mask: 0xFE00707F, .eigen: 0x3B, .decode: DecodeRType<ADDW>}, |
| 464 | {.name: "SUBW" , .type_mask: 0xFE00707F, .eigen: 0x4000003B, .decode: DecodeRType<SUBW>}, |
| 465 | {.name: "SLLW" , .type_mask: 0xFE00707F, .eigen: 0x103B, .decode: DecodeRType<SLLW>}, |
| 466 | {.name: "SRLW" , .type_mask: 0xFE00707F, .eigen: 0x503B, .decode: DecodeRType<SRLW>}, |
| 467 | {.name: "SRAW" , .type_mask: 0xFE00707F, .eigen: 0x4000503B, .decode: DecodeRType<SRAW>}, |
| 468 | |
| 469 | // RV32M & RV64M (The integer multiplication and division extension) // |
| 470 | {.name: "MUL" , .type_mask: 0xFE00707F, .eigen: 0x2000033, .decode: DecodeRType<MUL>}, |
| 471 | {.name: "MULH" , .type_mask: 0xFE00707F, .eigen: 0x2001033, .decode: DecodeRType<MULH>}, |
| 472 | {.name: "MULHSU" , .type_mask: 0xFE00707F, .eigen: 0x2002033, .decode: DecodeRType<MULHSU>}, |
| 473 | {.name: "MULHU" , .type_mask: 0xFE00707F, .eigen: 0x2003033, .decode: DecodeRType<MULHU>}, |
| 474 | {.name: "DIV" , .type_mask: 0xFE00707F, .eigen: 0x2004033, .decode: DecodeRType<DIV>}, |
| 475 | {.name: "DIVU" , .type_mask: 0xFE00707F, .eigen: 0x2005033, .decode: DecodeRType<DIVU>}, |
| 476 | {.name: "REM" , .type_mask: 0xFE00707F, .eigen: 0x2006033, .decode: DecodeRType<REM>}, |
| 477 | {.name: "REMU" , .type_mask: 0xFE00707F, .eigen: 0x2007033, .decode: DecodeRType<REMU>}, |
| 478 | {.name: "MULW" , .type_mask: 0xFE00707F, .eigen: 0x200003B, .decode: DecodeRType<MULW>}, |
| 479 | {.name: "DIVW" , .type_mask: 0xFE00707F, .eigen: 0x200403B, .decode: DecodeRType<DIVW>}, |
| 480 | {.name: "DIVUW" , .type_mask: 0xFE00707F, .eigen: 0x200503B, .decode: DecodeRType<DIVUW>}, |
| 481 | {.name: "REMW" , .type_mask: 0xFE00707F, .eigen: 0x200603B, .decode: DecodeRType<REMW>}, |
| 482 | {.name: "REMUW" , .type_mask: 0xFE00707F, .eigen: 0x200703B, .decode: DecodeRType<REMUW>}, |
| 483 | |
| 484 | // RV32A & RV64A (The standard atomic instruction extension) // |
| 485 | {.name: "LR_W" , .type_mask: 0xF9F0707F, .eigen: 0x1000202F, .decode: DecodeRRS1Type<LR_W>}, |
| 486 | {.name: "LR_D" , .type_mask: 0xF9F0707F, .eigen: 0x1000302F, .decode: DecodeRRS1Type<LR_D>}, |
| 487 | {.name: "SC_W" , .type_mask: 0xF800707F, .eigen: 0x1800202F, .decode: DecodeRType<SC_W>}, |
| 488 | {.name: "SC_D" , .type_mask: 0xF800707F, .eigen: 0x1800302F, .decode: DecodeRType<SC_D>}, |
| 489 | {.name: "AMOSWAP_W" , .type_mask: 0xF800707F, .eigen: 0x800202F, .decode: DecodeRType<AMOSWAP_W>}, |
| 490 | {.name: "AMOADD_W" , .type_mask: 0xF800707F, .eigen: 0x202F, .decode: DecodeRType<AMOADD_W>}, |
| 491 | {.name: "AMOXOR_W" , .type_mask: 0xF800707F, .eigen: 0x2000202F, .decode: DecodeRType<AMOXOR_W>}, |
| 492 | {.name: "AMOAND_W" , .type_mask: 0xF800707F, .eigen: 0x6000202F, .decode: DecodeRType<AMOAND_W>}, |
| 493 | {.name: "AMOOR_W" , .type_mask: 0xF800707F, .eigen: 0x4000202F, .decode: DecodeRType<AMOOR_W>}, |
| 494 | {.name: "AMOMIN_W" , .type_mask: 0xF800707F, .eigen: 0x8000202F, .decode: DecodeRType<AMOMIN_W>}, |
| 495 | {.name: "AMOMAX_W" , .type_mask: 0xF800707F, .eigen: 0xA000202F, .decode: DecodeRType<AMOMAX_W>}, |
| 496 | {.name: "AMOMINU_W" , .type_mask: 0xF800707F, .eigen: 0xC000202F, .decode: DecodeRType<AMOMINU_W>}, |
| 497 | {.name: "AMOMAXU_W" , .type_mask: 0xF800707F, .eigen: 0xE000202F, .decode: DecodeRType<AMOMAXU_W>}, |
| 498 | {.name: "AMOSWAP_D" , .type_mask: 0xF800707F, .eigen: 0x800302F, .decode: DecodeRType<AMOSWAP_D>}, |
| 499 | {.name: "AMOADD_D" , .type_mask: 0xF800707F, .eigen: 0x302F, .decode: DecodeRType<AMOADD_D>}, |
| 500 | {.name: "AMOXOR_D" , .type_mask: 0xF800707F, .eigen: 0x2000302F, .decode: DecodeRType<AMOXOR_D>}, |
| 501 | {.name: "AMOAND_D" , .type_mask: 0xF800707F, .eigen: 0x6000302F, .decode: DecodeRType<AMOAND_D>}, |
| 502 | {.name: "AMOOR_D" , .type_mask: 0xF800707F, .eigen: 0x4000302F, .decode: DecodeRType<AMOOR_D>}, |
| 503 | {.name: "AMOMIN_D" , .type_mask: 0xF800707F, .eigen: 0x8000302F, .decode: DecodeRType<AMOMIN_D>}, |
| 504 | {.name: "AMOMAX_D" , .type_mask: 0xF800707F, .eigen: 0xA000302F, .decode: DecodeRType<AMOMAX_D>}, |
| 505 | {.name: "AMOMINU_D" , .type_mask: 0xF800707F, .eigen: 0xC000302F, .decode: DecodeRType<AMOMINU_D>}, |
| 506 | {.name: "AMOMAXU_D" , .type_mask: 0xF800707F, .eigen: 0xE000302F, .decode: DecodeRType<AMOMAXU_D>}, |
| 507 | |
| 508 | // RVC (Compressed Instructions) // |
| 509 | {.name: "C_LWSP" , .type_mask: 0xE003, .eigen: 0x4002, .decode: DecodeC_LWSP}, |
| 510 | {.name: "C_LDSP" , .type_mask: 0xE003, .eigen: 0x6002, .decode: DecodeC_LDSP, .inst_type: RV64 | RV128}, |
| 511 | {.name: "C_SWSP" , .type_mask: 0xE003, .eigen: 0xC002, .decode: DecodeC_SWSP}, |
| 512 | {.name: "C_SDSP" , .type_mask: 0xE003, .eigen: 0xE002, .decode: DecodeC_SDSP, .inst_type: RV64 | RV128}, |
| 513 | {.name: "C_LW" , .type_mask: 0xE003, .eigen: 0x4000, .decode: DecodeC_LW}, |
| 514 | {.name: "C_LD" , .type_mask: 0xE003, .eigen: 0x6000, .decode: DecodeC_LD, .inst_type: RV64 | RV128}, |
| 515 | {.name: "C_SW" , .type_mask: 0xE003, .eigen: 0xC000, .decode: DecodeC_SW}, |
| 516 | {.name: "C_SD" , .type_mask: 0xE003, .eigen: 0xE000, .decode: DecodeC_SD, .inst_type: RV64 | RV128}, |
| 517 | {.name: "C_J" , .type_mask: 0xE003, .eigen: 0xA001, .decode: DecodeC_J}, |
| 518 | {.name: "C_JR" , .type_mask: 0xF07F, .eigen: 0x8002, .decode: DecodeC_JR}, |
| 519 | {.name: "C_JALR" , .type_mask: 0xF07F, .eigen: 0x9002, .decode: DecodeC_JALR}, |
| 520 | {.name: "C_BNEZ" , .type_mask: 0xE003, .eigen: 0xE001, .decode: DecodeC_BNEZ}, |
| 521 | {.name: "C_BEQZ" , .type_mask: 0xE003, .eigen: 0xC001, .decode: DecodeC_BEQZ}, |
| 522 | {.name: "C_LI" , .type_mask: 0xE003, .eigen: 0x4001, .decode: DecodeC_LI}, |
| 523 | {.name: "C_LUI_ADDI16SP" , .type_mask: 0xE003, .eigen: 0x6001, .decode: DecodeC_LUI_ADDI16SP}, |
| 524 | {.name: "C_ADDI" , .type_mask: 0xE003, .eigen: 0x1, .decode: DecodeC_ADDI}, |
| 525 | {.name: "C_ADDIW" , .type_mask: 0xE003, .eigen: 0x2001, .decode: DecodeC_ADDIW, .inst_type: RV64 | RV128}, |
| 526 | {.name: "C_ADDI4SPN" , .type_mask: 0xE003, .eigen: 0x0, .decode: DecodeC_ADDI4SPN}, |
| 527 | {.name: "C_SLLI" , .type_mask: 0xE003, .eigen: 0x2, .decode: DecodeC_SLLI, .inst_type: RV64 | RV128}, |
| 528 | {.name: "C_SRLI" , .type_mask: 0xEC03, .eigen: 0x8001, .decode: DecodeC_SRLI, .inst_type: RV64 | RV128}, |
| 529 | {.name: "C_SRAI" , .type_mask: 0xEC03, .eigen: 0x8401, .decode: DecodeC_SRAI, .inst_type: RV64 | RV128}, |
| 530 | {.name: "C_ANDI" , .type_mask: 0xEC03, .eigen: 0x8801, .decode: DecodeC_ANDI}, |
| 531 | {.name: "C_MV" , .type_mask: 0xF003, .eigen: 0x8002, .decode: DecodeC_MV}, |
| 532 | {.name: "C_ADD" , .type_mask: 0xF003, .eigen: 0x9002, .decode: DecodeC_ADD}, |
| 533 | {.name: "C_AND" , .type_mask: 0xFC63, .eigen: 0x8C61, .decode: DecodeC_AND}, |
| 534 | {.name: "C_OR" , .type_mask: 0xFC63, .eigen: 0x8C41, .decode: DecodeC_OR}, |
| 535 | {.name: "C_XOR" , .type_mask: 0xFC63, .eigen: 0x8C21, .decode: DecodeC_XOR}, |
| 536 | {.name: "C_SUB" , .type_mask: 0xFC63, .eigen: 0x8C01, .decode: DecodeC_SUB}, |
| 537 | {.name: "C_SUBW" , .type_mask: 0xFC63, .eigen: 0x9C01, .decode: DecodeC_SUBW, .inst_type: RV64 | RV128}, |
| 538 | {.name: "C_ADDW" , .type_mask: 0xFC63, .eigen: 0x9C21, .decode: DecodeC_ADDW, .inst_type: RV64 | RV128}, |
| 539 | // RV32FC // |
| 540 | {.name: "FLW" , .type_mask: 0xE003, .eigen: 0x6000, .decode: DecodeC_FLW, .inst_type: RV32}, |
| 541 | {.name: "FSW" , .type_mask: 0xE003, .eigen: 0xE000, .decode: DecodeC_FSW, .inst_type: RV32}, |
| 542 | {.name: "FLWSP" , .type_mask: 0xE003, .eigen: 0x6002, .decode: DecodeC_FLWSP, .inst_type: RV32}, |
| 543 | {.name: "FSWSP" , .type_mask: 0xE003, .eigen: 0xE002, .decode: DecodeC_FSWSP, .inst_type: RV32}, |
| 544 | // RVDC // |
| 545 | {.name: "FLDSP" , .type_mask: 0xE003, .eigen: 0x2002, .decode: DecodeC_FLDSP, .inst_type: RV32 | RV64}, |
| 546 | {.name: "FSDSP" , .type_mask: 0xE003, .eigen: 0xA002, .decode: DecodeC_FSDSP, .inst_type: RV32 | RV64}, |
| 547 | {.name: "FLD" , .type_mask: 0xE003, .eigen: 0x2000, .decode: DecodeC_FLD, .inst_type: RV32 | RV64}, |
| 548 | {.name: "FSD" , .type_mask: 0xE003, .eigen: 0xA000, .decode: DecodeC_FSD, .inst_type: RV32 | RV64}, |
| 549 | |
| 550 | // RV32F (Extension for Single-Precision Floating-Point) // |
| 551 | {.name: "FLW" , .type_mask: 0x707F, .eigen: 0x2007, .decode: DecodeIType<FLW>}, |
| 552 | {.name: "FSW" , .type_mask: 0x707F, .eigen: 0x2027, .decode: DecodeSType<FSW>}, |
| 553 | {.name: "FMADD_S" , .type_mask: 0x600007F, .eigen: 0x43, .decode: DecodeR4Type<FMADD_S>}, |
| 554 | {.name: "FMSUB_S" , .type_mask: 0x600007F, .eigen: 0x47, .decode: DecodeR4Type<FMSUB_S>}, |
| 555 | {.name: "FNMSUB_S" , .type_mask: 0x600007F, .eigen: 0x4B, .decode: DecodeR4Type<FNMSUB_S>}, |
| 556 | {.name: "FNMADD_S" , .type_mask: 0x600007F, .eigen: 0x4F, .decode: DecodeR4Type<FNMADD_S>}, |
| 557 | {.name: "FADD_S" , .type_mask: 0xFE00007F, .eigen: 0x53, .decode: DecodeRType<FADD_S>}, |
| 558 | {.name: "FSUB_S" , .type_mask: 0xFE00007F, .eigen: 0x8000053, .decode: DecodeRType<FSUB_S>}, |
| 559 | {.name: "FMUL_S" , .type_mask: 0xFE00007F, .eigen: 0x10000053, .decode: DecodeRType<FMUL_S>}, |
| 560 | {.name: "FDIV_S" , .type_mask: 0xFE00007F, .eigen: 0x18000053, .decode: DecodeRType<FDIV_S>}, |
| 561 | {.name: "FSQRT_S" , .type_mask: 0xFFF0007F, .eigen: 0x58000053, .decode: DecodeIType<FSQRT_S>}, |
| 562 | {.name: "FSGNJ_S" , .type_mask: 0xFE00707F, .eigen: 0x20000053, .decode: DecodeRType<FSGNJ_S>}, |
| 563 | {.name: "FSGNJN_S" , .type_mask: 0xFE00707F, .eigen: 0x20001053, .decode: DecodeRType<FSGNJN_S>}, |
| 564 | {.name: "FSGNJX_S" , .type_mask: 0xFE00707F, .eigen: 0x20002053, .decode: DecodeRType<FSGNJX_S>}, |
| 565 | {.name: "FMIN_S" , .type_mask: 0xFE00707F, .eigen: 0x28000053, .decode: DecodeRType<FMIN_S>}, |
| 566 | {.name: "FMAX_S" , .type_mask: 0xFE00707F, .eigen: 0x28001053, .decode: DecodeRType<FMAX_S>}, |
| 567 | {.name: "FCVT_W_S" , .type_mask: 0xFFF0007F, .eigen: 0xC0000053, .decode: DecodeIType<FCVT_W_S>}, |
| 568 | {.name: "FCVT_WU_S" , .type_mask: 0xFFF0007F, .eigen: 0xC0100053, .decode: DecodeIType<FCVT_WU_S>}, |
| 569 | {.name: "FMV_X_W" , .type_mask: 0xFFF0707F, .eigen: 0xE0000053, .decode: DecodeIType<FMV_X_W>}, |
| 570 | {.name: "FEQ_S" , .type_mask: 0xFE00707F, .eigen: 0xA0002053, .decode: DecodeRType<FEQ_S>}, |
| 571 | {.name: "FLT_S" , .type_mask: 0xFE00707F, .eigen: 0xA0001053, .decode: DecodeRType<FLT_S>}, |
| 572 | {.name: "FLE_S" , .type_mask: 0xFE00707F, .eigen: 0xA0000053, .decode: DecodeRType<FLE_S>}, |
| 573 | {.name: "FCLASS_S" , .type_mask: 0xFFF0707F, .eigen: 0xE0001053, .decode: DecodeIType<FCLASS_S>}, |
| 574 | {.name: "FCVT_S_W" , .type_mask: 0xFFF0007F, .eigen: 0xD0000053, .decode: DecodeIType<FCVT_S_W>}, |
| 575 | {.name: "FCVT_S_WU" , .type_mask: 0xFFF0007F, .eigen: 0xD0100053, .decode: DecodeIType<FCVT_S_WU>}, |
| 576 | {.name: "FMV_W_X" , .type_mask: 0xFFF0707F, .eigen: 0xF0000053, .decode: DecodeIType<FMV_W_X>}, |
| 577 | |
| 578 | // RV64F (Extension for Single-Precision Floating-Point) // |
| 579 | {.name: "FCVT_L_S" , .type_mask: 0xFFF0007F, .eigen: 0xC0200053, .decode: DecodeIType<FCVT_L_S>}, |
| 580 | {.name: "FCVT_LU_S" , .type_mask: 0xFFF0007F, .eigen: 0xC0300053, .decode: DecodeIType<FCVT_LU_S>}, |
| 581 | {.name: "FCVT_S_L" , .type_mask: 0xFFF0007F, .eigen: 0xD0200053, .decode: DecodeIType<FCVT_S_L>}, |
| 582 | {.name: "FCVT_S_LU" , .type_mask: 0xFFF0007F, .eigen: 0xD0300053, .decode: DecodeIType<FCVT_S_LU>}, |
| 583 | |
| 584 | // RV32D (Extension for Double-Precision Floating-Point) // |
| 585 | {.name: "FLD" , .type_mask: 0x707F, .eigen: 0x3007, .decode: DecodeIType<FLD>}, |
| 586 | {.name: "FSD" , .type_mask: 0x707F, .eigen: 0x3027, .decode: DecodeSType<FSD>}, |
| 587 | {.name: "FMADD_D" , .type_mask: 0x600007F, .eigen: 0x2000043, .decode: DecodeR4Type<FMADD_D>}, |
| 588 | {.name: "FMSUB_D" , .type_mask: 0x600007F, .eigen: 0x2000047, .decode: DecodeR4Type<FMSUB_D>}, |
| 589 | {.name: "FNMSUB_D" , .type_mask: 0x600007F, .eigen: 0x200004B, .decode: DecodeR4Type<FNMSUB_D>}, |
| 590 | {.name: "FNMADD_D" , .type_mask: 0x600007F, .eigen: 0x200004F, .decode: DecodeR4Type<FNMADD_D>}, |
| 591 | {.name: "FADD_D" , .type_mask: 0xFE00007F, .eigen: 0x2000053, .decode: DecodeRType<FADD_D>}, |
| 592 | {.name: "FSUB_D" , .type_mask: 0xFE00007F, .eigen: 0xA000053, .decode: DecodeRType<FSUB_D>}, |
| 593 | {.name: "FMUL_D" , .type_mask: 0xFE00007F, .eigen: 0x12000053, .decode: DecodeRType<FMUL_D>}, |
| 594 | {.name: "FDIV_D" , .type_mask: 0xFE00007F, .eigen: 0x1A000053, .decode: DecodeRType<FDIV_D>}, |
| 595 | {.name: "FSQRT_D" , .type_mask: 0xFFF0007F, .eigen: 0x5A000053, .decode: DecodeIType<FSQRT_D>}, |
| 596 | {.name: "FSGNJ_D" , .type_mask: 0xFE00707F, .eigen: 0x22000053, .decode: DecodeRType<FSGNJ_D>}, |
| 597 | {.name: "FSGNJN_D" , .type_mask: 0xFE00707F, .eigen: 0x22001053, .decode: DecodeRType<FSGNJN_D>}, |
| 598 | {.name: "FSGNJX_D" , .type_mask: 0xFE00707F, .eigen: 0x22002053, .decode: DecodeRType<FSGNJX_D>}, |
| 599 | {.name: "FMIN_D" , .type_mask: 0xFE00707F, .eigen: 0x2A000053, .decode: DecodeRType<FMIN_D>}, |
| 600 | {.name: "FMAX_D" , .type_mask: 0xFE00707F, .eigen: 0x2A001053, .decode: DecodeRType<FMAX_D>}, |
| 601 | {.name: "FCVT_S_D" , .type_mask: 0xFFF0007F, .eigen: 0x40100053, .decode: DecodeIType<FCVT_S_D>}, |
| 602 | {.name: "FCVT_D_S" , .type_mask: 0xFFF0007F, .eigen: 0x42000053, .decode: DecodeIType<FCVT_D_S>}, |
| 603 | {.name: "FEQ_D" , .type_mask: 0xFE00707F, .eigen: 0xA2002053, .decode: DecodeRType<FEQ_D>}, |
| 604 | {.name: "FLT_D" , .type_mask: 0xFE00707F, .eigen: 0xA2001053, .decode: DecodeRType<FLT_D>}, |
| 605 | {.name: "FLE_D" , .type_mask: 0xFE00707F, .eigen: 0xA2000053, .decode: DecodeRType<FLE_D>}, |
| 606 | {.name: "FCLASS_D" , .type_mask: 0xFFF0707F, .eigen: 0xE2001053, .decode: DecodeIType<FCLASS_D>}, |
| 607 | {.name: "FCVT_W_D" , .type_mask: 0xFFF0007F, .eigen: 0xC2000053, .decode: DecodeIType<FCVT_W_D>}, |
| 608 | {.name: "FCVT_WU_D" , .type_mask: 0xFFF0007F, .eigen: 0xC2100053, .decode: DecodeIType<FCVT_WU_D>}, |
| 609 | {.name: "FCVT_D_W" , .type_mask: 0xFFF0007F, .eigen: 0xD2000053, .decode: DecodeIType<FCVT_D_W>}, |
| 610 | {.name: "FCVT_D_WU" , .type_mask: 0xFFF0007F, .eigen: 0xD2100053, .decode: DecodeIType<FCVT_D_WU>}, |
| 611 | |
| 612 | // RV64D (Extension for Double-Precision Floating-Point) // |
| 613 | {.name: "FCVT_L_D" , .type_mask: 0xFFF0007F, .eigen: 0xC2200053, .decode: DecodeIType<FCVT_L_D>}, |
| 614 | {.name: "FCVT_LU_D" , .type_mask: 0xFFF0007F, .eigen: 0xC2300053, .decode: DecodeIType<FCVT_LU_D>}, |
| 615 | {.name: "FMV_X_D" , .type_mask: 0xFFF0707F, .eigen: 0xE2000053, .decode: DecodeIType<FMV_X_D>}, |
| 616 | {.name: "FCVT_D_L" , .type_mask: 0xFFF0007F, .eigen: 0xD2200053, .decode: DecodeIType<FCVT_D_L>}, |
| 617 | {.name: "FCVT_D_LU" , .type_mask: 0xFFF0007F, .eigen: 0xD2300053, .decode: DecodeIType<FCVT_D_LU>}, |
| 618 | {.name: "FMV_D_X" , .type_mask: 0xFFF0707F, .eigen: 0xF2000053, .decode: DecodeIType<FMV_D_X>}, |
| 619 | }; |
| 620 | |
| 621 | std::optional<DecodeResult> EmulateInstructionRISCV::Decode(uint32_t inst) { |
| 622 | Log *log = GetLog(mask: LLDBLog::Unwind); |
| 623 | |
| 624 | uint16_t try_rvc = uint16_t(inst & 0x0000ffff); |
| 625 | uint8_t inst_type = RV64; |
| 626 | |
| 627 | // Try to get size of RISCV instruction. |
| 628 | // 1.2 Instruction Length Encoding |
| 629 | bool is_16b = (inst & 0b11) != 0b11; |
| 630 | bool is_32b = (inst & 0x1f) != 0x1f; |
| 631 | bool is_48b = (inst & 0x3f) != 0x1f; |
| 632 | bool is_64b = (inst & 0x7f) != 0x3f; |
| 633 | if (is_16b) |
| 634 | m_last_size = 2; |
| 635 | else if (is_32b) |
| 636 | m_last_size = 4; |
| 637 | else if (is_48b) |
| 638 | m_last_size = 6; |
| 639 | else if (is_64b) |
| 640 | m_last_size = 8; |
| 641 | else |
| 642 | // Not Valid |
| 643 | m_last_size = std::nullopt; |
| 644 | |
| 645 | // if we have ArchSpec::eCore_riscv128 in the future, |
| 646 | // we also need to check it here |
| 647 | if (m_arch.GetCore() == ArchSpec::eCore_riscv32) |
| 648 | inst_type = RV32; |
| 649 | |
| 650 | for (const InstrPattern &pat : PATTERNS) { |
| 651 | if ((inst & pat.type_mask) == pat.eigen && |
| 652 | (inst_type & pat.inst_type) != 0) { |
| 653 | LLDB_LOGF( |
| 654 | log, "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64 ") was decoded to %s" , |
| 655 | __FUNCTION__, inst, m_addr, pat.name); |
| 656 | auto decoded = is_16b ? pat.decode(try_rvc) : pat.decode(inst); |
| 657 | return DecodeResult{.decoded: decoded, .inst: inst, .is_rvc: is_16b, .pattern: pat}; |
| 658 | } |
| 659 | } |
| 660 | LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(0x%x) was unsupported" , |
| 661 | __FUNCTION__, inst); |
| 662 | return std::nullopt; |
| 663 | } |
| 664 | |
| 665 | class Executor { |
| 666 | EmulateInstructionRISCV &m_emu; |
| 667 | bool m_ignore_cond; |
| 668 | bool m_is_rvc; |
| 669 | |
| 670 | public: |
| 671 | // also used in EvaluateInstruction() |
| 672 | static uint64_t size(bool is_rvc) { return is_rvc ? 2 : 4; } |
| 673 | |
| 674 | private: |
| 675 | uint64_t delta() { return size(is_rvc: m_is_rvc); } |
| 676 | |
| 677 | public: |
| 678 | Executor(EmulateInstructionRISCV &emulator, bool ignoreCond, bool is_rvc) |
| 679 | : m_emu(emulator), m_ignore_cond(ignoreCond), m_is_rvc(is_rvc) {} |
| 680 | |
| 681 | bool operator()(LUI inst) { return inst.rd.Write(emulator&: m_emu, value: SignExt(imm: inst.imm)); } |
| 682 | bool operator()(AUIPC inst) { |
| 683 | return transformOptional(O: m_emu.ReadPC(), |
| 684 | F: [&](uint64_t pc) { |
| 685 | return inst.rd.Write(emulator&: m_emu, |
| 686 | value: SignExt(imm: inst.imm) + pc); |
| 687 | }) |
| 688 | .value_or(u: false); |
| 689 | } |
| 690 | bool operator()(JAL inst) { |
| 691 | return transformOptional(O: m_emu.ReadPC(), |
| 692 | F: [&](uint64_t pc) { |
| 693 | return inst.rd.Write(emulator&: m_emu, value: pc + delta()) && |
| 694 | m_emu.WritePC(pc: SignExt(imm: inst.imm) + pc); |
| 695 | }) |
| 696 | .value_or(u: false); |
| 697 | } |
| 698 | bool operator()(JALR inst) { |
| 699 | return transformOptional(O: zipOpt(ts: m_emu.ReadPC(), ts: inst.rs1.Read(emulator&: m_emu)), |
| 700 | F: [&](auto &&tup) { |
| 701 | auto [pc, rs1] = tup; |
| 702 | return inst.rd.Write(emulator&: m_emu, value: pc + delta()) && |
| 703 | m_emu.WritePC(pc: (SignExt(imm: inst.imm) + rs1) & |
| 704 | ~1); |
| 705 | }) |
| 706 | .value_or(u: false); |
| 707 | } |
| 708 | bool operator()(B inst) { |
| 709 | return transformOptional(O: zipOpt(ts: m_emu.ReadPC(), ts: inst.rs1.Read(emulator&: m_emu), |
| 710 | ts: inst.rs2.Read(emulator&: m_emu)), |
| 711 | F: [&](auto &&tup) { |
| 712 | auto [pc, rs1, rs2] = tup; |
| 713 | if (m_ignore_cond || |
| 714 | CompareB(rs1, rs2, inst.funct3)) |
| 715 | return m_emu.WritePC(pc: SignExt(imm: inst.imm) + pc); |
| 716 | return true; |
| 717 | }) |
| 718 | .value_or(u: false); |
| 719 | } |
| 720 | bool operator()(LB inst) { |
| 721 | return Load<LB, uint8_t, int8_t>(emulator&: m_emu, inst, extend: SextW); |
| 722 | } |
| 723 | bool operator()(LH inst) { |
| 724 | return Load<LH, uint16_t, int16_t>(emulator&: m_emu, inst, extend: SextW); |
| 725 | } |
| 726 | bool operator()(LW inst) { |
| 727 | return Load<LW, uint32_t, int32_t>(emulator&: m_emu, inst, extend: SextW); |
| 728 | } |
| 729 | bool operator()(LBU inst) { |
| 730 | return Load<LBU, uint8_t, uint8_t>(emulator&: m_emu, inst, extend: ZextD); |
| 731 | } |
| 732 | bool operator()(LHU inst) { |
| 733 | return Load<LHU, uint16_t, uint16_t>(emulator&: m_emu, inst, extend: ZextD); |
| 734 | } |
| 735 | bool operator()(SB inst) { return Store<SB, uint8_t>(emulator&: m_emu, inst); } |
| 736 | bool operator()(SH inst) { return Store<SH, uint16_t>(emulator&: m_emu, inst); } |
| 737 | bool operator()(SW inst) { return Store<SW, uint32_t>(emulator&: m_emu, inst); } |
| 738 | bool operator()(ADDI inst) { |
| 739 | return transformOptional(O: inst.rs1.ReadI64(emulator&: m_emu), |
| 740 | F: [&](int64_t rs1) { |
| 741 | return inst.rd.Write( |
| 742 | emulator&: m_emu, value: rs1 + int64_t(SignExt(imm: inst.imm))); |
| 743 | }) |
| 744 | .value_or(u: false); |
| 745 | } |
| 746 | bool operator()(SLTI inst) { |
| 747 | return transformOptional(O: inst.rs1.ReadI64(emulator&: m_emu), |
| 748 | F: [&](int64_t rs1) { |
| 749 | return inst.rd.Write( |
| 750 | emulator&: m_emu, value: rs1 < int64_t(SignExt(imm: inst.imm))); |
| 751 | }) |
| 752 | .value_or(u: false); |
| 753 | } |
| 754 | bool operator()(SLTIU inst) { |
| 755 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
| 756 | F: [&](uint64_t rs1) { |
| 757 | return inst.rd.Write( |
| 758 | emulator&: m_emu, value: rs1 < uint64_t(SignExt(imm: inst.imm))); |
| 759 | }) |
| 760 | .value_or(u: false); |
| 761 | } |
| 762 | bool operator()(XORI inst) { |
| 763 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
| 764 | F: [&](uint64_t rs1) { |
| 765 | return inst.rd.Write( |
| 766 | emulator&: m_emu, value: rs1 ^ uint64_t(SignExt(imm: inst.imm))); |
| 767 | }) |
| 768 | .value_or(u: false); |
| 769 | } |
| 770 | bool operator()(ORI inst) { |
| 771 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
| 772 | F: [&](uint64_t rs1) { |
| 773 | return inst.rd.Write( |
| 774 | emulator&: m_emu, value: rs1 | uint64_t(SignExt(imm: inst.imm))); |
| 775 | }) |
| 776 | .value_or(u: false); |
| 777 | } |
| 778 | bool operator()(ANDI inst) { |
| 779 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
| 780 | F: [&](uint64_t rs1) { |
| 781 | return inst.rd.Write( |
| 782 | emulator&: m_emu, value: rs1 & uint64_t(SignExt(imm: inst.imm))); |
| 783 | }) |
| 784 | .value_or(u: false); |
| 785 | } |
| 786 | bool operator()(ADD inst) { |
| 787 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 788 | F: [&](auto &&tup) { |
| 789 | auto [rs1, rs2] = tup; |
| 790 | return inst.rd.Write(emulator&: m_emu, value: rs1 + rs2); |
| 791 | }) |
| 792 | .value_or(u: false); |
| 793 | } |
| 794 | bool operator()(SUB inst) { |
| 795 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 796 | F: [&](auto &&tup) { |
| 797 | auto [rs1, rs2] = tup; |
| 798 | return inst.rd.Write(emulator&: m_emu, value: rs1 - rs2); |
| 799 | }) |
| 800 | .value_or(u: false); |
| 801 | } |
| 802 | bool operator()(SLL inst) { |
| 803 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 804 | F: [&](auto &&tup) { |
| 805 | auto [rs1, rs2] = tup; |
| 806 | return inst.rd.Write(emulator&: m_emu, |
| 807 | value: rs1 << (rs2 & 0b111111)); |
| 808 | }) |
| 809 | .value_or(u: false); |
| 810 | } |
| 811 | bool operator()(SLT inst) { |
| 812 | return transformOptional( |
| 813 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.ReadI64(emulator&: m_emu)), |
| 814 | F: [&](auto &&tup) { |
| 815 | auto [rs1, rs2] = tup; |
| 816 | return inst.rd.Write(emulator&: m_emu, value: rs1 < rs2); |
| 817 | }) |
| 818 | .value_or(u: false); |
| 819 | } |
| 820 | bool operator()(SLTU inst) { |
| 821 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 822 | F: [&](auto &&tup) { |
| 823 | auto [rs1, rs2] = tup; |
| 824 | return inst.rd.Write(emulator&: m_emu, value: rs1 < rs2); |
| 825 | }) |
| 826 | .value_or(u: false); |
| 827 | } |
| 828 | bool operator()(XOR inst) { |
| 829 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 830 | F: [&](auto &&tup) { |
| 831 | auto [rs1, rs2] = tup; |
| 832 | return inst.rd.Write(emulator&: m_emu, value: rs1 ^ rs2); |
| 833 | }) |
| 834 | .value_or(u: false); |
| 835 | } |
| 836 | bool operator()(SRL inst) { |
| 837 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 838 | F: [&](auto &&tup) { |
| 839 | auto [rs1, rs2] = tup; |
| 840 | return inst.rd.Write(emulator&: m_emu, |
| 841 | value: rs1 >> (rs2 & 0b111111)); |
| 842 | }) |
| 843 | .value_or(u: false); |
| 844 | } |
| 845 | bool operator()(SRA inst) { |
| 846 | return transformOptional( |
| 847 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 848 | F: [&](auto &&tup) { |
| 849 | auto [rs1, rs2] = tup; |
| 850 | return inst.rd.Write(emulator&: m_emu, value: rs1 >> (rs2 & 0b111111)); |
| 851 | }) |
| 852 | .value_or(u: false); |
| 853 | } |
| 854 | bool operator()(OR inst) { |
| 855 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 856 | F: [&](auto &&tup) { |
| 857 | auto [rs1, rs2] = tup; |
| 858 | return inst.rd.Write(emulator&: m_emu, value: rs1 | rs2); |
| 859 | }) |
| 860 | .value_or(u: false); |
| 861 | } |
| 862 | bool operator()(AND inst) { |
| 863 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 864 | F: [&](auto &&tup) { |
| 865 | auto [rs1, rs2] = tup; |
| 866 | return inst.rd.Write(emulator&: m_emu, value: rs1 & rs2); |
| 867 | }) |
| 868 | .value_or(u: false); |
| 869 | } |
| 870 | bool operator()(LWU inst) { |
| 871 | return Load<LWU, uint32_t, uint32_t>(emulator&: m_emu, inst, extend: ZextD); |
| 872 | } |
| 873 | bool operator()(LD inst) { |
| 874 | return Load<LD, uint64_t, uint64_t>(emulator&: m_emu, inst, extend: ZextD); |
| 875 | } |
| 876 | bool operator()(SD inst) { return Store<SD, uint64_t>(emulator&: m_emu, inst); } |
| 877 | bool operator()(SLLI inst) { |
| 878 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
| 879 | F: [&](uint64_t rs1) { |
| 880 | return inst.rd.Write(emulator&: m_emu, value: rs1 << inst.shamt); |
| 881 | }) |
| 882 | .value_or(u: false); |
| 883 | } |
| 884 | bool operator()(SRLI inst) { |
| 885 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
| 886 | F: [&](uint64_t rs1) { |
| 887 | return inst.rd.Write(emulator&: m_emu, value: rs1 >> inst.shamt); |
| 888 | }) |
| 889 | .value_or(u: false); |
| 890 | } |
| 891 | bool operator()(SRAI inst) { |
| 892 | return transformOptional(O: inst.rs1.ReadI64(emulator&: m_emu), |
| 893 | F: [&](int64_t rs1) { |
| 894 | return inst.rd.Write(emulator&: m_emu, value: rs1 >> inst.shamt); |
| 895 | }) |
| 896 | .value_or(u: false); |
| 897 | } |
| 898 | bool operator()(ADDIW inst) { |
| 899 | return transformOptional(O: inst.rs1.ReadI32(emulator&: m_emu), |
| 900 | F: [&](int32_t rs1) { |
| 901 | return inst.rd.Write( |
| 902 | emulator&: m_emu, value: SextW(value: rs1 + SignExt(imm: inst.imm))); |
| 903 | }) |
| 904 | .value_or(u: false); |
| 905 | } |
| 906 | bool operator()(SLLIW inst) { |
| 907 | return transformOptional(O: inst.rs1.ReadU32(emulator&: m_emu), |
| 908 | F: [&](uint32_t rs1) { |
| 909 | return inst.rd.Write(emulator&: m_emu, |
| 910 | value: SextW(value: rs1 << inst.shamt)); |
| 911 | }) |
| 912 | .value_or(u: false); |
| 913 | } |
| 914 | bool operator()(SRLIW inst) { |
| 915 | return transformOptional(O: inst.rs1.ReadU32(emulator&: m_emu), |
| 916 | F: [&](uint32_t rs1) { |
| 917 | return inst.rd.Write(emulator&: m_emu, |
| 918 | value: SextW(value: rs1 >> inst.shamt)); |
| 919 | }) |
| 920 | .value_or(u: false); |
| 921 | } |
| 922 | bool operator()(SRAIW inst) { |
| 923 | return transformOptional(O: inst.rs1.ReadI32(emulator&: m_emu), |
| 924 | F: [&](int32_t rs1) { |
| 925 | return inst.rd.Write(emulator&: m_emu, |
| 926 | value: SextW(value: rs1 >> inst.shamt)); |
| 927 | }) |
| 928 | .value_or(u: false); |
| 929 | } |
| 930 | bool operator()(ADDW inst) { |
| 931 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 932 | F: [&](auto &&tup) { |
| 933 | auto [rs1, rs2] = tup; |
| 934 | return inst.rd.Write(emulator&: m_emu, |
| 935 | value: SextW(value: uint32_t(rs1 + rs2))); |
| 936 | }) |
| 937 | .value_or(u: false); |
| 938 | } |
| 939 | bool operator()(SUBW inst) { |
| 940 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 941 | F: [&](auto &&tup) { |
| 942 | auto [rs1, rs2] = tup; |
| 943 | return inst.rd.Write(emulator&: m_emu, |
| 944 | value: SextW(value: uint32_t(rs1 - rs2))); |
| 945 | }) |
| 946 | .value_or(u: false); |
| 947 | } |
| 948 | bool operator()(SLLW inst) { |
| 949 | return transformOptional( |
| 950 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
| 951 | F: [&](auto &&tup) { |
| 952 | auto [rs1, rs2] = tup; |
| 953 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 << (rs2 & 0b11111))); |
| 954 | }) |
| 955 | .value_or(u: false); |
| 956 | } |
| 957 | bool operator()(SRLW inst) { |
| 958 | return transformOptional( |
| 959 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
| 960 | F: [&](auto &&tup) { |
| 961 | auto [rs1, rs2] = tup; |
| 962 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 >> (rs2 & 0b11111))); |
| 963 | }) |
| 964 | .value_or(u: false); |
| 965 | } |
| 966 | bool operator()(SRAW inst) { |
| 967 | return transformOptional( |
| 968 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 969 | F: [&](auto &&tup) { |
| 970 | auto [rs1, rs2] = tup; |
| 971 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 >> (rs2 & 0b11111))); |
| 972 | }) |
| 973 | .value_or(u: false); |
| 974 | } |
| 975 | // RV32M & RV64M (Integer Multiplication and Division Extension) // |
| 976 | bool operator()(MUL inst) { |
| 977 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 978 | F: [&](auto &&tup) { |
| 979 | auto [rs1, rs2] = tup; |
| 980 | return inst.rd.Write(emulator&: m_emu, value: rs1 * rs2); |
| 981 | }) |
| 982 | .value_or(u: false); |
| 983 | } |
| 984 | bool operator()(MULH inst) { |
| 985 | return transformOptional( |
| 986 | O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 987 | F: [&](auto &&tup) { |
| 988 | auto [rs1, rs2] = tup; |
| 989 | // signed * signed |
| 990 | auto mul = APInt(128, rs1, true) * APInt(128, rs2, true); |
| 991 | return inst.rd.Write(emulator&: m_emu, |
| 992 | value: mul.ashr(ShiftAmt: 64).trunc(width: 64).getZExtValue()); |
| 993 | }) |
| 994 | .value_or(u: false); |
| 995 | } |
| 996 | bool operator()(MULHSU inst) { |
| 997 | return transformOptional( |
| 998 | O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 999 | F: [&](auto &&tup) { |
| 1000 | auto [rs1, rs2] = tup; |
| 1001 | // signed * unsigned |
| 1002 | auto mul = |
| 1003 | APInt(128, rs1, true).zext(width: 128) * APInt(128, rs2, false); |
| 1004 | return inst.rd.Write(emulator&: m_emu, |
| 1005 | value: mul.lshr(shiftAmt: 64).trunc(width: 64).getZExtValue()); |
| 1006 | }) |
| 1007 | .value_or(u: false); |
| 1008 | } |
| 1009 | bool operator()(MULHU inst) { |
| 1010 | return transformOptional( |
| 1011 | O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 1012 | F: [&](auto &&tup) { |
| 1013 | auto [rs1, rs2] = tup; |
| 1014 | // unsigned * unsigned |
| 1015 | auto mul = APInt(128, rs1, false) * APInt(128, rs2, false); |
| 1016 | return inst.rd.Write(emulator&: m_emu, |
| 1017 | value: mul.lshr(shiftAmt: 64).trunc(width: 64).getZExtValue()); |
| 1018 | }) |
| 1019 | .value_or(u: false); |
| 1020 | } |
| 1021 | bool operator()(DIV inst) { |
| 1022 | return transformOptional( |
| 1023 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.ReadI64(emulator&: m_emu)), |
| 1024 | F: [&](auto &&tup) { |
| 1025 | auto [dividend, divisor] = tup; |
| 1026 | |
| 1027 | if (divisor == 0) |
| 1028 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
| 1029 | |
| 1030 | if (dividend == INT64_MIN && divisor == -1) |
| 1031 | return inst.rd.Write(emulator&: m_emu, value: dividend); |
| 1032 | |
| 1033 | return inst.rd.Write(emulator&: m_emu, value: dividend / divisor); |
| 1034 | }) |
| 1035 | .value_or(u: false); |
| 1036 | } |
| 1037 | bool operator()(DIVU inst) { |
| 1038 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 1039 | F: [&](auto &&tup) { |
| 1040 | auto [dividend, divisor] = tup; |
| 1041 | |
| 1042 | if (divisor == 0) |
| 1043 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
| 1044 | |
| 1045 | return inst.rd.Write(emulator&: m_emu, value: dividend / divisor); |
| 1046 | }) |
| 1047 | .value_or(u: false); |
| 1048 | } |
| 1049 | bool operator()(REM inst) { |
| 1050 | return transformOptional( |
| 1051 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.ReadI64(emulator&: m_emu)), |
| 1052 | F: [&](auto &&tup) { |
| 1053 | auto [dividend, divisor] = tup; |
| 1054 | |
| 1055 | if (divisor == 0) |
| 1056 | return inst.rd.Write(emulator&: m_emu, value: dividend); |
| 1057 | |
| 1058 | if (dividend == INT64_MIN && divisor == -1) |
| 1059 | return inst.rd.Write(emulator&: m_emu, value: 0); |
| 1060 | |
| 1061 | return inst.rd.Write(emulator&: m_emu, value: dividend % divisor); |
| 1062 | }) |
| 1063 | .value_or(u: false); |
| 1064 | } |
| 1065 | bool operator()(REMU inst) { |
| 1066 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
| 1067 | F: [&](auto &&tup) { |
| 1068 | auto [dividend, divisor] = tup; |
| 1069 | |
| 1070 | if (divisor == 0) |
| 1071 | return inst.rd.Write(emulator&: m_emu, value: dividend); |
| 1072 | |
| 1073 | return inst.rd.Write(emulator&: m_emu, value: dividend % divisor); |
| 1074 | }) |
| 1075 | .value_or(u: false); |
| 1076 | } |
| 1077 | bool operator()(MULW inst) { |
| 1078 | return transformOptional( |
| 1079 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.ReadI32(emulator&: m_emu)), |
| 1080 | F: [&](auto &&tup) { |
| 1081 | auto [rs1, rs2] = tup; |
| 1082 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 * rs2)); |
| 1083 | }) |
| 1084 | .value_or(u: false); |
| 1085 | } |
| 1086 | bool operator()(DIVW inst) { |
| 1087 | return transformOptional( |
| 1088 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.ReadI32(emulator&: m_emu)), |
| 1089 | F: [&](auto &&tup) { |
| 1090 | auto [dividend, divisor] = tup; |
| 1091 | |
| 1092 | if (divisor == 0) |
| 1093 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
| 1094 | |
| 1095 | if (dividend == INT32_MIN && divisor == -1) |
| 1096 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend)); |
| 1097 | |
| 1098 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend / divisor)); |
| 1099 | }) |
| 1100 | .value_or(u: false); |
| 1101 | } |
| 1102 | bool operator()(DIVUW inst) { |
| 1103 | return transformOptional( |
| 1104 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
| 1105 | F: [&](auto &&tup) { |
| 1106 | auto [dividend, divisor] = tup; |
| 1107 | |
| 1108 | if (divisor == 0) |
| 1109 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
| 1110 | |
| 1111 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend / divisor)); |
| 1112 | }) |
| 1113 | .value_or(u: false); |
| 1114 | } |
| 1115 | bool operator()(REMW inst) { |
| 1116 | return transformOptional( |
| 1117 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.ReadI32(emulator&: m_emu)), |
| 1118 | F: [&](auto &&tup) { |
| 1119 | auto [dividend, divisor] = tup; |
| 1120 | |
| 1121 | if (divisor == 0) |
| 1122 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend)); |
| 1123 | |
| 1124 | if (dividend == INT32_MIN && divisor == -1) |
| 1125 | return inst.rd.Write(emulator&: m_emu, value: 0); |
| 1126 | |
| 1127 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend % divisor)); |
| 1128 | }) |
| 1129 | .value_or(u: false); |
| 1130 | } |
| 1131 | bool operator()(REMUW inst) { |
| 1132 | return transformOptional( |
| 1133 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
| 1134 | F: [&](auto &&tup) { |
| 1135 | auto [dividend, divisor] = tup; |
| 1136 | |
| 1137 | if (divisor == 0) |
| 1138 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend)); |
| 1139 | |
| 1140 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend % divisor)); |
| 1141 | }) |
| 1142 | .value_or(u: false); |
| 1143 | } |
| 1144 | // RV32A & RV64A (The standard atomic instruction extension) // |
| 1145 | bool operator()(LR_W) { return AtomicSequence(emulator&: m_emu); } |
| 1146 | bool operator()(LR_D) { return AtomicSequence(emulator&: m_emu); } |
| 1147 | bool operator()(SC_W) { |
| 1148 | llvm_unreachable("should be handled in AtomicSequence" ); |
| 1149 | } |
| 1150 | bool operator()(SC_D) { |
| 1151 | llvm_unreachable("should be handled in AtomicSequence" ); |
| 1152 | } |
| 1153 | bool operator()(AMOSWAP_W inst) { |
| 1154 | return AtomicSwap<AMOSWAP_W, uint32_t>(emulator&: m_emu, inst, align: 4, extend: SextW); |
| 1155 | } |
| 1156 | bool operator()(AMOADD_W inst) { |
| 1157 | return AtomicADD<AMOADD_W, uint32_t>(emulator&: m_emu, inst, align: 4, extend: SextW); |
| 1158 | } |
| 1159 | bool operator()(AMOXOR_W inst) { |
| 1160 | return AtomicBitOperate<AMOXOR_W, uint32_t>( |
| 1161 | emulator&: m_emu, inst, align: 4, extend: SextW, operate: [](uint32_t a, uint32_t b) { return a ^ b; }); |
| 1162 | } |
| 1163 | bool operator()(AMOAND_W inst) { |
| 1164 | return AtomicBitOperate<AMOAND_W, uint32_t>( |
| 1165 | emulator&: m_emu, inst, align: 4, extend: SextW, operate: [](uint32_t a, uint32_t b) { return a & b; }); |
| 1166 | } |
| 1167 | bool operator()(AMOOR_W inst) { |
| 1168 | return AtomicBitOperate<AMOOR_W, uint32_t>( |
| 1169 | emulator&: m_emu, inst, align: 4, extend: SextW, operate: [](uint32_t a, uint32_t b) { return a | b; }); |
| 1170 | } |
| 1171 | bool operator()(AMOMIN_W inst) { |
| 1172 | return AtomicCmp<AMOMIN_W, uint32_t>( |
| 1173 | emulator&: m_emu, inst, align: 4, extend: SextW, cmp: [](uint32_t a, uint32_t b) { |
| 1174 | return uint32_t(std::min(a: int32_t(a), b: int32_t(b))); |
| 1175 | }); |
| 1176 | } |
| 1177 | bool operator()(AMOMAX_W inst) { |
| 1178 | return AtomicCmp<AMOMAX_W, uint32_t>( |
| 1179 | emulator&: m_emu, inst, align: 4, extend: SextW, cmp: [](uint32_t a, uint32_t b) { |
| 1180 | return uint32_t(std::max(a: int32_t(a), b: int32_t(b))); |
| 1181 | }); |
| 1182 | } |
| 1183 | bool operator()(AMOMINU_W inst) { |
| 1184 | return AtomicCmp<AMOMINU_W, uint32_t>( |
| 1185 | emulator&: m_emu, inst, align: 4, extend: SextW, |
| 1186 | cmp: [](uint32_t a, uint32_t b) { return std::min(a: a, b: b); }); |
| 1187 | } |
| 1188 | bool operator()(AMOMAXU_W inst) { |
| 1189 | return AtomicCmp<AMOMAXU_W, uint32_t>( |
| 1190 | emulator&: m_emu, inst, align: 4, extend: SextW, |
| 1191 | cmp: [](uint32_t a, uint32_t b) { return std::max(a: a, b: b); }); |
| 1192 | } |
| 1193 | bool operator()(AMOSWAP_D inst) { |
| 1194 | return AtomicSwap<AMOSWAP_D, uint64_t>(emulator&: m_emu, inst, align: 8, extend: ZextD); |
| 1195 | } |
| 1196 | bool operator()(AMOADD_D inst) { |
| 1197 | return AtomicADD<AMOADD_D, uint64_t>(emulator&: m_emu, inst, align: 8, extend: ZextD); |
| 1198 | } |
| 1199 | bool operator()(AMOXOR_D inst) { |
| 1200 | return AtomicBitOperate<AMOXOR_D, uint64_t>( |
| 1201 | emulator&: m_emu, inst, align: 8, extend: ZextD, operate: [](uint64_t a, uint64_t b) { return a ^ b; }); |
| 1202 | } |
| 1203 | bool operator()(AMOAND_D inst) { |
| 1204 | return AtomicBitOperate<AMOAND_D, uint64_t>( |
| 1205 | emulator&: m_emu, inst, align: 8, extend: ZextD, operate: [](uint64_t a, uint64_t b) { return a & b; }); |
| 1206 | } |
| 1207 | bool operator()(AMOOR_D inst) { |
| 1208 | return AtomicBitOperate<AMOOR_D, uint64_t>( |
| 1209 | emulator&: m_emu, inst, align: 8, extend: ZextD, operate: [](uint64_t a, uint64_t b) { return a | b; }); |
| 1210 | } |
| 1211 | bool operator()(AMOMIN_D inst) { |
| 1212 | return AtomicCmp<AMOMIN_D, uint64_t>( |
| 1213 | emulator&: m_emu, inst, align: 8, extend: ZextD, cmp: [](uint64_t a, uint64_t b) { |
| 1214 | return uint64_t(std::min(a: int64_t(a), b: int64_t(b))); |
| 1215 | }); |
| 1216 | } |
| 1217 | bool operator()(AMOMAX_D inst) { |
| 1218 | return AtomicCmp<AMOMAX_D, uint64_t>( |
| 1219 | emulator&: m_emu, inst, align: 8, extend: ZextD, cmp: [](uint64_t a, uint64_t b) { |
| 1220 | return uint64_t(std::max(a: int64_t(a), b: int64_t(b))); |
| 1221 | }); |
| 1222 | } |
| 1223 | bool operator()(AMOMINU_D inst) { |
| 1224 | return AtomicCmp<AMOMINU_D, uint64_t>( |
| 1225 | emulator&: m_emu, inst, align: 8, extend: ZextD, |
| 1226 | cmp: [](uint64_t a, uint64_t b) { return std::min(a: a, b: b); }); |
| 1227 | } |
| 1228 | bool operator()(AMOMAXU_D inst) { |
| 1229 | return AtomicCmp<AMOMAXU_D, uint64_t>( |
| 1230 | emulator&: m_emu, inst, align: 8, extend: ZextD, |
| 1231 | cmp: [](uint64_t a, uint64_t b) { return std::max(a: a, b: b); }); |
| 1232 | } |
| 1233 | template <typename T> |
| 1234 | bool F_Load(T inst, const fltSemantics &(*semantics)(), |
| 1235 | unsigned int numBits) { |
| 1236 | return transformOptional(inst.rs1.Read(m_emu), |
| 1237 | [&](auto &&rs1) { |
| 1238 | uint64_t addr = rs1 + uint64_t(inst.imm); |
| 1239 | uint64_t bits = *m_emu.ReadMem<uint64_t>(addr); |
| 1240 | APFloat f(semantics(), APInt(numBits, bits)); |
| 1241 | return inst.rd.WriteAPFloat(m_emu, f); |
| 1242 | }) |
| 1243 | .value_or(false); |
| 1244 | } |
| 1245 | bool operator()(FLW inst) { return F_Load(inst, semantics: &APFloat::IEEEsingle, numBits: 32); } |
| 1246 | template <typename T> bool F_Store(T inst, bool isDouble) { |
| 1247 | return transformOptional(zipOpt(inst.rs1.Read(m_emu), |
| 1248 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
| 1249 | [&](auto &&tup) { |
| 1250 | auto [rs1, rs2] = tup; |
| 1251 | uint64_t addr = rs1 + uint64_t(inst.imm); |
| 1252 | uint64_t bits = |
| 1253 | rs2.bitcastToAPInt().getZExtValue(); |
| 1254 | return m_emu.WriteMem<uint64_t>(addr, value: bits); |
| 1255 | }) |
| 1256 | .value_or(false); |
| 1257 | } |
| 1258 | bool operator()(FSW inst) { return F_Store(inst, isDouble: false); } |
| 1259 | std::tuple<bool, APFloat> FusedMultiplyAdd(APFloat rs1, APFloat rs2, |
| 1260 | APFloat rs3) { |
| 1261 | auto opStatus = rs1.fusedMultiplyAdd(Multiplicand: rs2, Addend: rs3, RM: m_emu.GetRoundingMode()); |
| 1262 | auto res = m_emu.SetAccruedExceptions(opStatus); |
| 1263 | return {res, rs1}; |
| 1264 | } |
| 1265 | template <typename T> |
| 1266 | bool FMA(T inst, bool isDouble, float rs2_sign, float rs3_sign) { |
| 1267 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1268 | inst.rs2.ReadAPFloat(m_emu, isDouble), |
| 1269 | inst.rs3.ReadAPFloat(m_emu, isDouble)), |
| 1270 | [&](auto &&tup) { |
| 1271 | auto [rs1, rs2, rs3] = tup; |
| 1272 | rs2.copySign(APFloat(rs2_sign)); |
| 1273 | rs3.copySign(APFloat(rs3_sign)); |
| 1274 | auto [res, f] = FusedMultiplyAdd(rs1, rs2, rs3); |
| 1275 | return res && inst.rd.WriteAPFloat(m_emu, f); |
| 1276 | }) |
| 1277 | .value_or(false); |
| 1278 | } |
| 1279 | bool operator()(FMADD_S inst) { return FMA(inst, isDouble: false, rs2_sign: 1.0f, rs3_sign: 1.0f); } |
| 1280 | bool operator()(FMSUB_S inst) { return FMA(inst, isDouble: false, rs2_sign: 1.0f, rs3_sign: -1.0f); } |
| 1281 | bool operator()(FNMSUB_S inst) { return FMA(inst, isDouble: false, rs2_sign: -1.0f, rs3_sign: 1.0f); } |
| 1282 | bool operator()(FNMADD_S inst) { return FMA(inst, isDouble: false, rs2_sign: -1.0f, rs3_sign: -1.0f); } |
| 1283 | template <typename T> |
| 1284 | bool F_Op(T inst, bool isDouble, |
| 1285 | APFloat::opStatus (APFloat::*f)(const APFloat &RHS, |
| 1286 | APFloat::roundingMode RM)) { |
| 1287 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1288 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
| 1289 | [&](auto &&tup) { |
| 1290 | auto [rs1, rs2] = tup; |
| 1291 | auto res = |
| 1292 | ((&rs1)->*f)(rs2, m_emu.GetRoundingMode()); |
| 1293 | inst.rd.WriteAPFloat(m_emu, rs1); |
| 1294 | return m_emu.SetAccruedExceptions(res); |
| 1295 | }) |
| 1296 | .value_or(false); |
| 1297 | } |
| 1298 | bool operator()(FADD_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::add); } |
| 1299 | bool operator()(FSUB_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::subtract); } |
| 1300 | bool operator()(FMUL_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::multiply); } |
| 1301 | bool operator()(FDIV_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::divide); } |
| 1302 | bool operator()(FSQRT_S inst) { |
| 1303 | // TODO: APFloat doesn't have a sqrt function. |
| 1304 | return false; |
| 1305 | } |
| 1306 | template <typename T> bool F_SignInj(T inst, bool isDouble, bool isNegate) { |
| 1307 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1308 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
| 1309 | [&](auto &&tup) { |
| 1310 | auto [rs1, rs2] = tup; |
| 1311 | if (isNegate) |
| 1312 | rs2.changeSign(); |
| 1313 | rs1.copySign(rs2); |
| 1314 | return inst.rd.WriteAPFloat(m_emu, rs1); |
| 1315 | }) |
| 1316 | .value_or(false); |
| 1317 | } |
| 1318 | bool operator()(FSGNJ_S inst) { return F_SignInj(inst, isDouble: false, isNegate: false); } |
| 1319 | bool operator()(FSGNJN_S inst) { return F_SignInj(inst, isDouble: false, isNegate: true); } |
| 1320 | template <typename T> bool F_SignInjXor(T inst, bool isDouble) { |
| 1321 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1322 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
| 1323 | [&](auto &&tup) { |
| 1324 | auto [rs1, rs2] = tup; |
| 1325 | // spec: the sign bit is the XOR of the sign bits |
| 1326 | // of rs1 and rs2. if rs1 and rs2 have the same |
| 1327 | // signs set rs1 to positive else set rs1 to |
| 1328 | // negative |
| 1329 | if (rs1.isNegative() == rs2.isNegative()) { |
| 1330 | rs1.clearSign(); |
| 1331 | } else { |
| 1332 | rs1.clearSign(); |
| 1333 | rs1.changeSign(); |
| 1334 | } |
| 1335 | return inst.rd.WriteAPFloat(m_emu, rs1); |
| 1336 | }) |
| 1337 | .value_or(false); |
| 1338 | } |
| 1339 | bool operator()(FSGNJX_S inst) { return F_SignInjXor(inst, isDouble: false); } |
| 1340 | template <typename T> |
| 1341 | bool F_MAX_MIN(T inst, bool isDouble, |
| 1342 | APFloat (*f)(const APFloat &A, const APFloat &B)) { |
| 1343 | return transformOptional( |
| 1344 | zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1345 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
| 1346 | [&](auto &&tup) { |
| 1347 | auto [rs1, rs2] = tup; |
| 1348 | // If both inputs are NaNs, the result is the canonical NaN. |
| 1349 | // If only one operand is a NaN, the result is the non-NaN |
| 1350 | // operand. Signaling NaN inputs set the invalid operation |
| 1351 | // exception flag, even when the result is not NaN. |
| 1352 | if (rs1.isNaN() || rs2.isNaN()) |
| 1353 | m_emu.SetAccruedExceptions(APFloat::opInvalidOp); |
| 1354 | if (rs1.isNaN() && rs2.isNaN()) { |
| 1355 | auto canonicalNaN = APFloat::getQNaN(Sem: rs1.getSemantics()); |
| 1356 | return inst.rd.WriteAPFloat(m_emu, canonicalNaN); |
| 1357 | } |
| 1358 | return inst.rd.WriteAPFloat(m_emu, f(rs1, rs2)); |
| 1359 | }) |
| 1360 | .value_or(false); |
| 1361 | } |
| 1362 | bool operator()(FMIN_S inst) { return F_MAX_MIN(inst, isDouble: false, f: minnum); } |
| 1363 | bool operator()(FMAX_S inst) { return F_MAX_MIN(inst, isDouble: false, f: maxnum); } |
| 1364 | bool operator()(FCVT_W_S inst) { |
| 1365 | return FCVT_i2f<FCVT_W_S, int32_t, float>(inst, isDouble: false, |
| 1366 | f: &APFloat::convertToFloat); |
| 1367 | } |
| 1368 | bool operator()(FCVT_WU_S inst) { |
| 1369 | return FCVT_i2f<FCVT_WU_S, uint32_t, float>(inst, isDouble: false, |
| 1370 | f: &APFloat::convertToFloat); |
| 1371 | } |
| 1372 | template <typename T> bool FMV_f2i(T inst, bool isDouble) { |
| 1373 | return transformOptional( |
| 1374 | inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1375 | [&](auto &&rs1) { |
| 1376 | if (rs1.isNaN()) { |
| 1377 | if (isDouble) |
| 1378 | return inst.rd.Write(m_emu, 0x7ff8'0000'0000'0000); |
| 1379 | else |
| 1380 | return inst.rd.Write(m_emu, 0x7fc0'0000); |
| 1381 | } |
| 1382 | auto bits = rs1.bitcastToAPInt().getZExtValue(); |
| 1383 | if (isDouble) |
| 1384 | return inst.rd.Write(m_emu, bits); |
| 1385 | else |
| 1386 | return inst.rd.Write(m_emu, uint64_t(bits & 0xffff'ffff)); |
| 1387 | }) |
| 1388 | .value_or(false); |
| 1389 | } |
| 1390 | bool operator()(FMV_X_W inst) { return FMV_f2i(inst, isDouble: false); } |
| 1391 | enum F_CMP { |
| 1392 | FEQ, |
| 1393 | FLT, |
| 1394 | FLE, |
| 1395 | }; |
| 1396 | template <typename T> bool F_Compare(T inst, bool isDouble, F_CMP cmp) { |
| 1397 | return transformOptional( |
| 1398 | zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1399 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
| 1400 | [&](auto &&tup) { |
| 1401 | auto [rs1, rs2] = tup; |
| 1402 | if (rs1.isNaN() || rs2.isNaN()) { |
| 1403 | if (cmp == FEQ) { |
| 1404 | if (rs1.isSignaling() || rs2.isSignaling()) { |
| 1405 | auto res = |
| 1406 | m_emu.SetAccruedExceptions(APFloat::opInvalidOp); |
| 1407 | return res && inst.rd.Write(m_emu, 0); |
| 1408 | } |
| 1409 | } |
| 1410 | auto res = m_emu.SetAccruedExceptions(APFloat::opInvalidOp); |
| 1411 | return res && inst.rd.Write(m_emu, 0); |
| 1412 | } |
| 1413 | switch (cmp) { |
| 1414 | case FEQ: |
| 1415 | return inst.rd.Write(m_emu, |
| 1416 | rs1.compare(rs2) == APFloat::cmpEqual); |
| 1417 | case FLT: |
| 1418 | return inst.rd.Write(m_emu, rs1.compare(rs2) == |
| 1419 | APFloat::cmpLessThan); |
| 1420 | case FLE: |
| 1421 | return inst.rd.Write(m_emu, rs1.compare(rs2) != |
| 1422 | APFloat::cmpGreaterThan); |
| 1423 | } |
| 1424 | llvm_unreachable("unsupported F_CMP" ); |
| 1425 | }) |
| 1426 | .value_or(false); |
| 1427 | } |
| 1428 | |
| 1429 | bool operator()(FEQ_S inst) { return F_Compare(inst, isDouble: false, cmp: FEQ); } |
| 1430 | bool operator()(FLT_S inst) { return F_Compare(inst, isDouble: false, cmp: FLT); } |
| 1431 | bool operator()(FLE_S inst) { return F_Compare(inst, isDouble: false, cmp: FLE); } |
| 1432 | template <typename T> bool FCLASS(T inst, bool isDouble) { |
| 1433 | return transformOptional(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1434 | [&](auto &&rs1) { |
| 1435 | uint64_t result = 0; |
| 1436 | if (rs1.isInfinity() && rs1.isNegative()) |
| 1437 | result |= 1 << 0; |
| 1438 | // neg normal |
| 1439 | if (rs1.isNormal() && rs1.isNegative()) |
| 1440 | result |= 1 << 1; |
| 1441 | // neg subnormal |
| 1442 | if (rs1.isDenormal() && rs1.isNegative()) |
| 1443 | result |= 1 << 2; |
| 1444 | if (rs1.isNegZero()) |
| 1445 | result |= 1 << 3; |
| 1446 | if (rs1.isPosZero()) |
| 1447 | result |= 1 << 4; |
| 1448 | // pos normal |
| 1449 | if (rs1.isNormal() && !rs1.isNegative()) |
| 1450 | result |= 1 << 5; |
| 1451 | // pos subnormal |
| 1452 | if (rs1.isDenormal() && !rs1.isNegative()) |
| 1453 | result |= 1 << 6; |
| 1454 | if (rs1.isInfinity() && !rs1.isNegative()) |
| 1455 | result |= 1 << 7; |
| 1456 | if (rs1.isNaN()) { |
| 1457 | if (rs1.isSignaling()) |
| 1458 | result |= 1 << 8; |
| 1459 | else |
| 1460 | result |= 1 << 9; |
| 1461 | } |
| 1462 | return inst.rd.Write(m_emu, result); |
| 1463 | }) |
| 1464 | .value_or(false); |
| 1465 | } |
| 1466 | bool operator()(FCLASS_S inst) { return FCLASS(inst, isDouble: false); } |
| 1467 | template <typename T, typename E> |
| 1468 | bool FCVT_f2i(T inst, std::optional<E> (Rs::*f)(EmulateInstructionRISCV &emu), |
| 1469 | const fltSemantics &semantics) { |
| 1470 | return transformOptional(((&inst.rs1)->*f)(m_emu), |
| 1471 | [&](auto &&rs1) { |
| 1472 | APFloat apf(semantics, rs1); |
| 1473 | return inst.rd.WriteAPFloat(m_emu, apf); |
| 1474 | }) |
| 1475 | .value_or(false); |
| 1476 | } |
| 1477 | bool operator()(FCVT_S_W inst) { |
| 1478 | return FCVT_f2i(inst, f: &Rs::ReadI32, semantics: APFloat::IEEEsingle()); |
| 1479 | } |
| 1480 | bool operator()(FCVT_S_WU inst) { |
| 1481 | return FCVT_f2i(inst, f: &Rs::ReadU32, semantics: APFloat::IEEEsingle()); |
| 1482 | } |
| 1483 | template <typename T, typename E> |
| 1484 | bool FMV_i2f(T inst, unsigned int numBits, E (APInt::*f)() const) { |
| 1485 | return transformOptional(inst.rs1.Read(m_emu), |
| 1486 | [&](auto &&rs1) { |
| 1487 | APInt apInt(numBits, rs1); |
| 1488 | if (numBits == 32) // a.k.a. float |
| 1489 | apInt = APInt(numBits, NanUnBoxing(rs1)); |
| 1490 | APFloat apf((&apInt->*f)()); |
| 1491 | return inst.rd.WriteAPFloat(m_emu, apf); |
| 1492 | }) |
| 1493 | .value_or(false); |
| 1494 | } |
| 1495 | bool operator()(FMV_W_X inst) { |
| 1496 | return FMV_i2f(inst, numBits: 32, f: &APInt::bitsToFloat); |
| 1497 | } |
| 1498 | template <typename I, typename E, typename T> |
| 1499 | bool FCVT_i2f(I inst, bool isDouble, T (APFloat::*f)() const) { |
| 1500 | return transformOptional(inst.rs1.ReadAPFloat(m_emu, isDouble), |
| 1501 | [&](auto &&rs1) { |
| 1502 | E res = E((&rs1->*f)()); |
| 1503 | return inst.rd.Write(m_emu, uint64_t(res)); |
| 1504 | }) |
| 1505 | .value_or(false); |
| 1506 | } |
| 1507 | bool operator()(FCVT_L_S inst) { |
| 1508 | return FCVT_i2f<FCVT_L_S, int64_t, float>(inst, isDouble: false, |
| 1509 | f: &APFloat::convertToFloat); |
| 1510 | } |
| 1511 | bool operator()(FCVT_LU_S inst) { |
| 1512 | return FCVT_i2f<FCVT_LU_S, uint64_t, float>(inst, isDouble: false, |
| 1513 | f: &APFloat::convertToFloat); |
| 1514 | } |
| 1515 | bool operator()(FCVT_S_L inst) { |
| 1516 | return FCVT_f2i(inst, f: &Rs::ReadI64, semantics: APFloat::IEEEsingle()); |
| 1517 | } |
| 1518 | bool operator()(FCVT_S_LU inst) { |
| 1519 | return FCVT_f2i(inst, f: &Rs::Read, semantics: APFloat::IEEEsingle()); |
| 1520 | } |
| 1521 | bool operator()(FLD inst) { return F_Load(inst, semantics: &APFloat::IEEEdouble, numBits: 64); } |
| 1522 | bool operator()(FSD inst) { return F_Store(inst, isDouble: true); } |
| 1523 | bool operator()(FMADD_D inst) { return FMA(inst, isDouble: true, rs2_sign: 1.0f, rs3_sign: 1.0f); } |
| 1524 | bool operator()(FMSUB_D inst) { return FMA(inst, isDouble: true, rs2_sign: 1.0f, rs3_sign: -1.0f); } |
| 1525 | bool operator()(FNMSUB_D inst) { return FMA(inst, isDouble: true, rs2_sign: -1.0f, rs3_sign: 1.0f); } |
| 1526 | bool operator()(FNMADD_D inst) { return FMA(inst, isDouble: true, rs2_sign: -1.0f, rs3_sign: -1.0f); } |
| 1527 | bool operator()(FADD_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::add); } |
| 1528 | bool operator()(FSUB_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::subtract); } |
| 1529 | bool operator()(FMUL_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::multiply); } |
| 1530 | bool operator()(FDIV_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::divide); } |
| 1531 | bool operator()(FSQRT_D inst) { |
| 1532 | // TODO: APFloat doesn't have a sqrt function. |
| 1533 | return false; |
| 1534 | } |
| 1535 | bool operator()(FSGNJ_D inst) { return F_SignInj(inst, isDouble: true, isNegate: false); } |
| 1536 | bool operator()(FSGNJN_D inst) { return F_SignInj(inst, isDouble: true, isNegate: true); } |
| 1537 | bool operator()(FSGNJX_D inst) { return F_SignInjXor(inst, isDouble: true); } |
| 1538 | bool operator()(FMIN_D inst) { return F_MAX_MIN(inst, isDouble: true, f: minnum); } |
| 1539 | bool operator()(FMAX_D inst) { return F_MAX_MIN(inst, isDouble: true, f: maxnum); } |
| 1540 | bool operator()(FCVT_S_D inst) { |
| 1541 | return transformOptional(O: inst.rs1.ReadAPFloat(emulator&: m_emu, isDouble: true), |
| 1542 | F: [&](auto &&rs1) { |
| 1543 | double d = rs1.convertToDouble(); |
| 1544 | APFloat apf((float(d))); |
| 1545 | return inst.rd.WriteAPFloat(emulator&: m_emu, value: apf); |
| 1546 | }) |
| 1547 | .value_or(u: false); |
| 1548 | } |
| 1549 | bool operator()(FCVT_D_S inst) { |
| 1550 | return transformOptional(O: inst.rs1.ReadAPFloat(emulator&: m_emu, isDouble: false), |
| 1551 | F: [&](auto &&rs1) { |
| 1552 | float f = rs1.convertToFloat(); |
| 1553 | APFloat apf((double(f))); |
| 1554 | return inst.rd.WriteAPFloat(emulator&: m_emu, value: apf); |
| 1555 | }) |
| 1556 | .value_or(u: false); |
| 1557 | } |
| 1558 | bool operator()(FEQ_D inst) { return F_Compare(inst, isDouble: true, cmp: FEQ); } |
| 1559 | bool operator()(FLT_D inst) { return F_Compare(inst, isDouble: true, cmp: FLT); } |
| 1560 | bool operator()(FLE_D inst) { return F_Compare(inst, isDouble: true, cmp: FLE); } |
| 1561 | bool operator()(FCLASS_D inst) { return FCLASS(inst, isDouble: true); } |
| 1562 | bool operator()(FCVT_W_D inst) { |
| 1563 | return FCVT_i2f<FCVT_W_D, int32_t, double>(inst, isDouble: true, |
| 1564 | f: &APFloat::convertToDouble); |
| 1565 | } |
| 1566 | bool operator()(FCVT_WU_D inst) { |
| 1567 | return FCVT_i2f<FCVT_WU_D, uint32_t, double>(inst, isDouble: true, |
| 1568 | f: &APFloat::convertToDouble); |
| 1569 | } |
| 1570 | bool operator()(FCVT_D_W inst) { |
| 1571 | return FCVT_f2i(inst, f: &Rs::ReadI32, semantics: APFloat::IEEEdouble()); |
| 1572 | } |
| 1573 | bool operator()(FCVT_D_WU inst) { |
| 1574 | return FCVT_f2i(inst, f: &Rs::ReadU32, semantics: APFloat::IEEEdouble()); |
| 1575 | } |
| 1576 | bool operator()(FCVT_L_D inst) { |
| 1577 | return FCVT_i2f<FCVT_L_D, int64_t, double>(inst, isDouble: true, |
| 1578 | f: &APFloat::convertToDouble); |
| 1579 | } |
| 1580 | bool operator()(FCVT_LU_D inst) { |
| 1581 | return FCVT_i2f<FCVT_LU_D, uint64_t, double>(inst, isDouble: true, |
| 1582 | f: &APFloat::convertToDouble); |
| 1583 | } |
| 1584 | bool operator()(FMV_X_D inst) { return FMV_f2i(inst, isDouble: true); } |
| 1585 | bool operator()(FCVT_D_L inst) { |
| 1586 | return FCVT_f2i(inst, f: &Rs::ReadI64, semantics: APFloat::IEEEdouble()); |
| 1587 | } |
| 1588 | bool operator()(FCVT_D_LU inst) { |
| 1589 | return FCVT_f2i(inst, f: &Rs::Read, semantics: APFloat::IEEEdouble()); |
| 1590 | } |
| 1591 | bool operator()(FMV_D_X inst) { |
| 1592 | return FMV_i2f(inst, numBits: 64, f: &APInt::bitsToDouble); |
| 1593 | } |
| 1594 | bool operator()(INVALID inst) { return false; } |
| 1595 | bool operator()(RESERVED inst) { return false; } |
| 1596 | bool operator()(EBREAK inst) { return false; } |
| 1597 | bool operator()(HINT inst) { return true; } |
| 1598 | bool operator()(NOP inst) { return true; } |
| 1599 | }; |
| 1600 | |
| 1601 | bool EmulateInstructionRISCV::Execute(DecodeResult inst, bool ignore_cond) { |
| 1602 | return std::visit(visitor: Executor(*this, ignore_cond, inst.is_rvc), variants&: inst.decoded); |
| 1603 | } |
| 1604 | |
| 1605 | bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) { |
| 1606 | bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC; |
| 1607 | bool ignore_cond = options & eEmulateInstructionOptionIgnoreConditions; |
| 1608 | |
| 1609 | if (!increase_pc) |
| 1610 | return Execute(inst: m_decoded, ignore_cond); |
| 1611 | |
| 1612 | auto old_pc = ReadPC(); |
| 1613 | if (!old_pc) |
| 1614 | return false; |
| 1615 | |
| 1616 | bool success = Execute(inst: m_decoded, ignore_cond); |
| 1617 | if (!success) |
| 1618 | return false; |
| 1619 | |
| 1620 | auto new_pc = ReadPC(); |
| 1621 | if (!new_pc) |
| 1622 | return false; |
| 1623 | |
| 1624 | // If the pc is not updated during execution, we do it here. |
| 1625 | return new_pc != old_pc || |
| 1626 | WritePC(pc: *old_pc + Executor::size(is_rvc: m_decoded.is_rvc)); |
| 1627 | } |
| 1628 | |
| 1629 | std::optional<DecodeResult> |
| 1630 | EmulateInstructionRISCV::ReadInstructionAt(addr_t addr) { |
| 1631 | return transformOptional(O: ReadMem<uint32_t>(addr), |
| 1632 | F: [&](uint32_t inst) { return Decode(inst); }) |
| 1633 | .value_or(u: std::nullopt); |
| 1634 | } |
| 1635 | |
| 1636 | bool EmulateInstructionRISCV::ReadInstruction() { |
| 1637 | auto addr = ReadPC(); |
| 1638 | m_addr = addr.value_or(LLDB_INVALID_ADDRESS); |
| 1639 | if (!addr) |
| 1640 | return false; |
| 1641 | auto inst = ReadInstructionAt(addr: *addr); |
| 1642 | if (!inst) |
| 1643 | return false; |
| 1644 | m_decoded = *inst; |
| 1645 | if (inst->is_rvc) |
| 1646 | m_opcode.SetOpcode16(inst: inst->inst, order: GetByteOrder()); |
| 1647 | else |
| 1648 | m_opcode.SetOpcode32(inst: inst->inst, order: GetByteOrder()); |
| 1649 | return true; |
| 1650 | } |
| 1651 | |
| 1652 | std::optional<addr_t> EmulateInstructionRISCV::ReadPC() { |
| 1653 | bool success = false; |
| 1654 | auto addr = ReadRegisterUnsigned(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, |
| 1655 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
| 1656 | return success ? std::optional<addr_t>(addr) : std::nullopt; |
| 1657 | } |
| 1658 | |
| 1659 | bool EmulateInstructionRISCV::WritePC(addr_t pc) { |
| 1660 | EmulateInstruction::Context ctx; |
| 1661 | ctx.type = eContextAdvancePC; |
| 1662 | ctx.SetNoArgs(); |
| 1663 | return WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindGeneric, |
| 1664 | LLDB_REGNUM_GENERIC_PC, reg_value: pc); |
| 1665 | } |
| 1666 | |
| 1667 | RoundingMode EmulateInstructionRISCV::GetRoundingMode() { |
| 1668 | bool success = false; |
| 1669 | auto fcsr = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: fpr_fcsr_riscv, |
| 1670 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
| 1671 | if (!success) |
| 1672 | return RoundingMode::Invalid; |
| 1673 | auto frm = (fcsr >> 5) & 0x7; |
| 1674 | switch (frm) { |
| 1675 | case 0b000: |
| 1676 | return RoundingMode::NearestTiesToEven; |
| 1677 | case 0b001: |
| 1678 | return RoundingMode::TowardZero; |
| 1679 | case 0b010: |
| 1680 | return RoundingMode::TowardNegative; |
| 1681 | case 0b011: |
| 1682 | return RoundingMode::TowardPositive; |
| 1683 | case 0b111: |
| 1684 | return RoundingMode::Dynamic; |
| 1685 | default: |
| 1686 | // Reserved for future use. |
| 1687 | return RoundingMode::Invalid; |
| 1688 | } |
| 1689 | } |
| 1690 | |
| 1691 | bool EmulateInstructionRISCV::SetAccruedExceptions( |
| 1692 | APFloatBase::opStatus opStatus) { |
| 1693 | bool success = false; |
| 1694 | auto fcsr = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: fpr_fcsr_riscv, |
| 1695 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
| 1696 | if (!success) |
| 1697 | return false; |
| 1698 | switch (opStatus) { |
| 1699 | case APFloatBase::opInvalidOp: |
| 1700 | fcsr |= 1 << 4; |
| 1701 | break; |
| 1702 | case APFloatBase::opDivByZero: |
| 1703 | fcsr |= 1 << 3; |
| 1704 | break; |
| 1705 | case APFloatBase::opOverflow: |
| 1706 | fcsr |= 1 << 2; |
| 1707 | break; |
| 1708 | case APFloatBase::opUnderflow: |
| 1709 | fcsr |= 1 << 1; |
| 1710 | break; |
| 1711 | case APFloatBase::opInexact: |
| 1712 | fcsr |= 1 << 0; |
| 1713 | break; |
| 1714 | case APFloatBase::opOK: |
| 1715 | break; |
| 1716 | } |
| 1717 | EmulateInstruction::Context ctx; |
| 1718 | ctx.type = eContextRegisterStore; |
| 1719 | ctx.SetNoArgs(); |
| 1720 | return WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: fpr_fcsr_riscv, reg_value: fcsr); |
| 1721 | } |
| 1722 | |
| 1723 | std::optional<RegisterInfo> |
| 1724 | EmulateInstructionRISCV::GetRegisterInfo(RegisterKind reg_kind, |
| 1725 | uint32_t reg_index) { |
| 1726 | if (reg_kind == eRegisterKindGeneric) { |
| 1727 | switch (reg_index) { |
| 1728 | case LLDB_REGNUM_GENERIC_PC: |
| 1729 | reg_kind = eRegisterKindLLDB; |
| 1730 | reg_index = gpr_pc_riscv; |
| 1731 | break; |
| 1732 | case LLDB_REGNUM_GENERIC_SP: |
| 1733 | reg_kind = eRegisterKindLLDB; |
| 1734 | reg_index = gpr_sp_riscv; |
| 1735 | break; |
| 1736 | case LLDB_REGNUM_GENERIC_FP: |
| 1737 | reg_kind = eRegisterKindLLDB; |
| 1738 | reg_index = gpr_fp_riscv; |
| 1739 | break; |
| 1740 | case LLDB_REGNUM_GENERIC_RA: |
| 1741 | reg_kind = eRegisterKindLLDB; |
| 1742 | reg_index = gpr_ra_riscv; |
| 1743 | break; |
| 1744 | // We may handle LLDB_REGNUM_GENERIC_ARGx when more instructions are |
| 1745 | // supported. |
| 1746 | default: |
| 1747 | llvm_unreachable("unsupported register" ); |
| 1748 | } |
| 1749 | } |
| 1750 | |
| 1751 | RegisterInfoPOSIX_riscv64 reg_info(m_arch, |
| 1752 | RegisterInfoPOSIX_riscv64::eRegsetMaskAll); |
| 1753 | const RegisterInfo *array = reg_info.GetRegisterInfo(); |
| 1754 | const uint32_t length = reg_info.GetRegisterCount(); |
| 1755 | |
| 1756 | if (reg_index >= length || reg_kind != eRegisterKindLLDB) |
| 1757 | return {}; |
| 1758 | |
| 1759 | return array[reg_index]; |
| 1760 | } |
| 1761 | |
| 1762 | bool EmulateInstructionRISCV::SetTargetTriple(const ArchSpec &arch) { |
| 1763 | return SupportsThisArch(arch); |
| 1764 | } |
| 1765 | |
| 1766 | bool EmulateInstructionRISCV::TestEmulation(Stream &out_stream, ArchSpec &arch, |
| 1767 | OptionValueDictionary *test_data) { |
| 1768 | return false; |
| 1769 | } |
| 1770 | |
| 1771 | void EmulateInstructionRISCV::Initialize() { |
| 1772 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
| 1773 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
| 1774 | } |
| 1775 | |
| 1776 | void EmulateInstructionRISCV::Terminate() { |
| 1777 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
| 1778 | } |
| 1779 | |
| 1780 | lldb_private::EmulateInstruction * |
| 1781 | EmulateInstructionRISCV::CreateInstance(const ArchSpec &arch, |
| 1782 | InstructionType inst_type) { |
| 1783 | if (EmulateInstructionRISCV::SupportsThisInstructionType(inst_type) && |
| 1784 | SupportsThisArch(arch)) { |
| 1785 | return new EmulateInstructionRISCV(arch); |
| 1786 | } |
| 1787 | |
| 1788 | return nullptr; |
| 1789 | } |
| 1790 | |
| 1791 | bool EmulateInstructionRISCV::SupportsThisArch(const ArchSpec &arch) { |
| 1792 | return arch.GetTriple().isRISCV(); |
| 1793 | } |
| 1794 | |
| 1795 | } // namespace lldb_private |
| 1796 | |