From 502941ca5aa5ce2aa1e197e98d91e4e7a2f46a82 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Wed, 16 Oct 2024 16:06:37 -0700 Subject: [PATCH] More arcana about MPEs carrying class existentials The first word in a class existential is the class pointer itself. This pointer exposes spare bits differently depending on the platform, which becomes apparent when you try to reflect an Optional carrying such an MPE. Add new test cases and some logic to zero out the first word of spare bit information only on platforms with 8-byte pointers. --- .../public/RemoteInspection/TypeLowering.cpp | 22 ++++++++++++----- .../Reflection/reflect_Enum_values10.swift | 24 +++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/stdlib/public/RemoteInspection/TypeLowering.cpp b/stdlib/public/RemoteInspection/TypeLowering.cpp index 20ce27e9ea64d..74a4d83205ffe 100644 --- a/stdlib/public/RemoteInspection/TypeLowering.cpp +++ b/stdlib/public/RemoteInspection/TypeLowering.cpp @@ -437,14 +437,24 @@ BitMask RecordTypeInfo::getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const // Mask the rest of the fields as usual... break; } - case RecordKind::ClassExistential: - // Class existential is a bunch of pointers that expose spare bits - // ... so we can fall through ... + case RecordKind::ClassExistential: { + // First pointer in a Class Existential is the class pointer + // itself, which can be tagged or have other mysteries on 64-bit, so + // it exposes no spare bits from the first word there... + auto pointerBytes = TC.targetPointerSize(); + if (pointerBytes == 8) { + auto zeroPointerSizedMask = BitMask::zeroMask(pointerBytes); + mask.andMask(zeroPointerSizedMask, 0); + } + // Otherwise, it's the same as an Existential Metatype + DISPATCH_FALLTHROUGH; + } case RecordKind::ExistentialMetatype: { - // A bunch of pointers that expose spare bits + // All the pointers in an Existential Metatype expose spare bits... + auto pointerBytes = TC.targetPointerSize(); auto mpePointerSpareBits = TC.getBuilder().getMultiPayloadEnumPointerMask(); - auto mpePointerSpareBitMask = BitMask(TC.targetPointerSize(), mpePointerSpareBits); - for (int offset = 0; offset < (int)getSize(); offset += TC.targetPointerSize()) { + auto mpePointerSpareBitMask = BitMask(pointerBytes, mpePointerSpareBits); + for (int offset = 0; offset < (int)getSize(); offset += pointerBytes) { mask.andMask(mpePointerSpareBitMask, offset); } return mask; diff --git a/validation-test/Reflection/reflect_Enum_values10.swift b/validation-test/Reflection/reflect_Enum_values10.swift index a41f30eb19153..c9c54addaa68f 100644 --- a/validation-test/Reflection/reflect_Enum_values10.swift +++ b/validation-test/Reflection/reflect_Enum_values10.swift @@ -147,6 +147,30 @@ reflect(enumValue: Q2.e(C())) // CHECKALL-NEXT: (enum reflect_Enum_values10.Q2) // CHECKALL-NEXT: Value: .e(_) +reflect(enumValue: Optional.some(.a(C()))) + +// CHECKALL: Reflecting an enum value. +// CHECKALL-NEXT: Type reference: +// CHECKALL-NEXT: (bound_generic_enum Swift.Optional +// CHECKALL-NEXT: (enum reflect_Enum_values10.Q2)) +// CHECKALL-NEXT: Value: .some(.a(_)) + +reflect(enumValue: Optional.some(.e(C()))) + +// CHECKALL: Reflecting an enum value. +// CHECKALL-NEXT: Type reference: +// CHECKALL-NEXT: (bound_generic_enum Swift.Optional +// CHECKALL-NEXT: (enum reflect_Enum_values10.Q2)) +// CHECKALL-NEXT: Value: .some(.e(_)) + +reflect(enumValue: Optional.none) + +// CHECKALL: Reflecting an enum value. +// CHECKALL-NEXT: Type reference: +// CHECKALL-NEXT: (bound_generic_enum Swift.Optional +// CHECKALL-NEXT: (enum reflect_Enum_values10.Q2)) +// CHECKALL-NEXT: Value: .none + doneReflecting() // CHECKALL: Done.