1 | //===-- ABISysV_loongarch.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_loongarch.h" |
10 | |
11 | #include <array> |
12 | #include <limits> |
13 | #include <sstream> |
14 | |
15 | #include "llvm/ADT/StringRef.h" |
16 | #include "llvm/IR/DerivedTypes.h" |
17 | #include "llvm/Support/MathExtras.h" |
18 | |
19 | #include "Utility/LoongArch_DWARF_Registers.h" |
20 | #include "lldb/Core/PluginManager.h" |
21 | #include "lldb/Core/Value.h" |
22 | #include "lldb/Target/RegisterContext.h" |
23 | #include "lldb/Target/StackFrame.h" |
24 | #include "lldb/Target/Thread.h" |
25 | #include "lldb/Utility/LLDBLog.h" |
26 | #include "lldb/Utility/RegisterValue.h" |
27 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
28 | |
29 | #define DEFINE_REG_NAME(reg_num) ConstString(#reg_num).GetCString() |
30 | #define DEFINE_REG_NAME_STR(reg_name) ConstString(reg_name).GetCString() |
31 | |
32 | // The ABI is not a source of such information as size, offset, encoding, etc. |
33 | // of a register. Just provides correct dwarf and eh_frame numbers. |
34 | |
35 | #define DEFINE_GENERIC_REGISTER_STUB(dwarf_num, generic_num) \ |
36 | { \ |
37 | DEFINE_REG_NAME(dwarf_num), \ |
38 | DEFINE_REG_NAME_STR(nullptr), \ |
39 | 0, \ |
40 | 0, \ |
41 | eEncodingInvalid, \ |
42 | eFormatDefault, \ |
43 | {dwarf_num, dwarf_num, generic_num, LLDB_INVALID_REGNUM, dwarf_num}, \ |
44 | nullptr, \ |
45 | nullptr, \ |
46 | nullptr, \ |
47 | } |
48 | |
49 | #define DEFINE_REGISTER_STUB(dwarf_num) \ |
50 | DEFINE_GENERIC_REGISTER_STUB(dwarf_num, LLDB_INVALID_REGNUM) |
51 | |
52 | using namespace lldb; |
53 | using namespace lldb_private; |
54 | |
55 | LLDB_PLUGIN_DEFINE_ADV(ABISysV_loongarch, ABILoongArch) |
56 | |
57 | namespace { |
58 | namespace dwarf { |
59 | enum regnums { |
60 | r0, |
61 | r1, |
62 | ra = r1, |
63 | r2, |
64 | r3, |
65 | sp = r3, |
66 | r4, |
67 | r5, |
68 | r6, |
69 | r7, |
70 | r8, |
71 | r9, |
72 | r10, |
73 | r11, |
74 | r12, |
75 | r13, |
76 | r14, |
77 | r15, |
78 | r16, |
79 | r17, |
80 | r18, |
81 | r19, |
82 | r20, |
83 | r21, |
84 | r22, |
85 | fp = r22, |
86 | r23, |
87 | r24, |
88 | r25, |
89 | r26, |
90 | r27, |
91 | r28, |
92 | r29, |
93 | r30, |
94 | r31, |
95 | pc |
96 | }; |
97 | |
98 | static const std::array<RegisterInfo, 33> g_register_infos = { |
99 | ._M_elems: {DEFINE_REGISTER_STUB(r0), |
100 | DEFINE_GENERIC_REGISTER_STUB(r1, LLDB_REGNUM_GENERIC_RA), |
101 | DEFINE_REGISTER_STUB(r2), |
102 | DEFINE_GENERIC_REGISTER_STUB(r3, LLDB_REGNUM_GENERIC_SP), |
103 | DEFINE_GENERIC_REGISTER_STUB(r4, LLDB_REGNUM_GENERIC_ARG1), |
104 | DEFINE_GENERIC_REGISTER_STUB(r5, LLDB_REGNUM_GENERIC_ARG2), |
105 | DEFINE_GENERIC_REGISTER_STUB(r6, LLDB_REGNUM_GENERIC_ARG3), |
106 | DEFINE_GENERIC_REGISTER_STUB(r7, LLDB_REGNUM_GENERIC_ARG4), |
107 | DEFINE_GENERIC_REGISTER_STUB(r8, LLDB_REGNUM_GENERIC_ARG5), |
108 | DEFINE_GENERIC_REGISTER_STUB(r9, LLDB_REGNUM_GENERIC_ARG6), |
109 | DEFINE_GENERIC_REGISTER_STUB(r10, LLDB_REGNUM_GENERIC_ARG7), |
110 | DEFINE_GENERIC_REGISTER_STUB(r11, LLDB_REGNUM_GENERIC_ARG8), |
111 | DEFINE_REGISTER_STUB(r12), |
112 | DEFINE_REGISTER_STUB(r13), |
113 | DEFINE_REGISTER_STUB(r14), |
114 | DEFINE_REGISTER_STUB(r15), |
115 | DEFINE_REGISTER_STUB(r16), |
116 | DEFINE_REGISTER_STUB(r17), |
117 | DEFINE_REGISTER_STUB(r18), |
118 | DEFINE_REGISTER_STUB(r19), |
119 | DEFINE_REGISTER_STUB(r20), |
120 | DEFINE_REGISTER_STUB(r21), |
121 | DEFINE_GENERIC_REGISTER_STUB(r22, LLDB_REGNUM_GENERIC_FP), |
122 | DEFINE_REGISTER_STUB(r23), |
123 | DEFINE_REGISTER_STUB(r24), |
124 | DEFINE_REGISTER_STUB(r25), |
125 | DEFINE_REGISTER_STUB(r26), |
126 | DEFINE_REGISTER_STUB(r27), |
127 | DEFINE_REGISTER_STUB(r28), |
128 | DEFINE_REGISTER_STUB(r29), |
129 | DEFINE_REGISTER_STUB(r30), |
130 | DEFINE_REGISTER_STUB(r31), |
131 | DEFINE_GENERIC_REGISTER_STUB(pc, LLDB_REGNUM_GENERIC_PC)}}; |
132 | } // namespace dwarf |
133 | } // namespace |
134 | |
135 | // Number of argument registers (the base integer calling convention |
136 | // provides 8 argument registers, a0-a7) |
137 | static constexpr size_t g_regs_for_args_count = 8U; |
138 | |
139 | const RegisterInfo *ABISysV_loongarch::GetRegisterInfoArray(uint32_t &count) { |
140 | count = dwarf::g_register_infos.size(); |
141 | return dwarf::g_register_infos.data(); |
142 | } |
143 | |
144 | //------------------------------------------------------------------ |
145 | // Static Functions |
146 | //------------------------------------------------------------------ |
147 | |
148 | ABISP |
149 | ABISysV_loongarch::CreateInstance(ProcessSP process_sp, const ArchSpec &arch) { |
150 | llvm::Triple::ArchType machine = arch.GetTriple().getArch(); |
151 | |
152 | if (llvm::Triple::loongarch32 != machine && |
153 | llvm::Triple::loongarch64 != machine) |
154 | return ABISP(); |
155 | |
156 | ABISysV_loongarch *abi = |
157 | new ABISysV_loongarch(std::move(process_sp), MakeMCRegisterInfo(arch)); |
158 | if (abi) |
159 | abi->SetIsLA64(llvm::Triple::loongarch64 == machine); |
160 | return ABISP(abi); |
161 | } |
162 | |
163 | static bool UpdateRegister(RegisterContext *reg_ctx, |
164 | const lldb::RegisterKind reg_kind, |
165 | const uint32_t reg_num, const addr_t value) { |
166 | Log *log = GetLog(mask: LLDBLog::Expressions); |
167 | |
168 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(reg_kind, reg_num); |
169 | |
170 | LLDB_LOG(log, "Writing {0}: 0x{1:x}" , reg_info->name, |
171 | static_cast<uint64_t>(value)); |
172 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, uval: value)) { |
173 | LLDB_LOG(log, "Writing {0}: failed" , reg_info->name); |
174 | return false; |
175 | } |
176 | return true; |
177 | } |
178 | |
179 | static void LogInitInfo(Log &log, const Thread &thread, addr_t sp, |
180 | addr_t func_addr, addr_t return_addr, |
181 | const llvm::ArrayRef<addr_t> args) { |
182 | std::stringstream ss; |
183 | ss << "ABISysV_loongarch::PrepareTrivialCall" |
184 | << " (tid = 0x" << std::hex << thread.GetID() << ", sp = 0x" << sp |
185 | << ", func_addr = 0x" << func_addr << ", return_addr = 0x" << return_addr; |
186 | |
187 | for (auto [idx, arg] : enumerate(First: args)) |
188 | ss << ", arg" << std::dec << idx << " = 0x" << std::hex << arg; |
189 | ss << ")" ; |
190 | log.PutString(str: ss.str()); |
191 | } |
192 | |
193 | bool ABISysV_loongarch::PrepareTrivialCall(Thread &thread, addr_t sp, |
194 | addr_t func_addr, addr_t return_addr, |
195 | llvm::ArrayRef<addr_t> args) const { |
196 | Log *log = GetLog(mask: LLDBLog::Expressions); |
197 | if (log) |
198 | LogInitInfo(log&: *log, thread, sp, func_addr, return_addr, args); |
199 | |
200 | const auto reg_ctx_sp = thread.GetRegisterContext(); |
201 | if (!reg_ctx_sp) { |
202 | LLDB_LOG(log, "Failed to get RegisterContext" ); |
203 | return false; |
204 | } |
205 | |
206 | if (args.size() > g_regs_for_args_count) { |
207 | LLDB_LOG(log, "Function has {0} arguments, but only {1} are allowed!" , |
208 | args.size(), g_regs_for_args_count); |
209 | return false; |
210 | } |
211 | |
212 | // Write arguments to registers |
213 | for (auto [idx, arg] : enumerate(First&: args)) { |
214 | const RegisterInfo *reg_info = reg_ctx_sp->GetRegisterInfo( |
215 | reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + idx); |
216 | LLDB_LOG(log, "About to write arg{0} ({1:x}) into {2}" , idx, arg, |
217 | reg_info->name); |
218 | |
219 | if (!reg_ctx_sp->WriteRegisterFromUnsigned(reg_info, uval: arg)) { |
220 | LLDB_LOG(log, "Failed to write arg{0} ({1:x}) into {2}" , idx, arg, |
221 | reg_info->name); |
222 | return false; |
223 | } |
224 | } |
225 | |
226 | if (!UpdateRegister(reg_ctx: reg_ctx_sp.get(), reg_kind: eRegisterKindGeneric, |
227 | LLDB_REGNUM_GENERIC_PC, value: func_addr)) |
228 | return false; |
229 | if (!UpdateRegister(reg_ctx: reg_ctx_sp.get(), reg_kind: eRegisterKindGeneric, |
230 | LLDB_REGNUM_GENERIC_SP, value: sp)) |
231 | return false; |
232 | if (!UpdateRegister(reg_ctx: reg_ctx_sp.get(), reg_kind: eRegisterKindGeneric, |
233 | LLDB_REGNUM_GENERIC_RA, value: return_addr)) |
234 | return false; |
235 | |
236 | LLDB_LOG(log, "ABISysV_loongarch::{0}() success" , __FUNCTION__); |
237 | return true; |
238 | } |
239 | |
240 | bool ABISysV_loongarch::GetArgumentValues(Thread &thread, |
241 | ValueList &values) const { |
242 | // TODO: Implement |
243 | return false; |
244 | } |
245 | |
246 | Status ABISysV_loongarch::SetReturnValueObject(StackFrameSP &frame_sp, |
247 | ValueObjectSP &new_value_sp) { |
248 | Status result; |
249 | if (!new_value_sp) { |
250 | result = Status::FromErrorString(str: "Empty value object for return value." ); |
251 | return result; |
252 | } |
253 | |
254 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
255 | if (!compiler_type) { |
256 | result = Status::FromErrorString(str: "Null clang type for return value." ); |
257 | return result; |
258 | } |
259 | |
260 | auto ®_ctx = *frame_sp->GetThread()->GetRegisterContext(); |
261 | |
262 | bool is_signed = false; |
263 | if (!compiler_type.IsIntegerOrEnumerationType(is_signed) && |
264 | !compiler_type.IsPointerType()) { |
265 | result = Status::FromErrorString( |
266 | str: "We don't support returning other types at present" ); |
267 | return result; |
268 | } |
269 | |
270 | DataExtractor data; |
271 | size_t num_bytes = new_value_sp->GetData(data, error&: result); |
272 | |
273 | if (result.Fail()) { |
274 | result = Status::FromErrorStringWithFormat( |
275 | format: "Couldn't convert return value to raw data: %s" , result.AsCString()); |
276 | return result; |
277 | } |
278 | |
279 | size_t reg_size = m_is_la64 ? 8 : 4; |
280 | // Currently, we only support sizeof(data) <= 2 * reg_size. |
281 | // 1. If the (`size` <= reg_size), the `data` will be returned through `ARG1`. |
282 | // 2. If the (`size` > reg_size && `size` <= 2 * reg_size), the `data` will be |
283 | // returned through a pair of registers (ARG1 and ARG2), and the lower-ordered |
284 | // bits in the `ARG1`. |
285 | if (num_bytes > 2 * reg_size) { |
286 | result = Status::FromErrorString( |
287 | str: "We don't support returning large integer values at present." ); |
288 | return result; |
289 | } |
290 | |
291 | offset_t offset = 0; |
292 | uint64_t raw_value = data.GetMaxU64(offset_ptr: &offset, byte_size: num_bytes); |
293 | // According to psABI, i32 (no matter signed or unsigned) should be |
294 | // sign-extended in register. |
295 | if (4 == num_bytes && m_is_la64) |
296 | raw_value = llvm::SignExtend64<32>(x: raw_value); |
297 | auto reg_info = |
298 | reg_ctx.GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); |
299 | if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, uval: raw_value)) { |
300 | result = Status::FromErrorStringWithFormat( |
301 | format: "Couldn't write value to register %s" , reg_info->name); |
302 | return result; |
303 | } |
304 | |
305 | if (num_bytes <= reg_size) |
306 | return result; // Successfully written. |
307 | |
308 | // For loongarch32, get the upper 32 bits from raw_value and write them. |
309 | // For loongarch64, get the next 64 bits from data and write them. |
310 | if (4 == reg_size) |
311 | raw_value >>= 32; |
312 | else |
313 | raw_value = data.GetMaxU64(offset_ptr: &offset, byte_size: num_bytes - reg_size); |
314 | |
315 | reg_info = |
316 | reg_ctx.GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); |
317 | if (!reg_ctx.WriteRegisterFromUnsigned(reg_info, uval: raw_value)) |
318 | result = Status::FromErrorStringWithFormat( |
319 | format: "Couldn't write value to register %s" , reg_info->name); |
320 | |
321 | return result; |
322 | } |
323 | |
324 | template <typename T> |
325 | static void SetInteger(Scalar &scalar, uint64_t raw_value, bool is_signed) { |
326 | static_assert(std::is_unsigned<T>::value, "T must be an unsigned type." ); |
327 | raw_value &= std::numeric_limits<T>::max(); |
328 | if (is_signed) |
329 | scalar = static_cast<typename std::make_signed<T>::type>(raw_value); |
330 | else |
331 | scalar = static_cast<T>(raw_value); |
332 | } |
333 | |
334 | static bool SetSizedInteger(Scalar &scalar, uint64_t raw_value, |
335 | uint8_t size_in_bytes, bool is_signed) { |
336 | switch (size_in_bytes) { |
337 | default: |
338 | return false; |
339 | |
340 | case sizeof(uint64_t): |
341 | SetInteger<uint64_t>(scalar, raw_value, is_signed); |
342 | break; |
343 | |
344 | case sizeof(uint32_t): |
345 | SetInteger<uint32_t>(scalar, raw_value, is_signed); |
346 | break; |
347 | |
348 | case sizeof(uint16_t): |
349 | SetInteger<uint16_t>(scalar, raw_value, is_signed); |
350 | break; |
351 | |
352 | case sizeof(uint8_t): |
353 | SetInteger<uint8_t>(scalar, raw_value, is_signed); |
354 | break; |
355 | } |
356 | |
357 | return true; |
358 | } |
359 | |
360 | static bool SetSizedFloat(Scalar &scalar, uint64_t raw_value, |
361 | uint8_t size_in_bytes) { |
362 | switch (size_in_bytes) { |
363 | default: |
364 | return false; |
365 | |
366 | case sizeof(uint64_t): |
367 | scalar = *reinterpret_cast<double *>(&raw_value); |
368 | break; |
369 | |
370 | case sizeof(uint32_t): |
371 | scalar = *reinterpret_cast<float *>(&raw_value); |
372 | break; |
373 | } |
374 | |
375 | return true; |
376 | } |
377 | |
378 | static ValueObjectSP GetValObjFromIntRegs(Thread &thread, |
379 | const RegisterContextSP ®_ctx, |
380 | llvm::Triple::ArchType machine, |
381 | uint32_t type_flags, |
382 | uint32_t byte_size) { |
383 | Value value; |
384 | ValueObjectSP return_valobj_sp; |
385 | auto *reg_info_a0 = |
386 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); |
387 | auto *reg_info_a1 = |
388 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2); |
389 | uint64_t raw_value = 0; |
390 | |
391 | switch (byte_size) { |
392 | case sizeof(uint32_t): |
393 | // Read a0 to get the arg |
394 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0) & UINT32_MAX; |
395 | break; |
396 | case sizeof(uint64_t): |
397 | // Read a0 to get the arg on loongarch64, a0 and a1 on loongarch32 |
398 | if (llvm::Triple::loongarch32 == machine) { |
399 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0) & UINT32_MAX; |
400 | raw_value |= |
401 | (reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a1, fail_value: 0) & UINT32_MAX) << 32U; |
402 | } else { |
403 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0); |
404 | } |
405 | break; |
406 | case 16: { |
407 | // Read a0 and a1 to get the arg on loongarch64, not supported on |
408 | // loongarch32 |
409 | if (llvm::Triple::loongarch32 == machine) |
410 | return return_valobj_sp; |
411 | |
412 | // Create the ValueObjectSP here and return |
413 | std::unique_ptr<DataBufferHeap> heap_data_up( |
414 | new DataBufferHeap(byte_size, 0)); |
415 | const ByteOrder byte_order = thread.GetProcess()->GetByteOrder(); |
416 | RegisterValue reg_value_a0, reg_value_a1; |
417 | if (reg_ctx->ReadRegister(reg_info: reg_info_a0, reg_value&: reg_value_a0) && |
418 | reg_ctx->ReadRegister(reg_info: reg_info_a1, reg_value&: reg_value_a1)) { |
419 | Status error; |
420 | if (reg_value_a0.GetAsMemoryData(reg_info: *reg_info_a0, |
421 | dst: heap_data_up->GetBytes() + 0, dst_len: 8, |
422 | dst_byte_order: byte_order, error) && |
423 | reg_value_a1.GetAsMemoryData(reg_info: *reg_info_a1, |
424 | dst: heap_data_up->GetBytes() + 8, dst_len: 8, |
425 | dst_byte_order: byte_order, error)) { |
426 | value.SetBytes(bytes: heap_data_up.release(), len: byte_size); |
427 | return ValueObjectConstResult::Create( |
428 | exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), value, name: ConstString("" )); |
429 | } |
430 | } |
431 | break; |
432 | } |
433 | default: |
434 | return return_valobj_sp; |
435 | } |
436 | |
437 | if (type_flags & eTypeIsInteger) { |
438 | if (!SetSizedInteger(scalar&: value.GetScalar(), raw_value, size_in_bytes: byte_size, |
439 | is_signed: type_flags & eTypeIsSigned)) |
440 | return return_valobj_sp; |
441 | } else if (type_flags & eTypeIsFloat) { |
442 | if (!SetSizedFloat(scalar&: value.GetScalar(), raw_value, size_in_bytes: byte_size)) |
443 | return return_valobj_sp; |
444 | } else |
445 | return return_valobj_sp; |
446 | |
447 | value.SetValueType(Value::ValueType::Scalar); |
448 | return_valobj_sp = ValueObjectConstResult::Create( |
449 | exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), value, name: ConstString("" )); |
450 | return return_valobj_sp; |
451 | } |
452 | |
453 | static ValueObjectSP GetValObjFromFPRegs(Thread &thread, |
454 | const RegisterContextSP ®_ctx, |
455 | llvm::Triple::ArchType machine, |
456 | uint32_t type_flags, |
457 | uint32_t byte_size) { |
458 | auto *reg_info_fa0 = reg_ctx->GetRegisterInfoByName(reg_name: "f0" ); |
459 | bool use_fp_regs = false; |
460 | ValueObjectSP return_valobj_sp; |
461 | |
462 | if (byte_size <= 8) |
463 | use_fp_regs = true; |
464 | |
465 | if (use_fp_regs) { |
466 | uint64_t raw_value; |
467 | Value value; |
468 | raw_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_fa0, fail_value: 0); |
469 | if (!SetSizedFloat(scalar&: value.GetScalar(), raw_value, size_in_bytes: byte_size)) |
470 | return return_valobj_sp; |
471 | value.SetValueType(Value::ValueType::Scalar); |
472 | return ValueObjectConstResult::Create(exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), |
473 | value, name: ConstString("" )); |
474 | } |
475 | // we should never reach this, but if we do, use the integer registers |
476 | return GetValObjFromIntRegs(thread, reg_ctx, machine, type_flags, byte_size); |
477 | } |
478 | |
479 | ValueObjectSP ABISysV_loongarch::GetReturnValueObjectSimple( |
480 | Thread &thread, CompilerType &compiler_type) const { |
481 | ValueObjectSP return_valobj_sp; |
482 | |
483 | if (!compiler_type) |
484 | return return_valobj_sp; |
485 | |
486 | auto reg_ctx = thread.GetRegisterContext(); |
487 | if (!reg_ctx) |
488 | return return_valobj_sp; |
489 | |
490 | Value value; |
491 | value.SetCompilerType(compiler_type); |
492 | |
493 | const uint32_t type_flags = compiler_type.GetTypeInfo(); |
494 | const size_t byte_size = |
495 | llvm::expectedToOptional(E: compiler_type.GetByteSize(exe_scope: &thread)).value_or(u: 0); |
496 | const ArchSpec arch = thread.GetProcess()->GetTarget().GetArchitecture(); |
497 | const llvm::Triple::ArchType machine = arch.GetMachine(); |
498 | |
499 | if (type_flags & eTypeIsInteger) { |
500 | return_valobj_sp = |
501 | GetValObjFromIntRegs(thread, reg_ctx, machine, type_flags, byte_size); |
502 | return return_valobj_sp; |
503 | } |
504 | if (type_flags & eTypeIsPointer) { |
505 | const auto *reg_info_a0 = reg_ctx->GetRegisterInfo( |
506 | reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); |
507 | value.GetScalar() = reg_ctx->ReadRegisterAsUnsigned(reg_info: reg_info_a0, fail_value: 0); |
508 | value.SetValueType(Value::ValueType::Scalar); |
509 | return ValueObjectConstResult::Create(exe_scope: thread.GetStackFrameAtIndex(idx: 0).get(), |
510 | value, name: ConstString("" )); |
511 | } |
512 | if (type_flags & eTypeIsFloat) { |
513 | uint32_t float_count = 0; |
514 | bool is_complex = false; |
515 | |
516 | if (compiler_type.IsFloatingPointType(count&: float_count, is_complex) && |
517 | float_count == 1 && !is_complex) { |
518 | return_valobj_sp = |
519 | GetValObjFromFPRegs(thread, reg_ctx, machine, type_flags, byte_size); |
520 | return return_valobj_sp; |
521 | } |
522 | } |
523 | return return_valobj_sp; |
524 | } |
525 | |
526 | ValueObjectSP ABISysV_loongarch::GetReturnValueObjectImpl( |
527 | Thread &thread, CompilerType &return_compiler_type) const { |
528 | ValueObjectSP return_valobj_sp; |
529 | |
530 | if (!return_compiler_type) |
531 | return return_valobj_sp; |
532 | |
533 | ExecutionContext exe_ctx(thread.shared_from_this()); |
534 | return GetReturnValueObjectSimple(thread, compiler_type&: return_compiler_type); |
535 | } |
536 | |
537 | UnwindPlanSP ABISysV_loongarch::CreateFunctionEntryUnwindPlan() { |
538 | uint32_t pc_reg_num = loongarch_dwarf::dwarf_gpr_pc; |
539 | uint32_t sp_reg_num = loongarch_dwarf::dwarf_gpr_sp; |
540 | uint32_t ra_reg_num = loongarch_dwarf::dwarf_gpr_ra; |
541 | |
542 | UnwindPlan::Row row; |
543 | |
544 | // Define CFA as the stack pointer |
545 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: sp_reg_num, offset: 0); |
546 | |
547 | // Previous frame's pc is in ra |
548 | row.SetRegisterLocationToRegister(reg_num: pc_reg_num, other_reg_num: ra_reg_num, can_replace: true); |
549 | |
550 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
551 | plan_sp->AppendRow(row: std::move(row)); |
552 | plan_sp->SetSourceName("loongarch function-entry unwind plan" ); |
553 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
554 | return plan_sp; |
555 | } |
556 | |
557 | UnwindPlanSP ABISysV_loongarch::CreateDefaultUnwindPlan() { |
558 | uint32_t pc_reg_num = LLDB_REGNUM_GENERIC_PC; |
559 | uint32_t fp_reg_num = LLDB_REGNUM_GENERIC_FP; |
560 | |
561 | UnwindPlan::Row row; |
562 | |
563 | // Define the CFA as the current frame pointer value. |
564 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: fp_reg_num, offset: 0); |
565 | |
566 | int reg_size = 4; |
567 | if (m_is_la64) |
568 | reg_size = 8; |
569 | |
570 | // Assume the ra reg (return pc) and caller's frame pointer |
571 | // have been spilled to stack already. |
572 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: fp_reg_num, offset: reg_size * -2, can_replace: true); |
573 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: pc_reg_num, offset: reg_size * -1, can_replace: true); |
574 | |
575 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindGeneric); |
576 | plan_sp->AppendRow(row: std::move(row)); |
577 | plan_sp->SetSourceName("loongarch default unwind plan" ); |
578 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
579 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
580 | return plan_sp; |
581 | } |
582 | |
583 | bool ABISysV_loongarch::RegisterIsVolatile(const RegisterInfo *reg_info) { |
584 | return !RegisterIsCalleeSaved(reg_info); |
585 | } |
586 | |
587 | bool ABISysV_loongarch::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
588 | if (!reg_info) |
589 | return false; |
590 | |
591 | const char *name = reg_info->name; |
592 | ArchSpec arch = GetProcessSP()->GetTarget().GetArchitecture(); |
593 | uint32_t arch_flags = arch.GetFlags(); |
594 | // Floating point registers are only callee saved when using |
595 | // F or D hardware floating point ABIs. |
596 | bool is_hw_fp = (arch_flags & ArchSpec::eLoongArch_abi_mask) != 0; |
597 | |
598 | return llvm::StringSwitch<bool>(name) |
599 | // integer ABI names |
600 | .Cases(S0: "ra" , S1: "sp" , S2: "fp" , Value: true) |
601 | .Cases(S0: "s0" , S1: "s1" , S2: "s2" , S3: "s3" , S4: "s4" , S5: "s5" , S6: "s6" , S7: "s7" , S8: "s8" , S9: "s9" , Value: true) |
602 | // integer hardware names |
603 | .Cases(S0: "r1" , S1: "r3" , S2: "r22" , Value: true) |
604 | .Cases(S0: "r23" , S1: "r24" , S2: "r25" , S3: "r26" , S4: "r27" , S5: "r28" , S6: "r29" , S7: "r30" , S8: "31" , Value: true) |
605 | // floating point ABI names |
606 | .Cases(S0: "fs0" , S1: "fs1" , S2: "fs2" , S3: "fs3" , S4: "fs4" , S5: "fs5" , S6: "fs6" , S7: "fs7" , Value: is_hw_fp) |
607 | // floating point hardware names |
608 | .Cases(S0: "f24" , S1: "f25" , S2: "f26" , S3: "f27" , S4: "f28" , S5: "f29" , S6: "f30" , S7: "f31" , Value: is_hw_fp) |
609 | .Default(Value: false); |
610 | } |
611 | |
612 | void ABISysV_loongarch::Initialize() { |
613 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
614 | description: "System V ABI for LoongArch targets" , |
615 | create_callback: CreateInstance); |
616 | } |
617 | |
618 | void ABISysV_loongarch::Terminate() { |
619 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
620 | } |
621 | |
622 | static uint32_t GetGenericNum(llvm::StringRef name) { |
623 | return llvm::StringSwitch<uint32_t>(name) |
624 | .Case(S: "pc" , LLDB_REGNUM_GENERIC_PC) |
625 | .Cases(S0: "ra" , S1: "r1" , LLDB_REGNUM_GENERIC_RA) |
626 | .Cases(S0: "sp" , S1: "r3" , LLDB_REGNUM_GENERIC_SP) |
627 | .Cases(S0: "fp" , S1: "r22" , LLDB_REGNUM_GENERIC_FP) |
628 | .Cases(S0: "a0" , S1: "r4" , LLDB_REGNUM_GENERIC_ARG1) |
629 | .Cases(S0: "a1" , S1: "r5" , LLDB_REGNUM_GENERIC_ARG2) |
630 | .Cases(S0: "a2" , S1: "r6" , LLDB_REGNUM_GENERIC_ARG3) |
631 | .Cases(S0: "a3" , S1: "r7" , LLDB_REGNUM_GENERIC_ARG4) |
632 | .Cases(S0: "a4" , S1: "r8" , LLDB_REGNUM_GENERIC_ARG5) |
633 | .Cases(S0: "a5" , S1: "r9" , LLDB_REGNUM_GENERIC_ARG6) |
634 | .Cases(S0: "a6" , S1: "r10" , LLDB_REGNUM_GENERIC_ARG7) |
635 | .Cases(S0: "a7" , S1: "r11" , LLDB_REGNUM_GENERIC_ARG8) |
636 | .Default(LLDB_INVALID_REGNUM); |
637 | } |
638 | |
639 | void ABISysV_loongarch::AugmentRegisterInfo( |
640 | std::vector<lldb_private::DynamicRegisterInfo::Register> ®s) { |
641 | lldb_private::RegInfoBasedABI::AugmentRegisterInfo(regs); |
642 | |
643 | static const llvm::StringMap<llvm::StringRef> isa_to_abi_alias_map = { |
644 | {"r0" , "zero" }, {"r1" , "ra" }, {"r2" , "tp" }, {"r3" , "sp" }, |
645 | {"r4" , "a0" }, {"r5" , "a1" }, {"r6" , "a2" }, {"r7" , "a3" }, |
646 | {"r8" , "a4" }, {"r9" , "a5" }, {"r10" , "a6" }, {"r11" , "a7" }, |
647 | {"r12" , "t0" }, {"r13" , "t1" }, {"r14" , "t2" }, {"r15" , "t3" }, |
648 | {"r16" , "t4" }, {"r17" , "t5" }, {"r18" , "t6" }, {"r19" , "t7" }, |
649 | {"r20" , "t8" }, {"r22" , "fp" }, {"r23" , "s0" }, {"r24" , "s1" }, |
650 | {"r25" , "s2" }, {"r26" , "s3" }, {"r27" , "s4" }, {"r28" , "s5" }, |
651 | {"r29" , "s6" }, {"r30" , "s7" }, {"r31" , "s8" }}; |
652 | |
653 | for (auto it : llvm::enumerate(First&: regs)) { |
654 | llvm::StringRef reg_name = it.value().name.GetStringRef(); |
655 | |
656 | // Set alt name for certain registers for convenience |
657 | llvm::StringRef alias_name = isa_to_abi_alias_map.lookup(Key: reg_name); |
658 | if (!alias_name.empty()) |
659 | it.value().alt_name.SetString(alias_name); |
660 | |
661 | // Set generic regnum so lldb knows what the PC, etc is |
662 | it.value().regnum_generic = GetGenericNum(name: reg_name); |
663 | } |
664 | } |
665 | |