mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
447 lines
13 KiB
C++
447 lines
13 KiB
C++
//===--- MetadataLayout.h - Type metadata layout ----------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Information recording the layout of type metadata objects.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_IRGEN_METADATALAYOUT_H
|
|
#define SWIFT_IRGEN_METADATALAYOUT_H
|
|
|
|
#include "IRGen.h"
|
|
#include "swift/SIL/SILDeclRef.h"
|
|
|
|
namespace swift {
|
|
class ClassDecl;
|
|
class EnumDecl;
|
|
class StructDecl;
|
|
class VarDecl;
|
|
|
|
namespace irgen {
|
|
class Address;
|
|
class IRGenFunction;
|
|
class IRGenModule;
|
|
|
|
/// The total size and address point of a metadata object.
|
|
struct MetadataSize {
|
|
Size FullSize;
|
|
Size AddressPoint;
|
|
|
|
/// Return the offset from the address point to the end of the
|
|
/// metadata object.
|
|
Size getOffsetToEnd() const {
|
|
return FullSize - AddressPoint;
|
|
}
|
|
};
|
|
|
|
/// A base class for various kinds of metadata layout.
|
|
class MetadataLayout {
|
|
public:
|
|
enum class Kind {
|
|
Class,
|
|
Struct,
|
|
Enum,
|
|
ForeignClass,
|
|
};
|
|
|
|
class StoredOffset {
|
|
public:
|
|
enum Kind {
|
|
/// The high bits are an integer displacement.
|
|
Static = 0,
|
|
|
|
/// The high bits are an integer displacement relative to a offset stored
|
|
/// in a class metadata base offset global variable. This is used to
|
|
/// access members of class metadata where the superclass is resilient to
|
|
/// us, and therefore has an unknown size.
|
|
Dynamic,
|
|
};
|
|
|
|
private:
|
|
enum : uint64_t {
|
|
KindBits = 1,
|
|
KindMask = (1 << KindBits) - 1,
|
|
PayloadMask = ~uint64_t(KindMask)
|
|
};
|
|
|
|
mutable uintptr_t Data;
|
|
public:
|
|
StoredOffset() : Data(0) {}
|
|
explicit StoredOffset(Size offset, Kind kind)
|
|
: Data((static_cast<uint64_t>(offset.getValue()) << KindBits) | kind) {
|
|
assert(kind == Kind::Dynamic || !offset.isZero() &&
|
|
"cannot store a zero static offset");
|
|
if (kind == Kind::Static)
|
|
assert(getStaticOffset() == offset && "overflow");
|
|
if (kind == Kind::Dynamic)
|
|
assert(getRelativeOffset() == offset && "overflow");
|
|
}
|
|
|
|
bool isValid() const { return Data != 0; }
|
|
bool isStatic() const { return isValid() && (Data & KindMask) == Static; }
|
|
bool isDynamic() const { return (Data & KindMask) == Dynamic; }
|
|
|
|
/// If this is a metadata offset into a resilient class, returns the offset
|
|
/// relative to the size of the superclass metadata.
|
|
Size getRelativeOffset() const {
|
|
assert(isDynamic());
|
|
return Size(static_cast<int64_t>(Data) >> KindBits);
|
|
}
|
|
|
|
/// Returns the offset relative to start of metadata. Only used for
|
|
/// metadata fields whose offset is completely known at compile time.
|
|
Size getStaticOffset() const {
|
|
assert(isStatic());
|
|
return Size(static_cast<int64_t>(Data) >> KindBits);
|
|
}
|
|
};
|
|
|
|
private:
|
|
Kind TheKind;
|
|
|
|
protected:
|
|
MetadataSize TheSize;
|
|
|
|
MetadataLayout(Kind theKind) : TheKind(theKind) {}
|
|
|
|
MetadataLayout(const MetadataLayout &other) = delete;
|
|
MetadataLayout &operator=(const MetadataLayout &other) = delete;
|
|
|
|
public:
|
|
/// Destruct and deallocate this layout object.
|
|
void destroy() const;
|
|
|
|
Kind getKind() const { return TheKind; }
|
|
|
|
MetadataSize getSize() const { return TheSize; }
|
|
};
|
|
|
|
/// Base class for nominal type metadata layouts.
|
|
class NominalMetadataLayout : public MetadataLayout {
|
|
protected:
|
|
NominalTypeDecl *Nominal;
|
|
StoredOffset GenericRequirements;
|
|
|
|
NominalMetadataLayout(Kind kind, NominalTypeDecl *nominal)
|
|
: MetadataLayout(kind), Nominal(nominal) {}
|
|
|
|
Offset emitOffset(IRGenFunction &IGF, StoredOffset offset) const;
|
|
|
|
public:
|
|
NominalTypeDecl *getDecl() const {
|
|
return Nominal;
|
|
}
|
|
|
|
bool hasGenericRequirements() const {
|
|
return GenericRequirements.isValid();
|
|
}
|
|
|
|
/// Should only be used when emitting the nominal type descriptor.
|
|
Size getStaticGenericRequirementsOffset() const;
|
|
|
|
Offset getGenericRequirementsOffset(IRGenFunction &IGF) const;
|
|
|
|
static bool classof(const MetadataLayout *layout) {
|
|
switch (layout->getKind()) {
|
|
case MetadataLayout::Kind::Class:
|
|
case MetadataLayout::Kind::Enum:
|
|
case MetadataLayout::Kind::Struct:
|
|
return true;
|
|
|
|
case MetadataLayout::Kind::ForeignClass:
|
|
return false;
|
|
}
|
|
llvm_unreachable("unhandled kind");
|
|
}
|
|
};
|
|
|
|
/// Layout for class type metadata.
|
|
class ClassMetadataLayout : public NominalMetadataLayout {
|
|
public:
|
|
class MethodInfo {
|
|
public:
|
|
enum class Kind {
|
|
Offset,
|
|
DirectImpl,
|
|
};
|
|
|
|
private:
|
|
Kind TheKind;
|
|
union {
|
|
Offset TheOffset;
|
|
llvm::Function *TheImpl;
|
|
};
|
|
public:
|
|
MethodInfo(Offset offset)
|
|
: TheKind(Kind::Offset), TheOffset(offset) {}
|
|
MethodInfo(llvm::Function *impl)
|
|
: TheKind(Kind::DirectImpl), TheImpl(impl) {}
|
|
|
|
Kind getKind() const { return TheKind; }
|
|
|
|
Offset getOffset() const {
|
|
assert(getKind() == Kind::Offset);
|
|
return TheOffset;
|
|
}
|
|
llvm::Function *getDirectImpl() const {
|
|
assert(getKind() == Kind::DirectImpl);
|
|
return TheImpl;
|
|
}
|
|
};
|
|
|
|
private:
|
|
bool HasResilientSuperclass = false;
|
|
|
|
StoredOffset StartOfImmediateMembers;
|
|
|
|
StoredOffset MetadataSize;
|
|
StoredOffset MetadataAddressPoint;
|
|
|
|
StoredOffset InstanceSize;
|
|
StoredOffset InstanceAlignMask;
|
|
|
|
struct StoredMethodInfo {
|
|
MethodInfo::Kind TheKind;
|
|
union {
|
|
StoredOffset TheOffset;
|
|
llvm::Function *TheImpl;
|
|
};
|
|
StoredMethodInfo(StoredOffset offset) : TheKind(MethodInfo::Kind::Offset),
|
|
TheOffset(offset) {}
|
|
StoredMethodInfo(llvm::Function *impl)
|
|
: TheKind(MethodInfo::Kind::DirectImpl),
|
|
TheImpl(impl) {}
|
|
};
|
|
llvm::DenseMap<SILDeclRef, StoredMethodInfo> MethodInfos;
|
|
|
|
/// Field offsets for various fields.
|
|
llvm::DenseMap<VarDecl*, StoredOffset> FieldOffsets;
|
|
|
|
/// The start of the vtable.
|
|
StoredOffset VTableOffset;
|
|
|
|
/// The start of the field-offset vector.
|
|
StoredOffset FieldOffsetVector;
|
|
|
|
/// The number of members to add after superclass metadata.
|
|
unsigned NumImmediateMembers;
|
|
|
|
const StoredMethodInfo &getStoredMethodInfo(SILDeclRef method) const {
|
|
auto it = MethodInfos.find(method);
|
|
assert(it != MethodInfos.end());
|
|
return it->second;
|
|
}
|
|
|
|
const StoredOffset &getStoredFieldOffset(VarDecl *field) const {
|
|
auto it = FieldOffsets.find(field);
|
|
assert(it != FieldOffsets.end());
|
|
return it->second;
|
|
}
|
|
|
|
friend class IRGenModule;
|
|
ClassMetadataLayout(IRGenModule &IGM, ClassDecl *theClass);
|
|
|
|
public:
|
|
ClassDecl *getDecl() const {
|
|
return cast<ClassDecl>(Nominal);
|
|
}
|
|
|
|
bool hasResilientSuperclass() const {
|
|
return HasResilientSuperclass;
|
|
}
|
|
|
|
constexpr static bool areImmediateMembersNegative() {
|
|
return false;
|
|
}
|
|
|
|
Size getMetadataSizeOffset() const;
|
|
|
|
Size getMetadataAddressPointOffset() const;
|
|
|
|
Size getInstanceSizeOffset() const;
|
|
|
|
Size getInstanceAlignMaskOffset() const;
|
|
|
|
MethodInfo getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const;
|
|
|
|
const StoredMethodInfo *
|
|
getStoredMethodInfoIfPresent(SILDeclRef method) const {
|
|
auto it = MethodInfos.find(method);
|
|
if (it != MethodInfos.end())
|
|
return &it->second;
|
|
return nullptr;
|
|
}
|
|
|
|
Offset getFieldOffset(IRGenFunction &IGF, VarDecl *field) const;
|
|
|
|
/// Assuming that the given field offset is at a static offset in
|
|
/// the metadata, return that static offset.
|
|
///
|
|
/// DEPRECATED: callers should be updated to handle this in a
|
|
/// more arbitrary fashion.
|
|
Size getStaticFieldOffset(VarDecl *field) const;
|
|
|
|
/// Should only be used when emitting the nominal type descriptor.
|
|
Size getRelativeGenericRequirementsOffset() const;
|
|
|
|
Size getStaticFieldOffsetVectorOffset() const;
|
|
Size getRelativeFieldOffsetVectorOffset() const;
|
|
|
|
Size getStaticVTableOffset() const;
|
|
Size getRelativeVTableOffset() const;
|
|
|
|
Offset getFieldOffsetVectorOffset(IRGenFunction &IGF) const;
|
|
|
|
/// If the start of the immediate members is statically known, this
|
|
/// method will return it. Otherwise, it will assert.
|
|
Size getStartOfImmediateMembers() const {
|
|
return StartOfImmediateMembers.getStaticOffset();
|
|
}
|
|
|
|
/// The number of members to add after superclass metadata. The size of
|
|
/// this metadata is the superclass size plus the number of immediate
|
|
/// members in the class itself.
|
|
unsigned getNumImmediateMembers() const {
|
|
return NumImmediateMembers;
|
|
}
|
|
|
|
static bool classof(const MetadataLayout *layout) {
|
|
return layout->getKind() == Kind::Class;
|
|
}
|
|
};
|
|
|
|
/// Layout for enum type metadata.
|
|
class EnumMetadataLayout : public NominalMetadataLayout {
|
|
/// The offset of the payload size field, if there is one.
|
|
StoredOffset PayloadSizeOffset;
|
|
StoredOffset TrailingFlagsOffset;
|
|
|
|
// TODO: presumably it would be useful to store *something* here
|
|
// for resilience.
|
|
|
|
friend class IRGenModule;
|
|
EnumMetadataLayout(IRGenModule &IGM, EnumDecl *theEnum);
|
|
|
|
public:
|
|
EnumDecl *getDecl() const {
|
|
return cast<EnumDecl>(Nominal);
|
|
}
|
|
|
|
bool hasPayloadSizeOffset() const {
|
|
return PayloadSizeOffset.isValid();
|
|
}
|
|
|
|
Offset getPayloadSizeOffset() const;
|
|
Offset getTrailingFlagsOffset() const;
|
|
|
|
static bool classof(const MetadataLayout *layout) {
|
|
return layout->getKind() == Kind::Enum;
|
|
}
|
|
};
|
|
|
|
/// Layout for struct type metadata.
|
|
class StructMetadataLayout : public NominalMetadataLayout {
|
|
llvm::DenseMap<VarDecl*, StoredOffset> FieldOffsets;
|
|
StoredOffset TrailingFlagsOffset;
|
|
|
|
/// The start of the field-offset vector.
|
|
StoredOffset FieldOffsetVector;
|
|
|
|
const StoredOffset &getStoredFieldOffset(VarDecl *field) const {
|
|
auto it = FieldOffsets.find(field);
|
|
assert(it != FieldOffsets.end());
|
|
return it->second;
|
|
}
|
|
|
|
friend class IRGenModule;
|
|
StructMetadataLayout(IRGenModule &IGM, StructDecl *theStruct);
|
|
|
|
public:
|
|
StructDecl *getDecl() const {
|
|
return cast<StructDecl>(Nominal);
|
|
}
|
|
|
|
Offset getFieldOffset(IRGenFunction &IGF, VarDecl *field) const;
|
|
|
|
/// Assuming that the given field offset is at a static offset in
|
|
/// the metadata, return that static offset.
|
|
///
|
|
/// DEPRECATED: callers should be updated to handle this in a
|
|
/// more arbitrary fashion.
|
|
Size getStaticFieldOffset(VarDecl *field) const;
|
|
|
|
Offset getFieldOffsetVectorOffset() const;
|
|
Offset getTrailingFlagsOffset() const;
|
|
|
|
static bool classof(const MetadataLayout *layout) {
|
|
return layout->getKind() == Kind::Struct;
|
|
}
|
|
};
|
|
|
|
/// Layout for foreign class type metadata.
|
|
class ForeignClassMetadataLayout : public MetadataLayout {
|
|
ClassDecl *Class;
|
|
StoredOffset SuperClassOffset;
|
|
|
|
friend class IRGenModule;
|
|
ForeignClassMetadataLayout(IRGenModule &IGM, ClassDecl *theClass);
|
|
|
|
public:
|
|
StoredOffset getSuperClassOffset() const { return SuperClassOffset; }
|
|
|
|
static bool classof(const MetadataLayout *layout) {
|
|
return layout->getKind() == Kind::ForeignClass;
|
|
}
|
|
};
|
|
|
|
/// Emit the address of the field-offset slot in the given class metadata.
|
|
Address emitAddressOfClassFieldOffset(IRGenFunction &IGF,
|
|
llvm::Value *metadata,
|
|
ClassDecl *theClass,
|
|
VarDecl *field);
|
|
|
|
/// Get the offset to a field offset in the class type metadata.
|
|
///
|
|
/// DEPRECATED: callers should be updated to handle this in a more
|
|
/// arbitrary fashion.
|
|
Size getClassFieldOffsetOffset(IRGenModule &IGM,
|
|
ClassDecl *theClass,
|
|
VarDecl *field);
|
|
|
|
/// Emit the address of the field-offset vector in the given class or struct
|
|
/// metadata.
|
|
Address emitAddressOfFieldOffsetVector(IRGenFunction &IGF,
|
|
llvm::Value *metadata,
|
|
NominalTypeDecl *theDecl);
|
|
|
|
/// Given a reference to class type metadata of the given type,
|
|
/// decide the offset to the given field. This assumes that the
|
|
/// offset is stored in the metadata, i.e. its offset is potentially
|
|
/// dependent on generic arguments. The result is a ptrdiff_t.
|
|
llvm::Value *emitClassFieldOffset(IRGenFunction &IGF,
|
|
ClassDecl *theClass,
|
|
VarDecl *field,
|
|
llvm::Value *metadata);
|
|
|
|
/// Given a class metadata pointer, emit the address of its superclass field.
|
|
Address emitAddressOfSuperclassRefInClassMetadata(IRGenFunction &IGF,
|
|
llvm::Value *metadata);
|
|
|
|
Size getStaticTupleElementOffset(IRGenModule &IGM,
|
|
SILType tupleType,
|
|
unsigned eltIdx);
|
|
|
|
} // end namespace irgen
|
|
} // end namespace swift
|
|
|
|
#endif
|