blob: 1f8ec2a1adbeb8338ae2d9160463961dd6299a9f [file] [log] [blame]
//===--- MetadataLayout.cpp - Metadata construct layout -------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file includes code for laying out type metadata.
//
// It also implements certain low-level access routines for type metadata.
// These routines are generally declared in one of two different places:
//
// - Mid-level routines to extract data from metadata are declared in
// GenMeta.h. This file is a sort of sub-module of GenMeta.cpp.
//
// - Low-level routines to project the addresses of fields in metadata
// are declared in MetadataLayout.h.
//
//===----------------------------------------------------------------------===//
#include "MetadataLayout.h"
#include "GenMeta.h"
#include "ClassMetadataVisitor.h"
#include "EnumMetadataVisitor.h"
#include "IRGenFunction.h"
#include "StructMetadataVisitor.h"
#include "ForeignClassMetadataVisitor.h"
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/Optional.h"
using namespace swift;
using namespace irgen;
namespace {
template <class Impl, template <class> class Base>
class LayoutScanner : public Base<Impl> {
Optional<Size> AddressPoint;
protected:
Optional<Size> DynamicOffsetBase;
template <class... As>
LayoutScanner(As &&... args) : Base<Impl>(std::forward<As>(args)...) {}
public:
using StoredOffset = MetadataLayout::StoredOffset;
void noteAddressPoint() { AddressPoint = this->NextOffset; }
StoredOffset getNextOffset() const {
if (DynamicOffsetBase) {
return StoredOffset(this->NextOffset - *DynamicOffsetBase,
StoredOffset::Dynamic);
}
return StoredOffset(this->NextOffset - *AddressPoint,
StoredOffset::Static);
}
Size getAddressPoint() const {
return *AddressPoint;
}
MetadataSize getMetadataSize() const {
assert(AddressPoint.hasValue() && !AddressPoint->isInvalid()
&& "did not find address point?!");
assert(*AddressPoint < this->NextOffset
&& "address point is after end?!");
return {this->NextOffset, *AddressPoint};
}
};
}
ClassMetadataLayout &IRGenModule::getClassMetadataLayout(ClassDecl *decl) {
assert(!decl->isForeign() && "Use getForeignMetadataLayout()");
return cast<ClassMetadataLayout>(
getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}
ForeignClassMetadataLayout &IRGenModule::getForeignMetadataLayout(
ClassDecl *decl) {
assert(decl->isForeign() && "Use getMetadataLayout()");
return cast<ForeignClassMetadataLayout>(
getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}
EnumMetadataLayout &IRGenModule::getMetadataLayout(EnumDecl *decl) {
return cast<EnumMetadataLayout>(
getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}
StructMetadataLayout &IRGenModule::getMetadataLayout(StructDecl *decl) {
return cast<StructMetadataLayout>(
getMetadataLayout(static_cast<NominalTypeDecl*>(decl)));
}
NominalMetadataLayout &IRGenModule::getNominalMetadataLayout(
NominalTypeDecl *decl) {
return cast<NominalMetadataLayout>(getMetadataLayout(decl));
}
MetadataLayout &IRGenModule::getMetadataLayout(NominalTypeDecl *decl) {
auto &entry = MetadataLayouts[decl];
if (!entry) {
if (auto theClass = dyn_cast<ClassDecl>(decl)) {
if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType)
entry = new ForeignClassMetadataLayout(*this, theClass);
else
entry = new ClassMetadataLayout(*this, theClass);
} else if (auto theEnum = dyn_cast<EnumDecl>(decl)) {
entry = new EnumMetadataLayout(*this, theEnum);
} else if (auto theStruct = dyn_cast<StructDecl>(decl)) {
entry = new StructMetadataLayout(*this, theStruct);
} else {
llvm_unreachable("bad nominal type!");
}
}
return *cast<MetadataLayout>(entry);
}
void IRGenModule::destroyMetadataLayoutMap() {
for (auto &entry : MetadataLayouts) {
entry.second->destroy();
}
}
void MetadataLayout::destroy() const {
switch (getKind()) {
case Kind::Class:
delete cast<ClassMetadataLayout>(this);
return;
case Kind::Struct:
delete cast<StructMetadataLayout>(this);
return;
case Kind::Enum:
delete cast<EnumMetadataLayout>(this);
return;
case Kind::ForeignClass:
delete cast<ForeignClassMetadataLayout>(this);
return;
}
llvm_unreachable("bad kind");
}
/******************************* NOMINAL TYPES ********************************/
Offset NominalMetadataLayout::emitOffset(IRGenFunction &IGF,
StoredOffset offset) const {
assert(offset.isValid());
if (offset.isStatic())
return Offset(offset.getStaticOffset());
Address layoutAddr(
IGF.IGM.getAddrOfClassMetadataBounds(cast<ClassDecl>(getDecl()),
NotForDefinition),
IGF.IGM.getPointerAlignment());
auto offsetBaseAddr = IGF.Builder.CreateStructGEP(layoutAddr, 0, Size(0));
// FIXME: Should this be an invariant load?
llvm::Value *offsetVal = IGF.Builder.CreateLoad(offsetBaseAddr, "base");
auto relativeOffset = offset.getRelativeOffset().getValue();
if (relativeOffset != 0) {
offsetVal = IGF.Builder.CreateAdd(offsetVal,
llvm::ConstantInt::get(IGF.IGM.SizeTy,
relativeOffset));
}
return Offset(offsetVal);
}
Size
NominalMetadataLayout::getStaticGenericRequirementsOffset() const {
return GenericRequirements.getStaticOffset();
}
Offset
NominalMetadataLayout::getGenericRequirementsOffset(IRGenFunction &IGF) const {
return emitOffset(IGF, GenericRequirements);
}
static llvm::Value *emitLoadOfGenericRequirement(IRGenFunction &IGF,
llvm::Value *metadata,
NominalTypeDecl *decl,
unsigned reqtIndex,
llvm::Type *reqtTy) {
auto offset =
IGF.IGM.getNominalMetadataLayout(decl).getGenericRequirementsOffset(IGF);
offset = offset.offsetBy(IGF, Size(reqtIndex * IGF.IGM.getPointerSize()));
auto slot = IGF.emitAddressAtOffset(metadata, offset, reqtTy,
IGF.IGM.getPointerAlignment());
auto witness = IGF.emitInvariantLoad(slot);
return witness;
}
/// Given a reference to nominal type metadata of the given type,
/// derive a reference to the nth argument metadata. The type must
/// have generic arguments.
llvm::Value *irgen::emitArgumentMetadataRef(IRGenFunction &IGF,
NominalTypeDecl *decl,
const GenericTypeRequirements &reqts,
unsigned reqtIndex,
llvm::Value *metadata) {
assert(reqts.getRequirements()[reqtIndex].Protocol == nullptr);
return emitLoadOfGenericRequirement(IGF, metadata, decl, reqtIndex,
IGF.IGM.TypeMetadataPtrTy);
}
/// Given a reference to nominal type metadata of the given type,
/// derive a reference to a protocol witness table for the nth
/// argument metadata. The type must have generic arguments.
llvm::Value *irgen::emitArgumentWitnessTableRef(IRGenFunction &IGF,
NominalTypeDecl *decl,
const GenericTypeRequirements &reqts,
unsigned reqtIndex,
llvm::Value *metadata) {
assert(reqts.getRequirements()[reqtIndex].Protocol != nullptr);
return emitLoadOfGenericRequirement(IGF, metadata, decl, reqtIndex,
IGF.IGM.WitnessTablePtrTy);
}
Address irgen::emitAddressOfFieldOffsetVector(IRGenFunction &IGF,
llvm::Value *metadata,
NominalTypeDecl *decl) {
auto &layout = IGF.IGM.getMetadataLayout(decl);
auto offset = [&]() {
if (isa<ClassDecl>(decl)) {
return cast<ClassMetadataLayout>(layout)
.getFieldOffsetVectorOffset(IGF);
} else {
assert(isa<StructDecl>(decl));
return cast<StructMetadataLayout>(layout)
.getFieldOffsetVectorOffset();
}
}();
auto *elementSize = IGF.IGM.SizeTy;
if (isa<StructDecl>(decl))
elementSize = IGF.IGM.Int32Ty;
return IGF.emitAddressAtOffset(metadata, offset, elementSize,
IGF.IGM.getPointerAlignment());
}
/********************************** CLASSES ***********************************/
ClassMetadataLayout::ClassMetadataLayout(IRGenModule &IGM, ClassDecl *decl)
: NominalMetadataLayout(Kind::Class, decl), NumImmediateMembers(0) {
struct Scanner : LayoutScanner<Scanner, ClassMetadataScanner> {
using super = LayoutScanner;
ClassMetadataLayout &Layout;
Scanner(IRGenModule &IGM, ClassDecl *decl, ClassMetadataLayout &layout)
: super(IGM, decl), Layout(layout) {}
void noteResilientSuperclass() {
Layout.HasResilientSuperclass = true;
}
void noteStartOfImmediateMembers(ClassDecl *forClass) {
// If our superclass is resilient to us, or the class itself is resilient
// to us, we will access metadata members relative to a base offset.
if (forClass == Target) {
Layout.StartOfImmediateMembers = getNextOffset();
if (Layout.HasResilientSuperclass ||
IGM.hasResilientMetadata(forClass, ResilienceExpansion::Maximal)) {
assert(!DynamicOffsetBase);
DynamicOffsetBase = NextOffset;
}
}
}
void addClassSize() {
Layout.MetadataSize = getNextOffset();
super::addClassSize();
}
void addClassAddressPoint() {
Layout.MetadataAddressPoint = getNextOffset();
super::addClassAddressPoint();
}
void addInstanceSize() {
Layout.InstanceSize = getNextOffset();
super::addInstanceSize();
}
void addInstanceAlignMask() {
Layout.InstanceAlignMask = getNextOffset();
super::addInstanceAlignMask();
}
void noteStartOfGenericRequirements(ClassDecl *forClass) {
if (forClass == Target)
Layout.GenericRequirements = getNextOffset();
super::noteStartOfGenericRequirements(forClass);
}
void addGenericWitnessTable(ClassDecl *forClass) {
if (forClass == Target) {
Layout.NumImmediateMembers++;
}
super::addGenericWitnessTable(forClass);
}
void addGenericArgument(ClassDecl *forClass) {
if (forClass == Target) {
Layout.NumImmediateMembers++;
}
super::addGenericArgument(forClass);
}
void addMethod(SILDeclRef fn) {
if (fn.getDecl()->getDeclContext() == Target) {
Layout.NumImmediateMembers++;
Layout.MethodInfos.try_emplace(fn, getNextOffset());
}
super::addMethod(fn);
}
void noteStartOfFieldOffsets(ClassDecl *forClass) {
if (forClass == Target)
Layout.FieldOffsetVector = getNextOffset();
super::noteStartOfFieldOffsets(forClass);
}
void addFieldOffset(VarDecl *field) {
if (field->getDeclContext() == Target) {
Layout.NumImmediateMembers++;
Layout.FieldOffsets.try_emplace(field, getNextOffset());
}
super::addFieldOffset(field);
}
void addFieldOffsetPlaceholders(MissingMemberDecl *placeholder) {
if (placeholder->getDeclContext() == Target) {
Layout.NumImmediateMembers +=
placeholder->getNumberOfFieldOffsetVectorEntries();
}
super::addFieldOffsetPlaceholders(placeholder);
}
void addVTableEntries(ClassDecl *forClass) {
if (forClass == Target)
Layout.VTableOffset = getNextOffset();
super::addVTableEntries(forClass);
}
void layout() {
super::layout();
Layout.TheSize = getMetadataSize();
}
};
Scanner(IGM, decl, *this).layout();
}
Size ClassMetadataLayout::getMetadataSizeOffset() const {
assert(MetadataSize.isStatic());
return MetadataSize.getStaticOffset();
}
Size ClassMetadataLayout::getMetadataAddressPointOffset() const {
assert(MetadataAddressPoint.isStatic());
return MetadataAddressPoint.getStaticOffset();
}
Size ClassMetadataLayout::getInstanceSizeOffset() const {
assert(InstanceSize.isStatic());
return InstanceSize.getStaticOffset();
}
Size ClassMetadataLayout::getInstanceAlignMaskOffset() const {
assert(InstanceAlignMask.isStatic());
return InstanceAlignMask.getStaticOffset();
}
ClassMetadataLayout::MethodInfo
ClassMetadataLayout::getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const{
auto &stored = getStoredMethodInfo(method);
auto offset = emitOffset(IGF, stored.TheOffset);
return MethodInfo(offset);
}
Offset ClassMetadataLayout::getFieldOffset(IRGenFunction &IGF,
VarDecl *field) const {
return emitOffset(IGF, getStoredFieldOffset(field));
}
Size ClassMetadataLayout::getStaticFieldOffset(VarDecl *field) const {
auto &stored = getStoredFieldOffset(field);
assert(stored.isStatic() && "resilient class metadata layout unsupported!");
return stored.getStaticOffset();
}
Size
ClassMetadataLayout::getRelativeGenericRequirementsOffset() const {
return GenericRequirements.getRelativeOffset();
}
Size
ClassMetadataLayout::getStaticFieldOffsetVectorOffset() const {
return FieldOffsetVector.getStaticOffset();
}
Size
ClassMetadataLayout::getRelativeFieldOffsetVectorOffset() const {
return FieldOffsetVector.getRelativeOffset();
}
Size
ClassMetadataLayout::getStaticVTableOffset() const {
return VTableOffset.getStaticOffset();
}
Size
ClassMetadataLayout::getRelativeVTableOffset() const {
return VTableOffset.getRelativeOffset();
}
Offset
ClassMetadataLayout::getFieldOffsetVectorOffset(IRGenFunction &IGF) const {
return emitOffset(IGF, FieldOffsetVector);
}
Size irgen::getClassFieldOffsetOffset(IRGenModule &IGM, ClassDecl *theClass,
VarDecl *field) {
if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType)
return Size();
return IGM.getClassMetadataLayout(theClass).getStaticFieldOffset(field);
}
/// Given a reference to class metadata of the given type,
/// compute the field offset for a stored property.
/// The type must have dependent generic layout.
llvm::Value *irgen::emitClassFieldOffset(IRGenFunction &IGF,
ClassDecl *theClass,
VarDecl *field,
llvm::Value *metadata) {
auto slot = emitAddressOfClassFieldOffset(IGF, metadata, theClass, field);
return IGF.emitInvariantLoad(slot);
}
Address irgen::emitAddressOfClassFieldOffset(IRGenFunction &IGF,
llvm::Value *metadata,
ClassDecl *theClass,
VarDecl *field) {
auto offset =
IGF.IGM.getClassMetadataLayout(theClass).getFieldOffset(IGF, field);
auto slot = IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.SizeTy,
IGF.IGM.getPointerAlignment());
return slot;
}
Address irgen::emitAddressOfSuperclassRefInClassMetadata(IRGenFunction &IGF,
llvm::Value *metadata) {
// The superclass field in a class type is the first field past the isa.
unsigned index = 1;
Address addr(metadata, IGF.IGM.getPointerAlignment());
addr = IGF.Builder.CreateElementBitCast(addr, IGF.IGM.TypeMetadataPtrTy);
return IGF.Builder.CreateConstArrayGEP(addr, index, IGF.IGM.getPointerSize());
}
/*********************************** ENUMS ************************************/
EnumMetadataLayout::EnumMetadataLayout(IRGenModule &IGM, EnumDecl *decl)
: NominalMetadataLayout(Kind::Enum, decl) {
struct Scanner : LayoutScanner<Scanner, EnumMetadataScanner> {
using super = LayoutScanner;
EnumMetadataLayout &Layout;
Scanner(IRGenModule &IGM, EnumDecl *decl, EnumMetadataLayout &layout)
: super(IGM, decl), Layout(layout) {}
void noteStartOfTypeSpecificMembers() {
assert(getNextOffset().getStaticOffset() ==
IGM.getOffsetOfEnumTypeSpecificMetadataMembers());
}
void addPayloadSize() {
Layout.PayloadSizeOffset = getNextOffset();
super::addPayloadSize();
}
void noteStartOfGenericRequirements() {
Layout.GenericRequirements = getNextOffset();
super::noteStartOfGenericRequirements();
}
void layout() {
super::layout();
Layout.TheSize = getMetadataSize();
}
};
Scanner(IGM, decl, *this).layout();
}
Offset
EnumMetadataLayout::getPayloadSizeOffset() const {
assert(PayloadSizeOffset.isStatic());
return Offset(PayloadSizeOffset.getStaticOffset());
}
/********************************** STRUCTS ***********************************/
StructMetadataLayout::StructMetadataLayout(IRGenModule &IGM, StructDecl *decl)
: NominalMetadataLayout(Kind::Struct, decl) {
struct Scanner : LayoutScanner<Scanner, StructMetadataScanner> {
using super = LayoutScanner;
StructMetadataLayout &Layout;
Scanner(IRGenModule &IGM, StructDecl *decl, StructMetadataLayout &layout)
: super(IGM, decl), Layout(layout) {}
void noteStartOfTypeSpecificMembers() {
assert(getNextOffset().getStaticOffset() ==
IGM.getOffsetOfStructTypeSpecificMetadataMembers());
}
void noteStartOfGenericRequirements() {
Layout.GenericRequirements = getNextOffset();
super::noteStartOfGenericRequirements();
}
void noteStartOfFieldOffsets() {
Layout.FieldOffsetVector = getNextOffset();
super::noteStartOfFieldOffsets();
}
void addFieldOffset(VarDecl *field) {
Layout.FieldOffsets.try_emplace(field, getNextOffset());
super::addFieldOffset(field);
}
void noteEndOfFieldOffsets() {
super::noteEndOfFieldOffsets();
}
void layout() {
super::layout();
Layout.TheSize = getMetadataSize();
}
};
Scanner(IGM, decl, *this).layout();
}
Offset StructMetadataLayout::getFieldOffset(IRGenFunction &IGF,
VarDecl *field) const {
// TODO: implement resilient metadata layout
return Offset(getStaticFieldOffset(field));
}
Size StructMetadataLayout::getStaticFieldOffset(VarDecl *field) const {
auto &stored = getStoredFieldOffset(field);
assert(stored.isStatic() && "resilient struct metadata layout unsupported!");
return stored.getStaticOffset();
}
Offset
StructMetadataLayout::getFieldOffsetVectorOffset() const {
assert(FieldOffsetVector.isStatic());
return Offset(FieldOffsetVector.getStaticOffset());
}
/****************************** FOREIGN CLASSES *******************************/
ForeignClassMetadataLayout::ForeignClassMetadataLayout(IRGenModule &IGM,
ClassDecl *theClass)
: MetadataLayout(Kind::ForeignClass), Class(theClass) {
assert(theClass->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
"Not a foreign class");
struct Scanner : LayoutScanner<Scanner, ForeignClassMetadataScanner> {
using super = LayoutScanner;
ForeignClassMetadataLayout &Layout;
Scanner(IRGenModule &IGM, ClassDecl *decl,
ForeignClassMetadataLayout &layout)
: super(IGM, decl), Layout(layout) {}
void addSuperclass() {
Layout.SuperClassOffset = getNextOffset();
super::addSuperclass();
}
void layout() {
super::layout();
Layout.TheSize = getMetadataSize();
}
};
Scanner(IGM, theClass, *this).layout();
}