| 1 | //===-- ABISysV_riscv.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 "ABISysV_riscv.h" |
| 10 | |
| 11 | #include <array> |
| 12 | #include <limits> |
| 13 | #include <sstream> |
| 14 | |
| 15 | #include "llvm/ADT/STLExtras.h" |
| 16 | #include "llvm/IR/DerivedTypes.h" |
| 17 | |
| 18 | #include "Utility/RISCV_DWARF_Registers.h" |
| 19 | #include "lldb/Core/PluginManager.h" |
| 20 | #include "lldb/Core/Value.h" |
| 21 | #include "lldb/Target/RegisterContext.h" |
| 22 | #include "lldb/Target/StackFrame.h" |
| 23 | #include "lldb/Target/Thread.h" |
| 24 | #include "lldb/Utility/LLDBLog.h" |
| 25 | #include "lldb/Utility/RegisterValue.h" |
| 26 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
| 27 | |
| 28 | #define DEFINE_REG_NAME(reg_num) ConstString(#reg_num).GetCString() |
| 29 | #define DEFINE_REG_NAME_STR(reg_name) ConstString(reg_name).GetCString() |
| 30 | |
| 31 | // The ABI is not a source of such information as size, offset, encoding, etc. |
| 32 | // of a register. Just provides correct dwarf and eh_frame numbers. |
| 33 | |
| 34 | #define DEFINE_GENERIC_REGISTER_STUB(dwarf_num, str_name, generic_num) \ |
| 35 | { \ |
| 36 | DEFINE_REG_NAME(dwarf_num), DEFINE_REG_NAME_STR(str_name), 0, 0, \ |
| 37 | eEncodingInvalid, eFormatDefault, \ |
| 38 | {dwarf_num, dwarf_num, generic_num, LLDB_INVALID_REGNUM, dwarf_num}, \ |
| 39 | nullptr, nullptr, nullptr, \ |
| 40 | } |
| 41 | |
| 42 | #define DEFINE_REGISTER_STUB(dwarf_num, str_name) \ |
| 43 | DEFINE_GENERIC_REGISTER_STUB(dwarf_num, str_name, LLDB_INVALID_REGNUM) |
| 44 | |
| 45 | using namespace lldb; |
| 46 | using namespace lldb_private; |
| 47 | |
| 48 | LLDB_PLUGIN_DEFINE_ADV(ABISysV_riscv, ABIRISCV) |
| 49 | |
| 50 | namespace { |
| 51 | namespace dwarf { |
| 52 | enum regnums { |
| 53 | zero, |
| 54 | ra, |
| 55 | sp, |
| 56 | gp, |
| 57 | tp, |
| 58 | t0, |
| 59 | t1, |
| 60 | t2, |
| 61 | fp, |
| 62 | s0 = fp, |
| 63 | s1, |
| 64 | a0, |
| 65 | a1, |
| 66 | a2, |
| 67 | a3, |
| 68 | a4, |
| 69 | a5, |
| 70 | a6, |
| 71 | a7, |
| 72 | s2, |
| 73 | s3, |
| 74 | s4, |
| 75 | s5, |
| 76 | s6, |
| 77 | s7, |
| 78 | s8, |
| 79 | s9, |
| 80 | s10, |
| 81 | s11, |
| 82 | t3, |
| 83 | t4, |
| 84 | t5, |
| 85 | t6, |
| 86 | pc |
| 87 | }; |
| 88 | |
| 89 | static const std::array<RegisterInfo, 33> g_register_infos = { |
| 90 | ._M_elems: {DEFINE_REGISTER_STUB(zero, nullptr), |
| 91 | DEFINE_GENERIC_REGISTER_STUB(ra, nullptr, LLDB_REGNUM_GENERIC_RA), |
| 92 | DEFINE_GENERIC_REGISTER_STUB(sp, nullptr, LLDB_REGNUM_GENERIC_SP), |
| 93 | DEFINE_REGISTER_STUB(gp, nullptr), |
| 94 | DEFINE_REGISTER_STUB(tp, nullptr), |
| 95 | DEFINE_REGISTER_STUB(t0, nullptr), |
| 96 | DEFINE_REGISTER_STUB(t1, nullptr), |
| 97 | DEFINE_REGISTER_STUB(t2, nullptr), |
| 98 | DEFINE_GENERIC_REGISTER_STUB(fp, nullptr, LLDB_REGNUM_GENERIC_FP), |
| 99 | DEFINE_REGISTER_STUB(s1, nullptr), |
| 100 | DEFINE_GENERIC_REGISTER_STUB(a0, nullptr, LLDB_REGNUM_GENERIC_ARG1), |
| 101 | DEFINE_GENERIC_REGISTER_STUB(a1, nullptr, LLDB_REGNUM_GENERIC_ARG2), |
| 102 | DEFINE_GENERIC_REGISTER_STUB(a2, nullptr, LLDB_REGNUM_GENERIC_ARG3), |
| 103 | DEFINE_GENERIC_REGISTER_STUB(a3, nullptr, LLDB_REGNUM_GENERIC_ARG4), |
| 104 | DEFINE_GENERIC_REGISTER_STUB(a4, nullptr, LLDB_REGNUM_GENERIC_ARG5), |
| 105 | DEFINE_GENERIC_REGISTER_STUB(a5, nullptr, LLDB_REGNUM_GENERIC_ARG6), |
| 106 | DEFINE_GENERIC_REGISTER_STUB(a6, nullptr, LLDB_REGNUM_GENERIC_ARG7), |
| 107 | DEFINE_GENERIC_REGISTER_STUB(a7, nullptr, LLDB_REGNUM_GENERIC_ARG8), |
| 108 | DEFINE_REGISTER_STUB(s2, nullptr), |
| 109 | DEFINE_REGISTER_STUB(s3, nullptr), |
| 110 | DEFINE_REGISTER_STUB(s4, nullptr), |
| 111 | DEFINE_REGISTER_STUB(s5, nullptr), |
| 112 | DEFINE_REGISTER_STUB(s6, nullptr), |
| 113 | DEFINE_REGISTER_STUB(s7, nullptr), |
| 114 | DEFINE_REGISTER_STUB(s8, nullptr), |
| 115 | DEFINE_REGISTER_STUB(s9, nullptr), |
| 116 | DEFINE_REGISTER_STUB(s10, nullptr), |
| 117 | DEFINE_REGISTER_STUB(s11, nullptr), |
| 118 | DEFINE_REGISTER_STUB(t3, nullptr), |
| 119 | DEFINE_REGISTER_STUB(t4, nullptr), |
| 120 | DEFINE_REGISTER_STUB(t5, nullptr), |
| 121 | DEFINE_REGISTER_STUB(t6, nullptr), |
| 122 | DEFINE_GENERIC_REGISTER_STUB(pc, nullptr, LLDB_REGNUM_GENERIC_PC)}}; |
| 123 | } // namespace dwarf |
| 124 | } // namespace |
| 125 | |
| 126 | // Number of argument registers (the base integer calling convention |
| 127 | // provides 8 argument registers, a0-a7) |
| 128 | static constexpr size_t g_regs_for_args_count = 8U; |
| 129 | |
| 130 | const RegisterInfo *ABISysV_riscv::GetRegisterInfoArray(uint32_t &count) { |
| 131 | count = dwarf::g_register_infos.size(); |
| 132 | return dwarf::g_register_infos.data(); |
| 133 | } |
| 134 | |
| 135 | //------------------------------------------------------------------ |
| 136 | // Static Functions |
| 137 | //------------------------------------------------------------------ |
| 138 | |
| 139 | ABISP |
| 140 | ABISysV_riscv::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) { |
| 141 | llvm::Triple::ArchType machine = arch.GetTriple().getArch(); |
| 142 | |
| 143 | if (llvm::Triple::riscv32 != machine && llvm::Triple::riscv64 != machine) |
| 144 | return ABISP(); |
| 145 | |
| 146 | ABISysV_riscv *abi = new ABISysV_riscv(std::move(process_sp), |
| 147 | MakeMCRegisterInfo(arch)); |
| 148 | if (abi) |
| 149 | abi->SetIsRV64((llvm::Triple::riscv64 == machine) ? true : false); |
| 150 | return ABISP(abi); |
| 151 | } |
| 152 | |
| 153 | static inline size_t AugmentArgSize(bool is_rv64, size_t size_in_bytes) { |
| 154 | size_t word_size = is_rv64 ? 8 : 4; |
| 155 | return llvm::alignTo(Value: size_in_bytes, Align: word_size); |
| 156 | } |
| 157 | |
| 158 | static size_t |
| 159 | TotalArgsSizeInWords(bool is_rv64, |
| 160 | const llvm::ArrayRef<ABI::CallArgument> &args) { |
| 161 | size_t reg_size = is_rv64 ? 8 : 4; |
| 162 | size_t word_size = reg_size; |
| 163 | size_t total_size = 0; |
| 164 | for (const auto &arg : args) |
| 165 | total_size += |
| 166 | (ABI::CallArgument::TargetValue == arg.type ? AugmentArgSize(is_rv64, |
| 167 | size_in_bytes: arg.size) |
| 168 | : reg_size) / |
| 169 | word_size; |
| 170 | |
| 171 | return total_size; |
| 172 | } |
| 173 | |
| 174 | static bool UpdateRegister(RegisterContext *reg_ctx, |
| 175 | const lldb::RegisterKind reg_kind, |
| 176 | const uint32_t reg_num, const addr_t value) { |
| 177 | Log *log = GetLog(mask: LLDBLog::Expressions); |
| 178 | |
| 179 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(reg_kind, reg_num); |
| 180 | |
| 181 | LLDB_LOG(log, "Writing {0}: 0x{1:x}" , reg_info->name, |
| 182 | static_cast<uint64_t>(value)); |
| 183 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, uval: value)) { |
| 184 | LLDB_LOG(log, "Writing {0}: failed" , reg_info->name); |
| 185 | return false; |
| 186 | } |
| 187 | return true; |
| 188 | } |
| 189 | |
| 190 | static void LogInitInfo(Log &log, const Thread &thread, addr_t sp, |
| 191 | addr_t func_addr, addr_t return_addr, |
| 192 | const llvm::ArrayRef<addr_t> args) { |
| 193 | std::stringstream ss; |
| 194 | ss << "ABISysV_riscv::PrepareTrivialCall" |
| 195 | << " (tid = 0x" << std::hex << thread.GetID() << ", sp = 0x" << sp |
| 196 | << ", func_addr = 0x" << func_addr << ", return_addr = 0x" << return_addr; |
| 197 | |
| 198 | for (auto [idx, arg] : enumerate(First: args)) |
| 199 | ss << ", arg" << std::dec << idx << " = 0x" << std::hex << arg; |
| 200 | ss << ")" ; |
| 201 | log.PutString(str: ss.str()); |
| 202 | } |
| 203 | |
| 204 | bool ABISysV_riscv::PrepareTrivialCall(Thread &thread, addr_t sp, |
| 205 | addr_t func_addr, addr_t return_addr, |
| 206 | llvm::ArrayRef<addr_t> args) const { |
| 207 | Log *log = GetLog(mask: LLDBLog::Expressions); |
| 208 | if (log) |
| 209 | LogInitInfo(log&: *log, thread, sp, func_addr, return_addr, args); |
| 210 | |
| 211 | const auto reg_ctx_sp = thread.GetRegisterContext(); |
| 212 | if (!reg_ctx_sp) { |
| 213 | LLDB_LOG(log, "Failed to get RegisterContext" ); |
| 214 | return false; |
| 215 | } |
| 216 | |
| 217 | if (args.size() > g_regs_for_args_count) { |
| 218 | LLDB_LOG(log, "Function has {0} arguments, but only {1} are allowed!" , |
| 219 | args.size(), g_regs_for_args_count); |
| 220 | return false; |
| 221 | } |
| 222 | |
| 223 | // Write arguments to registers |
| 224 | for (auto [idx, arg] : enumerate(First&: args)) { |
| 225 | const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfo( |
| 226 | reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + idx); |
| 227 | LLDB_LOG(log, "About to write arg{0} (0x{1:x}) into {2}" , idx, arg, |
| 228 | reg_info->name); |
| 229 | |
| 230 | if (!reg_ctx_sp->WriteRegisterFromUnsigned(reg_info, uval: arg)) { |
| 231 | LLDB_LOG(log, "Failed to write arg{0} (0x{1:x}) into {2}" , idx, arg, |
| 232 | reg_info->name); |
| 233 | return false; |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | if (!UpdateRegister(reg_ctx: reg_ctx_sp.get(), reg_kind: eRegisterKindGeneric, |
| 238 | LLDB_REGNUM_GENERIC_PC, value: func_addr)) |
| 239 | return false; |
| 240 | if (!UpdateRegister(reg_ctx: reg_ctx_sp.get(), reg_kind: eRegisterKindGeneric, |
| 241 | LLDB_REGNUM_GENERIC_SP, value: sp)) |
| 242 | return false; |
| 243 | if (!UpdateRegister(reg_ctx: reg_ctx_sp.get(), reg_kind: eRegisterKindGeneric, |
| 244 | LLDB_REGNUM_GENERIC_RA, value: return_addr)) |
| 245 | return false; |
| 246 | |
| 247 | LLDB_LOG(log, "ABISysV_riscv::{0}() success" , __FUNCTION__); |
| 248 | return true; |
| 249 | } |
| 250 | |
| 251 | bool ABISysV_riscv::PrepareTrivialCall( |
| 252 | Thread &thread, addr_t sp, addr_t pc, addr_t ra, llvm::Type &prototype, |
| 253 | llvm::ArrayRef<ABI::CallArgument> args) const { |
| 254 | auto reg_ctx = thread.GetRegisterContext(); |
| 255 | if (!reg_ctx) |
| 256 | return false; |
| 257 | |
| 258 | uint32_t pc_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( |
| 259 | kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
| 260 | if (pc_reg == LLDB_INVALID_REGNUM) |
| 261 | return false; |
| 262 | |
| 263 | uint32_t ra_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( |
| 264 | kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); |
| 265 | if (ra_reg == LLDB_INVALID_REGNUM) |
| 266 | return false; |
| 267 | |
| 268 | uint32_t sp_reg = reg_ctx->ConvertRegisterKindToRegisterNumber( |
| 269 | kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); |
| 270 | if (sp_reg == LLDB_INVALID_REGNUM) |
| 271 | return false; |
| 272 | |
| 273 | Status error; |
| 274 | ProcessSP process = thread.GetProcess(); |
| 275 | if (!process) |
| 276 | return false; |
| 277 | |
| 278 | size_t reg_size = m_is_rv64 ? 8 : 4; |
| 279 | size_t word_size = reg_size; |
| 280 | // Push host data onto target. |
| 281 | for (const auto &arg : args) { |
| 282 | // Skip over target values. |
| 283 | if (arg.type == ABI::CallArgument::TargetValue) |
| 284 | continue; |
| 285 | |
| 286 | // Create space on the host stack for this data 4-byte aligned. |
| 287 | sp -= AugmentArgSize(is_rv64: m_is_rv64, size_in_bytes: arg.size); |
| 288 | |
| 289 | if (process->WriteMemory(vm_addr: sp, buf: arg.data_up.get(), size: arg.size, error) < |
| 290 | arg.size || |
| 291 | error.Fail()) |
| 292 | return false; |
| 293 | |
| 294 | // Update the argument with the target pointer. |
| 295 | *const_cast<addr_t *>(&arg.value) = sp; |
| 296 | } |
| 297 | |
| 298 | // Make sure number of parameters matches prototype. |
| 299 | assert(prototype.getFunctionNumParams() == args.size()); |
| 300 | |
| 301 | const size_t num_args = args.size(); |
| 302 | const size_t num_args_in_regs = |
| 303 | num_args > g_regs_for_args_count ? g_regs_for_args_count : num_args; |
| 304 | |
| 305 | // Number of arguments passed on stack. |
| 306 | size_t args_size = TotalArgsSizeInWords(is_rv64: m_is_rv64, args); |
| 307 | auto on_stack = args_size <= g_regs_for_args_count |
| 308 | ? 0 |
| 309 | : args_size - g_regs_for_args_count; |
| 310 | auto offset = on_stack * word_size; |
| 311 | |
| 312 | uint8_t reg_value[8]; |
| 313 | size_t reg_index = LLDB_REGNUM_GENERIC_ARG1; |
| 314 | |
| 315 | for (size_t i = 0; i < args_size; ++i) { |
| 316 | auto value = reinterpret_cast<const uint8_t *>(&args[i].value); |
| 317 | auto size = |
| 318 | ABI::CallArgument::TargetValue == args[i].type ? args[i].size : reg_size; |
| 319 | |
| 320 | // Pass arguments via registers. |
| 321 | if (i < num_args_in_regs) { |
| 322 | // copy value to register, padding if arg is smaller than register |
| 323 | auto end = size < reg_size ? size : reg_size; |
| 324 | memcpy(dest: reg_value, src: value, n: end); |
| 325 | if (reg_size > end) |
| 326 | memset(s: reg_value + end, c: 0, n: reg_size - end); |
| 327 | |
| 328 | RegisterValue reg_val_obj(llvm::ArrayRef(reg_value, reg_size), |
| 329 | eByteOrderLittle); |
| 330 | if (!reg_ctx->WriteRegister( |
| 331 | reg_info: reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, reg_num: reg_index), |
| 332 | reg_value: reg_val_obj)) |
| 333 | return false; |
| 334 | |
| 335 | // NOTE: It's unsafe to iterate through LLDB_REGNUM_GENERICs |
| 336 | // But the "a" registers are sequential in the RISC-V register space |
| 337 | ++reg_index; |
| 338 | } |
| 339 | |
| 340 | if (reg_index < g_regs_for_args_count || size == 0) |
| 341 | continue; |
| 342 | |
| 343 | // Remaining arguments are passed on the stack. |
| 344 | if (process->WriteMemory(vm_addr: sp - offset, buf: value, size, error) < size || |
| 345 | !error.Success()) |
| 346 | return false; |
| 347 | |
| 348 | offset -= AugmentArgSize(is_rv64: m_is_rv64, size_in_bytes: size); |
| 349 | } |
| 350 | |
| 351 | // Set stack pointer immediately below arguments. |
| 352 | sp -= on_stack * word_size; |
| 353 | |
| 354 | // Update registers with current function call state. |
| 355 | reg_ctx->WriteRegisterFromUnsigned(reg: pc_reg, uval: pc); |
| 356 | reg_ctx->WriteRegisterFromUnsigned(reg: ra_reg, uval: ra); |
| 357 | reg_ctx->WriteRegisterFromUnsigned(reg: sp_reg, uval: sp); |
| 358 | |
| 359 | return true; |
| 360 | } |
| 361 | |
| 362 | bool ABISysV_riscv::GetArgumentValues(Thread &thread, ValueList &values) const { |
| 363 | // TODO: Implement |
| 364 | return false; |
| 365 | } |
| 366 | |
| 367 | Status ABISysV_riscv::SetReturnValueObject(StackFrameSP &frame_sp, |
| 368 | ValueObjectSP &new_value_sp) { |
| 369 | Status result; |
| 370 | if (!new_value_sp) { |
| 371 | result = Status::FromErrorString(str: "Empty value object for return value." ); |
| 372 | return result; |
| 373 | } |
| 374 | |
| 375 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
| 376 | if (!compiler_type) { |
| 377 | result = Status::FromErrorString(str: "Null clang type for return value." ); |
| 378 | return result; |
| 379 | } |
| 380 | |
| 381 | auto ®_ctx = *frame_sp->GetThread()->GetRegisterContext(); |
| 382 | |
| 383 | bool is_signed = false; |
| 384 | if (!compiler_type.IsIntegerOrEnumerationType(is_signed) && |
| 385 | !compiler_type.IsPointerType()) { |
| 386 | result = Status::FromErrorString( |
| 387 | str: "We don't support returning other types at present" ); |
| 388 | return result; |
| 389 | } |
| 390 | |
| 391 | DataExtractor data; |
| 392 | size_t num_bytes = new_value_sp->GetData(data, error&: result); |
| 393 | |
| 394 | if (result.Fail()) { |
| 395 | result = Status::FromErrorStringWithFormat( |
| 396 | format: "Couldn't convert return value to raw data: %s" , result.AsCString()); |
| 397 | return result; |
| 398 | } |
| 399 | |
| 400 | size_t reg_size = m_is_rv64 ? 8 : 4; |
| 401 | if (num_bytes <= 2 * reg_size) { |
| 402 | offset_t offset = 0; |
| 403 | uint64_t raw_value = data.GetMaxU64(offset_ptr: &offset, byte_size: num_bytes); |
| 404 | |
| 405 | auto reg_info = |
| 406 | reg_ctx.GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); |
| 407 | if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, uval: raw_value)) { |
| 408 | result = Status::FromErrorStringWithFormat( |
| 409 | format: "Couldn't write value to register %s" , reg_info->name); |
| 410 | return result; |
| 411 | } |
| 412 | |
| 413 | if (num_bytes <= reg_size) |
| 414 | return result; // Successfully written. |
| 415 | |
| 416 | // for riscv32, get the upper 32 bits from raw_value and write them |
| 417 | // for riscv64, get the next 64 bits from data and write them |
| 418 | if (4 == reg_size) |
| 419 | raw_value >>= 32; |
| 420 | else |
| 421 | raw_value = data.GetMaxU64(offset_ptr: &offset, byte_size: num_bytes - reg_size); |
| 422 | reg_info = |
| 423 | reg_ctx.GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); |
| 424 | if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, uval: raw_value)) { |
| 425 | result = Status::FromErrorStringWithFormat( |
| 426 | format: "Couldn't write value to register %s" , reg_info->name); |
| 427 | } |
| 428 | |
| 429 | return result; |
| 430 | } |
| 431 | |
| 432 | result = Status::FromErrorString( |
| 433 | str: "We don't support returning large integer values at present." ); |
| 434 | return result; |
| 435 | } |
| 436 | |
| 437 | template <typename T> |
| 438 | static void SetInteger(Scalar &scalar, uint64_t raw_value, bool is_signed) { |
| 439 | raw_value &= std::numeric_limits<T>::max(); |
| 440 | if (is_signed) |
| 441 | scalar = static_cast<typename std::make_signed<T>::type>(raw_value); |
| 442 | else |
| 443 | scalar = static_cast<T>(raw_value); |
| 444 | } |
| 445 | |
| 446 | static bool SetSizedInteger(Scalar &scalar, uint64_t raw_value, |
| 447 | uint8_t size_in_bytes, bool is_signed) { |
| 448 | switch (size_in_bytes) { |
| 449 | default: |
| 450 | return false; |
| 451 | |
| 452 | case sizeof(uint64_t): |
| 453 | SetInteger<uint64_t>(scalar, raw_value, is_signed); |
| 454 | break; |
| 455 | |
| 456 | case sizeof(uint32_t): |
| 457 | SetInteger<uint32_t>(scalar, raw_value, is_signed); |
| 458 | break; |
| 459 | |
| 460 | case sizeof(uint16_t): |
| 461 | SetInteger<uint16_t>(scalar, raw_value, is_signed); |
| 462 | break; |
| 463 | |
| 464 | case sizeof(uint8_t): |
| 465 | SetInteger<uint8_t>(scalar, raw_value, is_signed); |
| 466 | break; |
| 467 | } |
| 468 | |
| 469 | return true; |
| 470 | } |
| 471 | |
| 472 | static bool SetSizedFloat(Scalar &scalar, uint64_t raw_value, |
| 473 | uint8_t size_in_bytes) { |
| 474 | switch (size_in_bytes) { |
| 475 | default: |
| 476 | return false; |
| 477 | |
| 478 | case sizeof(uint64_t): |
| 479 | scalar = *reinterpret_cast<double *>(&raw_value); |
| 480 | break; |
| 481 | |
| 482 | case sizeof(uint32_t): |
| 483 | scalar = *reinterpret_cast<float *>(&raw_value); |
| 484 | break; |
| 485 | } |
| 486 | |
| 487 | return true; |
| 488 | } |
| 489 | |
| 490 | static ValueObjectSP GetValObjFromIntRegs(Thread &thread, |
| 491 | const RegisterContextSP ®_ctx, |
| 492 | llvm::Triple::ArchType machine, |
| 493 | uint32_t type_flags, |
| 494 | uint32_t byte_size) { |
| 495 | Value value; |
| 496 | ValueObjectSP return_valobj_sp; |
| 497 | auto reg_info_a0 = |
| 498 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); |
| 499 | auto reg_info_a1 = |
| 500 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); |
| 501 | uint64_t raw_value; |
| 502 | |
| 503 | switch (byte_size) { |
| 504 | case sizeof(uint32_t): |
| 505 | // Read a0 to get the arg |
| 506 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0) & UINT32_MAX; |
| 507 | break; |
| 508 | case sizeof(uint64_t): |
| 509 | // Read a0 to get the arg on riscv64, a0 and a1 on riscv32 |
| 510 | if (llvm::Triple::riscv32 == machine) { |
| 511 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0) & UINT32_MAX; |
| 512 | raw_value |= |
| 513 | (reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a1, fail_value: 0) & UINT32_MAX) << 32U; |
| 514 | } else { |
| 515 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0); |
| 516 | } |
| 517 | break; |
| 518 | case 16: { |
| 519 | // Read a0 and a1 to get the arg on riscv64, not supported on riscv32 |
| 520 | if (llvm::Triple::riscv32 == machine) |
| 521 | return return_valobj_sp; |
| 522 | |
| 523 | // Create the ValueObjectSP here and return |
| 524 | std::unique_ptr<DataBufferHeap> heap_data_up( |
| 525 | new DataBufferHeap(byte_size, 0)); |
| 526 | const ByteOrder byte_order = thread.GetProcess()->GetByteOrder(); |
| 527 | RegisterValue reg_value_a0, reg_value_a1; |
| 528 | if (reg_ctx->ReadRegister(reg_info: reg_info_a0, reg_value&: reg_value_a0) && |
| 529 | reg_ctx->ReadRegister(reg_info: reg_info_a1, reg_value&: reg_value_a1)) { |
| 530 | Status error; |
| 531 | if (reg_value_a0.GetAsMemoryData(reg_info: *reg_info_a0, |
| 532 | dst: heap_data_up->GetBytes() + 0, dst_len: 8, |
| 533 | dst_byte_order: byte_order, error) && |
| 534 | reg_value_a1.GetAsMemoryData(reg_info: *reg_info_a1, |
| 535 | dst: heap_data_up->GetBytes() + 8, dst_len: 8, |
| 536 | dst_byte_order: byte_order, error)) { |
| 537 | value.SetBytes(bytes: heap_data_up.release(), len: byte_size); |
| 538 | return ValueObjectConstResult::Create( |
| 539 | exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), value, name: ConstString("" )); |
| 540 | } |
| 541 | } |
| 542 | break; |
| 543 | } |
| 544 | default: |
| 545 | return return_valobj_sp; |
| 546 | } |
| 547 | |
| 548 | if (type_flags & eTypeIsInteger) { |
| 549 | const bool is_signed = (type_flags & eTypeIsSigned) != 0; |
| 550 | if (!SetSizedInteger(scalar&: value.GetScalar(), raw_value, size_in_bytes: byte_size, is_signed)) |
| 551 | return return_valobj_sp; |
| 552 | } else if (type_flags & eTypeIsFloat) { |
| 553 | if (!SetSizedFloat(scalar&: value.GetScalar(), raw_value, size_in_bytes: byte_size)) |
| 554 | return return_valobj_sp; |
| 555 | } else |
| 556 | return return_valobj_sp; |
| 557 | |
| 558 | value.SetValueType(Value::ValueType::Scalar); |
| 559 | return_valobj_sp = ValueObjectConstResult::Create( |
| 560 | exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), value, name: ConstString("" )); |
| 561 | return return_valobj_sp; |
| 562 | } |
| 563 | |
| 564 | static ValueObjectSP |
| 565 | GetValObjFromFPRegs(Thread &thread, const RegisterContextSP ®_ctx, |
| 566 | llvm::Triple::ArchType machine, uint32_t arch_fp_flags, |
| 567 | uint32_t type_flags, uint32_t byte_size) { |
| 568 | auto reg_info_fa0 = reg_ctx->GetRegisterInfoByName(reg_name: "fa0" ); |
| 569 | bool use_fp_regs = false; |
| 570 | ValueObjectSP return_valobj_sp; |
| 571 | |
| 572 | switch (arch_fp_flags) { |
| 573 | // fp return value in integer registers a0 and possibly a1 |
| 574 | case ArchSpec::eRISCV_float_abi_soft: |
| 575 | return_valobj_sp = |
| 576 | GetValObjFromIntRegs(thread, reg_ctx, machine, type_flags, byte_size); |
| 577 | return return_valobj_sp; |
| 578 | // fp return value in fp register fa0 (only float) |
| 579 | case ArchSpec::eRISCV_float_abi_single: |
| 580 | if (byte_size <= 4) |
| 581 | use_fp_regs = true; |
| 582 | break; |
| 583 | // fp return value in fp registers fa0 (float, double) |
| 584 | case ArchSpec::eRISCV_float_abi_double: |
| 585 | [[fallthrough]]; |
| 586 | // fp return value in fp registers fa0 (float, double, quad) |
| 587 | // not implemented; act like they're doubles |
| 588 | case ArchSpec::eRISCV_float_abi_quad: |
| 589 | if (byte_size <= 8) |
| 590 | use_fp_regs = true; |
| 591 | break; |
| 592 | } |
| 593 | |
| 594 | if (use_fp_regs) { |
| 595 | uint64_t raw_value; |
| 596 | Value value; |
| 597 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_fa0, fail_value: 0); |
| 598 | if (!SetSizedFloat(scalar&: value.GetScalar(), raw_value, size_in_bytes: byte_size)) |
| 599 | return return_valobj_sp; |
| 600 | value.SetValueType(Value::ValueType::Scalar); |
| 601 | return ValueObjectConstResult::Create(exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), |
| 602 | value, name: ConstString("" )); |
| 603 | } |
| 604 | // we should never reach this, but if we do, use the integer registers |
| 605 | return GetValObjFromIntRegs(thread, reg_ctx, machine, type_flags, byte_size); |
| 606 | } |
| 607 | |
| 608 | ValueObjectSP |
| 609 | ABISysV_riscv::GetReturnValueObjectSimple(Thread &thread, |
| 610 | CompilerType &compiler_type) const { |
| 611 | ValueObjectSP return_valobj_sp; |
| 612 | |
| 613 | if (!compiler_type) |
| 614 | return return_valobj_sp; |
| 615 | |
| 616 | auto reg_ctx = thread.GetRegisterContext(); |
| 617 | if (!reg_ctx) |
| 618 | return return_valobj_sp; |
| 619 | |
| 620 | Value value; |
| 621 | value.SetCompilerType(compiler_type); |
| 622 | |
| 623 | const uint32_t type_flags = compiler_type.GetTypeInfo(); |
| 624 | const size_t byte_size = |
| 625 | llvm::expectedToOptional(E: compiler_type.GetByteSize(exe_scope: &thread)).value_or(u: 0); |
| 626 | const ArchSpec arch = thread.GetProcess()->GetTarget().GetArchitecture(); |
| 627 | const llvm::Triple::ArchType machine = arch.GetMachine(); |
| 628 | |
| 629 | // Integer return type. |
| 630 | if (type_flags & eTypeIsInteger) { |
| 631 | return_valobj_sp = |
| 632 | GetValObjFromIntRegs(thread, reg_ctx, machine, type_flags, byte_size); |
| 633 | return return_valobj_sp; |
| 634 | } |
| 635 | // Pointer return type. |
| 636 | else if (type_flags & eTypeIsPointer) { |
| 637 | auto reg_info_a0 = reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, |
| 638 | LLDB_REGNUM_GENERIC_ARG1); |
| 639 | value.GetScalar() = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0); |
| 640 | value.SetValueType(Value::ValueType::Scalar); |
| 641 | return ValueObjectConstResult::Create(exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), |
| 642 | value, name: ConstString("" )); |
| 643 | } |
| 644 | // Floating point return type. |
| 645 | else if (type_flags & eTypeIsFloat) { |
| 646 | uint32_t float_count = 0; |
| 647 | bool is_complex = false; |
| 648 | |
| 649 | if (compiler_type.IsFloatingPointType(count&: float_count, is_complex) && |
| 650 | float_count == 1 && !is_complex) { |
| 651 | const uint32_t arch_fp_flags = |
| 652 | arch.GetFlags() & ArchSpec::eRISCV_float_abi_mask; |
| 653 | return_valobj_sp = GetValObjFromFPRegs( |
| 654 | thread, reg_ctx, machine, arch_fp_flags, type_flags, byte_size); |
| 655 | return return_valobj_sp; |
| 656 | } |
| 657 | } |
| 658 | // Unsupported return type. |
| 659 | return return_valobj_sp; |
| 660 | } |
| 661 | |
| 662 | ValueObjectSP |
| 663 | ABISysV_riscv::GetReturnValueObjectImpl(lldb_private::Thread &thread, |
| 664 | llvm::Type &type) const { |
| 665 | Value value; |
| 666 | ValueObjectSP return_valobj_sp; |
| 667 | |
| 668 | auto reg_ctx = thread.GetRegisterContext(); |
| 669 | if (!reg_ctx) |
| 670 | return return_valobj_sp; |
| 671 | |
| 672 | uint32_t type_flags = 0; |
| 673 | if (type.isIntegerTy()) |
| 674 | type_flags = eTypeIsInteger; |
| 675 | else if (type.isVoidTy()) |
| 676 | type_flags = eTypeIsPointer; |
| 677 | else if (type.isFloatTy()) |
| 678 | type_flags = eTypeIsFloat; |
| 679 | |
| 680 | const uint32_t byte_size = type.getPrimitiveSizeInBits() / CHAR_BIT; |
| 681 | const ArchSpec arch = thread.GetProcess()->GetTarget().GetArchitecture(); |
| 682 | const llvm::Triple::ArchType machine = arch.GetMachine(); |
| 683 | |
| 684 | // Integer return type. |
| 685 | if (type_flags & eTypeIsInteger) { |
| 686 | return_valobj_sp = |
| 687 | GetValObjFromIntRegs(thread, reg_ctx, machine, type_flags, byte_size); |
| 688 | return return_valobj_sp; |
| 689 | } |
| 690 | // Pointer return type. |
| 691 | else if (type_flags & eTypeIsPointer) { |
| 692 | auto reg_info_a0 = reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, |
| 693 | LLDB_REGNUM_GENERIC_ARG1); |
| 694 | value.GetScalar() = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0); |
| 695 | value.SetValueType(Value::ValueType::Scalar); |
| 696 | return ValueObjectConstResult::Create(exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), |
| 697 | value, name: ConstString("" )); |
| 698 | } |
| 699 | // Floating point return type. |
| 700 | else if (type_flags & eTypeIsFloat) { |
| 701 | const uint32_t arch_fp_flags = |
| 702 | arch.GetFlags() & ArchSpec::eRISCV_float_abi_mask; |
| 703 | return_valobj_sp = GetValObjFromFPRegs( |
| 704 | thread, reg_ctx, machine, arch_fp_flags, type_flags, byte_size); |
| 705 | return return_valobj_sp; |
| 706 | } |
| 707 | // Unsupported return type. |
| 708 | return return_valobj_sp; |
| 709 | } |
| 710 | |
| 711 | ValueObjectSP ABISysV_riscv::GetReturnValueObjectImpl( |
| 712 | Thread &thread, CompilerType &return_compiler_type) const { |
| 713 | ValueObjectSP return_valobj_sp; |
| 714 | |
| 715 | if (!return_compiler_type) |
| 716 | return return_valobj_sp; |
| 717 | |
| 718 | ExecutionContext exe_ctx(thread.shared_from_this()); |
| 719 | return GetReturnValueObjectSimple(thread, compiler_type&: return_compiler_type); |
| 720 | } |
| 721 | |
| 722 | UnwindPlanSP ABISysV_riscv::CreateFunctionEntryUnwindPlan() { |
| 723 | uint32_t pc_reg_num = riscv_dwarf::dwarf_gpr_pc; |
| 724 | uint32_t sp_reg_num = riscv_dwarf::dwarf_gpr_sp; |
| 725 | uint32_t ra_reg_num = riscv_dwarf::dwarf_gpr_ra; |
| 726 | |
| 727 | UnwindPlan::Row row; |
| 728 | |
| 729 | // Define CFA as the stack pointer |
| 730 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: sp_reg_num, offset: 0); |
| 731 | |
| 732 | // Previous frame's pc is in ra |
| 733 | row.SetRegisterLocationToRegister(reg_num: pc_reg_num, other_reg_num: ra_reg_num, can_replace: true); |
| 734 | |
| 735 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
| 736 | plan_sp->AppendRow(row: std::move(row)); |
| 737 | plan_sp->SetSourceName("riscv function-entry unwind plan" ); |
| 738 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
| 739 | return plan_sp; |
| 740 | } |
| 741 | |
| 742 | UnwindPlanSP ABISysV_riscv::CreateDefaultUnwindPlan() { |
| 743 | uint32_t pc_reg_num = LLDB_REGNUM_GENERIC_PC; |
| 744 | uint32_t fp_reg_num = LLDB_REGNUM_GENERIC_FP; |
| 745 | |
| 746 | UnwindPlan::Row row; |
| 747 | |
| 748 | // Define the CFA as the current frame pointer value. |
| 749 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: fp_reg_num, offset: 0); |
| 750 | |
| 751 | int reg_size = 4; |
| 752 | if (m_is_rv64) |
| 753 | reg_size = 8; |
| 754 | |
| 755 | // Assume the ra reg (return pc) and caller's frame pointer |
| 756 | // have been spilled to stack already. |
| 757 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: fp_reg_num, offset: reg_size * -2, can_replace: true); |
| 758 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: pc_reg_num, offset: reg_size * -1, can_replace: true); |
| 759 | |
| 760 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindGeneric); |
| 761 | plan_sp->AppendRow(row: std::move(row)); |
| 762 | plan_sp->SetSourceName("riscv default unwind plan" ); |
| 763 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
| 764 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
| 765 | return plan_sp; |
| 766 | } |
| 767 | |
| 768 | bool ABISysV_riscv::RegisterIsVolatile(const RegisterInfo *reg_info) { |
| 769 | return !RegisterIsCalleeSaved(reg_info); |
| 770 | } |
| 771 | |
| 772 | bool ABISysV_riscv::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
| 773 | if (!reg_info) |
| 774 | return false; |
| 775 | |
| 776 | const char *name = reg_info->name; |
| 777 | ArchSpec arch = GetProcessSP()->GetTarget().GetArchitecture(); |
| 778 | uint32_t arch_flags = arch.GetFlags(); |
| 779 | // floating point registers are only callee saved when using |
| 780 | // F, D or Q hardware floating point ABIs |
| 781 | bool is_hw_fp = (arch_flags & ArchSpec::eRISCV_float_abi_mask) != 0; |
| 782 | |
| 783 | bool is_callee_saved = |
| 784 | llvm::StringSwitch<bool>(name) |
| 785 | // integer ABI names |
| 786 | .Cases(S0: "ra" , S1: "sp" , S2: "fp" , Value: true) |
| 787 | .Cases(S0: "s0" , S1: "s1" , S2: "s2" , S3: "s3" , S4: "s4" , S5: "s5" , S6: "s6" , S7: "s7" , S8: "s8" , S9: "s9" , |
| 788 | Value: true) |
| 789 | .Cases(S0: "s10" , S1: "s11" , Value: true) |
| 790 | // integer hardware names |
| 791 | .Cases(S0: "x1" , S1: "x2" , S2: "x8" , S3: "x9" , S4: "x18" , S5: "x19" , S6: "x20" , S7: "x21" , S8: "x22" , |
| 792 | Value: true) |
| 793 | .Cases(S0: "x23" , S1: "x24" , S2: "x25" , S3: "x26" , S4: "x27" , Value: true) |
| 794 | // floating point ABI names |
| 795 | .Cases(S0: "fs0" , S1: "fs1" , S2: "fs2" , S3: "fs3" , S4: "fs4" , S5: "fs5" , S6: "fs6" , S7: "fs7" , |
| 796 | Value: is_hw_fp) |
| 797 | .Cases(S0: "fs8" , S1: "fs9" , S2: "fs10" , S3: "fs11" , Value: is_hw_fp) |
| 798 | // floating point hardware names |
| 799 | .Cases(S0: "f8" , S1: "f9" , S2: "f18" , S3: "f19" , S4: "f20" , S5: "f21" , S6: "f22" , S7: "f23" , Value: is_hw_fp) |
| 800 | .Cases(S0: "f24" , S1: "f25" , S2: "f26" , S3: "f27" , Value: is_hw_fp) |
| 801 | .Default(Value: false); |
| 802 | |
| 803 | return is_callee_saved; |
| 804 | } |
| 805 | |
| 806 | void ABISysV_riscv::Initialize() { |
| 807 | PluginManager::RegisterPlugin( |
| 808 | name: GetPluginNameStatic(), description: "System V ABI for RISCV targets" , create_callback: CreateInstance); |
| 809 | } |
| 810 | |
| 811 | void ABISysV_riscv::Terminate() { |
| 812 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
| 813 | } |
| 814 | |
| 815 | static uint32_t GetGenericNum(llvm::StringRef name) { |
| 816 | return llvm::StringSwitch<uint32_t>(name) |
| 817 | .Case(S: "pc" , LLDB_REGNUM_GENERIC_PC) |
| 818 | .Cases(S0: "ra" , S1: "x1" , LLDB_REGNUM_GENERIC_RA) |
| 819 | .Cases(S0: "sp" , S1: "x2" , LLDB_REGNUM_GENERIC_SP) |
| 820 | .Cases(S0: "fp" , S1: "s0" , LLDB_REGNUM_GENERIC_FP) |
| 821 | .Case(S: "a0" , LLDB_REGNUM_GENERIC_ARG1) |
| 822 | .Case(S: "a1" , LLDB_REGNUM_GENERIC_ARG2) |
| 823 | .Case(S: "a2" , LLDB_REGNUM_GENERIC_ARG3) |
| 824 | .Case(S: "a3" , LLDB_REGNUM_GENERIC_ARG4) |
| 825 | .Case(S: "a4" , LLDB_REGNUM_GENERIC_ARG5) |
| 826 | .Case(S: "a5" , LLDB_REGNUM_GENERIC_ARG6) |
| 827 | .Case(S: "a6" , LLDB_REGNUM_GENERIC_ARG7) |
| 828 | .Case(S: "a7" , LLDB_REGNUM_GENERIC_ARG8) |
| 829 | .Default(LLDB_INVALID_REGNUM); |
| 830 | } |
| 831 | |
| 832 | void ABISysV_riscv::AugmentRegisterInfo( |
| 833 | std::vector<lldb_private::DynamicRegisterInfo::Register> ®s) { |
| 834 | lldb_private::RegInfoBasedABI::AugmentRegisterInfo(regs); |
| 835 | |
| 836 | for (auto it : llvm::enumerate(First&: regs)) { |
| 837 | // Set alt name for certain registers for convenience |
| 838 | if (it.value().name == "zero" ) |
| 839 | it.value().alt_name.SetCString("x0" ); |
| 840 | else if (it.value().name == "ra" ) |
| 841 | it.value().alt_name.SetCString("x1" ); |
| 842 | else if (it.value().name == "sp" ) |
| 843 | it.value().alt_name.SetCString("x2" ); |
| 844 | else if (it.value().name == "gp" ) |
| 845 | it.value().alt_name.SetCString("x3" ); |
| 846 | else if (it.value().name == "fp" ) |
| 847 | it.value().alt_name.SetCString("s0" ); |
| 848 | else if (it.value().name == "tp" ) |
| 849 | it.value().alt_name.SetCString("x4" ); |
| 850 | else if (it.value().name == "s0" ) |
| 851 | it.value().alt_name.SetCString("x8" ); |
| 852 | else if (it.value().name == "s1" ) |
| 853 | it.value().alt_name.SetCString("x9" ); |
| 854 | else if (it.value().name == "t0" ) |
| 855 | it.value().alt_name.SetCString("x5" ); |
| 856 | else if (it.value().name == "t1" ) |
| 857 | it.value().alt_name.SetCString("x6" ); |
| 858 | else if (it.value().name == "t2" ) |
| 859 | it.value().alt_name.SetCString("x7" ); |
| 860 | else if (it.value().name == "a0" ) |
| 861 | it.value().alt_name.SetCString("x10" ); |
| 862 | else if (it.value().name == "a1" ) |
| 863 | it.value().alt_name.SetCString("x11" ); |
| 864 | else if (it.value().name == "a2" ) |
| 865 | it.value().alt_name.SetCString("x12" ); |
| 866 | else if (it.value().name == "a3" ) |
| 867 | it.value().alt_name.SetCString("x13" ); |
| 868 | else if (it.value().name == "a4" ) |
| 869 | it.value().alt_name.SetCString("x14" ); |
| 870 | else if (it.value().name == "a5" ) |
| 871 | it.value().alt_name.SetCString("x15" ); |
| 872 | else if (it.value().name == "a6" ) |
| 873 | it.value().alt_name.SetCString("x16" ); |
| 874 | else if (it.value().name == "a7" ) |
| 875 | it.value().alt_name.SetCString("x17" ); |
| 876 | else if (it.value().name == "s2" ) |
| 877 | it.value().alt_name.SetCString("x18" ); |
| 878 | else if (it.value().name == "s3" ) |
| 879 | it.value().alt_name.SetCString("x19" ); |
| 880 | else if (it.value().name == "s4" ) |
| 881 | it.value().alt_name.SetCString("x20" ); |
| 882 | else if (it.value().name == "s5" ) |
| 883 | it.value().alt_name.SetCString("x21" ); |
| 884 | else if (it.value().name == "s6" ) |
| 885 | it.value().alt_name.SetCString("x22" ); |
| 886 | else if (it.value().name == "s7" ) |
| 887 | it.value().alt_name.SetCString("x23" ); |
| 888 | else if (it.value().name == "s8" ) |
| 889 | it.value().alt_name.SetCString("x24" ); |
| 890 | else if (it.value().name == "s9" ) |
| 891 | it.value().alt_name.SetCString("x25" ); |
| 892 | else if (it.value().name == "s10" ) |
| 893 | it.value().alt_name.SetCString("x26" ); |
| 894 | else if (it.value().name == "s11" ) |
| 895 | it.value().alt_name.SetCString("x27" ); |
| 896 | else if (it.value().name == "t3" ) |
| 897 | it.value().alt_name.SetCString("x28" ); |
| 898 | else if (it.value().name == "t4" ) |
| 899 | it.value().alt_name.SetCString("x29" ); |
| 900 | else if (it.value().name == "t5" ) |
| 901 | it.value().alt_name.SetCString("x30" ); |
| 902 | else if (it.value().name == "t6" ) |
| 903 | it.value().alt_name.SetCString("x31" ); |
| 904 | |
| 905 | // Set generic regnum so lldb knows what the PC, etc is |
| 906 | it.value().regnum_generic = GetGenericNum(name: it.value().name.GetStringRef()); |
| 907 | } |
| 908 | } |
| 909 | |