blob: 6d40eb4fed45d26e0eb4011998f432eca699f462 [file] [log] [blame]
//===--- 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, SILType classType) const;
public:
ClassTypeInfo(llvm::PointerType *irType, Size size,
SpareBitVector spareBits, Alignment align,
ClassDecl *theClass, ReferenceCounting refcount)
: HeapTypeInfo(irType, size, std::move(spareBits), align),
TheClass(theClass), Layout(nullptr), Refcount(refcount) {}
ReferenceCounting getReferenceCounting() const {
return Refcount;
}
~ClassTypeInfo() {
delete Layout;
}
ClassDecl *getClass() const { return TheClass; }
const StructLayout &getLayout(IRGenModule &IGM, SILType classType) const;
const ClassLayout &getClassLayout(IRGenModule &IGM, SILType type) const;
Alignment getHeapAlignment(IRGenModule &IGM, SILType type) const {
return getLayout(IGM, type).getAlignment();
}
ArrayRef<ElementLayout> getElements(IRGenModule &IGM, SILType type) const {
return getLayout(IGM, type).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);
}
namespace {
class ClassLayoutBuilder : public StructLayoutBuilder {
SmallVector<ElementLayout, 8> Elements;
SmallVector<VarDecl*, 8> AllStoredProperties;
SmallVector<FieldAccess, 8> AllFieldAccesses;
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, SILType classType)
: StructLayoutBuilder(IGM)
{
// Start by adding a heap header.
addHeapHeader();
// Next, add the fields for the given class.
auto theClass = classType.getClassOrBoundGenericClass();
assert(theClass);
addFieldsForClass(theClass, classType);
// 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.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()) {
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)) {
ClassMetadataRequiresDynamicInitialization = true;
// If the superclass is resilient to us, we cannot statically
// know the layout of either its instances or its class objects.
ClassHasFixedFieldCount = false;
ClassHasFixedSize = false;
// Furthermore, if the superclass is a generic context, we have to
// assume that its layout depends on its generic parameters.
// But this only propagates down to subclasses whose superclass type
// depends on the subclass's generic context.
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();
}
}
// Access strategies should be set by the abstract class layout,
// not using the concrete type information we have.
const ClassLayout *abstractLayout = nullptr;
SILType selfType = getSelfType(theClass);
if (classType != selfType) {
auto &selfTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
abstractLayout = &selfTI.getClassLayout(IGM, selfType);
}
// Collect fields from this class and add them to the layout as a chunk.
addDirectFieldsFromClass(theClass, classType, abstractLayout);
}
void addDirectFieldsFromClass(ClassDecl *theClass,
SILType classType,
const ClassLayout *abstractLayout) {
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;
}
size_t fieldIndex = AllStoredProperties.size();
assert(!abstractLayout ||
abstractLayout->getFieldIndex(var) == fieldIndex);
Elements.push_back(ElementLayout::getIncomplete(eltType));
AllStoredProperties.push_back(var);
AllFieldAccesses.push_back(getFieldAccess(abstractLayout, fieldIndex));
}
}
FieldAccess getFieldAccess(const ClassLayout *abstractLayout,
size_t abstractFieldIndex) {
// The class has fixed size, so the field offset is known statically.
if (ClassHasFixedSize) {
return FieldAccess::ConstantDirect;
}
// If the field offset can't be known at compile time, we need to
// load a field offset, either from a global or the metadata's field
// offset vector.
// The global will exist only if the abstract type has concrete layout,
// so if we're not laying out the abstract type, use its access rule.
if (abstractLayout) {
return abstractLayout->AllFieldAccesses[abstractFieldIndex];
}
// If layout doesn't depend on any generic parameters, but it's
// nonetheless not statically known (because either a superclass
// or a member type was resilient), then we can rely on the existence
// of a global field offset variable which will be initialized by
// either the Objective-C or Swift runtime, depending on the
// class's heritage.
if (ClassHasConcreteLayout) {
return FieldAccess::NonConstantDirect;
}
// If layout depends on generic parameters, we have to load the
// offset from the class metadata.
// If the layout of the class metadata is statically known, then
// there should be a fixed offset to the right offset.
if (ClassHasFixedFieldCount) {
return FieldAccess::ConstantIndirect;
}
// Otherwise, the offset of the offset is stored in a global variable
// that will be set up by the runtime.
return FieldAccess::NonConstantIndirect;
}
};
}
void ClassTypeInfo::generateLayout(IRGenModule &IGM, SILType classType) const {
assert(!Layout && FieldLayout.AllStoredProperties.empty() &&
"already generated layout");
// Add the heap header.
ClassLayoutBuilder builder(IGM, classType);
// generateLayout can call itself recursively in order to compute a layout
// for the abstract type. If classType shares an exemplar types with the
// abstract type, that will end up re-entrantly building the layout
// of the same ClassTypeInfo. We don't have anything else to do in this
// case.
if (Layout) {
assert(this == &IGM.getTypeInfo(
getSelfType(classType.getClassOrBoundGenericClass())));
return;
}
// Set the body of the class type.
auto classTy =
cast<llvm::StructType>(getStorageType()->getPointerElementType());
builder.setAsBodyOfStruct(classTy);
// Record the layout.
Layout = new StructLayout(builder, classType.getSwiftRValueType(), classTy,
builder.getElements());
FieldLayout = builder.getClassLayout();
}
const StructLayout &
ClassTypeInfo::getLayout(IRGenModule &IGM, SILType classType) const {
// Return the cached layout if available.
if (Layout) return *Layout;
generateLayout(IGM, classType);
return *Layout;
}
const ClassLayout &
ClassTypeInfo::getClassLayout(IRGenModule &IGM, SILType classType) const {
// Return the cached layout if available.
if (Layout)
return FieldLayout;
generateLayout(IGM, classType);
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 = baseClassTI.getClass();
auto &classLayout = baseClassTI.getClassLayout(IGF.IGM, baseType);
unsigned fieldIndex = classLayout.getFieldIndex(field);
switch (classLayout.AllFieldAccesses[fieldIndex]) {
case FieldAccess::ConstantDirect: {
Address baseAddr(base, baseClassTI.getHeapAlignment(IGF.IGM, baseType));
auto &element = baseClassTI.getElements(IGF.IGM, baseType)[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, baseType);
unsigned fieldIndex = classLayout.getFieldIndex(field);
switch (classLayout.AllFieldAccesses[fieldIndex]) {
case FieldAccess::ConstantDirect: {
auto &element = baseClassTI.getElements(IGM, baseType)[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, selfType);
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, selfType);
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, selfType);
// 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 selfType = getSelfType(Class);
auto &classTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
auto &layout = classTI.getLayout(IGM, selfType);
if (layout.isFixedLayout())
return layout.emitSize(IGM);
return nullptr;
}
llvm::Constant *irgen::tryEmitClassConstantFragileInstanceAlignMask(
IRGenModule &IGM,
ClassDecl *Class) {
auto selfType = getSelfType(Class);
auto &classTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
auto &layout = classTI.getLayout(IGM, selfType);
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);
SILType selfType = getSelfType(D);
auto &classTI = getTypeInfo(selfType).as<ClassTypeInfo>();
// Emit the class metadata.
emitClassMetadata(*this, D,
classTI.getLayout(*this, selfType),
classTI.getClassLayout(*this, selfType));
emitNestedTypeDecls(D->getMembers());
emitFieldMetadataRecord(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_"));
}
// Older OSs' libobjcs can't handle class property data.
if ((IGM.Triple.isMacOSX() && IGM.Triple.isMacOSXVersionLT(10, 11)) ||
(IGM.Triple.isiOS() && IGM.Triple.isOSVersionLT(9))) {
return null();
}
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, selfType);
auto &fieldLayout = classTI.getClassLayout(IGM, selfType);
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, selfType);
auto &fieldLayout = classTI.getClassLayout(IGM, selfType);
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(CanType type, ClassDecl *D) {
llvm::StructType *ST = IGM.createNominalType(type);
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::Open);
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;
SILType selfType = getSelfType(theClass);
auto &selfTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();
auto &layout = selfTI.getClassLayout(IGM, selfType);
return layout.MetadataRequiresDynamicInitialization;
}