Skip to content

Commit 0d361bd

Browse files
tbkkamikeash
andauthored
Teach RemoteMirror how to project enum values (#30161)
Teach RemoteMirror how to project enum values This adds two new functions to the SwiftRemoteMirror facility that support inspecting enum values. Currently, these support non-payload enums and single-payload enums, including nested enums and payloads with struct, tuple, and reference payloads. In particular, it handles nested `Optional` types. TODO: Multi-payload enums use different strategies for encoding the cases that aren't yet supported by this code. Note: This relies on information from dataLayoutQuery to correctly decode invalid pointer values that are used to encode enums. Existing clients will need to augment their DLQ functions before using these new APIs. Resolves rdar://59961527 ``` /// Projects the value of an enum. /// /// Takes the address and typeref for an enum and determines the /// index of the currently-selected case within the enum. /// /// Returns true iff the enum case could be successfully determined. /// In particular, note that this code may fail for valid in-memory data /// if the compiler is using a strategy we do not yet understand. SWIFT_REMOTE_MIRROR_LINKAGE int swift_reflection_projectEnumValue(SwiftReflectionContextRef ContextRef, swift_addr_t EnumAddress, swift_typeref_t EnumTypeRef, uint64_t *CaseIndex); /// Finds information about a particular enum case. /// /// Given an enum typeref and index of a case, returns: /// * Typeref of the associated payload or zero if there is no payload /// * Name of the case if known. /// /// The Name points to a freshly-allocated C string on the heap. You /// are responsible for freeing the string (via `free()`) when you are finished. SWIFT_REMOTE_MIRROR_LINKAGE int swift_reflection_getEnumCaseTypeRef(SwiftReflectionContextRef ContextRef, swift_typeref_t EnumTypeRef, unsigned CaseIndex, char **CaseName, swift_typeref_t *PayloadTypeRef); ``` Co-authored-by: Mike Ash <[email protected]>
1 parent c825ed8 commit 0d361bd

30 files changed

+6642
-526
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/BinaryFormat/ELF.h"
2424
#include "llvm/Object/COFF.h"
2525

26+
#include "swift/ABI/Enum.h"
2627
#include "swift/Remote/MemoryReader.h"
2728
#include "swift/Remote/MetadataReader.h"
2829
#include "swift/Reflection/Records.h"
@@ -710,6 +711,128 @@ class ReflectionContext
710711
}
711712
}
712713

714+
bool projectEnumValue(RemoteAddress EnumAddress,
715+
const TypeRef *EnumTR,
716+
int *CaseIndex) {
717+
if (EnumTR == nullptr)
718+
return false;
719+
auto EnumTI = getTypeInfo(EnumTR);
720+
if (EnumTI == nullptr)
721+
return false;
722+
723+
auto EnumRecordTI = dyn_cast<const RecordTypeInfo>(EnumTI);
724+
if (EnumRecordTI == nullptr)
725+
return false;
726+
auto EnumSize = EnumRecordTI->getSize();
727+
728+
auto Fields = EnumRecordTI->getFields();
729+
auto FieldCount = Fields.size();
730+
if (FieldCount == 0) {
731+
return false; // No fields?
732+
}
733+
if (FieldCount == 1) {
734+
*CaseIndex = 0; // Only possible field
735+
return true;
736+
}
737+
738+
switch (EnumRecordTI->getRecordKind()) {
739+
740+
case RecordKind::NoPayloadEnum: {
741+
if (EnumSize == 0) {
742+
*CaseIndex = 0;
743+
return true;
744+
}
745+
return getReader().readInteger(EnumAddress, EnumSize, CaseIndex);
746+
}
747+
748+
case RecordKind::SinglePayloadEnum: {
749+
FieldInfo PayloadCase = Fields[0];
750+
if (!PayloadCase.TR)
751+
return false;
752+
unsigned long NonPayloadCaseCount = FieldCount - 1;
753+
unsigned long PayloadExtraInhabitants = PayloadCase.TI.getNumExtraInhabitants();
754+
unsigned discriminator = 0;
755+
auto PayloadSize = PayloadCase.TI.getSize();
756+
if (NonPayloadCaseCount >= PayloadExtraInhabitants) {
757+
// There are more cases than inhabitants, we need a separate discriminator.
758+
auto TagInfo = getEnumTagCounts(PayloadSize, NonPayloadCaseCount, 1);
759+
auto TagSize = TagInfo.numTagBytes;
760+
auto TagAddress = RemoteAddress(EnumAddress.getAddressData() + PayloadSize);
761+
if (!getReader().readInteger(TagAddress, TagSize, &discriminator))
762+
return false;
763+
}
764+
765+
if (PayloadExtraInhabitants == 0) {
766+
// Payload has no XI, so discriminator fully determines the case
767+
*CaseIndex = discriminator;
768+
return true;
769+
} else if (discriminator == 0) {
770+
// The value overlays the payload ... ask the payload to decode it.
771+
int t;
772+
if (!PayloadCase.TI.readExtraInhabitantIndex(getReader(), EnumAddress, &t)) {
773+
return false;
774+
}
775+
if (t < 0) {
776+
*CaseIndex = 0;
777+
return true;
778+
} else if ((unsigned long)t <= NonPayloadCaseCount) {
779+
*CaseIndex = t + 1;
780+
return true;
781+
}
782+
return false;
783+
} else {
784+
// The entire payload area is available for additional cases:
785+
auto TagSize = std::max(PayloadSize, 4U); // XXX TODO XXX CHECK THIS
786+
auto offset = 1 + PayloadExtraInhabitants; // Cases coded with discriminator = 0
787+
unsigned casesInPayload = 1 << (TagSize * 8U);
788+
unsigned payloadCode;
789+
if (!getReader().readInteger(EnumAddress, TagSize, &payloadCode))
790+
return false;
791+
*CaseIndex = offset + (discriminator - 1) * casesInPayload + payloadCode;
792+
return true;
793+
}
794+
}
795+
796+
case RecordKind::MultiPayloadEnum: {
797+
// TODO: Support multipayload enums
798+
break;
799+
}
800+
801+
default:
802+
// Unknown record kind.
803+
break;
804+
}
805+
return false;
806+
}
807+
808+
bool getEnumCaseTypeRef(const TypeRef *EnumTR,
809+
unsigned CaseIndex,
810+
std::string &Name,
811+
const TypeRef **OutPayloadTR) {
812+
*OutPayloadTR = nullptr;
813+
814+
if (EnumTR == nullptr)
815+
return false;
816+
817+
auto EnumTI = getTypeInfo(EnumTR);
818+
if (EnumTI == nullptr)
819+
return false;
820+
821+
auto EnumRecordTI = dyn_cast<const RecordTypeInfo>(EnumTI);
822+
if (EnumRecordTI == nullptr)
823+
return false;
824+
825+
auto NumCases = EnumRecordTI->getNumFields();
826+
if (CaseIndex >= NumCases) {
827+
return false;
828+
} else {
829+
const auto Case = EnumRecordTI->getFields()[CaseIndex];
830+
Name = Case.Name;
831+
*OutPayloadTR = Case.TR;
832+
return true;
833+
}
834+
}
835+
713836
/// Return a description of the layout of a value with the given type.
714837
const TypeInfo *getTypeInfo(const TypeRef *TR) {
715838
return getBuilder().getTypeConverter().getTypeInfo(TR);

include/swift/Reflection/TypeLowering.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ enum class TypeInfoKind : unsigned {
106106
Builtin,
107107
Record,
108108
Reference,
109+
Invalid,
109110
};
110111

111112
class TypeInfo {
@@ -124,6 +125,10 @@ class TypeInfo {
124125
assert(Alignment > 0);
125126
}
126127

128+
TypeInfo(): Kind(TypeInfoKind::Invalid), Size(0), Alignment(0), Stride(0),
129+
NumExtraInhabitants(0), BitwiseTakable(true) {
130+
}
131+
127132
TypeInfoKind getKind() const { return Kind; }
128133

129134
unsigned getSize() const { return Size; }
@@ -134,11 +139,24 @@ class TypeInfo {
134139

135140
void dump() const;
136141
void dump(FILE *file, unsigned Indent = 0) const;
142+
143+
// Using the provided reader, inspect our value.
144+
// Return false if we can't inspect value.
145+
// Set *inhabitant to <0 if the value is valid (not an XI)
146+
// Else set *inhabitant to the XI value (counting from 0)
147+
virtual bool readExtraInhabitantIndex(remote::MemoryReader &reader,
148+
remote::RemoteAddress address,
149+
int *index) const {
150+
return false;
151+
}
152+
153+
virtual ~TypeInfo() { }
137154
};
138155

139156
struct FieldInfo {
140157
std::string Name;
141158
unsigned Offset;
159+
int Value;
142160
const TypeRef *TR;
143161
const TypeInfo &TI;
144162
};
@@ -155,6 +173,10 @@ class BuiltinTypeInfo : public TypeInfo {
155173
return Name;
156174
}
157175

176+
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
177+
remote::RemoteAddress address,
178+
int *extraInhabitantIndex) const;
179+
158180
static bool classof(const TypeInfo *TI) {
159181
return TI->getKind() == TypeInfoKind::Builtin;
160182
}
@@ -178,6 +200,10 @@ class RecordTypeInfo : public TypeInfo {
178200
unsigned getNumFields() const { return Fields.size(); }
179201
const std::vector<FieldInfo> &getFields() const { return Fields; }
180202

203+
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
204+
remote::RemoteAddress address,
205+
int *index) const;
206+
181207
static bool classof(const TypeInfo *TI) {
182208
return TI->getKind() == TypeInfoKind::Record;
183209
}
@@ -206,6 +232,16 @@ class ReferenceTypeInfo : public TypeInfo {
206232
return Refcounting;
207233
}
208234

235+
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
236+
remote::RemoteAddress address,
237+
int *extraInhabitantIndex) const {
238+
if (getNumExtraInhabitants() == 0) {
239+
*extraInhabitantIndex = -1;
240+
return true;
241+
}
242+
return reader.readHeapObjectExtraInhabitantIndex(address, extraInhabitantIndex);
243+
}
244+
209245
static bool classof(const TypeInfo *TI) {
210246
return TI->getKind() == TypeInfoKind::Reference;
211247
}

include/swift/Reflection/TypeRefBuilder.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -215,23 +215,24 @@ struct ClosureContextInfo {
215215

216216
struct FieldTypeInfo {
217217
std::string Name;
218+
int Value;
218219
const TypeRef *TR;
219220
bool Indirect;
220221

221-
FieldTypeInfo() : Name(""), TR(nullptr), Indirect(false) {}
222-
FieldTypeInfo(const std::string &Name, const TypeRef *TR, bool Indirect)
223-
: Name(Name), TR(TR), Indirect(Indirect) {}
222+
FieldTypeInfo() : Name(""), Value(0), TR(nullptr), Indirect(false) {}
223+
FieldTypeInfo(const std::string &Name, int Value, const TypeRef *TR, bool Indirect)
224+
: Name(Name), Value(Value), TR(TR), Indirect(Indirect) {}
224225

225-
static FieldTypeInfo forEmptyCase(std::string Name) {
226-
return FieldTypeInfo(Name, nullptr, false);
226+
static FieldTypeInfo forEmptyCase(std::string Name, int Value) {
227+
return FieldTypeInfo(Name, Value, nullptr, false);
227228
}
228229

229-
static FieldTypeInfo forIndirectCase(std::string Name, const TypeRef *TR) {
230-
return FieldTypeInfo(Name, TR, true);
230+
static FieldTypeInfo forIndirectCase(std::string Name, int Value, const TypeRef *TR) {
231+
return FieldTypeInfo(Name, Value, TR, true);
231232
}
232233

233-
static FieldTypeInfo forField(std::string Name, const TypeRef *TR) {
234-
return FieldTypeInfo(Name, TR, false);
234+
static FieldTypeInfo forField(std::string Name, int Value, const TypeRef *TR) {
235+
return FieldTypeInfo(Name, Value, TR, false);
235236
}
236237
};
237238

include/swift/Remote/InProcessMemoryReader.h

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
#include <cstring>
2323

24+
#if defined(__APPLE__) && defined(__MACH__)
25+
#include <TargetConditionals.h>
26+
#endif
27+
2428
namespace swift {
2529
namespace remote {
2630

@@ -29,19 +33,53 @@ namespace remote {
2933
class InProcessMemoryReader final : public MemoryReader {
3034
bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
3135
void *outBuffer) override {
36+
#if defined(__APPLE__) && __APPLE__
37+
auto applePlatform = true;
38+
#else
39+
auto applePlatform = false;
40+
#endif
41+
#if defined(__APPLE__) && __APPLE__ && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_IOS) && TARGET_OS_WATCH) || (defined(TARGET_OS_TV) && TARGET_OS_TV))
42+
auto iosDerivedPlatform = true;
43+
#else
44+
auto iosDerivedPlatform = false;
45+
#endif
46+
3247
switch (type) {
33-
case DLQ_GetPointerSize: {
34-
auto result = static_cast<uint8_t *>(outBuffer);
35-
*result = sizeof(void *);
36-
return true;
48+
case DLQ_GetPointerSize: {
49+
auto result = static_cast<uint8_t *>(outBuffer);
50+
*result = sizeof(void *);
51+
return true;
52+
}
53+
case DLQ_GetSizeSize: {
54+
auto result = static_cast<uint8_t *>(outBuffer);
55+
*result = sizeof(size_t);
56+
return true;
57+
}
58+
case DLQ_GetObjCReservedLowBits: {
59+
auto result = static_cast<uint8_t *>(outBuffer);
60+
if (applePlatform && !iosDerivedPlatform && (sizeof(void *) == 8)) {
61+
// Obj-C reserves low bit on 64-bit macOS only.
62+
// Other Apple platforms don't reserve this bit (even when
63+
// running on x86_64-based simulators).
64+
*result = 1;
65+
} else {
66+
*result = 0;
3767
}
38-
case DLQ_GetSizeSize: {
39-
auto result = static_cast<uint8_t *>(outBuffer);
40-
*result = sizeof(size_t);
41-
return true;
68+
return true;
69+
}
70+
case DLQ_GetLeastValidPointerValue: {
71+
auto result = static_cast<uint64_t *>(outBuffer);
72+
if (applePlatform && (sizeof(void *) == 8)) {
73+
// Swift reserves the first 4GiB on Apple 64-bit platforms
74+
*result = 0x100000000;
75+
return 1;
76+
} else {
77+
// Swift reserves the first 4KiB everywhere else
78+
*result = 0x1000;
4279
}
80+
return true;
81+
}
4382
}
44-
4583
return false;
4684
}
4785

0 commit comments

Comments
 (0)