1 //===-- TestLoongArchEmulator.cpp -----------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
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"
23 using namespace lldb_private
;
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); \
29 TEST_F(LoongArch##bit##EmulatorTester, test##name##continued) { \
30 testBcondBranch(this, name, false, rj_val, rd_val_continued); \
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); \
37 TEST_F(LoongArch##bit##EmulatorTester, test##name##continued) { \
38 testBZcondBranch(this, name, false, rj_val_continued); \
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); \
45 TEST_F(LoongArch##bit##EmulatorTester, test##name##continued) { \
46 testBCZcondBranch(this, name, false, cj_val_continued); \
49 struct LoongArch64EmulatorTester
: public EmulateInstructionLoongArch
,
51 RegisterInfoPOSIX_loongarch64::GPR gpr
;
52 RegisterInfoPOSIX_loongarch64::FPR fpr
;
54 LoongArch64EmulatorTester(
55 std::string triple
= "loongarch64-unknown-linux-gnu")
56 : EmulateInstructionLoongArch(ArchSpec(triple
)) {
57 EmulateInstruction::SetReadRegCallback(ReadRegisterCallback
);
58 EmulateInstruction::SetWriteRegCallback(WriteRegisterCallback
);
61 static bool ReadRegisterCallback(EmulateInstruction
*instruction
, void *baton
,
62 const RegisterInfo
*reg_info
,
63 RegisterValue
®_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(tester
->gpr
.gpr
[reg
], reg_info
->byte_size
);
69 else if (reg
== gpr_orig_a0_loongarch
)
70 reg_value
.SetUInt(tester
->gpr
.orig_a0
, reg_info
->byte_size
);
71 else if (reg
== gpr_pc_loongarch
)
72 reg_value
.SetUInt(tester
->gpr
.csr_era
, reg_info
->byte_size
);
73 else if (reg
== gpr_badv_loongarch
)
74 reg_value
.SetUInt(tester
->gpr
.csr_badv
, reg_info
->byte_size
);
75 else if (reg
== fpr_first_loongarch
+ 32)
77 reg_value
.SetUInt(tester
->fpr
.fcc
, reg_info
->byte_size
);
81 static bool WriteRegisterCallback(EmulateInstruction
*instruction
,
82 void *baton
, const Context
&context
,
83 const RegisterInfo
*reg_info
,
84 const RegisterValue
®_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();
100 // BEQ BNE BLT BGE BLTU BGEU
101 static uint32_t EncodeBcondType(uint32_t opcode
, uint32_t rj
, uint32_t rd
,
103 offs16
= offs16
& 0x0000ffff;
104 return opcode
<< 26 | offs16
<< 10 | rj
<< 5 | rd
;
107 static uint32_t BEQ(uint32_t rj
, uint32_t rd
, int32_t offs16
) {
108 return EncodeBcondType(0b010110, rj
, rd
, uint32_t(offs16
));
111 static uint32_t BNE(uint32_t rj
, uint32_t rd
, int32_t offs16
) {
112 return EncodeBcondType(0b010111, rj
, rd
, uint32_t(offs16
));
115 static uint32_t BLT(uint32_t rj
, uint32_t rd
, int32_t offs16
) {
116 return EncodeBcondType(0b011000, rj
, rd
, uint32_t(offs16
));
119 static uint32_t BGE(uint32_t rj
, uint32_t rd
, int32_t offs16
) {
120 return EncodeBcondType(0b011001, rj
, rd
, uint32_t(offs16
));
123 static uint32_t BLTU(uint32_t rj
, uint32_t rd
, int32_t offs16
) {
124 return EncodeBcondType(0b011010, rj
, rd
, uint32_t(offs16
));
127 static uint32_t BGEU(uint32_t rj
, uint32_t rd
, int32_t offs16
) {
128 return EncodeBcondType(0b011011, rj
, rd
, uint32_t(offs16
));
132 static uint32_t EncodeBZcondType(uint32_t opcode
, uint32_t rj
,
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
;
139 static uint32_t BEQZ(uint32_t rj
, int32_t offs21
) {
140 return EncodeBZcondType(0b010000, rj
, uint32_t(offs21
));
143 static uint32_t BNEZ(uint32_t rj
, int32_t offs21
) {
144 return EncodeBZcondType(0b010001, rj
, uint32_t(offs21
));
148 static uint32_t EncodeBCZcondType(uint32_t opcode
, uint8_t cj
,
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 |
156 static uint32_t BCEQZ(uint8_t cj
, int32_t offs21
) {
157 return EncodeBCZcondType(0b01001000, cj
, uint32_t(offs21
));
160 static uint32_t BCNEZ(uint8_t cj
, int32_t offs21
) {
161 return EncodeBCZcondType(0b01001001, cj
, uint32_t(offs21
));
164 using EncoderBcond
= uint32_t (*)(uint32_t rj
, uint32_t rd
, int32_t offs16
);
165 using EncoderBZcond
= uint32_t (*)(uint32_t rj
, int32_t offs21
);
166 using EncoderBCZcond
= uint32_t (*)(uint8_t cj
, int32_t offs21
);
168 TEST_F(LoongArch64EmulatorTester
, testJIRL
) {
169 bool success
= false;
170 addr_t old_pc
= 0x12000600;
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
);
181 ASSERT_TRUE(success
);
182 ASSERT_EQ(r1
, old_pc
+ 4);
183 ASSERT_EQ(pc
, gpr
.gpr
[12] + (offs16
* 4));
186 TEST_F(LoongArch64EmulatorTester
, testB
) {
187 bool success
= false;
188 addr_t old_pc
= 0x12000600;
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
);
197 ASSERT_TRUE(success
);
198 ASSERT_EQ(pc
, old_pc
+ (offs26
* 4));
201 TEST_F(LoongArch64EmulatorTester
, testBL
) {
202 bool success
= false;
203 addr_t old_pc
= 0x12000600;
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
);
213 ASSERT_TRUE(success
);
214 ASSERT_EQ(r1
, old_pc
+ 4);
215 ASSERT_EQ(pc
, old_pc
+ (offs26
* 4));
218 static 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(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
);
230 ASSERT_TRUE(success
);
231 ASSERT_EQ(pc
, old_pc
+ (branched
? (-256 * 4) : 4));
234 static void testBZcondBranch(LoongArch64EmulatorTester
*tester
,
235 EncoderBZcond encoder
, bool branched
,
237 bool success
= false;
238 addr_t old_pc
= 0x12000600;
239 tester
->WritePC(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
);
245 ASSERT_TRUE(success
);
246 ASSERT_EQ(pc
, old_pc
+ (branched
? (-256 * 4) : 4));
249 static void testBCZcondBranch(LoongArch64EmulatorTester
*tester
,
250 EncoderBCZcond encoder
, bool branched
,
252 bool success
= false;
253 addr_t old_pc
= 0x12000600;
254 tester
->WritePC(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
);
260 ASSERT_TRUE(success
);
261 ASSERT_EQ(pc
, old_pc
+ (branched
? (256 * 4) : 4));
264 GEN_BCOND_TEST(64, BEQ
, 1, 1, 0)
265 GEN_BCOND_TEST(64, BNE
, 1, 0, 1)
266 GEN_BCOND_TEST(64, BLT
, -2, 1, -3)
267 GEN_BCOND_TEST(64, BGE
, -2, -3, 1)
268 GEN_BCOND_TEST(64, BLTU
, -2, -1, 1)
269 GEN_BCOND_TEST(64, BGEU
, -2, 1, -1)
270 GEN_BZCOND_TEST(64, BEQZ
, 0, 1)
271 GEN_BZCOND_TEST(64, BNEZ
, 1, 0)
272 GEN_BCZCOND_TEST(64, BCEQZ
, 0, 1)
273 GEN_BCZCOND_TEST(64, BCNEZ
, 1, 0)