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