1//===---EmulateInstructionLoongArch.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 <cstdlib> 10#include <optional> 11 12#include "EmulateInstructionLoongArch.h" 13#include "Plugins/Process/Utility/InstructionUtils.h" 14#include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h" 15#include "Plugins/Process/Utility/lldb-loongarch-register-enums.h" 16#include "lldb/Core/Address.h" 17#include "lldb/Core/PluginManager.h" 18#include "lldb/Interpreter/OptionValueArray.h" 19#include "lldb/Interpreter/OptionValueDictionary.h" 20#include "lldb/Symbol/UnwindPlan.h" 21#include "lldb/Utility/ArchSpec.h" 22#include "lldb/Utility/LLDBLog.h" 23#include "lldb/Utility/RegisterValue.h" 24#include "lldb/Utility/Stream.h" 25#include "llvm/ADT/STLExtras.h" 26#include "llvm/Support/MathExtras.h" 27 28using namespace lldb; 29using namespace lldb_private; 30 31LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionLoongArch, InstructionLoongArch) 32 33namespace lldb_private { 34 35EmulateInstructionLoongArch::Opcode * 36EmulateInstructionLoongArch::GetOpcodeForInstruction(uint32_t inst) { 37 // TODO: Add the mask for other instruction. 38 static EmulateInstructionLoongArch::Opcode g_opcodes[] = { 39 {0xfc000000, 0x40000000, &EmulateInstructionLoongArch::EmulateBEQZ, 40 "beqz rj, offs21"}, 41 {0xfc000000, 0x44000000, &EmulateInstructionLoongArch::EmulateBNEZ, 42 "bnez rj, offs21"}, 43 {0xfc000300, 0x48000000, &EmulateInstructionLoongArch::EmulateBCEQZ, 44 "bceqz cj, offs21"}, 45 {0xfc000300, 0x48000100, &EmulateInstructionLoongArch::EmulateBCNEZ, 46 "bcnez cj, offs21"}, 47 {0xfc000000, 0x4c000000, &EmulateInstructionLoongArch::EmulateJIRL, 48 "jirl rd, rj, offs16"}, 49 {0xfc000000, 0x50000000, &EmulateInstructionLoongArch::EmulateB, 50 " b offs26"}, 51 {0xfc000000, 0x54000000, &EmulateInstructionLoongArch::EmulateBL, 52 "bl offs26"}, 53 {0xfc000000, 0x58000000, &EmulateInstructionLoongArch::EmulateBEQ, 54 "beq rj, rd, offs16"}, 55 {0xfc000000, 0x5c000000, &EmulateInstructionLoongArch::EmulateBNE, 56 "bne rj, rd, offs16"}, 57 {0xfc000000, 0x60000000, &EmulateInstructionLoongArch::EmulateBLT, 58 "blt rj, rd, offs16"}, 59 {0xfc000000, 0x64000000, &EmulateInstructionLoongArch::EmulateBGE, 60 "bge rj, rd, offs16"}, 61 {0xfc000000, 0x68000000, &EmulateInstructionLoongArch::EmulateBLTU, 62 "bltu rj, rd, offs16"}, 63 {0xfc000000, 0x6c000000, &EmulateInstructionLoongArch::EmulateBGEU, 64 "bgeu rj, rd, offs16"}, 65 {0x00000000, 0x00000000, &EmulateInstructionLoongArch::EmulateNonJMP, 66 "NonJMP"}}; 67 static const size_t num_loongarch_opcodes = std::size(g_opcodes); 68 69 for (size_t i = 0; i < num_loongarch_opcodes; ++i) 70 if ((g_opcodes[i].mask & inst) == g_opcodes[i].value) 71 return &g_opcodes[i]; 72 return nullptr; 73} 74 75bool EmulateInstructionLoongArch::TestExecute(uint32_t inst) { 76 Opcode *opcode_data = GetOpcodeForInstruction(inst); 77 if (!opcode_data) 78 return false; 79 // Call the Emulate... function. 80 if (!(this->*opcode_data->callback)(inst)) 81 return false; 82 return true; 83} 84 85bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) { 86 uint32_t inst_size = m_opcode.GetByteSize(); 87 uint32_t inst = m_opcode.GetOpcode32(); 88 bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC; 89 bool success = false; 90 91 Opcode *opcode_data = GetOpcodeForInstruction(inst); 92 if (!opcode_data) 93 return false; 94 95 lldb::addr_t old_pc = 0; 96 if (increase_pc) { 97 old_pc = ReadPC(&success); 98 if (!success) 99 return false; 100 } 101 102 // Call the Emulate... function. 103 if (!(this->*opcode_data->callback)(inst)) 104 return false; 105 106 if (increase_pc) { 107 lldb::addr_t new_pc = ReadPC(&success); 108 if (!success) 109 return false; 110 111 if (new_pc == old_pc && !WritePC(old_pc + inst_size)) 112 return false; 113 } 114 return true; 115} 116 117bool EmulateInstructionLoongArch::ReadInstruction() { 118 bool success = false; 119 m_addr = ReadPC(&success); 120 if (!success) { 121 m_addr = LLDB_INVALID_ADDRESS; 122 return false; 123 } 124 125 Context ctx; 126 ctx.type = eContextReadOpcode; 127 ctx.SetNoArgs(); 128 uint32_t inst = (uint32_t)ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success); 129 m_opcode.SetOpcode32(inst, GetByteOrder()); 130 131 return true; 132} 133 134lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) { 135 return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 136 LLDB_INVALID_ADDRESS, success); 137} 138 139bool EmulateInstructionLoongArch::WritePC(lldb::addr_t pc) { 140 EmulateInstruction::Context ctx; 141 ctx.type = eContextAdvancePC; 142 ctx.SetNoArgs(); 143 return WriteRegisterUnsigned(ctx, eRegisterKindGeneric, 144 LLDB_REGNUM_GENERIC_PC, pc); 145} 146 147std::optional<RegisterInfo> 148EmulateInstructionLoongArch::GetRegisterInfo(lldb::RegisterKind reg_kind, 149 uint32_t reg_index) { 150 if (reg_kind == eRegisterKindGeneric) { 151 switch (reg_index) { 152 case LLDB_REGNUM_GENERIC_PC: 153 reg_kind = eRegisterKindLLDB; 154 reg_index = gpr_pc_loongarch; 155 break; 156 case LLDB_REGNUM_GENERIC_SP: 157 reg_kind = eRegisterKindLLDB; 158 reg_index = gpr_sp_loongarch; 159 break; 160 case LLDB_REGNUM_GENERIC_FP: 161 reg_kind = eRegisterKindLLDB; 162 reg_index = gpr_fp_loongarch; 163 break; 164 case LLDB_REGNUM_GENERIC_RA: 165 reg_kind = eRegisterKindLLDB; 166 reg_index = gpr_ra_loongarch; 167 break; 168 // We may handle LLDB_REGNUM_GENERIC_ARGx when more instructions are 169 // supported. 170 default: 171 llvm_unreachable("unsupported register"); 172 } 173 } 174 175 const RegisterInfo *array = 176 RegisterInfoPOSIX_loongarch64::GetRegisterInfoPtr(m_arch); 177 const uint32_t length = 178 RegisterInfoPOSIX_loongarch64::GetRegisterInfoCount(m_arch); 179 180 if (reg_index >= length || reg_kind != eRegisterKindLLDB) 181 return {}; 182 return array[reg_index]; 183} 184 185bool EmulateInstructionLoongArch::SetTargetTriple(const ArchSpec &arch) { 186 return SupportsThisArch(arch); 187} 188 189bool EmulateInstructionLoongArch::TestEmulation( 190 Stream *out_stream, ArchSpec &arch, OptionValueDictionary *test_data) { 191 return false; 192} 193 194void EmulateInstructionLoongArch::Initialize() { 195 PluginManager::RegisterPlugin(GetPluginNameStatic(), 196 GetPluginDescriptionStatic(), CreateInstance); 197} 198 199void EmulateInstructionLoongArch::Terminate() { 200 PluginManager::UnregisterPlugin(CreateInstance); 201} 202 203lldb_private::EmulateInstruction * 204EmulateInstructionLoongArch::CreateInstance(const ArchSpec &arch, 205 InstructionType inst_type) { 206 if (EmulateInstructionLoongArch::SupportsThisInstructionType(inst_type) && 207 SupportsThisArch(arch)) 208 return new EmulateInstructionLoongArch(arch); 209 return nullptr; 210} 211 212bool EmulateInstructionLoongArch::SupportsThisArch(const ArchSpec &arch) { 213 return arch.GetTriple().isLoongArch(); 214} 215 216bool EmulateInstructionLoongArch::EmulateBEQZ(uint32_t inst) { 217 return IsLoongArch64() ? EmulateBEQZ64(inst) : false; 218} 219 220bool EmulateInstructionLoongArch::EmulateBNEZ(uint32_t inst) { 221 return IsLoongArch64() ? EmulateBNEZ64(inst) : false; 222} 223 224bool EmulateInstructionLoongArch::EmulateBCEQZ(uint32_t inst) { 225 return IsLoongArch64() ? EmulateBCEQZ64(inst) : false; 226} 227 228bool EmulateInstructionLoongArch::EmulateBCNEZ(uint32_t inst) { 229 return IsLoongArch64() ? EmulateBCNEZ64(inst) : false; 230} 231 232bool EmulateInstructionLoongArch::EmulateJIRL(uint32_t inst) { 233 return IsLoongArch64() ? EmulateJIRL64(inst) : false; 234} 235 236bool EmulateInstructionLoongArch::EmulateB(uint32_t inst) { 237 return IsLoongArch64() ? EmulateB64(inst) : false; 238} 239 240bool EmulateInstructionLoongArch::EmulateBL(uint32_t inst) { 241 return IsLoongArch64() ? EmulateBL64(inst) : false; 242} 243 244bool EmulateInstructionLoongArch::EmulateBEQ(uint32_t inst) { 245 return IsLoongArch64() ? EmulateBEQ64(inst) : false; 246} 247 248bool EmulateInstructionLoongArch::EmulateBNE(uint32_t inst) { 249 return IsLoongArch64() ? EmulateBNE64(inst) : false; 250} 251 252bool EmulateInstructionLoongArch::EmulateBLT(uint32_t inst) { 253 return IsLoongArch64() ? EmulateBLT64(inst) : false; 254} 255 256bool EmulateInstructionLoongArch::EmulateBGE(uint32_t inst) { 257 return IsLoongArch64() ? EmulateBGE64(inst) : false; 258} 259 260bool EmulateInstructionLoongArch::EmulateBLTU(uint32_t inst) { 261 return IsLoongArch64() ? EmulateBLTU64(inst) : false; 262} 263 264bool EmulateInstructionLoongArch::EmulateBGEU(uint32_t inst) { 265 return IsLoongArch64() ? EmulateBGEU64(inst) : false; 266} 267 268bool EmulateInstructionLoongArch::EmulateNonJMP(uint32_t inst) { return false; } 269 270// beqz rj, offs21 271// if GR[rj] == 0: 272// PC = PC + SignExtend({offs21, 2'b0}, GRLEN) 273bool EmulateInstructionLoongArch::EmulateBEQZ64(uint32_t inst) { 274 bool success = false; 275 uint32_t rj = Bits32(inst, 9, 5); 276 uint64_t pc = ReadPC(&success); 277 if (!success) 278 return false; 279 uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); 280 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 281 if (!success) 282 return false; 283 if (rj_val == 0) { 284 uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); 285 return WritePC(next_pc); 286 } else 287 return WritePC(pc + 4); 288} 289 290// bnez rj, offs21 291// if GR[rj] != 0: 292// PC = PC + SignExtend({offs21, 2'b0}, GRLEN) 293bool EmulateInstructionLoongArch::EmulateBNEZ64(uint32_t inst) { 294 bool success = false; 295 uint32_t rj = Bits32(inst, 9, 5); 296 uint64_t pc = ReadPC(&success); 297 if (!success) 298 return false; 299 uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); 300 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 301 if (!success) 302 return false; 303 if (rj_val != 0) { 304 uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); 305 return WritePC(next_pc); 306 } else 307 return WritePC(pc + 4); 308} 309 310// bceqz cj, offs21 311// if CFR[cj] == 0: 312// PC = PC + SignExtend({offs21, 2'b0}, GRLEN) 313bool EmulateInstructionLoongArch::EmulateBCEQZ64(uint32_t inst) { 314 bool success = false; 315 uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch; 316 uint64_t pc = ReadPC(&success); 317 if (!success) 318 return false; 319 uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); 320 uint8_t cj_val = 321 (uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success); 322 if (!success) 323 return false; 324 if (cj_val == 0) { 325 uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); 326 return WritePC(next_pc); 327 } else 328 return WritePC(pc + 4); 329 return false; 330} 331 332// bcnez cj, offs21 333// if CFR[cj] != 0: 334// PC = PC + SignExtend({offs21, 2'b0}, GRLEN) 335bool EmulateInstructionLoongArch::EmulateBCNEZ64(uint32_t inst) { 336 bool success = false; 337 uint32_t cj = Bits32(inst, 7, 5) + fpr_fcc0_loongarch; 338 uint64_t pc = ReadPC(&success); 339 if (!success) 340 return false; 341 uint32_t offs21 = Bits32(inst, 25, 10) + (Bits32(inst, 4, 0) << 16); 342 uint8_t cj_val = 343 (uint8_t)ReadRegisterUnsigned(eRegisterKindLLDB, cj, 0, &success); 344 if (!success) 345 return false; 346 if (cj_val != 0) { 347 uint64_t next_pc = pc + llvm::SignExtend64<23>(offs21 << 2); 348 return WritePC(next_pc); 349 } else 350 return WritePC(pc + 4); 351 return false; 352} 353 354// jirl rd, rj, offs16 355// GR[rd] = PC + 4 356// PC = GR[rj] + SignExtend({offs16, 2'b0}, GRLEN) 357bool EmulateInstructionLoongArch::EmulateJIRL64(uint32_t inst) { 358 uint32_t rj = Bits32(inst, 9, 5); 359 uint32_t rd = Bits32(inst, 4, 0); 360 bool success = false; 361 uint64_t pc = ReadPC(&success); 362 if (!success) 363 return false; 364 EmulateInstruction::Context ctx; 365 if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, rd, pc + 4)) 366 return false; 367 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 368 if (!success) 369 return false; 370 uint64_t next_pc = rj_val + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 371 return WritePC(next_pc); 372} 373 374// b offs26 375// PC = PC + SignExtend({offs26, 2' b0}, GRLEN) 376bool EmulateInstructionLoongArch::EmulateB64(uint32_t inst) { 377 bool success = false; 378 uint64_t pc = ReadPC(&success); 379 if (!success) 380 return false; 381 uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16); 382 uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2); 383 return WritePC(next_pc); 384} 385 386// bl offs26 387// GR[1] = PC + 4 388// PC = PC + SignExtend({offs26, 2'b0}, GRLEN) 389bool EmulateInstructionLoongArch::EmulateBL64(uint32_t inst) { 390 bool success = false; 391 uint64_t pc = ReadPC(&success); 392 if (!success) 393 return false; 394 EmulateInstruction::Context ctx; 395 if (!WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_loongarch, pc + 4)) 396 return false; 397 uint32_t offs26 = Bits32(inst, 25, 10) + (Bits32(inst, 9, 0) << 16); 398 uint64_t next_pc = pc + llvm::SignExtend64<28>(offs26 << 2); 399 return WritePC(next_pc); 400} 401 402// beq rj, rd, offs16 403// if GR[rj] == GR[rd]: 404// PC = PC + SignExtend({offs16, 2'b0}, GRLEN) 405bool EmulateInstructionLoongArch::EmulateBEQ64(uint32_t inst) { 406 bool success = false; 407 uint32_t rj = Bits32(inst, 9, 5); 408 uint32_t rd = Bits32(inst, 4, 0); 409 uint64_t pc = ReadPC(&success); 410 if (!success) 411 return false; 412 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 413 if (!success) 414 return false; 415 uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); 416 if (!success) 417 return false; 418 if (rj_val == rd_val) { 419 uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 420 return WritePC(next_pc); 421 } else 422 return WritePC(pc + 4); 423} 424 425// bne rj, rd, offs16 426// if GR[rj] != GR[rd]: 427// PC = PC + SignExtend({offs16, 2'b0}, GRLEN) 428bool EmulateInstructionLoongArch::EmulateBNE64(uint32_t inst) { 429 bool success = false; 430 uint32_t rj = Bits32(inst, 9, 5); 431 uint32_t rd = Bits32(inst, 4, 0); 432 uint64_t pc = ReadPC(&success); 433 if (!success) 434 return false; 435 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 436 if (!success) 437 return false; 438 uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); 439 if (!success) 440 return false; 441 if (rj_val != rd_val) { 442 uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 443 return WritePC(next_pc); 444 } else 445 return WritePC(pc + 4); 446} 447 448// blt rj, rd, offs16 449// if signed(GR[rj]) < signed(GR[rd]): 450// PC = PC + SignExtend({offs16, 2'b0}, GRLEN) 451bool EmulateInstructionLoongArch::EmulateBLT64(uint32_t inst) { 452 bool success = false; 453 uint32_t rj = Bits32(inst, 9, 5); 454 uint32_t rd = Bits32(inst, 4, 0); 455 uint64_t pc = ReadPC(&success); 456 if (!success) 457 return false; 458 int64_t rj_val = 459 (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 460 if (!success) 461 return false; 462 int64_t rd_val = 463 (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); 464 if (!success) 465 return false; 466 if (rj_val < rd_val) { 467 uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 468 return WritePC(next_pc); 469 } else 470 return WritePC(pc + 4); 471} 472 473// bge rj, rd, offs16 474// if signed(GR[rj]) >= signed(GR[rd]): 475// PC = PC + SignExtend({offs16, 2'b0}, GRLEN) 476bool EmulateInstructionLoongArch::EmulateBGE64(uint32_t inst) { 477 bool success = false; 478 uint32_t rj = Bits32(inst, 9, 5); 479 uint32_t rd = Bits32(inst, 4, 0); 480 uint64_t pc = ReadPC(&success); 481 if (!success) 482 return false; 483 int64_t rj_val = 484 (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 485 if (!success) 486 return false; 487 int64_t rd_val = 488 (int64_t)ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); 489 if (!success) 490 return false; 491 if (rj_val >= rd_val) { 492 uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 493 return WritePC(next_pc); 494 } else 495 return WritePC(pc + 4); 496} 497 498// bltu rj, rd, offs16 499// if unsigned(GR[rj]) < unsigned(GR[rd]): 500// PC = PC + SignExtend({offs16, 2'b0}, GRLEN) 501bool EmulateInstructionLoongArch::EmulateBLTU64(uint32_t inst) { 502 bool success = false; 503 uint32_t rj = Bits32(inst, 9, 5); 504 uint32_t rd = Bits32(inst, 4, 0); 505 uint64_t pc = ReadPC(&success); 506 if (!success) 507 return false; 508 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 509 if (!success) 510 return false; 511 uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); 512 if (!success) 513 return false; 514 if (rj_val < rd_val) { 515 uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 516 return WritePC(next_pc); 517 } else 518 return WritePC(pc + 4); 519} 520 521// bgeu rj, rd, offs16 522// if unsigned(GR[rj]) >= unsigned(GR[rd]): 523// PC = PC + SignExtend({offs16, 2'b0}, GRLEN) 524bool EmulateInstructionLoongArch::EmulateBGEU64(uint32_t inst) { 525 bool success = false; 526 uint32_t rj = Bits32(inst, 9, 5); 527 uint32_t rd = Bits32(inst, 4, 0); 528 uint64_t pc = ReadPC(&success); 529 if (!success) 530 return false; 531 uint64_t rj_val = ReadRegisterUnsigned(eRegisterKindLLDB, rj, 0, &success); 532 if (!success) 533 return false; 534 uint64_t rd_val = ReadRegisterUnsigned(eRegisterKindLLDB, rd, 0, &success); 535 if (!success) 536 return false; 537 if (rj_val >= rd_val) { 538 uint64_t next_pc = pc + llvm::SignExtend64<18>(Bits32(inst, 25, 10) << 2); 539 return WritePC(next_pc); 540 } else 541 return WritePC(pc + 4); 542} 543 544} // namespace lldb_private 545