xref: /llvm-project/lldb/unittests/Instruction/LoongArch/TestLoongArchEmulator.cpp (revision 52731683c9a2f4f8138c317201ea68eaedca7225)
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 
21 using namespace llvm;
22 using namespace lldb;
23 using 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 
49 struct LoongArch64EmulatorTester : public EmulateInstructionLoongArch,
50                                    testing::Test {
51   RegisterInfoPOSIX_loongarch64::GPR gpr;
52   RegisterInfoPOSIX_loongarch64::FPR fpr;
53 
LoongArch64EmulatorTesterLoongArch64EmulatorTester54   LoongArch64EmulatorTester(
55       std::string triple = "loongarch64-unknown-linux-gnu")
56       : EmulateInstructionLoongArch(ArchSpec(triple)) {
57     EmulateInstruction::SetReadRegCallback(ReadRegisterCallback);
58     EmulateInstruction::SetWriteRegCallback(WriteRegisterCallback);
59   }
60 
ReadRegisterCallbackLoongArch64EmulatorTester61   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(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)
76       // fcc0
77       reg_value.SetUInt(tester->fpr.fcc, reg_info->byte_size);
78     return true;
79   }
80 
WriteRegisterCallbackLoongArch64EmulatorTester81   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
EncodeBcondType(uint32_t opcode,uint32_t rj,uint32_t rd,uint32_t offs16)101 static 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 
BEQ(uint32_t rj,uint32_t rd,int32_t offs16)107 static uint32_t BEQ(uint32_t rj, uint32_t rd, int32_t offs16) {
108   return EncodeBcondType(0b010110, rj, rd, uint32_t(offs16));
109 }
110 
BNE(uint32_t rj,uint32_t rd,int32_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));
113 }
114 
BLT(uint32_t rj,uint32_t rd,int32_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));
117 }
118 
BGE(uint32_t rj,uint32_t rd,int32_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));
121 }
122 
BLTU(uint32_t rj,uint32_t rd,int32_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));
125 }
126 
BGEU(uint32_t rj,uint32_t rd,int32_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));
129 }
130 
131 // BEQZ BNEZ
EncodeBZcondType(uint32_t opcode,uint32_t rj,uint32_t offs21)132 static 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 
BEQZ(uint32_t rj,int32_t offs21)139 static uint32_t BEQZ(uint32_t rj, int32_t offs21) {
140   return EncodeBZcondType(0b010000, rj, uint32_t(offs21));
141 }
142 
BNEZ(uint32_t rj,int32_t offs21)143 static uint32_t BNEZ(uint32_t rj, int32_t offs21) {
144   return EncodeBZcondType(0b010001, rj, uint32_t(offs21));
145 }
146 
147 // BCEQZ BCNEZ
EncodeBCZcondType(uint32_t opcode,uint8_t cj,uint32_t offs21)148 static 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 
BCEQZ(uint8_t cj,int32_t offs21)156 static uint32_t BCEQZ(uint8_t cj, int32_t offs21) {
157   return EncodeBCZcondType(0b01001000, cj, uint32_t(offs21));
158 }
159 
BCNEZ(uint8_t cj,int32_t offs21)160 static uint32_t BCNEZ(uint8_t cj, int32_t offs21) {
161   return EncodeBCZcondType(0b01001001, cj, uint32_t(offs21));
162 }
163 
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);
167 
TEST_F(LoongArch64EmulatorTester,testJIRL)168 TEST_F(LoongArch64EmulatorTester, testJIRL) {
169   bool success = false;
170   addr_t old_pc = 0x12000600;
171   WritePC(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);
181   ASSERT_TRUE(success);
182   ASSERT_EQ(r1, old_pc + 4);
183   ASSERT_EQ(pc, gpr.gpr[12] + (offs16 * 4));
184 }
185 
TEST_F(LoongArch64EmulatorTester,testB)186 TEST_F(LoongArch64EmulatorTester, testB) {
187   bool success = false;
188   addr_t old_pc = 0x12000600;
189   WritePC(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);
197   ASSERT_TRUE(success);
198   ASSERT_EQ(pc, old_pc + (offs26 * 4));
199 }
200 
TEST_F(LoongArch64EmulatorTester,testBL)201 TEST_F(LoongArch64EmulatorTester, testBL) {
202   bool success = false;
203   addr_t old_pc = 0x12000600;
204   WritePC(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);
213   ASSERT_TRUE(success);
214   ASSERT_EQ(r1, old_pc + 4);
215   ASSERT_EQ(pc, old_pc + (offs26 * 4));
216 }
217 
testBcondBranch(LoongArch64EmulatorTester * tester,EncoderBcond encoder,bool branched,uint64_t rj_val,uint64_t rd_val)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));
232 }
233 
testBZcondBranch(LoongArch64EmulatorTester * tester,EncoderBZcond encoder,bool branched,uint64_t rj_val)234 static 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(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));
247 }
248 
testBCZcondBranch(LoongArch64EmulatorTester * tester,EncoderBCZcond encoder,bool branched,uint32_t cj_val)249 static 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(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));
262 }
263 
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)
274