268 lines · cpp
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-exception6//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 ®_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 // fcc077 reg_value.SetUInt(tester->fpr.fcc, 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 ®_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 BGEU101static 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(0b010110, rj, rd, uint32_t(offs16));109}110 111static uint32_t BNE(uint32_t rj, uint32_t rd, int32_t offs16) {112 return EncodeBcondType(0b010111, rj, rd, uint32_t(offs16));113}114 115static uint32_t BLT(uint32_t rj, uint32_t rd, int32_t offs16) {116 return EncodeBcondType(0b011000, rj, rd, uint32_t(offs16));117}118 119static uint32_t BGE(uint32_t rj, uint32_t rd, int32_t offs16) {120 return EncodeBcondType(0b011001, rj, rd, uint32_t(offs16));121}122 123static uint32_t BLTU(uint32_t rj, uint32_t rd, int32_t offs16) {124 return EncodeBcondType(0b011010, rj, rd, uint32_t(offs16));125}126 127static 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 BNEZ132static 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(0b010000, rj, uint32_t(offs21));141}142 143static uint32_t BNEZ(uint32_t rj, int32_t offs21) {144 return EncodeBZcondType(0b010001, rj, uint32_t(offs21));145}146 147// BCEQZ BCNEZ148static 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(0b01001000, cj, uint32_t(offs21));158}159 160static uint32_t BCNEZ(uint8_t cj, int32_t offs21) {161 return EncodeBCZcondType(0b01001001, cj, 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 addr_t old_pc = 0x12000600;170 WritePC(old_pc);171 // JIRL r1, r12, 0x10172 // | 31 26 | 25 10 | 9 5 | 4 0 |173 // | 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 |174 uint32_t inst = 0b01001100000000000100000110000001;175 uint32_t offs16 = 0x10;176 gpr.gpr[12] = 0x12000400;177 ASSERT_TRUE(TestExecute(inst));178 auto r1 = gpr.gpr[1];179 auto pc = ReadPC();180 ASSERT_TRUE(pc);181 ASSERT_EQ(r1, old_pc + 4);182 ASSERT_EQ(*pc, gpr.gpr[12] + (offs16 * 4));183}184 185TEST_F(LoongArch64EmulatorTester, testB) {186 addr_t old_pc = 0x12000600;187 WritePC(old_pc);188 // B 0x10010189 // | 31 26 | 25 10 | 9 0 |190 // | 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 |191 uint32_t inst = 0b01010000000000000100000000000001;192 uint32_t offs26 = 0x10010;193 ASSERT_TRUE(TestExecute(inst));194 auto pc = ReadPC();195 ASSERT_TRUE(pc);196 ASSERT_EQ(*pc, old_pc + (offs26 * 4));197}198 199TEST_F(LoongArch64EmulatorTester, testBL) {200 addr_t old_pc = 0x12000600;201 WritePC(old_pc);202 // BL 0x10010203 // | 31 26 | 25 10 | 9 0 |204 // | 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 |205 uint32_t inst = 0b01010100000000000100000000000001;206 uint32_t offs26 = 0x10010;207 ASSERT_TRUE(TestExecute(inst));208 auto r1 = gpr.gpr[1];209 auto pc = ReadPC();210 ASSERT_TRUE(pc);211 ASSERT_EQ(r1, old_pc + 4);212 ASSERT_EQ(*pc, old_pc + (offs26 * 4));213}214 215static void testBcondBranch(LoongArch64EmulatorTester *tester,216 EncoderBcond encoder, bool branched,217 uint64_t rj_val, uint64_t rd_val) {218 addr_t old_pc = 0x12000600;219 tester->WritePC(old_pc);220 tester->gpr.gpr[12] = rj_val;221 tester->gpr.gpr[13] = rd_val;222 // b<cmp> r12, r13, (-256)223 uint32_t inst = encoder(12, 13, -256);224 ASSERT_TRUE(tester->TestExecute(inst));225 auto pc = tester->ReadPC();226 ASSERT_TRUE(pc);227 ASSERT_EQ(*pc, old_pc + (branched ? (-256 * 4) : 4));228}229 230static void testBZcondBranch(LoongArch64EmulatorTester *tester,231 EncoderBZcond encoder, bool branched,232 uint64_t rj_val) {233 addr_t old_pc = 0x12000600;234 tester->WritePC(old_pc);235 tester->gpr.gpr[4] = rj_val;236 // b<cmp>z r4, (-256)237 uint32_t inst = encoder(4, -256);238 ASSERT_TRUE(tester->TestExecute(inst));239 auto pc = tester->ReadPC();240 ASSERT_TRUE(pc);241 ASSERT_EQ(*pc, old_pc + (branched ? (-256 * 4) : 4));242}243 244static void testBCZcondBranch(LoongArch64EmulatorTester *tester,245 EncoderBCZcond encoder, bool branched,246 uint32_t cj_val) {247 addr_t old_pc = 0x12000600;248 tester->WritePC(old_pc);249 tester->fpr.fcc = cj_val;250 // bc<cmp>z fcc0, 256251 uint32_t inst = encoder(0, 256);252 ASSERT_TRUE(tester->TestExecute(inst));253 auto pc = tester->ReadPC();254 ASSERT_TRUE(pc);255 ASSERT_EQ(*pc, old_pc + (branched ? (256 * 4) : 4));256}257 258GEN_BCOND_TEST(64, BEQ, 1, 1, 0)259GEN_BCOND_TEST(64, BNE, 1, 0, 1)260GEN_BCOND_TEST(64, BLT, -2, 1, -3)261GEN_BCOND_TEST(64, BGE, -2, -3, 1)262GEN_BCOND_TEST(64, BLTU, -2, -1, 1)263GEN_BCOND_TEST(64, BGEU, -2, 1, -1)264GEN_BZCOND_TEST(64, BEQZ, 0, 1)265GEN_BZCOND_TEST(64, BNEZ, 1, 0)266GEN_BCZCOND_TEST(64, BCEQZ, 0, 1)267GEN_BCZCOND_TEST(64, BCNEZ, 1, 0)268