mirror of
https://github.com/intel/llvm.git
synced 2026-01-17 23:45:25 +08:00
[ABI] Fix SystemV ABI to handle nested aggregate type returned in register
Add a function to flatten the nested aggregate type Differential Revision: https://reviews.llvm.org/D62702 Patch by Wanyi Ye <kusmour@gmail.com> llvm-svn: 362543
This commit is contained in:
@@ -598,6 +598,8 @@ public:
|
||||
|
||||
bool IsVoidType(lldb::opaque_compiler_type_t type) override;
|
||||
|
||||
bool CanPassInRegisters(const CompilerType &type) override;
|
||||
|
||||
bool SupportsLanguage(lldb::LanguageType language) override;
|
||||
|
||||
static bool GetCXXClassName(const CompilerType &type,
|
||||
|
||||
@@ -181,6 +181,8 @@ public:
|
||||
|
||||
virtual bool IsVoidType(lldb::opaque_compiler_type_t type) = 0;
|
||||
|
||||
virtual bool CanPassInRegisters(const CompilerType &type) = 0;
|
||||
|
||||
// TypeSystems can support more than one language
|
||||
virtual bool SupportsLanguage(lldb::LanguageType language) = 0;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
LEVEL = ../../make
|
||||
|
||||
C_SOURCES := call-func.c
|
||||
CXX_SOURCES := call-func.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
||||
|
||||
@@ -57,7 +57,7 @@ class ReturnValueTestCase(TestBase):
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
fun_name = frame.GetFunctionName()
|
||||
self.assertTrue(fun_name == "outer_sint")
|
||||
self.assertTrue(fun_name == "outer_sint(int)")
|
||||
|
||||
return_value = thread.GetStopReturnValue()
|
||||
self.assertTrue(return_value.IsValid())
|
||||
@@ -78,7 +78,7 @@ class ReturnValueTestCase(TestBase):
|
||||
|
||||
frame = thread.GetFrameAtIndex(1)
|
||||
fun_name = frame.GetFunctionName()
|
||||
self.assertTrue(fun_name == "outer_sint")
|
||||
self.assertTrue(fun_name == "outer_sint(int)")
|
||||
in_int = frame.FindVariable("value").GetValueAsSigned(error)
|
||||
self.assertTrue(error.Success())
|
||||
|
||||
@@ -98,7 +98,7 @@ class ReturnValueTestCase(TestBase):
|
||||
|
||||
# Now try some simple returns that have different types:
|
||||
inner_float_bkpt = self.target.BreakpointCreateByName(
|
||||
"inner_float", exe)
|
||||
"inner_float(float)", exe)
|
||||
self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT)
|
||||
self.process.Continue()
|
||||
thread_list = lldbutil.get_threads_stopped_at_breakpoint(
|
||||
@@ -118,7 +118,7 @@ class ReturnValueTestCase(TestBase):
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
fun_name = frame.GetFunctionName()
|
||||
self.assertTrue(fun_name == "outer_float")
|
||||
self.assertTrue(fun_name == "outer_float(float)")
|
||||
|
||||
#return_value = thread.GetStopReturnValue()
|
||||
#self.assertTrue(return_value.IsValid())
|
||||
@@ -190,6 +190,37 @@ class ReturnValueTestCase(TestBase):
|
||||
self.return_and_test_struct_value("return_ext_vector_size_float32_4")
|
||||
self.return_and_test_struct_value("return_ext_vector_size_float32_8")
|
||||
|
||||
# limit the nested struct and class tests to only x86_64
|
||||
@skipIf(archs=no_match(['x86_64']))
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24778")
|
||||
def test_for_cpp_support(self):
|
||||
self.build()
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
(self.target, self.process, thread, inner_sint_bkpt) = lldbutil.run_to_name_breakpoint(self, "inner_sint", exe_name = exe)
|
||||
|
||||
error = lldb.SBError()
|
||||
|
||||
self.target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(self.target, VALID_TARGET)
|
||||
|
||||
main_bktp = self.target.BreakpointCreateByName("main", exe)
|
||||
self.assertTrue(main_bktp, VALID_BREAKPOINT)
|
||||
|
||||
self.process = self.target.LaunchSimple(
|
||||
None, None, self.get_process_working_directory())
|
||||
self.assertEqual(len(lldbutil.get_threads_stopped_at_breakpoint(
|
||||
self.process, main_bktp)), 1)
|
||||
# nested struct tests
|
||||
self.return_and_test_struct_value("return_nested_one_float_three_base")
|
||||
self.return_and_test_struct_value("return_double_nested_one_float_one_nested")
|
||||
self.return_and_test_struct_value("return_nested_float_struct")
|
||||
# class test
|
||||
self.return_and_test_struct_value("return_base_class_one_char")
|
||||
self.return_and_test_struct_value("return_nested_class_float_and_base")
|
||||
self.return_and_test_struct_value("return_double_nested_class_float_and_nested")
|
||||
self.return_and_test_struct_value("return_base_class")
|
||||
self.return_and_test_struct_value("return_derived_class")
|
||||
|
||||
def return_and_test_struct_value(self, func_name):
|
||||
"""Pass in the name of the function to return from - takes in value, returns value."""
|
||||
|
||||
|
||||
@@ -301,6 +301,69 @@ return_one_int_one_pointer (struct one_int_one_pointer value)
|
||||
return value;
|
||||
}
|
||||
|
||||
struct base_one_char {
|
||||
char c;
|
||||
};
|
||||
|
||||
struct nested_one_float_three_base {
|
||||
float f;
|
||||
struct base_one_char b1;
|
||||
struct base_one_char b2;
|
||||
struct base_one_char b3;
|
||||
}; // returned in RAX for both SysV and Windows
|
||||
|
||||
struct nested_one_float_three_base
|
||||
return_nested_one_float_three_base (struct nested_one_float_three_base value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
struct double_nested_one_float_one_nested {
|
||||
float f;
|
||||
struct nested_one_float_three_base ns;
|
||||
}; // SysV-ABI: returned in XMM0 + RAX
|
||||
// Windows-ABI: returned in memory
|
||||
|
||||
struct double_nested_one_float_one_nested
|
||||
return_double_nested_one_float_one_nested(struct double_nested_one_float_one_nested value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
struct base_float_struct {
|
||||
float f1;
|
||||
float f2;
|
||||
};
|
||||
|
||||
struct nested_float_struct {
|
||||
double d;
|
||||
struct base_float_struct bfs;
|
||||
}; // SysV-ABI: return in xmm0 + xmm1
|
||||
// Windows-ABI: returned in memory
|
||||
|
||||
struct nested_float_struct
|
||||
return_nested_float_struct (struct nested_float_struct value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
struct six_double_three_int {
|
||||
double d1; // 8
|
||||
double d2; // 8
|
||||
int i1; // 4
|
||||
double d3; // 8
|
||||
double d4; // 8
|
||||
int i2; // 4
|
||||
double d5; // 8
|
||||
double d6; // 8
|
||||
int i3; // 4
|
||||
}; // returned in memeory on both SysV and Windows
|
||||
|
||||
struct six_double_three_int
|
||||
return_six_double_three_int (struct six_double_three_int value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
typedef float vector_size_float32_8 __attribute__((__vector_size__(8)));
|
||||
typedef float vector_size_float32_16 __attribute__((__vector_size__(16)));
|
||||
typedef float vector_size_float32_32 __attribute__((__vector_size__(32)));
|
||||
@@ -345,6 +408,100 @@ return_ext_vector_size_float32_8 (ext_vector_size_float32_8 value)
|
||||
return value;
|
||||
}
|
||||
|
||||
class base_class_one_char {
|
||||
public:
|
||||
char c = '!';
|
||||
}; // returned in RAX for both ABI
|
||||
|
||||
base_class_one_char
|
||||
return_base_class_one_char(base_class_one_char value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
class nested_class_float_and_base {
|
||||
public:
|
||||
float f = 0.1;
|
||||
base_class_one_char b;
|
||||
}; // returned in RAX for both ABI
|
||||
|
||||
nested_class_float_and_base
|
||||
return_nested_class_float_and_base(nested_class_float_and_base value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
class double_nested_class_float_and_nested {
|
||||
public:
|
||||
float f = 0.2;
|
||||
nested_class_float_and_base n;
|
||||
}; // SysV-ABI: returned in XMM0 + RAX
|
||||
// Windows-ABI: returned in memory
|
||||
|
||||
double_nested_class_float_and_nested
|
||||
return_double_nested_class_float_and_nested(
|
||||
double_nested_class_float_and_nested value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
class base_class {
|
||||
public:
|
||||
base_class() {
|
||||
c = 'a';
|
||||
c2 = 'b';
|
||||
}
|
||||
private:
|
||||
char c;
|
||||
protected:
|
||||
char c2;
|
||||
}; // returned in RAX for both ABI
|
||||
|
||||
base_class
|
||||
return_base_class(base_class value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
class sub_class : base_class {
|
||||
public:
|
||||
sub_class() {
|
||||
c2 = '&';
|
||||
i = 10;
|
||||
}
|
||||
private:
|
||||
int i;
|
||||
}; // size 8; should be returned in RAX
|
||||
// Since it's in register, lldb won't be able to get the
|
||||
// fields in base class, expected to fail.
|
||||
|
||||
sub_class
|
||||
return_sub_class(sub_class value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
class abstract_class {
|
||||
public:
|
||||
virtual char getChar() = 0;
|
||||
private:
|
||||
int i = 8;
|
||||
protected:
|
||||
char c = '!';
|
||||
};
|
||||
|
||||
class derived_class : abstract_class {
|
||||
public:
|
||||
derived_class() {
|
||||
c = '?';
|
||||
}
|
||||
char getChar() {
|
||||
return this->c;
|
||||
}
|
||||
private:
|
||||
char c2 = '$';
|
||||
}; // size: 16; contains non POD member, returned in memory
|
||||
|
||||
derived_class
|
||||
return_derived_class(derived_class value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
@@ -395,6 +552,49 @@ main ()
|
||||
return_one_int_one_double_packed ((struct one_int_one_double_packed) {10, 20.0});
|
||||
return_one_int_one_long ((struct one_int_one_long) {10, 20});
|
||||
|
||||
return_nested_one_float_three_base((struct nested_one_float_three_base) {
|
||||
10.0,
|
||||
(struct base_one_char) {
|
||||
'x'
|
||||
},
|
||||
(struct base_one_char) {
|
||||
'y'
|
||||
},
|
||||
(struct base_one_char) {
|
||||
'z'
|
||||
}
|
||||
});
|
||||
return_double_nested_one_float_one_nested((struct double_nested_one_float_one_nested) {
|
||||
10.0,
|
||||
(struct nested_one_float_three_base) {
|
||||
20.0,
|
||||
(struct base_one_char) {
|
||||
'x'
|
||||
},
|
||||
(struct base_one_char) {
|
||||
'y'
|
||||
},
|
||||
(struct base_one_char) {
|
||||
'z'
|
||||
}
|
||||
}});
|
||||
return_nested_float_struct((struct nested_float_struct) {
|
||||
10.0,
|
||||
(struct base_float_struct) {
|
||||
20.0,
|
||||
30.0
|
||||
}});
|
||||
return_six_double_three_int((struct six_double_three_int) {
|
||||
10.0, 20.0, 1, 30.0, 40.0, 2, 50.0, 60.0, 3});
|
||||
|
||||
return_base_class_one_char(base_class_one_char());
|
||||
return_nested_class_float_and_base(nested_class_float_and_base());
|
||||
return_double_nested_class_float_and_nested(double_nested_class_float_and_nested());
|
||||
return_base_class(base_class());
|
||||
// this is expected to fail
|
||||
return_sub_class(sub_class());
|
||||
return_derived_class(derived_class());
|
||||
|
||||
return_vector_size_float32_8 (( vector_size_float32_8 ){1.5, 2.25});
|
||||
return_vector_size_float32_16 (( vector_size_float32_16 ){1.5, 2.25, 4.125, 8.0625});
|
||||
return_vector_size_float32_32 (( vector_size_float32_32 ){1.5, 2.25, 4.125, 8.0625, 7.89, 8.52, 6.31, 9.12});
|
||||
@@ -28,7 +28,8 @@ class TestTrivialABI(TestBase):
|
||||
self.expr_test(True)
|
||||
|
||||
@skipUnlessSupportedTypeAttribute("trivial_abi")
|
||||
@expectedFailureAll(bugnumber="llvm.org/pr36870")
|
||||
# fixed for SysV-x86_64 ABI, but not Windows-x86_64
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr36870")
|
||||
def test_call_nontrivial(self):
|
||||
"""Test that we can print a variable & call a function on the same class w/o the trivial ABI marker."""
|
||||
self.build()
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "lldb/Utility/RegisterValue.h"
|
||||
#include "lldb/Utility/Status.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
||||
@@ -1558,6 +1560,55 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectSimple(
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
// The compiler will flatten the nested aggregate type into single
|
||||
// layer and push the value to stack
|
||||
// This helper function will flatten an aggregate type
|
||||
// and return true if it can be returned in register(s) by value
|
||||
// return false if the aggregate is in memory
|
||||
static bool FlattenAggregateType(
|
||||
Thread &thread, ExecutionContext &exe_ctx,
|
||||
CompilerType &return_compiler_type,
|
||||
uint32_t data_byte_offset,
|
||||
std::vector<uint32_t> &aggregate_field_offsets,
|
||||
std::vector<CompilerType> &aggregate_compiler_types) {
|
||||
|
||||
const uint32_t num_children = return_compiler_type.GetNumFields();
|
||||
for (uint32_t idx = 0; idx < num_children; ++idx) {
|
||||
std::string name;
|
||||
bool is_signed;
|
||||
uint32_t count;
|
||||
bool is_complex;
|
||||
|
||||
uint64_t field_bit_offset = 0;
|
||||
CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
|
||||
idx, name, &field_bit_offset, nullptr, nullptr);
|
||||
llvm::Optional<uint64_t> field_bit_width =
|
||||
field_compiler_type.GetBitSize(&thread);
|
||||
|
||||
// if we don't know the size of the field (e.g. invalid type), exit
|
||||
if (!field_bit_width || *field_bit_width == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t field_byte_offset = field_bit_offset / 8 + data_byte_offset;
|
||||
|
||||
const uint32_t field_type_flags = field_compiler_type.GetTypeInfo();
|
||||
if (field_compiler_type.IsIntegerOrEnumerationType(is_signed) ||
|
||||
field_compiler_type.IsPointerType() ||
|
||||
field_compiler_type.IsFloatingPointType(count, is_complex)) {
|
||||
aggregate_field_offsets.push_back(field_byte_offset);
|
||||
aggregate_compiler_types.push_back(field_compiler_type);
|
||||
} else if (field_type_flags & eTypeHasChildren) {
|
||||
if (!FlattenAggregateType(thread, exe_ctx, field_compiler_type,
|
||||
field_byte_offset, aggregate_field_offsets,
|
||||
aggregate_compiler_types)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
Thread &thread, CompilerType &return_compiler_type) const {
|
||||
ValueObjectSP return_valobj_sp;
|
||||
@@ -1580,10 +1631,17 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
if (return_compiler_type.IsAggregateType()) {
|
||||
Target *target = exe_ctx.GetTargetPtr();
|
||||
bool is_memory = true;
|
||||
if (*bit_width <= 128) {
|
||||
ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder();
|
||||
std::vector<uint32_t> aggregate_field_offsets;
|
||||
std::vector<CompilerType> aggregate_compiler_types;
|
||||
if (return_compiler_type.GetTypeSystem()->CanPassInRegisters(
|
||||
return_compiler_type) &&
|
||||
*bit_width <= 128 &&
|
||||
FlattenAggregateType(thread, exe_ctx, return_compiler_type,
|
||||
0, aggregate_field_offsets,
|
||||
aggregate_compiler_types)) {
|
||||
ByteOrder byte_order = target->GetArchitecture().GetByteOrder();
|
||||
DataBufferSP data_sp(new DataBufferHeap(16, 0));
|
||||
DataExtractor return_ext(data_sp, target_byte_order,
|
||||
DataExtractor return_ext(data_sp, byte_order,
|
||||
target->GetArchitecture().GetAddressByteSize());
|
||||
|
||||
const RegisterInfo *rax_info =
|
||||
@@ -1613,36 +1671,27 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
uint32_t integer_bytes =
|
||||
0; // Tracks how much of the rax/rds registers we've consumed so far
|
||||
|
||||
const uint32_t num_children = return_compiler_type.GetNumFields();
|
||||
// in case of the returned type is a subclass of non-abstract-base class
|
||||
// it will have a padding to skip the base content
|
||||
if (aggregate_field_offsets.size()) {
|
||||
fp_bytes = aggregate_field_offsets[0];
|
||||
integer_bytes = aggregate_field_offsets[0];
|
||||
}
|
||||
|
||||
const uint32_t num_children = aggregate_compiler_types.size();
|
||||
|
||||
// Since we are in the small struct regime, assume we are not in memory.
|
||||
is_memory = false;
|
||||
|
||||
for (uint32_t idx = 0; idx < num_children; idx++) {
|
||||
std::string name;
|
||||
uint64_t field_bit_offset = 0;
|
||||
bool is_signed;
|
||||
bool is_complex;
|
||||
uint32_t count;
|
||||
bool is_complex;
|
||||
|
||||
CompilerType field_compiler_type = return_compiler_type.GetFieldAtIndex(
|
||||
idx, name, &field_bit_offset, nullptr, nullptr);
|
||||
llvm::Optional<uint64_t> field_bit_width =
|
||||
field_compiler_type.GetBitSize(&thread);
|
||||
CompilerType field_compiler_type = aggregate_compiler_types[idx];
|
||||
uint32_t field_byte_width = (uint32_t) (*field_compiler_type.GetByteSize(&thread));
|
||||
uint32_t field_byte_offset = aggregate_field_offsets[idx];
|
||||
|
||||
// if we don't know the size of the field (e.g. invalid type), just
|
||||
// bail out
|
||||
if (!field_bit_width || *field_bit_width == 0)
|
||||
break;
|
||||
|
||||
// If there are any unaligned fields, this is stored in memory.
|
||||
if (field_bit_offset % *field_bit_width != 0) {
|
||||
is_memory = true;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t field_byte_width = *field_bit_width / 8;
|
||||
uint32_t field_byte_offset = field_bit_offset / 8;
|
||||
uint32_t field_bit_width = field_byte_width * 8;
|
||||
|
||||
DataExtractor *copy_from_extractor = nullptr;
|
||||
uint32_t copy_from_offset = 0;
|
||||
@@ -1674,10 +1723,10 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
}
|
||||
} else if (field_compiler_type.IsFloatingPointType(count, is_complex)) {
|
||||
// Structs with long doubles are always passed in memory.
|
||||
if (*field_bit_width == 128) {
|
||||
if (field_bit_width == 128) {
|
||||
is_memory = true;
|
||||
break;
|
||||
} else if (*field_bit_width == 64) {
|
||||
} else if (field_bit_width == 64) {
|
||||
// These have to be in a single xmm register.
|
||||
if (fp_bytes == 0)
|
||||
copy_from_extractor = &xmm0_data;
|
||||
@@ -1686,7 +1735,7 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
|
||||
copy_from_offset = 0;
|
||||
fp_bytes += field_byte_width;
|
||||
} else if (*field_bit_width == 32) {
|
||||
} else if (field_bit_width == 32) {
|
||||
// This one is kind of complicated. If we are in an "eightbyte"
|
||||
// with another float, we'll be stuffed into an xmm register with
|
||||
// it. If we are in an "eightbyte" with one or more ints, then we
|
||||
@@ -1695,18 +1744,15 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
if (field_byte_offset % 8 == 0) {
|
||||
// We are at the beginning of one of the eightbytes, so check the
|
||||
// next element (if any)
|
||||
if (idx == num_children - 1)
|
||||
if (idx == num_children - 1) {
|
||||
in_gpr = false;
|
||||
else {
|
||||
uint64_t next_field_bit_offset = 0;
|
||||
} else {
|
||||
CompilerType next_field_compiler_type =
|
||||
return_compiler_type.GetFieldAtIndex(idx + 1, name,
|
||||
&next_field_bit_offset,
|
||||
nullptr, nullptr);
|
||||
aggregate_compiler_types[idx + 1];
|
||||
if (next_field_compiler_type.IsIntegerOrEnumerationType(
|
||||
is_signed))
|
||||
is_signed)) {
|
||||
in_gpr = true;
|
||||
else {
|
||||
} else {
|
||||
copy_from_offset = 0;
|
||||
in_gpr = false;
|
||||
}
|
||||
@@ -1715,18 +1761,15 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
// We are inside of an eightbyte, so see if the field before us
|
||||
// is floating point: This could happen if somebody put padding
|
||||
// in the structure.
|
||||
if (idx == 0)
|
||||
if (idx == 0) {
|
||||
in_gpr = false;
|
||||
else {
|
||||
uint64_t prev_field_bit_offset = 0;
|
||||
} else {
|
||||
CompilerType prev_field_compiler_type =
|
||||
return_compiler_type.GetFieldAtIndex(idx - 1, name,
|
||||
&prev_field_bit_offset,
|
||||
nullptr, nullptr);
|
||||
aggregate_compiler_types[idx - 1];
|
||||
if (prev_field_compiler_type.IsIntegerOrEnumerationType(
|
||||
is_signed))
|
||||
is_signed)) {
|
||||
in_gpr = true;
|
||||
else {
|
||||
} else {
|
||||
copy_from_offset = 4;
|
||||
in_gpr = false;
|
||||
}
|
||||
@@ -1759,7 +1802,6 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These two tests are just sanity checks. If I somehow get the type
|
||||
// calculation wrong above it is better to just return nothing than to
|
||||
// assert or crash.
|
||||
@@ -1768,13 +1810,11 @@ ValueObjectSP ABISysV_x86_64::GetReturnValueObjectImpl(
|
||||
if (copy_from_offset + field_byte_width >
|
||||
copy_from_extractor->GetByteSize())
|
||||
return return_valobj_sp;
|
||||
|
||||
copy_from_extractor->CopyByteOrderedData(
|
||||
copy_from_offset, field_byte_width,
|
||||
data_sp->GetBytes() + field_byte_offset, field_byte_width,
|
||||
target_byte_order);
|
||||
byte_order);
|
||||
}
|
||||
|
||||
if (!is_memory) {
|
||||
// The result is in our data buffer. Let's make a variable object out
|
||||
// of it:
|
||||
|
||||
@@ -3911,6 +3911,14 @@ bool ClangASTContext::IsVoidType(lldb::opaque_compiler_type_t type) {
|
||||
return GetCanonicalQualType(type)->isVoidType();
|
||||
}
|
||||
|
||||
bool ClangASTContext::CanPassInRegisters(const CompilerType &type) {
|
||||
if (auto *record_decl =
|
||||
ClangASTContext::GetAsRecordDecl(type)) {
|
||||
return record_decl->canPassInRegisters();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ClangASTContext::SupportsLanguage(lldb::LanguageType language) {
|
||||
return ClangASTContextSupportsLanguage(language);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user