mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1930 lines
75 KiB
C++
1930 lines
75 KiB
C++
//===--- GenClass.cpp - Swift IR Generation For 'class' Types -------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements IR generation for class types.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "GenClass.h"
|
|
|
|
#include "swift/ABI/Class.h"
|
|
#include "swift/ABI/MetadataValues.h"
|
|
#include "swift/AST/AttrKind.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/IRGenOptions.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/PrettyStackTrace.h"
|
|
#include "swift/AST/TypeMemberVisitor.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SIL/SILType.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/IR/CallSite.h"
|
|
|
|
#include "Explosion.h"
|
|
#include "GenFunc.h"
|
|
#include "GenMeta.h"
|
|
#include "GenObjC.h"
|
|
#include "GenProto.h"
|
|
#include "GenType.h"
|
|
#include "IRGenDebugInfo.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenModule.h"
|
|
#include "GenHeap.h"
|
|
#include "HeapTypeInfo.h"
|
|
#include "Linking.h"
|
|
#include "MemberAccessStrategy.h"
|
|
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
static ClassDecl *getRootClass(ClassDecl *theClass) {
|
|
while (theClass->hasSuperclass()) {
|
|
theClass = theClass->getSuperclass()->getClassOrBoundGenericClass();
|
|
assert(theClass && "base type of class not a class?");
|
|
}
|
|
return theClass;
|
|
}
|
|
|
|
/// What reference counting mechanism does a class have?
|
|
ReferenceCounting irgen::getReferenceCountingForClass(IRGenModule &IGM,
|
|
ClassDecl *theClass) {
|
|
// If ObjC interop is disabled, we have a Swift refcount.
|
|
if (!IGM.ObjCInterop)
|
|
return ReferenceCounting::Native;
|
|
|
|
// NOTE: if you change this, change Type::usesNativeReferenceCounting.
|
|
// If the root class is implemented in swift, then we have a swift
|
|
// refcount; otherwise, we have an ObjC refcount.
|
|
if (hasKnownSwiftImplementation(IGM, getRootClass(theClass)))
|
|
return ReferenceCounting::Native;
|
|
|
|
return ReferenceCounting::ObjC;
|
|
}
|
|
|
|
/// What isa encoding mechanism does a type have?
|
|
IsaEncoding irgen::getIsaEncodingForType(IRGenModule &IGM,
|
|
CanType type) {
|
|
if (auto theClass = type->getClassOrBoundGenericClass()) {
|
|
// We can access the isas of pure Swift classes directly.
|
|
if (hasKnownSwiftImplementation(IGM, getRootClass(theClass)))
|
|
return IsaEncoding::Pointer;
|
|
// For ObjC or mixed classes, we need to use object_getClass.
|
|
return IsaEncoding::ObjC;
|
|
}
|
|
// Non-class heap objects should be pure Swift, so we can access their isas
|
|
// directly.
|
|
return IsaEncoding::Pointer;
|
|
}
|
|
|
|
namespace {
|
|
/// Layout information for class types.
|
|
class ClassTypeInfo : public HeapTypeInfo<ClassTypeInfo> {
|
|
ClassDecl *TheClass;
|
|
mutable StructLayout *Layout;
|
|
mutable ClassLayout FieldLayout;
|
|
|
|
/// Can we use swift reference-counting, or do we have to use
|
|
/// objc_retain/release?
|
|
const ReferenceCounting Refcount;
|
|
|
|
void generateLayout(IRGenModule &IGM) const;
|
|
|
|
public:
|
|
ClassTypeInfo(llvm::PointerType *irType, Size size,
|
|
SpareBitVector spareBits, Alignment align,
|
|
ClassDecl *D, ReferenceCounting refcount)
|
|
: HeapTypeInfo(irType, size, std::move(spareBits), align), TheClass(D),
|
|
Layout(nullptr), Refcount(refcount) {}
|
|
|
|
ReferenceCounting getReferenceCounting() const {
|
|
return Refcount;
|
|
}
|
|
|
|
~ClassTypeInfo() {
|
|
delete Layout;
|
|
}
|
|
|
|
ClassDecl *getClass() const { return TheClass; }
|
|
|
|
const StructLayout &getLayout(IRGenModule &IGM) const;
|
|
const struct ClassLayout &getClassLayout(IRGenModule &IGM) const;
|
|
|
|
Alignment getHeapAlignment(IRGenModule &IGM) const {
|
|
return getLayout(IGM).getAlignment();
|
|
}
|
|
ArrayRef<ElementLayout> getElements(IRGenModule &IGM) const {
|
|
return getLayout(IGM).getElements();
|
|
}
|
|
};
|
|
} // end anonymous namespace.
|
|
|
|
/// Return the lowered type for the class's 'self' type within its context.
|
|
static SILType getSelfType(ClassDecl *base) {
|
|
auto loweredTy = base->getDeclaredTypeInContext()->getCanonicalType();
|
|
return SILType::getPrimitiveObjectType(loweredTy);
|
|
}
|
|
|
|
/// Return the type info for the class's 'self' type within its context.
|
|
static const ClassTypeInfo &getSelfTypeInfo(IRGenModule &IGM, ClassDecl *base) {
|
|
return IGM.getTypeInfo(getSelfType(base)).as<ClassTypeInfo>();
|
|
}
|
|
|
|
namespace {
|
|
class ClassLayoutBuilder : public StructLayoutBuilder {
|
|
SmallVector<ElementLayout, 8> Elements;
|
|
SmallVector<VarDecl*, 8> AllStoredProperties;
|
|
SmallVector<FieldAccess, 8> AllFieldAccesses;
|
|
|
|
// The manner in which this class embeds the field offset vector of
|
|
// the superclass.
|
|
//
|
|
// - ConstantDirect - size and content of superclass metadata is known
|
|
// at compile time.
|
|
// - NonConstantDirect - size of superclass metadata is known, however
|
|
// some field offsets depend on the sizes of resilient types, or the
|
|
// size of an imported Objective-C base class.
|
|
// - ConstantIndirect - size of superclass metadata is known, however
|
|
// some field offsets depend on generic parameters, sizes of
|
|
// resilient types, or the size of an imported Objective-C base class.
|
|
// - NonConstantIndirect - size of superclass metadata is unknown,
|
|
// so all class metadata entries for members of this class must be
|
|
// accessed indirectly.
|
|
FieldAccess MetadataAccess = FieldAccess::ConstantDirect;
|
|
|
|
unsigned NumInherited = 0;
|
|
|
|
// Does the class metadata require dynamic initialization above and
|
|
// beyond what the runtime can automatically achieve?
|
|
//
|
|
// This is true if the class or any of its ancestors:
|
|
// - is generic,
|
|
// - is resilient,
|
|
// - has a parent type which isn't emittable as a constant,
|
|
// - or has a field with resilient layout.
|
|
bool ClassMetadataRequiresDynamicInitialization = false;
|
|
|
|
// Does the superclass have a fixed number of stored properties?
|
|
// If not, and the class has generally-dependent layout, we have to
|
|
// access stored properties through an indirect offset into the field
|
|
// offset vector.
|
|
bool ClassHasFixedFieldCount = true;
|
|
|
|
// Does the class have a fixed size up until the current point?
|
|
// If not, we have to access stored properties either ivar offset globals,
|
|
// or through the field offset vector, based on whether the layout has
|
|
// dependent layout.
|
|
bool ClassHasFixedSize = true;
|
|
|
|
// Does the class have identical layout under all generic substitutions?
|
|
// If not, we can have to access stored properties through the field
|
|
// offset vector in the instantiated type metadata.
|
|
bool ClassHasConcreteLayout = true;
|
|
|
|
public:
|
|
ClassLayoutBuilder(IRGenModule &IGM, ClassDecl *theClass)
|
|
: StructLayoutBuilder(IGM)
|
|
{
|
|
// Start by adding a heap header.
|
|
addHeapHeader();
|
|
|
|
// Next, add the fields for the given class.
|
|
addFieldsForClass(theClass, getSelfType(theClass));
|
|
|
|
// Add these fields to the builder.
|
|
addFields(Elements, LayoutStrategy::Universal);
|
|
}
|
|
|
|
/// Return the element layouts.
|
|
ArrayRef<ElementLayout> getElements() const {
|
|
return Elements;
|
|
}
|
|
|
|
ClassLayout getClassLayout() const {
|
|
ClassLayout fieldLayout;
|
|
auto allStoredProps = IGM.Context.AllocateCopy(AllStoredProperties);
|
|
auto inheritedStoredProps = allStoredProps.slice(0, NumInherited);
|
|
fieldLayout.AllStoredProperties = allStoredProps;
|
|
fieldLayout.InheritedStoredProperties = inheritedStoredProps;
|
|
fieldLayout.AllFieldAccesses = IGM.Context.AllocateCopy(AllFieldAccesses);
|
|
fieldLayout.MetadataAccess = MetadataAccess;
|
|
fieldLayout.MetadataRequiresDynamicInitialization =
|
|
ClassMetadataRequiresDynamicInitialization;
|
|
return fieldLayout;
|
|
}
|
|
|
|
private:
|
|
void addFieldsForClass(ClassDecl *theClass,
|
|
SILType classType) {
|
|
if (theClass->isGenericContext())
|
|
ClassMetadataRequiresDynamicInitialization = true;
|
|
|
|
if (!ClassMetadataRequiresDynamicInitialization) {
|
|
if (auto parentType =
|
|
theClass->getDeclContext()->getDeclaredTypeInContext()) {
|
|
if (!tryEmitConstantTypeMetadataRef(IGM,
|
|
parentType->getCanonicalType(),
|
|
SymbolReferenceKind::Absolute))
|
|
ClassMetadataRequiresDynamicInitialization = true;
|
|
}
|
|
}
|
|
|
|
if (theClass->hasSuperclass()) {
|
|
// TODO: apply substitutions when computing base-class layouts!
|
|
SILType superclassType = classType.getSuperclass(nullptr);
|
|
auto superclass = superclassType.getClassOrBoundGenericClass();
|
|
assert(superclass);
|
|
|
|
if (superclass->hasClangNode()) {
|
|
// As a special case, assume NSObject has a fixed layout.
|
|
if (superclass->getName() !=
|
|
IGM.Context.getSwiftId(KnownFoundationEntity::NSObject)) {
|
|
// If the superclass was imported from Objective-C, its size is
|
|
// not known at compile time. However, since the field offset
|
|
// vector only stores offsets of stored properties defined in
|
|
// Swift, we don't have to worry about indirect indexing of
|
|
// the field offset vector.
|
|
ClassHasFixedSize = false;
|
|
}
|
|
} else if (IGM.isResilient(superclass, ResilienceExpansion::Maximal)) {
|
|
// If the superclass is resilient, the number of stored properties
|
|
// is not known at compile time.
|
|
ClassMetadataRequiresDynamicInitialization = true;
|
|
|
|
ClassHasFixedFieldCount = false;
|
|
ClassHasFixedSize = false;
|
|
|
|
// If the superclass is in a generic context, conservatively
|
|
// assume the layout depends on generic parameters, since we
|
|
// can't look at stored properties.
|
|
if (superclassType.hasArchetype())
|
|
ClassHasConcreteLayout = false;
|
|
} else {
|
|
// Otherwise, we have total knowledge of the class and its
|
|
// fields, so walk them to compute the layout.
|
|
addFieldsForClass(superclass, superclassType);
|
|
// Count the fields we got from the superclass.
|
|
NumInherited = Elements.size();
|
|
}
|
|
}
|
|
|
|
// The final value is field access for the superclass of the class we're
|
|
// building.
|
|
MetadataAccess = getCurFieldAccess();
|
|
|
|
// Collect fields from this class and add them to the layout as a chunk.
|
|
addDirectFieldsFromClass(theClass, classType);
|
|
}
|
|
|
|
void addDirectFieldsFromClass(ClassDecl *theClass,
|
|
SILType classType) {
|
|
for (VarDecl *var : theClass->getStoredProperties()) {
|
|
SILType type = classType.getFieldType(var, IGM.getSILModule());
|
|
auto &eltType = IGM.getTypeInfo(type);
|
|
|
|
if (!eltType.isFixedSize()) {
|
|
ClassMetadataRequiresDynamicInitialization = true;
|
|
ClassHasFixedSize = false;
|
|
|
|
if (type.hasArchetype())
|
|
ClassHasConcreteLayout = false;
|
|
}
|
|
|
|
Elements.push_back(ElementLayout::getIncomplete(eltType));
|
|
AllStoredProperties.push_back(var);
|
|
AllFieldAccesses.push_back(getCurFieldAccess());
|
|
}
|
|
}
|
|
|
|
FieldAccess getCurFieldAccess() const {
|
|
if (ClassHasConcreteLayout) {
|
|
if (ClassHasFixedSize) {
|
|
// No generic parameter dependencies, fixed size. The field offset
|
|
// is known at compile time.
|
|
return FieldAccess::ConstantDirect;
|
|
} else {
|
|
// No generic parameter dependencies, but some stored properties
|
|
// have unknown size. The field offset is stored in a global constant
|
|
// and set up by the Objective-C or Swift runtime, depending on the
|
|
// class's heritage.
|
|
return FieldAccess::NonConstantDirect;
|
|
}
|
|
} else {
|
|
if (ClassHasFixedFieldCount) {
|
|
// Layout depends on generic parameters, but the number of fields
|
|
// is known. The field offset is loaded from a fixed offset in the
|
|
// field offset vector in type metadata.
|
|
return FieldAccess::ConstantIndirect;
|
|
} else {
|
|
// Layout depends on generic parameters, and the number of fields
|
|
// is not known at compile time either. The field index is loaded
|
|
// from a global variable set up by the runtime, and then this
|
|
// index is used to load the offset from the field offset vector.
|
|
return FieldAccess::NonConstantIndirect;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
void ClassTypeInfo::generateLayout(IRGenModule &IGM) const {
|
|
assert(!Layout && FieldLayout.AllStoredProperties.empty() &&
|
|
"already generated layout");
|
|
|
|
// Add the heap header.
|
|
ClassLayoutBuilder builder(IGM, getClass());
|
|
|
|
// Set the body of the class type.
|
|
auto classPtrTy = cast<llvm::PointerType>(getStorageType());
|
|
auto classTy = cast<llvm::StructType>(classPtrTy->getElementType());
|
|
builder.setAsBodyOfStruct(classTy);
|
|
|
|
// Record the layout.
|
|
Layout = new StructLayout(builder,
|
|
TheClass->getDeclaredTypeInContext()->getCanonicalType(),
|
|
classTy, builder.getElements());
|
|
FieldLayout = builder.getClassLayout();
|
|
}
|
|
|
|
const StructLayout &ClassTypeInfo::getLayout(IRGenModule &IGM) const {
|
|
// Return the cached layout if available.
|
|
if (Layout) return *Layout;
|
|
|
|
generateLayout(IGM);
|
|
return *Layout;
|
|
}
|
|
|
|
const ClassLayout &ClassTypeInfo::getClassLayout(IRGenModule &IGM) const {
|
|
// Return the cached layout if available.
|
|
if (Layout)
|
|
return FieldLayout;
|
|
|
|
generateLayout(IGM);
|
|
return FieldLayout;
|
|
}
|
|
|
|
/// Cast the base to i8*, apply the given inbounds offset (in bytes,
|
|
/// as a size_t), and cast to a pointer to the given type.
|
|
llvm::Value *IRGenFunction::emitByteOffsetGEP(llvm::Value *base,
|
|
llvm::Value *offset,
|
|
llvm::Type *objectType,
|
|
const llvm::Twine &name) {
|
|
assert(offset->getType() == IGM.SizeTy);
|
|
auto addr = Builder.CreateBitCast(base, IGM.Int8PtrTy);
|
|
addr = Builder.CreateInBoundsGEP(addr, offset);
|
|
return Builder.CreateBitCast(addr, objectType->getPointerTo(), name);
|
|
}
|
|
|
|
/// Cast the base to i8*, apply the given inbounds offset (in bytes,
|
|
/// as a size_t), and create an address in the given type.
|
|
Address IRGenFunction::emitByteOffsetGEP(llvm::Value *base,
|
|
llvm::Value *offset,
|
|
const TypeInfo &type,
|
|
const llvm::Twine &name) {
|
|
auto addr = emitByteOffsetGEP(base, offset, type.getStorageType(), name);
|
|
return type.getAddressForPointer(addr);
|
|
}
|
|
|
|
/// Emit a field l-value by applying the given offset to the given base.
|
|
static OwnedAddress emitAddressAtOffset(IRGenFunction &IGF,
|
|
SILType baseType,
|
|
llvm::Value *base,
|
|
llvm::Value *offset,
|
|
VarDecl *field) {
|
|
auto &fieldTI =
|
|
IGF.getTypeInfo(baseType.getFieldType(field, IGF.getSILModule()));
|
|
auto addr = IGF.emitByteOffsetGEP(base, offset, fieldTI,
|
|
base->getName() + "." + field->getName().str());
|
|
return OwnedAddress(addr, base);
|
|
}
|
|
|
|
OwnedAddress irgen::projectPhysicalClassMemberAddress(IRGenFunction &IGF,
|
|
llvm::Value *base,
|
|
SILType baseType,
|
|
SILType fieldType,
|
|
VarDecl *field) {
|
|
// If the field is empty, its address doesn't matter.
|
|
auto &fieldTI = IGF.getTypeInfo(fieldType);
|
|
if (fieldTI.isKnownEmpty(ResilienceExpansion::Maximal)) {
|
|
return OwnedAddress(fieldTI.getUndefAddress(), base);
|
|
}
|
|
|
|
auto &baseClassTI = IGF.getTypeInfo(baseType).as<ClassTypeInfo>();
|
|
ClassDecl *baseClass = baseType.getClassOrBoundGenericClass();
|
|
|
|
// TODO: Lay out the class based on the substituted baseType rather than
|
|
// the generic type. Doing this requires that we also handle
|
|
// specialized layout in ClassTypeInfo.
|
|
|
|
auto &classLayout = baseClassTI.getClassLayout(IGF.IGM);
|
|
unsigned fieldIndex = classLayout.getFieldIndex(field);
|
|
|
|
switch (classLayout.AllFieldAccesses[fieldIndex]) {
|
|
case FieldAccess::ConstantDirect: {
|
|
Address baseAddr(base, baseClassTI.getHeapAlignment(IGF.IGM));
|
|
auto &element = baseClassTI.getElements(IGF.IGM)[fieldIndex];
|
|
Address memberAddr = element.project(IGF, baseAddr, None);
|
|
// We may need to bitcast the address if the field is of a generic type.
|
|
if (memberAddr.getType()->getElementType() != fieldTI.getStorageType())
|
|
memberAddr = IGF.Builder.CreateBitCast(memberAddr,
|
|
fieldTI.getStorageType()->getPointerTo());
|
|
return OwnedAddress(memberAddr, base);
|
|
}
|
|
|
|
case FieldAccess::NonConstantDirect: {
|
|
Address offsetA = IGF.IGM.getAddrOfFieldOffset(field, /*indirect*/ false,
|
|
NotForDefinition);
|
|
auto offsetVar = cast<llvm::GlobalVariable>(offsetA.getAddress());
|
|
offsetVar->setConstant(false);
|
|
auto offset = IGF.Builder.CreateLoad(offsetA, "offset");
|
|
return emitAddressAtOffset(IGF, baseType, base, offset, field);
|
|
}
|
|
|
|
case FieldAccess::ConstantIndirect: {
|
|
auto metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType);
|
|
auto offset = emitClassFieldOffset(IGF, baseClass, field, metadata);
|
|
return emitAddressAtOffset(IGF, baseType, base, offset, field);
|
|
}
|
|
|
|
case FieldAccess::NonConstantIndirect: {
|
|
auto metadata = emitHeapMetadataRefForHeapObject(IGF, base, baseType);
|
|
Address indirectOffsetA =
|
|
IGF.IGM.getAddrOfFieldOffset(field, /*indirect*/ true,
|
|
NotForDefinition);
|
|
auto offsetVar = cast<llvm::GlobalVariable>(indirectOffsetA.getAddress());
|
|
offsetVar->setConstant(false);
|
|
auto indirectOffset =
|
|
IGF.Builder.CreateLoad(indirectOffsetA, "indirect-offset");
|
|
auto offsetA =
|
|
IGF.emitByteOffsetGEP(metadata, indirectOffset, IGF.IGM.SizeTy);
|
|
auto offset =
|
|
IGF.Builder.CreateLoad(Address(offsetA, IGF.IGM.getPointerAlignment()));
|
|
return emitAddressAtOffset(IGF, baseType, base, offset, field);
|
|
}
|
|
}
|
|
llvm_unreachable("bad field-access strategy");
|
|
}
|
|
|
|
MemberAccessStrategy
|
|
irgen::getPhysicalClassMemberAccessStrategy(IRGenModule &IGM,
|
|
SILType baseType, VarDecl *field) {
|
|
auto &baseClassTI = IGM.getTypeInfo(baseType).as<ClassTypeInfo>();
|
|
ClassDecl *baseClass = baseType.getClassOrBoundGenericClass();
|
|
|
|
auto &classLayout = baseClassTI.getClassLayout(IGM);
|
|
unsigned fieldIndex = classLayout.getFieldIndex(field);
|
|
|
|
switch (classLayout.AllFieldAccesses[fieldIndex]) {
|
|
case FieldAccess::ConstantDirect: {
|
|
auto &element = baseClassTI.getElements(IGM)[fieldIndex];
|
|
return MemberAccessStrategy::getDirectFixed(element.getByteOffset());
|
|
}
|
|
|
|
case FieldAccess::NonConstantDirect: {
|
|
std::string symbol =
|
|
LinkEntity::forFieldOffset(field, /*indirect*/ false).mangleAsString();
|
|
return MemberAccessStrategy::getDirectGlobal(std::move(symbol),
|
|
MemberAccessStrategy::OffsetKind::Bytes_Word);
|
|
}
|
|
|
|
case FieldAccess::ConstantIndirect: {
|
|
Size indirectOffset = getClassFieldOffset(IGM, baseClass, field);
|
|
return MemberAccessStrategy::getIndirectFixed(indirectOffset,
|
|
MemberAccessStrategy::OffsetKind::Bytes_Word);
|
|
}
|
|
|
|
case FieldAccess::NonConstantIndirect: {
|
|
std::string symbol =
|
|
LinkEntity::forFieldOffset(field, /*indirect*/ true).mangleAsString();
|
|
return MemberAccessStrategy::getIndirectGlobal(std::move(symbol),
|
|
MemberAccessStrategy::OffsetKind::Bytes_Word,
|
|
MemberAccessStrategy::OffsetKind::Bytes_Word);
|
|
}
|
|
}
|
|
llvm_unreachable("bad field-access strategy");
|
|
}
|
|
|
|
/// Emit an allocation of a class.
|
|
llvm::Value *irgen::emitClassAllocation(IRGenFunction &IGF, SILType selfType,
|
|
bool objc, int &StackAllocSize) {
|
|
auto &classTI = IGF.getTypeInfo(selfType).as<ClassTypeInfo>();
|
|
auto classType = selfType.getSwiftRValueType();
|
|
|
|
// If we need to use Objective-C allocation, do so.
|
|
// If the root class isn't known to use the Swift allocator, we need
|
|
// to call [self alloc].
|
|
if (objc) {
|
|
llvm::Value *metadata =
|
|
emitClassHeapMetadataRef(IGF, classType, MetadataValueType::ObjCClass,
|
|
/*allow uninitialized*/ true);
|
|
StackAllocSize = -1;
|
|
return emitObjCAllocObjectCall(IGF, metadata, selfType.getSwiftRValueType());
|
|
}
|
|
|
|
llvm::Value *metadata =
|
|
emitClassHeapMetadataRef(IGF, classType, MetadataValueType::TypeMetadata);
|
|
|
|
// FIXME: Long-term, we clearly need a specialized runtime entry point.
|
|
llvm::Value *size, *alignMask;
|
|
std::tie(size, alignMask)
|
|
= emitClassFragileInstanceSizeAndAlignMask(IGF,
|
|
selfType.getClassOrBoundGenericClass(),
|
|
metadata);
|
|
|
|
auto &layout = classTI.getLayout(IGF.IGM);
|
|
llvm::Type *destType = layout.getType()->getPointerTo();
|
|
llvm::Value *val = nullptr;
|
|
if (layout.isFixedLayout() &&
|
|
(int)layout.getSize().getValue() < StackAllocSize) {
|
|
// Allocate the object on the stack.
|
|
auto *Ty = layout.getType();
|
|
auto Alloca = IGF.createAlloca(Ty, layout.getAlignment(),
|
|
"reference.raw");
|
|
val = Alloca.getAddress();
|
|
assert(val->getType() == destType);
|
|
val = IGF.Builder.CreateBitCast(val, IGF.IGM.RefCountedPtrTy);
|
|
val = IGF.emitInitStackObjectCall(metadata, val, "reference.new");
|
|
StackAllocSize = layout.getSize().getValue();
|
|
} else {
|
|
// Allocate the object on the heap.
|
|
val = IGF.emitAllocObjectCall(metadata, size, alignMask, "reference.new");
|
|
StackAllocSize = -1;
|
|
}
|
|
return IGF.Builder.CreateBitCast(val, destType);
|
|
}
|
|
|
|
llvm::Value *irgen::emitClassAllocationDynamic(IRGenFunction &IGF,
|
|
llvm::Value *metadata,
|
|
SILType selfType,
|
|
bool objc) {
|
|
// If we need to use Objective-C allocation, do so.
|
|
if (objc) {
|
|
return emitObjCAllocObjectCall(IGF, metadata,
|
|
selfType.getSwiftRValueType());
|
|
}
|
|
|
|
// Otherwise, allocate using Swift's routines.
|
|
llvm::Value *size, *alignMask;
|
|
std::tie(size, alignMask)
|
|
= emitClassResilientInstanceSizeAndAlignMask(IGF,
|
|
selfType.getClassOrBoundGenericClass(),
|
|
metadata);
|
|
|
|
llvm::Value *val = IGF.emitAllocObjectCall(metadata, size, alignMask,
|
|
"reference.new");
|
|
auto &classTI = IGF.getTypeInfo(selfType).as<ClassTypeInfo>();
|
|
auto &layout = classTI.getLayout(IGF.IGM);
|
|
llvm::Type *destType = layout.getType()->getPointerTo();
|
|
return IGF.Builder.CreateBitCast(val, destType);
|
|
}
|
|
|
|
/// Look for the instance method:
|
|
/// func __getInstanceSizeAndAlignMask() -> (Int, Int)
|
|
/// and use it to populate 'size' and 'alignMask' if it's present.
|
|
static bool getInstanceSizeByMethod(IRGenFunction &IGF,
|
|
CanType selfType,
|
|
ClassDecl *selfClass,
|
|
llvm::Value *selfValue,
|
|
llvm::Value *&size,
|
|
llvm::Value *&alignMask) {
|
|
// Look for a single instance method with the magic name.
|
|
FuncDecl *fn; {
|
|
auto name = IGF.IGM.Context.getIdentifier("__getInstanceSizeAndAlignMask");
|
|
SmallVector<ValueDecl*, 4> results;
|
|
selfClass->lookupQualified(selfType, name, NL_KnownNonCascadingDependency,
|
|
nullptr, results);
|
|
if (results.size() != 1)
|
|
return false;
|
|
fn = dyn_cast<FuncDecl>(results[0]);
|
|
if (!fn)
|
|
return false;
|
|
}
|
|
|
|
// Check whether the SIL module defines it. (We need a type for it.)
|
|
SILDeclRef fnRef(fn, SILDeclRef::Kind::Func,
|
|
ResilienceExpansion::Minimal,
|
|
/*uncurryLevel*/ 1,
|
|
/*foreign*/ false);
|
|
SILFunction *silFn = IGF.getSILModule().lookUpFunction(fnRef);
|
|
if (!silFn)
|
|
return false;
|
|
|
|
// Check that it returns two size_t's and takes no other arguments.
|
|
auto fnType = silFn->getLoweredFunctionType();
|
|
if (fnType->getParameters().size() != 1)
|
|
return false;
|
|
if (fnType->getNumDirectResults() != 2 ||
|
|
fnType->getNumIndirectResults() != 0)
|
|
return false;
|
|
auto results = fnType->getDirectResults();
|
|
if (results[0].getConvention() != ResultConvention::Unowned ||
|
|
results[1].getConvention() != ResultConvention::Unowned)
|
|
return false;
|
|
llvm::Function *llvmFn =
|
|
IGF.IGM.getAddrOfSILFunction(silFn, NotForDefinition);
|
|
auto llvmFnTy = llvmFn->getFunctionType();
|
|
if (llvmFnTy->getNumParams() != 1) return false;
|
|
auto returnType = dyn_cast<llvm::StructType>(llvmFnTy->getReturnType());
|
|
if (!returnType ||
|
|
returnType->getNumElements() != 2 ||
|
|
returnType->getElementType(0) != IGF.IGM.SizeTy ||
|
|
returnType->getElementType(1) != IGF.IGM.SizeTy)
|
|
return false;
|
|
|
|
// Retain 'self' if necessary.
|
|
if (fnType->getParameters()[0].isConsumed()) {
|
|
IGF.emitNativeStrongRetain(selfValue);
|
|
}
|
|
|
|
// Adjust down to the defining subclass type if necessary.
|
|
selfValue = IGF.Builder.CreateBitCast(selfValue, llvmFnTy->getParamType(0));
|
|
|
|
// Emit a direct call.
|
|
auto result = IGF.Builder.CreateCall(llvmFn, selfValue);
|
|
result->setCallingConv(llvmFn->getCallingConv());
|
|
|
|
// Extract the size and alignment.
|
|
size = IGF.Builder.CreateExtractValue(result, 0, "size");
|
|
alignMask = IGF.Builder.CreateExtractValue(result, 1, "alignMask");
|
|
return true;
|
|
}
|
|
|
|
/// Get the instance size and alignment mask for the given class
|
|
/// instance.
|
|
static void getInstanceSizeAndAlignMask(IRGenFunction &IGF,
|
|
SILType selfType,
|
|
ClassDecl *selfClass,
|
|
llvm::Value *selfValue,
|
|
llvm::Value *&size,
|
|
llvm::Value *&alignMask) {
|
|
// Use the magic __getInstanceSizeAndAlignMask method if we can
|
|
// see a declaration of it
|
|
if (getInstanceSizeByMethod(IGF, selfType.getSwiftRValueType(),
|
|
selfClass, selfValue, size, alignMask))
|
|
return;
|
|
|
|
// Try to determine the size of the object we're deallocating.
|
|
auto &info = IGF.IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
|
|
auto &layout = info.getLayout(IGF.IGM);
|
|
|
|
// If it's fixed, emit the constant size and alignment mask.
|
|
if (layout.isFixedLayout()) {
|
|
size = layout.emitSize(IGF.IGM);
|
|
alignMask = layout.emitAlignMask(IGF.IGM);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, get them from the metadata.
|
|
llvm::Value *metadata =
|
|
emitHeapMetadataRefForHeapObject(IGF, selfValue, selfType);
|
|
std::tie(size, alignMask)
|
|
= emitClassFragileInstanceSizeAndAlignMask(IGF, selfClass, metadata);
|
|
}
|
|
|
|
void irgen::emitClassDeallocation(IRGenFunction &IGF, SILType selfType,
|
|
llvm::Value *selfValue) {
|
|
auto *theClass = selfType.getClassOrBoundGenericClass();
|
|
|
|
llvm::Value *size, *alignMask;
|
|
getInstanceSizeAndAlignMask(IGF, selfType, theClass, selfValue,
|
|
size, alignMask);
|
|
|
|
selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.RefCountedPtrTy);
|
|
emitDeallocateClassInstance(IGF, selfValue, size, alignMask);
|
|
}
|
|
|
|
void irgen::emitPartialClassDeallocation(IRGenFunction &IGF,
|
|
SILType selfType,
|
|
llvm::Value *selfValue,
|
|
llvm::Value *metadataValue) {
|
|
auto *theClass = selfType.getClassOrBoundGenericClass();
|
|
|
|
llvm::Value *size, *alignMask;
|
|
getInstanceSizeAndAlignMask(IGF, selfType, theClass, selfValue,
|
|
size, alignMask);
|
|
|
|
selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.RefCountedPtrTy);
|
|
emitDeallocatePartialClassInstance(IGF, selfValue, metadataValue,
|
|
size, alignMask);
|
|
}
|
|
|
|
llvm::Constant *irgen::tryEmitClassConstantFragileInstanceSize(
|
|
IRGenModule &IGM,
|
|
ClassDecl *Class) {
|
|
auto &classTI = getSelfTypeInfo(IGM, Class);
|
|
|
|
auto &layout = classTI.getLayout(IGM);
|
|
if (layout.isFixedLayout())
|
|
return layout.emitSize(IGM);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::Constant *irgen::tryEmitClassConstantFragileInstanceAlignMask(
|
|
IRGenModule &IGM,
|
|
ClassDecl *Class) {
|
|
auto &classTI = getSelfTypeInfo(IGM, Class);
|
|
|
|
auto &layout = classTI.getLayout(IGM);
|
|
if (layout.isFixedLayout())
|
|
return layout.emitAlignMask(IGM);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// emitClassDecl - Emit all the declarations associated with this class type.
|
|
void IRGenModule::emitClassDecl(ClassDecl *D) {
|
|
PrettyStackTraceDecl prettyStackTrace("emitting class metadata for", D);
|
|
|
|
auto &classTI = Types.getTypeInfo(D).as<ClassTypeInfo>();
|
|
|
|
// Emit the class metadata.
|
|
emitClassMetadata(*this, D,
|
|
classTI.getLayout(*this),
|
|
classTI.getClassLayout(*this));
|
|
emitNestedTypeDecls(D->getMembers());
|
|
addNominalTypeDecl(D);
|
|
}
|
|
|
|
namespace {
|
|
enum ForMetaClass_t : bool {
|
|
ForClass = false,
|
|
ForMetaClass = true
|
|
};
|
|
|
|
typedef std::pair<ClassDecl*, Module*> CategoryNameKey;
|
|
/// Used to provide unique names to ObjC categories generated by Swift
|
|
/// extensions. The first category for a class in a module gets the module's
|
|
/// name as its key, e.g., NSObject (MySwiftModule). Another extension of the
|
|
/// same class in the same module gets a category name with a number appended,
|
|
/// e.g., NSObject (MySwiftModule1).
|
|
llvm::DenseMap<CategoryNameKey, unsigned> CategoryCounts;
|
|
|
|
/// A class for building ObjC class data (in Objective-C terms, class_ro_t),
|
|
/// category data (category_t), or protocol data (protocol_t).
|
|
class ClassDataBuilder : public ClassMemberVisitor<ClassDataBuilder> {
|
|
IRGenModule &IGM;
|
|
PointerUnion<ClassDecl *, ProtocolDecl *> TheEntity;
|
|
ExtensionDecl *TheExtension;
|
|
const StructLayout *Layout;
|
|
const ClassLayout *FieldLayout;
|
|
|
|
ClassDecl *getClass() const {
|
|
return TheEntity.get<ClassDecl*>();
|
|
}
|
|
ProtocolDecl *getProtocol() const {
|
|
return TheEntity.get<ProtocolDecl*>();
|
|
}
|
|
|
|
bool isBuildingClass() const {
|
|
return TheEntity.is<ClassDecl*>() && !TheExtension;
|
|
}
|
|
bool isBuildingCategory() const {
|
|
return TheEntity.is<ClassDecl*>() && TheExtension;
|
|
}
|
|
bool isBuildingProtocol() const {
|
|
return TheEntity.is<ProtocolDecl*>();
|
|
}
|
|
|
|
bool HasNonTrivialDestructor = false;
|
|
bool HasNonTrivialConstructor = false;
|
|
llvm::SmallString<16> CategoryName;
|
|
SmallVector<llvm::Constant*, 8> Ivars;
|
|
SmallVector<llvm::Constant*, 16> InstanceMethods;
|
|
SmallVector<llvm::Constant*, 16> ClassMethods;
|
|
SmallVector<llvm::Constant*, 16> OptInstanceMethods;
|
|
SmallVector<llvm::Constant*, 16> OptClassMethods;
|
|
SmallVector<llvm::Constant*, 4> Protocols;
|
|
SmallVector<llvm::Constant*, 8> InstanceProperties;
|
|
SmallVector<llvm::Constant*, 8> ClassProperties;
|
|
SmallVector<llvm::Constant*, 8> InstanceMethodTypesExt;
|
|
SmallVector<llvm::Constant*, 8> ClassMethodTypesExt;
|
|
SmallVector<llvm::Constant*, 8> OptInstanceMethodTypesExt;
|
|
SmallVector<llvm::Constant*, 8> OptClassMethodTypesExt;
|
|
|
|
llvm::Constant *Name = nullptr;
|
|
/// Index of the first non-inherited field in the layout.
|
|
unsigned FirstFieldIndex;
|
|
unsigned NextFieldIndex;
|
|
public:
|
|
ClassDataBuilder(IRGenModule &IGM, ClassDecl *theClass,
|
|
const StructLayout &layout,
|
|
const ClassLayout &fieldLayout)
|
|
: IGM(IGM), TheEntity(theClass), TheExtension(nullptr),
|
|
Layout(&layout),
|
|
FieldLayout(&fieldLayout)
|
|
{
|
|
FirstFieldIndex = fieldLayout.InheritedStoredProperties.size();
|
|
NextFieldIndex = FirstFieldIndex;
|
|
|
|
visitConformances(theClass);
|
|
visitMembers(theClass);
|
|
|
|
if (Lowering::usesObjCAllocator(theClass)) {
|
|
addIVarInitializer();
|
|
addIVarDestroyer();
|
|
}
|
|
}
|
|
|
|
ClassDataBuilder(IRGenModule &IGM, ClassDecl *theClass,
|
|
ExtensionDecl *theExtension)
|
|
: IGM(IGM), TheEntity(theClass), TheExtension(theExtension),
|
|
Layout(nullptr), FieldLayout(nullptr)
|
|
{
|
|
FirstFieldIndex = -1;
|
|
NextFieldIndex = -1;
|
|
|
|
buildCategoryName(CategoryName);
|
|
|
|
visitConformances(theExtension);
|
|
|
|
for (Decl *member : TheExtension->getMembers())
|
|
visit(member);
|
|
}
|
|
|
|
ClassDataBuilder(IRGenModule &IGM, ProtocolDecl *theProtocol)
|
|
: IGM(IGM), TheEntity(theProtocol), TheExtension(nullptr)
|
|
{
|
|
// Gather protocol references for all of the explicitly-specified
|
|
// Objective-C protocol conformances.
|
|
// FIXME: We can't use visitConformances() because there are no
|
|
// conformances for protocols to protocols right now.
|
|
for (ProtocolDecl *p : theProtocol->getInheritedProtocols(nullptr)) {
|
|
if (!p->isObjC())
|
|
continue;
|
|
// Don't emit the magic AnyObject conformance.
|
|
if (p == IGM.Context.getProtocol(KnownProtocolKind::AnyObject))
|
|
continue;
|
|
Protocols.push_back(buildProtocolRef(p));
|
|
}
|
|
|
|
for (Decl *member : theProtocol->getMembers())
|
|
visit(member);
|
|
}
|
|
|
|
/// Gather protocol records for all of the explicitly-specified Objective-C
|
|
/// protocol conformances.
|
|
void visitConformances(DeclContext *dc) {
|
|
for (auto conformance : dc->getLocalConformances(
|
|
ConformanceLookupKind::OnlyExplicit,
|
|
nullptr, /*sorted=*/true)) {
|
|
ProtocolDecl *proto = conformance->getProtocol();
|
|
if (!proto->isObjC())
|
|
continue;
|
|
|
|
// Don't emit the magic AnyObject conformance.
|
|
if (auto known = proto->getKnownProtocolKind())
|
|
if (*known == KnownProtocolKind::AnyObject)
|
|
continue;
|
|
|
|
Protocols.push_back(buildProtocolRef(proto));
|
|
}
|
|
}
|
|
|
|
llvm::Constant *getMetaclassRefOrNull(ClassDecl *theClass) {
|
|
if (theClass->isGenericContext() && !theClass->hasClangNode()) {
|
|
return llvm::ConstantPointerNull::get(IGM.ObjCClassPtrTy);
|
|
} else {
|
|
return IGM.getAddrOfMetaclassObject(theClass, NotForDefinition);
|
|
}
|
|
}
|
|
|
|
void buildMetaclassStub() {
|
|
assert(Layout && "can't build a metaclass from a category");
|
|
// The isa is the metaclass pointer for the root class.
|
|
auto rootClass = getRootClassForMetaclass(IGM, TheEntity.get<ClassDecl *>());
|
|
auto rootPtr = getMetaclassRefOrNull(rootClass);
|
|
|
|
// The superclass of the metaclass is the metaclass of the
|
|
// superclass. Note that for metaclass stubs, we can always
|
|
// ignore parent contexts and generic arguments.
|
|
//
|
|
// If this class has no formal superclass, then its actual
|
|
// superclass is SwiftObject, i.e. the root class.
|
|
llvm::Constant *superPtr;
|
|
if (getClass()->hasSuperclass()) {
|
|
auto base = getClass()->getSuperclass()->getClassOrBoundGenericClass();
|
|
superPtr = getMetaclassRefOrNull(base);
|
|
} else {
|
|
superPtr = getMetaclassRefOrNull(
|
|
IGM.getObjCRuntimeBaseForSwiftRootClass(getClass()));
|
|
}
|
|
|
|
auto dataPtr = emitROData(ForMetaClass);
|
|
dataPtr = llvm::ConstantExpr::getPtrToInt(dataPtr, IGM.IntPtrTy);
|
|
|
|
llvm::Constant *fields[] = {
|
|
rootPtr,
|
|
superPtr,
|
|
IGM.getObjCEmptyCachePtr(),
|
|
IGM.getObjCEmptyVTablePtr(),
|
|
dataPtr
|
|
};
|
|
auto init = llvm::ConstantStruct::get(IGM.ObjCClassStructTy,
|
|
makeArrayRef(fields));
|
|
auto metaclass =
|
|
cast<llvm::GlobalVariable>(
|
|
IGM.getAddrOfMetaclassObject(getClass(), ForDefinition));
|
|
metaclass->setInitializer(init);
|
|
}
|
|
|
|
private:
|
|
void buildCategoryName(SmallVectorImpl<char> &s) {
|
|
llvm::raw_svector_ostream os(s);
|
|
// Find the module the extension is declared in.
|
|
Module *TheModule = TheExtension->getParentModule();
|
|
|
|
os << TheModule->getName();
|
|
|
|
unsigned categoryCount = CategoryCounts[{getClass(), TheModule}]++;
|
|
if (categoryCount > 0)
|
|
os << categoryCount;
|
|
}
|
|
|
|
public:
|
|
llvm::Constant *emitCategory() {
|
|
assert(TheExtension && "can't emit category data for a class");
|
|
llvm::Constant *fields[8];
|
|
// struct category_t {
|
|
// char const *name;
|
|
fields[0] = IGM.getAddrOfGlobalString(CategoryName);
|
|
// const class_t *theClass;
|
|
if (getClass()->hasClangNode())
|
|
fields[1] = IGM.getAddrOfObjCClass(getClass(), NotForDefinition);
|
|
else {
|
|
auto type = getSelfType(getClass()).getSwiftRValueType();
|
|
llvm::Constant *metadata =
|
|
tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true);
|
|
assert(metadata &&
|
|
"extended objc class doesn't have constant metadata?");
|
|
fields[1] = metadata;
|
|
}
|
|
// const method_list_t *instanceMethods;
|
|
fields[2] = buildInstanceMethodList();
|
|
// const method_list_t *classMethods;
|
|
fields[3] = buildClassMethodList();
|
|
// const protocol_list_t *baseProtocols;
|
|
fields[4] = buildProtocolList();
|
|
// const property_list_t *properties;
|
|
fields[5] = buildPropertyList(ForClass);
|
|
// const property_list_t *classProperties;
|
|
fields[6] = buildPropertyList(ForMetaClass);
|
|
// uint32_t size;
|
|
auto sizeofPointer = IGM.getPointerSize().getValue();
|
|
unsigned size = sizeofPointer * llvm::array_lengthof(fields);
|
|
// Adjust for 'size', which is always 32 bits rather than pointer-sized.
|
|
// FIXME: Clang does this by using non-ad-hoc types for ObjC runtime
|
|
// structures.
|
|
size -= (sizeofPointer - 4) * 1;
|
|
fields[7] = llvm::ConstantInt::get(IGM.Int32Ty, size);
|
|
// };
|
|
|
|
return buildGlobalVariable(fields, "_CATEGORY_");
|
|
}
|
|
|
|
llvm::Constant *emitProtocol() {
|
|
llvm::Constant *fields[13];
|
|
llvm::SmallString<64> nameBuffer;
|
|
|
|
assert(isBuildingProtocol() && "not emitting a protocol");
|
|
|
|
// struct protocol_t {
|
|
// Class super;
|
|
fields[0] = null();
|
|
// char const *name;
|
|
fields[1] = IGM.getAddrOfGlobalString(getEntityName(nameBuffer));
|
|
// const protocol_list_t *baseProtocols;
|
|
fields[2] = buildProtocolList();
|
|
// const method_list_t *requiredInstanceMethods;
|
|
fields[3] = buildInstanceMethodList();
|
|
// const method_list_t *requiredClassMethods;
|
|
fields[4] = buildClassMethodList();
|
|
// const method_list_t *optionalInstanceMethods;
|
|
fields[5] = buildOptInstanceMethodList();
|
|
// const method_list_t *optionalClassMethods;
|
|
fields[6] = buildOptClassMethodList();
|
|
// const property_list_t *properties;
|
|
fields[7] = buildPropertyList(ForClass);
|
|
|
|
// uint32_t size;
|
|
auto sizeofPointer = IGM.getPointerSize().getValue();
|
|
unsigned size = sizeofPointer * llvm::array_lengthof(fields);
|
|
// Adjust for 'size' and 'flags', which are always 32 bits rather than
|
|
// pointer-sized.
|
|
// FIXME: Clang does this by using non-ad-hoc types for ObjC runtime
|
|
// structures.
|
|
size -= (sizeofPointer - 4) * 2;
|
|
fields[8] = llvm::ConstantInt::get(IGM.Int32Ty, size);
|
|
|
|
// uint32_t flags;
|
|
auto flags = ProtocolDescriptorFlags()
|
|
.withSwift(!getProtocol()->hasClangNode())
|
|
.withClassConstraint(ProtocolClassConstraint::Class)
|
|
.withDispatchStrategy(ProtocolDispatchStrategy::ObjC)
|
|
.withSpecialProtocol(getSpecialProtocolID(getProtocol()));
|
|
|
|
fields[9] = llvm::ConstantInt::get(IGM.Int32Ty, flags.getIntValue());
|
|
|
|
// const char ** extendedMethodTypes;
|
|
fields[10] = buildOptExtendedMethodTypes();
|
|
// const char *demangledName;
|
|
fields[11] = null();
|
|
// const property_list_t *classProperties;
|
|
fields[12] = buildPropertyList(ForMetaClass);
|
|
// };
|
|
|
|
return buildGlobalVariable(fields, "_PROTOCOL_");
|
|
}
|
|
|
|
llvm::Constant *emitRODataFields(ForMetaClass_t forMeta) {
|
|
assert(Layout && "can't emit rodata for a category");
|
|
SmallVector<llvm::Constant*, 11> fields;
|
|
// struct _class_ro_t {
|
|
// uint32_t flags;
|
|
fields.push_back(buildFlags(forMeta));
|
|
|
|
// uint32_t instanceStart;
|
|
// uint32_t instanceSize;
|
|
// The runtime requires that the ivar offsets be initialized to
|
|
// a valid layout of the ivars of this class, bounded by these
|
|
// two values. If the instanceSize of the superclass equals the
|
|
// stored instanceStart of the subclass, the ivar offsets
|
|
// will not be changed.
|
|
Size instanceStart;
|
|
Size instanceSize;
|
|
if (forMeta) {
|
|
// sizeof(struct class_t)
|
|
instanceSize = Size(5 * IGM.getPointerSize().getValue());
|
|
// historical nonsense
|
|
instanceStart = instanceSize;
|
|
} else {
|
|
instanceSize = Layout->getSize();
|
|
if (Layout->getElements().empty()
|
|
|| Layout->getElements().size() == FirstFieldIndex) {
|
|
instanceStart = instanceSize;
|
|
} else if (Layout->getElement(FirstFieldIndex).getKind()
|
|
== ElementLayout::Kind::Fixed ||
|
|
Layout->getElement(FirstFieldIndex).getKind()
|
|
== ElementLayout::Kind::Empty) {
|
|
// FIXME: assumes layout is always sequential!
|
|
instanceStart = Layout->getElement(FirstFieldIndex).getByteOffset();
|
|
} else {
|
|
instanceStart = Size(0);
|
|
}
|
|
}
|
|
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty,
|
|
instanceStart.getValue()));
|
|
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty,
|
|
instanceSize.getValue()));
|
|
|
|
// uint32_t reserved; // only when building for 64bit targets
|
|
if (IGM.getPointerAlignment().getValue() > 4) {
|
|
assert(IGM.getPointerAlignment().getValue() == 8);
|
|
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty, 0));
|
|
}
|
|
|
|
// const uint8_t *ivarLayout;
|
|
// GC/ARC layout. TODO.
|
|
fields.push_back(null());
|
|
|
|
// const char *name;
|
|
// It is correct to use the same name for both class and metaclass.
|
|
fields.push_back(buildName());
|
|
|
|
// const method_list_t *baseMethods;
|
|
fields.push_back(forMeta ? buildClassMethodList()
|
|
: buildInstanceMethodList());
|
|
|
|
// const protocol_list_t *baseProtocols;
|
|
// Apparently, this list is the same in the class and the metaclass.
|
|
fields.push_back(buildProtocolList());
|
|
|
|
// const ivar_list_t *ivars;
|
|
fields.push_back(forMeta ? null() : buildIvarList());
|
|
|
|
// const uint8_t *weakIvarLayout;
|
|
// More GC/ARC layout. TODO.
|
|
fields.push_back(null());
|
|
|
|
// const property_list_t *baseProperties;
|
|
fields.push_back(buildPropertyList(forMeta));
|
|
|
|
// };
|
|
|
|
return llvm::ConstantStruct::getAnon(IGM.getLLVMContext(), fields);
|
|
}
|
|
|
|
llvm::Constant *emitROData(ForMetaClass_t forMeta) {
|
|
auto fields = emitRODataFields(forMeta);
|
|
|
|
auto dataSuffix = forMeta ? "_METACLASS_DATA_" : "_DATA_";
|
|
return buildGlobalVariable(fields, dataSuffix);
|
|
}
|
|
|
|
private:
|
|
llvm::Constant *buildFlags(ForMetaClass_t forMeta) {
|
|
ObjCClassFlags flags = ObjCClassFlags::CompiledByARC;
|
|
|
|
// Mark metaclasses as appropriate.
|
|
if (forMeta) {
|
|
flags |= ObjCClassFlags::Meta;
|
|
|
|
// Non-metaclasses need us to record things whether primitive
|
|
// construction/destructor is trivial.
|
|
} else if (HasNonTrivialDestructor || HasNonTrivialConstructor) {
|
|
flags |= ObjCClassFlags::HasCXXStructors;
|
|
if (!HasNonTrivialConstructor)
|
|
flags |= ObjCClassFlags::HasCXXDestructorOnly;
|
|
}
|
|
|
|
// FIXME: set ObjCClassFlags::Hidden when appropriate
|
|
return llvm::ConstantInt::get(IGM.Int32Ty, uint32_t(flags));
|
|
}
|
|
|
|
llvm::Constant *buildName() {
|
|
if (Name) return Name;
|
|
|
|
// If the class is generic, we'll instantiate its name at runtime.
|
|
if (getClass()->isGenericContext()) {
|
|
Name = llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
return Name;
|
|
}
|
|
|
|
llvm::SmallString<64> buffer;
|
|
Name = IGM.getAddrOfGlobalString(getClass()->getObjCRuntimeName(buffer));
|
|
return Name;
|
|
}
|
|
|
|
llvm::Constant *null() {
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
}
|
|
|
|
/*** Methods ***********************************************************/
|
|
|
|
public:
|
|
/// Methods need to be collected into the appropriate methods list.
|
|
void visitFuncDecl(FuncDecl *method) {
|
|
if (!isBuildingProtocol() &&
|
|
!requiresObjCMethodDescriptor(method)) return;
|
|
|
|
// getters and setters funcdecls will be handled by their parent
|
|
// var/subscript.
|
|
if (method->isAccessor()) return;
|
|
|
|
// Don't emit getters/setters for @NSManaged methods.
|
|
if (method->getAttrs().hasAttribute<NSManagedAttr>())
|
|
return;
|
|
|
|
llvm::Constant *entry = emitObjCMethodDescriptor(IGM, method);
|
|
// This pointer will be set if we need to store the extended method type
|
|
// encoding.
|
|
SmallVectorImpl<llvm::Constant *> *ExtMethodTypesList = nullptr;
|
|
if (!method->isStatic()) {
|
|
if (method->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
OptInstanceMethods.push_back(entry);
|
|
if (isBuildingProtocol())
|
|
ExtMethodTypesList = &OptInstanceMethodTypesExt;
|
|
}
|
|
else {
|
|
InstanceMethods.push_back(entry);
|
|
if (isBuildingProtocol())
|
|
ExtMethodTypesList = &InstanceMethodTypesExt;
|
|
}
|
|
} else {
|
|
if (method->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
OptClassMethods.push_back(entry);
|
|
if (isBuildingProtocol())
|
|
ExtMethodTypesList = &OptClassMethodTypesExt;
|
|
}
|
|
else {
|
|
ClassMethods.push_back(entry);
|
|
if (isBuildingProtocol())
|
|
ExtMethodTypesList = &ClassMethodTypesExt;
|
|
}
|
|
}
|
|
if (ExtMethodTypesList) {
|
|
ExtMethodTypesList->push_back(
|
|
getMethodTypeExtendedEncoding(IGM, method));
|
|
}
|
|
}
|
|
|
|
/// Constructors need to be collected into the appropriate methods list.
|
|
void visitConstructorDecl(ConstructorDecl *constructor) {
|
|
if (!isBuildingProtocol() &&
|
|
!requiresObjCMethodDescriptor(constructor)) return;
|
|
llvm::Constant *entry = emitObjCMethodDescriptor(IGM, constructor);
|
|
if (constructor->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
OptInstanceMethods.push_back(entry);
|
|
if (isBuildingProtocol())
|
|
OptInstanceMethodTypesExt.push_back(
|
|
getMethodTypeExtendedEncoding(IGM, constructor));
|
|
} else {
|
|
InstanceMethods.push_back(entry);
|
|
if (isBuildingProtocol())
|
|
InstanceMethodTypesExt.push_back(
|
|
getMethodTypeExtendedEncoding(IGM, constructor));
|
|
}
|
|
}
|
|
|
|
/// Determine whether the given destructor has an Objective-C
|
|
/// definition.
|
|
bool hasObjCDeallocDefinition(DestructorDecl *destructor) {
|
|
// If we have the destructor body, we know whether SILGen
|
|
// generated a -dealloc body.
|
|
if (auto braceStmt = destructor->getBody())
|
|
return braceStmt->getNumElements() != 0;
|
|
|
|
// We don't have a destructor body, so hunt for the SIL function
|
|
// for it.
|
|
SILDeclRef dtorRef(destructor, SILDeclRef::Kind::Deallocator,
|
|
ResilienceExpansion::Minimal,
|
|
SILDeclRef::ConstructAtNaturalUncurryLevel,
|
|
/*isForeign=*/true);
|
|
if (auto silFn = IGM.getSILModule().lookUpFunction(dtorRef))
|
|
return silFn->isDefinition();
|
|
|
|
// The Objective-C thunk was never even declared, so it is not defined.
|
|
return false;
|
|
}
|
|
|
|
/// Destructors need to be collected into the instance methods
|
|
/// list
|
|
void visitDestructorDecl(DestructorDecl *destructor) {
|
|
auto classDecl = cast<ClassDecl>(destructor->getDeclContext());
|
|
if (Lowering::usesObjCAllocator(classDecl) &&
|
|
hasObjCDeallocDefinition(destructor)) {
|
|
llvm::Constant *entry = emitObjCMethodDescriptor(IGM, destructor);
|
|
InstanceMethods.push_back(entry);
|
|
}
|
|
}
|
|
|
|
void addIVarInitializer() {
|
|
if (auto entry = emitObjCIVarInitDestroyDescriptor(IGM, getClass(),
|
|
false)) {
|
|
InstanceMethods.push_back(*entry);
|
|
|
|
HasNonTrivialConstructor = true;
|
|
}
|
|
}
|
|
|
|
void addIVarDestroyer() {
|
|
if (auto entry = emitObjCIVarInitDestroyDescriptor(IGM, getClass(),
|
|
true)) {
|
|
InstanceMethods.push_back(*entry);
|
|
|
|
HasNonTrivialDestructor = true;
|
|
}
|
|
}
|
|
|
|
private:
|
|
StringRef chooseNamePrefix(StringRef forClass,
|
|
StringRef forCategory,
|
|
StringRef forProtocol) {
|
|
if (isBuildingCategory())
|
|
return forCategory;
|
|
if (isBuildingClass())
|
|
return forClass;
|
|
if (isBuildingProtocol())
|
|
return forProtocol;
|
|
|
|
llvm_unreachable("not a class, category, or protocol?!");
|
|
}
|
|
|
|
llvm::Constant *buildClassMethodList() {
|
|
return buildMethodList(ClassMethods,
|
|
chooseNamePrefix("_CLASS_METHODS_",
|
|
"_CATEGORY_CLASS_METHODS_",
|
|
"_PROTOCOL_CLASS_METHODS_"));
|
|
}
|
|
|
|
llvm::Constant *buildInstanceMethodList() {
|
|
return buildMethodList(InstanceMethods,
|
|
chooseNamePrefix("_INSTANCE_METHODS_",
|
|
"_CATEGORY_INSTANCE_METHODS_",
|
|
"_PROTOCOL_INSTANCE_METHODS_"));
|
|
}
|
|
|
|
llvm::Constant *buildOptClassMethodList() {
|
|
return buildMethodList(OptClassMethods,
|
|
"_PROTOCOL_CLASS_METHODS_OPT_");
|
|
}
|
|
|
|
llvm::Constant *buildOptInstanceMethodList() {
|
|
return buildMethodList(OptInstanceMethods,
|
|
"_PROTOCOL_INSTANCE_METHODS_OPT_");
|
|
}
|
|
|
|
llvm::Constant *buildOptExtendedMethodTypes() {
|
|
SmallVector<llvm::Constant*, 16> AllMethodTypesExt;
|
|
assert(InstanceMethodTypesExt.size() == InstanceMethods.size()
|
|
&& "number of instance methods does not match extended types");
|
|
assert(ClassMethodTypesExt.size() == ClassMethods.size()
|
|
&& "number of class methods does not match extended types");
|
|
assert(OptInstanceMethodTypesExt.size() == OptInstanceMethods.size()
|
|
&& "number of optional instance methods does not match extended types");
|
|
assert(OptClassMethodTypesExt.size() == OptClassMethods.size()
|
|
&& "number of optional class methods does not match extended types");
|
|
AllMethodTypesExt.insert(AllMethodTypesExt.end(),
|
|
InstanceMethodTypesExt.begin(), InstanceMethodTypesExt.end());
|
|
AllMethodTypesExt.insert(AllMethodTypesExt.end(),
|
|
ClassMethodTypesExt.begin(), ClassMethodTypesExt.end());
|
|
AllMethodTypesExt.insert(AllMethodTypesExt.end(),
|
|
OptInstanceMethodTypesExt.begin(), OptInstanceMethodTypesExt.end());
|
|
AllMethodTypesExt.insert(AllMethodTypesExt.end(),
|
|
OptClassMethodTypesExt.begin(), OptClassMethodTypesExt.end());
|
|
return buildMethodList(AllMethodTypesExt,
|
|
"_PROTOCOL_METHOD_TYPES_");
|
|
}
|
|
|
|
/// struct method_list_t {
|
|
/// uint32_t entsize; // runtime uses low bits for its own purposes
|
|
/// uint32_t count;
|
|
/// method_t list[count];
|
|
/// };
|
|
///
|
|
/// This method does not return a value of a predictable type.
|
|
llvm::Constant *buildMethodList(ArrayRef<llvm::Constant*> methods,
|
|
StringRef name) {
|
|
return buildOptionalList(methods, 3 * IGM.getPointerSize(), name);
|
|
}
|
|
|
|
/*** Protocols *********************************************************/
|
|
|
|
/// typedef uintptr_t protocol_ref_t; // protocol_t*, but unremapped
|
|
llvm::Constant *buildProtocolRef(ProtocolDecl *protocol) {
|
|
assert(protocol->isObjC());
|
|
return IGM.getAddrOfObjCProtocolRecord(protocol, NotForDefinition);
|
|
}
|
|
|
|
/// struct protocol_list_t {
|
|
/// uintptr_t count;
|
|
/// protocol_ref_t[count];
|
|
/// };
|
|
///
|
|
/// This method does not return a value of a predictable type.
|
|
llvm::Constant *buildProtocolList() {
|
|
return buildOptionalList(Protocols, Size(0),
|
|
chooseNamePrefix("_PROTOCOLS_",
|
|
"_CATEGORY_PROTOCOLS_",
|
|
"_PROTOCOL_PROTOCOLS_"));
|
|
}
|
|
|
|
/*** Ivars *************************************************************/
|
|
|
|
public:
|
|
/// Variables might be stored or computed.
|
|
void visitVarDecl(VarDecl *var) {
|
|
if (var->hasStorage() && !var->isStatic())
|
|
visitStoredVar(var);
|
|
else
|
|
visitProperty(var);
|
|
}
|
|
|
|
private:
|
|
/// Ivars need to be collected in the ivars list, and they also
|
|
/// affect flags.
|
|
void visitStoredVar(VarDecl *var) {
|
|
// FIXME: how to handle ivar extensions in categories?
|
|
if (!Layout)
|
|
return;
|
|
|
|
// For now, we never try to emit specialized versions of the
|
|
// metadata statically, so compute the field layout using the
|
|
// originally-declared type.
|
|
SILType fieldType =
|
|
IGM.getLoweredType(IGM.getSILTypes().getAbstractionPattern(var),
|
|
var->getType());
|
|
Ivars.push_back(buildIvar(var, fieldType));
|
|
|
|
// Build property accessors for the ivar if necessary.
|
|
visitProperty(var);
|
|
}
|
|
|
|
/// struct ivar_t {
|
|
/// uintptr_t *offset;
|
|
/// const char *name;
|
|
/// const char *type;
|
|
/// uint32_t alignment; // actually the log2 of the alignment
|
|
/// uint32_t size;
|
|
/// };
|
|
llvm::Constant *buildIvar(VarDecl *ivar, SILType loweredType) {
|
|
assert(Layout && "can't build ivar for category");
|
|
// FIXME: this is not always the right thing to do!
|
|
//auto &elt = Layout->getElement(NextFieldIndex++);
|
|
auto &ivarTI = IGM.getTypeInfo(loweredType);
|
|
|
|
llvm::Constant *offsetPtr;
|
|
switch (FieldLayout->AllFieldAccesses[NextFieldIndex++]) {
|
|
case FieldAccess::ConstantDirect:
|
|
case FieldAccess::NonConstantDirect: {
|
|
// If the field offset is fixed relative to the start of the superclass,
|
|
// reference the global from the ivar metadata so that the Objective-C
|
|
// runtime will slide it down.
|
|
auto offsetAddr = IGM.getAddrOfFieldOffset(ivar, /*indirect*/ false,
|
|
NotForDefinition);
|
|
offsetPtr = cast<llvm::Constant>(offsetAddr.getAddress());
|
|
break;
|
|
}
|
|
case FieldAccess::ConstantIndirect:
|
|
case FieldAccess::NonConstantIndirect:
|
|
// Otherwise, swift_initClassMetadata_UniversalStrategy() will point
|
|
// the Objective-C runtime into the field offset vector of the
|
|
// instantiated metadata.
|
|
offsetPtr
|
|
= llvm::ConstantPointerNull::get(IGM.IntPtrTy->getPointerTo());
|
|
break;
|
|
}
|
|
|
|
// TODO: clang puts this in __TEXT,__objc_methname,cstring_literals
|
|
auto name = IGM.getAddrOfGlobalString(ivar->getName().str());
|
|
|
|
// TODO: clang puts this in __TEXT,__objc_methtype,cstring_literals
|
|
auto typeEncode = IGM.getAddrOfGlobalString("");
|
|
|
|
Size size;
|
|
Alignment alignment;
|
|
if (auto fixedTI = dyn_cast<FixedTypeInfo>(&ivarTI)) {
|
|
size = fixedTI->getFixedSize();
|
|
alignment = fixedTI->getFixedAlignment();
|
|
} else {
|
|
// FIXME: set something up to fill these in at runtime!
|
|
size = Size(0);
|
|
alignment = Alignment(1);
|
|
}
|
|
|
|
// If the size is larger than we can represent in 32-bits,
|
|
// complain about the unimplementable ivar.
|
|
if (uint32_t(size.getValue()) != size.getValue()) {
|
|
IGM.error(ivar->getLoc(),
|
|
"ivar size (" + Twine(size.getValue()) +
|
|
" bytes) overflows Objective-C ivar layout");
|
|
size = Size(0);
|
|
}
|
|
|
|
llvm::Constant *fields[] = {
|
|
offsetPtr,
|
|
name,
|
|
typeEncode,
|
|
llvm::ConstantInt::get(IGM.Int32Ty, alignment.log2()),
|
|
llvm::ConstantInt::get(IGM.Int32Ty, size.getValue()),
|
|
};
|
|
return llvm::ConstantStruct::getAnon(IGM.getLLVMContext(), fields);
|
|
}
|
|
|
|
/// struct ivar_list_t {
|
|
/// uint32_t entsize;
|
|
/// uint32_t count;
|
|
/// ivar_t list[count];
|
|
/// };
|
|
///
|
|
/// This method does not return a value of a predictable type.
|
|
llvm::Constant *buildIvarList() {
|
|
Size eltSize = 3 * IGM.getPointerSize() + Size(8);
|
|
return buildOptionalList(Ivars, eltSize, "_IVARS_");
|
|
}
|
|
|
|
/*** Properties ********************************************************/
|
|
|
|
/// Properties need to be collected in the properties list.
|
|
void visitProperty(VarDecl *var) {
|
|
if (requiresObjCPropertyDescriptor(IGM, var)) {
|
|
if (llvm::Constant *prop = buildProperty(var)) {
|
|
auto &properties =
|
|
(var->isStatic() ? ClassProperties : InstanceProperties);
|
|
properties.push_back(prop);
|
|
}
|
|
|
|
// Don't emit getter/setter descriptors for @NSManaged properties.
|
|
if (var->getAttrs().hasAttribute<NSManagedAttr>() ||
|
|
// Don't emit descriptors for properties without accessors.
|
|
var->getGetter() == nullptr)
|
|
return;
|
|
|
|
SmallVectorImpl<llvm::Constant *> *methods;
|
|
SmallVectorImpl<llvm::Constant *> *extMethodTypes = nullptr;
|
|
if (var->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
if (var->isStatic()) {
|
|
methods = &OptClassMethods;
|
|
if (isBuildingProtocol())
|
|
extMethodTypes = &OptClassMethodTypesExt;
|
|
} else {
|
|
methods = &OptInstanceMethods;
|
|
if (isBuildingProtocol())
|
|
extMethodTypes = &OptInstanceMethodTypesExt;
|
|
}
|
|
} else {
|
|
if (var->isStatic()) {
|
|
methods = &ClassMethods;
|
|
if (isBuildingProtocol())
|
|
extMethodTypes = &ClassMethodTypesExt;
|
|
} else {
|
|
methods = &InstanceMethods;
|
|
if (isBuildingProtocol())
|
|
extMethodTypes = &InstanceMethodTypesExt;
|
|
}
|
|
}
|
|
|
|
auto getter_setter = emitObjCPropertyMethodDescriptors(IGM, var);
|
|
methods->push_back(getter_setter.first);
|
|
|
|
if (getter_setter.second)
|
|
methods->push_back(getter_setter.second);
|
|
|
|
// Get the getter and setter extended encodings, if needed.
|
|
if (extMethodTypes) {
|
|
extMethodTypes->push_back(
|
|
getMethodTypeExtendedEncoding(IGM, var->getGetter()));
|
|
if (auto setter = var->getSetter()) {
|
|
assert(getter_setter.second && "no descriptor for setter?!");
|
|
extMethodTypes->push_back(
|
|
getMethodTypeExtendedEncoding(IGM, setter));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Build the property attribute string for a property decl.
|
|
void buildPropertyAttributes(VarDecl *prop, SmallVectorImpl<char> &out) {
|
|
llvm::raw_svector_ostream outs(out);
|
|
|
|
auto propTy = prop->getType()->getReferenceStorageReferent();
|
|
|
|
// Emit the type encoding for the property.
|
|
outs << 'T';
|
|
|
|
std::string typeEnc;
|
|
getObjCEncodingForPropertyType(IGM, prop, typeEnc);
|
|
outs << typeEnc;
|
|
|
|
// Emit other attributes.
|
|
|
|
// All Swift properties are (nonatomic).
|
|
outs << ",N";
|
|
|
|
// @NSManaged properties are @dynamic.
|
|
if (prop->getAttrs().hasAttribute<NSManagedAttr>())
|
|
outs << ",D";
|
|
|
|
auto isObject = propTy->hasRetainablePointerRepresentation();
|
|
auto hasObjectEncoding = typeEnc[0] == '@';
|
|
|
|
// Determine the assignment semantics.
|
|
// Get-only properties are (readonly).
|
|
if (!prop->isSettable(prop->getDeclContext()))
|
|
outs << ",R";
|
|
// Weak and Unowned properties are (weak).
|
|
else if (prop->getAttrs().hasAttribute<OwnershipAttr>())
|
|
outs << ",W";
|
|
// If the property is @NSCopying, or is bridged to a value class, the
|
|
// property is (copy).
|
|
else if (prop->getAttrs().hasAttribute<NSCopyingAttr>()
|
|
|| (hasObjectEncoding && !isObject))
|
|
outs << ",C";
|
|
// If it's of a managed object type, it is (retain).
|
|
else if (isObject)
|
|
outs << ",&";
|
|
// Otherwise, the property is of a value type, so it is
|
|
// the default (assign).
|
|
else
|
|
(void)0;
|
|
|
|
// If the property has storage, emit the ivar name last.
|
|
if (prop->hasStorage())
|
|
outs << ",V" << prop->getName();
|
|
}
|
|
|
|
/// struct property_t {
|
|
/// const char *name;
|
|
/// const char *attributes;
|
|
/// };
|
|
llvm::Constant *buildProperty(VarDecl *prop) {
|
|
llvm::SmallString<16> propertyAttributes;
|
|
buildPropertyAttributes(prop, propertyAttributes);
|
|
|
|
llvm::Constant *fields[] = {
|
|
IGM.getAddrOfGlobalString(prop->getObjCPropertyName().str()),
|
|
IGM.getAddrOfGlobalString(propertyAttributes)
|
|
};
|
|
return llvm::ConstantStruct::getAnon(IGM.getLLVMContext(), fields);
|
|
}
|
|
|
|
/// struct property_list_t {
|
|
/// uint32_t entsize;
|
|
/// uint32_t count;
|
|
/// property_t list[count];
|
|
/// };
|
|
///
|
|
/// This method does not return a value of a predictable type.
|
|
llvm::Constant *buildPropertyList(ForMetaClass_t classOrMeta) {
|
|
Size eltSize = 2 * IGM.getPointerSize();
|
|
StringRef namePrefix;
|
|
if (classOrMeta == ForClass) {
|
|
return buildOptionalList(InstanceProperties, eltSize,
|
|
chooseNamePrefix("_PROPERTIES_",
|
|
"_CATEGORY_PROPERTIES_",
|
|
"_PROTOCOL_PROPERTIES_"));
|
|
}
|
|
return buildOptionalList(ClassProperties, eltSize,
|
|
chooseNamePrefix("_CLASS_PROPERTIES_",
|
|
"_CATEGORY_CLASS_PROPERTIES_",
|
|
"_PROTOCOL_CLASS_PROPERTIES_"));
|
|
}
|
|
|
|
/*** General ***********************************************************/
|
|
|
|
/// Build a list structure from the given array of objects.
|
|
/// If the array is empty, use null. The assumption is that every
|
|
/// initializer has the same size.
|
|
///
|
|
/// \param optionalEltSize - if non-zero, a size which needs
|
|
/// to be placed in the list header
|
|
llvm::Constant *buildOptionalList(ArrayRef<llvm::Constant*> objects,
|
|
Size optionalEltSize,
|
|
StringRef nameBase) {
|
|
if (objects.empty())
|
|
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
|
|
|
|
SmallVector<llvm::Constant*, 3> fields;
|
|
|
|
// FIXME. _PROTOCOL_METHOD_TYPES_ does not have the first two entries.
|
|
// May want to pull this into its own routine for performance; if needed.
|
|
if (!nameBase.equals("_PROTOCOL_METHOD_TYPES_")) {
|
|
// In all of the foo_list_t structs, either:
|
|
// - there's a 32-bit entry size and a 32-bit count or
|
|
// - there's no entry size and a uintptr_t count.
|
|
if (!optionalEltSize.isZero()) {
|
|
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty,
|
|
optionalEltSize.getValue()));
|
|
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty, objects.size()));
|
|
} else {
|
|
fields.push_back(llvm::ConstantInt::get(IGM.IntPtrTy, objects.size()));
|
|
}
|
|
}
|
|
|
|
auto arrayTy =
|
|
llvm::ArrayType::get(objects[0]->getType(), objects.size());
|
|
fields.push_back(llvm::ConstantArray::get(arrayTy, objects));
|
|
|
|
return buildGlobalVariable(fields, nameBase);
|
|
}
|
|
|
|
/// Get the name of the class or protocol to mangle into the ObjC symbol
|
|
/// name.
|
|
StringRef getEntityName(llvm::SmallVectorImpl<char> &buffer) const {
|
|
if (auto theClass = TheEntity.dyn_cast<ClassDecl*>()) {
|
|
return theClass->getObjCRuntimeName(buffer);
|
|
}
|
|
|
|
if (auto theProtocol = TheEntity.dyn_cast<ProtocolDecl*>()) {
|
|
return theProtocol->getObjCRuntimeName(buffer);
|
|
}
|
|
|
|
llvm_unreachable("not a class or protocol?!");
|
|
}
|
|
|
|
/// Build a private global variable as a structure containing the
|
|
/// given fields.
|
|
llvm::Constant *buildGlobalVariable(llvm::Constant *init,
|
|
StringRef nameBase) {
|
|
llvm::SmallString<64> nameBuffer;
|
|
auto var = new llvm::GlobalVariable(IGM.Module, init->getType(),
|
|
/*constant*/ true,
|
|
llvm::GlobalVariable::PrivateLinkage,
|
|
init,
|
|
Twine(nameBase)
|
|
+ getEntityName(nameBuffer)
|
|
+ (TheExtension
|
|
? Twine("_$_") + CategoryName.str()
|
|
: Twine()));
|
|
var->setAlignment(IGM.getPointerAlignment().getValue());
|
|
switch (IGM.TargetInfo.OutputObjectFormat) {
|
|
case llvm::Triple::MachO:
|
|
var->setSection("__DATA, __objc_const");
|
|
break;
|
|
case llvm::Triple::ELF:
|
|
var->setSection(".data");
|
|
break;
|
|
default:
|
|
llvm_unreachable("Don't know how to emit private global constants for "
|
|
"the selected object format.");
|
|
}
|
|
return var;
|
|
}
|
|
|
|
llvm::Constant *buildGlobalVariable(ArrayRef<llvm::Constant*> fields,
|
|
StringRef nameBase) {
|
|
auto init = llvm::ConstantStruct::getAnon(IGM.getLLVMContext(), fields);
|
|
return buildGlobalVariable(init, nameBase);
|
|
}
|
|
|
|
public:
|
|
/// Member types don't get any representation.
|
|
/// Maybe this should change for reflection purposes?
|
|
void visitTypeDecl(TypeDecl *type) {}
|
|
|
|
/// Pattern-bindings don't require anything special as long as
|
|
/// these initializations are performed in the constructor, not
|
|
/// .cxx_construct.
|
|
void visitPatternBindingDecl(PatternBindingDecl *binding) {}
|
|
|
|
/// Subscripts should probably be collected in extended metadata.
|
|
void visitSubscriptDecl(SubscriptDecl *subscript) {
|
|
if (!requiresObjCSubscriptDescriptor(IGM, subscript)) return;
|
|
auto getter_setter = emitObjCSubscriptMethodDescriptors(IGM, subscript);
|
|
if (subscript->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
OptInstanceMethods.push_back(getter_setter.first);
|
|
if (isBuildingProtocol())
|
|
OptInstanceMethodTypesExt.push_back(
|
|
getMethodTypeExtendedEncoding(IGM, subscript->getGetter()));
|
|
} else {
|
|
InstanceMethods.push_back(getter_setter.first);
|
|
if (isBuildingProtocol())
|
|
InstanceMethodTypesExt.push_back(
|
|
getMethodTypeExtendedEncoding(IGM, subscript->getGetter()));
|
|
}
|
|
|
|
if (getter_setter.second) {
|
|
assert(subscript->getSetter() && "no descriptor for setter?!");
|
|
if (subscript->getAttrs().hasAttribute<OptionalAttr>()) {
|
|
OptInstanceMethods.push_back(getter_setter.second);
|
|
if (isBuildingProtocol())
|
|
OptInstanceMethodTypesExt.push_back(
|
|
getMethodTypeExtendedEncoding(IGM, subscript->getSetter()));
|
|
} else {
|
|
InstanceMethods.push_back(getter_setter.second);
|
|
if (isBuildingProtocol())
|
|
InstanceMethodTypesExt.push_back(
|
|
getMethodTypeExtendedEncoding(IGM, subscript->getSetter()));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Emit the private data (RO-data) associated with a class.
|
|
llvm::Constant *irgen::emitClassPrivateData(IRGenModule &IGM,
|
|
ClassDecl *cls) {
|
|
assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode");
|
|
SILType selfType = getSelfType(cls);
|
|
auto &classTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
|
|
auto &layout = classTI.getLayout(IGM);
|
|
auto &fieldLayout = classTI.getClassLayout(IGM);
|
|
ClassDataBuilder builder(IGM, cls, layout, fieldLayout);
|
|
|
|
// First, build the metaclass object.
|
|
builder.buildMetaclassStub();
|
|
|
|
// Then build the class RO-data.
|
|
return builder.emitROData(ForClass);
|
|
}
|
|
|
|
std::tuple<llvm::Constant * /*classData*/,
|
|
llvm::Constant * /*metaclassData*/,
|
|
Size>
|
|
irgen::emitClassPrivateDataFields(IRGenModule &IGM, ClassDecl *cls) {
|
|
assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode");
|
|
SILType selfType = getSelfType(cls);
|
|
auto &classTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
|
|
auto &layout = classTI.getLayout(IGM);
|
|
auto &fieldLayout = classTI.getClassLayout(IGM);
|
|
ClassDataBuilder builder(IGM, cls, layout, fieldLayout);
|
|
|
|
auto classFields = builder.emitRODataFields(ForClass);
|
|
auto metaclassFields = builder.emitRODataFields(ForMetaClass);
|
|
Size size(IGM.DataLayout.getTypeAllocSize(classFields->getType()));
|
|
return std::make_tuple(classFields, metaclassFields, size);
|
|
}
|
|
|
|
/// Emit the metadata for an ObjC category.
|
|
llvm::Constant *irgen::emitCategoryData(IRGenModule &IGM,
|
|
ExtensionDecl *ext) {
|
|
assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode");
|
|
ClassDecl *cls = ext->getAsClassOrClassExtensionContext();
|
|
assert(cls && "generating category metadata for a non-class extension");
|
|
|
|
ClassDataBuilder builder(IGM, cls, ext);
|
|
|
|
return builder.emitCategory();
|
|
}
|
|
|
|
/// Emit the metadata for an ObjC protocol.
|
|
llvm::Constant *irgen::emitObjCProtocolData(IRGenModule &IGM,
|
|
ProtocolDecl *proto) {
|
|
assert(proto->isObjC() && "not an objc protocol");
|
|
ClassDataBuilder builder(IGM, proto);
|
|
return builder.emitProtocol();
|
|
}
|
|
|
|
const TypeInfo *TypeConverter::convertClassType(ClassDecl *D) {
|
|
llvm::StructType *ST = IGM.createNominalType(D);
|
|
llvm::PointerType *irType = ST->getPointerTo();
|
|
ReferenceCounting refcount = ::getReferenceCountingForClass(IGM, D);
|
|
|
|
SpareBitVector spareBits;
|
|
|
|
// Classes known to be implemented in Swift can be assumed not to have tagged
|
|
// pointer representations, so we can use spare bits for enum layout with
|
|
// them. We can't make that assumption about imported ObjC types.
|
|
if (D->hasClangNode() && IGM.TargetInfo.hasObjCTaggedPointers())
|
|
spareBits.appendClearBits(IGM.getPointerSize().getValueInBits());
|
|
else
|
|
spareBits = IGM.getHeapObjectSpareBits();
|
|
|
|
return new ClassTypeInfo(irType, IGM.getPointerSize(),
|
|
std::move(spareBits),
|
|
IGM.getPointerAlignment(),
|
|
D, refcount);
|
|
}
|
|
|
|
/// Lazily declare a fake-looking class to represent an ObjC runtime base class.
|
|
ClassDecl *IRGenModule::getObjCRuntimeBaseClass(Identifier name,
|
|
Identifier objcName) {
|
|
auto found = SwiftRootClasses.find(name);
|
|
if (found != SwiftRootClasses.end())
|
|
return found->second;
|
|
|
|
// Make a really fake-looking class.
|
|
auto SwiftRootClass = new (Context) ClassDecl(SourceLoc(), name, SourceLoc(),
|
|
MutableArrayRef<TypeLoc>(),
|
|
/*generics*/ nullptr,
|
|
Context.TheBuiltinModule);
|
|
SwiftRootClass->computeType();
|
|
SwiftRootClass->setIsObjC(true);
|
|
SwiftRootClass->getAttrs().add(ObjCAttr::createNullary(Context, objcName,
|
|
/*implicit=*/true));
|
|
SwiftRootClass->setImplicit();
|
|
SwiftRootClass->setAccessibility(Accessibility::Public);
|
|
|
|
SwiftRootClasses.insert({name, SwiftRootClass});
|
|
return SwiftRootClass;
|
|
}
|
|
|
|
/// Lazily declare the ObjC runtime base class for a Swift root class.
|
|
ClassDecl *
|
|
IRGenModule::getObjCRuntimeBaseForSwiftRootClass(ClassDecl *theClass) {
|
|
assert(!theClass->hasSuperclass() && "must pass a root class");
|
|
|
|
Identifier name;
|
|
// If the class declares its own ObjC runtime base, use it.
|
|
if (auto baseAttr = theClass->getAttrs()
|
|
.getAttribute<SwiftNativeObjCRuntimeBaseAttr>()) {
|
|
name = baseAttr->BaseClassName;
|
|
} else {
|
|
// Otherwise, use the standard SwiftObject class.
|
|
name = Context.Id_SwiftObject;
|
|
}
|
|
return getObjCRuntimeBaseClass(name, name);
|
|
}
|
|
|
|
ClassDecl *irgen::getRootClassForMetaclass(IRGenModule &IGM, ClassDecl *C) {
|
|
while (auto superclass = C->getSuperclass())
|
|
C = superclass->getClassOrBoundGenericClass();
|
|
|
|
// If the formal root class is imported from Objective-C, then
|
|
// we should use that. For a class that's really implemented in
|
|
// Objective-C, this is obviously right. For a class that's
|
|
// really implemented in Swift, but that we're importing via an
|
|
// Objective-C interface, this would be wrong --- except such a
|
|
// class can never be a formal root class, because a Swift class
|
|
// without a formal superclass will actually be parented by
|
|
// SwiftObject (or maybe eventually something else like it),
|
|
// which will be visible in the Objective-C type system.
|
|
if (C->hasClangNode()) return C;
|
|
|
|
// FIXME: If the root class specifies its own runtime ObjC base class,
|
|
// assume that that base class ultimately inherits NSObject.
|
|
if (C->getAttrs().hasAttribute<SwiftNativeObjCRuntimeBaseAttr>())
|
|
return IGM.getObjCRuntimeBaseClass(
|
|
IGM.Context.getSwiftId(KnownFoundationEntity::NSObject),
|
|
IGM.Context.getIdentifier("NSObject"));
|
|
|
|
return IGM.getObjCRuntimeBaseClass(IGM.Context.Id_SwiftObject,
|
|
IGM.Context.Id_SwiftObject);
|
|
}
|
|
|
|
bool irgen::doesClassMetadataRequireDynamicInitialization(IRGenModule &IGM,
|
|
ClassDecl *theClass) {
|
|
// Classes imported from Objective-C never requires dynamic initialization.
|
|
if (theClass->hasClangNode())
|
|
return false;
|
|
|
|
auto &layout = getSelfTypeInfo(IGM, theClass).getClassLayout(IGM);
|
|
return layout.MetadataRequiresDynamicInitialization;
|
|
}
|