[llvm-branch-commits] [lldb] [lldb][LoongArch] Function calls support in lldb expressions (PR #114742)

David Spickett via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Nov 4 02:22:07 PST 2024


================
@@ -0,0 +1,664 @@
+//===-- ABISysV_loongarch.cpp----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ABISysV_loongarch.h"
+
+#include <array>
+#include <limits>
+#include <sstream>
+
+#include "llvm/IR/DerivedTypes.h"
+
+#include "Utility/LoongArch_DWARF_Registers.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/ValueObject/ValueObjectConstResult.h"
+
+#define DEFINE_REG_NAME(reg_num) ConstString(#reg_num).GetCString()
+#define DEFINE_REG_NAME_STR(reg_name) ConstString(reg_name).GetCString()
+
+// The ABI is not a source of such information as size, offset, encoding, etc.
+// of a register. Just provides correct dwarf and eh_frame numbers.
+
+#define DEFINE_GENERIC_REGISTER_STUB(dwarf_num, str_name, generic_num)         \
+  {                                                                            \
+      DEFINE_REG_NAME(dwarf_num),                                              \
+      DEFINE_REG_NAME_STR(str_name),                                           \
+      0,                                                                       \
+      0,                                                                       \
+      eEncodingInvalid,                                                        \
+      eFormatDefault,                                                          \
+      {dwarf_num, dwarf_num, generic_num, LLDB_INVALID_REGNUM, dwarf_num},     \
+      nullptr,                                                                 \
+      nullptr,                                                                 \
+      nullptr,                                                                 \
+  }
+
+#define DEFINE_REGISTER_STUB(dwarf_num, str_name)                              \
+  DEFINE_GENERIC_REGISTER_STUB(dwarf_num, str_name, LLDB_INVALID_REGNUM)
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE_ADV(ABISysV_loongarch, ABILoongArch)
+
+namespace {
+namespace dwarf {
+enum regnums {
+  r0,
+  ra,
+  r1 = ra,
+  r2,
+  sp,
+  r3 = sp,
+  r4,
+  r5,
+  r6,
+  r7,
+  r8,
+  r9,
+  r10,
+  r11,
+  r12,
+  r13,
+  r14,
+  r15,
+  r16,
+  r17,
+  r18,
+  r19,
+  r20,
+  r21,
+  fp,
+  r22 = fp,
+  r23,
+  r24,
+  r25,
+  r26,
+  r27,
+  r28,
+  r29,
+  r30,
+  r31,
+  pc
+};
+
+static const std::array<RegisterInfo, 33> g_register_infos = {
+    {DEFINE_REGISTER_STUB(r0, nullptr),
+     DEFINE_GENERIC_REGISTER_STUB(r1, nullptr, LLDB_REGNUM_GENERIC_RA),
+     DEFINE_REGISTER_STUB(r2, nullptr),
+     DEFINE_GENERIC_REGISTER_STUB(r3, nullptr, LLDB_REGNUM_GENERIC_SP),
+     DEFINE_GENERIC_REGISTER_STUB(r4, nullptr, LLDB_REGNUM_GENERIC_ARG1),
+     DEFINE_GENERIC_REGISTER_STUB(r5, nullptr, LLDB_REGNUM_GENERIC_ARG2),
+     DEFINE_GENERIC_REGISTER_STUB(r6, nullptr, LLDB_REGNUM_GENERIC_ARG3),
+     DEFINE_GENERIC_REGISTER_STUB(r7, nullptr, LLDB_REGNUM_GENERIC_ARG4),
+     DEFINE_GENERIC_REGISTER_STUB(r8, nullptr, LLDB_REGNUM_GENERIC_ARG5),
+     DEFINE_GENERIC_REGISTER_STUB(r9, nullptr, LLDB_REGNUM_GENERIC_ARG6),
+     DEFINE_GENERIC_REGISTER_STUB(r10, nullptr, LLDB_REGNUM_GENERIC_ARG7),
+     DEFINE_GENERIC_REGISTER_STUB(r11, nullptr, LLDB_REGNUM_GENERIC_ARG8),
+     DEFINE_REGISTER_STUB(r12, nullptr),
+     DEFINE_REGISTER_STUB(r13, nullptr),
+     DEFINE_REGISTER_STUB(r14, nullptr),
+     DEFINE_REGISTER_STUB(r15, nullptr),
+     DEFINE_REGISTER_STUB(r16, nullptr),
+     DEFINE_REGISTER_STUB(r17, nullptr),
+     DEFINE_REGISTER_STUB(r18, nullptr),
+     DEFINE_REGISTER_STUB(r19, nullptr),
+     DEFINE_REGISTER_STUB(r20, nullptr),
+     DEFINE_REGISTER_STUB(r21, nullptr),
+     DEFINE_GENERIC_REGISTER_STUB(r22, nullptr, LLDB_REGNUM_GENERIC_FP),
+     DEFINE_REGISTER_STUB(r23, nullptr),
+     DEFINE_REGISTER_STUB(r24, nullptr),
+     DEFINE_REGISTER_STUB(r25, nullptr),
+     DEFINE_REGISTER_STUB(r26, nullptr),
+     DEFINE_REGISTER_STUB(r27, nullptr),
+     DEFINE_REGISTER_STUB(r28, nullptr),
+     DEFINE_REGISTER_STUB(r29, nullptr),
+     DEFINE_REGISTER_STUB(r30, nullptr),
+     DEFINE_REGISTER_STUB(r31, nullptr),
+     DEFINE_GENERIC_REGISTER_STUB(pc, nullptr, LLDB_REGNUM_GENERIC_PC)}};
+} // namespace dwarf
+} // namespace
+
+// Number of argument registers (the base integer calling convention
+// provides 8 argument registers, a0-a7)
+static constexpr size_t g_regs_for_args_count = 8U;
+
+const RegisterInfo *ABISysV_loongarch::GetRegisterInfoArray(uint32_t &count) {
+  count = dwarf::g_register_infos.size();
+  return dwarf::g_register_infos.data();
+}
+
+//------------------------------------------------------------------
+// Static Functions
+//------------------------------------------------------------------
+
+ABISP
+ABISysV_loongarch::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) {
+  llvm::Triple::ArchType machine = arch.GetTriple().getArch();
+
+  if (llvm::Triple::loongarch32 != machine &&
+      llvm::Triple::loongarch64 != machine)
+    return ABISP();
+
+  ABISysV_loongarch *abi =
+      new ABISysV_loongarch(std::move(process_sp), MakeMCRegisterInfo(arch));
+  if (abi)
+    abi->SetIsLA64((llvm::Triple::loongarch64 == machine) ? true : false);
+  return ABISP(abi);
+}
+
+static bool UpdateRegister(RegisterContext *reg_ctx,
+                           const lldb::RegisterKind reg_kind,
+                           const uint32_t reg_num, const addr_t value) {
+  Log *log = GetLog(LLDBLog::Expressions);
+
+  const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(reg_kind, reg_num);
+
+  LLDB_LOG(log, "Writing {0}: 0x{1:x}", reg_info->name,
+           static_cast<uint64_t>(value));
+  if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, value)) {
+    LLDB_LOG(log, "Writing {0}: failed", reg_info->name);
+    return false;
+  }
+  return true;
+}
+
+static void LogInitInfo(Log &log, const Thread &thread, addr_t sp,
+                        addr_t func_addr, addr_t return_addr,
+                        const llvm::ArrayRef<addr_t> args) {
+  std::stringstream ss;
+  ss << "ABISysV_loongarch::PrepareTrivialCall"
+     << " (tid = 0x" << std::hex << thread.GetID() << ", sp = 0x" << sp
+     << ", func_addr = 0x" << func_addr << ", return_addr = 0x" << return_addr;
+
+  for (auto [idx, arg] : enumerate(args))
+    ss << ", arg" << std::dec << idx << " = 0x" << std::hex << arg;
+  ss << ")";
+  log.PutString(ss.str());
+}
+
+bool ABISysV_loongarch::PrepareTrivialCall(Thread &thread, addr_t sp,
+                                           addr_t func_addr, addr_t return_addr,
+                                           llvm::ArrayRef<addr_t> args) const {
+  Log *log = GetLog(LLDBLog::Expressions);
+  if (log)
+    LogInitInfo(*log, thread, sp, func_addr, return_addr, args);
+
+  const auto reg_ctx_sp = thread.GetRegisterContext();
+  if (!reg_ctx_sp) {
+    LLDB_LOG(log, "Failed to get RegisterContext");
+    return false;
+  }
+
+  if (args.size() > g_regs_for_args_count) {
+    LLDB_LOG(log, "Function has {0} arguments, but only {1} are allowed!",
+             args.size(), g_regs_for_args_count);
+    return false;
+  }
+
+  // Write arguments to registers
+  for (auto [idx, arg] : enumerate(args)) {
+    const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfo(
+        eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + idx);
+    LLDB_LOG(log, "About to write arg{0} (0x{1:x}) into {2}", idx, arg,
+             reg_info->name);
+
+    if (!reg_ctx_sp->WriteRegisterFromUnsigned(reg_info, arg)) {
+      LLDB_LOG(log, "Failed to write arg{0} (0x{1:x}) into {2}", idx, arg,
+               reg_info->name);
+      return false;
+    }
+  }
+
+  if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric,
+                      LLDB_REGNUM_GENERIC_PC, func_addr))
+    return false;
+  if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric,
+                      LLDB_REGNUM_GENERIC_SP, sp))
+    return false;
+  if (!UpdateRegister(reg_ctx_sp.get(), eRegisterKindGeneric,
+                      LLDB_REGNUM_GENERIC_RA, return_addr))
+    return false;
+
+  LLDB_LOG(log, "ABISysV_riscv::{0}() success", __FUNCTION__);
+  return true;
+}
+
+bool ABISysV_loongarch::GetArgumentValues(Thread &thread,
+                                          ValueList &values) const {
+  // TODO: Implement
+  return false;
+}
+
+Status ABISysV_loongarch::SetReturnValueObject(StackFrameSP &frame_sp,
+                                               ValueObjectSP &new_value_sp) {
+  Status result;
+  if (!new_value_sp) {
+    result = Status::FromErrorString("Empty value object for return value.");
+    return result;
+  }
+
+  CompilerType compiler_type = new_value_sp->GetCompilerType();
+  if (!compiler_type) {
+    result = Status::FromErrorString("Null clang type for return value.");
+    return result;
+  }
+
+  auto &reg_ctx = *frame_sp->GetThread()->GetRegisterContext();
+
+  bool is_signed = false;
+  if (!compiler_type.IsIntegerOrEnumerationType(is_signed) &&
+      !compiler_type.IsPointerType()) {
+    result = Status::FromErrorString(
+        "We don't support returning other types at present");
+    return result;
+  }
+
+  DataExtractor data;
+  size_t num_bytes = new_value_sp->GetData(data, result);
+
+  if (result.Fail()) {
+    result = Status::FromErrorStringWithFormat(
+        "Couldn't convert return value to raw data: %s", result.AsCString());
+    return result;
+  }
+
+  size_t reg_size = m_is_la64 ? 8 : 4;
+  if (num_bytes <= 2 * reg_size) {
+    offset_t offset = 0;
+    uint64_t raw_value = data.GetMaxU64(&offset, num_bytes);
+
+    auto reg_info =
+        reg_ctx.GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
+    if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, raw_value)) {
+      result = Status::FromErrorStringWithFormat(
+          "Couldn't write value to register %s", reg_info->name);
+      return result;
+    }
+
+    if (num_bytes <= reg_size)
+      return result; // Successfully written.
+
+    // for loongarch32, get the upper 32 bits from raw_value and write them
+    // for loongarch64, get the next 64 bits from data and write them
+    if (4 == reg_size)
+      raw_value >>= 32;
+    else
+      raw_value = data.GetMaxU64(&offset, num_bytes - reg_size);
+    reg_info =
+        reg_ctx.GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
+    if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, raw_value)) {
+      result = Status::FromErrorStringWithFormat(
+          "Couldn't write value to register %s", reg_info->name);
+    }
+
+    return result;
+  }
+
+  result = Status::FromErrorString(
+      "We don't support returning large integer values at present.");
+  return result;
+}
+
+template <typename T>
+static void SetInteger(Scalar &scalar, uint64_t raw_value, bool is_signed) {
+  raw_value &= std::numeric_limits<T>::max();
----------------
DavidSpickett wrote:

If T is a signed int, isn't the sign bit of a positive signed integer 0 and therefore this will remove the sign bit from the raw value in that case?

Should this use the max of the unsigned version of T? Or instead work out the number of bits from sizeof(T) and make a mask from that.

As I'm also suspicious of what uint64_t &= signed integer is going to do.

https://github.com/llvm/llvm-project/pull/114742


More information about the llvm-branch-commits mailing list