Files
swift-mirror/lib/IRGen/MetadataLayout.cpp
Nate Chandler 2b50150e61 [metadata prespecialization] Zero trailing flags.
Previously, the trailing flags field of runtime instantiated generic
metadata for types which had prespecialization enabled were not zeroed.
Consequently, the field always contained garbage.  Often, metadata was
instantiated on new (and so, zeroed) pages, so the garbage happened to
be zero as is appropriate.  However, when the metadata was instantiated
on pages which had previously been dirtied, the garbage value would
sometimes indicate that the metadata was canonical statically
specialized.  When that occurred, swift_checkMetadataState would
incorrectly return a metadata state of complete, despite the fact that
the metadata might not in fact be complete.  As a result, the runtime
was trafficking in incomplete metadata as if it were complete, resulting
in various crashes that would arise from for example missing a witness
table or a value witness table.

Here the problem is corrected.  The trailing flags field of structs and
enums that have the field is set to 0.

For structs, this is accomplished by modifying the extra data pattern to
exist not only when there is fixed type info for the type but also when
the type is being prespecialized.  Specifically, the extra data for a
struct is, rather than an i32 array of field offsets, a struct
consisting of none, one, or both of the following: (1) the array of
field offsets, (2) the trailing flags.

Similarly, enums now have an extra data pattern which consists of none,
one, or both of the following: (1) the payload size, (2) the trailing
flags.  Enum metadata extra data setting was previously achieved by
customizing the metadata allocation function; that customization is now
eliminated, being replaced with the shared runtime code for copying
extra data into place, a modest code size savings.

rdar://problem/61465515
2020-04-23 18:18:40 -07:00

672 lines
22 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 "ForeignClassMetadataVisitor.h"
#include "TupleMetadataVisitor.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(GenericRequirement requirement,
ClassDecl *forClass) {
if (forClass == Target) {
Layout.NumImmediateMembers++;
}
super::addGenericWitnessTable(requirement, forClass);
}
void addGenericArgument(GenericRequirement requirement,
ClassDecl *forClass) {
if (forClass == Target) {
Layout.NumImmediateMembers++;
}
super::addGenericArgument(requirement, 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());
}
Size irgen::getStaticTupleElementOffset(IRGenModule &IGM,
SILType tupleType,
unsigned eltIdx) {
assert(tupleType.is<TupleType>() && "not a tuple type");
struct TupleElementOffsetScanner
: LayoutScanner<TupleElementOffsetScanner, TupleMetadataScanner> {
private:
using super = LayoutScanner;
// 8 seems a reasonable potential max number tuple elements to start with
llvm::SmallVector<Size, 8> Offsets;
public:
TupleElementOffsetScanner(IRGenModule &IGM, TupleType *const tupleType)
: super(IGM, tupleType) {}
void addElement(unsigned eltIdx, const TupleTypeElt &elt) {
Offsets.push_back(NextOffset);
super::addElement(eltIdx, elt);
}
Size getElementOffset(unsigned eltIdx) const {
return Offsets[eltIdx];
}
};
TupleElementOffsetScanner s(IGM, tupleType.getAs<TupleType>().getPointer());
s.layout();
return s.getElementOffset(eltIdx);
}
/*********************************** 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 addTrailingFlags() {
Layout.TrailingFlagsOffset = getNextOffset();
super::addTrailingFlags();
}
void layout() {
super::layout();
Layout.TheSize = getMetadataSize();
}
};
Scanner(IGM, decl, *this).layout();
}
Offset
EnumMetadataLayout::getPayloadSizeOffset() const {
assert(PayloadSizeOffset.isStatic());
return Offset(PayloadSizeOffset.getStaticOffset());
}
Offset EnumMetadataLayout::getTrailingFlagsOffset() const {
assert(TrailingFlagsOffset.isStatic());
return Offset(TrailingFlagsOffset.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 addTrailingFlags() {
Layout.TrailingFlagsOffset = getNextOffset();
super::addTrailingFlags();
}
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());
}
Offset
StructMetadataLayout::getTrailingFlagsOffset() const {
assert(TrailingFlagsOffset.isStatic());
return Offset(TrailingFlagsOffset.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();
}