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 | // check whether the compressed encode could be valid |
626 | uint16_t mask = try_rvc & 0b11; |
627 | bool is_rvc = try_rvc != 0 && mask != 3; |
628 | uint8_t inst_type = RV64; |
629 | |
630 | // if we have ArchSpec::eCore_riscv128 in the future, |
631 | // we also need to check it here |
632 | if (m_arch.GetCore() == ArchSpec::eCore_riscv32) |
633 | inst_type = RV32; |
634 | |
635 | for (const InstrPattern &pat : PATTERNS) { |
636 | if ((inst & pat.type_mask) == pat.eigen && |
637 | (inst_type & pat.inst_type) != 0) { |
638 | LLDB_LOGF( |
639 | log, "EmulateInstructionRISCV::%s: inst(%x at %" PRIx64 ") was decoded to %s" , |
640 | __FUNCTION__, inst, m_addr, pat.name); |
641 | auto decoded = is_rvc ? pat.decode(try_rvc) : pat.decode(inst); |
642 | return DecodeResult{.decoded: decoded, .inst: inst, .is_rvc: is_rvc, .pattern: pat}; |
643 | } |
644 | } |
645 | LLDB_LOGF(log, "EmulateInstructionRISCV::%s: inst(0x%x) was unsupported" , |
646 | __FUNCTION__, inst); |
647 | return std::nullopt; |
648 | } |
649 | |
650 | class Executor { |
651 | EmulateInstructionRISCV &m_emu; |
652 | bool m_ignore_cond; |
653 | bool m_is_rvc; |
654 | |
655 | public: |
656 | // also used in EvaluateInstruction() |
657 | static uint64_t size(bool is_rvc) { return is_rvc ? 2 : 4; } |
658 | |
659 | private: |
660 | uint64_t delta() { return size(is_rvc: m_is_rvc); } |
661 | |
662 | public: |
663 | Executor(EmulateInstructionRISCV &emulator, bool ignoreCond, bool is_rvc) |
664 | : m_emu(emulator), m_ignore_cond(ignoreCond), m_is_rvc(is_rvc) {} |
665 | |
666 | bool operator()(LUI inst) { return inst.rd.Write(emulator&: m_emu, value: SignExt(imm: inst.imm)); } |
667 | bool operator()(AUIPC inst) { |
668 | return transformOptional(O: m_emu.ReadPC(), |
669 | F: [&](uint64_t pc) { |
670 | return inst.rd.Write(emulator&: m_emu, |
671 | value: SignExt(imm: inst.imm) + pc); |
672 | }) |
673 | .value_or(u: false); |
674 | } |
675 | bool operator()(JAL inst) { |
676 | return transformOptional(O: m_emu.ReadPC(), |
677 | F: [&](uint64_t pc) { |
678 | return inst.rd.Write(emulator&: m_emu, value: pc + delta()) && |
679 | m_emu.WritePC(pc: SignExt(imm: inst.imm) + pc); |
680 | }) |
681 | .value_or(u: false); |
682 | } |
683 | bool operator()(JALR inst) { |
684 | return transformOptional(O: zipOpt(ts: m_emu.ReadPC(), ts: inst.rs1.Read(emulator&: m_emu)), |
685 | F: [&](auto &&tup) { |
686 | auto [pc, rs1] = tup; |
687 | return inst.rd.Write(emulator&: m_emu, value: pc + delta()) && |
688 | m_emu.WritePC(pc: (SignExt(imm: inst.imm) + rs1) & |
689 | ~1); |
690 | }) |
691 | .value_or(u: false); |
692 | } |
693 | bool operator()(B inst) { |
694 | return transformOptional(O: zipOpt(ts: m_emu.ReadPC(), ts: inst.rs1.Read(emulator&: m_emu), |
695 | ts: inst.rs2.Read(emulator&: m_emu)), |
696 | F: [&](auto &&tup) { |
697 | auto [pc, rs1, rs2] = tup; |
698 | if (m_ignore_cond || |
699 | CompareB(rs1, rs2, inst.funct3)) |
700 | return m_emu.WritePC(pc: SignExt(imm: inst.imm) + pc); |
701 | return true; |
702 | }) |
703 | .value_or(u: false); |
704 | } |
705 | bool operator()(LB inst) { |
706 | return Load<LB, uint8_t, int8_t>(emulator&: m_emu, inst, extend: SextW); |
707 | } |
708 | bool operator()(LH inst) { |
709 | return Load<LH, uint16_t, int16_t>(emulator&: m_emu, inst, extend: SextW); |
710 | } |
711 | bool operator()(LW inst) { |
712 | return Load<LW, uint32_t, int32_t>(emulator&: m_emu, inst, extend: SextW); |
713 | } |
714 | bool operator()(LBU inst) { |
715 | return Load<LBU, uint8_t, uint8_t>(emulator&: m_emu, inst, extend: ZextD); |
716 | } |
717 | bool operator()(LHU inst) { |
718 | return Load<LHU, uint16_t, uint16_t>(emulator&: m_emu, inst, extend: ZextD); |
719 | } |
720 | bool operator()(SB inst) { return Store<SB, uint8_t>(emulator&: m_emu, inst); } |
721 | bool operator()(SH inst) { return Store<SH, uint16_t>(emulator&: m_emu, inst); } |
722 | bool operator()(SW inst) { return Store<SW, uint32_t>(emulator&: m_emu, inst); } |
723 | bool operator()(ADDI inst) { |
724 | return transformOptional(O: inst.rs1.ReadI64(emulator&: m_emu), |
725 | F: [&](int64_t rs1) { |
726 | return inst.rd.Write( |
727 | emulator&: m_emu, value: rs1 + int64_t(SignExt(imm: inst.imm))); |
728 | }) |
729 | .value_or(u: false); |
730 | } |
731 | bool operator()(SLTI inst) { |
732 | return transformOptional(O: inst.rs1.ReadI64(emulator&: m_emu), |
733 | F: [&](int64_t rs1) { |
734 | return inst.rd.Write( |
735 | emulator&: m_emu, value: rs1 < int64_t(SignExt(imm: inst.imm))); |
736 | }) |
737 | .value_or(u: false); |
738 | } |
739 | bool operator()(SLTIU inst) { |
740 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
741 | F: [&](uint64_t rs1) { |
742 | return inst.rd.Write( |
743 | emulator&: m_emu, value: rs1 < uint64_t(SignExt(imm: inst.imm))); |
744 | }) |
745 | .value_or(u: false); |
746 | } |
747 | bool operator()(XORI inst) { |
748 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
749 | F: [&](uint64_t rs1) { |
750 | return inst.rd.Write( |
751 | emulator&: m_emu, value: rs1 ^ uint64_t(SignExt(imm: inst.imm))); |
752 | }) |
753 | .value_or(u: false); |
754 | } |
755 | bool operator()(ORI inst) { |
756 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
757 | F: [&](uint64_t rs1) { |
758 | return inst.rd.Write( |
759 | emulator&: m_emu, value: rs1 | uint64_t(SignExt(imm: inst.imm))); |
760 | }) |
761 | .value_or(u: false); |
762 | } |
763 | bool operator()(ANDI inst) { |
764 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
765 | F: [&](uint64_t rs1) { |
766 | return inst.rd.Write( |
767 | emulator&: m_emu, value: rs1 & uint64_t(SignExt(imm: inst.imm))); |
768 | }) |
769 | .value_or(u: false); |
770 | } |
771 | bool operator()(ADD inst) { |
772 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
773 | F: [&](auto &&tup) { |
774 | auto [rs1, rs2] = tup; |
775 | return inst.rd.Write(emulator&: m_emu, value: rs1 + rs2); |
776 | }) |
777 | .value_or(u: false); |
778 | } |
779 | bool operator()(SUB inst) { |
780 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
781 | F: [&](auto &&tup) { |
782 | auto [rs1, rs2] = tup; |
783 | return inst.rd.Write(emulator&: m_emu, value: rs1 - rs2); |
784 | }) |
785 | .value_or(u: false); |
786 | } |
787 | bool operator()(SLL inst) { |
788 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
789 | F: [&](auto &&tup) { |
790 | auto [rs1, rs2] = tup; |
791 | return inst.rd.Write(emulator&: m_emu, |
792 | value: rs1 << (rs2 & 0b111111)); |
793 | }) |
794 | .value_or(u: false); |
795 | } |
796 | bool operator()(SLT inst) { |
797 | return transformOptional( |
798 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.ReadI64(emulator&: m_emu)), |
799 | F: [&](auto &&tup) { |
800 | auto [rs1, rs2] = tup; |
801 | return inst.rd.Write(emulator&: m_emu, value: rs1 < rs2); |
802 | }) |
803 | .value_or(u: false); |
804 | } |
805 | bool operator()(SLTU inst) { |
806 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
807 | F: [&](auto &&tup) { |
808 | auto [rs1, rs2] = tup; |
809 | return inst.rd.Write(emulator&: m_emu, value: rs1 < rs2); |
810 | }) |
811 | .value_or(u: false); |
812 | } |
813 | bool operator()(XOR inst) { |
814 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
815 | F: [&](auto &&tup) { |
816 | auto [rs1, rs2] = tup; |
817 | return inst.rd.Write(emulator&: m_emu, value: rs1 ^ rs2); |
818 | }) |
819 | .value_or(u: false); |
820 | } |
821 | bool operator()(SRL inst) { |
822 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
823 | F: [&](auto &&tup) { |
824 | auto [rs1, rs2] = tup; |
825 | return inst.rd.Write(emulator&: m_emu, |
826 | value: rs1 >> (rs2 & 0b111111)); |
827 | }) |
828 | .value_or(u: false); |
829 | } |
830 | bool operator()(SRA inst) { |
831 | return transformOptional( |
832 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
833 | F: [&](auto &&tup) { |
834 | auto [rs1, rs2] = tup; |
835 | return inst.rd.Write(emulator&: m_emu, value: rs1 >> (rs2 & 0b111111)); |
836 | }) |
837 | .value_or(u: false); |
838 | } |
839 | bool operator()(OR inst) { |
840 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
841 | F: [&](auto &&tup) { |
842 | auto [rs1, rs2] = tup; |
843 | return inst.rd.Write(emulator&: m_emu, value: rs1 | rs2); |
844 | }) |
845 | .value_or(u: false); |
846 | } |
847 | bool operator()(AND inst) { |
848 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
849 | F: [&](auto &&tup) { |
850 | auto [rs1, rs2] = tup; |
851 | return inst.rd.Write(emulator&: m_emu, value: rs1 & rs2); |
852 | }) |
853 | .value_or(u: false); |
854 | } |
855 | bool operator()(LWU inst) { |
856 | return Load<LWU, uint32_t, uint32_t>(emulator&: m_emu, inst, extend: ZextD); |
857 | } |
858 | bool operator()(LD inst) { |
859 | return Load<LD, uint64_t, uint64_t>(emulator&: m_emu, inst, extend: ZextD); |
860 | } |
861 | bool operator()(SD inst) { return Store<SD, uint64_t>(emulator&: m_emu, inst); } |
862 | bool operator()(SLLI inst) { |
863 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
864 | F: [&](uint64_t rs1) { |
865 | return inst.rd.Write(emulator&: m_emu, value: rs1 << inst.shamt); |
866 | }) |
867 | .value_or(u: false); |
868 | } |
869 | bool operator()(SRLI inst) { |
870 | return transformOptional(O: inst.rs1.Read(emulator&: m_emu), |
871 | F: [&](uint64_t rs1) { |
872 | return inst.rd.Write(emulator&: m_emu, value: rs1 >> inst.shamt); |
873 | }) |
874 | .value_or(u: false); |
875 | } |
876 | bool operator()(SRAI inst) { |
877 | return transformOptional(O: inst.rs1.ReadI64(emulator&: m_emu), |
878 | F: [&](int64_t rs1) { |
879 | return inst.rd.Write(emulator&: m_emu, value: rs1 >> inst.shamt); |
880 | }) |
881 | .value_or(u: false); |
882 | } |
883 | bool operator()(ADDIW inst) { |
884 | return transformOptional(O: inst.rs1.ReadI32(emulator&: m_emu), |
885 | F: [&](int32_t rs1) { |
886 | return inst.rd.Write( |
887 | emulator&: m_emu, value: SextW(value: rs1 + SignExt(imm: inst.imm))); |
888 | }) |
889 | .value_or(u: false); |
890 | } |
891 | bool operator()(SLLIW inst) { |
892 | return transformOptional(O: inst.rs1.ReadU32(emulator&: m_emu), |
893 | F: [&](uint32_t rs1) { |
894 | return inst.rd.Write(emulator&: m_emu, |
895 | value: SextW(value: rs1 << inst.shamt)); |
896 | }) |
897 | .value_or(u: false); |
898 | } |
899 | bool operator()(SRLIW inst) { |
900 | return transformOptional(O: inst.rs1.ReadU32(emulator&: m_emu), |
901 | F: [&](uint32_t rs1) { |
902 | return inst.rd.Write(emulator&: m_emu, |
903 | value: SextW(value: rs1 >> inst.shamt)); |
904 | }) |
905 | .value_or(u: false); |
906 | } |
907 | bool operator()(SRAIW inst) { |
908 | return transformOptional(O: inst.rs1.ReadI32(emulator&: m_emu), |
909 | F: [&](int32_t rs1) { |
910 | return inst.rd.Write(emulator&: m_emu, |
911 | value: SextW(value: rs1 >> inst.shamt)); |
912 | }) |
913 | .value_or(u: false); |
914 | } |
915 | bool operator()(ADDW inst) { |
916 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
917 | F: [&](auto &&tup) { |
918 | auto [rs1, rs2] = tup; |
919 | return inst.rd.Write(emulator&: m_emu, |
920 | value: SextW(value: uint32_t(rs1 + rs2))); |
921 | }) |
922 | .value_or(u: false); |
923 | } |
924 | bool operator()(SUBW inst) { |
925 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
926 | F: [&](auto &&tup) { |
927 | auto [rs1, rs2] = tup; |
928 | return inst.rd.Write(emulator&: m_emu, |
929 | value: SextW(value: uint32_t(rs1 - rs2))); |
930 | }) |
931 | .value_or(u: false); |
932 | } |
933 | bool operator()(SLLW inst) { |
934 | return transformOptional( |
935 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
936 | F: [&](auto &&tup) { |
937 | auto [rs1, rs2] = tup; |
938 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 << (rs2 & 0b11111))); |
939 | }) |
940 | .value_or(u: false); |
941 | } |
942 | bool operator()(SRLW inst) { |
943 | return transformOptional( |
944 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
945 | F: [&](auto &&tup) { |
946 | auto [rs1, rs2] = tup; |
947 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 >> (rs2 & 0b11111))); |
948 | }) |
949 | .value_or(u: false); |
950 | } |
951 | bool operator()(SRAW inst) { |
952 | return transformOptional( |
953 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
954 | F: [&](auto &&tup) { |
955 | auto [rs1, rs2] = tup; |
956 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 >> (rs2 & 0b11111))); |
957 | }) |
958 | .value_or(u: false); |
959 | } |
960 | // RV32M & RV64M (Integer Multiplication and Division Extension) // |
961 | bool operator()(MUL inst) { |
962 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
963 | F: [&](auto &&tup) { |
964 | auto [rs1, rs2] = tup; |
965 | return inst.rd.Write(emulator&: m_emu, value: rs1 * rs2); |
966 | }) |
967 | .value_or(u: false); |
968 | } |
969 | bool operator()(MULH inst) { |
970 | return transformOptional( |
971 | O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
972 | F: [&](auto &&tup) { |
973 | auto [rs1, rs2] = tup; |
974 | // signed * signed |
975 | auto mul = APInt(128, rs1, true) * APInt(128, rs2, true); |
976 | return inst.rd.Write(emulator&: m_emu, |
977 | value: mul.ashr(ShiftAmt: 64).trunc(width: 64).getZExtValue()); |
978 | }) |
979 | .value_or(u: false); |
980 | } |
981 | bool operator()(MULHSU inst) { |
982 | return transformOptional( |
983 | O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
984 | F: [&](auto &&tup) { |
985 | auto [rs1, rs2] = tup; |
986 | // signed * unsigned |
987 | auto mul = |
988 | APInt(128, rs1, true).zext(width: 128) * APInt(128, rs2, false); |
989 | return inst.rd.Write(emulator&: m_emu, |
990 | value: mul.lshr(shiftAmt: 64).trunc(width: 64).getZExtValue()); |
991 | }) |
992 | .value_or(u: false); |
993 | } |
994 | bool operator()(MULHU inst) { |
995 | return transformOptional( |
996 | O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(emulator&: m_emu)), |
997 | F: [&](auto &&tup) { |
998 | auto [rs1, rs2] = tup; |
999 | // unsigned * unsigned |
1000 | auto mul = APInt(128, rs1, false) * APInt(128, rs2, false); |
1001 | return inst.rd.Write(emulator&: m_emu, |
1002 | value: mul.lshr(shiftAmt: 64).trunc(width: 64).getZExtValue()); |
1003 | }) |
1004 | .value_or(u: false); |
1005 | } |
1006 | bool operator()(DIV inst) { |
1007 | return transformOptional( |
1008 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.ReadI64(emulator&: m_emu)), |
1009 | F: [&](auto &&tup) { |
1010 | auto [dividend, divisor] = tup; |
1011 | |
1012 | if (divisor == 0) |
1013 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
1014 | |
1015 | if (dividend == INT64_MIN && divisor == -1) |
1016 | return inst.rd.Write(emulator&: m_emu, value: dividend); |
1017 | |
1018 | return inst.rd.Write(emulator&: m_emu, value: dividend / divisor); |
1019 | }) |
1020 | .value_or(u: false); |
1021 | } |
1022 | bool operator()(DIVU inst) { |
1023 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(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 | return inst.rd.Write(emulator&: m_emu, value: dividend / divisor); |
1031 | }) |
1032 | .value_or(u: false); |
1033 | } |
1034 | bool operator()(REM inst) { |
1035 | return transformOptional( |
1036 | O: zipOpt(ts: inst.rs1.ReadI64(emulator&: m_emu), ts: inst.rs2.ReadI64(emulator&: m_emu)), |
1037 | F: [&](auto &&tup) { |
1038 | auto [dividend, divisor] = tup; |
1039 | |
1040 | if (divisor == 0) |
1041 | return inst.rd.Write(emulator&: m_emu, value: dividend); |
1042 | |
1043 | if (dividend == INT64_MIN && divisor == -1) |
1044 | return inst.rd.Write(emulator&: m_emu, value: 0); |
1045 | |
1046 | return inst.rd.Write(emulator&: m_emu, value: dividend % divisor); |
1047 | }) |
1048 | .value_or(u: false); |
1049 | } |
1050 | bool operator()(REMU inst) { |
1051 | return transformOptional(O: zipOpt(ts: inst.rs1.Read(emulator&: m_emu), ts: inst.rs2.Read(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 | return inst.rd.Write(emulator&: m_emu, value: dividend % divisor); |
1059 | }) |
1060 | .value_or(u: false); |
1061 | } |
1062 | bool operator()(MULW inst) { |
1063 | return transformOptional( |
1064 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.ReadI32(emulator&: m_emu)), |
1065 | F: [&](auto &&tup) { |
1066 | auto [rs1, rs2] = tup; |
1067 | return inst.rd.Write(emulator&: m_emu, value: SextW(rs1 * rs2)); |
1068 | }) |
1069 | .value_or(u: false); |
1070 | } |
1071 | bool operator()(DIVW inst) { |
1072 | return transformOptional( |
1073 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.ReadI32(emulator&: m_emu)), |
1074 | F: [&](auto &&tup) { |
1075 | auto [dividend, divisor] = tup; |
1076 | |
1077 | if (divisor == 0) |
1078 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
1079 | |
1080 | if (dividend == INT32_MIN && divisor == -1) |
1081 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend)); |
1082 | |
1083 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend / divisor)); |
1084 | }) |
1085 | .value_or(u: false); |
1086 | } |
1087 | bool operator()(DIVUW inst) { |
1088 | return transformOptional( |
1089 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
1090 | F: [&](auto &&tup) { |
1091 | auto [dividend, divisor] = tup; |
1092 | |
1093 | if (divisor == 0) |
1094 | return inst.rd.Write(emulator&: m_emu, UINT64_MAX); |
1095 | |
1096 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend / divisor)); |
1097 | }) |
1098 | .value_or(u: false); |
1099 | } |
1100 | bool operator()(REMW inst) { |
1101 | return transformOptional( |
1102 | O: zipOpt(ts: inst.rs1.ReadI32(emulator&: m_emu), ts: inst.rs2.ReadI32(emulator&: m_emu)), |
1103 | F: [&](auto &&tup) { |
1104 | auto [dividend, divisor] = tup; |
1105 | |
1106 | if (divisor == 0) |
1107 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend)); |
1108 | |
1109 | if (dividend == INT32_MIN && divisor == -1) |
1110 | return inst.rd.Write(emulator&: m_emu, value: 0); |
1111 | |
1112 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend % divisor)); |
1113 | }) |
1114 | .value_or(u: false); |
1115 | } |
1116 | bool operator()(REMUW inst) { |
1117 | return transformOptional( |
1118 | O: zipOpt(ts: inst.rs1.ReadU32(emulator&: m_emu), ts: inst.rs2.ReadU32(emulator&: m_emu)), |
1119 | F: [&](auto &&tup) { |
1120 | auto [dividend, divisor] = tup; |
1121 | |
1122 | if (divisor == 0) |
1123 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend)); |
1124 | |
1125 | return inst.rd.Write(emulator&: m_emu, value: SextW(dividend % divisor)); |
1126 | }) |
1127 | .value_or(u: false); |
1128 | } |
1129 | // RV32A & RV64A (The standard atomic instruction extension) // |
1130 | bool operator()(LR_W) { return AtomicSequence(emulator&: m_emu); } |
1131 | bool operator()(LR_D) { return AtomicSequence(emulator&: m_emu); } |
1132 | bool operator()(SC_W) { |
1133 | llvm_unreachable("should be handled in AtomicSequence" ); |
1134 | } |
1135 | bool operator()(SC_D) { |
1136 | llvm_unreachable("should be handled in AtomicSequence" ); |
1137 | } |
1138 | bool operator()(AMOSWAP_W inst) { |
1139 | return AtomicSwap<AMOSWAP_W, uint32_t>(emulator&: m_emu, inst, align: 4, extend: SextW); |
1140 | } |
1141 | bool operator()(AMOADD_W inst) { |
1142 | return AtomicADD<AMOADD_W, uint32_t>(emulator&: m_emu, inst, align: 4, extend: SextW); |
1143 | } |
1144 | bool operator()(AMOXOR_W inst) { |
1145 | return AtomicBitOperate<AMOXOR_W, uint32_t>( |
1146 | emulator&: m_emu, inst, align: 4, extend: SextW, operate: [](uint32_t a, uint32_t b) { return a ^ b; }); |
1147 | } |
1148 | bool operator()(AMOAND_W inst) { |
1149 | return AtomicBitOperate<AMOAND_W, uint32_t>( |
1150 | emulator&: m_emu, inst, align: 4, extend: SextW, operate: [](uint32_t a, uint32_t b) { return a & b; }); |
1151 | } |
1152 | bool operator()(AMOOR_W inst) { |
1153 | return AtomicBitOperate<AMOOR_W, uint32_t>( |
1154 | emulator&: m_emu, inst, align: 4, extend: SextW, operate: [](uint32_t a, uint32_t b) { return a | b; }); |
1155 | } |
1156 | bool operator()(AMOMIN_W inst) { |
1157 | return AtomicCmp<AMOMIN_W, uint32_t>( |
1158 | emulator&: m_emu, inst, align: 4, extend: SextW, cmp: [](uint32_t a, uint32_t b) { |
1159 | return uint32_t(std::min(a: int32_t(a), b: int32_t(b))); |
1160 | }); |
1161 | } |
1162 | bool operator()(AMOMAX_W inst) { |
1163 | return AtomicCmp<AMOMAX_W, uint32_t>( |
1164 | emulator&: m_emu, inst, align: 4, extend: SextW, cmp: [](uint32_t a, uint32_t b) { |
1165 | return uint32_t(std::max(a: int32_t(a), b: int32_t(b))); |
1166 | }); |
1167 | } |
1168 | bool operator()(AMOMINU_W inst) { |
1169 | return AtomicCmp<AMOMINU_W, uint32_t>( |
1170 | emulator&: m_emu, inst, align: 4, extend: SextW, |
1171 | cmp: [](uint32_t a, uint32_t b) { return std::min(a: a, b: b); }); |
1172 | } |
1173 | bool operator()(AMOMAXU_W inst) { |
1174 | return AtomicCmp<AMOMAXU_W, uint32_t>( |
1175 | emulator&: m_emu, inst, align: 4, extend: SextW, |
1176 | cmp: [](uint32_t a, uint32_t b) { return std::max(a: a, b: b); }); |
1177 | } |
1178 | bool operator()(AMOSWAP_D inst) { |
1179 | return AtomicSwap<AMOSWAP_D, uint64_t>(emulator&: m_emu, inst, align: 8, extend: ZextD); |
1180 | } |
1181 | bool operator()(AMOADD_D inst) { |
1182 | return AtomicADD<AMOADD_D, uint64_t>(emulator&: m_emu, inst, align: 8, extend: ZextD); |
1183 | } |
1184 | bool operator()(AMOXOR_D inst) { |
1185 | return AtomicBitOperate<AMOXOR_D, uint64_t>( |
1186 | emulator&: m_emu, inst, align: 8, extend: ZextD, operate: [](uint64_t a, uint64_t b) { return a ^ b; }); |
1187 | } |
1188 | bool operator()(AMOAND_D inst) { |
1189 | return AtomicBitOperate<AMOAND_D, uint64_t>( |
1190 | emulator&: m_emu, inst, align: 8, extend: ZextD, operate: [](uint64_t a, uint64_t b) { return a & b; }); |
1191 | } |
1192 | bool operator()(AMOOR_D inst) { |
1193 | return AtomicBitOperate<AMOOR_D, uint64_t>( |
1194 | emulator&: m_emu, inst, align: 8, extend: ZextD, operate: [](uint64_t a, uint64_t b) { return a | b; }); |
1195 | } |
1196 | bool operator()(AMOMIN_D inst) { |
1197 | return AtomicCmp<AMOMIN_D, uint64_t>( |
1198 | emulator&: m_emu, inst, align: 8, extend: ZextD, cmp: [](uint64_t a, uint64_t b) { |
1199 | return uint64_t(std::min(a: int64_t(a), b: int64_t(b))); |
1200 | }); |
1201 | } |
1202 | bool operator()(AMOMAX_D inst) { |
1203 | return AtomicCmp<AMOMAX_D, uint64_t>( |
1204 | emulator&: m_emu, inst, align: 8, extend: ZextD, cmp: [](uint64_t a, uint64_t b) { |
1205 | return uint64_t(std::max(a: int64_t(a), b: int64_t(b))); |
1206 | }); |
1207 | } |
1208 | bool operator()(AMOMINU_D inst) { |
1209 | return AtomicCmp<AMOMINU_D, uint64_t>( |
1210 | emulator&: m_emu, inst, align: 8, extend: ZextD, |
1211 | cmp: [](uint64_t a, uint64_t b) { return std::min(a: a, b: b); }); |
1212 | } |
1213 | bool operator()(AMOMAXU_D inst) { |
1214 | return AtomicCmp<AMOMAXU_D, uint64_t>( |
1215 | emulator&: m_emu, inst, align: 8, extend: ZextD, |
1216 | cmp: [](uint64_t a, uint64_t b) { return std::max(a: a, b: b); }); |
1217 | } |
1218 | template <typename T> |
1219 | bool F_Load(T inst, const fltSemantics &(*semantics)(), |
1220 | unsigned int numBits) { |
1221 | return transformOptional(inst.rs1.Read(m_emu), |
1222 | [&](auto &&rs1) { |
1223 | uint64_t addr = rs1 + uint64_t(inst.imm); |
1224 | uint64_t bits = *m_emu.ReadMem<uint64_t>(addr); |
1225 | APFloat f(semantics(), APInt(numBits, bits)); |
1226 | return inst.rd.WriteAPFloat(m_emu, f); |
1227 | }) |
1228 | .value_or(false); |
1229 | } |
1230 | bool operator()(FLW inst) { return F_Load(inst, semantics: &APFloat::IEEEsingle, numBits: 32); } |
1231 | template <typename T> bool F_Store(T inst, bool isDouble) { |
1232 | return transformOptional(zipOpt(inst.rs1.Read(m_emu), |
1233 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
1234 | [&](auto &&tup) { |
1235 | auto [rs1, rs2] = tup; |
1236 | uint64_t addr = rs1 + uint64_t(inst.imm); |
1237 | uint64_t bits = |
1238 | rs2.bitcastToAPInt().getZExtValue(); |
1239 | return m_emu.WriteMem<uint64_t>(addr, value: bits); |
1240 | }) |
1241 | .value_or(false); |
1242 | } |
1243 | bool operator()(FSW inst) { return F_Store(inst, isDouble: false); } |
1244 | std::tuple<bool, APFloat> FusedMultiplyAdd(APFloat rs1, APFloat rs2, |
1245 | APFloat rs3) { |
1246 | auto opStatus = rs1.fusedMultiplyAdd(Multiplicand: rs2, Addend: rs3, RM: m_emu.GetRoundingMode()); |
1247 | auto res = m_emu.SetAccruedExceptions(opStatus); |
1248 | return {res, rs1}; |
1249 | } |
1250 | template <typename T> |
1251 | bool FMA(T inst, bool isDouble, float rs2_sign, float rs3_sign) { |
1252 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1253 | inst.rs2.ReadAPFloat(m_emu, isDouble), |
1254 | inst.rs3.ReadAPFloat(m_emu, isDouble)), |
1255 | [&](auto &&tup) { |
1256 | auto [rs1, rs2, rs3] = tup; |
1257 | rs2.copySign(APFloat(rs2_sign)); |
1258 | rs3.copySign(APFloat(rs3_sign)); |
1259 | auto [res, f] = FusedMultiplyAdd(rs1, rs2, rs3); |
1260 | return res && inst.rd.WriteAPFloat(m_emu, f); |
1261 | }) |
1262 | .value_or(false); |
1263 | } |
1264 | bool operator()(FMADD_S inst) { return FMA(inst, isDouble: false, rs2_sign: 1.0f, rs3_sign: 1.0f); } |
1265 | bool operator()(FMSUB_S inst) { return FMA(inst, isDouble: false, rs2_sign: 1.0f, rs3_sign: -1.0f); } |
1266 | bool operator()(FNMSUB_S inst) { return FMA(inst, isDouble: false, rs2_sign: -1.0f, rs3_sign: 1.0f); } |
1267 | bool operator()(FNMADD_S inst) { return FMA(inst, isDouble: false, rs2_sign: -1.0f, rs3_sign: -1.0f); } |
1268 | template <typename T> |
1269 | bool F_Op(T inst, bool isDouble, |
1270 | APFloat::opStatus (APFloat::*f)(const APFloat &RHS, |
1271 | APFloat::roundingMode RM)) { |
1272 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1273 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
1274 | [&](auto &&tup) { |
1275 | auto [rs1, rs2] = tup; |
1276 | auto res = |
1277 | ((&rs1)->*f)(rs2, m_emu.GetRoundingMode()); |
1278 | inst.rd.WriteAPFloat(m_emu, rs1); |
1279 | return m_emu.SetAccruedExceptions(res); |
1280 | }) |
1281 | .value_or(false); |
1282 | } |
1283 | bool operator()(FADD_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::add); } |
1284 | bool operator()(FSUB_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::subtract); } |
1285 | bool operator()(FMUL_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::multiply); } |
1286 | bool operator()(FDIV_S inst) { return F_Op(inst, isDouble: false, f: &APFloat::divide); } |
1287 | bool operator()(FSQRT_S inst) { |
1288 | // TODO: APFloat doesn't have a sqrt function. |
1289 | return false; |
1290 | } |
1291 | template <typename T> bool F_SignInj(T inst, bool isDouble, bool isNegate) { |
1292 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1293 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
1294 | [&](auto &&tup) { |
1295 | auto [rs1, rs2] = tup; |
1296 | if (isNegate) |
1297 | rs2.changeSign(); |
1298 | rs1.copySign(rs2); |
1299 | return inst.rd.WriteAPFloat(m_emu, rs1); |
1300 | }) |
1301 | .value_or(false); |
1302 | } |
1303 | bool operator()(FSGNJ_S inst) { return F_SignInj(inst, isDouble: false, isNegate: false); } |
1304 | bool operator()(FSGNJN_S inst) { return F_SignInj(inst, isDouble: false, isNegate: true); } |
1305 | template <typename T> bool F_SignInjXor(T inst, bool isDouble) { |
1306 | return transformOptional(zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1307 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
1308 | [&](auto &&tup) { |
1309 | auto [rs1, rs2] = tup; |
1310 | // spec: the sign bit is the XOR of the sign bits |
1311 | // of rs1 and rs2. if rs1 and rs2 have the same |
1312 | // signs set rs1 to positive else set rs1 to |
1313 | // negative |
1314 | if (rs1.isNegative() == rs2.isNegative()) { |
1315 | rs1.clearSign(); |
1316 | } else { |
1317 | rs1.clearSign(); |
1318 | rs1.changeSign(); |
1319 | } |
1320 | return inst.rd.WriteAPFloat(m_emu, rs1); |
1321 | }) |
1322 | .value_or(false); |
1323 | } |
1324 | bool operator()(FSGNJX_S inst) { return F_SignInjXor(inst, isDouble: false); } |
1325 | template <typename T> |
1326 | bool F_MAX_MIN(T inst, bool isDouble, |
1327 | APFloat (*f)(const APFloat &A, const APFloat &B)) { |
1328 | return transformOptional( |
1329 | zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1330 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
1331 | [&](auto &&tup) { |
1332 | auto [rs1, rs2] = tup; |
1333 | // If both inputs are NaNs, the result is the canonical NaN. |
1334 | // If only one operand is a NaN, the result is the non-NaN |
1335 | // operand. Signaling NaN inputs set the invalid operation |
1336 | // exception flag, even when the result is not NaN. |
1337 | if (rs1.isNaN() || rs2.isNaN()) |
1338 | m_emu.SetAccruedExceptions(APFloat::opInvalidOp); |
1339 | if (rs1.isNaN() && rs2.isNaN()) { |
1340 | auto canonicalNaN = APFloat::getQNaN(Sem: rs1.getSemantics()); |
1341 | return inst.rd.WriteAPFloat(m_emu, canonicalNaN); |
1342 | } |
1343 | return inst.rd.WriteAPFloat(m_emu, f(rs1, rs2)); |
1344 | }) |
1345 | .value_or(false); |
1346 | } |
1347 | bool operator()(FMIN_S inst) { return F_MAX_MIN(inst, isDouble: false, f: minnum); } |
1348 | bool operator()(FMAX_S inst) { return F_MAX_MIN(inst, isDouble: false, f: maxnum); } |
1349 | bool operator()(FCVT_W_S inst) { |
1350 | return FCVT_i2f<FCVT_W_S, int32_t, float>(inst, isDouble: false, |
1351 | f: &APFloat::convertToFloat); |
1352 | } |
1353 | bool operator()(FCVT_WU_S inst) { |
1354 | return FCVT_i2f<FCVT_WU_S, uint32_t, float>(inst, isDouble: false, |
1355 | f: &APFloat::convertToFloat); |
1356 | } |
1357 | template <typename T> bool FMV_f2i(T inst, bool isDouble) { |
1358 | return transformOptional( |
1359 | inst.rs1.ReadAPFloat(m_emu, isDouble), |
1360 | [&](auto &&rs1) { |
1361 | if (rs1.isNaN()) { |
1362 | if (isDouble) |
1363 | return inst.rd.Write(m_emu, 0x7ff8'0000'0000'0000); |
1364 | else |
1365 | return inst.rd.Write(m_emu, 0x7fc0'0000); |
1366 | } |
1367 | auto bits = rs1.bitcastToAPInt().getZExtValue(); |
1368 | if (isDouble) |
1369 | return inst.rd.Write(m_emu, bits); |
1370 | else |
1371 | return inst.rd.Write(m_emu, uint64_t(bits & 0xffff'ffff)); |
1372 | }) |
1373 | .value_or(false); |
1374 | } |
1375 | bool operator()(FMV_X_W inst) { return FMV_f2i(inst, isDouble: false); } |
1376 | enum F_CMP { |
1377 | FEQ, |
1378 | FLT, |
1379 | FLE, |
1380 | }; |
1381 | template <typename T> bool F_Compare(T inst, bool isDouble, F_CMP cmp) { |
1382 | return transformOptional( |
1383 | zipOpt(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1384 | inst.rs2.ReadAPFloat(m_emu, isDouble)), |
1385 | [&](auto &&tup) { |
1386 | auto [rs1, rs2] = tup; |
1387 | if (rs1.isNaN() || rs2.isNaN()) { |
1388 | if (cmp == FEQ) { |
1389 | if (rs1.isSignaling() || rs2.isSignaling()) { |
1390 | auto res = |
1391 | m_emu.SetAccruedExceptions(APFloat::opInvalidOp); |
1392 | return res && inst.rd.Write(m_emu, 0); |
1393 | } |
1394 | } |
1395 | auto res = m_emu.SetAccruedExceptions(APFloat::opInvalidOp); |
1396 | return res && inst.rd.Write(m_emu, 0); |
1397 | } |
1398 | switch (cmp) { |
1399 | case FEQ: |
1400 | return inst.rd.Write(m_emu, |
1401 | rs1.compare(rs2) == APFloat::cmpEqual); |
1402 | case FLT: |
1403 | return inst.rd.Write(m_emu, rs1.compare(rs2) == |
1404 | APFloat::cmpLessThan); |
1405 | case FLE: |
1406 | return inst.rd.Write(m_emu, rs1.compare(rs2) != |
1407 | APFloat::cmpGreaterThan); |
1408 | } |
1409 | llvm_unreachable("unsupported F_CMP" ); |
1410 | }) |
1411 | .value_or(false); |
1412 | } |
1413 | |
1414 | bool operator()(FEQ_S inst) { return F_Compare(inst, isDouble: false, cmp: FEQ); } |
1415 | bool operator()(FLT_S inst) { return F_Compare(inst, isDouble: false, cmp: FLT); } |
1416 | bool operator()(FLE_S inst) { return F_Compare(inst, isDouble: false, cmp: FLE); } |
1417 | template <typename T> bool FCLASS(T inst, bool isDouble) { |
1418 | return transformOptional(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1419 | [&](auto &&rs1) { |
1420 | uint64_t result = 0; |
1421 | if (rs1.isInfinity() && rs1.isNegative()) |
1422 | result |= 1 << 0; |
1423 | // neg normal |
1424 | if (rs1.isNormal() && rs1.isNegative()) |
1425 | result |= 1 << 1; |
1426 | // neg subnormal |
1427 | if (rs1.isDenormal() && rs1.isNegative()) |
1428 | result |= 1 << 2; |
1429 | if (rs1.isNegZero()) |
1430 | result |= 1 << 3; |
1431 | if (rs1.isPosZero()) |
1432 | result |= 1 << 4; |
1433 | // pos normal |
1434 | if (rs1.isNormal() && !rs1.isNegative()) |
1435 | result |= 1 << 5; |
1436 | // pos subnormal |
1437 | if (rs1.isDenormal() && !rs1.isNegative()) |
1438 | result |= 1 << 6; |
1439 | if (rs1.isInfinity() && !rs1.isNegative()) |
1440 | result |= 1 << 7; |
1441 | if (rs1.isNaN()) { |
1442 | if (rs1.isSignaling()) |
1443 | result |= 1 << 8; |
1444 | else |
1445 | result |= 1 << 9; |
1446 | } |
1447 | return inst.rd.Write(m_emu, result); |
1448 | }) |
1449 | .value_or(false); |
1450 | } |
1451 | bool operator()(FCLASS_S inst) { return FCLASS(inst, isDouble: false); } |
1452 | template <typename T, typename E> |
1453 | bool FCVT_f2i(T inst, std::optional<E> (Rs::*f)(EmulateInstructionRISCV &emu), |
1454 | const fltSemantics &semantics) { |
1455 | return transformOptional(((&inst.rs1)->*f)(m_emu), |
1456 | [&](auto &&rs1) { |
1457 | APFloat apf(semantics, rs1); |
1458 | return inst.rd.WriteAPFloat(m_emu, apf); |
1459 | }) |
1460 | .value_or(false); |
1461 | } |
1462 | bool operator()(FCVT_S_W inst) { |
1463 | return FCVT_f2i(inst, f: &Rs::ReadI32, semantics: APFloat::IEEEsingle()); |
1464 | } |
1465 | bool operator()(FCVT_S_WU inst) { |
1466 | return FCVT_f2i(inst, f: &Rs::ReadU32, semantics: APFloat::IEEEsingle()); |
1467 | } |
1468 | template <typename T, typename E> |
1469 | bool FMV_i2f(T inst, unsigned int numBits, E (APInt::*f)() const) { |
1470 | return transformOptional(inst.rs1.Read(m_emu), |
1471 | [&](auto &&rs1) { |
1472 | APInt apInt(numBits, rs1); |
1473 | if (numBits == 32) // a.k.a. float |
1474 | apInt = APInt(numBits, NanUnBoxing(rs1)); |
1475 | APFloat apf((&apInt->*f)()); |
1476 | return inst.rd.WriteAPFloat(m_emu, apf); |
1477 | }) |
1478 | .value_or(false); |
1479 | } |
1480 | bool operator()(FMV_W_X inst) { |
1481 | return FMV_i2f(inst, numBits: 32, f: &APInt::bitsToFloat); |
1482 | } |
1483 | template <typename I, typename E, typename T> |
1484 | bool FCVT_i2f(I inst, bool isDouble, T (APFloat::*f)() const) { |
1485 | return transformOptional(inst.rs1.ReadAPFloat(m_emu, isDouble), |
1486 | [&](auto &&rs1) { |
1487 | E res = E((&rs1->*f)()); |
1488 | return inst.rd.Write(m_emu, uint64_t(res)); |
1489 | }) |
1490 | .value_or(false); |
1491 | } |
1492 | bool operator()(FCVT_L_S inst) { |
1493 | return FCVT_i2f<FCVT_L_S, int64_t, float>(inst, isDouble: false, |
1494 | f: &APFloat::convertToFloat); |
1495 | } |
1496 | bool operator()(FCVT_LU_S inst) { |
1497 | return FCVT_i2f<FCVT_LU_S, uint64_t, float>(inst, isDouble: false, |
1498 | f: &APFloat::convertToFloat); |
1499 | } |
1500 | bool operator()(FCVT_S_L inst) { |
1501 | return FCVT_f2i(inst, f: &Rs::ReadI64, semantics: APFloat::IEEEsingle()); |
1502 | } |
1503 | bool operator()(FCVT_S_LU inst) { |
1504 | return FCVT_f2i(inst, f: &Rs::Read, semantics: APFloat::IEEEsingle()); |
1505 | } |
1506 | bool operator()(FLD inst) { return F_Load(inst, semantics: &APFloat::IEEEdouble, numBits: 64); } |
1507 | bool operator()(FSD inst) { return F_Store(inst, isDouble: true); } |
1508 | bool operator()(FMADD_D inst) { return FMA(inst, isDouble: true, rs2_sign: 1.0f, rs3_sign: 1.0f); } |
1509 | bool operator()(FMSUB_D inst) { return FMA(inst, isDouble: true, rs2_sign: 1.0f, rs3_sign: -1.0f); } |
1510 | bool operator()(FNMSUB_D inst) { return FMA(inst, isDouble: true, rs2_sign: -1.0f, rs3_sign: 1.0f); } |
1511 | bool operator()(FNMADD_D inst) { return FMA(inst, isDouble: true, rs2_sign: -1.0f, rs3_sign: -1.0f); } |
1512 | bool operator()(FADD_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::add); } |
1513 | bool operator()(FSUB_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::subtract); } |
1514 | bool operator()(FMUL_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::multiply); } |
1515 | bool operator()(FDIV_D inst) { return F_Op(inst, isDouble: true, f: &APFloat::divide); } |
1516 | bool operator()(FSQRT_D inst) { |
1517 | // TODO: APFloat doesn't have a sqrt function. |
1518 | return false; |
1519 | } |
1520 | bool operator()(FSGNJ_D inst) { return F_SignInj(inst, isDouble: true, isNegate: false); } |
1521 | bool operator()(FSGNJN_D inst) { return F_SignInj(inst, isDouble: true, isNegate: true); } |
1522 | bool operator()(FSGNJX_D inst) { return F_SignInjXor(inst, isDouble: true); } |
1523 | bool operator()(FMIN_D inst) { return F_MAX_MIN(inst, isDouble: true, f: minnum); } |
1524 | bool operator()(FMAX_D inst) { return F_MAX_MIN(inst, isDouble: true, f: maxnum); } |
1525 | bool operator()(FCVT_S_D inst) { |
1526 | return transformOptional(O: inst.rs1.ReadAPFloat(emulator&: m_emu, isDouble: true), |
1527 | F: [&](auto &&rs1) { |
1528 | double d = rs1.convertToDouble(); |
1529 | APFloat apf((float(d))); |
1530 | return inst.rd.WriteAPFloat(emulator&: m_emu, value: apf); |
1531 | }) |
1532 | .value_or(u: false); |
1533 | } |
1534 | bool operator()(FCVT_D_S inst) { |
1535 | return transformOptional(O: inst.rs1.ReadAPFloat(emulator&: m_emu, isDouble: false), |
1536 | F: [&](auto &&rs1) { |
1537 | float f = rs1.convertToFloat(); |
1538 | APFloat apf((double(f))); |
1539 | return inst.rd.WriteAPFloat(emulator&: m_emu, value: apf); |
1540 | }) |
1541 | .value_or(u: false); |
1542 | } |
1543 | bool operator()(FEQ_D inst) { return F_Compare(inst, isDouble: true, cmp: FEQ); } |
1544 | bool operator()(FLT_D inst) { return F_Compare(inst, isDouble: true, cmp: FLT); } |
1545 | bool operator()(FLE_D inst) { return F_Compare(inst, isDouble: true, cmp: FLE); } |
1546 | bool operator()(FCLASS_D inst) { return FCLASS(inst, isDouble: true); } |
1547 | bool operator()(FCVT_W_D inst) { |
1548 | return FCVT_i2f<FCVT_W_D, int32_t, double>(inst, isDouble: true, |
1549 | f: &APFloat::convertToDouble); |
1550 | } |
1551 | bool operator()(FCVT_WU_D inst) { |
1552 | return FCVT_i2f<FCVT_WU_D, uint32_t, double>(inst, isDouble: true, |
1553 | f: &APFloat::convertToDouble); |
1554 | } |
1555 | bool operator()(FCVT_D_W inst) { |
1556 | return FCVT_f2i(inst, f: &Rs::ReadI32, semantics: APFloat::IEEEdouble()); |
1557 | } |
1558 | bool operator()(FCVT_D_WU inst) { |
1559 | return FCVT_f2i(inst, f: &Rs::ReadU32, semantics: APFloat::IEEEdouble()); |
1560 | } |
1561 | bool operator()(FCVT_L_D inst) { |
1562 | return FCVT_i2f<FCVT_L_D, int64_t, double>(inst, isDouble: true, |
1563 | f: &APFloat::convertToDouble); |
1564 | } |
1565 | bool operator()(FCVT_LU_D inst) { |
1566 | return FCVT_i2f<FCVT_LU_D, uint64_t, double>(inst, isDouble: true, |
1567 | f: &APFloat::convertToDouble); |
1568 | } |
1569 | bool operator()(FMV_X_D inst) { return FMV_f2i(inst, isDouble: true); } |
1570 | bool operator()(FCVT_D_L inst) { |
1571 | return FCVT_f2i(inst, f: &Rs::ReadI64, semantics: APFloat::IEEEdouble()); |
1572 | } |
1573 | bool operator()(FCVT_D_LU inst) { |
1574 | return FCVT_f2i(inst, f: &Rs::Read, semantics: APFloat::IEEEdouble()); |
1575 | } |
1576 | bool operator()(FMV_D_X inst) { |
1577 | return FMV_i2f(inst, numBits: 64, f: &APInt::bitsToDouble); |
1578 | } |
1579 | bool operator()(INVALID inst) { return false; } |
1580 | bool operator()(RESERVED inst) { return false; } |
1581 | bool operator()(EBREAK inst) { return false; } |
1582 | bool operator()(HINT inst) { return true; } |
1583 | bool operator()(NOP inst) { return true; } |
1584 | }; |
1585 | |
1586 | bool EmulateInstructionRISCV::Execute(DecodeResult inst, bool ignore_cond) { |
1587 | return std::visit(visitor: Executor(*this, ignore_cond, inst.is_rvc), variants&: inst.decoded); |
1588 | } |
1589 | |
1590 | bool EmulateInstructionRISCV::EvaluateInstruction(uint32_t options) { |
1591 | bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC; |
1592 | bool ignore_cond = options & eEmulateInstructionOptionIgnoreConditions; |
1593 | |
1594 | if (!increase_pc) |
1595 | return Execute(inst: m_decoded, ignore_cond); |
1596 | |
1597 | auto old_pc = ReadPC(); |
1598 | if (!old_pc) |
1599 | return false; |
1600 | |
1601 | bool success = Execute(inst: m_decoded, ignore_cond); |
1602 | if (!success) |
1603 | return false; |
1604 | |
1605 | auto new_pc = ReadPC(); |
1606 | if (!new_pc) |
1607 | return false; |
1608 | |
1609 | // If the pc is not updated during execution, we do it here. |
1610 | return new_pc != old_pc || |
1611 | WritePC(pc: *old_pc + Executor::size(is_rvc: m_decoded.is_rvc)); |
1612 | } |
1613 | |
1614 | std::optional<DecodeResult> |
1615 | EmulateInstructionRISCV::ReadInstructionAt(addr_t addr) { |
1616 | return transformOptional(O: ReadMem<uint32_t>(addr), |
1617 | F: [&](uint32_t inst) { return Decode(inst); }) |
1618 | .value_or(u: std::nullopt); |
1619 | } |
1620 | |
1621 | bool EmulateInstructionRISCV::ReadInstruction() { |
1622 | auto addr = ReadPC(); |
1623 | m_addr = addr.value_or(LLDB_INVALID_ADDRESS); |
1624 | if (!addr) |
1625 | return false; |
1626 | auto inst = ReadInstructionAt(addr: *addr); |
1627 | if (!inst) |
1628 | return false; |
1629 | m_decoded = *inst; |
1630 | if (inst->is_rvc) |
1631 | m_opcode.SetOpcode16(inst: inst->inst, order: GetByteOrder()); |
1632 | else |
1633 | m_opcode.SetOpcode32(inst: inst->inst, order: GetByteOrder()); |
1634 | return true; |
1635 | } |
1636 | |
1637 | std::optional<addr_t> EmulateInstructionRISCV::ReadPC() { |
1638 | bool success = false; |
1639 | auto addr = ReadRegisterUnsigned(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, |
1640 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
1641 | return success ? std::optional<addr_t>(addr) : std::nullopt; |
1642 | } |
1643 | |
1644 | bool EmulateInstructionRISCV::WritePC(addr_t pc) { |
1645 | EmulateInstruction::Context ctx; |
1646 | ctx.type = eContextAdvancePC; |
1647 | ctx.SetNoArgs(); |
1648 | return WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindGeneric, |
1649 | LLDB_REGNUM_GENERIC_PC, reg_value: pc); |
1650 | } |
1651 | |
1652 | RoundingMode EmulateInstructionRISCV::GetRoundingMode() { |
1653 | bool success = false; |
1654 | auto fcsr = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: fpr_fcsr_riscv, |
1655 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
1656 | if (!success) |
1657 | return RoundingMode::Invalid; |
1658 | auto frm = (fcsr >> 5) & 0x7; |
1659 | switch (frm) { |
1660 | case 0b000: |
1661 | return RoundingMode::NearestTiesToEven; |
1662 | case 0b001: |
1663 | return RoundingMode::TowardZero; |
1664 | case 0b010: |
1665 | return RoundingMode::TowardNegative; |
1666 | case 0b011: |
1667 | return RoundingMode::TowardPositive; |
1668 | case 0b111: |
1669 | return RoundingMode::Dynamic; |
1670 | default: |
1671 | // Reserved for future use. |
1672 | return RoundingMode::Invalid; |
1673 | } |
1674 | } |
1675 | |
1676 | bool EmulateInstructionRISCV::SetAccruedExceptions( |
1677 | APFloatBase::opStatus opStatus) { |
1678 | bool success = false; |
1679 | auto fcsr = ReadRegisterUnsigned(reg_kind: eRegisterKindLLDB, reg_num: fpr_fcsr_riscv, |
1680 | LLDB_INVALID_ADDRESS, success_ptr: &success); |
1681 | if (!success) |
1682 | return false; |
1683 | switch (opStatus) { |
1684 | case APFloatBase::opInvalidOp: |
1685 | fcsr |= 1 << 4; |
1686 | break; |
1687 | case APFloatBase::opDivByZero: |
1688 | fcsr |= 1 << 3; |
1689 | break; |
1690 | case APFloatBase::opOverflow: |
1691 | fcsr |= 1 << 2; |
1692 | break; |
1693 | case APFloatBase::opUnderflow: |
1694 | fcsr |= 1 << 1; |
1695 | break; |
1696 | case APFloatBase::opInexact: |
1697 | fcsr |= 1 << 0; |
1698 | break; |
1699 | case APFloatBase::opOK: |
1700 | break; |
1701 | } |
1702 | EmulateInstruction::Context ctx; |
1703 | ctx.type = eContextRegisterStore; |
1704 | ctx.SetNoArgs(); |
1705 | return WriteRegisterUnsigned(context: ctx, reg_kind: eRegisterKindLLDB, reg_num: fpr_fcsr_riscv, reg_value: fcsr); |
1706 | } |
1707 | |
1708 | std::optional<RegisterInfo> |
1709 | EmulateInstructionRISCV::GetRegisterInfo(RegisterKind reg_kind, |
1710 | uint32_t reg_index) { |
1711 | if (reg_kind == eRegisterKindGeneric) { |
1712 | switch (reg_index) { |
1713 | case LLDB_REGNUM_GENERIC_PC: |
1714 | reg_kind = eRegisterKindLLDB; |
1715 | reg_index = gpr_pc_riscv; |
1716 | break; |
1717 | case LLDB_REGNUM_GENERIC_SP: |
1718 | reg_kind = eRegisterKindLLDB; |
1719 | reg_index = gpr_sp_riscv; |
1720 | break; |
1721 | case LLDB_REGNUM_GENERIC_FP: |
1722 | reg_kind = eRegisterKindLLDB; |
1723 | reg_index = gpr_fp_riscv; |
1724 | break; |
1725 | case LLDB_REGNUM_GENERIC_RA: |
1726 | reg_kind = eRegisterKindLLDB; |
1727 | reg_index = gpr_ra_riscv; |
1728 | break; |
1729 | // We may handle LLDB_REGNUM_GENERIC_ARGx when more instructions are |
1730 | // supported. |
1731 | default: |
1732 | llvm_unreachable("unsupported register" ); |
1733 | } |
1734 | } |
1735 | |
1736 | const RegisterInfo *array = |
1737 | RegisterInfoPOSIX_riscv64::GetRegisterInfoPtr(target_arch: m_arch); |
1738 | const uint32_t length = |
1739 | RegisterInfoPOSIX_riscv64::GetRegisterInfoCount(target_arch: m_arch); |
1740 | |
1741 | if (reg_index >= length || reg_kind != eRegisterKindLLDB) |
1742 | return {}; |
1743 | |
1744 | return array[reg_index]; |
1745 | } |
1746 | |
1747 | bool EmulateInstructionRISCV::SetTargetTriple(const ArchSpec &arch) { |
1748 | return SupportsThisArch(arch); |
1749 | } |
1750 | |
1751 | bool EmulateInstructionRISCV::TestEmulation(Stream &out_stream, ArchSpec &arch, |
1752 | OptionValueDictionary *test_data) { |
1753 | return false; |
1754 | } |
1755 | |
1756 | void EmulateInstructionRISCV::Initialize() { |
1757 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
1758 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
1759 | } |
1760 | |
1761 | void EmulateInstructionRISCV::Terminate() { |
1762 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
1763 | } |
1764 | |
1765 | lldb_private::EmulateInstruction * |
1766 | EmulateInstructionRISCV::CreateInstance(const ArchSpec &arch, |
1767 | InstructionType inst_type) { |
1768 | if (EmulateInstructionRISCV::SupportsThisInstructionType(inst_type) && |
1769 | SupportsThisArch(arch)) { |
1770 | return new EmulateInstructionRISCV(arch); |
1771 | } |
1772 | |
1773 | return nullptr; |
1774 | } |
1775 | |
1776 | bool EmulateInstructionRISCV::SupportsThisArch(const ArchSpec &arch) { |
1777 | return arch.GetTriple().isRISCV(); |
1778 | } |
1779 | |
1780 | } // namespace lldb_private |
1781 | |