Skip to content
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
123 changes: 123 additions & 0 deletions include/swift/Reflection/ReflectionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/COFF.h"

#include "swift/ABI/Enum.h"
#include "swift/Remote/MemoryReader.h"
#include "swift/Remote/MetadataReader.h"
#include "swift/Reflection/Records.h"
Expand Down Expand Up @@ -710,6 +711,128 @@ class ReflectionContext
}
}

bool projectEnumValue(RemoteAddress EnumAddress,
const TypeRef *EnumTR,
int *CaseIndex) {
if (EnumTR == nullptr)
return false;
auto EnumTI = getTypeInfo(EnumTR);
if (EnumTI == nullptr)
return false;

auto EnumRecordTI = dyn_cast<const RecordTypeInfo>(EnumTI);
if (EnumRecordTI == nullptr)
return false;
auto EnumSize = EnumRecordTI->getSize();

auto Fields = EnumRecordTI->getFields();
auto FieldCount = Fields.size();
if (FieldCount == 0) {
return false; // No fields?
}
if (FieldCount == 1) {
*CaseIndex = 0; // Only possible field
return true;
}

switch (EnumRecordTI->getRecordKind()) {

case RecordKind::NoPayloadEnum: {
if (EnumSize == 0) {
*CaseIndex = 0;
return true;
}
return getReader().readInteger(EnumAddress, EnumSize, CaseIndex);
}

case RecordKind::SinglePayloadEnum: {
FieldInfo PayloadCase = Fields[0];
if (!PayloadCase.TR)
return false;
unsigned long NonPayloadCaseCount = FieldCount - 1;
unsigned long PayloadExtraInhabitants = PayloadCase.TI.getNumExtraInhabitants();
unsigned discriminator = 0;
auto PayloadSize = PayloadCase.TI.getSize();
if (NonPayloadCaseCount >= PayloadExtraInhabitants) {
// There are more cases than inhabitants, we need a separate discriminator.
auto TagInfo = getEnumTagCounts(PayloadSize, NonPayloadCaseCount, 1);
auto TagSize = TagInfo.numTagBytes;
auto TagAddress = RemoteAddress(EnumAddress.getAddressData() + PayloadSize);
if (!getReader().readInteger(TagAddress, TagSize, &discriminator))
return false;
}

if (PayloadExtraInhabitants == 0) {
// Payload has no XI, so discriminator fully determines the case
*CaseIndex = discriminator;
return true;
} else if (discriminator == 0) {
// The value overlays the payload ... ask the payload to decode it.
int t;
if (!PayloadCase.TI.readExtraInhabitantIndex(getReader(), EnumAddress, &t)) {
return false;
}
if (t < 0) {
*CaseIndex = 0;
return true;
} else if ((unsigned long)t <= NonPayloadCaseCount) {
*CaseIndex = t + 1;
return true;
}
return false;
} else {
// The entire payload area is available for additional cases:
auto TagSize = std::max(PayloadSize, 4U); // XXX TODO XXX CHECK THIS
auto offset = 1 + PayloadExtraInhabitants; // Cases coded with discriminator = 0
unsigned casesInPayload = 1 << (TagSize * 8U);
unsigned payloadCode;
if (!getReader().readInteger(EnumAddress, TagSize, &payloadCode))
return false;
*CaseIndex = offset + (discriminator - 1) * casesInPayload + payloadCode;
return true;
}
}

case RecordKind::MultiPayloadEnum: {
// TODO: Support multipayload enums
break;
}

default:
// Unknown record kind.
break;
}
return false;
}

bool getEnumCaseTypeRef(const TypeRef *EnumTR,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this API is redundant. In the C API, we already have swift_reflection_childOfTypeRef()

unsigned CaseIndex,
std::string &Name,
const TypeRef **OutPayloadTR) {
*OutPayloadTR = nullptr;

if (EnumTR == nullptr)
return false;

auto EnumTI = getTypeInfo(EnumTR);
if (EnumTI == nullptr)
return false;

auto EnumRecordTI = dyn_cast<const RecordTypeInfo>(EnumTI);
if (EnumRecordTI == nullptr)
return false;

auto NumCases = EnumRecordTI->getNumFields();
if (CaseIndex >= NumCases) {
return false;
} else {
const auto Case = EnumRecordTI->getFields()[CaseIndex];
Name = Case.Name;
*OutPayloadTR = Case.TR;
return true;
}
}

/// Return a description of the layout of a value with the given type.
const TypeInfo *getTypeInfo(const TypeRef *TR) {
return getBuilder().getTypeConverter().getTypeInfo(TR);
Expand Down
36 changes: 36 additions & 0 deletions include/swift/Reflection/TypeLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ enum class TypeInfoKind : unsigned {
Builtin,
Record,
Reference,
Invalid,
};

class TypeInfo {
Expand All @@ -124,6 +125,10 @@ class TypeInfo {
assert(Alignment > 0);
}

TypeInfo(): Kind(TypeInfoKind::Invalid), Size(0), Alignment(0), Stride(0),
NumExtraInhabitants(0), BitwiseTakable(true) {
}

TypeInfoKind getKind() const { return Kind; }

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

void dump() const;
void dump(FILE *file, unsigned Indent = 0) const;

// Using the provided reader, inspect our value.
// Return false if we can't inspect value.
// Set *inhabitant to <0 if the value is valid (not an XI)
// Else set *inhabitant to the XI value (counting from 0)
virtual bool readExtraInhabitantIndex(remote::MemoryReader &reader,
remote::RemoteAddress address,
int *index) const {
return false;
}

virtual ~TypeInfo() { }
};

struct FieldInfo {
std::string Name;
unsigned Offset;
int Value;
const TypeRef *TR;
const TypeInfo &TI;
};
Expand All @@ -155,6 +173,10 @@ class BuiltinTypeInfo : public TypeInfo {
return Name;
}

bool readExtraInhabitantIndex(remote::MemoryReader &reader,
remote::RemoteAddress address,
int *extraInhabitantIndex) const;

static bool classof(const TypeInfo *TI) {
return TI->getKind() == TypeInfoKind::Builtin;
}
Expand All @@ -178,6 +200,10 @@ class RecordTypeInfo : public TypeInfo {
unsigned getNumFields() const { return Fields.size(); }
const std::vector<FieldInfo> &getFields() const { return Fields; }

bool readExtraInhabitantIndex(remote::MemoryReader &reader,
remote::RemoteAddress address,
int *index) const;

static bool classof(const TypeInfo *TI) {
return TI->getKind() == TypeInfoKind::Record;
}
Expand Down Expand Up @@ -206,6 +232,16 @@ class ReferenceTypeInfo : public TypeInfo {
return Refcounting;
}

bool readExtraInhabitantIndex(remote::MemoryReader &reader,
remote::RemoteAddress address,
int *extraInhabitantIndex) const {
if (getNumExtraInhabitants() == 0) {
*extraInhabitantIndex = -1;
return true;
}
return reader.readHeapObjectExtraInhabitantIndex(address, extraInhabitantIndex);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect ReferenceTypeInfo is used for both function pointers and heap pointers, but I'm not quite sure how to distinguish those cases.

}

static bool classof(const TypeInfo *TI) {
return TI->getKind() == TypeInfoKind::Reference;
}
Expand Down
19 changes: 10 additions & 9 deletions include/swift/Reflection/TypeRefBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,23 +215,24 @@ struct ClosureContextInfo {

struct FieldTypeInfo {
std::string Name;
int Value;
const TypeRef *TR;
bool Indirect;

FieldTypeInfo() : Name(""), TR(nullptr), Indirect(false) {}
FieldTypeInfo(const std::string &Name, const TypeRef *TR, bool Indirect)
: Name(Name), TR(TR), Indirect(Indirect) {}
FieldTypeInfo() : Name(""), Value(0), TR(nullptr), Indirect(false) {}
FieldTypeInfo(const std::string &Name, int Value, const TypeRef *TR, bool Indirect)
: Name(Name), Value(Value), TR(TR), Indirect(Indirect) {}

static FieldTypeInfo forEmptyCase(std::string Name) {
return FieldTypeInfo(Name, nullptr, false);
static FieldTypeInfo forEmptyCase(std::string Name, int Value) {
return FieldTypeInfo(Name, Value, nullptr, false);
}

static FieldTypeInfo forIndirectCase(std::string Name, const TypeRef *TR) {
return FieldTypeInfo(Name, TR, true);
static FieldTypeInfo forIndirectCase(std::string Name, int Value, const TypeRef *TR) {
return FieldTypeInfo(Name, Value, TR, true);
}

static FieldTypeInfo forField(std::string Name, const TypeRef *TR) {
return FieldTypeInfo(Name, TR, false);
static FieldTypeInfo forField(std::string Name, int Value, const TypeRef *TR) {
return FieldTypeInfo(Name, Value, TR, false);
}
};

Expand Down
56 changes: 47 additions & 9 deletions include/swift/Remote/InProcessMemoryReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@

#include <cstring>

#if defined(__APPLE__) && defined(__MACH__)
#include <TargetConditionals.h>
#endif

namespace swift {
namespace remote {

Expand All @@ -29,19 +33,53 @@ namespace remote {
class InProcessMemoryReader final : public MemoryReader {
bool queryDataLayout(DataLayoutQueryType type, void *inBuffer,
void *outBuffer) override {
#if defined(__APPLE__) && __APPLE__
auto applePlatform = true;
#else
auto applePlatform = false;
#endif
#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))
auto iosDerivedPlatform = true;
#else
auto iosDerivedPlatform = false;
#endif

switch (type) {
case DLQ_GetPointerSize: {
auto result = static_cast<uint8_t *>(outBuffer);
*result = sizeof(void *);
return true;
case DLQ_GetPointerSize: {
auto result = static_cast<uint8_t *>(outBuffer);
*result = sizeof(void *);
return true;
}
case DLQ_GetSizeSize: {
auto result = static_cast<uint8_t *>(outBuffer);
*result = sizeof(size_t);
return true;
}
case DLQ_GetObjCReservedLowBits: {
auto result = static_cast<uint8_t *>(outBuffer);
if (applePlatform && !iosDerivedPlatform && (sizeof(void *) == 8)) {
// Obj-C reserves low bit on 64-bit macOS only.
// Other Apple platforms don't reserve this bit (even when
// running on x86_64-based simulators).
*result = 1;
} else {
*result = 0;
}
case DLQ_GetSizeSize: {
auto result = static_cast<uint8_t *>(outBuffer);
*result = sizeof(size_t);
return true;
return true;
}
case DLQ_GetLeastValidPointerValue: {
auto result = static_cast<uint64_t *>(outBuffer);
if (applePlatform && (sizeof(void *) == 8)) {
// Swift reserves the first 4GiB on Apple 64-bit platforms
*result = 0x100000000;
return 1;
} else {
// Swift reserves the first 4KiB everywhere else
*result = 0x1000;
}
return true;
}
}

return false;
}

Expand Down
Loading