mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Instead of storing an llvm::Constant, store an offset relative to the class metadata base.
498 lines
16 KiB
C++
498 lines
16 KiB
C++
//===--- 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 "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::getMetadataLayout(ClassDecl *decl) {
|
|
return cast<ClassMetadataLayout>(
|
|
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::getMetadataLayout(NominalTypeDecl *decl) {
|
|
auto &entry = MetadataLayouts[decl];
|
|
if (!entry) {
|
|
if (auto theClass = dyn_cast<ClassDecl>(decl)) {
|
|
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<NominalMetadataLayout>(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;
|
|
}
|
|
llvm_unreachable("bad kind");
|
|
}
|
|
|
|
/******************************* NOMINAL TYPES ********************************/
|
|
|
|
Size
|
|
NominalMetadataLayout::getStaticGenericRequirementsOffset() const {
|
|
assert(GenericRequirements.isValid());
|
|
assert(GenericRequirements.isStatic() && "resilient metadata layout unsupported!");
|
|
return GenericRequirements.getStaticOffset();
|
|
}
|
|
|
|
Offset
|
|
NominalMetadataLayout::getGenericRequirementsOffset(IRGenFunction &IGF) const {
|
|
assert(GenericRequirements.isValid());
|
|
assert(GenericRequirements.isStatic() && "resilient metadata layout unsupported!");
|
|
return Offset(GenericRequirements.getStaticOffset());
|
|
}
|
|
|
|
static llvm::Value *emitLoadOfGenericRequirement(IRGenFunction &IGF,
|
|
llvm::Value *metadata,
|
|
NominalTypeDecl *decl,
|
|
unsigned reqtIndex,
|
|
llvm::Type *reqtTy) {
|
|
auto offset =
|
|
IGF.IGM.getMetadataLayout(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();
|
|
}
|
|
}();
|
|
|
|
return IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.SizeTy,
|
|
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() {}
|
|
|
|
void noteStartOfImmediateMembers(ClassDecl *theClass) {}
|
|
|
|
void addClassSize() {
|
|
Layout.MetadataSize = getNextOffset();
|
|
super::addClassSize();
|
|
}
|
|
|
|
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(CanType argType, ProtocolConformanceRef conf,
|
|
ClassDecl *forClass) {
|
|
if (forClass == Target) {
|
|
Layout.NumImmediateMembers++;
|
|
}
|
|
super::addGenericWitnessTable(argType, conf, forClass);
|
|
}
|
|
|
|
void addGenericArgument(CanType argType, ClassDecl *forClass) {
|
|
if (forClass == Target) {
|
|
Layout.NumImmediateMembers++;
|
|
}
|
|
super::addGenericArgument(argType, 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::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);
|
|
|
|
assert(stored.TheOffset.isStatic() &&
|
|
"resilient class metadata layout unsupported!");
|
|
auto offset = Offset(stored.TheOffset.getStaticOffset());
|
|
|
|
return MethodInfo(offset);
|
|
}
|
|
|
|
Size ClassMetadataLayout::getStaticMethodOffset(SILDeclRef method) const{
|
|
auto &stored = getStoredMethodInfo(method);
|
|
|
|
assert(stored.TheOffset.isStatic() &&
|
|
"resilient class metadata layout unsupported!");
|
|
return stored.TheOffset.getStaticOffset();
|
|
}
|
|
|
|
Size
|
|
ClassMetadataLayout::getStaticVTableOffset() const {
|
|
// TODO: if class is resilient, return the offset relative to the start
|
|
// of immediate class metadata
|
|
assert(VTableOffset.isStatic());
|
|
return VTableOffset.getStaticOffset();
|
|
}
|
|
|
|
Offset
|
|
ClassMetadataLayout::getVTableOffset(IRGenFunction &IGF) const {
|
|
// TODO: implement resilient metadata layout
|
|
assert(VTableOffset.isStatic());
|
|
return Offset(VTableOffset.getStaticOffset());
|
|
}
|
|
|
|
Offset ClassMetadataLayout::getFieldOffset(IRGenFunction &IGF,
|
|
VarDecl *field) const {
|
|
// TODO: implement resilient metadata layout
|
|
return Offset(getStaticFieldOffset(field));
|
|
}
|
|
Size ClassMetadataLayout::getStaticFieldOffset(VarDecl *field) const {
|
|
auto &stored = getStoredFieldOffset(field);
|
|
assert(stored.isStatic() && "resilient class metadata layout unsupported!");
|
|
return stored.getStaticOffset();
|
|
}
|
|
|
|
Size
|
|
ClassMetadataLayout::getStaticFieldOffsetVectorOffset() const {
|
|
// TODO: if class is resilient, return the offset relative to the start
|
|
// of immediate class metadata
|
|
assert(FieldOffsetVector.isStatic());
|
|
return FieldOffsetVector.getStaticOffset();
|
|
}
|
|
|
|
Offset
|
|
ClassMetadataLayout::getFieldOffsetVectorOffset(IRGenFunction &IGF) const {
|
|
// TODO: implement resilient metadata layout
|
|
assert(FieldOffsetVector.isStatic());
|
|
return Offset(FieldOffsetVector.getStaticOffset());
|
|
}
|
|
|
|
Size irgen::getClassFieldOffsetOffset(IRGenModule &IGM, ClassDecl *theClass,
|
|
VarDecl *field) {
|
|
return IGM.getMetadataLayout(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.getMetadataLayout(theClass).getFieldOffset(IGF, field);
|
|
auto slot = IGF.emitAddressAtOffset(metadata, offset, IGF.IGM.SizeTy,
|
|
IGF.IGM.getPointerAlignment());
|
|
return slot;
|
|
}
|
|
|
|
/*********************************** 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 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 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 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());
|
|
}
|