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 |
Definitions
- regnums
- g_register_infos
- g_regs_for_args_count
- GetRegisterInfoArray
- CreateInstance
- AugmentArgSize
- TotalArgsSizeInWords
- UpdateRegister
- LogInitInfo
- PrepareTrivialCall
- PrepareTrivialCall
- GetArgumentValues
- SetReturnValueObject
- SetInteger
- SetSizedInteger
- SetSizedFloat
- GetValObjFromIntRegs
- GetValObjFromFPRegs
- GetReturnValueObjectSimple
- GetReturnValueObjectImpl
- GetReturnValueObjectImpl
- CreateFunctionEntryUnwindPlan
- CreateDefaultUnwindPlan
- RegisterIsVolatile
- RegisterIsCalleeSaved
- Initialize
- Terminate
- GetGenericNum
Learn to use CMake with our Intro Training
Find out more