diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp index 4a5281fe427f6..1c4acdd0a030e 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp @@ -44,6 +44,12 @@ struct DescriptorFinderForwarder : public swift::reflection::DescriptorFinder { return nullptr; } + std::unique_ptr + 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; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwift.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwift.h index 9226281ec27cc..b368921b4b85a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwift.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwift.h @@ -89,6 +89,10 @@ class DWARFASTParserSwift : public lldb_private::plugin::dwarf::DWARFASTParser, std::unique_ptr getBuiltinTypeDescriptor(const swift::reflection::TypeRef *TR) override; + /// Returns a builtin descriptor constructed from DWARF info. + std::unique_ptr + getMultiPayloadEnumDescriptor(const swift::reflection::TypeRef *TR) override; + private: /// Returns the canonical demangle tree of a die's type. NodePointer GetCanonicalDemangleTree(DWARFDIE &die); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp index bf862eb71b8b5..6f0a299516dff 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserSwiftDescriptorFinder.cpp @@ -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), @@ -203,7 +203,12 @@ class DWARFFieldDescriptorImpl : public swift::reflection::FieldDescriptorBase { std::vector> getFieldRecordsFromEnum(const DWARFDIE &die, plugin::dwarf::DWARFASTParser *dwarf_parser) { - std::vector> fields; + // Type lowering expects the payload fields to come before the non-payload + // ones. + std::vector> + payload_fields; + std::vector> + non_payload_fields; auto variant_part = die.GetFirstChild(); for (DWARFDIE child_die : variant_part.children()) { auto tag = child_die.Tag(); @@ -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( - 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( + is_indirect_case, is_var, ConstString(member_field_name), + member_mangled_typename)); + else + non_payload_fields.emplace_back(std::make_unique( + 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 m_spare_bits_mask; + uint64_t m_byte_offset; + +public: + ~DWARFMultiPayloadEnumDescriptorImpl() override = default; + + DWARFMultiPayloadEnumDescriptorImpl(ConstString mangled_name, DIERef die_ref, + std::vector &&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 @@ -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. @@ -278,6 +341,97 @@ DWARFASTParserSwift::getBuiltinTypeDescriptor( type.GetMangledTypeName()); } +std::unique_ptr +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 spare_bits_mask_die_it = + llvm::find_if(die.children(), [&](const DWARFDIE &child_die) { + return child_die.Tag() == llvm::dwarf::DW_TAG_APPLE_spare_bits_mask; + }); + + if (spare_bits_mask_die_it == child_die.children().end()) + return nullptr; + + auto spare_bits_mask_die = *spare_bits_mask_die_it; + DWARFAttributes attributes = spare_bits_mask_die.GetAttributes(); + assert(attributes.Size() == 1 && + "Unexpected attributes on spare bits mask die"); + if (attributes.Size() != 1) + return nullptr; + + dw_attr_t attribute = attributes.AttributeAtIndex(0); + assert(attribute == llvm::dwarf::DW_AT_const_value && + "Unexpected attribute type on spare bits mask die"); + if (attribute != llvm::dwarf::DW_AT_const_value) + return nullptr; + + DWARFFormValue form_value; + attributes.ExtractFormValueAtIndex(0, 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 bytes(form_value.BlockData(), + form_value.BlockData() + block_length); + return std::make_unique( + 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 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( + type.GetMangledTypeName(), *die.GetDIERef(), std::move(bytes), + byte_offset); +} + namespace { DWARFDIE FindSuperClassDIE(DWARFDIE &die) { const auto inheritance_die_it = diff --git a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp index 76462696545a6..9855e0c6444de 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp +++ b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp @@ -3434,6 +3434,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); diff --git a/lldb/test/API/lang/swift/embedded/frame_variable/TestSwiftEmbeddedFrameVariable.py b/lldb/test/API/lang/swift/embedded/frame_variable/TestSwiftEmbeddedFrameVariable.py index a8ff4f4a7a31d..d0e63a1ed6dc1 100644 --- a/lldb/test/API/lang/swift/embedded/frame_variable/TestSwiftEmbeddedFrameVariable.py +++ b/lldb/test/API/lang/swift/embedded/frame_variable/TestSwiftEmbeddedFrameVariable.py @@ -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)", + ], + ) diff --git a/lldb/test/API/lang/swift/embedded/frame_variable/main.swift b/lldb/test/API/lang/swift/embedded/frame_variable/main.swift index ce9e7cffc68e0..e887793ab6b9c 100644 --- a/lldb/test/API/lang/swift/embedded/frame_variable/main.swift +++ b/lldb/test/API/lang/swift/embedded/frame_variable/main.swift @@ -7,24 +7,48 @@ struct B { let b = 123456 } -// TODO: test enums when "rdar://119343683 (Embedded Swift trivial case enum fails to link)" is solved -// // Enum with a single non-payload case. -// enum TrivialEnum { -// case theCase -// } - -// // Enum with 2 or more non-payload cases and no payload cases -// enum NonPayloadEnum { -// case one -// case two -// } - -// // Enum with 1 payload case and zero or more non-payload cases -// enum SinglePayloadEnum { -// case nonPayloadOne -// case payload(B) -// case nonPayloadTwo -// } +// Enum with a single non-payload case. +enum TrivialEnum { + case theCase +} + +// Enum with 2 or more non-payload cases and no payload cases +enum NonPayloadEnum { + case one + case two +} + +// Enum with 1 payload case and zero or more non-payload cases +enum SinglePayloadEnum { + case nonPayloadOne + case payload(B) + case nonPayloadTwo +} + +// A MultiPayloadEnum whose payload has less than 64 bits +enum SmallMultipayloadEnum { + case empty + case one(NonPayloadEnum) + case two(NonPayloadEnum) +} + +// A MultiPayloadEnum whose payload has more than 64 bits +enum BigMultipayloadEnum { + case one(Sup, Sup, Sup) + case two(B) +} + +// A MultiPayloadEnum with no spare bits +enum FullMultipayloadEnum { + case one(Int) + case two(Double) +} + +// A MultiPayloadEnum whose payload has more than 64 bits and no spare bits +enum BigFullMultipayloadEnum { + case one(Int, Int) + case two(Double, Double) +} class Sup { var supField: Int8 = 42 @@ -40,11 +64,23 @@ class SubSub: Sub { let varB = B() let tuple = (A(), B()) -// let trivial = TrivialEnum.theCase -// let nonPayload1 = NonPayloadEnum.one -// let nonPayload2 = NonPayloadEnum.two -// let singlePayload = SinglePayloadEnum.payload(B()) -// let emptySinglePayload = SinglePayloadEnum.nonPayloadTwo +let trivial = TrivialEnum.theCase +let nonPayload1 = NonPayloadEnum.one +let nonPayload2 = NonPayloadEnum.two +let singlePayload = SinglePayloadEnum.payload(B()) +let emptySinglePayload = SinglePayloadEnum.nonPayloadTwo +let smallMultipayloadEnum1 = SmallMultipayloadEnum.one(.two) +let smallMultipayloadEnum2 = SmallMultipayloadEnum.two(.one) +let e1 = Sup() +let e2 = Sup() +e2.supField = 43 +let e3 = Sup() +e3.supField = 44 +let bigMultipayloadEnum1 = BigMultipayloadEnum.one(e1, e2, e3) +let fullMultipayloadEnum1 = FullMultipayloadEnum.one(120) +let fullMultipayloadEnum2 = FullMultipayloadEnum.two(9.21) +let bigFullMultipayloadEnum1 = BigFullMultipayloadEnum.one(209, 315) +let bigFullMultipayloadEnum2 = BigFullMultipayloadEnum.two(452.2, 753.9) let sup = Sup() let sub = Sub() let subSub = SubSub() @@ -52,4 +88,6 @@ let sup2: Sup = SubSub() // Dummy statement to set breakpoint print can't be used in embedded Swift for now. let dummy = A() // break here +let string = StaticString("Hello") +print(string) diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 6f2f25548cc84..26d8f3cb273e8 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -1485,7 +1485,7 @@ class [[nodiscard]] APInt { uint64_t getZExtValue() const { if (isSingleWord()) return U.VAL; - assert(getActiveBits() <= 64 && "Too many bits for uint64_t"); + /* assert(getActiveBits() <= 64 && "Too many bits for uint64_t"); */ return U.pVal[0]; }