blob: 9d6f62d702cfe4c6c01de01b5d535daeb0ace200 [file] [log] [blame]
//===--- GenHeap.cpp - Layout of heap objects and their metadata ----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements routines for arbitrary Swift-native heap objects,
// such as layout and reference-counting.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Path.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Intrinsics.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/ABI/MetadataValues.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/IRGenOptions.h"
#include "ConstantBuilder.h"
#include "Explosion.h"
#include "GenClass.h"
#include "GenProto.h"
#include "GenType.h"
#include "IRGenDebugInfo.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "HeapTypeInfo.h"
#include "IndirectTypeInfo.h"
#include "MetadataRequest.h"
#include "GenHeap.h"
using namespace swift;
using namespace irgen;
namespace {
#define NEVER_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, Nativeness) \
class Nativeness##Name##ReferenceTypeInfo \
: public IndirectTypeInfo<Nativeness##Name##ReferenceTypeInfo, \
FixedTypeInfo> { \
llvm::PointerIntPair<llvm::Type*, 1, bool> ValueTypeAndIsOptional; \
public: \
Nativeness##Name##ReferenceTypeInfo(llvm::Type *valueType, \
llvm::Type *type, \
Size size, Alignment alignment, \
SpareBitVector &&spareBits, \
bool isOptional) \
: IndirectTypeInfo(type, size, std::move(spareBits), alignment, \
IsNotPOD, IsNotBitwiseTakable, IsFixedSize), \
ValueTypeAndIsOptional(valueType, isOptional) {} \
void initializeWithCopy(IRGenFunction &IGF, Address destAddr, \
Address srcAddr, SILType T, \
bool isOutlined) const override { \
IGF.emit##Nativeness##Name##CopyInit(destAddr, srcAddr); \
} \
void initializeWithTake(IRGenFunction &IGF, Address destAddr, \
Address srcAddr, SILType T, \
bool isOutlined) const override { \
IGF.emit##Nativeness##Name##TakeInit(destAddr, srcAddr); \
} \
void assignWithCopy(IRGenFunction &IGF, Address destAddr, Address srcAddr, \
SILType T, bool isOutlined) const override { \
IGF.emit##Nativeness##Name##CopyAssign(destAddr, srcAddr); \
} \
void assignWithTake(IRGenFunction &IGF, Address destAddr, Address srcAddr, \
SILType T, bool isOutlined) const override { \
IGF.emit##Nativeness##Name##TakeAssign(destAddr, srcAddr); \
} \
void destroy(IRGenFunction &IGF, Address addr, SILType T, \
bool isOutlined) const override { \
IGF.emit##Nativeness##Name##Destroy(addr); \
} \
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { \
auto count = IGM.getReferenceStorageExtraInhabitantCount( \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
return count - ValueTypeAndIsOptional.getInt(); \
} \
APInt getFixedExtraInhabitantValue(IRGenModule &IGM, \
unsigned bits, \
unsigned index) const override { \
return IGM.getReferenceStorageExtraInhabitantValue(bits, \
index + ValueTypeAndIsOptional.getInt(), \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, \
SILType T, bool isOutlined) \
const override { \
return IGF.getReferenceStorageExtraInhabitantIndex(src, \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, \
Address dest, SILType T, bool isOutlined) \
const override { \
return IGF.storeReferenceStorageExtraInhabitant(index, dest, \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override { \
return IGM.getReferenceStorageExtraInhabitantMask( \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
llvm::Type *getOptionalIntType() const { \
return llvm::IntegerType::get( \
ValueTypeAndIsOptional.getPointer()->getContext(), \
getFixedSize().getValueInBits()); \
} \
};
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, Nativeness) \
class Nativeness##Name##ReferenceTypeInfo \
: public SingleScalarTypeInfo<Nativeness##Name##ReferenceTypeInfo, \
LoadableTypeInfo> { \
llvm::PointerIntPair<llvm::Type*, 1, bool> ValueTypeAndIsOptional; \
public: \
Nativeness##Name##ReferenceTypeInfo(llvm::Type *valueType, \
llvm::Type *type, \
Size size, Alignment alignment, \
SpareBitVector &&spareBits, \
bool isOptional) \
: SingleScalarTypeInfo(type, size, std::move(spareBits), \
alignment, IsNotPOD, IsFixedSize), \
ValueTypeAndIsOptional(valueType, isOptional) {} \
enum { IsScalarPOD = false }; \
llvm::Type *getScalarType() const { \
return ValueTypeAndIsOptional.getPointer(); \
} \
Address projectScalar(IRGenFunction &IGF, Address addr) const { \
return IGF.Builder.CreateBitCast(addr, getScalarType()->getPointerTo()); \
} \
void emitScalarRetain(IRGenFunction &IGF, llvm::Value *value, \
Atomicity atomicity) const { \
IGF.emit##Nativeness##Name##Retain(value, atomicity); \
} \
void emitScalarRelease(IRGenFunction &IGF, llvm::Value *value, \
Atomicity atomicity) const { \
IGF.emit##Nativeness##Name##Release(value, atomicity); \
} \
void emitScalarFixLifetime(IRGenFunction &IGF, llvm::Value *value) const { \
IGF.emitFixLifetime(value); \
} \
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { \
auto count = IGM.getReferenceStorageExtraInhabitantCount( \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
return count - ValueTypeAndIsOptional.getInt(); \
} \
APInt getFixedExtraInhabitantValue(IRGenModule &IGM, \
unsigned bits, \
unsigned index) const override { \
return IGM.getReferenceStorageExtraInhabitantValue(bits, \
index + ValueTypeAndIsOptional.getInt(), \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, \
SILType T, bool isOutlined) \
const override { \
return IGF.getReferenceStorageExtraInhabitantIndex(src, \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, \
Address dest, SILType T, bool isOutlined) \
const override { \
return IGF.storeReferenceStorageExtraInhabitant(index, dest, \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override { \
return IGM.getReferenceStorageExtraInhabitantMask( \
ReferenceOwnership::Name, \
ReferenceCounting::Nativeness); \
} \
};
// The nativeness of a reference storage type is a policy decision.
// Please also see the related ALWAYS_NATIVE and SOMETIMES_UNKNOWN macros
// later in this file that expand to the following boilerplate:
// TypeConverter::create##Name##StorageType
NEVER_LOADABLE_CHECKED_REF_STORAGE_HELPER(Weak, Native)
NEVER_LOADABLE_CHECKED_REF_STORAGE_HELPER(Weak, Unknown)
NEVER_LOADABLE_CHECKED_REF_STORAGE_HELPER(Unowned, Unknown)
ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER(Unowned, Native)
#undef NEVER_LOADABLE_CHECKED_REF_STORAGE_HELPER
#undef ALWAYS_LOADABLE_CHECKED_REF_STORAGE_HELPER
#define UNCHECKED_REF_STORAGE(Name, ...) \
class Name##ReferenceTypeInfo \
: public PODSingleScalarTypeInfo<Name##ReferenceTypeInfo, \
LoadableTypeInfo> { \
bool IsOptional; \
public: \
Name##ReferenceTypeInfo(llvm::Type *type, \
const SpareBitVector &spareBits, \
Size size, Alignment alignment, bool isOptional) \
: PODSingleScalarTypeInfo(type, size, spareBits, alignment), \
IsOptional(isOptional) {} \
/* Static types have the same spare bits as managed heap objects. */ \
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override { \
return getHeapObjectExtraInhabitantCount(IGM) - IsOptional; \
} \
APInt getFixedExtraInhabitantValue(IRGenModule &IGM, \
unsigned bits, \
unsigned index) const override { \
return getHeapObjectFixedExtraInhabitantValue(IGM, bits, \
index + IsOptional, 0); \
} \
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src, \
SILType T, bool isOutlined) \
const override { \
return getHeapObjectExtraInhabitantIndex(IGF, src); \
} \
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index, \
Address dest, SILType T, bool isOutlined) \
const override { \
return storeHeapObjectExtraInhabitant(IGF, index, dest); \
} \
};
#include "swift/AST/ReferenceStorage.def"
} // end anonymous namespace
/// Produce a constant to place in a metatype's isa field
/// corresponding to the given metadata kind.
static llvm::ConstantInt *getMetadataKind(IRGenModule &IGM,
MetadataKind kind) {
return llvm::ConstantInt::get(IGM.MetadataKindTy, uint32_t(kind));
}
/// Perform the layout required for a heap object.
HeapLayout::HeapLayout(IRGenModule &IGM, LayoutStrategy strategy,
ArrayRef<SILType> fieldTypes,
ArrayRef<const TypeInfo *> fieldTypeInfos,
llvm::StructType *typeToFill,
NecessaryBindings &&bindings)
: StructLayout(IGM, /*decl=*/nullptr, LayoutKind::HeapObject, strategy,
fieldTypeInfos, typeToFill),
ElementTypes(fieldTypes.begin(), fieldTypes.end()),
Bindings(std::move(bindings))
{
#ifndef NDEBUG
assert(fieldTypeInfos.size() == fieldTypes.size()
&& "type infos don't match types");
if (!Bindings.empty()) {
assert(fieldTypeInfos.size() >= 1 && "no field for bindings");
auto fixedBindingsField = dyn_cast<FixedTypeInfo>(fieldTypeInfos[0]);
assert(fixedBindingsField
&& "bindings field is not fixed size");
assert(fixedBindingsField->getFixedSize()
== Bindings.getBufferSize(IGM)
&& fixedBindingsField->getFixedAlignment()
== IGM.getPointerAlignment()
&& "bindings field doesn't fit bindings");
}
#endif
}
static llvm::Value *calcInitOffset(swift::irgen::IRGenFunction &IGF,
unsigned int i,
const swift::irgen::HeapLayout &layout) {
llvm::Value *offset = nullptr;
if (i == 0) {
auto startoffset = layout.getSize();
offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, startoffset.getValue());
return offset;
}
auto &prevElt = layout.getElement(i - 1);
auto prevType = layout.getElementTypes()[i - 1];
// Start calculating offsets from the last fixed-offset field.
Size lastFixedOffset = layout.getElement(i - 1).getByteOffset();
if (auto *fixedType = dyn_cast<FixedTypeInfo>(&prevElt.getType())) {
// If the last fixed-offset field is also fixed-size, we can
// statically compute the end of the fixed-offset fields.
auto fixedEnd = lastFixedOffset + fixedType->getFixedSize();
offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, fixedEnd.getValue());
} else {
// Otherwise, we need to add the dynamic size to the fixed start
// offset.
offset = llvm::ConstantInt::get(IGF.IGM.SizeTy, lastFixedOffset.getValue());
offset = IGF.Builder.CreateAdd(
offset, prevElt.getType().getSize(IGF, prevType));
}
return offset;
}
HeapNonFixedOffsets::HeapNonFixedOffsets(IRGenFunction &IGF,
const HeapLayout &layout) {
if (!layout.isFixedLayout()) {
// Calculate all the non-fixed layouts.
// TODO: We could be lazier about this.
llvm::Value *offset = nullptr;
llvm::Value *totalAlign = llvm::ConstantInt::get(IGF.IGM.SizeTy,
layout.getAlignment().getMaskValue());
for (unsigned i : indices(layout.getElements())) {
auto &elt = layout.getElement(i);
auto eltTy = layout.getElementTypes()[i];
switch (elt.getKind()) {
case ElementLayout::Kind::InitialNonFixedSize:
// Factor the non-fixed-size field's alignment into the total alignment.
totalAlign = IGF.Builder.CreateOr(totalAlign,
elt.getType().getAlignmentMask(IGF, eltTy));
LLVM_FALLTHROUGH;
case ElementLayout::Kind::Empty:
case ElementLayout::Kind::Fixed:
// Don't need to dynamically calculate this offset.
Offsets.push_back(nullptr);
break;
case ElementLayout::Kind::NonFixed:
// Start calculating non-fixed offsets from the end of the first fixed
// field.
if (!offset) {
offset = calcInitOffset(IGF, i, layout);
}
// Round up to alignment to get the offset.
auto alignMask = elt.getType().getAlignmentMask(IGF, eltTy);
auto notAlignMask = IGF.Builder.CreateNot(alignMask);
offset = IGF.Builder.CreateAdd(offset, alignMask);
offset = IGF.Builder.CreateAnd(offset, notAlignMask);
Offsets.push_back(offset);
// Advance by the field's size to start the next field.
offset = IGF.Builder.CreateAdd(offset,
elt.getType().getSize(IGF, eltTy));
totalAlign = IGF.Builder.CreateOr(totalAlign, alignMask);
break;
}
}
TotalSize = offset;
TotalAlignMask = totalAlign;
} else {
TotalSize = layout.emitSize(IGF.IGM);
TotalAlignMask = layout.emitAlignMask(IGF.IGM);
}
}
void irgen::emitDeallocateHeapObject(IRGenFunction &IGF,
llvm::Value *object,
llvm::Value *size,
llvm::Value *alignMask) {
// FIXME: We should call a fast deallocator for heap objects with
// known size.
IGF.Builder.CreateCall(IGF.IGM.getDeallocObjectFn(),
{object, size, alignMask});
}
void emitDeallocateUninitializedHeapObject(IRGenFunction &IGF,
llvm::Value *object,
llvm::Value *size,
llvm::Value *alignMask) {
IGF.Builder.CreateCall(IGF.IGM.getDeallocUninitializedObjectFn(),
{object, size, alignMask});
}
void irgen::emitDeallocateClassInstance(IRGenFunction &IGF,
llvm::Value *object,
llvm::Value *size,
llvm::Value *alignMask) {
// FIXME: We should call a fast deallocator for heap objects with
// known size.
IGF.Builder.CreateCall(IGF.IGM.getDeallocClassInstanceFn(),
{object, size, alignMask});
}
void irgen::emitDeallocatePartialClassInstance(IRGenFunction &IGF,
llvm::Value *object,
llvm::Value *metadata,
llvm::Value *size,
llvm::Value *alignMask) {
// FIXME: We should call a fast deallocator for heap objects with
// known size.
IGF.Builder.CreateCall(IGF.IGM.getDeallocPartialClassInstanceFn(),
{object, metadata, size, alignMask});
}
/// Create the destructor function for a layout.
/// TODO: give this some reasonable name and possibly linkage.
static llvm::Function *createDtorFn(IRGenModule &IGM,
const HeapLayout &layout) {
llvm::Function *fn =
llvm::Function::Create(IGM.DeallocatingDtorTy,
llvm::Function::PrivateLinkage,
"objectdestroy", &IGM.Module);
auto attrs = IGM.constructInitialAttributes();
IGM.addSwiftSelfAttributes(attrs, 0);
fn->setAttributes(attrs);
fn->setCallingConv(IGM.SwiftCC);
IRGenFunction IGF(IGM, fn);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, fn);
Address structAddr = layout.emitCastTo(IGF, &*fn->arg_begin());
// Bind necessary bindings, if we have them.
if (layout.hasBindings()) {
// The type metadata bindings should be at a fixed offset, so we can pass
// None for NonFixedOffsets. If we didn't, we'd have a chicken-egg problem.
auto bindingsAddr = layout.getElement(0).project(IGF, structAddr, None);
layout.getBindings().restore(IGF, bindingsAddr, MetadataState::Complete);
}
// Figure out the non-fixed offsets.
HeapNonFixedOffsets offsets(IGF, layout);
// Destroy the fields.
for (unsigned i : indices(layout.getElements())) {
auto &field = layout.getElement(i);
auto fieldTy = layout.getElementTypes()[i];
if (field.isPOD())
continue;
field.getType().destroy(
IGF, field.project(IGF, structAddr, offsets), fieldTy,
true /*Called from metadata constructors: must be outlined*/);
}
emitDeallocateHeapObject(IGF, &*fn->arg_begin(), offsets.getSize(),
offsets.getAlignMask());
IGF.Builder.CreateRetVoid();
return fn;
}
/// Create the size function for a layout.
/// TODO: give this some reasonable name and possibly linkage.
llvm::Constant *HeapLayout::createSizeFn(IRGenModule &IGM) const {
llvm::Function *fn =
llvm::Function::Create(IGM.DeallocatingDtorTy,
llvm::Function::PrivateLinkage,
"objectsize", &IGM.Module);
fn->setAttributes(IGM.constructInitialAttributes());
IRGenFunction IGF(IGM, fn);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, fn);
// Ignore the object pointer; we aren't a dynamically-sized array,
// so it's pointless.
llvm::Value *size = emitSize(IGM);
IGF.Builder.CreateRet(size);
return fn;
}
static llvm::Constant *buildPrivateMetadata(IRGenModule &IGM,
const HeapLayout &layout,
llvm::Constant *dtorFn,
llvm::Constant *captureDescriptor,
MetadataKind kind) {
// Build the fields of the private metadata.
ConstantInitBuilder builder(IGM);
auto fields = builder.beginStruct(IGM.FullBoxMetadataStructTy);
fields.add(dtorFn);
fields.addNullPointer(IGM.WitnessTablePtrTy);
{
auto kindStruct = fields.beginStruct(IGM.TypeMetadataStructTy);
kindStruct.add(getMetadataKind(IGM, kind));
kindStruct.finishAndAddTo(fields);
}
// Figure out the offset to the first element, which is necessary to be able
// to polymorphically project as a generic box.
auto elements = layout.getElements();
Size offset;
if (!elements.empty()
&& elements[0].getKind() == ElementLayout::Kind::Fixed)
offset = elements[0].getByteOffset();
else
offset = Size(0);
fields.addInt32(offset.getValue());
fields.add(captureDescriptor);
llvm::GlobalVariable *var =
fields.finishAndCreateGlobal("metadata",
IGM.getPointerAlignment(),
/*constant*/ true,
llvm::GlobalVariable::PrivateLinkage);
llvm::Constant *indices[] = {
llvm::ConstantInt::get(IGM.Int32Ty, 0),
llvm::ConstantInt::get(IGM.Int32Ty, 2)
};
return llvm::ConstantExpr::getInBoundsGetElementPtr(
/*Ty=*/nullptr, var, indices);
}
llvm::Constant *
HeapLayout::getPrivateMetadata(IRGenModule &IGM,
llvm::Constant *captureDescriptor) const {
if (!privateMetadata)
privateMetadata = buildPrivateMetadata(IGM, *this, createDtorFn(IGM, *this),
captureDescriptor,
MetadataKind::HeapLocalVariable);
return privateMetadata;
}
llvm::Value *IRGenFunction::emitUnmanagedAlloc(const HeapLayout &layout,
const llvm::Twine &name,
llvm::Constant *captureDescriptor,
const HeapNonFixedOffsets *offsets) {
llvm::Value *metadata = layout.getPrivateMetadata(IGM, captureDescriptor);
llvm::Value *size, *alignMask;
if (offsets) {
size = offsets->getSize();
alignMask = offsets->getAlignMask();
} else {
size = layout.emitSize(IGM);
alignMask = layout.emitAlignMask(IGM);
}
return emitAllocObjectCall(metadata, size, alignMask, name);
}
namespace {
class BuiltinNativeObjectTypeInfo
: public HeapTypeInfo<BuiltinNativeObjectTypeInfo> {
public:
BuiltinNativeObjectTypeInfo(llvm::PointerType *storage,
Size size, SpareBitVector spareBits,
Alignment align)
: HeapTypeInfo(storage, size, spareBits, align) {}
/// Builtin.NativeObject uses Swift native reference-counting.
ReferenceCounting getReferenceCounting() const {
return ReferenceCounting::Native;
}
};
} // end anonymous namespace
const LoadableTypeInfo *TypeConverter::convertBuiltinNativeObject() {
return new BuiltinNativeObjectTypeInfo(IGM.RefCountedPtrTy,
IGM.getPointerSize(),
IGM.getHeapObjectSpareBits(),
IGM.getPointerAlignment());
}
unsigned IRGenModule::getReferenceStorageExtraInhabitantCount(
ReferenceOwnership ownership,
ReferenceCounting style) const {
switch (style) {
case ReferenceCounting::Native:
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
if (ownership == ReferenceOwnership::Name) \
break;
#include "swift/AST/ReferenceStorage.def"
if (ObjCInterop)
break;
return getHeapObjectExtraInhabitantCount(*this);
case ReferenceCounting::Block:
case ReferenceCounting::ObjC:
case ReferenceCounting::Unknown:
break;
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("Unsupported reference-counting style");
}
// The default behavior uses pointer semantics, therefore null is the only
// extra inhabitant allowed.
return 1;
}
SpareBitVector IRGenModule::getReferenceStorageSpareBits(
ReferenceOwnership ownership,
ReferenceCounting style) const {
// We have to be conservative (even with native reference-counting) in order
// to interoperate with code that might be working more generically with the
// memory/type.
switch (style) {
case ReferenceCounting::Native:
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
if (ownership == ReferenceOwnership::Name) \
break;
#include "swift/AST/ReferenceStorage.def"
if (ObjCInterop)
break;
return getHeapObjectSpareBits();
case ReferenceCounting::Block:
case ReferenceCounting::ObjC:
case ReferenceCounting::Unknown:
break;
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("Unsupported reference-counting style");
}
// The default behavior uses pointer semantics.
return SpareBitVector::getConstant(getPointerSize().getValueInBits(), false);
}
APInt IRGenModule::getReferenceStorageExtraInhabitantValue(unsigned bits,
unsigned index,
ReferenceOwnership ownership,
ReferenceCounting style) const {
// We have to be conservative (even with native reference-counting) in order
// to interoperate with code that might be working more generically with the
// memory/type.
switch (style) {
case ReferenceCounting::Native:
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
if (ownership == ReferenceOwnership::Name) \
break;
#include "swift/AST/ReferenceStorage.def"
if (ObjCInterop)
break;
return getHeapObjectFixedExtraInhabitantValue(*this, bits, index, 0);
case ReferenceCounting::Block:
case ReferenceCounting::ObjC:
case ReferenceCounting::Unknown:
break;
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("Unsupported reference-counting style");
}
// The default behavior allows for only one legal extra inhabitant, therefore
// this must be the null pattern.
assert(index == 0);
return APInt(bits, 0);
}
APInt IRGenModule::getReferenceStorageExtraInhabitantMask(
ReferenceOwnership ownership,
ReferenceCounting style) const {
switch (style) {
case ReferenceCounting::Native:
case ReferenceCounting::Block:
case ReferenceCounting::ObjC:
case ReferenceCounting::Unknown:
break;
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("Unsupported reference-counting style");
}
return APInt::getAllOnesValue(getPointerSize().getValueInBits());
}
llvm::Value *IRGenFunction::getReferenceStorageExtraInhabitantIndex(Address src,
ReferenceOwnership ownership,
ReferenceCounting style) {
switch (style) {
case ReferenceCounting::Native:
if (IGM.ObjCInterop)
break;
return getHeapObjectExtraInhabitantIndex(*this, src);
case ReferenceCounting::Block:
case ReferenceCounting::ObjC:
case ReferenceCounting::Unknown:
break;
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("Unsupported reference-counting style");
}
// The default behavior allows for only one legal extra inhabitant, therefore
// this must be the null pattern.
auto PtrTy =
#define CHECKED_REF_STORAGE(Name, ...) \
ownership == ReferenceOwnership::Name ? IGM.Name##ReferencePtrTy :
#include "swift/AST/ReferenceStorage.def"
nullptr;
(void)PtrTy;
assert(src.getAddress()->getType() == PtrTy);
src = Builder.CreateStructGEP(src, 0, Size(0));
llvm::Value *ptr = Builder.CreateLoad(src);
llvm::Value *isNull = Builder.CreateIsNull(ptr);
llvm::Value *result =
Builder.CreateSelect(isNull, Builder.getInt32(0),
llvm::ConstantInt::getSigned(IGM.Int32Ty, -1));
return result;
}
void IRGenFunction::storeReferenceStorageExtraInhabitant(llvm::Value *index,
Address dest,
ReferenceOwnership ownership,
ReferenceCounting style) {
switch (style) {
case ReferenceCounting::Native:
if (IGM.ObjCInterop)
break;
return storeHeapObjectExtraInhabitant(*this, index, dest);
case ReferenceCounting::Block:
case ReferenceCounting::ObjC:
case ReferenceCounting::Unknown:
break;
case ReferenceCounting::Bridge:
case ReferenceCounting::Error:
llvm_unreachable("Unsupported reference-counting style");
}
// The default behavior allows for only one legal extra inhabitant, therefore
// this must be the null pattern.
auto PtrTy =
#define CHECKED_REF_STORAGE(Name, ...) \
ownership == ReferenceOwnership::Name ? IGM.Name##ReferencePtrTy :
#include "swift/AST/ReferenceStorage.def"
nullptr;
(void)PtrTy;
assert(dest.getAddress()->getType() == PtrTy);
dest = Builder.CreateStructGEP(dest, 0, Size(0));
llvm::Value *null = llvm::ConstantPointerNull::get(IGM.RefCountedPtrTy);
Builder.CreateStore(null, dest);
}
#define SOMETIMES_UNKNOWN(Name) \
const TypeInfo * \
TypeConverter::create##Name##StorageType(llvm::Type *valueType, \
ReferenceCounting style, \
bool isOptional) { \
auto &&spareBits = IGM.getReferenceStorageSpareBits( \
ReferenceOwnership::Name, style); \
switch (style) { \
case ReferenceCounting::Native: \
return new Native##Name##ReferenceTypeInfo(valueType, \
IGM.Name##ReferencePtrTy->getElementType(), \
IGM.getPointerSize(), \
IGM.getPointerAlignment(), \
std::move(spareBits), \
isOptional); \
case ReferenceCounting::ObjC: \
case ReferenceCounting::Block: \
case ReferenceCounting::Unknown: \
return new Unknown##Name##ReferenceTypeInfo(valueType, \
IGM.Name##ReferencePtrTy->getElementType(), \
IGM.getPointerSize(), \
IGM.getPointerAlignment(), \
std::move(spareBits), \
isOptional); \
case ReferenceCounting::Bridge: \
case ReferenceCounting::Error: \
llvm_unreachable("not supported!"); \
} \
llvm_unreachable("bad reference-counting style"); \
}
#define ALWAYS_NATIVE(Name) \
const TypeInfo * \
TypeConverter::create##Name##StorageType(llvm::Type *valueType, \
ReferenceCounting style, \
bool isOptional) { \
assert(style == ReferenceCounting::Native); \
auto &&spareBits = IGM.getReferenceStorageSpareBits( \
ReferenceOwnership::Name, \
ReferenceCounting::Native); \
return new Native##Name##ReferenceTypeInfo(valueType, \
IGM.Name##ReferencePtrTy->getElementType(), \
IGM.getPointerSize(), \
IGM.getPointerAlignment(), \
std::move(spareBits), \
isOptional); \
}
// Always native versus "sometimes unknown" reference storage type policy:
SOMETIMES_UNKNOWN(Weak)
SOMETIMES_UNKNOWN(Unowned)
#undef SOMETIMES_UNKNOWN
#undef ALWAYS_NATIVE
#define CHECKED_REF_STORAGE(Name, ...) \
static_assert(&TypeConverter::create##Name##StorageType != nullptr, \
"Missing reference storage type converter helper constructor");
#define UNCHECKED_REF_STORAGE(Name, ...) \
const TypeInfo * \
TypeConverter::create##Name##StorageType(llvm::Type *valueType, \
ReferenceCounting style, \
bool isOptional) { \
(void)style; /* unused */ \
return new Name##ReferenceTypeInfo(valueType, \
IGM.getHeapObjectSpareBits(), \
IGM.getPointerSize(), \
IGM.getPointerAlignment(), \
isOptional); \
}
#include "swift/AST/ReferenceStorage.def"
/// Does the given value superficially not require reference-counting?
static bool doesNotRequireRefCounting(llvm::Value *value) {
// Constants never require reference-counting.
return isa<llvm::ConstantPointerNull>(value);
}
static llvm::FunctionType *getTypeOfFunction(llvm::Constant *fn) {
return cast<llvm::FunctionType>(fn->getType()->getPointerElementType());
}
/// Emit a unary call to perform a ref-counting operation.
///
/// \param fn - expected signature 'void (T)' or 'T (T)'
static void emitUnaryRefCountCall(IRGenFunction &IGF,
llvm::Constant *fn,
llvm::Value *value) {
auto cc = IGF.IGM.DefaultCC;
auto fun = dyn_cast<llvm::Function>(fn);
if (fun)
cc = fun->getCallingConv();
// Instead of casting the input, we cast the function type.
// This tends to produce less IR, but might be evil.
auto origFnType = getTypeOfFunction(fn);
if (value->getType() != origFnType->getParamType(0)) {
auto resultTy = origFnType->getReturnType() == IGF.IGM.VoidTy
? IGF.IGM.VoidTy
: value->getType();
llvm::FunctionType *fnType =
llvm::FunctionType::get(resultTy, value->getType(), false);
fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
}
// Emit the call.
llvm::CallInst *call = IGF.Builder.CreateCall(fn, value);
if (fun && fun->hasParamAttribute(0, llvm::Attribute::Returned))
call->addParamAttr(0, llvm::Attribute::Returned);
call->setCallingConv(cc);
call->setDoesNotThrow();
}
/// Emit a copy-like call to perform a ref-counting operation.
///
/// \param fn - expected signature 'void (T, T)' or 'T (T, T)'
static void emitCopyLikeCall(IRGenFunction &IGF,
llvm::Constant *fn,
llvm::Value *dest,
llvm::Value *src) {
assert(dest->getType() == src->getType() &&
"type mismatch in binary refcounting operation");
auto cc = IGF.IGM.DefaultCC;
auto fun = dyn_cast<llvm::Function>(fn);
if (fun)
cc = fun->getCallingConv();
// Instead of casting the inputs, we cast the function type.
// This tends to produce less IR, but might be evil.
auto origFnType = getTypeOfFunction(fn);
if (dest->getType() != origFnType->getParamType(0)) {
llvm::Type *paramTypes[] = { dest->getType(), dest->getType() };
auto resultTy = origFnType->getReturnType() == IGF.IGM.VoidTy
? IGF.IGM.VoidTy
: dest->getType();
llvm::FunctionType *fnType =
llvm::FunctionType::get(resultTy, paramTypes, false);
fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
}
// Emit the call.
llvm::CallInst *call = IGF.Builder.CreateCall(fn, {dest, src});
if (fun && fun->hasParamAttribute(0, llvm::Attribute::Returned))
call->addParamAttr(0, llvm::Attribute::Returned);
call->setCallingConv(cc);
call->setDoesNotThrow();
}
/// Emit a call to a function with a loadWeak-like signature.
///
/// \param fn - expected signature 'T (Weak*)'
static llvm::Value *emitLoadWeakLikeCall(IRGenFunction &IGF,
llvm::Constant *fn,
llvm::Value *addr,
llvm::Type *resultType) {
assert((addr->getType() == IGF.IGM.WeakReferencePtrTy ||
addr->getType() == IGF.IGM.UnownedReferencePtrTy) &&
"address is not of a weak or unowned reference");
auto cc = IGF.IGM.DefaultCC;
if (auto fun = dyn_cast<llvm::Function>(fn))
cc = fun->getCallingConv();
// Instead of casting the output, we cast the function type.
// This tends to produce less IR, but might be evil.
if (resultType != getTypeOfFunction(fn)->getReturnType()) {
llvm::Type *paramTypes[] = { addr->getType() };
llvm::FunctionType *fnType =
llvm::FunctionType::get(resultType, paramTypes, false);
fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
}
// Emit the call.
llvm::CallInst *call = IGF.Builder.CreateCall(fn, addr);
call->setCallingConv(cc);
call->setDoesNotThrow();
return call;
}
/// Emit a call to a function with a storeWeak-like signature.
///
/// \param fn - expected signature 'void (Weak*, T)' or 'Weak* (Weak*, T)'
static void emitStoreWeakLikeCall(IRGenFunction &IGF,
llvm::Constant *fn,
llvm::Value *addr,
llvm::Value *value) {
assert((addr->getType() == IGF.IGM.WeakReferencePtrTy ||
addr->getType() == IGF.IGM.UnownedReferencePtrTy) &&
"address is not of a weak or unowned reference");
auto cc = IGF.IGM.DefaultCC;
auto fun = dyn_cast<llvm::Function>(fn);
if (fun)
cc = fun->getCallingConv();
// Instead of casting the inputs, we cast the function type.
// This tends to produce less IR, but might be evil.
auto origFnType = getTypeOfFunction(fn);
if (value->getType() != origFnType->getParamType(1)) {
llvm::Type *paramTypes[] = { addr->getType(), value->getType() };
auto resultTy = origFnType->getReturnType() == IGF.IGM.VoidTy
? IGF.IGM.VoidTy
: addr->getType();
llvm::FunctionType *fnType =
llvm::FunctionType::get(resultTy, paramTypes, false);
fn = llvm::ConstantExpr::getBitCast(fn, fnType->getPointerTo());
}
// Emit the call.
llvm::CallInst *call = IGF.Builder.CreateCall(fn, {addr, value});
if (fun && fun->hasParamAttribute(0, llvm::Attribute::Returned))
call->addParamAttr(0, llvm::Attribute::Returned);
call->setCallingConv(cc);
call->setDoesNotThrow();
}
/// Emit a call to swift_retain.
void IRGenFunction::emitNativeStrongRetain(llvm::Value *value,
Atomicity atomicity) {
if (doesNotRequireRefCounting(value))
return;
// Make sure the input pointer is the right type.
if (value->getType() != IGM.RefCountedPtrTy)
value = Builder.CreateBitCast(value, IGM.RefCountedPtrTy);
// Emit the call.
llvm::CallInst *call = Builder.CreateCall(
(atomicity == Atomicity::Atomic) ? IGM.getNativeStrongRetainFn()
: IGM.getNativeNonAtomicStrongRetainFn(),
value);
call->setDoesNotThrow();
call->addParamAttr(0, llvm::Attribute::Returned);
}
/// Emit a store of a live value to the given retaining variable.
void IRGenFunction::emitNativeStrongAssign(llvm::Value *newValue,
Address address) {
// Pull the old value out of the address.
llvm::Value *oldValue = Builder.CreateLoad(address);
// We assume the new value is already retained.
Builder.CreateStore(newValue, address);
// Release the old value.
emitNativeStrongRelease(oldValue, getDefaultAtomicity());
}
/// Emit an initialize of a live value to the given retaining variable.
void IRGenFunction::emitNativeStrongInit(llvm::Value *newValue,
Address address) {
// We assume the new value is already retained.
Builder.CreateStore(newValue, address);
}
/// Emit a release of a live value with the given refcounting implementation.
void IRGenFunction::emitStrongRelease(llvm::Value *value,
ReferenceCounting refcounting,
Atomicity atomicity) {
switch (refcounting) {
case ReferenceCounting::Native:
return emitNativeStrongRelease(value, atomicity);
case ReferenceCounting::ObjC:
return emitObjCStrongRelease(value);
case ReferenceCounting::Block:
return emitBlockRelease(value);
case ReferenceCounting::Unknown:
return emitUnknownStrongRelease(value, atomicity);
case ReferenceCounting::Bridge:
return emitBridgeStrongRelease(value, atomicity);
case ReferenceCounting::Error:
return emitErrorStrongRelease(value);
}
}
void IRGenFunction::emitStrongRetain(llvm::Value *value,
ReferenceCounting refcounting,
Atomicity atomicity) {
switch (refcounting) {
case ReferenceCounting::Native:
emitNativeStrongRetain(value, atomicity);
return;
case ReferenceCounting::Bridge:
emitBridgeStrongRetain(value, atomicity);
return;
case ReferenceCounting::ObjC:
emitObjCStrongRetain(value);
return;
case ReferenceCounting::Block:
emitBlockCopyCall(value);
return;
case ReferenceCounting::Unknown:
emitUnknownStrongRetain(value, atomicity);
return;
case ReferenceCounting::Error:
emitErrorStrongRetain(value);
return;
}
}
llvm::Type *IRGenModule::getReferenceType(ReferenceCounting refcounting) {
switch (refcounting) {
case ReferenceCounting::Native:
return RefCountedPtrTy;
case ReferenceCounting::Bridge:
return BridgeObjectPtrTy;
case ReferenceCounting::ObjC:
return ObjCPtrTy;
case ReferenceCounting::Block:
return ObjCBlockPtrTy;
case ReferenceCounting::Unknown:
return UnknownRefCountedPtrTy;
case ReferenceCounting::Error:
return ErrorPtrTy;
}
llvm_unreachable("Not a valid ReferenceCounting.");
}
#define DEFINE_BINARY_OPERATION(KIND, RESULT, TYPE1, TYPE2) \
RESULT IRGenFunction::emit##KIND(TYPE1 val1, TYPE2 val2, \
ReferenceCounting style) { \
switch (style) { \
case ReferenceCounting::Native: \
return emitNative##KIND(val1, val2); \
case ReferenceCounting::ObjC: \
case ReferenceCounting::Unknown: \
return emitUnknown##KIND(val1, val2); \
case ReferenceCounting::Bridge: \
case ReferenceCounting::Block: \
case ReferenceCounting::Error: \
llvm_unreachable("unsupported reference kind with reference storage"); \
} \
llvm_unreachable("bad refcounting style"); \
}
#define DEFINE_UNARY_OPERATION(KIND, RESULT, TYPE1) \
RESULT IRGenFunction::emit##KIND(TYPE1 val1, ReferenceCounting style) { \
switch (style) { \
case ReferenceCounting::Native: \
return emitNative##KIND(val1); \
case ReferenceCounting::ObjC: \
case ReferenceCounting::Unknown: \
return emitUnknown##KIND(val1); \
case ReferenceCounting::Bridge: \
case ReferenceCounting::Block: \
case ReferenceCounting::Error: \
llvm_unreachable("unsupported reference kind with reference storage"); \
} \
llvm_unreachable("bad refcounting style"); \
}
#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
DEFINE_BINARY_OPERATION(Name##CopyInit, void, Address, Address) \
DEFINE_BINARY_OPERATION(Name##TakeInit, void, Address, Address) \
DEFINE_BINARY_OPERATION(Name##CopyAssign, void, Address, Address) \
DEFINE_BINARY_OPERATION(Name##TakeAssign, void, Address, Address) \
DEFINE_BINARY_OPERATION(Name##Init, void, llvm::Value *, Address) \
DEFINE_BINARY_OPERATION(Name##Assign, void, llvm::Value *, Address) \
DEFINE_BINARY_OPERATION(Name##LoadStrong, llvm::Value *, Address,llvm::Type*)\
DEFINE_BINARY_OPERATION(Name##TakeStrong, llvm::Value *, Address,llvm::Type*)\
DEFINE_UNARY_OPERATION(Name##Destroy, void, Address)
#include "swift/AST/ReferenceStorage.def"
#undef DEFINE_UNARY_OPERATION
#undef DEFINE_BINARY_OPERATION
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \
void IRGenFunction::emit##Name##Retain(llvm::Value *value, \
ReferenceCounting style, \
Atomicity atomicity) { \
assert(style == ReferenceCounting::Native && \
"only native references support scalar reference-counting"); \
emitNative##Name##Retain(value, atomicity); \
} \
void IRGenFunction::emit##Name##Release(llvm::Value *value, \
ReferenceCounting style, \
Atomicity atomicity) { \
assert(style == ReferenceCounting::Native && \
"only native references support scalar reference-counting"); \
emitNative##Name##Release(value, atomicity); \
} \
void IRGenFunction::emitStrongRetain##Name(llvm::Value *value, \
ReferenceCounting style, \
Atomicity atomicity) { \
assert(style == ReferenceCounting::Native && \
"only native references support scalar reference-counting"); \
emitNativeStrongRetain##Name(value, atomicity); \
} \
void IRGenFunction::emitStrongRetainAnd##Name##Release(llvm::Value *value, \
ReferenceCounting style, \
Atomicity atomicity) { \
assert(style == ReferenceCounting::Native && \
"only native references support scalar reference-counting"); \
emitNativeStrongRetainAnd##Name##Release(value, atomicity); \
} \
void IRGenFunction::emitNative##Name##Init(llvm::Value *value, \
Address dest) { \
value = Builder.CreateBitCast(value, IGM.RefCountedPtrTy); \
dest = Builder.CreateStructGEP(dest, 0, Size(0)); \
Builder.CreateStore(value, dest); \
emitNative##Name##Retain(value, getDefaultAtomicity()); \
} \
void IRGenFunction::emitNative##Name##Assign(llvm::Value *value, \
Address dest) { \
value = Builder.CreateBitCast(value, IGM.RefCountedPtrTy); \
dest = Builder.CreateStructGEP(dest, 0, Size(0)); \
auto oldValue = Builder.CreateLoad(dest); \
Builder.CreateStore(value, dest); \
emitNative##Name##Retain(value, getDefaultAtomicity()); \
emitNative##Name##Release(oldValue, getDefaultAtomicity()); \
} \
llvm::Value *IRGenFunction::emitNative##Name##LoadStrong(Address src, \
llvm::Type *type) { \
src = Builder.CreateStructGEP(src, 0, Size(0)); \
llvm::Value *value = Builder.CreateLoad(src); \
value = Builder.CreateBitCast(value, type); \
emitNativeStrongRetain##Name(value, getDefaultAtomicity()); \
return value; \
} \
llvm::Value *IRGenFunction::emitNative##Name##TakeStrong(Address src, \
llvm::Type *type) { \
src = Builder.CreateStructGEP(src, 0, Size(0)); \
llvm::Value *value = Builder.CreateLoad(src); \
value = Builder.CreateBitCast(value, type); \
emitNativeStrongRetainAnd##Name##Release(value, getDefaultAtomicity()); \
return value; \
} \
void IRGenFunction::emitNative##Name##Destroy(Address ref) { \
ref = Builder.CreateStructGEP(ref, 0, Size(0)); \
llvm::Value *value = Builder.CreateLoad(ref); \
emitNative##Name##Release(value, getDefaultAtomicity()); \
} \
void IRGenFunction::emitNative##Name##CopyInit(Address dest, Address src) { \
src = Builder.CreateStructGEP(src, 0, Size(0)); \
dest = Builder.CreateStructGEP(dest, 0, Size(0)); \
llvm::Value *newValue = Builder.CreateLoad(src); \
Builder.CreateStore(newValue, dest); \
emitNative##Name##Retain(newValue, getDefaultAtomicity()); \
} \
void IRGenFunction::emitNative##Name##TakeInit(Address dest, Address src) { \
src = Builder.CreateStructGEP(src, 0, Size(0)); \
dest = Builder.CreateStructGEP(dest, 0, Size(0)); \
llvm::Value *newValue = Builder.CreateLoad(src); \
Builder.CreateStore(newValue, dest); \
} \
void IRGenFunction::emitNative##Name##CopyAssign(Address dest, Address src) { \
src = Builder.CreateStructGEP(src, 0, Size(0)); \
dest = Builder.CreateStructGEP(dest, 0, Size(0)); \
llvm::Value *newValue = Builder.CreateLoad(src); \
llvm::Value *oldValue = Builder.CreateLoad(dest); \
Builder.CreateStore(newValue, dest); \
emitNative##Name##Retain(newValue, getDefaultAtomicity()); \
emitNative##Name##Release(oldValue, getDefaultAtomicity()); \
} \
void IRGenFunction::emitNative##Name##TakeAssign(Address dest, Address src) { \
src = Builder.CreateStructGEP(src, 0, Size(0)); \
dest = Builder.CreateStructGEP(dest, 0, Size(0)); \
llvm::Value *newValue = Builder.CreateLoad(src); \
llvm::Value *oldValue = Builder.CreateLoad(dest); \
Builder.CreateStore(newValue, dest); \
emitNative##Name##Release(oldValue, getDefaultAtomicity()); \
}
#include "swift/AST/ReferenceStorage.def"
/// Emit a release of a live value.
void IRGenFunction::emitNativeStrongRelease(llvm::Value *value,
Atomicity atomicity) {
if (doesNotRequireRefCounting(value))
return;
emitUnaryRefCountCall(*this, (atomicity == Atomicity::Atomic)
? IGM.getNativeStrongReleaseFn()
: IGM.getNativeNonAtomicStrongReleaseFn(),
value);
}
void IRGenFunction::emitNativeSetDeallocating(llvm::Value *value) {
if (doesNotRequireRefCounting(value)) return;
emitUnaryRefCountCall(*this, IGM.getNativeSetDeallocatingFn(), value);
}
llvm::Constant *IRGenModule::getFixLifetimeFn() {
if (FixLifetimeFn)
return FixLifetimeFn;
// Generate a private stub function for the LLVM ARC optimizer to recognize.
auto fixLifetimeTy = llvm::FunctionType::get(VoidTy, RefCountedPtrTy,
/*isVarArg*/ false);
auto fixLifetime = llvm::Function::Create(fixLifetimeTy,
llvm::GlobalValue::PrivateLinkage,
"__swift_fixLifetime",
&Module);
assert(fixLifetime->getName().equals("__swift_fixLifetime")
&& "fixLifetime symbol name got mangled?!");
// Don't inline the function, so it stays as a signal to the ARC passes.
// The ARC passes will remove references to the function when they're
// no longer needed.
fixLifetime->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::NoInline);
// Give the function an empty body.
auto entry = llvm::BasicBlock::Create(LLVMContext, "", fixLifetime);
llvm::ReturnInst::Create(LLVMContext, entry);
FixLifetimeFn = fixLifetime;
return fixLifetime;
}
/// Fix the lifetime of a live value. This communicates to the LLVM level ARC
/// optimizer not to touch this value.
void IRGenFunction::emitFixLifetime(llvm::Value *value) {
// If we aren't running the LLVM ARC optimizer, we don't need to emit this.
if (!IGM.IRGen.Opts.shouldOptimize() ||
IGM.IRGen.Opts.DisableSwiftSpecificLLVMOptzns)
return;
if (doesNotRequireRefCounting(value)) return;
emitUnaryRefCountCall(*this, IGM.getFixLifetimeFn(), value);
}
void IRGenFunction::emitUnknownStrongRetain(llvm::Value *value,
Atomicity atomicity) {
if (doesNotRequireRefCounting(value))
return;
emitUnaryRefCountCall(*this,
(atomicity == Atomicity::Atomic)
? IGM.getUnknownObjectRetainFn()
: IGM.getNonAtomicUnknownObjectRetainFn(),
value);
}
void IRGenFunction::emitUnknownStrongRelease(llvm::Value *value,
Atomicity atomicity) {
if (doesNotRequireRefCounting(value))
return;
emitUnaryRefCountCall(*this,
(atomicity == Atomicity::Atomic)
? IGM.getUnknownObjectReleaseFn()
: IGM.getNonAtomicUnknownObjectReleaseFn(),
value);
}
void IRGenFunction::emitBridgeStrongRetain(llvm::Value *value,
Atomicity atomicity) {
emitUnaryRefCountCall(*this,
(atomicity == Atomicity::Atomic)
? IGM.getBridgeObjectStrongRetainFn()
: IGM.getNonAtomicBridgeObjectStrongRetainFn(),
value);
}
void IRGenFunction::emitBridgeStrongRelease(llvm::Value *value,
Atomicity atomicity) {
emitUnaryRefCountCall(*this,
(atomicity == Atomicity::Atomic)
? IGM.getBridgeObjectStrongReleaseFn()
: IGM.getNonAtomicBridgeObjectStrongReleaseFn(),
value);
}
void IRGenFunction::emitErrorStrongRetain(llvm::Value *value) {
emitUnaryRefCountCall(*this, IGM.getErrorStrongRetainFn(), value);
}
void IRGenFunction::emitErrorStrongRelease(llvm::Value *value) {
emitUnaryRefCountCall(*this, IGM.getErrorStrongReleaseFn(), value);
}
llvm::Value *IRGenFunction::emitLoadRefcountedPtr(Address addr,
ReferenceCounting style) {
Address src =
Builder.CreateBitCast(addr, IGM.getReferenceType(style)->getPointerTo());
return Builder.CreateLoad(src);
}
llvm::Value *IRGenFunction::
emitIsUniqueCall(llvm::Value *value, SourceLoc loc, bool isNonNull) {
llvm::Constant *fn;
if (value->getType() == IGM.RefCountedPtrTy) {
if (isNonNull)
fn = IGM.getIsUniquelyReferenced_nonNull_nativeFn();
else
fn = IGM.getIsUniquelyReferenced_nativeFn();
} else if (value->getType() == IGM.UnknownRefCountedPtrTy) {
if (isNonNull)
fn = IGM.getIsUniquelyReferencedNonObjC_nonNullFn();
else
fn = IGM.getIsUniquelyReferencedNonObjCFn();
} else if (value->getType() == IGM.BridgeObjectPtrTy) {
if (!isNonNull)
unimplemented(loc, "optional bridge ref");
fn = IGM.getIsUniquelyReferencedNonObjC_nonNull_bridgeObjectFn();
} else {
llvm_unreachable("Unexpected LLVM type for a refcounted pointer.");
}
llvm::CallInst *call = Builder.CreateCall(fn, value);
call->setDoesNotThrow();
return call;
}
llvm::Value *IRGenFunction::emitIsEscapingClosureCall(
llvm::Value *value, SourceLoc sourceLoc, unsigned verificationType) {
auto loc = SILLocation::decode(sourceLoc, IGM.Context.SourceMgr);
auto line = llvm::ConstantInt::get(IGM.Int32Ty, loc.Line);
auto col = llvm::ConstantInt::get(IGM.Int32Ty, loc.Column);
// Only output the filepath in debug mode. It is going to leak into the
// executable. This is the same behavior as asserts.
auto filename = IGM.IRGen.Opts.shouldOptimize()
? IGM.getAddrOfGlobalString("")
: IGM.getAddrOfGlobalString(loc.Filename);
auto filenameLength =
llvm::ConstantInt::get(IGM.Int32Ty, loc.Filename.size());
auto type = llvm::ConstantInt::get(IGM.Int32Ty, verificationType);
llvm::CallInst *call =
Builder.CreateCall(IGM.getIsEscapingClosureAtFileLocationFn(),
{value, filename, filenameLength, line, col, type});
call->setDoesNotThrow();
return call;
}
namespace {
/// Basic layout and common operations for box types.
class BoxTypeInfo : public HeapTypeInfo<BoxTypeInfo> {
public:
BoxTypeInfo(IRGenModule &IGM)
: HeapTypeInfo(IGM.RefCountedPtrTy, IGM.getPointerSize(),
IGM.getHeapObjectSpareBits(), IGM.getPointerAlignment())
{}
ReferenceCounting getReferenceCounting() const {
// Boxes are always native-refcounted.
return ReferenceCounting::Native;
}
/// Allocate a box of the given type.
virtual OwnedAddress
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
const llvm::Twine &name) const = 0;
/// Deallocate an uninitialized box.
virtual void
deallocate(IRGenFunction &IGF, llvm::Value *box, SILType boxedType) const = 0;
/// Project the address of the contained value from a box.
virtual Address
project(IRGenFunction &IGF, llvm::Value *box, SILType boxedType) const = 0;
};
/// Common implementation for empty box type info.
class EmptyBoxTypeInfo final : public BoxTypeInfo {
public:
EmptyBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
OwnedAddress
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
const llvm::Twine &name) const override {
return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(),
IGF.emitAllocEmptyBoxCall());
}
void
deallocate(IRGenFunction &IGF, llvm::Value *box, SILType boxedType)
const override {
/* Nothing to do; the box should be nil. */
}
Address
project(IRGenFunction &IGF, llvm::Value *box, SILType boxedType)
const override {
return IGF.getTypeInfo(boxedType).getUndefAddress();
}
};
/// Common implementation for non-fixed box type info.
class NonFixedBoxTypeInfo final : public BoxTypeInfo {
public:
NonFixedBoxTypeInfo(IRGenModule &IGM) : BoxTypeInfo(IGM) {}
OwnedAddress
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
const llvm::Twine &name) const override {
auto &ti = IGF.getTypeInfo(boxedType);
// Use the runtime to allocate a box of the appropriate size.
auto metadata = IGF.emitTypeMetadataRefForLayout(boxedType);
llvm::Value *box, *address;
IGF.emitAllocBoxCall(metadata, box, address);
address = IGF.Builder.CreateBitCast(address,
ti.getStorageType()->getPointerTo());
return {ti.getAddressForPointer(address), box};
}
void
deallocate(IRGenFunction &IGF, llvm::Value *box, SILType boxedType)
const override {
auto metadata = IGF.emitTypeMetadataRefForLayout(boxedType);
IGF.emitDeallocBoxCall(box, metadata);
}
Address
project(IRGenFunction &IGF, llvm::Value *box, SILType boxedType)
const override {
auto &ti = IGF.getTypeInfo(boxedType);
auto metadata = IGF.emitTypeMetadataRefForLayout(boxedType);
llvm::Value *address = IGF.emitProjectBoxCall(box, metadata);
address = IGF.Builder.CreateBitCast(address,
ti.getStorageType()->getPointerTo());
return ti.getAddressForPointer(address);
}
};
/// Base implementation for fixed-sized boxes.
class FixedBoxTypeInfoBase : public BoxTypeInfo {
HeapLayout layout;
public:
FixedBoxTypeInfoBase(IRGenModule &IGM, HeapLayout &&layout)
: BoxTypeInfo(IGM), layout(std::move(layout))
{}
OwnedAddress
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
const llvm::Twine &name)
const override {
// Allocate a new object using the layout.
auto boxedInterfaceType = boxedType;
if (env) {
boxedInterfaceType = boxedType.mapTypeOutOfContext();
}
auto boxDescriptor = IGF.IGM.getAddrOfBoxDescriptor(
boxedInterfaceType.getASTType());
llvm::Value *allocation = IGF.emitUnmanagedAlloc(layout, name,
boxDescriptor);
Address rawAddr = project(IGF, allocation, boxedType);
return {rawAddr, allocation};
}
void
deallocate(IRGenFunction &IGF, llvm::Value *box, SILType _)
const override {
auto size = layout.emitSize(IGF.IGM);
auto alignMask = layout.emitAlignMask(IGF.IGM);
emitDeallocateUninitializedHeapObject(IGF, box, size, alignMask);
}
Address
project(IRGenFunction &IGF, llvm::Value *box, SILType boxedType)
const override {
Address rawAddr = layout.emitCastTo(IGF, box);
rawAddr = layout.getElement(0).project(IGF, rawAddr, None);
auto &ti = IGF.getTypeInfo(boxedType);
return IGF.Builder.CreateBitCast(rawAddr,
ti.getStorageType()->getPointerTo());
}
};
static HeapLayout getHeapLayoutForSingleTypeInfo(IRGenModule &IGM,
const TypeInfo &ti) {
return HeapLayout(IGM, LayoutStrategy::Optimal, SILType(), &ti);
}
/// Common implementation for POD boxes of a known stride and alignment.
class PODBoxTypeInfo final : public FixedBoxTypeInfoBase {
public:
PODBoxTypeInfo(IRGenModule &IGM, Size stride, Alignment alignment)
: FixedBoxTypeInfoBase(IGM, getHeapLayoutForSingleTypeInfo(IGM,
IGM.getOpaqueStorageTypeInfo(stride, alignment))) {
}
};
/// Common implementation for single-refcounted boxes.
class SingleRefcountedBoxTypeInfo final : public FixedBoxTypeInfoBase {
public:
SingleRefcountedBoxTypeInfo(IRGenModule &IGM, ReferenceCounting refcounting)
: FixedBoxTypeInfoBase(IGM, getHeapLayoutForSingleTypeInfo(IGM,
IGM.getReferenceObjectTypeInfo(refcounting)))
{
}
};
/// Implementation of a box for a specific type.
class FixedBoxTypeInfo final : public FixedBoxTypeInfoBase {
public:
FixedBoxTypeInfo(IRGenModule &IGM, SILType T)
: FixedBoxTypeInfoBase(IGM,
HeapLayout(IGM, LayoutStrategy::Optimal, T, &IGM.getTypeInfo(T)))
{}
};
} // end anonymous namespace
const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
// We can share a type info for all dynamic-sized heap metadata.
// TODO: Multi-field boxes
assert(T->getLayout()->getFields().size() == 1
&& "multi-field boxes not implemented yet");
auto &eltTI = IGM.getTypeInfoForLowered(
T->getFieldLoweredType(IGM.getSILModule(), 0));
if (!eltTI.isFixedSize()) {
if (!NonFixedBoxTI)
NonFixedBoxTI = new NonFixedBoxTypeInfo(IGM);
return NonFixedBoxTI;
}
// For fixed-sized types, we can emit concrete box metadata.
auto &fixedTI = cast<FixedTypeInfo>(eltTI);
// Because we assume in enum's that payloads with a Builtin.NativeObject which
// is also the type for indirect enum cases have extra inhabitants of pointers
// we can't have a nil pointer as a representation for an empty box type --
// nil conflicts with the extra inhabitants. We return a static singleton
// empty box object instead.
if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)) {
if (!EmptyBoxTI)
EmptyBoxTI = new EmptyBoxTypeInfo(IGM);
return EmptyBoxTI;
}
// We can share box info for all similarly-shaped POD types.
if (fixedTI.isPOD(ResilienceExpansion::Maximal)) {
auto stride = fixedTI.getFixedStride();
auto align = fixedTI.getFixedAlignment();
auto foundPOD = PODBoxTI.find({stride.getValue(),align.getValue()});
if (foundPOD == PODBoxTI.end()) {
auto newPOD = new PODBoxTypeInfo(IGM, stride, align);
PODBoxTI.insert({{stride.getValue(), align.getValue()}, newPOD});
return newPOD;
}
return foundPOD->second;
}
// We can share box info for all single-refcounted types.
if (fixedTI.isSingleSwiftRetainablePointer(ResilienceExpansion::Maximal)) {
if (!SwiftRetainablePointerBoxTI)
SwiftRetainablePointerBoxTI
= new SingleRefcountedBoxTypeInfo(IGM, ReferenceCounting::Native);
return SwiftRetainablePointerBoxTI;
}
// TODO: Other common shapes? Optional-of-Refcounted would be nice.
// Produce a tailored box metadata for the type.
assert(T->getLayout()->getFields().size() == 1
&& "multi-field boxes not implemented yet");
return new FixedBoxTypeInfo(IGM, T->getFieldType(IGM.getSILModule(), 0));
}
OwnedAddress
irgen::emitAllocateBox(IRGenFunction &IGF, CanSILBoxType boxType,
GenericEnvironment *env,
const llvm::Twine &name) {
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
assert(boxType->getLayout()->getFields().size() == 1
&& "multi-field boxes not implemented yet");
return boxTI.allocate(IGF,
boxType->getFieldType(IGF.IGM.getSILModule(), 0), env,
name);
}
void irgen::emitDeallocateBox(IRGenFunction &IGF,
llvm::Value *box,
CanSILBoxType boxType) {
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
assert(boxType->getLayout()->getFields().size() == 1
&& "multi-field boxes not implemented yet");
return boxTI.deallocate(IGF, box,
boxType->getFieldType(IGF.IGM.getSILModule(), 0));
}
Address irgen::emitProjectBox(IRGenFunction &IGF,
llvm::Value *box,
CanSILBoxType boxType) {
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
assert(boxType->getLayout()->getFields().size() == 1
&& "multi-field boxes not implemented yet");
return boxTI.project(IGF, box,
boxType->getFieldType(IGF.IGM.getSILModule(), 0));
}
Address irgen::emitAllocateExistentialBoxInBuffer(
IRGenFunction &IGF, SILType boxedType, Address destBuffer,
GenericEnvironment *env, const llvm::Twine &name, bool isOutlined) {
// Get a box for the boxed value.
auto boxType = SILBoxType::get(boxedType.getASTType());
auto &boxTI = IGF.getTypeInfoForLowered(boxType).as<BoxTypeInfo>();
OwnedAddress owned = boxTI.allocate(IGF, boxedType, env, name);
Explosion box;
box.add(owned.getOwner());
boxTI.initialize(IGF, box,
Address(IGF.Builder.CreateBitCast(
destBuffer.getAddress(),
owned.getOwner()->getType()->getPointerTo()),
destBuffer.getAlignment()),
isOutlined);
return owned.getAddress();
}
#define DEFINE_VALUE_OP(ID) \
void IRGenFunction::emit##ID(llvm::Value *value, Atomicity atomicity) { \
if (doesNotRequireRefCounting(value)) return; \
emitUnaryRefCountCall(*this, (atomicity == Atomicity::Atomic) \
? IGM.get##ID##Fn() : IGM.getNonAtomic##ID##Fn(), \
value); \
}
#define DEFINE_ADDR_OP(ID) \
void IRGenFunction::emit##ID(Address addr) { \
emitUnaryRefCountCall(*this, IGM.get##ID##Fn(), addr.getAddress()); \
}
#define DEFINE_COPY_OP(ID) \
void IRGenFunction::emit##ID(Address dest, Address src) { \
emitCopyLikeCall(*this, IGM.get##ID##Fn(), dest.getAddress(), \
src.getAddress()); \
}
#define DEFINE_LOAD_WEAK_OP(ID) \
llvm::Value *IRGenFunction::emit##ID(Address src, llvm::Type *type) { \
return emitLoadWeakLikeCall(*this, IGM.get##ID##Fn(), \
src.getAddress(), type); \
}
#define DEFINE_STORE_WEAK_OP(ID) \
void IRGenFunction::emit##ID(llvm::Value *value, Address src) { \
emitStoreWeakLikeCall(*this, IGM.get##ID##Fn(), \
src.getAddress(), value); \
}
#define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, Nativeness) \
DEFINE_LOAD_WEAK_OP(Nativeness##Name##LoadStrong) \
DEFINE_LOAD_WEAK_OP(Nativeness##Name##TakeStrong) \
DEFINE_STORE_WEAK_OP(Nativeness##Name##Init) \
DEFINE_STORE_WEAK_OP(Nativeness##Name##Assign) \
DEFINE_ADDR_OP(Nativeness##Name##Destroy) \
DEFINE_COPY_OP(Nativeness##Name##CopyInit) \
DEFINE_COPY_OP(Nativeness##Name##CopyAssign) \
DEFINE_COPY_OP(Nativeness##Name##TakeInit) \
DEFINE_COPY_OP(Nativeness##Name##TakeAssign)
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, Unknown) \
NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, Native)
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
DEFINE_VALUE_OP(NativeStrongRetain##Name) \
DEFINE_VALUE_OP(NativeStrongRetainAnd##Name##Release) \
DEFINE_VALUE_OP(Native##Name##Release) \
DEFINE_VALUE_OP(Native##Name##Retain)
#define SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER(Name, Unknown) \
ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, "...")
#include "swift/AST/ReferenceStorage.def"
#undef DEFINE_VALUE_OP
#undef DEFINE_ADDR_OP
#undef DEFINE_COPY_OP
#undef DEFINE_LOAD_WEAK_OP
#undef DEFINE_STORE_WEAK_OP
#undef NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE_HELPER
llvm::Value *IRGenFunction::getLocalSelfMetadata() {
assert(LocalSelf && "no local self metadata");
switch (SelfKind) {
case SwiftMetatype:
return LocalSelf;
case ObjCMetatype:
return emitObjCMetadataRefForMetadata(*this, LocalSelf);
case ObjectReference:
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf,
MetatypeRepresentation::Thick);
}
llvm_unreachable("Not a valid LocalSelfKind.");
}
/// Given a non-tagged object pointer, load a pointer to its class object.
llvm::Value *irgen::emitLoadOfObjCHeapMetadataRef(IRGenFunction &IGF,
llvm::Value *object) {
if (IGF.IGM.TargetInfo.hasISAMasking()) {
object = IGF.Builder.CreateBitCast(object,
IGF.IGM.IntPtrTy->getPointerTo());
llvm::Value *metadata =
IGF.Builder.CreateLoad(Address(object, IGF.IGM.getPointerAlignment()));
llvm::Value *mask = IGF.Builder.CreateLoad(IGF.IGM.getAddrOfObjCISAMask());
metadata = IGF.Builder.CreateAnd(metadata, mask);
metadata = IGF.Builder.CreateIntToPtr(metadata, IGF.IGM.TypeMetadataPtrTy);
return metadata;
} else if (IGF.IGM.TargetInfo.hasOpaqueISAs()) {
return emitHeapMetadataRefForUnknownHeapObject(IGF, object);
} else {
object = IGF.Builder.CreateBitCast(object,
IGF.IGM.TypeMetadataPtrTy->getPointerTo());
llvm::Value *metadata =
IGF.Builder.CreateLoad(Address(object, IGF.IGM.getPointerAlignment()));
return metadata;
}
}
/// Given a pointer to a heap object (i.e. definitely not a tagged
/// pointer), load its heap metadata pointer.
static llvm::Value *emitLoadOfHeapMetadataRef(IRGenFunction &IGF,
llvm::Value *object,
IsaEncoding isaEncoding,
bool suppressCast) {
switch (isaEncoding) {
case IsaEncoding::Pointer: {
// Drill into the object pointer. Rather than bitcasting, we make
// an effort to do something that should explode if we get something
// mistyped.
llvm::StructType *structTy =
cast<llvm::StructType>(
cast<llvm::PointerType>(object->getType())->getElementType());
llvm::Value *slot;
// We need a bitcast if we're dealing with an opaque class.
if (structTy->isOpaque()) {
auto metadataPtrPtrTy = IGF.IGM.TypeMetadataPtrTy->getPointerTo();
slot = IGF.Builder.CreateBitCast(object, metadataPtrPtrTy);
// Otherwise, make a GEP.
} else {
auto zero = llvm::ConstantInt::get(IGF.IGM.Int32Ty, 0);
SmallVector<llvm::Value*, 4> indexes;
indexes.push_back(zero);
do {
indexes.push_back(zero);
// Keep drilling down to the first element type.
auto eltTy = structTy->getElementType(0);
assert(isa<llvm::StructType>(eltTy) || eltTy == IGF.IGM.TypeMetadataPtrTy);
structTy = dyn_cast<llvm::StructType>(eltTy);
} while (structTy != nullptr);
slot = IGF.Builder.CreateInBoundsGEP(object, indexes);
if (!suppressCast) {
slot = IGF.Builder.CreateBitCast(slot,
IGF.IGM.TypeMetadataPtrTy->getPointerTo());
}
}
auto metadata = IGF.Builder.CreateLoad(Address(slot,
IGF.IGM.getPointerAlignment()));
if (IGF.IGM.EnableValueNames && object->hasName())
metadata->setName(llvm::Twine(object->getName()) + ".metadata");
return metadata;
}
case IsaEncoding::ObjC: {
// Feed the object pointer to object_getClass.
llvm::Value *objcClass = emitLoadOfObjCHeapMetadataRef(IGF, object);
objcClass = IGF.Builder.CreateBitCast(objcClass, IGF.IGM.TypeMetadataPtrTy);
return objcClass;
}
}
llvm_unreachable("Not a valid IsaEncoding.");
}
/// Given an object of class type, produce the heap metadata reference
/// as an %objc_class*.
llvm::Value *irgen::emitHeapMetadataRefForHeapObject(IRGenFunction &IGF,
llvm::Value *object,
CanType objectType,
bool suppressCast) {
ClassDecl *theClass = objectType.getClassOrBoundGenericClass();
if (theClass && isKnownNotTaggedPointer(IGF.IGM, theClass))
return emitLoadOfHeapMetadataRef(IGF, object,
getIsaEncodingForType(IGF.IGM, objectType),
suppressCast);
// OK, ask the runtime for the class pointer of this potentially-ObjC object.
return emitHeapMetadataRefForUnknownHeapObject(IGF, object);
}
llvm::Value *irgen::emitHeapMetadataRefForHeapObject(IRGenFunction &IGF,
llvm::Value *object,
SILType objectType,
bool suppressCast) {
return emitHeapMetadataRefForHeapObject(IGF, object,
objectType.getASTType(),
suppressCast);
}
/// Given an opaque class instance pointer, produce the type metadata reference
/// as a %type*.
llvm::Value *irgen::emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
llvm::Value *object,
MetatypeRepresentation repr) {
object = IGF.Builder.CreateBitCast(object, IGF.IGM.ObjCPtrTy);
llvm::CallInst *metadata;
switch (repr) {
case MetatypeRepresentation::ObjC:
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassFromObjectFn(),
object,
object->getName() + ".Type");
break;
case MetatypeRepresentation::Thick:
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
object,
object->getName() + ".Type");
break;
case MetatypeRepresentation::Thin:
llvm_unreachable("class metadata can't be thin");
}
metadata->setDoesNotThrow();
metadata->setOnlyReadsMemory();
return metadata;
}
llvm::Value *irgen::
emitHeapMetadataRefForUnknownHeapObject(IRGenFunction &IGF,
llvm::Value *object) {
object = IGF.Builder.CreateBitCast(object, IGF.IGM.ObjCPtrTy);
auto metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectClassFn(),
object,
object->getName() + ".Type");
metadata->setCallingConv(llvm::CallingConv::C);
metadata->setDoesNotThrow();
metadata->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadOnly);
return metadata;
}
/// Given an object of class type, produce the type metadata reference
/// as a %type*.
llvm::Value *irgen::emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
llvm::Value *object,
MetatypeRepresentation repr,
SILType objectType,
bool allowArtificialSubclasses){
switch (auto isaEncoding =
getIsaEncodingForType(IGF.IGM, objectType.getASTType())) {
case IsaEncoding::Pointer:
// Directly load the isa pointer from a pure Swift class.
return emitLoadOfHeapMetadataRef(IGF, object, isaEncoding,
/*suppressCast*/ false);
case IsaEncoding::ObjC:
// A class defined in Swift that inherits from an Objective-C class may
// end up dynamically subclassed by ObjC runtime hackery. The artificial
// subclass isn't a formal Swift type, so isn't appropriate as the result
// of `type(of:)`, but is still a physical subtype of the real class object,
// so can be used for some purposes like satisfying type parameters in
// generic signatures.
if (allowArtificialSubclasses
&& hasKnownSwiftMetadata(IGF.IGM, objectType.getASTType()))
return emitLoadOfHeapMetadataRef(IGF, object, isaEncoding,
/*suppressCast*/ false);
// Ask the Swift runtime to find the dynamic type. This will look through
// dynamic subclasses of Swift classes, and use the -class message for
// ObjC classes.
return emitDynamicTypeOfOpaqueHeapObject(IGF, object, repr);
}
llvm_unreachable("unhandled ISA encoding");
}
/// What isa encoding mechanism does a type have?
IsaEncoding irgen::getIsaEncodingForType(IRGenModule &IGM,
CanType type) {
if (!IGM.ObjCInterop) return IsaEncoding::Pointer;
// This needs to be kept up-to-date with hasKnownSwiftMetadata.
if (auto theClass = type->getClassOrBoundGenericClass()) {
// We can access the isas of pure Swift classes directly.
if (!theClass->checkAncestry(AncestryFlags::ClangImported))
return IsaEncoding::Pointer;
// For ObjC or mixed classes, we need to use object_getClass.
return IsaEncoding::ObjC;
}
// Existentials use the encoding of the enclosed dynamic type.
if (type->isAnyExistentialType()) {
return getIsaEncodingForType(IGM, OpenedArchetypeType::getAny(type));
}
if (auto archetype = dyn_cast<ArchetypeType>(type)) {
// If we have a concrete superclass constraint, just recurse.
if (auto superclass = archetype->getSuperclass()) {
return getIsaEncodingForType(IGM, superclass->getCanonicalType());
}
// Otherwise, we must just have a class constraint. Use the
// conservative answer.
return IsaEncoding::ObjC;
}
// Non-class heap objects should be pure Swift, so we can access their isas
// directly.
return IsaEncoding::Pointer;
}