1 | //===-- ABISysV_ppc64.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_ppc64.h" |
10 | |
11 | #include "llvm/ADT/STLExtras.h" |
12 | #include "llvm/TargetParser/Triple.h" |
13 | |
14 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
15 | #include "Utility/PPC64LE_DWARF_Registers.h" |
16 | #include "Utility/PPC64_DWARF_Registers.h" |
17 | #include "lldb/Core/Module.h" |
18 | #include "lldb/Core/PluginManager.h" |
19 | #include "lldb/Core/Value.h" |
20 | #include "lldb/Symbol/UnwindPlan.h" |
21 | #include "lldb/Target/Process.h" |
22 | #include "lldb/Target/RegisterContext.h" |
23 | #include "lldb/Target/StackFrame.h" |
24 | #include "lldb/Target/Target.h" |
25 | #include "lldb/Target/Thread.h" |
26 | #include "lldb/Utility/ConstString.h" |
27 | #include "lldb/Utility/DataExtractor.h" |
28 | #include "lldb/Utility/LLDBLog.h" |
29 | #include "lldb/Utility/Log.h" |
30 | #include "lldb/Utility/RegisterValue.h" |
31 | #include "lldb/Utility/Status.h" |
32 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
33 | #include "lldb/ValueObject/ValueObjectMemory.h" |
34 | #include "lldb/ValueObject/ValueObjectRegister.h" |
35 | |
36 | #include "clang/AST/ASTContext.h" |
37 | #include "clang/AST/Attr.h" |
38 | #include "clang/AST/Decl.h" |
39 | |
40 | #define DECLARE_REGISTER_INFOS_PPC64_STRUCT |
41 | #include "Plugins/Process/Utility/RegisterInfos_ppc64.h" |
42 | #undef DECLARE_REGISTER_INFOS_PPC64_STRUCT |
43 | |
44 | #define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT |
45 | #include "Plugins/Process/Utility/RegisterInfos_ppc64le.h" |
46 | #undef DECLARE_REGISTER_INFOS_PPC64LE_STRUCT |
47 | #include <optional> |
48 | |
49 | using namespace lldb; |
50 | using namespace lldb_private; |
51 | |
52 | LLDB_PLUGIN_DEFINE(ABISysV_ppc64) |
53 | |
54 | const lldb_private::RegisterInfo * |
55 | ABISysV_ppc64::GetRegisterInfoArray(uint32_t &count) { |
56 | if (GetByteOrder() == lldb::eByteOrderLittle) { |
57 | count = std::size(g_register_infos_ppc64le); |
58 | return g_register_infos_ppc64le; |
59 | } else { |
60 | count = std::size(g_register_infos_ppc64); |
61 | return g_register_infos_ppc64; |
62 | } |
63 | } |
64 | |
65 | size_t ABISysV_ppc64::GetRedZoneSize() const { return 224; } |
66 | |
67 | lldb::ByteOrder ABISysV_ppc64::GetByteOrder() const { |
68 | return GetProcessSP()->GetByteOrder(); |
69 | } |
70 | |
71 | // Static Functions |
72 | |
73 | ABISP |
74 | ABISysV_ppc64::CreateInstance(lldb::ProcessSP process_sp, |
75 | const ArchSpec &arch) { |
76 | if (arch.GetTriple().isPPC64()) |
77 | return ABISP( |
78 | new ABISysV_ppc64(std::move(process_sp), MakeMCRegisterInfo(arch))); |
79 | return ABISP(); |
80 | } |
81 | |
82 | bool ABISysV_ppc64::PrepareTrivialCall(Thread &thread, addr_t sp, |
83 | addr_t func_addr, addr_t return_addr, |
84 | llvm::ArrayRef<addr_t> args) const { |
85 | Log *log = GetLog(mask: LLDBLog::Expressions); |
86 | |
87 | if (log) { |
88 | StreamString s; |
89 | s.Printf(format: "ABISysV_ppc64::PrepareTrivialCall (tid = 0x%"PRIx64 |
90 | ", sp = 0x%"PRIx64 ", func_addr = 0x%"PRIx64 |
91 | ", return_addr = 0x%"PRIx64, |
92 | thread.GetID(), (uint64_t)sp, (uint64_t)func_addr, |
93 | (uint64_t)return_addr); |
94 | |
95 | for (size_t i = 0; i < args.size(); ++i) |
96 | s.Printf(format: ", arg%"PRIu64 " = 0x%"PRIx64, static_cast<uint64_t>(i + 1), |
97 | args[i]); |
98 | s.PutCString(cstr: ")"); |
99 | log->PutString(str: s.GetString()); |
100 | } |
101 | |
102 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
103 | if (!reg_ctx) |
104 | return false; |
105 | |
106 | const RegisterInfo *reg_info = nullptr; |
107 | |
108 | if (args.size() > 8) // TODO handle more than 8 arguments |
109 | return false; |
110 | |
111 | for (size_t i = 0; i < args.size(); ++i) { |
112 | reg_info = reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, |
113 | LLDB_REGNUM_GENERIC_ARG1 + i); |
114 | LLDB_LOGF(log, "About to write arg%"PRIu64 " (0x%"PRIx64 ") into %s", |
115 | static_cast<uint64_t>(i + 1), args[i], reg_info->name); |
116 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, uval: args[i])) |
117 | return false; |
118 | } |
119 | |
120 | // First, align the SP |
121 | |
122 | LLDB_LOGF(log, "16-byte aligning SP: 0x%"PRIx64 " to 0x%"PRIx64, |
123 | (uint64_t)sp, (uint64_t)(sp & ~0xfull)); |
124 | |
125 | sp &= ~(0xfull); // 16-byte alignment |
126 | |
127 | sp -= 544; // allocate frame to save TOC, RA and SP. |
128 | |
129 | Status error; |
130 | uint64_t reg_value; |
131 | const RegisterInfo *pc_reg_info = |
132 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); |
133 | const RegisterInfo *sp_reg_info = |
134 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); |
135 | ProcessSP process_sp(thread.GetProcess()); |
136 | const RegisterInfo *lr_reg_info = |
137 | reg_ctx->GetRegisterInfo(reg_kind: eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); |
138 | const RegisterInfo *r2_reg_info = reg_ctx->GetRegisterInfoAtIndex(reg: 2); |
139 | const RegisterInfo *r12_reg_info = reg_ctx->GetRegisterInfoAtIndex(reg: 12); |
140 | |
141 | // Save return address onto the stack. |
142 | LLDB_LOGF(log, |
143 | "Pushing the return address onto the stack: 0x%"PRIx64 |
144 | "(+16): 0x%"PRIx64, |
145 | (uint64_t)sp, (uint64_t)return_addr); |
146 | if (!process_sp->WritePointerToMemory(vm_addr: sp + 16, ptr_value: return_addr, error)) |
147 | return false; |
148 | |
149 | // Write the return address to link register. |
150 | LLDB_LOGF(log, "Writing LR: 0x%"PRIx64, (uint64_t)return_addr); |
151 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info: lr_reg_info, uval: return_addr)) |
152 | return false; |
153 | |
154 | // Write target address to %r12 register. |
155 | LLDB_LOGF(log, "Writing R12: 0x%"PRIx64, (uint64_t)func_addr); |
156 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info: r12_reg_info, uval: func_addr)) |
157 | return false; |
158 | |
159 | // Read TOC pointer value. |
160 | reg_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: r2_reg_info, fail_value: 0); |
161 | |
162 | // Write TOC pointer onto the stack. |
163 | uint64_t stack_offset; |
164 | if (GetByteOrder() == lldb::eByteOrderLittle) |
165 | stack_offset = 24; |
166 | else |
167 | stack_offset = 40; |
168 | |
169 | LLDB_LOGF(log, "Writing R2 (TOC) at SP(0x%"PRIx64 ")+%d: 0x%"PRIx64, |
170 | (uint64_t)(sp + stack_offset), (int)stack_offset, |
171 | (uint64_t)reg_value); |
172 | if (!process_sp->WritePointerToMemory(vm_addr: sp + stack_offset, ptr_value: reg_value, error)) |
173 | return false; |
174 | |
175 | // Read the current SP value. |
176 | reg_value = reg_ctx->ReadRegisterAsUnsigned(reg_info: sp_reg_info, fail_value: 0); |
177 | |
178 | // Save current SP onto the stack. |
179 | LLDB_LOGF(log, "Writing SP at SP(0x%"PRIx64 ")+0: 0x%"PRIx64, (uint64_t)sp, |
180 | (uint64_t)reg_value); |
181 | if (!process_sp->WritePointerToMemory(vm_addr: sp, ptr_value: reg_value, error)) |
182 | return false; |
183 | |
184 | // %r1 is set to the actual stack value. |
185 | LLDB_LOGF(log, "Writing SP: 0x%"PRIx64, (uint64_t)sp); |
186 | |
187 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info: sp_reg_info, uval: sp)) |
188 | return false; |
189 | |
190 | // %pc is set to the address of the called function. |
191 | |
192 | LLDB_LOGF(log, "Writing IP: 0x%"PRIx64, (uint64_t)func_addr); |
193 | |
194 | if (!reg_ctx->WriteRegisterFromUnsigned(reg_info: pc_reg_info, uval: func_addr)) |
195 | return false; |
196 | |
197 | return true; |
198 | } |
199 | |
200 | static bool ReadIntegerArgument(Scalar &scalar, unsigned int bit_width, |
201 | bool is_signed, Thread &thread, |
202 | uint32_t *argument_register_ids, |
203 | unsigned int ¤t_argument_register, |
204 | addr_t ¤t_stack_argument) { |
205 | if (bit_width > 64) |
206 | return false; // Scalar can't hold large integer arguments |
207 | |
208 | if (current_argument_register < 6) { |
209 | scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned( |
210 | reg: argument_register_ids[current_argument_register], fail_value: 0); |
211 | current_argument_register++; |
212 | if (is_signed) |
213 | scalar.SignExtend(bit_pos: bit_width); |
214 | } else { |
215 | uint32_t byte_size = (bit_width + (8 - 1)) / 8; |
216 | Status error; |
217 | if (thread.GetProcess()->ReadScalarIntegerFromMemory( |
218 | addr: current_stack_argument, byte_size, is_signed, scalar, error)) { |
219 | current_stack_argument += byte_size; |
220 | return true; |
221 | } |
222 | return false; |
223 | } |
224 | return true; |
225 | } |
226 | |
227 | bool ABISysV_ppc64::GetArgumentValues(Thread &thread, ValueList &values) const { |
228 | unsigned int num_values = values.GetSize(); |
229 | unsigned int value_index; |
230 | |
231 | // Extract the register context so we can read arguments from registers |
232 | |
233 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
234 | |
235 | if (!reg_ctx) |
236 | return false; |
237 | |
238 | // Get the pointer to the first stack argument so we have a place to start |
239 | // when reading data |
240 | |
241 | addr_t sp = reg_ctx->GetSP(fail_value: 0); |
242 | |
243 | if (!sp) |
244 | return false; |
245 | |
246 | uint64_t stack_offset; |
247 | if (GetByteOrder() == lldb::eByteOrderLittle) |
248 | stack_offset = 32; |
249 | else |
250 | stack_offset = 48; |
251 | |
252 | // jump over return address. |
253 | addr_t current_stack_argument = sp + stack_offset; |
254 | uint32_t argument_register_ids[8]; |
255 | |
256 | for (size_t i = 0; i < 8; ++i) { |
257 | argument_register_ids[i] = |
258 | reg_ctx |
259 | ->GetRegisterInfo(reg_kind: eRegisterKindGeneric, |
260 | LLDB_REGNUM_GENERIC_ARG1 + i) |
261 | ->kinds[eRegisterKindLLDB]; |
262 | } |
263 | |
264 | unsigned int current_argument_register = 0; |
265 | |
266 | for (value_index = 0; value_index < num_values; ++value_index) { |
267 | Value *value = values.GetValueAtIndex(idx: value_index); |
268 | |
269 | if (!value) |
270 | return false; |
271 | |
272 | // We currently only support extracting values with Clang QualTypes. Do we |
273 | // care about others? |
274 | CompilerType compiler_type = value->GetCompilerType(); |
275 | std::optional<uint64_t> bit_size = |
276 | llvm::expectedToOptional(E: compiler_type.GetBitSize(exe_scope: &thread)); |
277 | if (!bit_size) |
278 | return false; |
279 | bool is_signed; |
280 | |
281 | if (compiler_type.IsIntegerOrEnumerationType(is_signed)) { |
282 | ReadIntegerArgument(scalar&: value->GetScalar(), bit_width: *bit_size, is_signed, thread, |
283 | argument_register_ids, current_argument_register, |
284 | current_stack_argument); |
285 | } else if (compiler_type.IsPointerType()) { |
286 | ReadIntegerArgument(scalar&: value->GetScalar(), bit_width: *bit_size, is_signed: false, thread, |
287 | argument_register_ids, current_argument_register, |
288 | current_stack_argument); |
289 | } |
290 | } |
291 | |
292 | return true; |
293 | } |
294 | |
295 | Status ABISysV_ppc64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, |
296 | lldb::ValueObjectSP &new_value_sp) { |
297 | Status error; |
298 | if (!new_value_sp) { |
299 | error = Status::FromErrorString(str: "Empty value object for return value."); |
300 | return error; |
301 | } |
302 | |
303 | CompilerType compiler_type = new_value_sp->GetCompilerType(); |
304 | if (!compiler_type) { |
305 | error = Status::FromErrorString(str: "Null clang type for return value."); |
306 | return error; |
307 | } |
308 | |
309 | Thread *thread = frame_sp->GetThread().get(); |
310 | |
311 | bool is_signed; |
312 | uint32_t count; |
313 | bool is_complex; |
314 | |
315 | RegisterContext *reg_ctx = thread->GetRegisterContext().get(); |
316 | |
317 | bool set_it_simple = false; |
318 | if (compiler_type.IsIntegerOrEnumerationType(is_signed) || |
319 | compiler_type.IsPointerType()) { |
320 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name: "r3", start_idx: 0); |
321 | |
322 | DataExtractor data; |
323 | Status data_error; |
324 | size_t num_bytes = new_value_sp->GetData(data, error&: data_error); |
325 | if (data_error.Fail()) { |
326 | error = Status::FromErrorStringWithFormat( |
327 | format: "Couldn't convert return value to raw data: %s", |
328 | data_error.AsCString()); |
329 | return error; |
330 | } |
331 | lldb::offset_t offset = 0; |
332 | if (num_bytes <= 8) { |
333 | uint64_t raw_value = data.GetMaxU64(offset_ptr: &offset, byte_size: num_bytes); |
334 | |
335 | if (reg_ctx->WriteRegisterFromUnsigned(reg_info, uval: raw_value)) |
336 | set_it_simple = true; |
337 | } else { |
338 | error = Status::FromErrorString( |
339 | str: "We don't support returning longer than 64 bit " |
340 | "integer values at present."); |
341 | } |
342 | } else if (compiler_type.IsFloatingPointType(count, is_complex)) { |
343 | if (is_complex) |
344 | error = Status::FromErrorString( |
345 | str: "We don't support returning complex values at present"); |
346 | else { |
347 | std::optional<uint64_t> bit_width = |
348 | llvm::expectedToOptional(E: compiler_type.GetBitSize(exe_scope: frame_sp.get())); |
349 | if (!bit_width) { |
350 | error = Status::FromErrorString(str: "can't get size of type"); |
351 | return error; |
352 | } |
353 | if (*bit_width <= 64) { |
354 | DataExtractor data; |
355 | Status data_error; |
356 | size_t num_bytes = new_value_sp->GetData(data, error&: data_error); |
357 | if (data_error.Fail()) { |
358 | error = Status::FromErrorStringWithFormat( |
359 | format: "Couldn't convert return value to raw data: %s", |
360 | data_error.AsCString()); |
361 | return error; |
362 | } |
363 | |
364 | unsigned char buffer[16]; |
365 | ByteOrder byte_order = data.GetByteOrder(); |
366 | |
367 | data.CopyByteOrderedData(src_offset: 0, src_len: num_bytes, dst: buffer, dst_len: 16, dst_byte_order: byte_order); |
368 | set_it_simple = true; |
369 | } else { |
370 | // FIXME - don't know how to do 80 bit long doubles yet. |
371 | error = Status::FromErrorString( |
372 | str: "We don't support returning float values > 64 bits at present"); |
373 | } |
374 | } |
375 | } |
376 | |
377 | if (!set_it_simple) { |
378 | // Okay we've got a structure or something that doesn't fit in a simple |
379 | // register. We should figure out where it really goes, but we don't |
380 | // support this yet. |
381 | error = Status::FromErrorString( |
382 | str: "We only support setting simple integer and float " |
383 | "return types at present."); |
384 | } |
385 | |
386 | return error; |
387 | } |
388 | |
389 | // |
390 | // ReturnValueExtractor |
391 | // |
392 | |
393 | namespace { |
394 | |
395 | #define LOG_PREFIX "ReturnValueExtractor: " |
396 | |
397 | class ReturnValueExtractor { |
398 | // This class represents a register, from which data may be extracted. |
399 | // |
400 | // It may be constructed by directly specifying its index (where 0 is the |
401 | // first register used to return values) or by specifying the offset of a |
402 | // given struct field, in which case the appropriated register index will be |
403 | // calculated. |
404 | class Register { |
405 | public: |
406 | enum Type { |
407 | GPR, // General Purpose Register |
408 | FPR // Floating Point Register |
409 | }; |
410 | |
411 | // main constructor |
412 | // |
413 | // offs - field offset in struct |
414 | Register(Type ty, uint32_t index, uint32_t offs, RegisterContext *reg_ctx, |
415 | ByteOrder byte_order) |
416 | : m_index(index), m_offs(offs % sizeof(uint64_t)), |
417 | m_avail(sizeof(uint64_t) - m_offs), m_type(ty), m_reg_ctx(reg_ctx), |
418 | m_byte_order(byte_order) {} |
419 | |
420 | // explicit index, no offset |
421 | Register(Type ty, uint32_t index, RegisterContext *reg_ctx, |
422 | ByteOrder byte_order) |
423 | : Register(ty, index, 0, reg_ctx, byte_order) {} |
424 | |
425 | // GPR, calculate index from offs |
426 | Register(uint32_t offs, RegisterContext *reg_ctx, ByteOrder byte_order) |
427 | : Register(GPR, offs / sizeof(uint64_t), offs, reg_ctx, byte_order) {} |
428 | |
429 | uint32_t Index() const { return m_index; } |
430 | |
431 | // register offset where data is located |
432 | uint32_t Offs() const { return m_offs; } |
433 | |
434 | // available bytes in this register |
435 | uint32_t Avail() const { return m_avail; } |
436 | |
437 | bool IsValid() const { |
438 | if (m_index > 7) { |
439 | LLDB_LOG(m_log, LOG_PREFIX |
440 | "No more than 8 registers should be used to return values"); |
441 | return false; |
442 | } |
443 | return true; |
444 | } |
445 | |
446 | std::string GetName() const { |
447 | if (m_type == GPR) |
448 | return ("r"+ llvm::Twine(m_index + 3)).str(); |
449 | else |
450 | return ("f"+ llvm::Twine(m_index + 1)).str(); |
451 | } |
452 | |
453 | // get raw register data |
454 | bool GetRawData(uint64_t &raw_data) { |
455 | const RegisterInfo *reg_info = |
456 | m_reg_ctx->GetRegisterInfoByName(reg_name: GetName()); |
457 | if (!reg_info) { |
458 | LLDB_LOG(m_log, LOG_PREFIX "Failed to get RegisterInfo"); |
459 | return false; |
460 | } |
461 | |
462 | RegisterValue reg_val; |
463 | if (!m_reg_ctx->ReadRegister(reg_info, reg_value&: reg_val)) { |
464 | LLDB_LOG(m_log, LOG_PREFIX "ReadRegister() failed"); |
465 | return false; |
466 | } |
467 | |
468 | Status error; |
469 | uint32_t rc = reg_val.GetAsMemoryData( |
470 | reg_info: *reg_info, dst: &raw_data, dst_len: sizeof(raw_data), dst_byte_order: m_byte_order, error); |
471 | if (rc != sizeof(raw_data)) { |
472 | LLDB_LOG(m_log, LOG_PREFIX "GetAsMemoryData() failed"); |
473 | return false; |
474 | } |
475 | |
476 | return true; |
477 | } |
478 | |
479 | private: |
480 | uint32_t m_index; |
481 | uint32_t m_offs; |
482 | uint32_t m_avail; |
483 | Type m_type; |
484 | RegisterContext *m_reg_ctx; |
485 | ByteOrder m_byte_order; |
486 | Log *m_log = GetLog(mask: LLDBLog::Expressions); |
487 | }; |
488 | |
489 | Register GetGPR(uint32_t index) const { |
490 | return Register(Register::GPR, index, m_reg_ctx, m_byte_order); |
491 | } |
492 | |
493 | Register GetFPR(uint32_t index) const { |
494 | return Register(Register::FPR, index, m_reg_ctx, m_byte_order); |
495 | } |
496 | |
497 | Register GetGPRByOffs(uint32_t offs) const { |
498 | return Register(offs, m_reg_ctx, m_byte_order); |
499 | } |
500 | |
501 | public: |
502 | // factory |
503 | static llvm::Expected<ReturnValueExtractor> Create(Thread &thread, |
504 | CompilerType &type) { |
505 | RegisterContext *reg_ctx = thread.GetRegisterContext().get(); |
506 | if (!reg_ctx) |
507 | return llvm::createStringError(LOG_PREFIX |
508 | "Failed to get RegisterContext"); |
509 | |
510 | ProcessSP process_sp = thread.GetProcess(); |
511 | if (!process_sp) |
512 | return llvm::createStringError(LOG_PREFIX "GetProcess() failed"); |
513 | |
514 | return ReturnValueExtractor(thread, type, reg_ctx, process_sp); |
515 | } |
516 | |
517 | // main method: get value of the type specified at construction time |
518 | ValueObjectSP GetValue() { |
519 | const uint32_t type_flags = m_type.GetTypeInfo(); |
520 | |
521 | // call the appropriate type handler |
522 | ValueSP value_sp; |
523 | ValueObjectSP valobj_sp; |
524 | if (type_flags & eTypeIsScalar) { |
525 | if (type_flags & eTypeIsInteger) { |
526 | value_sp = GetIntegerValue(reg_index: 0); |
527 | } else if (type_flags & eTypeIsFloat) { |
528 | if (type_flags & eTypeIsComplex) { |
529 | LLDB_LOG(m_log, LOG_PREFIX "Complex numbers are not supported yet"); |
530 | return ValueObjectSP(); |
531 | } else { |
532 | value_sp = GetFloatValue(type&: m_type, reg_index: 0); |
533 | } |
534 | } |
535 | } else if (type_flags & eTypeIsPointer) { |
536 | value_sp = GetPointerValue(reg_index: 0); |
537 | } |
538 | |
539 | if (value_sp) { |
540 | valobj_sp = ValueObjectConstResult::Create( |
541 | exe_scope: m_thread.GetStackFrameAtIndex(idx: 0).get(), value&: *value_sp, name: ConstString("")); |
542 | } else if (type_flags & eTypeIsVector) { |
543 | valobj_sp = GetVectorValueObject(); |
544 | } else if (type_flags & eTypeIsStructUnion || type_flags & eTypeIsClass) { |
545 | valobj_sp = GetStructValueObject(); |
546 | } |
547 | |
548 | return valobj_sp; |
549 | } |
550 | |
551 | private: |
552 | // data |
553 | Thread &m_thread; |
554 | CompilerType &m_type; |
555 | uint64_t m_byte_size; |
556 | std::unique_ptr<DataBufferHeap> m_data_up; |
557 | int32_t m_src_offs = 0; |
558 | int32_t m_dst_offs = 0; |
559 | bool m_packed = false; |
560 | Log *m_log = GetLog(mask: LLDBLog::Expressions); |
561 | RegisterContext *m_reg_ctx; |
562 | ProcessSP m_process_sp; |
563 | ByteOrder m_byte_order; |
564 | uint32_t m_addr_size; |
565 | |
566 | // methods |
567 | |
568 | // constructor |
569 | ReturnValueExtractor(Thread &thread, CompilerType &type, |
570 | RegisterContext *reg_ctx, ProcessSP process_sp) |
571 | : m_thread(thread), m_type(type), |
572 | m_byte_size( |
573 | llvm::expectedToOptional(E: m_type.GetByteSize(exe_scope: &thread)).value_or(u: 0)), |
574 | m_data_up(new DataBufferHeap(m_byte_size, 0)), m_reg_ctx(reg_ctx), |
575 | m_process_sp(process_sp), m_byte_order(process_sp->GetByteOrder()), |
576 | m_addr_size( |
577 | process_sp->GetTarget().GetArchitecture().GetAddressByteSize()) {} |
578 | |
579 | // build a new scalar value |
580 | ValueSP NewScalarValue(CompilerType &type) { |
581 | ValueSP value_sp(new Value); |
582 | value_sp->SetCompilerType(type); |
583 | value_sp->SetValueType(Value::ValueType::Scalar); |
584 | return value_sp; |
585 | } |
586 | |
587 | // get an integer value in the specified register |
588 | ValueSP GetIntegerValue(uint32_t reg_index) { |
589 | uint64_t raw_value; |
590 | auto reg = GetGPR(index: reg_index); |
591 | if (!reg.GetRawData(raw_data&: raw_value)) |
592 | return ValueSP(); |
593 | |
594 | // build value from data |
595 | ValueSP value_sp(NewScalarValue(type&: m_type)); |
596 | |
597 | uint32_t type_flags = m_type.GetTypeInfo(); |
598 | bool is_signed = (type_flags & eTypeIsSigned) != 0; |
599 | |
600 | switch (m_byte_size) { |
601 | case sizeof(uint64_t): |
602 | if (is_signed) |
603 | value_sp->GetScalar() = (int64_t)(raw_value); |
604 | else |
605 | value_sp->GetScalar() = (uint64_t)(raw_value); |
606 | break; |
607 | |
608 | case sizeof(uint32_t): |
609 | if (is_signed) |
610 | value_sp->GetScalar() = (int32_t)(raw_value & UINT32_MAX); |
611 | else |
612 | value_sp->GetScalar() = (uint32_t)(raw_value & UINT32_MAX); |
613 | break; |
614 | |
615 | case sizeof(uint16_t): |
616 | if (is_signed) |
617 | value_sp->GetScalar() = (int16_t)(raw_value & UINT16_MAX); |
618 | else |
619 | value_sp->GetScalar() = (uint16_t)(raw_value & UINT16_MAX); |
620 | break; |
621 | |
622 | case sizeof(uint8_t): |
623 | if (is_signed) |
624 | value_sp->GetScalar() = (int8_t)(raw_value & UINT8_MAX); |
625 | else |
626 | value_sp->GetScalar() = (uint8_t)(raw_value & UINT8_MAX); |
627 | break; |
628 | |
629 | default: |
630 | llvm_unreachable("Invalid integer size"); |
631 | } |
632 | |
633 | return value_sp; |
634 | } |
635 | |
636 | // get a floating point value on the specified register |
637 | ValueSP GetFloatValue(CompilerType &type, uint32_t reg_index) { |
638 | uint64_t raw_data; |
639 | auto reg = GetFPR(index: reg_index); |
640 | if (!reg.GetRawData(raw_data)) |
641 | return {}; |
642 | |
643 | // build value from data |
644 | ValueSP value_sp(NewScalarValue(type)); |
645 | |
646 | DataExtractor de(&raw_data, sizeof(raw_data), m_byte_order, m_addr_size); |
647 | |
648 | lldb::offset_t offset = 0; |
649 | std::optional<uint64_t> byte_size = |
650 | llvm::expectedToOptional(E: type.GetByteSize(exe_scope: m_process_sp.get())); |
651 | if (!byte_size) |
652 | return {}; |
653 | switch (*byte_size) { |
654 | case sizeof(float): |
655 | value_sp->GetScalar() = (float)de.GetDouble(offset_ptr: &offset); |
656 | break; |
657 | |
658 | case sizeof(double): |
659 | value_sp->GetScalar() = de.GetDouble(offset_ptr: &offset); |
660 | break; |
661 | |
662 | default: |
663 | llvm_unreachable("Invalid floating point size"); |
664 | } |
665 | |
666 | return value_sp; |
667 | } |
668 | |
669 | // get pointer value from register |
670 | ValueSP GetPointerValue(uint32_t reg_index) { |
671 | uint64_t raw_data; |
672 | auto reg = GetGPR(index: reg_index); |
673 | if (!reg.GetRawData(raw_data)) |
674 | return ValueSP(); |
675 | |
676 | // build value from raw data |
677 | ValueSP value_sp(NewScalarValue(type&: m_type)); |
678 | value_sp->GetScalar() = raw_data; |
679 | return value_sp; |
680 | } |
681 | |
682 | // build the ValueObject from our data buffer |
683 | ValueObjectSP BuildValueObject() { |
684 | DataExtractor de(DataBufferSP(m_data_up.release()), m_byte_order, |
685 | m_addr_size); |
686 | return ValueObjectConstResult::Create(exe_scope: &m_thread, compiler_type: m_type, name: ConstString(""), |
687 | data: de); |
688 | } |
689 | |
690 | // get a vector return value |
691 | ValueObjectSP GetVectorValueObject() { |
692 | const uint32_t MAX_VRS = 2; |
693 | |
694 | // get first V register used to return values |
695 | const RegisterInfo *vr[MAX_VRS]; |
696 | vr[0] = m_reg_ctx->GetRegisterInfoByName(reg_name: "vr2"); |
697 | if (!vr[0]) { |
698 | LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr2 RegisterInfo"); |
699 | return ValueObjectSP(); |
700 | } |
701 | |
702 | const uint32_t vr_size = vr[0]->byte_size; |
703 | size_t vrs = 1; |
704 | if (m_byte_size > 2 * vr_size) { |
705 | LLDB_LOG( |
706 | m_log, LOG_PREFIX |
707 | "Returning vectors that don't fit in 2 VR regs is not supported"); |
708 | return ValueObjectSP(); |
709 | } |
710 | |
711 | // load vr3, if needed |
712 | if (m_byte_size > vr_size) { |
713 | vrs++; |
714 | vr[1] = m_reg_ctx->GetRegisterInfoByName(reg_name: "vr3"); |
715 | if (!vr[1]) { |
716 | LLDB_LOG(m_log, LOG_PREFIX "Failed to get vr3 RegisterInfo"); |
717 | return ValueObjectSP(); |
718 | } |
719 | } |
720 | |
721 | // Get the whole contents of vector registers and let the logic here |
722 | // arrange the data properly. |
723 | |
724 | RegisterValue vr_val[MAX_VRS]; |
725 | Status error; |
726 | std::unique_ptr<DataBufferHeap> vr_data( |
727 | new DataBufferHeap(vrs * vr_size, 0)); |
728 | |
729 | for (uint32_t i = 0; i < vrs; i++) { |
730 | if (!m_reg_ctx->ReadRegister(reg_info: vr[i], reg_value&: vr_val[i])) { |
731 | LLDB_LOG(m_log, LOG_PREFIX "Failed to read vector register contents"); |
732 | return ValueObjectSP(); |
733 | } |
734 | if (!vr_val[i].GetAsMemoryData(reg_info: *vr[i], dst: vr_data->GetBytes() + i * vr_size, |
735 | dst_len: vr_size, dst_byte_order: m_byte_order, error)) { |
736 | LLDB_LOG(m_log, LOG_PREFIX "Failed to extract vector register bytes"); |
737 | return ValueObjectSP(); |
738 | } |
739 | } |
740 | |
741 | // The compiler generated code seems to always put the vector elements at |
742 | // the end of the vector register, in case they don't occupy all of it. |
743 | // This offset variable handles this. |
744 | uint32_t offs = 0; |
745 | if (m_byte_size < vr_size) |
746 | offs = vr_size - m_byte_size; |
747 | |
748 | // copy extracted data to our buffer |
749 | memcpy(dest: m_data_up->GetBytes(), src: vr_data->GetBytes() + offs, n: m_byte_size); |
750 | return BuildValueObject(); |
751 | } |
752 | |
753 | // get a struct return value |
754 | ValueObjectSP GetStructValueObject() { |
755 | // case 1: get from stack |
756 | if (m_byte_size > 2 * sizeof(uint64_t)) { |
757 | uint64_t addr; |
758 | auto reg = GetGPR(index: 0); |
759 | if (!reg.GetRawData(raw_data&: addr)) |
760 | return {}; |
761 | |
762 | Status error; |
763 | size_t rc = m_process_sp->ReadMemory(vm_addr: addr, buf: m_data_up->GetBytes(), |
764 | size: m_byte_size, error); |
765 | if (rc != m_byte_size) { |
766 | LLDB_LOG(m_log, LOG_PREFIX "Failed to read memory pointed by r3"); |
767 | return ValueObjectSP(); |
768 | } |
769 | return BuildValueObject(); |
770 | } |
771 | |
772 | // get number of children |
773 | const bool omit_empty_base_classes = true; |
774 | auto n_or_err = m_type.GetNumChildren(omit_empty_base_classes, exe_ctx: nullptr); |
775 | if (!n_or_err) { |
776 | LLDB_LOG_ERROR(m_log, n_or_err.takeError(), LOG_PREFIX "{0}"); |
777 | return {}; |
778 | } |
779 | uint32_t n = *n_or_err; |
780 | if (!n) { |
781 | LLDB_LOG(m_log, LOG_PREFIX "No children found in struct"); |
782 | return {}; |
783 | } |
784 | |
785 | // case 2: homogeneous double or float aggregate |
786 | CompilerType elem_type; |
787 | if (m_type.IsHomogeneousAggregate(base_type_ptr: &elem_type)) { |
788 | uint32_t type_flags = elem_type.GetTypeInfo(); |
789 | std::optional<uint64_t> elem_size = |
790 | llvm::expectedToOptional(E: elem_type.GetByteSize(exe_scope: m_process_sp.get())); |
791 | if (!elem_size) |
792 | return {}; |
793 | if (type_flags & eTypeIsComplex || !(type_flags & eTypeIsFloat)) { |
794 | LLDB_LOG(m_log, |
795 | LOG_PREFIX "Unexpected type found in homogeneous aggregate"); |
796 | return {}; |
797 | } |
798 | |
799 | for (uint32_t i = 0; i < n; i++) { |
800 | ValueSP val_sp = GetFloatValue(type&: elem_type, reg_index: i); |
801 | if (!val_sp) |
802 | return {}; |
803 | |
804 | // copy to buffer |
805 | Status error; |
806 | size_t rc = val_sp->GetScalar().GetAsMemoryData( |
807 | dst: m_data_up->GetBytes() + m_dst_offs, dst_len: *elem_size, dst_byte_order: m_byte_order, |
808 | error); |
809 | if (rc != *elem_size) { |
810 | LLDB_LOG(m_log, LOG_PREFIX "Failed to get float data"); |
811 | return {}; |
812 | } |
813 | m_dst_offs += *elem_size; |
814 | } |
815 | return BuildValueObject(); |
816 | } |
817 | |
818 | // case 3: get from GPRs |
819 | |
820 | // first, check if this is a packed struct or not |
821 | auto ast = m_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>(); |
822 | if (ast) { |
823 | clang::RecordDecl *record_decl = TypeSystemClang::GetAsRecordDecl(type: m_type); |
824 | |
825 | if (record_decl) { |
826 | auto attrs = record_decl->attrs(); |
827 | for (const auto &attr : attrs) { |
828 | if (attr->getKind() == clang::attr::Packed) { |
829 | m_packed = true; |
830 | break; |
831 | } |
832 | } |
833 | } |
834 | } |
835 | |
836 | LLDB_LOG(m_log, LOG_PREFIX "{0} struct", |
837 | m_packed ? "packed": "not packed"); |
838 | |
839 | for (uint32_t i = 0; i < n; i++) { |
840 | std::string name; |
841 | uint32_t size; |
842 | (void)GetChildType(i, name, size); |
843 | // NOTE: the offset returned by GetChildCompilerTypeAtIndex() |
844 | // can't be used because it never considers alignment bytes |
845 | // between struct fields. |
846 | LLDB_LOG(m_log, LOG_PREFIX "field={0}, size={1}", name, size); |
847 | if (!ExtractField(size)) |
848 | return ValueObjectSP(); |
849 | } |
850 | |
851 | return BuildValueObject(); |
852 | } |
853 | |
854 | // extract 'size' bytes at 'offs' from GPRs |
855 | bool ExtractFromRegs(int32_t offs, uint32_t size, void *buf) { |
856 | while (size) { |
857 | auto reg = GetGPRByOffs(offs); |
858 | if (!reg.IsValid()) |
859 | return false; |
860 | |
861 | uint32_t n = std::min(a: reg.Avail(), b: size); |
862 | uint64_t raw_data; |
863 | |
864 | if (!reg.GetRawData(raw_data)) |
865 | return false; |
866 | |
867 | memcpy(dest: buf, src: (char *)&raw_data + reg.Offs(), n: n); |
868 | offs += n; |
869 | size -= n; |
870 | buf = (char *)buf + n; |
871 | } |
872 | return true; |
873 | } |
874 | |
875 | // extract one field from GPRs and put it in our buffer |
876 | bool ExtractField(uint32_t size) { |
877 | auto reg = GetGPRByOffs(offs: m_src_offs); |
878 | if (!reg.IsValid()) |
879 | return false; |
880 | |
881 | // handle padding |
882 | if (!m_packed) { |
883 | uint32_t n = m_src_offs % size; |
884 | |
885 | // not 'size' bytes aligned |
886 | if (n) { |
887 | LLDB_LOG(m_log, |
888 | LOG_PREFIX "Extracting {0} alignment bytes at offset {1}", n, |
889 | m_src_offs); |
890 | // get alignment bytes |
891 | if (!ExtractFromRegs(offs: m_src_offs, size: n, buf: m_data_up->GetBytes() + m_dst_offs)) |
892 | return false; |
893 | m_src_offs += n; |
894 | m_dst_offs += n; |
895 | } |
896 | } |
897 | |
898 | // get field |
899 | LLDB_LOG(m_log, LOG_PREFIX "Extracting {0} field bytes at offset {1}", size, |
900 | m_src_offs); |
901 | if (!ExtractFromRegs(offs: m_src_offs, size, buf: m_data_up->GetBytes() + m_dst_offs)) |
902 | return false; |
903 | m_src_offs += size; |
904 | m_dst_offs += size; |
905 | return true; |
906 | } |
907 | |
908 | // get child |
909 | llvm::Expected<CompilerType> GetChildType(uint32_t i, std::string &name, |
910 | uint32_t &size) { |
911 | // GetChild constant inputs |
912 | const bool transparent_pointers = false; |
913 | const bool omit_empty_base_classes = true; |
914 | const bool ignore_array_bounds = false; |
915 | // GetChild output params |
916 | int32_t child_offs; |
917 | uint32_t child_bitfield_bit_size; |
918 | uint32_t child_bitfield_bit_offset; |
919 | bool child_is_base_class; |
920 | bool child_is_deref_of_parent; |
921 | ValueObject *valobj = nullptr; |
922 | uint64_t language_flags; |
923 | ExecutionContext exe_ctx; |
924 | m_thread.CalculateExecutionContext(exe_ctx); |
925 | |
926 | return m_type.GetChildCompilerTypeAtIndex( |
927 | exe_ctx: &exe_ctx, idx: i, transparent_pointers, omit_empty_base_classes, |
928 | ignore_array_bounds, child_name&: name, child_byte_size&: size, child_byte_offset&: child_offs, child_bitfield_bit_size, |
929 | child_bitfield_bit_offset, child_is_base_class, |
930 | child_is_deref_of_parent, valobj, language_flags); |
931 | } |
932 | }; |
933 | |
934 | #undef LOG_PREFIX |
935 | |
936 | } // anonymous namespace |
937 | |
938 | ValueObjectSP |
939 | ABISysV_ppc64::GetReturnValueObjectSimple(Thread &thread, |
940 | CompilerType &type) const { |
941 | if (!type) |
942 | return ValueObjectSP(); |
943 | |
944 | auto exp_extractor = ReturnValueExtractor::Create(thread, type); |
945 | if (!exp_extractor) { |
946 | Log *log = GetLog(mask: LLDBLog::Expressions); |
947 | LLDB_LOG_ERROR(log, exp_extractor.takeError(), |
948 | "Extracting return value failed: {0}"); |
949 | return ValueObjectSP(); |
950 | } |
951 | |
952 | return exp_extractor.get().GetValue(); |
953 | } |
954 | |
955 | ValueObjectSP ABISysV_ppc64::GetReturnValueObjectImpl( |
956 | Thread &thread, CompilerType &return_compiler_type) const { |
957 | return GetReturnValueObjectSimple(thread, type&: return_compiler_type); |
958 | } |
959 | |
960 | UnwindPlanSP ABISysV_ppc64::CreateFunctionEntryUnwindPlan() { |
961 | |
962 | uint32_t lr_reg_num; |
963 | uint32_t sp_reg_num; |
964 | uint32_t pc_reg_num; |
965 | |
966 | if (GetByteOrder() == lldb::eByteOrderLittle) { |
967 | lr_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le; |
968 | sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le; |
969 | pc_reg_num = ppc64le_dwarf::dwarf_pc_ppc64le; |
970 | } else { |
971 | lr_reg_num = ppc64_dwarf::dwarf_lr_ppc64; |
972 | sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64; |
973 | pc_reg_num = ppc64_dwarf::dwarf_pc_ppc64; |
974 | } |
975 | |
976 | UnwindPlan::Row row; |
977 | |
978 | // Our Call Frame Address is the stack pointer value |
979 | row.GetCFAValue().SetIsRegisterPlusOffset(reg_num: sp_reg_num, offset: 0); |
980 | |
981 | // The previous PC is in the LR. All other registers are the same. |
982 | row.SetRegisterLocationToRegister(reg_num: pc_reg_num, other_reg_num: lr_reg_num, can_replace: true); |
983 | |
984 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
985 | plan_sp->AppendRow(row: std::move(row)); |
986 | plan_sp->SetSourceName("ppc64 at-func-entry default"); |
987 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
988 | return plan_sp; |
989 | } |
990 | |
991 | UnwindPlanSP ABISysV_ppc64::CreateDefaultUnwindPlan() { |
992 | uint32_t sp_reg_num; |
993 | uint32_t pc_reg_num; |
994 | uint32_t cr_reg_num; |
995 | |
996 | if (GetByteOrder() == lldb::eByteOrderLittle) { |
997 | sp_reg_num = ppc64le_dwarf::dwarf_r1_ppc64le; |
998 | pc_reg_num = ppc64le_dwarf::dwarf_lr_ppc64le; |
999 | cr_reg_num = ppc64le_dwarf::dwarf_cr_ppc64le; |
1000 | } else { |
1001 | sp_reg_num = ppc64_dwarf::dwarf_r1_ppc64; |
1002 | pc_reg_num = ppc64_dwarf::dwarf_lr_ppc64; |
1003 | cr_reg_num = ppc64_dwarf::dwarf_cr_ppc64; |
1004 | } |
1005 | |
1006 | UnwindPlan::Row row; |
1007 | const int32_t ptr_size = 8; |
1008 | row.SetUnspecifiedRegistersAreUndefined(true); |
1009 | row.GetCFAValue().SetIsRegisterDereferenced(sp_reg_num); |
1010 | |
1011 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: pc_reg_num, offset: ptr_size * 2, can_replace: true); |
1012 | row.SetRegisterLocationToIsCFAPlusOffset(reg_num: sp_reg_num, offset: 0, can_replace: true); |
1013 | row.SetRegisterLocationToAtCFAPlusOffset(reg_num: cr_reg_num, offset: ptr_size, can_replace: true); |
1014 | |
1015 | auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindDWARF); |
1016 | plan_sp->AppendRow(row: std::move(row)); |
1017 | plan_sp->SetSourceName("ppc64 default unwind plan"); |
1018 | plan_sp->SetSourcedFromCompiler(eLazyBoolNo); |
1019 | plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
1020 | plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo); |
1021 | plan_sp->SetReturnAddressRegister(pc_reg_num); |
1022 | return plan_sp; |
1023 | } |
1024 | |
1025 | bool ABISysV_ppc64::RegisterIsVolatile(const RegisterInfo *reg_info) { |
1026 | return !RegisterIsCalleeSaved(reg_info); |
1027 | } |
1028 | |
1029 | // See "Register Usage" in the |
1030 | // "System V Application Binary Interface" |
1031 | // "64-bit PowerPC ELF Application Binary Interface Supplement" current version |
1032 | // is 2 released 2015 at |
1033 | // https://members.openpowerfoundation.org/document/dl/576 |
1034 | bool ABISysV_ppc64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { |
1035 | if (reg_info) { |
1036 | // Preserved registers are : |
1037 | // r1,r2,r13-r31 |
1038 | // cr2-cr4 (partially preserved) |
1039 | // f14-f31 (not yet) |
1040 | // v20-v31 (not yet) |
1041 | // vrsave (not yet) |
1042 | |
1043 | const char *name = reg_info->name; |
1044 | if (name[0] == 'r') { |
1045 | if ((name[1] == '1' || name[1] == '2') && name[2] == '\0') |
1046 | return true; |
1047 | if (name[1] == '1' && name[2] > '2') |
1048 | return true; |
1049 | if ((name[1] == '2' || name[1] == '3') && name[2] != '\0') |
1050 | return true; |
1051 | } |
1052 | |
1053 | if (name[0] == 'f' && name[1] >= '0' && name[2] <= '9') { |
1054 | if (name[2] == '\0') |
1055 | return false; |
1056 | if (name[1] == '1' && name[2] >= '4') |
1057 | return true; |
1058 | if ((name[1] == '2' || name[1] == '3') && name[2] != '\0') |
1059 | return true; |
1060 | } |
1061 | |
1062 | if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp |
1063 | return true; |
1064 | if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp |
1065 | return false; |
1066 | if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc |
1067 | return true; |
1068 | } |
1069 | return false; |
1070 | } |
1071 | |
1072 | void ABISysV_ppc64::Initialize() { |
1073 | PluginManager::RegisterPlugin( |
1074 | name: GetPluginNameStatic(), description: "System V ABI for ppc64 targets", create_callback: CreateInstance); |
1075 | } |
1076 | |
1077 | void ABISysV_ppc64::Terminate() { |
1078 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
1079 | } |
1080 |
Definitions
- GetRegisterInfoArray
- GetRedZoneSize
- GetByteOrder
- CreateInstance
- PrepareTrivialCall
- ReadIntegerArgument
- GetArgumentValues
- SetReturnValueObject
- ReturnValueExtractor
- Register
- Type
- Register
- Register
- Register
- Index
- Offs
- Avail
- IsValid
- GetName
- GetRawData
- GetGPR
- GetFPR
- GetGPRByOffs
- Create
- GetValue
- ReturnValueExtractor
- NewScalarValue
- GetIntegerValue
- GetFloatValue
- GetPointerValue
- BuildValueObject
- GetVectorValueObject
- GetStructValueObject
- ExtractFromRegs
- ExtractField
- GetChildType
- GetReturnValueObjectSimple
- GetReturnValueObjectImpl
- CreateFunctionEntryUnwindPlan
- CreateDefaultUnwindPlan
- RegisterIsVolatile
- RegisterIsCalleeSaved
- Initialize
Learn to use CMake with our Intro Training
Find out more