1 | //===-- TestRISCVEmulator.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 "gtest/gtest.h" |
10 | |
11 | #include "lldb/Core/Address.h" |
12 | #include "lldb/Core/Disassembler.h" |
13 | #include "lldb/Core/PluginManager.h" |
14 | #include "lldb/Target/ExecutionContext.h" |
15 | #include "lldb/Utility/ArchSpec.h" |
16 | #include "lldb/Utility/RegisterValue.h" |
17 | |
18 | #include "Plugins/Instruction/RISCV/EmulateInstructionRISCV.h" |
19 | #include "Plugins/Process/Utility/RegisterInfoPOSIX_riscv64.h" |
20 | #include "Plugins/Process/Utility/lldb-riscv-register-enums.h" |
21 | |
22 | using namespace llvm; |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | |
26 | struct RISCVEmulatorTester : public EmulateInstructionRISCV, testing::Test { |
27 | RegisterInfoPOSIX_riscv64::GPR gpr; |
28 | RegisterInfoPOSIX_riscv64::FPR fpr; |
29 | uint8_t memory[1024] = {0}; |
30 | |
31 | RISCVEmulatorTester(std::string triple = "riscv64-unknown-linux-gnu" ) |
32 | : EmulateInstructionRISCV(ArchSpec(triple)) { |
33 | EmulateInstruction::SetReadRegCallback(ReadRegisterCallback); |
34 | EmulateInstruction::SetWriteRegCallback(WriteRegisterCallback); |
35 | EmulateInstruction::SetReadMemCallback(ReadMemoryCallback); |
36 | EmulateInstruction::SetWriteMemCallback(WriteMemoryCallback); |
37 | ClearAll(); |
38 | } |
39 | |
40 | static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton, |
41 | const RegisterInfo *reg_info, |
42 | RegisterValue ®_value) { |
43 | RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction; |
44 | uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; |
45 | if (reg == gpr_x0_riscv) |
46 | reg_value.SetUInt(uint: 0, byte_size: reg_info->byte_size); |
47 | if (reg >= gpr_pc_riscv && reg <= gpr_x31_riscv) |
48 | reg_value.SetUInt(uint: tester->gpr.gpr[reg], byte_size: reg_info->byte_size); |
49 | if (reg >= fpr_f0_riscv && reg <= fpr_f31_riscv) |
50 | reg_value.SetUInt(uint: tester->fpr.fpr[reg - fpr_f0_riscv], |
51 | byte_size: reg_info->byte_size); |
52 | if (reg == fpr_fcsr_riscv) |
53 | reg_value.SetUInt(uint: tester->fpr.fcsr, byte_size: reg_info->byte_size); |
54 | return true; |
55 | } |
56 | |
57 | static bool WriteRegisterCallback(EmulateInstruction *instruction, |
58 | void *baton, const Context &context, |
59 | const RegisterInfo *reg_info, |
60 | const RegisterValue ®_value) { |
61 | RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction; |
62 | uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; |
63 | if (reg >= gpr_pc_riscv && reg <= gpr_x31_riscv) |
64 | tester->gpr.gpr[reg] = reg_value.GetAsUInt64(); |
65 | if (reg >= fpr_f0_riscv && reg <= fpr_f31_riscv) |
66 | tester->fpr.fpr[reg - fpr_f0_riscv] = reg_value.GetAsUInt64(); |
67 | if (reg == fpr_fcsr_riscv) |
68 | tester->fpr.fcsr = reg_value.GetAsUInt32(); |
69 | return true; |
70 | } |
71 | |
72 | static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton, |
73 | const Context &context, addr_t addr, |
74 | void *dst, size_t length) { |
75 | RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction; |
76 | assert(addr + length < sizeof(tester->memory)); |
77 | memcpy(dest: dst, src: tester->memory + addr, n: length); |
78 | return length; |
79 | }; |
80 | |
81 | static size_t WriteMemoryCallback(EmulateInstruction *instruction, |
82 | void *baton, const Context &context, |
83 | addr_t addr, const void *dst, |
84 | size_t length) { |
85 | RISCVEmulatorTester *tester = (RISCVEmulatorTester *)instruction; |
86 | assert(addr + length < sizeof(tester->memory)); |
87 | memcpy(dest: tester->memory + addr, src: dst, n: length); |
88 | return length; |
89 | }; |
90 | |
91 | bool DecodeAndExecute(uint32_t inst, bool ignore_cond) { |
92 | return llvm::transformOptional( |
93 | O: Decode(inst), |
94 | F: [&](DecodeResult res) { return Execute(inst: res, ignore_cond); }) |
95 | .value_or(u: false); |
96 | } |
97 | |
98 | void ClearAll() { |
99 | memset(s: &gpr, c: 0, n: sizeof(gpr)); |
100 | memset(s: &fpr, c: 0, n: sizeof(fpr)); |
101 | memset(s: memory, c: 0, n: sizeof(memory)); |
102 | } |
103 | }; |
104 | |
105 | TEST_F(RISCVEmulatorTester, testJAL) { |
106 | addr_t old_pc = 0x114514; |
107 | WritePC(pc: old_pc); |
108 | // jal x1, -6*4 |
109 | uint32_t inst = 0b11111110100111111111000011101111; |
110 | ASSERT_TRUE(DecodeAndExecute(inst, false)); |
111 | auto x1 = gpr.gpr[1]; |
112 | auto pc = ReadPC(); |
113 | ASSERT_TRUE(pc.has_value()); |
114 | ASSERT_EQ(x1, old_pc + 4); |
115 | ASSERT_EQ(*pc, old_pc + (-6 * 4)); |
116 | } |
117 | |
118 | constexpr uint32_t EncodeIType(uint32_t opcode, uint32_t funct3, uint32_t rd, |
119 | uint32_t rs1, uint32_t imm) { |
120 | return imm << 20 | rs1 << 15 | funct3 << 12 | rd << 7 | opcode; |
121 | } |
122 | |
123 | constexpr uint32_t EncodeJALR(uint32_t rd, uint32_t rs1, int32_t offset) { |
124 | return EncodeIType(opcode: 0b1100111, funct3: 0, rd, rs1, imm: uint32_t(offset)); |
125 | } |
126 | |
127 | TEST_F(RISCVEmulatorTester, testJALR) { |
128 | addr_t old_pc = 0x114514; |
129 | addr_t old_x2 = 0x1024; |
130 | WritePC(pc: old_pc); |
131 | gpr.gpr[2] = old_x2; |
132 | // jalr x1, x2(-255) |
133 | uint32_t inst = EncodeJALR(rd: 1, rs1: 2, offset: -255); |
134 | ASSERT_TRUE(DecodeAndExecute(inst, false)); |
135 | auto x1 = gpr.gpr[1]; |
136 | auto pc = ReadPC(); |
137 | ASSERT_TRUE(pc.has_value()); |
138 | ASSERT_EQ(x1, old_pc + 4); |
139 | // JALR always zeros the bottom bit of the target address. |
140 | ASSERT_EQ(*pc, (old_x2 + (-255)) & (~1)); |
141 | } |
142 | |
143 | constexpr uint32_t EncodeBType(uint32_t opcode, uint32_t funct3, uint32_t rs1, |
144 | uint32_t rs2, uint32_t imm) { |
145 | uint32_t bimm = (imm & (0b1 << 11)) >> 4 | (imm & (0b11110)) << 7 | |
146 | (imm & (0b111111 << 5)) << 20 | (imm & (0b1 << 12)) << 19; |
147 | |
148 | return rs2 << 20 | rs1 << 15 | funct3 << 12 | opcode | bimm; |
149 | } |
150 | |
151 | constexpr uint32_t BEQ(uint32_t rs1, uint32_t rs2, int32_t offset) { |
152 | return EncodeBType(opcode: 0b1100011, funct3: 0b000, rs1, rs2, imm: uint32_t(offset)); |
153 | } |
154 | |
155 | constexpr uint32_t BNE(uint32_t rs1, uint32_t rs2, int32_t offset) { |
156 | return EncodeBType(opcode: 0b1100011, funct3: 0b001, rs1, rs2, imm: uint32_t(offset)); |
157 | } |
158 | |
159 | constexpr uint32_t BLT(uint32_t rs1, uint32_t rs2, int32_t offset) { |
160 | return EncodeBType(opcode: 0b1100011, funct3: 0b100, rs1, rs2, imm: uint32_t(offset)); |
161 | } |
162 | |
163 | constexpr uint32_t BGE(uint32_t rs1, uint32_t rs2, int32_t offset) { |
164 | return EncodeBType(opcode: 0b1100011, funct3: 0b101, rs1, rs2, imm: uint32_t(offset)); |
165 | } |
166 | |
167 | constexpr uint32_t BLTU(uint32_t rs1, uint32_t rs2, int32_t offset) { |
168 | return EncodeBType(opcode: 0b1100011, funct3: 0b110, rs1, rs2, imm: uint32_t(offset)); |
169 | } |
170 | |
171 | constexpr uint32_t BGEU(uint32_t rs1, uint32_t rs2, int32_t offset) { |
172 | return EncodeBType(opcode: 0b1100011, funct3: 0b111, rs1, rs2, imm: uint32_t(offset)); |
173 | } |
174 | |
175 | using EncoderB = uint32_t (*)(uint32_t rs1, uint32_t rs2, int32_t offset); |
176 | |
177 | static void testBranch(RISCVEmulatorTester *tester, EncoderB encoder, |
178 | bool branched, uint64_t rs1, uint64_t rs2) { |
179 | // prepare test registers |
180 | addr_t old_pc = 0x114514; |
181 | tester->WritePC(pc: old_pc); |
182 | tester->gpr.gpr[1] = rs1; |
183 | tester->gpr.gpr[2] = rs2; |
184 | // b<cmp> x1, x2, (-256) |
185 | uint32_t inst = encoder(1, 2, -256); |
186 | ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); |
187 | auto pc = tester->ReadPC(); |
188 | ASSERT_TRUE(pc.has_value()); |
189 | ASSERT_EQ(*pc, old_pc + (branched ? (-256) : 0)); |
190 | } |
191 | |
192 | #define GEN_BRANCH_TEST(name, rs1, rs2_branched, rs2_continued) \ |
193 | TEST_F(RISCVEmulatorTester, test##name##Branched) { \ |
194 | testBranch(this, name, true, rs1, rs2_branched); \ |
195 | } \ |
196 | TEST_F(RISCVEmulatorTester, test##name##Continued) { \ |
197 | testBranch(this, name, false, rs1, rs2_continued); \ |
198 | } |
199 | |
200 | static void CheckRD(RISCVEmulatorTester *tester, uint64_t rd, uint64_t value) { |
201 | ASSERT_EQ(tester->gpr.gpr[rd], value); |
202 | } |
203 | |
204 | template <typename T> |
205 | static void CheckMem(RISCVEmulatorTester *tester, uint64_t addr, |
206 | uint64_t value) { |
207 | auto mem = tester->ReadMem<T>(addr); |
208 | ASSERT_TRUE(mem.has_value()); |
209 | ASSERT_EQ(*mem, value); |
210 | } |
211 | |
212 | using RS1 = uint64_t; |
213 | using RS2 = uint64_t; |
214 | using PC = uint64_t; |
215 | using RDComputer = std::function<uint64_t(RS1, RS2, PC)>; |
216 | |
217 | static void TestInst(RISCVEmulatorTester *tester, DecodeResult inst, |
218 | bool has_rs2, RDComputer rd_val) { |
219 | |
220 | addr_t old_pc = 0x114514; |
221 | tester->WritePC(pc: old_pc); |
222 | uint32_t rd = DecodeRD(inst: inst.inst); |
223 | uint32_t rs1 = DecodeRS1(inst: inst.inst); |
224 | uint32_t rs2 = 0; |
225 | |
226 | uint64_t rs1_val = 0x19; |
227 | uint64_t rs2_val = 0x81; |
228 | |
229 | if (rs1) |
230 | tester->gpr.gpr[rs1] = rs1_val; |
231 | |
232 | if (has_rs2) { |
233 | rs2 = DecodeRS2(inst: inst.inst); |
234 | if (rs2) { |
235 | if (rs1 == rs2) |
236 | rs2_val = rs1_val; |
237 | tester->gpr.gpr[rs2] = rs2_val; |
238 | } |
239 | } |
240 | |
241 | ASSERT_TRUE(tester->Execute(inst, false)); |
242 | CheckRD(tester, rd, value: rd_val(rs1_val, rs2 ? rs2_val : 0, old_pc)); |
243 | } |
244 | |
245 | template <typename T> |
246 | static void TestAtomic(RISCVEmulatorTester *tester, uint64_t inst, T rs1_val, |
247 | T rs2_val, T rd_expected, T mem_expected) { |
248 | // Atomic inst must have rs1 and rs2 |
249 | |
250 | uint32_t rd = DecodeRD(inst); |
251 | uint32_t rs1 = DecodeRS1(inst); |
252 | uint32_t rs2 = DecodeRS2(inst); |
253 | |
254 | // addr was stored in rs1 |
255 | uint64_t atomic_addr = 0x100; |
256 | |
257 | tester->gpr.gpr[rs1] = atomic_addr; |
258 | tester->gpr.gpr[rs2] = rs2_val; |
259 | |
260 | // Write and check rs1_val in atomic_addr |
261 | ASSERT_TRUE(tester->WriteMem<T>(atomic_addr, rs1_val)); |
262 | CheckMem<T>(tester, atomic_addr, rs1_val); |
263 | |
264 | ASSERT_TRUE(tester->DecodeAndExecute(inst, false)); |
265 | CheckRD(tester, rd, rd_expected); |
266 | CheckMem<T>(tester, atomic_addr, mem_expected); |
267 | } |
268 | |
269 | TEST_F(RISCVEmulatorTester, TestAtomicSequence) { |
270 | this->WritePC(pc: 0x0); |
271 | *(uint32_t *)this->memory = 0x100427af; // lr.w a5,(s0) |
272 | *(uint32_t *)(this->memory + 4) = 0x00079663; // bnez a5,12 |
273 | *(uint32_t *)(this->memory + 8) = 0x1ce426af; // sc.w.aq a3,a4,(s0) |
274 | *(uint32_t *)(this->memory + 12) = 0xfe069ae3; // bnez a3,-12 |
275 | ASSERT_TRUE(this->DecodeAndExecute(*(uint32_t *)this->memory, false)); |
276 | ASSERT_EQ(this->gpr.gpr[0], uint64_t(16)); |
277 | } |
278 | |
279 | struct TestDecode { |
280 | uint32_t inst; |
281 | RISCVInst inst_type; |
282 | }; |
283 | |
284 | TEST_F(RISCVEmulatorTester, TestCDecode) { |
285 | std::vector<TestDecode> tests = { |
286 | {.inst: 0x0000, .inst_type: INVALID{.inst: 0x0000}}, |
287 | {.inst: 0x0010, .inst_type: RESERVED{.inst: 0x0010}}, |
288 | // ADDI4SPN here, decode as ADDI |
289 | {.inst: 0x0024, .inst_type: ADDI{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 2}, .imm: 8}}, |
290 | {.inst: 0x2084, .inst_type: FLD{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .imm: 0}}, |
291 | {.inst: 0x4488, .inst_type: LW{.rd: Rd{.rd: 10}, .rs1: Rs{.rs: 9}, .imm: 8}}, |
292 | {.inst: 0x6488, .inst_type: LD{.rd: Rd{.rd: 10}, .rs1: Rs{.rs: 9}, .imm: 8}}, |
293 | {.inst: 0xA084, .inst_type: FSD{.rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}, .imm: 0}}, |
294 | {.inst: 0xC488, .inst_type: SW{.rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 10}, .imm: 8}}, |
295 | {.inst: 0xE488, .inst_type: SD{.rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 10}, .imm: 8}}, |
296 | {.inst: 0x1001, .inst_type: NOP{.inst: 0x1001}}, |
297 | {.inst: 0x1085, .inst_type: ADDI{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 1}, .imm: uint32_t(-31)}}, |
298 | {.inst: 0x2081, .inst_type: ADDIW{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 1}, .imm: 0}}, |
299 | // ADDI16SP here, decode as ADDI |
300 | {.inst: 0x7101, .inst_type: ADDI{.rd: Rd{.rd: 2}, .rs1: Rs{.rs: 2}, .imm: uint32_t(-512)}}, |
301 | {.inst: 0x4081, .inst_type: ADDI{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 0}, .imm: 0}}, |
302 | {.inst: 0x7081, .inst_type: LUI{.rd: Rd{.rd: 1}, .imm: uint32_t(-131072)}}, |
303 | {.inst: 0x8085, .inst_type: SRLI{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .shamt: 1}}, |
304 | {.inst: 0x8485, .inst_type: SRAI{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .shamt: 1}}, |
305 | {.inst: 0x8881, .inst_type: ANDI{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .imm: 0}}, |
306 | {.inst: 0x8C85, .inst_type: SUB{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}}}, |
307 | {.inst: 0x8CA5, .inst_type: XOR{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}}}, |
308 | {.inst: 0x8CC5, .inst_type: OR{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}}}, |
309 | {.inst: 0x8CE5, .inst_type: AND{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}}}, |
310 | {.inst: 0x9C85, .inst_type: SUBW{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}}}, |
311 | {.inst: 0x9CA5, .inst_type: ADDW{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}}}, |
312 | // C.J here, decoded as JAL |
313 | {.inst: 0xA001, .inst_type: JAL{.rd: Rd{.rd: 0}, .imm: 0}}, |
314 | {.inst: 0xC081, .inst_type: B{.rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 0}, .imm: 0, .funct3: 0b000}}, |
315 | {.inst: 0xE081, .inst_type: B{.rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 0}, .imm: 0, .funct3: 0b001}}, |
316 | {.inst: 0x1082, .inst_type: SLLI{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 1}, .shamt: 32}}, |
317 | {.inst: 0x1002, .inst_type: HINT{.inst: 0x1002}}, |
318 | // SLLI64 here, decoded as HINT if not in RV128 |
319 | {.inst: 0x0082, .inst_type: HINT{.inst: 0x0082}}, |
320 | // FLDSP here, decoded as FLD |
321 | {.inst: 0x2082, .inst_type: FLD{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 2}, .imm: 0}}, |
322 | // LWSP here, decoded as LW |
323 | {.inst: 0x4082, .inst_type: LW{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 2}, .imm: 0}}, |
324 | // LDSP here, decoded as LD |
325 | {.inst: 0x6082, .inst_type: LD{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 2}, .imm: 0}}, |
326 | // C.JR here, decoded as JALR |
327 | {.inst: 0x8082, .inst_type: JALR{.rd: Rd{.rd: 0}, .rs1: Rs{.rs: 1}, .imm: 0}}, |
328 | // C.MV here, decoded as ADD |
329 | {.inst: 0x8086, .inst_type: ADD{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 0}, .rs2: Rs{.rs: 1}}}, |
330 | {.inst: 0x9002, .inst_type: EBREAK{.inst: 0x9002}}, |
331 | {.inst: 0x9082, .inst_type: JALR{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 1}, .imm: 0}}, |
332 | {.inst: 0x9086, .inst_type: ADD{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 1}, .rs2: Rs{.rs: 1}}}, |
333 | // C.FSDSP here, decoded as FSD |
334 | {.inst: 0xA006, .inst_type: FSD{.rs1: Rs{.rs: 2}, .rs2: Rs{.rs: 1}, .imm: 0}}, |
335 | // C.SWSP here, decoded as SW |
336 | {.inst: 0xC006, .inst_type: SW{.rs1: Rs{.rs: 2}, .rs2: Rs{.rs: 1}, .imm: 0}}, |
337 | // C.SDSP here, decoded as SD |
338 | {.inst: 0xE006, .inst_type: SD{.rs1: Rs{.rs: 2}, .rs2: Rs{.rs: 1}, .imm: 0}}, |
339 | }; |
340 | |
341 | for (auto i : tests) { |
342 | auto decode = this->Decode(inst: i.inst); |
343 | ASSERT_TRUE(decode.has_value()); |
344 | ASSERT_EQ(decode->decoded, i.inst_type); |
345 | } |
346 | } |
347 | |
348 | class RISCVEmulatorTester32 : public RISCVEmulatorTester { |
349 | public: |
350 | RISCVEmulatorTester32() : RISCVEmulatorTester("riscv32-unknown-linux-gnu" ) {} |
351 | }; |
352 | |
353 | TEST_F(RISCVEmulatorTester32, TestCDecodeRV32) { |
354 | std::vector<TestDecode> tests = { |
355 | {.inst: 0x6002, .inst_type: FLW{.rd: Rd{.rd: 0}, .rs1: Rs{.rs: 2}, .imm: 0}}, |
356 | {.inst: 0xE006, .inst_type: FSW{.rs1: Rs{.rs: 2}, .rs2: Rs{.rs: 1}, .imm: 0}}, |
357 | {.inst: 0x6000, .inst_type: FLW{.rd: Rd{.rd: 8}, .rs1: Rs{.rs: 8}, .imm: 0}}, |
358 | {.inst: 0xE000, .inst_type: FSW{.rs1: Rs{.rs: 8}, .rs2: Rs{.rs: 8}, .imm: 0}}, |
359 | |
360 | {.inst: 0x2084, .inst_type: FLD{.rd: Rd{.rd: 9}, .rs1: Rs{.rs: 9}, .imm: 0}}, |
361 | {.inst: 0xA084, .inst_type: FSD{.rs1: Rs{.rs: 9}, .rs2: Rs{.rs: 9}, .imm: 0}}, |
362 | {.inst: 0x2082, .inst_type: FLD{.rd: Rd{.rd: 1}, .rs1: Rs{.rs: 2}, .imm: 0}}, |
363 | {.inst: 0xA006, .inst_type: FSD{.rs1: Rs{.rs: 2}, .rs2: Rs{.rs: 1}, .imm: 0}}, |
364 | }; |
365 | |
366 | for (auto i : tests) { |
367 | auto decode = this->Decode(inst: i.inst); |
368 | ASSERT_TRUE(decode.has_value()); |
369 | ASSERT_EQ(decode->decoded, i.inst_type); |
370 | } |
371 | } |
372 | |
373 | // GEN_BRANCH_TEST(opcode, imm1, imm2, imm3): |
374 | // It should branch for instruction `opcode imm1, imm2` |
375 | // It should do nothing for instruction `opcode imm1, imm3` |
376 | GEN_BRANCH_TEST(BEQ, 1, 1, 0) |
377 | GEN_BRANCH_TEST(BNE, 1, 0, 1) |
378 | GEN_BRANCH_TEST(BLT, -2, 1, -3) |
379 | GEN_BRANCH_TEST(BGE, -2, -3, 1) |
380 | GEN_BRANCH_TEST(BLTU, -2, -1, 1) |
381 | GEN_BRANCH_TEST(BGEU, -2, 1, -1) |
382 | |
383 | struct TestData { |
384 | uint32_t inst; |
385 | std::string name; |
386 | bool has_rs2; |
387 | RDComputer rd_val; |
388 | }; |
389 | |
390 | TEST_F(RISCVEmulatorTester, TestDecodeAndExcute) { |
391 | std::vector<TestData> tests = { |
392 | // RV32I & RV64I Tests |
393 | {.inst: 0x00010113, .name: "ADDI" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return rs1 + 0; }}, |
394 | {.inst: 0x00023517, .name: "AUIPC" , .has_rs2: false, .rd_val: [](RS1, RS2, PC pc) { return pc + 143360; }}, |
395 | {.inst: 0x0006079b, .name: "ADDIW" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return rs1 + 0; }}, |
396 | {.inst: 0x00110837, .name: "LUI" , .has_rs2: false, .rd_val: [](RS1, RS2, PC pc) { return 1114112; }}, |
397 | {.inst: 0x00147513, .name: "ANDI" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return rs1 & 1; }}, |
398 | {.inst: 0x00153513, .name: "SLTIU" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return 0; }}, |
399 | {.inst: 0x00256513, .name: "ORI" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return rs1 | 2; }}, |
400 | {.inst: 0x00451a13, .name: "SLLI" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return rs1 << 4; }}, |
401 | {.inst: 0x00455693, .name: "SRLI" , .has_rs2: false, .rd_val: [](RS1 rs1, RS2, PC) { return rs1 >> 4; }}, |
402 | {.inst: 0x00a035b3, .name: "SLTU" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs2 != 0; }}, |
403 | {.inst: 0x00b50633, .name: "ADD" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 + rs2; }}, |
404 | {.inst: 0x40d507b3, .name: "SUB" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 - rs2; }}, |
405 | |
406 | // RV32M & RV64M Tests |
407 | {.inst: 0x02f787b3, .name: "MUL" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 * rs2; }}, |
408 | {.inst: 0x2F797B3, .name: "MULH" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return 0; }}, |
409 | {.inst: 0x2F7A7B3, .name: "MULHSU" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return 0; }}, |
410 | {.inst: 0x2F7B7B3, .name: "MULHU" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return 0; }}, |
411 | {.inst: 0x02f747b3, .name: "DIV" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, |
412 | {.inst: 0x02f757b3, .name: "DIVU" , .has_rs2: true, |
413 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, |
414 | {.inst: 0x02f767b3, .name: "REM" , .has_rs2: true, .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, |
415 | {.inst: 0x02f777b3, .name: "REMU" , .has_rs2: true, |
416 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, |
417 | {.inst: 0x02f787bb, .name: "MULW" , .has_rs2: true, |
418 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 * rs2; }}, |
419 | {.inst: 0x02f747bb, .name: "DIVW" , .has_rs2: true, |
420 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, |
421 | {.inst: 0x02f757bb, .name: "DIVUW" , .has_rs2: true, |
422 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 / rs2; }}, |
423 | {.inst: 0x02f767bb, .name: "REMW" , .has_rs2: true, |
424 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, |
425 | {.inst: 0x02f777bb, .name: "REMUW" , .has_rs2: true, |
426 | .rd_val: [](RS1 rs1, RS2 rs2, PC) { return rs1 % rs2; }}, |
427 | }; |
428 | for (auto i : tests) { |
429 | auto decode = this->Decode(inst: i.inst); |
430 | ASSERT_TRUE(decode.has_value()); |
431 | std::string name = decode->pattern.name; |
432 | ASSERT_EQ(name, i.name); |
433 | TestInst(tester: this, inst: *decode, has_rs2: i.has_rs2, rd_val: i.rd_val); |
434 | } |
435 | } |
436 | |
437 | TEST_F(RISCVEmulatorTester, TestAMOSWAP) { |
438 | TestAtomic<uint32_t>(tester: this, inst: 0x8F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x2); |
439 | TestAtomic<uint64_t>(tester: this, inst: 0x8F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x2); |
440 | } |
441 | |
442 | TEST_F(RISCVEmulatorTester, TestAMOADD) { |
443 | TestAtomic<uint32_t>(tester: this, inst: 0xF7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x3); |
444 | TestAtomic<uint64_t>(tester: this, inst: 0xF7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x3); |
445 | } |
446 | |
447 | TEST_F(RISCVEmulatorTester, TestAMOXOR) { |
448 | TestAtomic<uint32_t>(tester: this, inst: 0x20F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x3); |
449 | TestAtomic<uint32_t>(tester: this, inst: 0x20F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x3); |
450 | } |
451 | |
452 | TEST_F(RISCVEmulatorTester, TestAMOAND) { |
453 | TestAtomic<uint32_t>(tester: this, inst: 0x60F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x0); |
454 | TestAtomic<uint64_t>(tester: this, inst: 0x60F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x0); |
455 | } |
456 | |
457 | TEST_F(RISCVEmulatorTester, TestAMOOR) { |
458 | TestAtomic<uint32_t>(tester: this, inst: 0x40F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x3); |
459 | TestAtomic<uint32_t>(tester: this, inst: 0x40F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x3); |
460 | } |
461 | |
462 | TEST_F(RISCVEmulatorTester, TestAMOMIN) { |
463 | TestAtomic<uint32_t>(tester: this, inst: 0x80F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x1); |
464 | TestAtomic<uint64_t>(tester: this, inst: 0x80F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x1); |
465 | } |
466 | |
467 | TEST_F(RISCVEmulatorTester, TestAMOMAX) { |
468 | TestAtomic<uint32_t>(tester: this, inst: 0xA0F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x2); |
469 | TestAtomic<uint64_t>(tester: this, inst: 0xA0F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x2); |
470 | } |
471 | |
472 | TEST_F(RISCVEmulatorTester, TestAMOMINU) { |
473 | TestAtomic<uint32_t>(tester: this, inst: 0xC0F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x1); |
474 | TestAtomic<uint64_t>(tester: this, inst: 0xC0F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x1); |
475 | } |
476 | |
477 | TEST_F(RISCVEmulatorTester, TestAMOMAXU) { |
478 | TestAtomic<uint32_t>(tester: this, inst: 0xE0F7282F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x2); |
479 | TestAtomic<uint64_t>(tester: this, inst: 0xE0F7382F, rs1_val: 0x1, rs2_val: 0x2, rd_expected: 0x1, mem_expected: 0x2); |
480 | } |
481 | |
482 | template <typename T> struct F_D_CalInst { |
483 | uint32_t inst; |
484 | std::string name; |
485 | T rs1_val; |
486 | T rs2_val; |
487 | T rd_val; |
488 | }; |
489 | |
490 | using FloatCalInst = F_D_CalInst<float>; |
491 | using DoubleCalInst = F_D_CalInst<double>; |
492 | |
493 | template <typename T> |
494 | static void TestF_D_CalInst(RISCVEmulatorTester *tester, DecodeResult inst, |
495 | T rs1_val, T rs2_val, T rd_exp) { |
496 | std::vector<std::string> CMPs = {"FEQ_S" , "FLT_S" , "FLE_S" , |
497 | "FEQ_D" , "FLT_D" , "FLE_D" }; |
498 | std::vector<std::string> FMAs = {"FMADD_S" , "FMSUB_S" , "FNMSUB_S" , |
499 | "FNMADD_S" , "FMADD_D" , "FMSUB_D" , |
500 | "FNMSUB_D" , "FNMADD_D" }; |
501 | |
502 | uint32_t rd = DecodeRD(inst: inst.inst); |
503 | uint32_t rs1 = DecodeRS1(inst: inst.inst); |
504 | uint32_t rs2 = DecodeRS2(inst: inst.inst); |
505 | |
506 | APFloat ap_rs1_val(rs1_val); |
507 | APFloat ap_rs2_val(rs2_val); |
508 | APFloat ap_rs3_val(0.0f); |
509 | static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, |
510 | "T should be float or double" ); |
511 | if constexpr (std::is_same_v<T, float>) |
512 | ap_rs3_val = APFloat(0.5f); |
513 | if constexpr (std::is_same_v<T, double>) |
514 | ap_rs3_val = APFloat(0.5); |
515 | |
516 | if (rs1) |
517 | tester->fpr.fpr[rs1] = ap_rs1_val.bitcastToAPInt().getZExtValue(); |
518 | if (rs2) |
519 | tester->fpr.fpr[rs2] = ap_rs2_val.bitcastToAPInt().getZExtValue(); |
520 | for (auto i : FMAs) { |
521 | if (inst.pattern.name == i) { |
522 | uint32_t rs3 = DecodeRS3(inst: inst.inst); |
523 | tester->fpr.fpr[rs3] = ap_rs3_val.bitcastToAPInt().getZExtValue(); |
524 | } |
525 | } |
526 | ASSERT_TRUE(tester->Execute(inst, false)); |
527 | for (auto i : CMPs) { |
528 | if (inst.pattern.name == i) { |
529 | ASSERT_EQ(tester->gpr.gpr[rd], rd_exp); |
530 | return; |
531 | } |
532 | } |
533 | |
534 | if constexpr (std::is_same_v<T, float>) { |
535 | APInt apInt(32, tester->fpr.fpr[rd]); |
536 | APFloat rd_val(apInt.bitsToFloat()); |
537 | ASSERT_EQ(rd_val.convertToFloat(), rd_exp); |
538 | } |
539 | if constexpr (std::is_same_v<T, double>) { |
540 | APInt apInt(64, tester->fpr.fpr[rd]); |
541 | APFloat rd_val(apInt.bitsToDouble()); |
542 | ASSERT_EQ(rd_val.convertToDouble(), rd_exp); |
543 | } |
544 | } |
545 | |
546 | TEST_F(RISCVEmulatorTester, TestFloatInst) { |
547 | std::vector<FloatCalInst> tests = { |
548 | {.inst: 0x21F253, .name: "FADD_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 1.0f}, |
549 | {.inst: 0x821F253, .name: "FSUB_S" , .rs1_val: 1.0f, .rs2_val: 0.5f, .rd_val: 0.5f}, |
550 | {.inst: 0x1021F253, .name: "FMUL_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 0.25f}, |
551 | {.inst: 0x1821F253, .name: "FDIV_S" , .rs1_val: 0.1f, .rs2_val: 0.1f, .rd_val: 1.0f}, |
552 | {.inst: 0x20218253, .name: "FSGNJ_S" , .rs1_val: 0.5f, .rs2_val: 0.2f, .rd_val: 0.5f}, |
553 | {.inst: 0x20219253, .name: "FSGNJN_S" , .rs1_val: 0.5f, .rs2_val: -1.0f, .rd_val: 0.5f}, |
554 | {.inst: 0x2021A253, .name: "FSGNJX_S" , .rs1_val: -0.5f, .rs2_val: -0.5f, .rd_val: 0.5f}, |
555 | {.inst: 0x2021A253, .name: "FSGNJX_S" , .rs1_val: -0.5f, .rs2_val: 0.5f, .rd_val: -0.5f}, |
556 | {.inst: 0x28218253, .name: "FMIN_S" , .rs1_val: -0.5f, .rs2_val: 0.5f, .rd_val: -0.5f}, |
557 | {.inst: 0x28218253, .name: "FMIN_S" , .rs1_val: -0.5f, .rs2_val: -0.6f, .rd_val: -0.6f}, |
558 | {.inst: 0x28218253, .name: "FMIN_S" , .rs1_val: 0.5f, .rs2_val: 0.6f, .rd_val: 0.5f}, |
559 | {.inst: 0x28219253, .name: "FMAX_S" , .rs1_val: -0.5f, .rs2_val: -0.6f, .rd_val: -0.5f}, |
560 | {.inst: 0x28219253, .name: "FMAX_S" , .rs1_val: 0.5f, .rs2_val: 0.6f, .rd_val: 0.6f}, |
561 | {.inst: 0x28219253, .name: "FMAX_S" , .rs1_val: 0.5f, .rs2_val: -0.6f, .rd_val: 0.5f}, |
562 | {.inst: 0xA021A253, .name: "FEQ_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 1}, |
563 | {.inst: 0xA021A253, .name: "FEQ_S" , .rs1_val: 0.5f, .rs2_val: -0.5f, .rd_val: 0}, |
564 | {.inst: 0xA021A253, .name: "FEQ_S" , .rs1_val: -0.5f, .rs2_val: 0.5f, .rd_val: 0}, |
565 | {.inst: 0xA021A253, .name: "FEQ_S" , .rs1_val: 0.4f, .rs2_val: 0.5f, .rd_val: 0}, |
566 | {.inst: 0xA0219253, .name: "FLT_S" , .rs1_val: 0.4f, .rs2_val: 0.5f, .rd_val: 1}, |
567 | {.inst: 0xA0219253, .name: "FLT_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 0}, |
568 | {.inst: 0xA0218253, .name: "FLE_S" , .rs1_val: 0.4f, .rs2_val: 0.5f, .rd_val: 1}, |
569 | {.inst: 0xA0218253, .name: "FLE_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 1}, |
570 | {.inst: 0x4021F243, .name: "FMADD_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 0.75f}, |
571 | {.inst: 0x4021F247, .name: "FMSUB_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: -0.25f}, |
572 | {.inst: 0x4021F24B, .name: "FNMSUB_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: 0.25f}, |
573 | {.inst: 0x4021F24F, .name: "FNMADD_S" , .rs1_val: 0.5f, .rs2_val: 0.5f, .rd_val: -0.75f}, |
574 | }; |
575 | for (auto i : tests) { |
576 | auto decode = this->Decode(inst: i.inst); |
577 | ASSERT_TRUE(decode.has_value()); |
578 | std::string name = decode->pattern.name; |
579 | ASSERT_EQ(name, i.name); |
580 | TestF_D_CalInst(tester: this, inst: *decode, rs1_val: i.rs1_val, rs2_val: i.rs2_val, rd_exp: i.rd_val); |
581 | } |
582 | } |
583 | |
584 | TEST_F(RISCVEmulatorTester, TestDoubleInst) { |
585 | std::vector<DoubleCalInst> tests = { |
586 | {.inst: 0x221F253, .name: "FADD_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 1.0}, |
587 | {.inst: 0xA21F253, .name: "FSUB_D" , .rs1_val: 1.0, .rs2_val: 0.5, .rd_val: 0.5}, |
588 | {.inst: 0x1221F253, .name: "FMUL_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 0.25}, |
589 | {.inst: 0x1A21F253, .name: "FDIV_D" , .rs1_val: 0.1, .rs2_val: 0.1, .rd_val: 1.0}, |
590 | {.inst: 0x22218253, .name: "FSGNJ_D" , .rs1_val: 0.5, .rs2_val: 0.2, .rd_val: 0.5}, |
591 | {.inst: 0x22219253, .name: "FSGNJN_D" , .rs1_val: 0.5, .rs2_val: -1.0, .rd_val: 0.5}, |
592 | {.inst: 0x2221A253, .name: "FSGNJX_D" , .rs1_val: -0.5, .rs2_val: -0.5, .rd_val: 0.5}, |
593 | {.inst: 0x2221A253, .name: "FSGNJX_D" , .rs1_val: -0.5, .rs2_val: 0.5, .rd_val: -0.5}, |
594 | {.inst: 0x2A218253, .name: "FMIN_D" , .rs1_val: -0.5, .rs2_val: 0.5, .rd_val: -0.5}, |
595 | {.inst: 0x2A218253, .name: "FMIN_D" , .rs1_val: -0.5, .rs2_val: -0.6, .rd_val: -0.6}, |
596 | {.inst: 0x2A218253, .name: "FMIN_D" , .rs1_val: 0.5, .rs2_val: 0.6, .rd_val: 0.5}, |
597 | {.inst: 0x2A219253, .name: "FMAX_D" , .rs1_val: -0.5, .rs2_val: -0.6, .rd_val: -0.5}, |
598 | {.inst: 0x2A219253, .name: "FMAX_D" , .rs1_val: 0.5, .rs2_val: 0.6, .rd_val: 0.6}, |
599 | {.inst: 0x2A219253, .name: "FMAX_D" , .rs1_val: 0.5, .rs2_val: -0.6, .rd_val: 0.5}, |
600 | {.inst: 0xA221A253, .name: "FEQ_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 1}, |
601 | {.inst: 0xA221A253, .name: "FEQ_D" , .rs1_val: 0.5, .rs2_val: -0.5, .rd_val: 0}, |
602 | {.inst: 0xA221A253, .name: "FEQ_D" , .rs1_val: -0.5, .rs2_val: 0.5, .rd_val: 0}, |
603 | {.inst: 0xA221A253, .name: "FEQ_D" , .rs1_val: 0.4, .rs2_val: 0.5, .rd_val: 0}, |
604 | {.inst: 0xA2219253, .name: "FLT_D" , .rs1_val: 0.4, .rs2_val: 0.5, .rd_val: 1}, |
605 | {.inst: 0xA2219253, .name: "FLT_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 0}, |
606 | {.inst: 0xA2218253, .name: "FLE_D" , .rs1_val: 0.4, .rs2_val: 0.5, .rd_val: 1}, |
607 | {.inst: 0xA2218253, .name: "FLE_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 1}, |
608 | {.inst: 0x4221F243, .name: "FMADD_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 0.75}, |
609 | {.inst: 0x4221F247, .name: "FMSUB_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: -0.25}, |
610 | {.inst: 0x4221F24B, .name: "FNMSUB_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: 0.25}, |
611 | {.inst: 0x4221F24F, .name: "FNMADD_D" , .rs1_val: 0.5, .rs2_val: 0.5, .rd_val: -0.75}, |
612 | }; |
613 | for (auto i : tests) { |
614 | auto decode = this->Decode(inst: i.inst); |
615 | ASSERT_TRUE(decode.has_value()); |
616 | std::string name = decode->pattern.name; |
617 | ASSERT_EQ(name, i.name); |
618 | TestF_D_CalInst(tester: this, inst: *decode, rs1_val: i.rs1_val, rs2_val: i.rs2_val, rd_exp: i.rd_val); |
619 | } |
620 | } |
621 | |
622 | template <typename T> |
623 | static void TestInverse(RISCVEmulatorTester *tester, uint32_t f_reg, |
624 | uint32_t x_reg, DecodeResult f2i, DecodeResult i2f, |
625 | APFloat apf_val) { |
626 | uint64_t exp_x; |
627 | if constexpr (std::is_same_v<T, float>) |
628 | exp_x = uint64_t(apf_val.convertToFloat()); |
629 | if constexpr (std::is_same_v<T, double>) |
630 | exp_x = uint64_t(apf_val.convertToDouble()); |
631 | T exp_f = T(exp_x); |
632 | |
633 | // convert float/double to int. |
634 | tester->fpr.fpr[f_reg] = apf_val.bitcastToAPInt().getZExtValue(); |
635 | ASSERT_TRUE(tester->Execute(f2i, false)); |
636 | ASSERT_EQ(tester->gpr.gpr[x_reg], exp_x); |
637 | |
638 | // then convert int to float/double back. |
639 | ASSERT_TRUE(tester->Execute(i2f, false)); |
640 | ASSERT_EQ(tester->fpr.fpr[f_reg], |
641 | APFloat(exp_f).bitcastToAPInt().getZExtValue()); |
642 | } |
643 | |
644 | struct FCVTInst { |
645 | uint32_t f2i; |
646 | uint32_t i2f; |
647 | APFloat data; |
648 | bool isDouble; |
649 | }; |
650 | |
651 | TEST_F(RISCVEmulatorTester, TestFCVT) { |
652 | std::vector<FCVTInst> tests{ |
653 | // FCVT_W_S and FCVT_S_W |
654 | {.f2i: 0xC000F0D3, .i2f: 0xD000F0D3, .data: APFloat(12.0f), .isDouble: false}, |
655 | // FCVT_WU_S and FCVT_S_WU |
656 | {.f2i: 0xC010F0D3, .i2f: 0xD010F0D3, .data: APFloat(12.0f), .isDouble: false}, |
657 | // FCVT_L_S and FCVT_S_L |
658 | {.f2i: 0xC020F0D3, .i2f: 0xD020F0D3, .data: APFloat(12.0f), .isDouble: false}, |
659 | // FCVT_LU_S and FCVT_S_LU |
660 | {.f2i: 0xC030F0D3, .i2f: 0xD030F0D3, .data: APFloat(12.0f), .isDouble: false}, |
661 | // FCVT_W_D and FCVT_D_W |
662 | {.f2i: 0xC200F0D3, .i2f: 0xD200F0D3, .data: APFloat(12.0), .isDouble: true}, |
663 | // FCVT_WU_D and FCVT_D_WU |
664 | {.f2i: 0xC210F0D3, .i2f: 0xD210F0D3, .data: APFloat(12.0), .isDouble: true}, |
665 | // FCVT_L_D and FCVT_D_L |
666 | {.f2i: 0xC220F0D3, .i2f: 0xD220F0D3, .data: APFloat(12.0), .isDouble: true}, |
667 | // FCVT_LU_D and FCVT_D_LU |
668 | {.f2i: 0xC230F0D3, .i2f: 0xD230F0D3, .data: APFloat(12.0), .isDouble: true}, |
669 | }; |
670 | for (auto i : tests) { |
671 | auto f2i = this->Decode(inst: i.f2i); |
672 | auto i2f = this->Decode(inst: i.i2f); |
673 | ASSERT_TRUE(f2i.has_value()); |
674 | ASSERT_TRUE(i2f.has_value()); |
675 | uint32_t f_reg = DecodeRS1(inst: (*f2i).inst); |
676 | uint32_t x_reg = DecodeRS1(inst: (*i2f).inst); |
677 | if (i.isDouble) |
678 | TestInverse<double>(tester: this, f_reg, x_reg, f2i: *f2i, i2f: *i2f, apf_val: i.data); |
679 | else |
680 | TestInverse<float>(tester: this, f_reg, x_reg, f2i: *f2i, i2f: *i2f, apf_val: i.data); |
681 | } |
682 | } |
683 | |
684 | TEST_F(RISCVEmulatorTester, TestFDInverse) { |
685 | // FCVT_S_D |
686 | auto d2f = this->Decode(inst: 0x4010F0D3); |
687 | // FCVT_S_D |
688 | auto f2d = this->Decode(inst: 0x4200F0D3); |
689 | ASSERT_TRUE(d2f.has_value()); |
690 | ASSERT_TRUE(f2d.has_value()); |
691 | auto data = APFloat(12.0); |
692 | uint32_t reg = DecodeRS1(inst: (*d2f).inst); |
693 | float exp_f = 12.0f; |
694 | double exp_d = 12.0; |
695 | |
696 | // double to float |
697 | this->fpr.fpr[reg] = data.bitcastToAPInt().getZExtValue(); |
698 | ASSERT_TRUE(this->Execute(*d2f, false)); |
699 | ASSERT_EQ(this->fpr.fpr[reg], APFloat(exp_f).bitcastToAPInt().getZExtValue()); |
700 | |
701 | // float to double |
702 | ASSERT_TRUE(this->Execute(*f2d, false)); |
703 | ASSERT_EQ(this->fpr.fpr[reg], APFloat(exp_d).bitcastToAPInt().getZExtValue()); |
704 | } |
705 | |
706 | TEST_F(RISCVEmulatorTester, TestFloatLSInst) { |
707 | uint32_t FLWInst = 0x1A207; // imm = 0 |
708 | uint32_t FSWInst = 0x21A827; // imm = 16 |
709 | |
710 | APFloat apf(12.0f); |
711 | uint64_t bits = apf.bitcastToAPInt().getZExtValue(); |
712 | |
713 | *(uint64_t *)this->memory = bits; |
714 | auto decode = this->Decode(inst: FLWInst); |
715 | ASSERT_TRUE(decode.has_value()); |
716 | std::string name = decode->pattern.name; |
717 | ASSERT_EQ(name, "FLW" ); |
718 | ASSERT_TRUE(this->Execute(*decode, false)); |
719 | ASSERT_EQ(this->fpr.fpr[DecodeRD(FLWInst)], bits); |
720 | |
721 | this->fpr.fpr[DecodeRS2(inst: FSWInst)] = bits; |
722 | decode = this->Decode(inst: FSWInst); |
723 | ASSERT_TRUE(decode.has_value()); |
724 | name = decode->pattern.name; |
725 | ASSERT_EQ(name, "FSW" ); |
726 | ASSERT_TRUE(this->Execute(*decode, false)); |
727 | ASSERT_EQ(*(uint32_t *)(this->memory + 16), bits); |
728 | } |
729 | |
730 | TEST_F(RISCVEmulatorTester, TestDoubleLSInst) { |
731 | uint32_t FLDInst = 0x1B207; // imm = 0 |
732 | uint32_t FSDInst = 0x21B827; // imm = 16 |
733 | |
734 | APFloat apf(12.0); |
735 | uint64_t bits = apf.bitcastToAPInt().getZExtValue(); |
736 | |
737 | *(uint64_t *)this->memory = bits; |
738 | auto decode = this->Decode(inst: FLDInst); |
739 | ASSERT_TRUE(decode.has_value()); |
740 | std::string name = decode->pattern.name; |
741 | ASSERT_EQ(name, "FLD" ); |
742 | ASSERT_TRUE(this->Execute(*decode, false)); |
743 | ASSERT_EQ(this->fpr.fpr[DecodeRD(FLDInst)], bits); |
744 | |
745 | this->fpr.fpr[DecodeRS2(inst: FSDInst)] = bits; |
746 | decode = this->Decode(inst: FSDInst); |
747 | ASSERT_TRUE(decode.has_value()); |
748 | name = decode->pattern.name; |
749 | ASSERT_EQ(name, "FSD" ); |
750 | ASSERT_TRUE(this->Execute(*decode, false)); |
751 | ASSERT_EQ(*(uint64_t *)(this->memory + 16), bits); |
752 | } |
753 | |
754 | TEST_F(RISCVEmulatorTester, TestFMV_X_WInst) { |
755 | auto FMV_X_WInst = 0xE0018253; |
756 | |
757 | APFloat apf(12.0f); |
758 | auto exp_bits = apf.bitcastToAPInt().getZExtValue(); |
759 | this->fpr.fpr[DecodeRS1(inst: FMV_X_WInst)] = NanBoxing(val: exp_bits); |
760 | auto decode = this->Decode(inst: FMV_X_WInst); |
761 | ASSERT_TRUE(decode.has_value()); |
762 | std::string name = decode->pattern.name; |
763 | ASSERT_EQ(name, "FMV_X_W" ); |
764 | ASSERT_TRUE(this->Execute(*decode, false)); |
765 | ASSERT_EQ(this->gpr.gpr[DecodeRD(FMV_X_WInst)], exp_bits); |
766 | } |
767 | |
768 | TEST_F(RISCVEmulatorTester, TestFMV_X_DInst) { |
769 | auto FMV_X_DInst = 0xE2018253; |
770 | |
771 | APFloat apf(12.0); |
772 | auto exp_bits = apf.bitcastToAPInt().getZExtValue(); |
773 | this->fpr.fpr[DecodeRS1(inst: FMV_X_DInst)] = exp_bits; |
774 | auto decode = this->Decode(inst: FMV_X_DInst); |
775 | ASSERT_TRUE(decode.has_value()); |
776 | std::string name = decode->pattern.name; |
777 | ASSERT_EQ(name, "FMV_X_D" ); |
778 | ASSERT_TRUE(this->Execute(*decode, false)); |
779 | ASSERT_EQ(this->gpr.gpr[DecodeRD(FMV_X_DInst)], exp_bits); |
780 | } |
781 | |
782 | TEST_F(RISCVEmulatorTester, TestFMV_W_XInst) { |
783 | auto FMV_W_XInst = 0xF0018253; |
784 | |
785 | APFloat apf(12.0f); |
786 | uint64_t exp_bits = NanUnBoxing(val: apf.bitcastToAPInt().getZExtValue()); |
787 | this->gpr.gpr[DecodeRS1(inst: FMV_W_XInst)] = exp_bits; |
788 | auto decode = this->Decode(inst: FMV_W_XInst); |
789 | ASSERT_TRUE(decode.has_value()); |
790 | std::string name = decode->pattern.name; |
791 | ASSERT_EQ(name, "FMV_W_X" ); |
792 | ASSERT_TRUE(this->Execute(*decode, false)); |
793 | ASSERT_EQ(this->fpr.fpr[DecodeRD(FMV_W_XInst)], exp_bits); |
794 | } |
795 | |
796 | TEST_F(RISCVEmulatorTester, TestFMV_D_XInst) { |
797 | auto FMV_D_XInst = 0xF2018253; |
798 | |
799 | APFloat apf(12.0); |
800 | uint64_t bits = apf.bitcastToAPInt().getZExtValue(); |
801 | this->gpr.gpr[DecodeRS1(inst: FMV_D_XInst)] = bits; |
802 | auto decode = this->Decode(inst: FMV_D_XInst); |
803 | ASSERT_TRUE(decode.has_value()); |
804 | std::string name = decode->pattern.name; |
805 | ASSERT_EQ(name, "FMV_D_X" ); |
806 | ASSERT_TRUE(this->Execute(*decode, false)); |
807 | ASSERT_EQ(this->fpr.fpr[DecodeRD(FMV_D_XInst)], bits); |
808 | } |
809 | |