1//===-- TestLoongArchEmulator.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 "lldb/Core/Address.h"
10#include "lldb/Core/Disassembler.h"
11#include "lldb/Core/PluginManager.h"
12#include "lldb/Target/ExecutionContext.h"
13#include "lldb/Utility/ArchSpec.h"
14#include "lldb/Utility/RegisterValue.h"
15#include "gtest/gtest.h"
16
17#include "Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h"
18#include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h"
19#include "Plugins/Process/Utility/lldb-loongarch-register-enums.h"
20
21using namespace llvm;
22using namespace lldb;
23using namespace lldb_private;
24
25#define GEN_BCOND_TEST(bit, name, rj_val, rd_val_branched, rd_val_continued) \
26 TEST_F(LoongArch##bit##EmulatorTester, test##name##branched) { \
27 testBcondBranch(this, name, true, rj_val, rd_val_branched); \
28 } \
29 TEST_F(LoongArch##bit##EmulatorTester, test##name##continued) { \
30 testBcondBranch(this, name, false, rj_val, rd_val_continued); \
31 }
32
33#define GEN_BZCOND_TEST(bit, name, rj_val_branched, rj_val_continued) \
34 TEST_F(LoongArch##bit##EmulatorTester, test##name##branched) { \
35 testBZcondBranch(this, name, true, rj_val_branched); \
36 } \
37 TEST_F(LoongArch##bit##EmulatorTester, test##name##continued) { \
38 testBZcondBranch(this, name, false, rj_val_continued); \
39 }
40
41#define GEN_BCZCOND_TEST(bit, name, cj_val_branched, cj_val_continued) \
42 TEST_F(LoongArch##bit##EmulatorTester, test##name##branched) { \
43 testBCZcondBranch(this, name, true, cj_val_branched); \
44 } \
45 TEST_F(LoongArch##bit##EmulatorTester, test##name##continued) { \
46 testBCZcondBranch(this, name, false, cj_val_continued); \
47 }
48
49struct LoongArch64EmulatorTester : public EmulateInstructionLoongArch,
50 testing::Test {
51 RegisterInfoPOSIX_loongarch64::GPR gpr;
52 RegisterInfoPOSIX_loongarch64::FPR fpr;
53
54 LoongArch64EmulatorTester(
55 std::string triple = "loongarch64-unknown-linux-gnu")
56 : EmulateInstructionLoongArch(ArchSpec(triple)) {
57 EmulateInstruction::SetReadRegCallback(ReadRegisterCallback);
58 EmulateInstruction::SetWriteRegCallback(WriteRegisterCallback);
59 }
60
61 static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
62 const RegisterInfo *reg_info,
63 RegisterValue &reg_value) {
64 LoongArch64EmulatorTester *tester =
65 (LoongArch64EmulatorTester *)instruction;
66 uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
67 if (reg >= gpr_r0_loongarch && reg <= gpr_r31_loongarch)
68 reg_value.SetUInt(uint: tester->gpr.gpr[reg], byte_size: reg_info->byte_size);
69 else if (reg == gpr_orig_a0_loongarch)
70 reg_value.SetUInt(uint: tester->gpr.orig_a0, byte_size: reg_info->byte_size);
71 else if (reg == gpr_pc_loongarch)
72 reg_value.SetUInt(uint: tester->gpr.csr_era, byte_size: reg_info->byte_size);
73 else if (reg == gpr_badv_loongarch)
74 reg_value.SetUInt(uint: tester->gpr.csr_badv, byte_size: reg_info->byte_size);
75 else if (reg == fpr_first_loongarch + 32)
76 // fcc0
77 reg_value.SetUInt(uint: tester->fpr.fcc, byte_size: reg_info->byte_size);
78 return true;
79 }
80
81 static bool WriteRegisterCallback(EmulateInstruction *instruction,
82 void *baton, const Context &context,
83 const RegisterInfo *reg_info,
84 const RegisterValue &reg_value) {
85 LoongArch64EmulatorTester *tester =
86 (LoongArch64EmulatorTester *)instruction;
87 uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
88 if (reg >= gpr_r0_loongarch && reg <= gpr_r31_loongarch)
89 tester->gpr.gpr[reg] = reg_value.GetAsUInt64();
90 else if (reg == gpr_orig_a0_loongarch)
91 tester->gpr.orig_a0 = reg_value.GetAsUInt64();
92 else if (reg == gpr_pc_loongarch)
93 tester->gpr.csr_era = reg_value.GetAsUInt64();
94 else if (reg == gpr_badv_loongarch)
95 tester->gpr.csr_badv = reg_value.GetAsUInt64();
96 return true;
97 }
98};
99
100// BEQ BNE BLT BGE BLTU BGEU
101static uint32_t EncodeBcondType(uint32_t opcode, uint32_t rj, uint32_t rd,
102 uint32_t offs16) {
103 offs16 = offs16 & 0x0000ffff;
104 return opcode << 26 | offs16 << 10 | rj << 5 | rd;
105}
106
107static uint32_t BEQ(uint32_t rj, uint32_t rd, int32_t offs16) {
108 return EncodeBcondType(opcode: 0b010110, rj, rd, offs16: uint32_t(offs16));
109}
110
111static uint32_t BNE(uint32_t rj, uint32_t rd, int32_t offs16) {
112 return EncodeBcondType(opcode: 0b010111, rj, rd, offs16: uint32_t(offs16));
113}
114
115static uint32_t BLT(uint32_t rj, uint32_t rd, int32_t offs16) {
116 return EncodeBcondType(opcode: 0b011000, rj, rd, offs16: uint32_t(offs16));
117}
118
119static uint32_t BGE(uint32_t rj, uint32_t rd, int32_t offs16) {
120 return EncodeBcondType(opcode: 0b011001, rj, rd, offs16: uint32_t(offs16));
121}
122
123static uint32_t BLTU(uint32_t rj, uint32_t rd, int32_t offs16) {
124 return EncodeBcondType(opcode: 0b011010, rj, rd, offs16: uint32_t(offs16));
125}
126
127static uint32_t BGEU(uint32_t rj, uint32_t rd, int32_t offs16) {
128 return EncodeBcondType(opcode: 0b011011, rj, rd, offs16: uint32_t(offs16));
129}
130
131// BEQZ BNEZ
132static uint32_t EncodeBZcondType(uint32_t opcode, uint32_t rj,
133 uint32_t offs21) {
134 uint32_t offs20_16 = (offs21 & 0x001f0000) >> 16;
135 uint32_t offs15_0 = offs21 & 0x0000ffff;
136 return opcode << 26 | offs15_0 << 10 | rj << 5 | offs20_16;
137}
138
139static uint32_t BEQZ(uint32_t rj, int32_t offs21) {
140 return EncodeBZcondType(opcode: 0b010000, rj, offs21: uint32_t(offs21));
141}
142
143static uint32_t BNEZ(uint32_t rj, int32_t offs21) {
144 return EncodeBZcondType(opcode: 0b010001, rj, offs21: uint32_t(offs21));
145}
146
147// BCEQZ BCNEZ
148static uint32_t EncodeBCZcondType(uint32_t opcode, uint8_t cj,
149 uint32_t offs21) {
150 uint32_t offs20_16 = (offs21 & 0x001f0000) >> 16;
151 uint32_t offs15_0 = offs21 & 0x0000ffff;
152 return (opcode >> 2) << 26 | offs15_0 << 10 | (opcode & 0b11) << 8 | cj << 5 |
153 offs20_16;
154}
155
156static uint32_t BCEQZ(uint8_t cj, int32_t offs21) {
157 return EncodeBCZcondType(opcode: 0b01001000, cj, offs21: uint32_t(offs21));
158}
159
160static uint32_t BCNEZ(uint8_t cj, int32_t offs21) {
161 return EncodeBCZcondType(opcode: 0b01001001, cj, offs21: uint32_t(offs21));
162}
163
164using EncoderBcond = uint32_t (*)(uint32_t rj, uint32_t rd, int32_t offs16);
165using EncoderBZcond = uint32_t (*)(uint32_t rj, int32_t offs21);
166using EncoderBCZcond = uint32_t (*)(uint8_t cj, int32_t offs21);
167
168TEST_F(LoongArch64EmulatorTester, testJIRL) {
169 bool success = false;
170 addr_t old_pc = 0x12000600;
171 WritePC(pc: old_pc);
172 // JIRL r1, r12, 0x10
173 // | 31 26 | 25 10 | 9 5 | 4 0 |
174 // | 0 1 0 0 1 1 | 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 | 0 1 1 0 0 | 0 0 0 0 1 |
175 uint32_t inst = 0b01001100000000000100000110000001;
176 uint32_t offs16 = 0x10;
177 gpr.gpr[12] = 0x12000400;
178 ASSERT_TRUE(TestExecute(inst));
179 auto r1 = gpr.gpr[1];
180 auto pc = ReadPC(success: &success);
181 ASSERT_TRUE(success);
182 ASSERT_EQ(r1, old_pc + 4);
183 ASSERT_EQ(pc, gpr.gpr[12] + (offs16 * 4));
184}
185
186TEST_F(LoongArch64EmulatorTester, testB) {
187 bool success = false;
188 addr_t old_pc = 0x12000600;
189 WritePC(pc: old_pc);
190 // B 0x10010
191 // | 31 26 | 25 10 | 9 0 |
192 // | 0 1 0 1 0 0 | 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 | 0 0 0 0 0 0 0 0 0 1 |
193 uint32_t inst = 0b01010000000000000100000000000001;
194 uint32_t offs26 = 0x10010;
195 ASSERT_TRUE(TestExecute(inst));
196 auto pc = ReadPC(success: &success);
197 ASSERT_TRUE(success);
198 ASSERT_EQ(pc, old_pc + (offs26 * 4));
199}
200
201TEST_F(LoongArch64EmulatorTester, testBL) {
202 bool success = false;
203 addr_t old_pc = 0x12000600;
204 WritePC(pc: old_pc);
205 // BL 0x10010
206 // | 31 26 | 25 10 | 9 0 |
207 // | 0 1 0 1 0 1 | 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 | 0 0 0 0 0 0 0 0 0 1 |
208 uint32_t inst = 0b01010100000000000100000000000001;
209 uint32_t offs26 = 0x10010;
210 ASSERT_TRUE(TestExecute(inst));
211 auto r1 = gpr.gpr[1];
212 auto pc = ReadPC(success: &success);
213 ASSERT_TRUE(success);
214 ASSERT_EQ(r1, old_pc + 4);
215 ASSERT_EQ(pc, old_pc + (offs26 * 4));
216}
217
218static void testBcondBranch(LoongArch64EmulatorTester *tester,
219 EncoderBcond encoder, bool branched,
220 uint64_t rj_val, uint64_t rd_val) {
221 bool success = false;
222 addr_t old_pc = 0x12000600;
223 tester->WritePC(pc: old_pc);
224 tester->gpr.gpr[12] = rj_val;
225 tester->gpr.gpr[13] = rd_val;
226 // b<cmp> r12, r13, (-256)
227 uint32_t inst = encoder(12, 13, -256);
228 ASSERT_TRUE(tester->TestExecute(inst));
229 auto pc = tester->ReadPC(success: &success);
230 ASSERT_TRUE(success);
231 ASSERT_EQ(pc, old_pc + (branched ? (-256 * 4) : 4));
232}
233
234static void testBZcondBranch(LoongArch64EmulatorTester *tester,
235 EncoderBZcond encoder, bool branched,
236 uint64_t rj_val) {
237 bool success = false;
238 addr_t old_pc = 0x12000600;
239 tester->WritePC(pc: old_pc);
240 tester->gpr.gpr[4] = rj_val;
241 // b<cmp>z r4, (-256)
242 uint32_t inst = encoder(4, -256);
243 ASSERT_TRUE(tester->TestExecute(inst));
244 auto pc = tester->ReadPC(success: &success);
245 ASSERT_TRUE(success);
246 ASSERT_EQ(pc, old_pc + (branched ? (-256 * 4) : 4));
247}
248
249static void testBCZcondBranch(LoongArch64EmulatorTester *tester,
250 EncoderBCZcond encoder, bool branched,
251 uint32_t cj_val) {
252 bool success = false;
253 addr_t old_pc = 0x12000600;
254 tester->WritePC(pc: old_pc);
255 tester->fpr.fcc = cj_val;
256 // bc<cmp>z fcc0, 256
257 uint32_t inst = encoder(0, 256);
258 ASSERT_TRUE(tester->TestExecute(inst));
259 auto pc = tester->ReadPC(success: &success);
260 ASSERT_TRUE(success);
261 ASSERT_EQ(pc, old_pc + (branched ? (256 * 4) : 4));
262}
263
264GEN_BCOND_TEST(64, BEQ, 1, 1, 0)
265GEN_BCOND_TEST(64, BNE, 1, 0, 1)
266GEN_BCOND_TEST(64, BLT, -2, 1, -3)
267GEN_BCOND_TEST(64, BGE, -2, -3, 1)
268GEN_BCOND_TEST(64, BLTU, -2, -1, 1)
269GEN_BCOND_TEST(64, BGEU, -2, 1, -1)
270GEN_BZCOND_TEST(64, BEQZ, 0, 1)
271GEN_BZCOND_TEST(64, BNEZ, 1, 0)
272GEN_BCZCOND_TEST(64, BCEQZ, 0, 1)
273GEN_BCZCOND_TEST(64, BCNEZ, 1, 0)
274

source code of lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp