blob: 9c5db239bc7e165922b81e02dabbd5a51498feee [file] [log] [blame]
//===--- 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;
}
}
};
/// Layout for class type metadata.
class ClassMetadataLayout : public NominalMetadataLayout {
public:
class MethodInfo {
Offset TheOffset;
public:
MethodInfo(Offset offset)
: TheOffset(offset) {}
Offset getOffset() const { return TheOffset; }
};
private:
bool HasResilientSuperclass = false;
StoredOffset StartOfImmediateMembers;
StoredOffset MetadataSize;
StoredOffset MetadataAddressPoint;
StoredOffset InstanceSize;
StoredOffset InstanceAlignMask;
struct StoredMethodInfo {
StoredOffset TheOffset;
StoredMethodInfo(StoredOffset offset) : TheOffset(offset) {}
};
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;
/// Returns the start of the vtable in the class metadata.
Offset getVTableOffset(IRGenFunction &IGF) const;
/// Returns the size of the vtable, in words.
unsigned getVTableSize() const {
return MethodInfos.size();
}
MethodInfo getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const;
/// Assuming that the given method 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 getStaticMethodOffset(SILDeclRef method) const;
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;
// 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;
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;
/// 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;
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);
} // end namespace irgen
} // end namespace swift
#endif