Skip to content

[lldb] Emit specification and spare_bits_mask in debug info, consume them in lldb #8243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ struct DescriptorFinderForwarder : public swift::reflection::DescriptorFinder {
return nullptr;
}

std::unique_ptr<swift::reflection::MultiPayloadEnumDescriptorBase>
getMultiPayloadEnumDescriptor(const swift::reflection::TypeRef *TR) override {
if (m_descriptor_finder)
return m_descriptor_finder->getMultiPayloadEnumDescriptor(TR);
return nullptr;
}
void SetExternalDescriptorFinder(
swift::reflection::DescriptorFinder *desciptor_finder) {
m_descriptor_finder = desciptor_finder;
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwift.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ class DWARFASTParserSwift : public lldb_private::plugin::dwarf::DWARFASTParser,
std::unique_ptr<swift::reflection::BuiltinTypeDescriptorBase>
getBuiltinTypeDescriptor(const swift::reflection::TypeRef *TR) override;

/// Returns a builtin descriptor constructed from DWARF info.
std::unique_ptr<swift::reflection::MultiPayloadEnumDescriptorBase>
getMultiPayloadEnumDescriptor(const swift::reflection::TypeRef *TR) override;

private:
/// Returns the canonical demangle tree of a die's type.
NodePointer GetCanonicalDemangleTree(DWARFDIE &die);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ getFieldDescriptorKindForDie(CompilerType type) {
return swift::reflection::FieldDescriptorKind::Class;
case lldb::eTypeClassStruct:
return swift::reflection::FieldDescriptorKind::Struct;
case lldb::eTypeClassEnumeration:
case lldb::eTypeClassUnion:
return swift::reflection::FieldDescriptorKind::Enum;
default:
LLDB_LOG(GetLog(LLDBLog::Types),
Expand Down Expand Up @@ -203,7 +203,12 @@ class DWARFFieldDescriptorImpl : public swift::reflection::FieldDescriptorBase {
std::vector<std::unique_ptr<swift::reflection::FieldRecordBase>>
getFieldRecordsFromEnum(const DWARFDIE &die,
plugin::dwarf::DWARFASTParser *dwarf_parser) {
std::vector<std::unique_ptr<swift::reflection::FieldRecordBase>> fields;
// Type lowering expects the payload fields to come before the non-payload
// ones.
std::vector<std::unique_ptr<swift::reflection::FieldRecordBase>>
payload_fields;
std::vector<std::unique_ptr<swift::reflection::FieldRecordBase>>
non_payload_fields;
auto variant_part = die.GetFirstChild();
for (DWARFDIE child_die : variant_part.children()) {
auto tag = child_die.Tag();
Expand All @@ -227,11 +232,69 @@ class DWARFFieldDescriptorImpl : public swift::reflection::FieldDescriptorBase {
bool is_indirect_case = false;
// Unused by type info construction.
bool is_var = false;
fields.emplace_back(std::make_unique<DWARFFieldRecordImpl>(
is_indirect_case, is_var, ConstString(member_field_name),
member_mangled_typename));

// If there is a type, this case has a payload.
if (member_type)
payload_fields.emplace_back(std::make_unique<DWARFFieldRecordImpl>(
is_indirect_case, is_var, ConstString(member_field_name),
member_mangled_typename));
else
non_payload_fields.emplace_back(std::make_unique<DWARFFieldRecordImpl>(
is_indirect_case, is_var, ConstString(member_field_name),
member_mangled_typename));
}
return fields;
// Add the non-payload cases to the end.
payload_fields.insert(payload_fields.end(),
std::make_move_iterator(non_payload_fields.begin()),
std::make_move_iterator(non_payload_fields.end()));
return payload_fields;
}
};

class DWARFMultiPayloadEnumDescriptorImpl
: public swift::reflection::MultiPayloadEnumDescriptorBase {
ConstString m_mangled_name;
DIERef m_die_ref;
std::vector<uint8_t> m_spare_bits_mask;
uint64_t m_byte_offset;

public:
~DWARFMultiPayloadEnumDescriptorImpl() override = default;

DWARFMultiPayloadEnumDescriptorImpl(ConstString mangled_name, DIERef die_ref,
std::vector<uint8_t> &&spare_bits_mask,
uint64_t byte_offset)
: swift::reflection::MultiPayloadEnumDescriptorBase(),
m_mangled_name(mangled_name), m_die_ref(die_ref),
m_spare_bits_mask(std::move(spare_bits_mask)),
m_byte_offset(byte_offset) {}

llvm::StringRef getMangledTypeName() override {
return m_mangled_name.GetStringRef();
}

uint32_t getContentsSizeInWords() const override {
return m_spare_bits_mask.size() / 4;
}

size_t getSizeInBytes() const override { return m_spare_bits_mask.size(); }

uint32_t getFlags() const override { return usesPayloadSpareBits(); }

bool usesPayloadSpareBits() const override {
return !m_spare_bits_mask.empty();
}

uint32_t getPayloadSpareBitMaskByteOffset() const override {
return m_byte_offset;
}

uint32_t getPayloadSpareBitMaskByteCount() const override {
return getSizeInBytes();
}

const uint8_t *getPayloadSpareBits() const override {
return m_spare_bits_mask.data();
}
};
} // namespace
Expand Down Expand Up @@ -261,8 +324,8 @@ DWARFASTParserSwift::getBuiltinTypeDescriptor(
die.GetAttributeValueAsUnsigned(DW_AT_byte_size, LLDB_INVALID_ADDRESS);
if (byte_size == LLDB_INVALID_ADDRESS)
return {};
auto alignment = die.GetAttributeValueAsUnsigned(
DW_AT_alignment, byte_size == 0 ? 1 : byte_size);

auto alignment = die.GetAttributeValueAsUnsigned(DW_AT_alignment, 8);

// TODO: this seems simple to calculate but maybe we should encode the stride
// in DWARF? That's what reflection metadata does.
Expand All @@ -278,6 +341,83 @@ DWARFASTParserSwift::getBuiltinTypeDescriptor(
type.GetMangledTypeName());
}

std::unique_ptr<swift::reflection::MultiPayloadEnumDescriptorBase>
DWARFASTParserSwift::getMultiPayloadEnumDescriptor(
const swift::reflection::TypeRef *TR) {
if (!Target::GetGlobalProperties().GetSwiftEnableFullDwarfDebugging())
return nullptr;

auto pair = getTypeAndDie(m_swift_typesystem, TR);
if (!pair)
return nullptr;

auto [type, die] = *pair;
if (!die)
return nullptr;

auto kind = getFieldDescriptorKindForDie(type);
if (!kind)
return nullptr;

auto child_die = die.GetFirstChild();
auto bit_offset =
child_die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_bit_offset, 0);

auto byte_offset = (bit_offset + 7) / 8;

const auto &attributes = child_die.GetAttributes();
auto spare_bits_mask_idx =
attributes.FindAttributeIndex(llvm::dwarf::DW_AT_APPLE_spare_bits_mask);
if (spare_bits_mask_idx == UINT32_MAX)
return nullptr;

DWARFFormValue form_value;
attributes.ExtractFormValueAtIndex(spare_bits_mask_idx, form_value);

if (!form_value.IsValid()) {
if (auto *log = GetLog(LLDBLog::Types)) {
std::stringstream ss;
TR->dump(ss);
LLDB_LOG(log,
"Could not produce MultiPayloadEnumTypeInfo for typeref: {0}",
ss.str());
}
return nullptr;
}
// If there's a block data, this is a number bigger than 64 bits already
// encoded as an array.
if (form_value.BlockData()) {
uint64_t block_length = form_value.Unsigned();
std::vector<uint8_t> bytes(form_value.BlockData(),
form_value.BlockData() + block_length);
return std::make_unique<DWARFMultiPayloadEnumDescriptorImpl>(
type.GetMangledTypeName(), *die.GetDIERef(),
std::move(bytes), byte_offset);
}

// If there is no block data, the spare bits mask is encoded as a single 64
// bit number. Convert this to a byte array with only the amount of bytes
// necessary to cover the whole number (see
// MultiPayloadEnumDescriptorBuilder::layout on GenReflection.cpp for a
// similar calculation when emitting this into metadata).
llvm::APInt bits(64, form_value.Unsigned());
auto bitsInMask = bits.getActiveBits();
uint32_t bytesInMask = (bitsInMask + 7) / 8;
auto wordsInMask = (bytesInMask + 3) / 4;
bits = bits.zextOrTrunc(wordsInMask * 32);

std::vector<uint8_t> bytes;
for (size_t i = 0; i < bytesInMask; ++i) {
uint8_t byte = bits.extractBitsAsZExtValue(8, 0);
bytes.push_back(byte);
bits.lshrInPlace(8);
}

return std::make_unique<DWARFMultiPayloadEnumDescriptorImpl>(
type.GetMangledTypeName(), *die.GetDIERef(), std::move(bytes),
byte_offset);
}

namespace {
DWARFDIE FindSuperClassDIE(DWARFDIE &die) {
const auto inheritance_die_it =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3443,6 +3443,9 @@ size_t TypeSystemSwiftTypeRef::GetIndexOfChildMemberWithName(
#ifndef NDEBUG
// This block is a custom VALIDATE_AND_RETURN implementation to support
// checking the return value, plus the by-ref `child_indexes`.
if (!ModuleList::GetGlobalModuleListProperties()
.GetSwiftValidateTypeSystem())
return index_size;
if (!GetSwiftASTContextFromExecutionContext(exe_ctx))
return index_size;
auto swift_scratch_ctx_lock = SwiftScratchContextLock(exe_ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,102 @@ def test(self):
],
)

# TODO: test enums when "rdar://119343683 (Embedded Swift trivial case enum fails to link)" is solved
self.expect(
"frame variable nonPayload1", substrs=["NonPayloadEnum) nonPayload1 = one"]
)
self.expect(
"frame variable nonPayload2", substrs=["NonPayloadEnum) nonPayload2 = two"]
)
self.expect(
"frame variable singlePayload",
substrs=[
"SinglePayloadEnum) singlePayload = ",
"payload {",
"a = (field = 4.2000000000000002)",
"b = 123456",
],
)
self.expect(
"frame variable emptySinglePayload",
substrs=["SinglePayloadEnum) emptySinglePayload = nonPayloadTwo"],
)

self.expect("frame variable sup", substrs=["Sup) sup = ", "supField = 42"])
self.expect("frame variable sub", substrs=["Sub) sub = ", "Sup = {", "supField = 42", "subField = {", "a = (field = 4.2000000000000002", "b = 123456"])
self.expect("frame variable subSub", substrs=["SubSub) subSub =", "a.Sub = {", "a.Sup = {", "supField = 42", "subField = {", "a = (field = 4.2000000000000002", "b = 123456", "subSubField = (field = 4.2000000000000002)"])
self.expect(
"frame variable smallMultipayloadEnum1",
substrs=[
"SmallMultipayloadEnum) smallMultipayloadEnum1 = one {",
"one = two",
],
)
self.expect(
"frame variable smallMultipayloadEnum2",
substrs=[
"SmallMultipayloadEnum) smallMultipayloadEnum2 = two {",
"two = one",
],
)
self.expect(
"frame variable bigMultipayloadEnum1",
substrs=[
"BigMultipayloadEnum) bigMultipayloadEnum1 = one {",
"0 = ",
"(supField = 42)",
"1 = ",
"(supField = 43)",
"2 = ",
"(supField = 44)",
],
)

self.expect(
"frame variable fullMultipayloadEnum1",
substrs=["FullMultipayloadEnum) fullMultipayloadEnum1 = ", "(one = 120)"],
)
self.expect(
"frame variable fullMultipayloadEnum2",
substrs=[
"FullMultipayloadEnum) fullMultipayloadEnum2 = ",
"(two = 9.2100000000000008)",
],
)

self.expect(
"frame variable bigFullMultipayloadEnum1",
substrs=[
"a.BigFullMultipayloadEnum) bigFullMultipayloadEnum1 = one {",
"one = (0 = 209, 1 = 315)",
],
)
self.expect(
"frame variable bigFullMultipayloadEnum2",
substrs=[
"a.BigFullMultipayloadEnum) bigFullMultipayloadEnum2 = two {",
"two = (0 = 452.19999999999999, 1 = 753.89999999999998)",
],
)

self.expect("frame variable sup", substrs=["Sup) sup = ", "supField = 42"])
self.expect(
"frame variable sub",
substrs=[
"Sub) sub = ",
"Sup = {",
"supField = 42",
"subField = {",
"a = (field = 4.2000000000000002",
"b = 123456",
],
)
self.expect(
"frame variable subSub",
substrs=[
"SubSub) subSub =",
"a.Sub = {",
"a.Sup = {",
"supField = 42",
"subField = {",
"a = (field = 4.2000000000000002",
"b = 123456",
"subSubField = (field = 4.2000000000000002)",
],
)
Loading