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 28 using namespace lldb; 29 using namespace lldb_private; 30 31 LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionLoongArch, InstructionLoongArch) 32 33 namespace lldb_private { 34 35 EmulateInstructionLoongArch::Opcode * 36 EmulateInstructionLoongArch::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 75 bool 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 85 bool 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 117 bool 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 134 lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) { 135 return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, 136 LLDB_INVALID_ADDRESS, success); 137 } 138 139 bool 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 147 std::optional<RegisterInfo> 148 EmulateInstructionLoongArch::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 185 bool EmulateInstructionLoongArch::SetTargetTriple(const ArchSpec &arch) { 186 return SupportsThisArch(arch); 187 } 188 189 bool EmulateInstructionLoongArch::TestEmulation( 190 Stream &out_stream, ArchSpec &arch, OptionValueDictionary *test_data) { 191 return false; 192 } 193 194 void EmulateInstructionLoongArch::Initialize() { 195 PluginManager::RegisterPlugin(GetPluginNameStatic(), 196 GetPluginDescriptionStatic(), CreateInstance); 197 } 198 199 void EmulateInstructionLoongArch::Terminate() { 200 PluginManager::UnregisterPlugin(CreateInstance); 201 } 202 203 lldb_private::EmulateInstruction * 204 EmulateInstructionLoongArch::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 212 bool EmulateInstructionLoongArch::SupportsThisArch(const ArchSpec &arch) { 213 return arch.GetTriple().isLoongArch(); 214 } 215 216 bool EmulateInstructionLoongArch::EmulateBEQZ(uint32_t inst) { 217 return IsLoongArch64() ? EmulateBEQZ64(inst) : false; 218 } 219 220 bool EmulateInstructionLoongArch::EmulateBNEZ(uint32_t inst) { 221 return IsLoongArch64() ? EmulateBNEZ64(inst) : false; 222 } 223 224 bool EmulateInstructionLoongArch::EmulateBCEQZ(uint32_t inst) { 225 return IsLoongArch64() ? EmulateBCEQZ64(inst) : false; 226 } 227 228 bool EmulateInstructionLoongArch::EmulateBCNEZ(uint32_t inst) { 229 return IsLoongArch64() ? EmulateBCNEZ64(inst) : false; 230 } 231 232 bool EmulateInstructionLoongArch::EmulateJIRL(uint32_t inst) { 233 return IsLoongArch64() ? EmulateJIRL64(inst) : false; 234 } 235 236 bool EmulateInstructionLoongArch::EmulateB(uint32_t inst) { 237 return IsLoongArch64() ? EmulateB64(inst) : false; 238 } 239 240 bool EmulateInstructionLoongArch::EmulateBL(uint32_t inst) { 241 return IsLoongArch64() ? EmulateBL64(inst) : false; 242 } 243 244 bool EmulateInstructionLoongArch::EmulateBEQ(uint32_t inst) { 245 return IsLoongArch64() ? EmulateBEQ64(inst) : false; 246 } 247 248 bool EmulateInstructionLoongArch::EmulateBNE(uint32_t inst) { 249 return IsLoongArch64() ? EmulateBNE64(inst) : false; 250 } 251 252 bool EmulateInstructionLoongArch::EmulateBLT(uint32_t inst) { 253 return IsLoongArch64() ? EmulateBLT64(inst) : false; 254 } 255 256 bool EmulateInstructionLoongArch::EmulateBGE(uint32_t inst) { 257 return IsLoongArch64() ? EmulateBGE64(inst) : false; 258 } 259 260 bool EmulateInstructionLoongArch::EmulateBLTU(uint32_t inst) { 261 return IsLoongArch64() ? EmulateBLTU64(inst) : false; 262 } 263 264 bool EmulateInstructionLoongArch::EmulateBGEU(uint32_t inst) { 265 return IsLoongArch64() ? EmulateBGEU64(inst) : false; 266 } 267 268 bool 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) 273 bool 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) 293 bool 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) 313 bool 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) 335 bool 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) 357 bool 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) 376 bool 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) 389 bool 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) 405 bool 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) 428 bool 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) 451 bool 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) 476 bool 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) 501 bool 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) 524 bool 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