blob: b2b27ddb639f1931c97619a16df11aa5c2f0a226 [file] [log] [blame]
//===--- GenExistential.cpp - Swift IR Generation for Existential Types ---===//
//
// 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 IR generation for existential types in Swift.
//
//===----------------------------------------------------------------------===//
#include "GenExistential.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/TypeLowering.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "EnumPayload.h"
#include "Explosion.h"
#include "FixedTypeInfo.h"
#include "GenClass.h"
#include "GenHeap.h"
#include "GenMeta.h"
#include "GenOpaque.h"
#include "GenPoly.h"
#include "GenProto.h"
#include "GenType.h"
#include "HeapTypeInfo.h"
#include "IndirectTypeInfo.h"
#include "IRGenDebugInfo.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "Linking.h"
#include "NonFixedTypeInfo.h"
#include "ProtocolInfo.h"
#include "TypeInfo.h"
#include "WeakTypeInfo.h"
using namespace swift;
using namespace irgen;
namespace {
/// The layout of an existential buffer. This is intended to be a
/// small, easily-computed type that can be passed around by value.
class OpaqueExistentialLayout {
private:
unsigned NumTables;
// If you add anything to the layout computation, you might need
// to update certain uses; check the external uses of getNumTables().
// For example, getAssignExistentialsFunction relies on being uniqued
// for different layout kinds.
public:
explicit OpaqueExistentialLayout(unsigned numTables)
: NumTables(numTables) {}
unsigned getNumTables() const { return NumTables; }
Size getSize(IRGenModule &IGM) const {
return getFixedBufferSize(IGM)
+ IGM.getPointerSize() * (getNumTables() + 1);
}
Alignment getAlignment(IRGenModule &IGM) const {
return getFixedBufferAlignment(IGM);
}
// friend bool operator==(ExistentialLayout a, ExistentialLayout b) {
// return a.NumTables == b.NumTables;
// }
/// Given the address of an existential object, drill down to the
/// buffer.
Address projectExistentialBuffer(IRGenFunction &IGF, Address addr) const {
return IGF.Builder.CreateStructGEP(addr, 0, Size(0));
}
/// Given the address of an existential object, drill down to the
/// witness-table field.
Address projectWitnessTable(IRGenFunction &IGF, Address addr,
unsigned which) const {
assert(which < getNumTables());
return IGF.Builder.CreateStructGEP(addr, which + 2,
getFixedBufferSize(IGF.IGM)
+ IGF.IGM.getPointerSize() * (which + 1));
}
/// Given the address of an existential object, load its witness table.
llvm::Value *loadWitnessTable(IRGenFunction &IGF, Address addr,
unsigned which) const {
return IGF.Builder.CreateLoad(projectWitnessTable(IGF, addr, which));
}
/// Given the address of an existential object, drill down to the
/// metadata field.
Address projectMetadataRef(IRGenFunction &IGF, Address addr) {
return IGF.Builder.CreateStructGEP(addr, 1, getFixedBufferSize(IGF.IGM));
}
/// Given the address of an existential object, load its metadata
/// object.
llvm::Value *loadMetadataRef(IRGenFunction &IGF, Address addr) {
return IGF.Builder.CreateLoad(projectMetadataRef(IGF, addr));
}
};
} // end anonymous namespace
/// Given the address of an existential object, destroy it.
static void emitDestroyExistential(IRGenFunction &IGF, Address addr,
OpaqueExistentialLayout layout) {
llvm::Value *metadata = layout.loadMetadataRef(IGF, addr);
Address object = layout.projectExistentialBuffer(IGF, addr);
emitDestroyBufferCall(IGF, metadata, object);
}
static llvm::Constant *getAssignExistentialsFunction(IRGenModule &IGM,
llvm::Type *objectPtrTy,
OpaqueExistentialLayout layout);
static llvm::Constant *getAssignBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy);
static llvm::Constant *getDestroyBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy);
static llvm::Constant *getInitWithTakeBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy);
static llvm::Constant *getInitWithCopyBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy);
static llvm::Constant *
getProjectBoxedOpaqueExistentialFunction(IRGenFunction &IGF,
OpenedExistentialAccess accessKind,
OpaqueExistentialLayout existLayout);
namespace {
/// A helper class for implementing existential type infos that
/// store an existential value of some sort.
template <class Derived, class Base>
class ExistentialTypeInfoBase : public Base,
private llvm::TrailingObjects<Derived, ProtocolEntry> {
friend class llvm::TrailingObjects<Derived, ProtocolEntry>;
/// The number of non-trivial protocols for this existential.
unsigned NumStoredProtocols;
protected:
const Derived &asDerived() const {
return *static_cast<const Derived*>(this);
}
Derived &asDerived() {
return *static_cast<Derived*>(this);
}
template <class... As>
ExistentialTypeInfoBase(ArrayRef<ProtocolEntry> protocols,
As &&...args)
: Base(std::forward<As>(args)...),
NumStoredProtocols(protocols.size()) {
std::uninitialized_copy(protocols.begin(), protocols.end(),
this->template getTrailingObjects<ProtocolEntry>());
}
public:
template <class... As>
static const Derived *
create(ArrayRef<ProtocolEntry> protocols, As &&...args)
{
void *buffer =
operator new(ExistentialTypeInfoBase::template totalSizeToAlloc<ProtocolEntry>(protocols.size()));
return new (buffer) Derived(protocols, std::forward<As>(args)...);
}
/// Returns the number of protocol witness tables directly carried
/// by values of this type.
unsigned getNumStoredProtocols() const { return NumStoredProtocols; }
/// Returns the protocols that values of this type are known to
/// implement. This can be empty, meaning that values of this
/// type are not know to implement any protocols, although we do
/// still know how to manipulate them.
ArrayRef<ProtocolEntry> getStoredProtocols() const {
return {this->template getTrailingObjects<ProtocolEntry>(),
NumStoredProtocols};
}
/// Given an existential object, find the witness table
/// corresponding to the given protocol.
llvm::Value *findWitnessTable(IRGenFunction &IGF,
Explosion &container,
ProtocolDecl *protocol) const {
assert(NumStoredProtocols != 0 &&
"finding a witness table in a trivial existential");
return emitImpliedWitnessTableRef(IGF, getStoredProtocols(), protocol,
[&](unsigned originIndex) {
return asDerived().extractWitnessTable(IGF, container, originIndex);
});
}
/// Given the address of an existential object, find the witness
/// table corresponding to the given protocol.
llvm::Value *findWitnessTable(IRGenFunction &IGF, Address obj,
ProtocolDecl *protocol) const {
assert(NumStoredProtocols != 0 &&
"finding a witness table in a trivial existential");
return emitImpliedWitnessTableRef(IGF, getStoredProtocols(), protocol,
[&](unsigned originIndex) {
return asDerived().loadWitnessTable(IGF, obj, originIndex);
});
}
/// Given the witness table vector from an existential object, find the
/// witness table corresponding to the given protocol.
llvm::Value *findWitnessTable(IRGenFunction &IGF,
ArrayRef<llvm::Value *> witnesses,
ProtocolDecl *protocol) const {
return emitImpliedWitnessTableRef(IGF, getStoredProtocols(), protocol,
[&](unsigned originIndex) {
return witnesses[originIndex];
});
}
/// Given the address of an existential object, find the witness
/// table of a directly-stored witness table.
llvm::Value *loadWitnessTable(IRGenFunction &IGF, Address obj,
unsigned which) const {
return IGF.Builder.CreateLoad(
asDerived().projectWitnessTable(IGF, obj, which));
}
void emitCopyOfTables(IRGenFunction &IGF, Address dest, Address src) const {
if (NumStoredProtocols == 0) return;
Explosion temp;
asDerived().emitLoadOfTables(IGF, src, temp);
asDerived().emitStoreOfTables(IGF, temp, dest);
}
void emitLoadOfTables(IRGenFunction &IGF, Address existential,
Explosion &out) const {
for (unsigned i = 0; i != NumStoredProtocols; ++i) {
auto tableAddr = asDerived().projectWitnessTable(IGF, existential, i);
out.add(IGF.Builder.CreateLoad(tableAddr));
}
}
void emitStoreOfTables(IRGenFunction &IGF, Explosion &in,
Address existential) const {
for (unsigned i = 0; i != NumStoredProtocols; ++i) {
auto tableAddr = asDerived().projectWitnessTable(IGF, existential, i);
IGF.Builder.CreateStore(in.claimNext(), tableAddr);
}
}
};
/// A TypeInfo implementation for existential types, i.e., types like:
/// Printable
/// Printable & Serializable
/// Any
/// with the semantic translation:
/// \exists t : Printable . t
/// t here is an ArchetypeType.
///
/// This is used for both ProtocolTypes and ProtocolCompositionTypes.
class OpaqueExistentialTypeInfo final :
public ExistentialTypeInfoBase<OpaqueExistentialTypeInfo,
IndirectTypeInfo<OpaqueExistentialTypeInfo, FixedTypeInfo>> {
using super =
ExistentialTypeInfoBase<OpaqueExistentialTypeInfo,
IndirectTypeInfo<OpaqueExistentialTypeInfo, FixedTypeInfo>>;
friend super;
// FIXME: We could get spare bits out of the metadata and/or witness
// pointers.
OpaqueExistentialTypeInfo(ArrayRef<ProtocolEntry> protocols,
llvm::Type *ty, Size size, Alignment align)
: super(protocols, ty, size,
SpareBitVector::getConstant(size.getValueInBits(), false), align,
IsNotPOD, IsNotBitwiseTakable, IsFixedSize) {}
public:
OpaqueExistentialLayout getLayout() const {
return OpaqueExistentialLayout(getNumStoredProtocols());
}
Address projectWitnessTable(IRGenFunction &IGF, Address obj,
unsigned index) const {
return getLayout().projectWitnessTable(IGF, obj, index);
}
void assignWithCopy(IRGenFunction &IGF, Address dest, Address src,
SILType T) const override {
auto objPtrTy = dest.getAddress()->getType();
// Use copy-on-write existentials?
if (IGF.IGM.getSILModule().getOptions().UseCOWExistentials) {
auto fn = getAssignBoxedOpaqueExistentialBufferFunction(
IGF.IGM, getLayout(), objPtrTy);
auto call =
IGF.Builder.CreateCall(fn, {dest.getAddress(), src.getAddress()});
call->setCallingConv(IGF.IGM.DefaultCC);
call->setDoesNotThrow();
return;
}
auto fn = getAssignExistentialsFunction(IGF.IGM, objPtrTy, getLayout());
auto call = IGF.Builder.CreateCall(
fn, {dest.getAddress(), src.getAddress()});
call->setCallingConv(IGF.IGM.DefaultCC);
call->setDoesNotThrow();
}
llvm::Value *copyType(IRGenFunction &IGF, Address dest, Address src) const {
auto layout = getLayout();
llvm::Value *metadata = layout.loadMetadataRef(IGF, src);
IGF.Builder.CreateStore(metadata, layout.projectMetadataRef(IGF, dest));
// Load the witness tables and copy them into the new object.
emitCopyOfTables(IGF, dest, src);
return metadata;
}
void initializeWithCopy(IRGenFunction &IGF,
Address dest, Address src,
SILType T) const override {
llvm::Value *metadata = copyType(IGF, dest, src);
auto layout = getLayout();
// Project down to the buffers and ask the witnesses to do a
// copy-initialize.
Address srcBuffer = layout.projectExistentialBuffer(IGF, src);
Address destBuffer = layout.projectExistentialBuffer(IGF, dest);
emitInitializeBufferWithCopyOfBufferCall(IGF, metadata,
destBuffer,
srcBuffer);
}
void initializeWithTake(IRGenFunction &IGF,
Address dest, Address src,
SILType T) const override {
llvm::Value *metadata = copyType(IGF, dest, src);
auto layout = getLayout();
// Project down to the buffers and ask the witnesses to do a
// take-initialize.
Address srcBuffer = layout.projectExistentialBuffer(IGF, src);
Address destBuffer = layout.projectExistentialBuffer(IGF, dest);
emitInitializeBufferWithTakeOfBufferCall(IGF, metadata,
destBuffer,
srcBuffer);
}
void destroy(IRGenFunction &IGF, Address addr, SILType T) const override {
// Use copy-on-write existentials?
if (IGF.IGM.getSILModule().getOptions().UseCOWExistentials) {
auto fn = getDestroyBoxedOpaqueExistentialBufferFunction(
IGF.IGM, getLayout(), addr.getAddress()->getType());
auto call = IGF.Builder.CreateCall(fn, {addr.getAddress()});
call->setCallingConv(IGF.IGM.DefaultCC);
call->setDoesNotThrow();
return;
}
emitDestroyExistential(IGF, addr, getLayout());
}
};
/// A type implementation for address-only reference storage of
/// class existential types.
template <class Impl, class Base>
class AddressOnlyClassExistentialTypeInfoBase :
public ExistentialTypeInfoBase<Impl, IndirectTypeInfo<Impl, Base>> {
using super = ExistentialTypeInfoBase<Impl, IndirectTypeInfo<Impl, Base>>;
using super::asDerived;
using super::emitCopyOfTables;
using super::getNumStoredProtocols;
protected:
const ReferenceCounting Refcounting;
template <class... As>
AddressOnlyClassExistentialTypeInfoBase(ArrayRef<ProtocolEntry> protocols,
ReferenceCounting refcounting,
As &&...args)
: super(protocols, std::forward<As>(args)...),
Refcounting(refcounting) {
}
public:
Address projectWitnessTable(IRGenFunction &IGF, Address container,
unsigned index) const {
assert(index < getNumStoredProtocols());
return IGF.Builder.CreateStructGEP(container, index + 1,
(index + 1) * IGF.IGM.getPointerSize());
}
Address projectValue(IRGenFunction &IGF, Address existential) const {
return IGF.Builder.CreateStructGEP(existential, 0, Size(0),
existential.getAddress()->getName() + ".weakref");
}
void assignWithCopy(IRGenFunction &IGF, Address dest, Address src,
SILType T) const override {
Address destValue = projectValue(IGF, dest);
Address srcValue = projectValue(IGF, src);
asDerived().emitValueAssignWithCopy(IGF, destValue, srcValue);
emitCopyOfTables(IGF, dest, src);
}
void initializeWithCopy(IRGenFunction &IGF,
Address dest, Address src,
SILType T) const override {
Address destValue = projectValue(IGF, dest);
Address srcValue = projectValue(IGF, src);
asDerived().emitValueInitializeWithCopy(IGF, destValue, srcValue);
emitCopyOfTables(IGF, dest, src);
}
void assignWithTake(IRGenFunction &IGF,
Address dest, Address src,
SILType T) const override {
Address destValue = projectValue(IGF, dest);
Address srcValue = projectValue(IGF, src);
asDerived().emitValueAssignWithTake(IGF, destValue, srcValue);
emitCopyOfTables(IGF, dest, src);
}
void initializeWithTake(IRGenFunction &IGF,
Address dest, Address src,
SILType T) const override {
Address destValue = projectValue(IGF, dest);
Address srcValue = projectValue(IGF, src);
asDerived().emitValueInitializeWithTake(IGF, destValue, srcValue);
emitCopyOfTables(IGF, dest, src);
}
void destroy(IRGenFunction &IGF, Address existential,
SILType T) const override {
Address valueAddr = projectValue(IGF, existential);
asDerived().emitValueDestroy(IGF, valueAddr);
}
/// Given an explosion with multiple pointer elements in them, pack them
/// into an enum payload explosion.
/// FIXME: Assumes the explosion is broken into word-sized integer chunks.
/// Should use EnumPayload.
void mergeExplosion(Explosion &In, Explosion &Out, IRGenFunction &IGF)
const {
// We always have at least one entry.
auto *part = In.claimNext();
Out.add(IGF.Builder.CreatePtrToInt(part, IGF.IGM.IntPtrTy));
for (unsigned i = 0; i != getNumStoredProtocols(); ++i) {
part = In.claimNext();
Out.add(IGF.Builder.CreatePtrToInt(part, IGF.IGM.IntPtrTy));
}
}
// Given an exploded enum payload consisting of consecutive word-sized
// chunks, cast them to their underlying component types.
// FIXME: Assumes the payload is word-chunked. Should use
void decomposeExplosion(Explosion &InE, Explosion &OutE,
IRGenFunction &IGF) const {
// The first entry is always the weak*.
llvm::Value *weak = InE.claimNext();
if (Refcounting == ReferenceCounting::Native)
OutE.add(IGF.Builder.CreateBitOrPointerCast(weak,
IGF.IGM.RefCountedPtrTy));
else
OutE.add(IGF.Builder.CreateBitOrPointerCast(weak,
IGF.IGM.UnknownRefCountedPtrTy));
// Collect the witness tables.
for (unsigned i = 0, e = getNumStoredProtocols(); i != e; ++i) {
llvm::Value *witness = InE.claimNext();
OutE.add(IGF.Builder.CreateBitOrPointerCast(witness,
IGF.IGM.WitnessTablePtrTy));
}
}
};
/// A type implementation for 'weak' existential types.
class WeakClassExistentialTypeInfo final :
public AddressOnlyClassExistentialTypeInfoBase<WeakClassExistentialTypeInfo,
WeakTypeInfo> {
public:
WeakClassExistentialTypeInfo(ArrayRef<ProtocolEntry> protocols,
llvm::Type *ty, Size size, Alignment align,
SpareBitVector &&spareBits,
ReferenceCounting refcounting)
: AddressOnlyClassExistentialTypeInfoBase(protocols, refcounting,
ty, size, align,
std::move(spareBits)) {
}
void emitValueAssignWithCopy(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitWeakCopyAssign(dest, src, Refcounting);
}
void emitValueInitializeWithCopy(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitWeakCopyInit(dest, src, Refcounting);
}
void emitValueAssignWithTake(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitWeakTakeAssign(dest, src, Refcounting);
}
void emitValueInitializeWithTake(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitWeakTakeInit(dest, src, Refcounting);
}
void emitValueDestroy(IRGenFunction &IGF, Address addr) const {
IGF.emitWeakDestroy(addr, Refcounting);
}
// These explosions must follow the same schema as
// ClassExistentialTypeInfo, i.e. first the value, then the tables.
void weakLoadStrong(IRGenFunction &IGF, Address existential,
Explosion &out) const override {
Explosion temp;
Address valueAddr = projectValue(IGF, existential);
llvm::Type *resultType = IGF.IGM.getReferenceType(Refcounting);
temp.add(IGF.emitWeakLoadStrong(valueAddr, resultType, Refcounting));
emitLoadOfTables(IGF, existential, temp);
mergeExplosion(temp, out, IGF);
}
void weakTakeStrong(IRGenFunction &IGF, Address existential,
Explosion &out) const override {
Explosion temp;
Address valueAddr = projectValue(IGF, existential);
llvm::Type *resultType = IGF.IGM.getReferenceType(Refcounting);
temp.add(IGF.emitWeakTakeStrong(valueAddr, resultType, Refcounting));
emitLoadOfTables(IGF, existential, temp);
mergeExplosion(temp, out, IGF);
}
void weakInit(IRGenFunction &IGF, Explosion &in,
Address existential) const override {
Explosion temp;
decomposeExplosion(in, temp, IGF);
llvm::Value *value = temp.claimNext();
assert(value->getType() == IGF.IGM.getReferenceType(Refcounting));
emitStoreOfTables(IGF, temp, existential);
Address valueAddr = projectValue(IGF, existential);
IGF.emitWeakInit(value, valueAddr, Refcounting);
}
void weakAssign(IRGenFunction &IGF, Explosion &in,
Address existential) const override {
Explosion temp;
decomposeExplosion(in, temp, IGF);
llvm::Value *value = temp.claimNext();
assert(value->getType() == IGF.IGM.getReferenceType(Refcounting));
emitStoreOfTables(IGF, temp, existential);
Address valueAddr = projectValue(IGF, existential);
IGF.emitWeakAssign(value, valueAddr, Refcounting);
}
};
/// A type implementation for address-only @unowned existential types.
class AddressOnlyUnownedClassExistentialTypeInfo final :
public AddressOnlyClassExistentialTypeInfoBase<
AddressOnlyUnownedClassExistentialTypeInfo,
FixedTypeInfo> {
public:
AddressOnlyUnownedClassExistentialTypeInfo(ArrayRef<ProtocolEntry> protocols,
llvm::Type *ty,
SpareBitVector &&spareBits,
Size size, Alignment align,
ReferenceCounting refcounting)
: AddressOnlyClassExistentialTypeInfoBase(protocols, refcounting,
ty, size, std::move(spareBits),
align, IsNotPOD,
IsNotBitwiseTakable,
IsFixedSize) {
}
void emitValueAssignWithCopy(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitUnownedCopyAssign(dest, src, Refcounting);
}
void emitValueInitializeWithCopy(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitUnownedCopyInit(dest, src, Refcounting);
}
void emitValueAssignWithTake(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitUnownedTakeAssign(dest, src, Refcounting);
}
void emitValueInitializeWithTake(IRGenFunction &IGF,
Address dest, Address src) const {
IGF.emitUnownedTakeInit(dest, src, Refcounting);
}
void emitValueDestroy(IRGenFunction &IGF, Address addr) const {
IGF.emitUnownedDestroy(addr, Refcounting);
}
bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
return true;
}
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
return IGM.getUnownedExtraInhabitantCount(Refcounting);
}
APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
unsigned bits,
unsigned index) const override {
return IGM.getUnownedExtraInhabitantValue(bits, index, Refcounting);
}
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src,
SILType T) const override {
Address valueSrc = projectValue(IGF, src);
return IGF.getUnownedExtraInhabitantIndex(valueSrc, Refcounting);
}
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index,
Address dest, SILType T) const override {
Address valueDest = projectValue(IGF, dest);
return IGF.storeUnownedExtraInhabitant(index, valueDest, Refcounting);
}
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
APInt bits = IGM.getUnownedExtraInhabitantMask(Refcounting);
// Zext out to the size of the existential.
bits = bits.zextOrTrunc(getFixedSize().getValueInBits());
return bits;
}
};
/// A helper class for working with existential types that can be
/// exploded into scalars.
///
/// The subclass must provide:
/// void emitValueRetain(IRGenFunction &IGF, llvm::Value *value) const;
/// void emitValueRelease(IRGenFunction &IGF, llvm::Value *value) const;
/// void emitValueFixLifetime(IRGenFunction &IGF,
/// llvm::Value *value) const;
/// const LoadableTypeInfo &
/// getValueTypeInfoForExtraInhabitants(IRGenModule &IGM) const;
/// The value type info is only used to manage extra inhabitants, so it's
/// okay for it to implement different semantics.
template <class Derived, class Base>
class ScalarExistentialTypeInfoBase :
public ExistentialTypeInfoBase<Derived, ScalarTypeInfo<Derived, Base>> {
using super =
ExistentialTypeInfoBase<Derived, ScalarTypeInfo<Derived, Base>>;
protected:
template <class... T>
ScalarExistentialTypeInfoBase(T &&...args)
: super(std::forward<T>(args)...) {}
using super::asDerived;
public:
/// The storage type of a class existential is a struct containing
/// a refcounted pointer to the class instance value followed by
/// witness table pointers for each conformed-to protocol. Unlike opaque
/// existentials, a class existential does not need to store type
/// metadata as an additional element, since it can be derived from the
/// class instance.
llvm::StructType *getStorageType() const {
return cast<llvm::StructType>(TypeInfo::getStorageType());
}
using super::getNumStoredProtocols;
unsigned getExplosionSize() const final {
return 1 + getNumStoredProtocols();
}
void getSchema(ExplosionSchema &schema) const override {
schema.add(ExplosionSchema::Element::forScalar(asDerived().getValueType()));
llvm::StructType *ty = getStorageType();
for (unsigned i = 1, e = getExplosionSize(); i != e; ++i)
schema.add(ExplosionSchema::Element::forScalar(ty->getElementType(i)));
}
void addToAggLowering(IRGenModule &IGM, SwiftAggLowering &lowering,
Size offset) const override {
auto ptrSize = IGM.getPointerSize();
LoadableTypeInfo::addScalarToAggLowering(IGM, lowering,
asDerived().getValueType(),
offset, ptrSize);
llvm::StructType *ty = getStorageType();
for (unsigned i = 1, e = getExplosionSize(); i != e; ++i)
LoadableTypeInfo::addScalarToAggLowering(IGM, lowering,
ty->getElementType(i),
offset + i * ptrSize, ptrSize);
}
/// Given the address of a class existential container, returns
/// the address of a witness table pointer.
Address projectWitnessTable(IRGenFunction &IGF, Address address,
unsigned n) const {
assert(n < getNumStoredProtocols() && "witness table index out of bounds");
return IGF.Builder.CreateStructGEP(address, n+1,
IGF.IGM.getPointerSize() * (n+1));
}
/// Return the type of the instance value.
llvm::Type *getValueType() const {
return getStorageType()->getElementType(0);
}
/// Given the address of a class existential container, returns
/// the address of its instance pointer.
Address projectValue(IRGenFunction &IGF, Address address) const {
return IGF.Builder.CreateStructGEP(address, 0, Size(0));
}
llvm::Value *loadValue(IRGenFunction &IGF, Address addr) const {
return IGF.Builder.CreateLoad(asDerived().projectValue(IGF, addr));
}
/// Given a class existential container, returns a witness table
/// pointer out of the container, and the type metadata pointer for the
/// value.
llvm::Value *
extractWitnessTable(IRGenFunction &IGF, Explosion &container,
unsigned which) const {
assert(which < getNumStoredProtocols() && "witness table index out of bounds");
ArrayRef<llvm::Value *> values = container.claim(getExplosionSize());
return values[which+1];
}
/// Deconstruct an existential object into witness tables and instance
/// pointer.
std::pair<ArrayRef<llvm::Value*>, llvm::Value*>
getWitnessTablesAndValue(Explosion &container) const {
llvm::Value *instance = container.claimNext();
ArrayRef<llvm::Value*> witnesses = container.claim(getNumStoredProtocols());
return {witnesses, instance};
}
/// Given an existential object, returns the payload value.
llvm::Value *getValue(IRGenFunction &IGF, Explosion &container) const {
llvm::Value *instance = container.claimNext();
(void)container.claim(getNumStoredProtocols());
return instance;
}
void loadAsCopy(IRGenFunction &IGF, Address address,
Explosion &out) const override {
// Load the instance pointer, which is unknown-refcounted.
llvm::Value *instance = asDerived().loadValue(IGF, address);
asDerived().emitValueRetain(IGF, instance, IGF.getDefaultAtomicity());
out.add(instance);
// Load the witness table pointers.
asDerived().emitLoadOfTables(IGF, address, out);
}
void loadAsTake(IRGenFunction &IGF, Address address,
Explosion &e) const override {
// Load the instance pointer.
e.add(asDerived().loadValue(IGF, address));
// Load the witness table pointers.
asDerived().emitLoadOfTables(IGF, address, e);
}
void assign(IRGenFunction &IGF, Explosion &e,
Address address) const override {
// Assign the value.
Address instanceAddr = asDerived().projectValue(IGF, address);
llvm::Value *old = IGF.Builder.CreateLoad(instanceAddr);
IGF.Builder.CreateStore(e.claimNext(), instanceAddr);
asDerived().emitValueRelease(IGF, old, IGF.getDefaultAtomicity());
// Store the witness table pointers.
asDerived().emitStoreOfTables(IGF, e, address);
}
void initialize(IRGenFunction &IGF, Explosion &e,
Address address) const override {
// Store the instance pointer.
IGF.Builder.CreateStore(e.claimNext(),
asDerived().projectValue(IGF, address));
// Store the witness table pointers.
asDerived().emitStoreOfTables(IGF, e, address);
}
void copy(IRGenFunction &IGF, Explosion &src, Explosion &dest,
Atomicity atomicity)
const override {
// Copy the instance pointer.
llvm::Value *value = src.claimNext();
dest.add(value);
asDerived().emitValueRetain(IGF, value, atomicity);
// Transfer the witness table pointers.
src.transferInto(dest, getNumStoredProtocols());
}
void consume(IRGenFunction &IGF, Explosion &src, Atomicity atomicity)
const override {
// Copy the instance pointer.
llvm::Value *value = src.claimNext();
asDerived().emitValueRelease(IGF, value, atomicity);
// Throw out the witness table pointers.
(void)src.claim(getNumStoredProtocols());
}
void fixLifetime(IRGenFunction &IGF, Explosion &src) const override {
// Copy the instance pointer.
llvm::Value *value = src.claimNext();
asDerived().emitValueFixLifetime(IGF, value);
// Throw out the witness table pointers.
(void)src.claim(getNumStoredProtocols());
}
void destroy(IRGenFunction &IGF, Address addr, SILType T) const override {
llvm::Value *value = asDerived().loadValue(IGF, addr);
asDerived().emitValueRelease(IGF, value, IGF.getDefaultAtomicity());
}
void packIntoEnumPayload(IRGenFunction &IGF,
EnumPayload &payload,
Explosion &src,
unsigned offset) const override {
payload.insertValue(IGF, src.claimNext(), offset);
auto wordSize = IGF.IGM.getPointerSize().getValueInBits();
for (unsigned i = 0; i < getNumStoredProtocols(); ++i) {
offset += wordSize;
payload.insertValue(IGF, src.claimNext(), offset);
}
}
void unpackFromEnumPayload(IRGenFunction &IGF,
const EnumPayload &payload,
Explosion &dest,
unsigned offset) const override {
ExplosionSchema schema;
getSchema(schema);
dest.add(payload.extractValue(IGF, schema[0].getScalarType(), offset));
auto wordSize = IGF.IGM.getPointerSize().getValueInBits();
for (unsigned i = 0; i < getNumStoredProtocols(); ++i) {
offset += wordSize;
dest.add(payload.extractValue(IGF, IGF.IGM.WitnessTablePtrTy, offset));
}
}
// Extra inhabitants of the various scalar existential containers.
// We use the heap object extra inhabitants over the class pointer value.
// We could get even more extra inhabitants from the witness table
// pointer(s), but it's unlikely we would ever need to.
bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
assert(asDerived().getValueTypeInfoForExtraInhabitants(IGM)
.mayHaveExtraInhabitants(IGM));
return true;
}
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
return asDerived().getValueTypeInfoForExtraInhabitants(IGM)
.getFixedExtraInhabitantCount(IGM);
}
APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
unsigned bits,
unsigned index) const override {
// Note that we pass down the original bit-width.
return asDerived().getValueTypeInfoForExtraInhabitants(IGM)
.getFixedExtraInhabitantValue(IGM, bits, index);
}
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src,
SILType T)
const override {
// NB: We assume that the witness table slots are zero if an extra
// inhabitant is stored in the container.
src = projectValue(IGF, src);
return asDerived().getValueTypeInfoForExtraInhabitants(IGF.IGM)
.getExtraInhabitantIndex(IGF, src, SILType());
}
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index,
Address dest, SILType T) const override {
Address valueDest = projectValue(IGF, dest);
asDerived().getValueTypeInfoForExtraInhabitants(IGF.IGM)
.storeExtraInhabitant(IGF, index, valueDest, SILType());
}
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
// Ask the value type for its mask.
APInt bits = asDerived().getValueTypeInfoForExtraInhabitants(IGM)
.getFixedExtraInhabitantMask(IGM);
// Zext out to the size of the existential.
bits = bits.zextOrTrunc(asDerived().getFixedSize().getValueInBits());
return bits;
}
};
/// A type implementation for loadable [unowned] class existential types.
class LoadableUnownedClassExistentialTypeInfo final
: public ScalarExistentialTypeInfoBase<
LoadableUnownedClassExistentialTypeInfo,
LoadableTypeInfo> {
ReferenceCounting Refcounting;
llvm::Type *ValueType;
public:
LoadableUnownedClassExistentialTypeInfo(
ArrayRef<ProtocolEntry> storedProtocols,
llvm::Type *valueTy,
llvm::Type *ty,
const SpareBitVector &spareBits,
Size size, Alignment align,
ReferenceCounting refcounting)
: ScalarExistentialTypeInfoBase(storedProtocols, ty, size,
spareBits, align, IsNotPOD, IsFixedSize),
Refcounting(refcounting), ValueType(valueTy) {
assert(refcounting == ReferenceCounting::Native ||
refcounting == ReferenceCounting::Unknown);
}
llvm::Type *getValueType() const {
return ValueType;
}
Address projectValue(IRGenFunction &IGF, Address addr) const {
Address valueAddr = ScalarExistentialTypeInfoBase::projectValue(IGF, addr);
return IGF.Builder.CreateBitCast(valueAddr, ValueType->getPointerTo());
}
void emitValueRetain(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
IGF.emitUnownedRetain(value, Refcounting, atomicity);
}
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
IGF.emitUnownedRelease(value, Refcounting, atomicity);
}
void emitValueFixLifetime(IRGenFunction &IGF, llvm::Value *value) const {
IGF.emitFixLifetime(value);
}
const LoadableTypeInfo &
getValueTypeInfoForExtraInhabitants(IRGenModule &IGM) const {
llvm_unreachable("should have overridden all actual uses of this");
}
bool mayHaveExtraInhabitants(IRGenModule &IGM) const override {
return true;
}
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
return IGM.getUnownedExtraInhabitantCount(Refcounting);
}
APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
unsigned bits,
unsigned index) const override {
return IGM.getUnownedExtraInhabitantValue(bits, index, Refcounting);
}
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF, Address src,
SILType T) const override {
Address valueSrc = projectValue(IGF, src);
return IGF.getUnownedExtraInhabitantIndex(valueSrc, Refcounting);
}
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index,
Address dest, SILType T) const override {
Address valueDest = projectValue(IGF, dest);
return IGF.storeUnownedExtraInhabitant(index, valueDest, Refcounting);
}
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
APInt bits = IGM.getUnownedExtraInhabitantMask(Refcounting);
// Zext out to the size of the existential.
bits = bits.zextOrTrunc(asDerived().getFixedSize().getValueInBits());
return bits;
}
};
/// A type implementation for @unowned(unsafe) class existential types.
class UnmanagedClassExistentialTypeInfo final
: public ScalarExistentialTypeInfoBase<UnmanagedClassExistentialTypeInfo,
LoadableTypeInfo> {
public:
UnmanagedClassExistentialTypeInfo(ArrayRef<ProtocolEntry> storedProtocols,
llvm::Type *ty,
const SpareBitVector &spareBits,
Size size, Alignment align)
: ScalarExistentialTypeInfoBase(storedProtocols, ty, size,
spareBits, align, IsPOD, IsFixedSize) {}
const LoadableTypeInfo &
getValueTypeInfoForExtraInhabitants(IRGenModule &IGM) const {
if (!IGM.ObjCInterop)
return IGM.getNativeObjectTypeInfo();
else
return IGM.getUnknownObjectTypeInfo();
}
void emitValueRetain(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
// do nothing
}
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
// do nothing
}
void emitValueFixLifetime(IRGenFunction &IGF, llvm::Value *value) const {
// do nothing
}
};
/// A type info implementation for class existential types, that is,
/// an existential type known to conform to one or more class protocols.
/// Class existentials can be represented directly as an aggregation
/// of a refcounted pointer plus witness tables instead of using an indirect
/// buffer.
class ClassExistentialTypeInfo final
: public ScalarExistentialTypeInfoBase<ClassExistentialTypeInfo,
ReferenceTypeInfo>
{
ReferenceCounting Refcounting;
friend ExistentialTypeInfoBase;
ClassExistentialTypeInfo(ArrayRef<ProtocolEntry> protocols,
llvm::Type *ty,
Size size,
SpareBitVector &&spareBits,
Alignment align,
ReferenceCounting refcounting)
: ScalarExistentialTypeInfoBase(protocols, ty, size,
std::move(spareBits), align),
Refcounting(refcounting) {
assert(refcounting == ReferenceCounting::Native ||
refcounting == ReferenceCounting::Unknown);
}
public:
bool isSingleRetainablePointer(ResilienceExpansion expansion,
ReferenceCounting *refcounting) const override{
if (refcounting) *refcounting = Refcounting;
return getNumStoredProtocols() == 0;
}
const LoadableTypeInfo &
getValueTypeInfoForExtraInhabitants(IRGenModule &IGM) const {
if (Refcounting == ReferenceCounting::Native)
return IGM.getNativeObjectTypeInfo();
else
return IGM.getUnknownObjectTypeInfo();
}
void strongRetain(IRGenFunction &IGF, Explosion &e,
Atomicity atomicity) const override {
IGF.emitStrongRetain(e.claimNext(), Refcounting, atomicity);
(void)e.claim(getNumStoredProtocols());
}
void strongRelease(IRGenFunction &IGF, Explosion &e,
Atomicity atomicity) const override {
IGF.emitStrongRelease(e.claimNext(), Refcounting, atomicity);
(void)e.claim(getNumStoredProtocols());
}
void strongRetainUnowned(IRGenFunction &IGF, Explosion &e,
Atomicity atomicity) const override {
IGF.emitStrongRetainUnowned(e.claimNext(), Refcounting, atomicity);
(void)e.claim(getNumStoredProtocols());
}
void strongRetainUnownedRelease(IRGenFunction &IGF,
Explosion &e,
Atomicity atomicity) const override {
IGF.emitStrongRetainAndUnownedRelease(e.claimNext(), Refcounting,
atomicity);
(void)e.claim(getNumStoredProtocols());
}
void unownedRetain(IRGenFunction &IGF, Explosion &e,
Atomicity atomicity) const override {
IGF.emitUnownedRetain(e.claimNext(), Refcounting, atomicity);
(void)e.claim(getNumStoredProtocols());
}
void unownedRelease(IRGenFunction &IGF, Explosion &e,
Atomicity atomicity) const override {
IGF.emitUnownedRelease(e.claimNext(), Refcounting, atomicity);
(void)e.claim(getNumStoredProtocols());
}
void unownedLoadStrong(IRGenFunction &IGF, Address existential,
Explosion &out) const override {
Address valueAddr = projectValue(IGF, existential);
out.add(IGF.emitUnownedLoadStrong(valueAddr,
IGF.IGM.getReferenceType(Refcounting),
Refcounting));
emitLoadOfTables(IGF, existential, out);
}
void unownedTakeStrong(IRGenFunction &IGF, Address existential,
Explosion &out) const override {
Address valueAddr = projectValue(IGF, existential);
out.add(IGF.emitUnownedTakeStrong(valueAddr,
IGF.IGM.getReferenceType(Refcounting),
Refcounting));
emitLoadOfTables(IGF, existential, out);
}
void unownedInit(IRGenFunction &IGF, Explosion &in,
Address existential) const override {
llvm::Value *value = in.claimNext();
emitStoreOfTables(IGF, in, existential);
Address valueAddr = projectValue(IGF, existential);
IGF.emitUnownedInit(value, valueAddr, Refcounting);
}
void unownedAssign(IRGenFunction &IGF, Explosion &in,
Address existential) const override {
llvm::Value *value = in.claimNext();
emitStoreOfTables(IGF, in, existential);
Address valueAddr = projectValue(IGF, existential);
IGF.emitUnownedAssign(value, valueAddr, Refcounting);
}
void emitValueRetain(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
IGF.emitStrongRetain(value, Refcounting, atomicity);
}
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
IGF.emitStrongRelease(value, Refcounting, atomicity);
}
void emitValueFixLifetime(IRGenFunction &IGF, llvm::Value *value) const {
IGF.emitFixLifetime(value);
}
LoadedRef loadRefcountedPtr(IRGenFunction &IGF, SourceLoc loc,
Address existential) const override {
Address valueAddr = projectValue(IGF, existential);
return LoadedRef(IGF.emitLoadRefcountedPtr(valueAddr, Refcounting), true);
}
const TypeInfo *
createUnownedStorageType(TypeConverter &TC) const override {
// We can just re-use the storage type for the @unowned(safe) type.
SpareBitVector spareBits =
TC.IGM.getUnownedReferenceSpareBits(Refcounting);
for (unsigned i = 0, e = getNumStoredProtocols(); i != e; ++i)
spareBits.append(TC.IGM.getWitnessTablePtrSpareBits());
auto storageTy = buildReferenceStorageType(TC.IGM,
TC.IGM.UnownedReferencePtrTy->getElementType());
if (TC.IGM.isUnownedReferenceAddressOnly(Refcounting)) {
return AddressOnlyUnownedClassExistentialTypeInfo::create(
getStoredProtocols(),
storageTy,
std::move(spareBits),
getFixedSize(),
getFixedAlignment(),
Refcounting);
} else {
return LoadableUnownedClassExistentialTypeInfo::create(
getStoredProtocols(),
getValueType(),
storageTy,
std::move(spareBits),
getFixedSize(),
getFixedAlignment(),
Refcounting);
}
}
const TypeInfo *
createUnmanagedStorageType(TypeConverter &TC) const override {
// We can just re-use the storage type for the @unowned(unsafe) type.
return UnmanagedClassExistentialTypeInfo::create(getStoredProtocols(),
getStorageType(),
getSpareBits(),
getFixedSize(),
getFixedAlignment());
}
const WeakTypeInfo *
createWeakStorageType(TypeConverter &TC) const override {
Size size = TC.IGM.getWeakReferenceSize()
+ getNumStoredProtocols() * TC.IGM.getPointerSize();
Alignment align = TC.IGM.getWeakReferenceAlignment();
assert(align == TC.IGM.getPointerAlignment() &&
"[weak] alignment not pointer alignment; fix existential layout");
(void)align;
auto storageTy = buildReferenceStorageType(TC.IGM,
TC.IGM.WeakReferencePtrTy->getElementType());
SpareBitVector spareBits = TC.IGM.getWeakReferenceSpareBits();
for (unsigned i = 0, e = getNumStoredProtocols(); i != e; ++i)
spareBits.append(TC.IGM.getWitnessTablePtrSpareBits());
return WeakClassExistentialTypeInfo::create(getStoredProtocols(),
storageTy, size, align,
std::move(spareBits),
Refcounting);
}
llvm::StructType *buildReferenceStorageType(IRGenModule &IGM,
llvm::Type *refStorageTy) const {
SmallVector<llvm::Type*, 8> fieldTys;
fieldTys.push_back(refStorageTy);
fieldTys.resize(getNumStoredProtocols() + 1, IGM.WitnessTablePtrTy);
auto storageTy = llvm::StructType::get(IGM.getLLVMContext(), fieldTys);
return storageTy;
}
};
/// A type implementation for existential metatypes.
class ExistentialMetatypeTypeInfo final
: public ScalarExistentialTypeInfoBase<ExistentialMetatypeTypeInfo,
LoadableTypeInfo> {
const LoadableTypeInfo &MetatypeTI;
friend ExistentialTypeInfoBase;
ExistentialMetatypeTypeInfo(ArrayRef<ProtocolEntry> storedProtocols,
llvm::Type *ty, Size size,
SpareBitVector &&spareBits,
Alignment align,
const LoadableTypeInfo &metatypeTI)
: ScalarExistentialTypeInfoBase(storedProtocols, ty, size,
std::move(spareBits), align, IsPOD,
IsFixedSize),
MetatypeTI(metatypeTI) {}
public:
const LoadableTypeInfo &
getValueTypeInfoForExtraInhabitants(IRGenModule &IGM) const {
return MetatypeTI;
}
void emitValueRetain(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
// do nothing
}
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value,
Atomicity atomicity) const {
// do nothing
}
void emitValueFixLifetime(IRGenFunction &IGF, llvm::Value *value) const {
// do nothing
}
};
/// Type info for error existentials, currently the only kind of boxed
/// existential.
class ErrorExistentialTypeInfo : public HeapTypeInfo<ErrorExistentialTypeInfo>
{
ProtocolEntry ErrorEntry;
ReferenceCounting Refcounting;
public:
ErrorExistentialTypeInfo(llvm::PointerType *storage,
Size size, SpareBitVector spareBits,
Alignment align,
const ProtocolEntry &errorProtocolEntry,
ReferenceCounting refcounting)
: HeapTypeInfo(storage, size, spareBits, align),
ErrorEntry(errorProtocolEntry),
Refcounting(refcounting) {}
ReferenceCounting getReferenceCounting() const {
// Error uses its own RC entry points.
return Refcounting;
}
ArrayRef<ProtocolEntry> getStoredProtocols() const {
return ErrorEntry;
}
};
} // end anonymous namespace
static const TypeInfo *createErrorExistentialTypeInfo(IRGenModule &IGM,
ArrayRef<ProtocolDecl*> protocols) {
// The Error existential has a special boxed representation. It has
// space only for witnesses to the Error protocol.
assert(protocols.size() == 1
&& *protocols[0]->getKnownProtocolKind() == KnownProtocolKind::Error);
const ProtocolInfo &impl = IGM.getProtocolInfo(protocols[0]);
auto refcounting = (!IGM.ObjCInterop
? ReferenceCounting::Native
: ReferenceCounting::Error);
return new ErrorExistentialTypeInfo(IGM.ErrorPtrTy,
IGM.getPointerSize(),
IGM.getHeapObjectSpareBits(),
IGM.getPointerAlignment(),
ProtocolEntry(protocols[0], impl),
refcounting);
}
static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T,
ArrayRef<ProtocolDecl*> protocols) {
SmallVector<llvm::Type*, 5> fields;
SmallVector<ProtocolEntry, 4> entries;
// Check for special existentials.
if (protocols.size() == 1) {
switch (getSpecialProtocolID(protocols[0])) {
case SpecialProtocol::Error:
// Error has a special runtime representation.
return createErrorExistentialTypeInfo(IGM, protocols);
// Other existentials have standard representations.
case SpecialProtocol::AnyObject:
case SpecialProtocol::None:
break;
}
}
llvm::StructType *type;
if (isa<ProtocolType>(T))
type = IGM.createNominalType(T);
else if (auto compT = dyn_cast<ProtocolCompositionType>(T))
// Protocol composition types are not nominal, but we name them anyway.
type = IGM.createNominalType(compT.getPointer());
else
llvm_unreachable("unknown existential type kind");
assert(type->isOpaque() && "creating existential type in concrete struct");
// In an opaque metadata, the first two fields are the fixed buffer
// followed by the metadata reference. In a class metadata, the
// first field is the class instance.
//
// Leave space in the buffer for both, but make sure we set it up later.
fields.push_back(nullptr);
fields.push_back(nullptr);
bool requiresClass = false;
bool allowsTaggedPointers = true;
for (auto protocol : protocols) {
// The existential container is class-constrained if any of its protocol
// constraints are.
requiresClass |= protocol->requiresClass();
if (protocol->getAttrs().hasAttribute<UnsafeNoObjCTaggedPointerAttr>())
allowsTaggedPointers = false;
// ObjC protocols need no layout or witness table info. All dispatch is done
// through objc_msgSend.
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
continue;
// Find the protocol layout.
const ProtocolInfo &impl = IGM.getProtocolInfo(protocol);
entries.push_back(ProtocolEntry(protocol, impl));
// Each protocol gets a witness table.
fields.push_back(IGM.WitnessTablePtrTy);
}
// If the existential is class, lower it to a class
// existential representation.
if (requiresClass) {
// If we're not using the Objective-C runtime, we can use the
// native reference counting entry points.
ReferenceCounting refcounting;
// Replace the type metadata pointer with the class instance.
if (!IGM.ObjCInterop) {
refcounting = ReferenceCounting::Native;
fields[1] = IGM.RefCountedPtrTy;
} else {
refcounting = ReferenceCounting::Unknown;
fields[1] = IGM.UnknownRefCountedPtrTy;
}
auto classFields = llvm::makeArrayRef(fields).slice(1);
type->setBody(classFields);
Alignment align = IGM.getPointerAlignment();
Size size = classFields.size() * IGM.getPointerSize();
SpareBitVector spareBits;
// The class pointer is an unknown heap object, so it may be a tagged
// pointer, if the platform has those.
if (allowsTaggedPointers && IGM.TargetInfo.hasObjCTaggedPointers()) {
spareBits.appendClearBits(IGM.getPointerSize().getValueInBits());
} else {
// If the platform doesn't use ObjC tagged pointers, we can go crazy.
spareBits.append(IGM.getHeapObjectSpareBits());
}
for (unsigned i = 1, e = classFields.size(); i < e; ++i) {
spareBits.append(IGM.getWitnessTablePtrSpareBits());
}
return ClassExistentialTypeInfo::create(entries, type,
size, std::move(spareBits), align,
refcounting);
}
// Set up the first two fields.
fields[0] = IGM.getFixedBufferTy();
fields[1] = IGM.TypeMetadataPtrTy;
type->setBody(fields);
OpaqueExistentialLayout layout(entries.size());
Alignment align = layout.getAlignment(IGM);
Size size = layout.getSize(IGM);
return OpaqueExistentialTypeInfo::create(entries, type, size, align);
}
const TypeInfo *TypeConverter::convertProtocolType(ProtocolType *T) {
// Protocol types are nominal.
return createExistentialTypeInfo(IGM, CanType(T), T->getDecl());
}
const TypeInfo *
TypeConverter::convertProtocolCompositionType(ProtocolCompositionType *T) {
// Find the canonical protocols. There might not be any.
SmallVector<ProtocolDecl*, 4> protocols;
T->getAnyExistentialTypeProtocols(protocols);
return createExistentialTypeInfo(IGM, CanType(T), protocols);
}
const TypeInfo *
TypeConverter::convertExistentialMetatypeType(ExistentialMetatypeType *T) {
assert(T->hasRepresentation() &&
"metatype should have been assigned a representation by SIL");
SmallVector<ProtocolDecl*, 4> protocols;
T->getAnyExistentialTypeProtocols(protocols);
SmallVector<ProtocolEntry, 4> entries;
SmallVector<llvm::Type*, 4> fields;
SpareBitVector spareBits;
assert(T->getRepresentation() != MetatypeRepresentation::Thin &&
"existential metatypes cannot have thin representation");
auto &baseTI = getMetatypeTypeInfo(T->getRepresentation());
fields.push_back(baseTI.getStorageType());
spareBits.append(baseTI.getSpareBits());
for (auto protocol : protocols) {
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
continue;
// Find the protocol layout.
const ProtocolInfo &impl = IGM.getProtocolInfo(protocol);
entries.push_back(ProtocolEntry(protocol, impl));
// Each protocol gets a witness table.
fields.push_back(IGM.WitnessTablePtrTy);
spareBits.append(IGM.getWitnessTablePtrSpareBits());
}
llvm::StructType *type = llvm::StructType::get(IGM.getLLVMContext(), fields);
Size size = IGM.getPointerSize() * fields.size();
Alignment align = IGM.getPointerAlignment();
return ExistentialMetatypeTypeInfo::create(entries, type, size,
std::move(spareBits),
align, baseTI);
}
/// Return a function which performs an assignment operation on two
/// existentials.
///
/// Existential types are nominal, so we potentially need to cast the
/// function to the appropriate object-pointer type.
static llvm::Constant *getAssignExistentialsFunction(IRGenModule &IGM,
llvm::Type *objectPtrTy,
OpaqueExistentialLayout layout) {
llvm::Type *argTys[] = { objectPtrTy, objectPtrTy };
// __swift_assign_existentials_N is the well-known function for
// assigning existential types with N witness tables.
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_assign_existentials_" << layout.getNumTables();
return IGM.getOrCreateHelperFunction(fnName, IGM.VoidTy, argTys,
[&](IRGenFunction &IGF) {
auto it = IGF.CurFn->arg_begin();
Address dest(&*(it++), getFixedBufferAlignment(IGM));
Address src(&*(it++), getFixedBufferAlignment(IGM));
// If doing a self-assignment, we're done.
llvm::BasicBlock *doneBB = IGF.createBasicBlock("done");
llvm::BasicBlock *contBB = IGF.createBasicBlock("cont");
llvm::Value *isSelfAssign =
IGF.Builder.CreateICmpEQ(dest.getAddress(), src.getAddress(),
"isSelfAssign");
IGF.Builder.CreateCondBr(isSelfAssign, doneBB, contBB);
// Project down to the buffers.
IGF.Builder.emitBlock(contBB);
// We don't need a ConditionalDominanceScope here because (1) there's no
// code in the other condition and (2) we immediately return.
Address destBuffer = layout.projectExistentialBuffer(IGF, dest);
Address srcBuffer = layout.projectExistentialBuffer(IGF, src);
// Load the metadata tables.
Address destMetadataSlot = layout.projectMetadataRef(IGF, dest);
llvm::Value *destMetadata = IGF.Builder.CreateLoad(destMetadataSlot);
llvm::Value *srcMetadata = layout.loadMetadataRef(IGF, src);
// Check whether the metadata match.
llvm::BasicBlock *matchBB = IGF.createBasicBlock("match");
llvm::BasicBlock *noMatchBB = IGF.createBasicBlock("no-match");
llvm::Value *sameMetadata =
IGF.Builder.CreateICmpEQ(destMetadata, srcMetadata, "sameMetadata");
IGF.Builder.CreateCondBr(sameMetadata, matchBB, noMatchBB);
// If so, do a direct assignment.
IGF.Builder.emitBlock(matchBB);
{
ConditionalDominanceScope matchCondition(IGF);
llvm::Value *destObject =
emitProjectBufferCall(IGF, destMetadata, destBuffer);
llvm::Value *srcObject =
emitProjectBufferCall(IGF, destMetadata, srcBuffer);
emitAssignWithCopyCall(IGF, destMetadata,
Address(destObject, Alignment(1)),
Address(srcObject, Alignment(1)));
IGF.Builder.CreateBr(doneBB);
}
// Otherwise, destroy and copy-initialize.
// TODO: should we copy-initialize and then destroy? That's
// possible if we copy aside, which is a small expense but
// always safe. Otherwise the destroy (which can invoke user code)
// could see invalid memory at this address. These are basically
// the madnesses that boost::variant has to go through, with the
// advantage of address-invariance.
IGF.Builder.emitBlock(noMatchBB);
{
ConditionalDominanceScope noMatchCondition(IGF);
// Store the metadata ref.
IGF.Builder.CreateStore(srcMetadata, destMetadataSlot);
// Store the protocol witness tables.
unsigned numTables = layout.getNumTables();
for (unsigned i = 0, e = numTables; i != e; ++i) {
Address destTableSlot = layout.projectWitnessTable(IGF, dest, i);
llvm::Value *srcTable = layout.loadWitnessTable(IGF, src, i);
// Overwrite the old witness table.
IGF.Builder.CreateStore(srcTable, destTableSlot);
}
// Destroy the old value.
emitDestroyBufferCall(IGF, destMetadata, destBuffer);
// Copy-initialize with the new value. Again, pull a value
// witness table from the source metadata if we can't use a
// protocol witness table.
emitInitializeBufferWithCopyOfBufferCall(IGF, srcMetadata,
destBuffer,
srcBuffer);
IGF.Builder.CreateBr(doneBB);
}
// All done.
IGF.Builder.emitBlock(doneBB);
IGF.Builder.CreateRetVoid();
}, true /*noinline*/);
}
/// Emit protocol witness table pointers for the given protocol conformances,
/// passing each emitted witness table index into the given function body.
static void forEachProtocolWitnessTable(IRGenFunction &IGF,
CanType srcType, llvm::Value **srcMetadataCache,
CanType destType,
ArrayRef<ProtocolEntry> protocols,
ArrayRef<ProtocolConformanceRef> conformances,
std::function<void (unsigned, llvm::Value*)> body) {
// Collect the conformances that need witness tables.
SmallVector<ProtocolDecl*, 2> destProtocols;
destType.getAnyExistentialTypeProtocols(destProtocols);
SmallVector<ProtocolConformanceRef, 2> witnessConformances;
assert(destProtocols.size() == conformances.size() &&
"mismatched protocol conformances");
for (unsigned i = 0, size = destProtocols.size(); i < size; ++i)
if (Lowering::TypeConverter::protocolRequiresWitnessTable(destProtocols[i]))
witnessConformances.push_back(conformances[i]);
assert(protocols.size() == witnessConformances.size() &&
"mismatched protocol conformances");
for (unsigned i = 0, e = protocols.size(); i < e; ++i) {
assert(protocols[i].getProtocol()
== witnessConformances[i].getRequirement());
auto table = emitWitnessTableRef(IGF, srcType, srcMetadataCache,
witnessConformances[i]);
body(i, table);
}
}
#ifndef NDEBUG
static bool _isError(SILType baseTy) {
llvm::SmallVector<ProtocolDecl*, 1> protos;
return baseTy.getSwiftRValueType()->isExistentialType(protos)
&& protos.size() == 1
&& protos[0]->getKnownProtocolKind()
&& *protos[0]->getKnownProtocolKind() == KnownProtocolKind::Error;
}
#endif
/// Project the address of the value inside a boxed existential container.
ContainedAddress irgen::emitBoxedExistentialProjection(IRGenFunction &IGF,
Explosion &base,
SILType baseTy,
CanType projectedType) {
// TODO: Non-ErrorType boxed existentials.
assert(_isError(baseTy));
// Get the reference to the existential box.
llvm::Value *box = base.claimNext();
// Allocate scratch space to invoke the runtime.
Address scratch = IGF.createAlloca(IGF.IGM.Int8PtrTy,
IGF.IGM.getPointerAlignment(),
"project_error_scratch");
Address out = IGF.createAlloca(IGF.IGM.OpenedErrorTripleTy,
IGF.IGM.getPointerAlignment(),
"project_error_out");
IGF.Builder.CreateCall(IGF.IGM.getGetErrorValueFn(), {box,
scratch.getAddress(),
out.getAddress()});
// Load the 'out' values.
auto &projectedTI = IGF.getTypeInfoForLowered(projectedType);
auto projectedPtrAddr = IGF.Builder.CreateStructGEP(out, 0, Size(0));
llvm::Value *projectedPtr = IGF.Builder.CreateLoad(projectedPtrAddr);
projectedPtr = IGF.Builder.CreateBitCast(projectedPtr,
projectedTI.getStorageType()->getPointerTo());
auto projected = projectedTI.getAddressForPointer(projectedPtr);
return ContainedAddress(out, projected);
}
/// Project the address of the value inside a boxed existential container,
/// and open an archetype to its contained type.
Address irgen::emitOpenExistentialBox(IRGenFunction &IGF,
Explosion &base,
SILType baseTy,
CanArchetypeType openedArchetype) {
ContainedAddress box = emitBoxedExistentialProjection(IGF, base, baseTy,
openedArchetype);
Address out = box.getContainer();
auto metadataAddr = IGF.Builder.CreateStructGEP(out, 1,
IGF.IGM.getPointerSize());
auto metadata = IGF.Builder.CreateLoad(metadataAddr);
auto witnessAddr = IGF.Builder.CreateStructGEP(out, 2,
2 * IGF.IGM.getPointerSize());
auto witness = IGF.Builder.CreateLoad(witnessAddr);
IGF.bindArchetype(openedArchetype, metadata, witness);
return box.getAddress();
}
/// Allocate a boxed existential container with uninitialized space to hold a
/// value of a given type.
OwnedAddress irgen::emitBoxedExistentialContainerAllocation(IRGenFunction &IGF,
SILType destType,
CanType formalSrcType,
ArrayRef<ProtocolConformanceRef> conformances) {
// TODO: Non-Error boxed existentials.
assert(_isError(destType));
auto &destTI = IGF.getTypeInfo(destType).as<ErrorExistentialTypeInfo>();
auto srcMetadata = IGF.emitTypeMetadataRef(formalSrcType);
// Should only be one conformance, for the Error protocol.
assert(conformances.size() == 1 && destTI.getStoredProtocols().size() == 1);
const ProtocolEntry &entry = destTI.getStoredProtocols()[0];
(void) entry;
assert(entry.getProtocol() == conformances[0].getRequirement());
auto witness = emitWitnessTableRef(IGF, formalSrcType, &srcMetadata,
conformances[0]);
// Call the runtime to allocate the box.
// TODO: When there's a store or copy_addr immediately into the box, peephole
// it into the initializer parameter to allocError.
auto result = IGF.Builder.CreateCall(IGF.IGM.getAllocErrorFn(),
{srcMetadata, witness,
llvm::ConstantPointerNull::get(IGF.IGM.OpaquePtrTy),
llvm::ConstantInt::get(IGF.IGM.Int1Ty, 0)});
// Extract the box and value address from the result.
auto box = IGF.Builder.CreateExtractValue(result, 0);
auto addr = IGF.Builder.CreateExtractValue(result, 1);
auto archetype = ArchetypeType::getOpened(destType.getSwiftRValueType());
auto &srcTI = IGF.getTypeInfoForUnlowered(AbstractionPattern(archetype),
formalSrcType);
addr = IGF.Builder.CreateBitCast(addr,
srcTI.getStorageType()->getPointerTo());
return OwnedAddress(srcTI.getAddressForPointer(addr), box);
}
/// Deallocate a boxed existential container with uninitialized space to hold a
/// value of a given type.
void irgen::emitBoxedExistentialContainerDeallocation(IRGenFunction &IGF,
Explosion &container,
SILType containerType,
CanType valueType) {
// TODO: Non-Error boxed existentials.
assert(_isError(containerType));
auto box = container.claimNext();
auto srcMetadata = IGF.emitTypeMetadataRef(valueType);
IGF.Builder.CreateCall(IGF.IGM.getDeallocErrorFn(), {box, srcMetadata});
}
/// "Deinitialize" an existential container whose contained value is allocated
/// but uninitialized, by deallocating the buffer owned by the container if any.
void irgen::emitOpaqueExistentialContainerDeinit(IRGenFunction &IGF,
Address container,
SILType type) {
assert(type.isExistentialType());
assert(!type.isClassExistentialType());
auto &ti = IGF.getTypeInfo(type).as<OpaqueExistentialTypeInfo>();
auto layout = ti.getLayout();
llvm::Value *metadata = layout.loadMetadataRef(IGF, container);
Address buffer = layout.projectExistentialBuffer(IGF, container);
emitDeallocateBufferCall(IGF, metadata, buffer);
}
/// Emit a class existential container from a class instance value
/// as an explosion.
void irgen::emitClassExistentialContainer(IRGenFunction &IGF,
Explosion &out,
SILType outType,
llvm::Value *instance,
CanType instanceFormalType,
SILType instanceLoweredType,
ArrayRef<ProtocolConformanceRef> conformances) {
// As a special case, an Error existential can be represented as a
// reference to an already existing NSError or CFError instance.
SmallVector<ProtocolDecl*, 4> protocols;
if (outType.getSwiftRValueType()->isExistentialType(protocols)
&& protocols.size() == 1) {
switch (getSpecialProtocolID(protocols[0])) {
case SpecialProtocol::Error: {
// Bitcast the incoming class reference to Error.
out.add(IGF.Builder.CreateBitCast(instance, IGF.IGM.ErrorPtrTy));
return;
}
case SpecialProtocol::AnyObject:
case SpecialProtocol::None:
break;
}
}
assert(outType.isClassExistentialType() &&
"creating a non-class existential type");
auto &destTI = IGF.getTypeInfo(outType).as<ClassExistentialTypeInfo>();
// Cast the instance pointer to an opaque refcounted pointer.
llvm::Value *opaqueInstance;
if (!IGF.IGM.ObjCInterop)
opaqueInstance = IGF.Builder.CreateBitCast(instance,
IGF.IGM.RefCountedPtrTy);
else
opaqueInstance = IGF.Builder.CreateBitCast(instance,
IGF.IGM.UnknownRefCountedPtrTy);
out.add(opaqueInstance);
// Emit the witness table pointers.
llvm::Value *instanceMetadata = nullptr;
forEachProtocolWitnessTable(IGF, instanceFormalType, &instanceMetadata,
outType.getSwiftRValueType(),
destTI.getStoredProtocols(),
conformances,
[&](unsigned i, llvm::Value *ptable) {
out.add(ptable);
});
}
/// Emit an existential container initialization operation for a concrete type.
/// Returns the address of the uninitialized fixed-size buffer for the concrete
/// value.
Address irgen::emitOpaqueExistentialContainerInit(IRGenFunction &IGF,
Address dest,
SILType destType,
CanType formalSrcType,
SILType loweredSrcType,
ArrayRef<ProtocolConformanceRef> conformances) {
assert(!destType.isClassExistentialType() &&
"initializing a class existential container as opaque");
auto &destTI = IGF.getTypeInfo(destType).as<OpaqueExistentialTypeInfo>();
OpaqueExistentialLayout destLayout = destTI.getLayout();
assert(destTI.getStoredProtocols().size() == conformances.size());
// First, write out the metadata.
llvm::Value *metadata = IGF.emitTypeMetadataRef(formalSrcType);
IGF.Builder.CreateStore(metadata, destLayout.projectMetadataRef(IGF, dest));
// Next, write the protocol witness tables.
forEachProtocolWitnessTable(IGF, formalSrcType, &metadata,
destType.getSwiftRValueType(),
destTI.getStoredProtocols(), conformances,
[&](unsigned i, llvm::Value *ptable) {
Address ptableSlot = destLayout.projectWitnessTable(IGF, dest, i);
IGF.Builder.CreateStore(ptable, ptableSlot);
});
// Finally, evaluate into the buffer.
// Project down to the destination fixed-size buffer.
return destLayout.projectExistentialBuffer(IGF, dest);
}
/// Emit an existential metatype container from a metatype value
/// as an explosion.
void irgen::emitExistentialMetatypeContainer(IRGenFunction &IGF,
Explosion &out, SILType outType,
llvm::Value *metatype, SILType metatypeType,
ArrayRef<ProtocolConformanceRef> conformances) {
assert(outType.is<ExistentialMetatypeType>());
auto &destTI = IGF.getTypeInfo(outType).as<ExistentialMetatypeTypeInfo>();
out.add(metatype);
auto srcType = metatypeType.castTo<MetatypeType>().getInstanceType();
auto destType = outType.castTo<ExistentialMetatypeType>().getInstanceType();
while (auto destMetatypeType = dyn_cast<ExistentialMetatypeType>(destType)) {
destType = destMetatypeType.getInstanceType();
srcType = cast<AnyMetatypeType>(srcType).getInstanceType();
}
// Emit the witness table pointers.
llvm::Value *srcMetadata = nullptr;
forEachProtocolWitnessTable(IGF, srcType, &srcMetadata, destType,
destTI.getStoredProtocols(),
conformances,
[&](unsigned i, llvm::Value *ptable) {
out.add(ptable);
});
}
void irgen::emitMetatypeOfOpaqueExistential(IRGenFunction &IGF, Address addr,
SILType type, Explosion &out) {
assert(type.isExistentialType());
assert(!type.isClassExistentialType());
auto &baseTI = IGF.getTypeInfo(type).as<OpaqueExistentialTypeInfo>();
// Get the static metadata.
auto existLayout = baseTI.getLayout();
llvm::Value *metadata = existLayout.loadMetadataRef(IGF, addr);
// Project the buffer and apply the 'typeof' value witness.
Address buffer = existLayout.projectExistentialBuffer(IGF, addr);
llvm::Value *object;
if (IGF.getSILModule().getOptions().UseCOWExistentials) {
auto *projectFunc = getProjectBoxedOpaqueExistentialFunction(
IGF, OpenedExistentialAccess::Immutable, existLayout);
auto *addrOfValue =
IGF.Builder.CreateCall(projectFunc, {buffer.getAddress(), metadata});
addrOfValue->setCallingConv(IGF.IGM.DefaultCC);
addrOfValue->setDoesNotThrow();
object = addrOfValue;
} else
object = emitProjectBufferCall(IGF, metadata, buffer);
llvm::Value *dynamicType =
IGF.Builder.CreateCall(IGF.IGM.getGetDynamicTypeFn(),
{object, metadata,
llvm::ConstantInt::get(IGF.IGM.Int1Ty, 1)});
out.add(dynamicType);
// Get the witness tables.
baseTI.emitLoadOfTables(IGF, addr, out);
}
void irgen::emitMetatypeOfBoxedExistential(IRGenFunction &IGF, Explosion &value,
SILType type, Explosion &out) {
// TODO: Non-Error boxed existentials.
assert(_isError(type));
// Get the reference to the existential box.
llvm::Value *box = value.claimNext();
// Allocate scratch space to invoke the runtime.
Address scratchAddr = IGF.createAlloca(IGF.IGM.Int8PtrTy,
IGF.IGM.getPointerAlignment(),
"project_error_scratch");
Address outAddr = IGF.createAlloca(IGF.IGM.OpenedErrorTripleTy,
IGF.IGM.getPointerAlignment(),
"project_error_out");
IGF.Builder.CreateCall(IGF.IGM.getGetErrorValueFn(), {box,
scratchAddr.getAddress(),
outAddr.getAddress()});
auto projectedPtrAddr = IGF.Builder.CreateStructGEP(outAddr, 0, Size(0));
auto projectedPtr = IGF.Builder.CreateLoad(projectedPtrAddr);
auto metadataAddr = IGF.Builder.CreateStructGEP(outAddr, 1,
IGF.IGM.getPointerSize());
auto metadata = IGF.Builder.CreateLoad(metadataAddr);
auto dynamicType =
IGF.Builder.CreateCall(IGF.IGM.getGetDynamicTypeFn(),
{projectedPtr, metadata,
llvm::ConstantInt::get(IGF.IGM.Int1Ty, 1)});
auto witnessAddr = IGF.Builder.CreateStructGEP(outAddr, 2,
2 * IGF.IGM.getPointerSize());
auto witness = IGF.Builder.CreateLoad(witnessAddr);
out.add(dynamicType);
out.add(witness);
}
void irgen::emitMetatypeOfClassExistential(IRGenFunction &IGF, Explosion &value,
SILType metatypeTy,
SILType existentialTy,
Explosion &out) {
assert(existentialTy.isClassExistentialType());
auto &baseTI = IGF.getTypeInfo(existentialTy).as<ClassExistentialTypeInfo>();
// Extract the class instance pointer.
auto tablesAndValue = baseTI.getWitnessTablesAndValue(value);
// Get the type metadata.
llvm::Value *instance = tablesAndValue.second;
auto metaTy = metatypeTy.castTo<ExistentialMetatypeType>();
auto repr = metaTy->getRepresentation();
assert(repr != MetatypeRepresentation::Thin &&
"Class metatypes should have a thin representation");
assert((IGF.IGM.ObjCInterop || repr != MetatypeRepresentation::ObjC) &&
"Class metatypes should not have ObjC representation without runtime");
if (repr == MetatypeRepresentation::Thick) {
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance);
out.add(dynamicType);
} else if (repr == MetatypeRepresentation::ObjC) {
auto dynamicType = emitHeapMetadataRefForUnknownHeapObject(IGF, instance);
out.add(dynamicType);
} else {
llvm_unreachable("Unknown metatype representation");
}
// Get the witness tables.
out.add(tablesAndValue.first);
}
void irgen::emitMetatypeOfMetatype(IRGenFunction &IGF, Explosion &value,
SILType existentialTy,
Explosion &out) {
assert(existentialTy.is<ExistentialMetatypeType>());
auto &baseTI = IGF.getTypeInfo(existentialTy).as<ExistentialMetatypeTypeInfo>();
auto tablesAndValue = baseTI.getWitnessTablesAndValue(value);
llvm::Value *dynamicType = IGF.Builder.CreateCall(
IGF.IGM.getGetMetatypeMetadataFn(), tablesAndValue.second);
out.add(dynamicType);
out.add(tablesAndValue.first);
}
/// Emit a projection from an existential container to its concrete value
/// buffer with the type metadata for the contained value.
///
/// \param _openedArchetype When non-null, the opened archetype
/// that captures the details of this existential.
std::pair<Address, llvm::Value*>
irgen::emitIndirectExistentialProjectionWithMetadata(IRGenFunction &IGF,
Address base,
SILType baseTy,
CanType _openedArchetype) {
CanArchetypeType openedArchetype;
if (_openedArchetype) openedArchetype = cast<ArchetypeType>(_openedArchetype);
assert(baseTy.isExistentialType());
if (baseTy.isClassExistentialType()) {
auto &baseTI = IGF.getTypeInfo(baseTy).as<ClassExistentialTypeInfo>();
auto valueAddr = baseTI.projectValue(IGF, base);
auto value = IGF.Builder.CreateLoad(valueAddr);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
// If we are projecting into an opened archetype, capture the
// witness tables.
if (openedArchetype) {
SmallVector<llvm::Value *, 4> wtables;
for (unsigned i = 0, n = baseTI.getNumStoredProtocols(); i != n; ++i) {
auto wtableAddr = baseTI.projectWitnessTable(IGF, base, i);
wtables.push_back(IGF.Builder.CreateLoad(wtableAddr));
}
IGF.bindArchetype(openedArchetype, metadata, wtables);
}
return {valueAddr, metadata};
} else {
auto &baseTI = IGF.getTypeInfo(baseTy).as<OpaqueExistentialTypeInfo>();
auto layout = baseTI.getLayout();
llvm::Value *metadata = layout.loadMetadataRef(IGF, base);
Address buffer = layout.projectExistentialBuffer(IGF, base);
llvm::Value *object =
emitProjectBufferCall(IGF, metadata, buffer);
// If we are projecting into an opened archetype, capture the
// witness tables.
if (openedArchetype) {
SmallVector<llvm::Value *, 4> wtables;
for (unsigned i = 0, n = layout.getNumTables(); i != n; ++i) {
wtables.push_back(layout.loadWitnessTable(IGF, base, i));
}
IGF.bindArchetype(openedArchetype, metadata, wtables);
}
return {Address(object, Alignment(1)), metadata};
}
}
/// Emit a projection from an existential container to its concrete value
/// buffer.
Address irgen::emitOpaqueExistentialProjection(IRGenFunction &IGF,
Address base,
SILType baseTy,
CanArchetypeType openedArchetype)
{
return emitIndirectExistentialProjectionWithMetadata(IGF, base, baseTy,
openedArchetype)
.first;
}
/// Extract the instance pointer from a class existential value.
llvm::Value *
irgen::emitClassExistentialProjection(IRGenFunction &IGF,
Explosion &base,
SILType baseTy,
CanArchetypeType openedArchetype) {
assert(baseTy.isClassExistentialType());
auto &baseTI = IGF.getTypeInfo(baseTy).as<ClassExistentialTypeInfo>();
if (!openedArchetype)
return baseTI.getValue(IGF, base);
// Capture the metadata and witness tables from this existential
// into the given archetype.
ArrayRef<llvm::Value*> wtables;
llvm::Value *value;
std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
IGF.bindArchetype(openedArchetype, metadata, wtables);
return value;
}
/// Extract the metatype pointer from a class existential value.
llvm::Value *
irgen::emitExistentialMetatypeProjection(IRGenFunction &IGF,
Explosion &base,
SILType baseTy,
CanType openedTy) {
assert(baseTy.is<ExistentialMetatypeType>());
auto &baseTI = IGF.getTypeInfo(baseTy).as<ExistentialMetatypeTypeInfo>();
if (!openedTy)
return baseTI.getValue(IGF, base);
// Capture the metadata and witness tables from this existential
// into the given archetype.
ArrayRef<llvm::Value*> wtables;
llvm::Value *value;
std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base);
auto existentialType = baseTy.castTo<ExistentialMetatypeType>();
auto targetType = cast<MetatypeType>(openedTy);
// If we're starting with an ObjC representation, convert it to a
// class type and let's go.
llvm::Value *metatype;
if (existentialType->getRepresentation() == MetatypeRepresentation::ObjC) {
metatype = emitObjCMetadataRefForMetadata(IGF, value);
// Otherwise, we have type metadata.
} else {
assert(existentialType->getRepresentation()
== MetatypeRepresentation::Thick);
metatype = value;
// The type we need to bind to the archetype is the one that's
// deep in the type.
while (!isa<ArchetypeType>(targetType.getInstanceType())) {
targetType = cast<MetatypeType>(targetType.getInstanceType());
existentialType =
cast<ExistentialMetatypeType>(existentialType.getInstanceType());
metatype = emitMetatypeInstanceType(IGF, metatype);
}
}
auto openedArchetype = cast<ArchetypeType>(targetType.getInstanceType());
IGF.bindArchetype(openedArchetype, metatype, wtables);
return value;
}
static Address castToOpaquePtr(IRGenFunction &IGF, Address addr) {
return Address(
IGF.Builder.CreateBitCast(addr.getAddress(), IGF.IGM.OpaquePtrTy),
addr.getAlignment());
}
static llvm::Constant *getAllocateBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy) {
llvm::Type *argTys[] = {existContainerPointerTy};
// __swift_allocate_boxed_opaque_existential__N is the well-known function for
// allocating buffers in existential containers of types with N witness
// tables.
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_allocate_boxed_opaque_existential_"
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.OpaquePtrTy, argTys, [&](IRGenFunction &IGF) {
auto it = IGF.CurFn->arg_begin();
Address existentialContainer(&*(it++), existLayout.getAlignment(IGM));
// Dynamically check whether this type is inline or needs an allocation.
auto *metadata = existLayout.loadMetadataRef(IGF, existentialContainer);
llvm::Value *isInline, *flags;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
llvm::BasicBlock *doneBB = IGF.createBasicBlock("done");
llvm::BasicBlock *allocateBB = IGF.createBasicBlock("allocateBox");
llvm::Value *addressInBox;
Address existentialBuffer =
existLayout.projectExistentialBuffer(IGF, existentialContainer);
llvm::Value *addressInline = IGF.Builder.CreateBitCast(
existentialBuffer.getAddress(), IGF.IGM.OpaquePtrTy);
IGF.Builder.CreateCondBr(isInline, doneBB, allocateBB);
IGF.Builder.emitBlock(doneBB);
IGF.Builder.CreateRet(addressInline);
// Use the runtime to allocate a box of the appropriate size.
{
IGF.Builder.emitBlock(allocateBB);
ConditionalDominanceScope allocateCondition(IGF);
llvm::Value *box, *address;
IGF.emitAllocBoxCall(metadata, box, address);
addressInBox =
IGF.Builder.CreateBitCast(address, IGF.IGM.OpaquePtrTy);
IGF.Builder.CreateStore(
box,
Address(IGF.Builder.CreateBitCast(existentialBuffer.getAddress(),
box->getType()->getPointerTo()),
existLayout.getAlignment(IGF.IGM)));
IGF.Builder.CreateRet(addressInBox);
}
}, true /*noinline*/);
}
Address irgen::emitAllocateBoxedOpaqueExistentialBuffer(
IRGenFunction &IGF, SILType existentialType, SILType valueType,
Address existentialContainer, GenericEnvironment *genericEnv) {
// Project to the existential buffer in the existential container.
auto &existentialTI =
IGF.getTypeInfo(existentialType).as<OpaqueExistentialTypeInfo>();
OpaqueExistentialLayout existLayout = existentialTI.getLayout();
Address existentialBuffer =
existLayout.projectExistentialBuffer(IGF, existentialContainer);
auto &valueTI = IGF.getTypeInfo(valueType);
auto *valuePointerType = valueTI.getStorageType()->getPointerTo();
// Check if the value is fixed size.
if (auto *fixedTI = dyn_cast<FixedTypeInfo>(&valueTI)) {
// Don't allocate an out-of-line buffer if the fixed buffer size is
// sufficient.
if (fixedTI->getFixedPacking(IGF.IGM) == FixedPacking::OffsetZero) {
return valueTI.getAddressForPointer(IGF.Builder.CreateBitCast(
existentialBuffer.getAddress(), valuePointerType));
}
// Otherwise, allocate a box with enough storage.
Address addr = emitAllocateExistentialBoxInBuffer(
IGF, valueType, existentialBuffer, genericEnv, "exist.box.addr");
return addr;
}
/// Call a function to handle the non-fixed case.
auto *allocateFun = getAllocateBoxedOpaqueExistentialBufferFunction(
IGF.IGM, existLayout, existentialContainer.getAddress()->getType());
auto *call =
IGF.Builder.CreateCall(allocateFun, {existentialContainer.getAddress()});
call->setCallingConv(IGF.IGM.DefaultCC);
call->setDoesNotThrow();
auto addressOfValue = IGF.Builder.CreateBitCast(call, valuePointerType);
return valueTI.getAddressForPointer(addressOfValue);
}
static llvm::Constant *getDeallocateBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy) {
llvm::Type *argTys[] = {existContainerPointerTy};
// __swift_deallocate_boxed_opaque_existential_N is the well-known function
// for deallocating buffers in existential containers of types with N witness
// tables.
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_deallocate_boxed_opaque_existential_"
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.VoidTy, argTys, [&](IRGenFunction &IGF) {
auto &Builder = IGF.Builder;
auto it = IGF.CurFn->arg_begin();
Address existentialContainer(&*(it++), existLayout.getAlignment(IGM));
// Dynamically check whether this type is inline or needs a
// deallocation.
auto *metadata = existLayout.loadMetadataRef(IGF, existentialContainer);
llvm::Value *isInline, *flags;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
llvm::BasicBlock *doneBB = IGF.createBasicBlock("done");
llvm::BasicBlock *deallocateBB = IGF.createBasicBlock("deallocateBox");
Builder.CreateCondBr(isInline, doneBB, deallocateBB);
// We are done. Return.
Builder.emitBlock(doneBB);
Builder.CreateRetVoid();
// We have an allocated uninitialized box. Deallocate the box.
// No ConditionalDominanceScope because no code is executed that could
// affect the caches.
Builder.emitBlock(deallocateBB);
// Project to the existential buffer address.
auto existentialBuffer =
existLayout.projectExistentialBuffer(IGF, existentialContainer);
auto *boxReferenceAddr =
Builder.CreateBitCast(existentialBuffer.getAddress(),
IGM.RefCountedPtrTy->getPointerTo());
// Load the reference.
auto *boxReference = Builder.CreateLoad(
boxReferenceAddr, existentialBuffer.getAlignment());
// Size and alignment requirements of the boxed value.
auto *size = emitLoadOfSize(IGF, metadata);
auto *alignmentMask = emitAlignMaskFromFlags(IGF, flags);
// Size = ((sizeof(HeapObject) + align) & ~align) + size
auto *heapHeaderSize = llvm::ConstantInt::get(
IGF.IGM.SizeTy, getHeapHeaderSize(IGM).getValue());
size = Builder.CreateAdd(
Builder.CreateAnd(Builder.CreateAdd(heapHeaderSize, alignmentMask),
Builder.CreateNot(alignmentMask)),
size);
// At least pointer aligned.
// AlignmentMask = alignmentMask | alignof(void*) - 1
llvm::Value *pointerAlignMask = llvm::ConstantInt::get(
IGF.IGM.SizeTy, IGF.IGM.getPointerAlignment().getValue() - 1);
alignmentMask = Builder.CreateOr(alignmentMask, pointerAlignMask);
IGF.emitDeallocRawCall(
Builder.CreateBitCast(boxReference, IGF.IGM.Int8PtrTy), size,
alignmentMask);
// We are done. Return.
Builder.CreateRetVoid();
}, true /*noinline*/);
}
void irgen::emitDeallocateBoxedOpaqueExistentialBuffer(
IRGenFunction &IGF, SILType existentialType, Address existentialContainer) {
// Project to the existential buffer in the existential container.
auto &existentialTI =
IGF.getTypeInfo(existentialType).as<OpaqueExistentialTypeInfo>();
OpaqueExistentialLayout existLayout = existentialTI.getLayout();
auto *deallocateFun = getDeallocateBoxedOpaqueExistentialBufferFunction(
IGF.IGM, existLayout, existentialContainer.getAddress()->getType());
auto *call = IGF.Builder.CreateCall(deallocateFun,
{existentialContainer.getAddress()});
call->setCallingConv(IGF.IGM.DefaultCC);
call->setDoesNotThrow();
return;
}
static llvm::Constant *
getProjectBoxedOpaqueExistentialFunction(IRGenFunction &IGF,
OpenedExistentialAccess accessKind,
OpaqueExistentialLayout existLayout) {
auto &IGM = IGF.IGM;
auto *existentialBufferTy = IGM.getFixedBufferTy()->getPointerTo();
llvm::Type *argTys[] = {existentialBufferTy, IGM.TypeMetadataPtrTy};
// __swift_project_boxed_opaque_existential_N is the well-known function for
// projecting buffers in existential containers of types with N witness
// tables.
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< (accessKind == OpenedExistentialAccess::Immutable
? "__swift_project_boxed_opaque_existential_"
: "__swift_mutable_project_boxed_opaque_existential_")
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.OpaquePtrTy, argTys, [&](IRGenFunction &IGF) {
auto &Builder = IGF.Builder;
auto &IGM = IGF.IGM;
auto it = IGF.CurFn->arg_begin();
Address existentialBuffer(&*(it++), existLayout.getAlignment(IGM));
auto *metadata = &*(it++);
// Dynamically check whether this type is inline or needs a
// deallocation.
llvm::Value *isInline, *flags;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
llvm::BasicBlock *doneBB = IGF.createBasicBlock("done");
llvm::BasicBlock *boxedBB = IGF.createBasicBlock("boxed");
llvm::Value *addressInline = Builder.CreateBitCast(
existentialBuffer.getAddress(), IGM.OpaquePtrTy);
Builder.CreateCondBr(isInline, doneBB, boxedBB);
// We are done. Return the pointer to the address of the value.
Builder.emitBlock(doneBB);
IGF.Builder.CreateRet(addressInline);
// We have a boxed representation.
Builder.emitBlock(boxedBB);
if (accessKind == OpenedExistentialAccess::Immutable) {
// Project to the existential buffer address.
auto *boxReferenceAddr =
Builder.CreateBitCast(existentialBuffer.getAddress(),
IGM.RefCountedPtrTy->getPointerTo());
// Load the reference.
auto *boxReference = Builder.CreateLoad(
boxReferenceAddr, existentialBuffer.getAlignment());
// Size and alignment requirements of the boxed value.
auto *alignmentMask = emitAlignMaskFromFlags(IGF, flags);
// StartOffset = ((sizeof(HeapObject) + align) & ~align)
auto *heapHeaderSize = llvm::ConstantInt::get(
IGF.IGM.SizeTy, getHeapHeaderSize(IGM).getValue());
auto *startOffset = Builder.CreateAnd(
Builder.CreateAdd(heapHeaderSize, alignmentMask),
Builder.CreateNot(alignmentMask));
auto *addressInBox =
IGF.emitByteOffsetGEP(boxReference, startOffset, IGM.OpaqueTy);
IGF.Builder.CreateRet(addressInBox);
return;
}
// If we are opening this existential for mutating check the reference
// count and copy if the boxed is not uniquely owned by this reference.
assert(accessKind == OpenedExistentialAccess::Mutable);
auto *alignmentMask = emitAlignMaskFromFlags(IGF, flags);
llvm::Value *box, *objectAddr;
IGF.emitMakeBoxUniqueCall(
Builder.CreateBitCast(existentialBuffer.getAddress(),
IGM.OpaquePtrTy),
metadata, alignmentMask, box, objectAddr);
IGF.Builder.CreateRet(objectAddr);
}, true /*noinline*/);
}
Address irgen::emitOpaqueBoxedExistentialProjection(
IRGenFunction &IGF, OpenedExistentialAccess accessKind, Address base,
SILType existentialTy, CanArchetypeType openedArchetype) {
assert(existentialTy.isExistentialType());
if (existentialTy.isClassExistentialType()) {
auto &baseTI =
IGF.getTypeInfo(existentialTy).as<ClassExistentialTypeInfo>();
auto valueAddr = baseTI.projectValue(IGF, base);
auto value = IGF.Builder.CreateLoad(valueAddr);
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
// If we are projecting into an opened archetype, capture the
// witness tables.
if (openedArchetype) {
SmallVector<llvm::Value *, 4> wtables;
for (unsigned i = 0, n = baseTI.getNumStoredProtocols(); i != n; ++i) {
auto wtableAddr = baseTI.projectWitnessTable(IGF, base, i);
wtables.push_back(IGF.Builder.CreateLoad(wtableAddr));
}
IGF.bindArchetype(openedArchetype, metadata, wtables);
}
return valueAddr;
}
auto &baseTI = IGF.getTypeInfo(existentialTy).as<OpaqueExistentialTypeInfo>();
auto layout = baseTI.getLayout();
llvm::Value *metadata = layout.loadMetadataRef(IGF, base);
// If we are projecting into an opened archetype, capture the
// witness tables.
if (openedArchetype) {
SmallVector<llvm::Value *, 4> wtables;
for (unsigned i = 0, n = layout.getNumTables(); i != n; ++i) {
wtables.push_back(layout.loadWitnessTable(IGF, base, i));
}
IGF.bindArchetype(openedArchetype, metadata, wtables);
}
Address buffer = layout.projectExistentialBuffer(IGF, base);
auto *projectFunc =
getProjectBoxedOpaqueExistentialFunction(IGF, accessKind, layout);
auto *addrOfValue =
IGF.Builder.CreateCall(projectFunc, {buffer.getAddress(), metadata});
addrOfValue->setCallingConv(IGF.IGM.DefaultCC);
addrOfValue->setDoesNotThrow();
return Address(addrOfValue, Alignment(1));
}
static void initBufferWithCopyOfReference(IRGenFunction &IGF,
OpaqueExistentialLayout existLayout,
Address destBuffer,
Address srcBuffer) {
auto &IGM = IGF.IGM;
auto &Builder = IGF.Builder;
auto *destReferenceAddr = Builder.CreateBitCast(
destBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
auto *srcReferenceAddr = Builder.CreateBitCast(
srcBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
auto *srcReference =
Builder.CreateLoad(srcReferenceAddr, srcBuffer.getAlignment());
IGF.emitNativeStrongRetain(srcReference, IGF.getDefaultAtomicity());
IGF.Builder.CreateStore(
srcReference,
Address(destReferenceAddr, existLayout.getAlignment(IGF.IGM)));
}
static llvm::Constant *getAssignBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy) {
llvm::Type *argTys[] = {existContainerPointerTy, existContainerPointerTy};
// __swift_assign_box_in_existentials_N is the well-known function for
// assigning buffers in existential containers of types with N witness
// tables.
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_assign_boxed_opaque_existential_"
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.VoidTy, argTys, [&](IRGenFunction &IGF) {
auto it = IGF.CurFn->arg_begin();
Address dest(&*(it++), getFixedBufferAlignment(IGM));
Address src(&*(it++), getFixedBufferAlignment(IGM));
auto &Builder = IGF.Builder;
// If doing a self-assignment, we're done.
llvm::BasicBlock *doneBB = IGF.createBasicBlock("done");
llvm::BasicBlock *contBB = IGF.createBasicBlock("cont");
llvm::Value *isSelfAssign = Builder.CreateICmpEQ(
dest.getAddress(), src.getAddress(), "isSelfAssign");
Builder.CreateCondBr(isSelfAssign, doneBB, contBB);
Builder.emitBlock(contBB);
// We don't need a ConditionalDominanceScope here because (1) there's no
// code in the other condition and (2) we immediately return.
Address destBuffer = existLayout.projectExistentialBuffer(IGF, dest);
Address srcBuffer = existLayout.projectExistentialBuffer(IGF, src);
// Load the metadata tables.
Address destMetadataSlot = existLayout.projectMetadataRef(IGF, dest);
llvm::Value *destMetadata = Builder.CreateLoad(destMetadataSlot);
llvm::Value *srcMetadata = existLayout.loadMetadataRef(IGF, src);
// Check whether the metadata match.
auto *matchBB = IGF.createBasicBlock("match");
auto *noMatchBB = IGF.createBasicBlock("no-match");
auto *sameMetadata =
Builder.CreateICmpEQ(destMetadata, srcMetadata, "sameMetadata");
Builder.CreateCondBr(sameMetadata, matchBB, noMatchBB);
Builder.emitBlock(matchBB);
{
// Metadata pointers match.
ConditionalDominanceScope matchCondition(IGF);
llvm::Value *isInline, *flags;
auto *metadata = destMetadata;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
auto *matchInlineBB = IGF.createBasicBlock("match-inline");
auto *matchOutlineBB = IGF.createBasicBlock("match-outline");
Builder.CreateCondBr(isInline, matchInlineBB, matchOutlineBB);
// Inline.
Builder.emitBlock(matchInlineBB);
{
ConditionalDominanceScope inlineCondition(IGF);
emitAssignWithCopyCall(IGF, metadata,
castToOpaquePtr(IGF, destBuffer),
castToOpaquePtr(IGF, srcBuffer));
Builder.CreateBr(doneBB);
}
// Outline.
Builder.emitBlock(matchOutlineBB);
{
ConditionalDominanceScope outlineCondition(IGF);
auto *destReferenceAddr = Builder.CreateBitCast(
destBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
auto *srcReferenceAddr = Builder.CreateBitCast(
srcBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
// Load the reference.
auto *destReference = Builder.CreateLoad(destReferenceAddr,
destBuffer.getAlignment());
auto *srcReference =
Builder.CreateLoad(srcReferenceAddr, srcBuffer.getAlignment());
IGF.emitNativeStrongRetain(srcReference, IGF.getDefaultAtomicity());
IGF.emitNativeStrongRelease(destReference,
IGF.getDefaultAtomicity());
IGF.Builder.CreateStore(
srcReference,
Address(destReferenceAddr, existLayout.getAlignment(IGF.IGM)));
Builder.CreateBr(doneBB);
}
}
Builder.emitBlock(noMatchBB);
{
// Metdata pointers don't match.
ConditionalDominanceScope noMatchCondition(IGF);
// Store the metadata ref.
IGF.Builder.CreateStore(srcMetadata, destMetadataSlot);
// Store the protocol witness tables.
unsigned numTables = existLayout.getNumTables();
for (unsigned i = 0, e = numTables; i != e; ++i) {
Address destTableSlot =
existLayout.projectWitnessTable(IGF, dest, i);
llvm::Value *srcTable = existLayout.loadWitnessTable(IGF, src, i);
// Overwrite the old witness table.
IGF.Builder.CreateStore(srcTable, destTableSlot);
}
// Check whether buffers are inline.
llvm::Value *isDestInline, *destFlags;
llvm::Value *isSrcInline, *srcFlags;
std::tie(isDestInline, destFlags) =
emitLoadOfIsInline(IGF, destMetadata);
std::tie(isSrcInline, srcFlags) =
emitLoadOfIsInline(IGF, srcMetadata);
Address tmpBuffer = IGF.createAlloca(IGM.getFixedBufferTy(),
existLayout.getAlignment(IGM),
"tmpInlineBuffer");
auto *destInlineBB = IGF.createBasicBlock("dest-inline");
auto *destOutlineBB = IGF.createBasicBlock("dest-outline");
// Check whether the destination is inline.
Builder.CreateCondBr(isDestInline, destInlineBB, destOutlineBB);
Builder.emitBlock(destInlineBB);
{
ConditionalDominanceScope destInlineCondition(IGF);
// Move asside so that we can destroy later.
emitInitializeWithTakeCall(IGF, destMetadata,
castToOpaquePtr(IGF, tmpBuffer),
castToOpaquePtr(IGF, destBuffer));
auto *srcInlineBB = IGF.createBasicBlock("dest-inline-src-inline");
auto *srcOutlineBB =
IGF.createBasicBlock("dest-inline-src-outline");
auto *contBB2 = IGF.createBasicBlock("dest-inline-cont");
// Check whether the source is inline.
Builder.CreateCondBr(isSrcInline, srcInlineBB, srcOutlineBB);
Builder.emitBlock(srcInlineBB);
{
// initializeWithCopy(dest, src)
ConditionalDominanceScope domScope(IGF);
emitInitializeWithCopyCall(IGF, srcMetadata,
castToOpaquePtr(IGF, destBuffer),
castToOpaquePtr(IGF, srcBuffer));
Builder.CreateBr(contBB2);
}
Builder.emitBlock(srcOutlineBB);
{
// dest[0] = src[0]
// swift_retain(src[0])
ConditionalDominanceScope domScope(IGF);
initBufferWithCopyOfReference(IGF, existLayout, destBuffer,
srcBuffer);
Builder.CreateBr(contBB2);
}
Builder.emitBlock(contBB2);
{
ConditionalDominanceScope domScope(IGF);
// destroy(tmpBuffer)
emitDestroyCall(IGF, destMetadata,
castToOpaquePtr(IGF, tmpBuffer));
Builder.CreateBr(doneBB);
}
}
Builder.emitBlock(destOutlineBB);
{
ConditionalDominanceScope destOutlineCondition(IGF);
// tmpRef = dest[0]
auto *destReferenceAddr = Builder.CreateBitCast(
destBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
auto *destReference =
Builder.CreateLoad(destReferenceAddr, srcBuffer.getAlignment());
auto *srcInlineBB = IGF.createBasicBlock("dest-outline-src-inline");
auto *srcOutlineBB =
IGF.createBasicBlock("dest-outline-src-outline");
auto *contBB2 = IGF.createBasicBlock("dest-outline-cont");
// Check whether the source is inline.
Builder.CreateCondBr(isSrcInline, srcInlineBB, srcOutlineBB);
Builder.emitBlock(srcInlineBB);
{
// initializeWithCopy(dest, src)
ConditionalDominanceScope domScope(IGF);
emitInitializeWithCopyCall(IGF, srcMetadata,
castToOpaquePtr(IGF, destBuffer),
castToOpaquePtr(IGF, srcBuffer));
Builder.CreateBr(contBB2);
}
Builder.emitBlock(srcOutlineBB);
{
// dest[0] = src[0]
// swift_retain(src[0])
ConditionalDominanceScope domScope(IGF);
initBufferWithCopyOfReference(IGF, existLayout, destBuffer,
srcBuffer);
Builder.CreateBr(contBB2);
}
Builder.emitBlock(contBB2);
{
ConditionalDominanceScope domScope(IGF);
// swift_release(tmpRef)
IGF.emitNativeStrongRelease(destReference,
IGF.getDefaultAtomicity());
Builder.CreateBr(doneBB);
}
}
}
Builder.emitBlock(doneBB);
Builder.CreateRetVoid();
}, true /*noinline*/);
}
static llvm::Constant *getDestroyBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy) {
llvm::Type *argTys[] = {existContainerPointerTy};
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_destroy_boxed_opaque_existential_"
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.VoidTy, argTys, [&](IRGenFunction &IGF) {
auto &Builder = IGF.Builder;
auto it = IGF.CurFn->arg_begin();
Address existentialContainer(&*(it++), existLayout.getAlignment(IGM));
auto *metadata = existLayout.loadMetadataRef(IGF, existentialContainer);
auto buffer =
existLayout.projectExistentialBuffer(IGF, existentialContainer);
// Is the value stored inline?
llvm::Value *isInline, *flags;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
auto *inlineBB = IGF.createBasicBlock("inline");
auto *outlineBB = IGF.createBasicBlock("outline");
Builder.CreateCondBr(isInline, inlineBB, outlineBB);
Builder.emitBlock(inlineBB);
{
ConditionalDominanceScope domScope(IGF);
auto *opaquePtrToBuffer =
Builder.CreateBitCast(buffer.getAddress(), IGM.OpaquePtrTy);
emitDestroyCall(IGF, metadata,
Address(opaquePtrToBuffer, buffer.getAlignment()));
Builder.CreateRetVoid();
}
Builder.emitBlock(outlineBB);
{
ConditionalDominanceScope domScope(IGF);
// swift_release(buffer[0])
auto *referenceAddr = Builder.CreateBitCast(
buffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
auto *reference =
Builder.CreateLoad(referenceAddr, buffer.getAlignment());
IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity());
Builder.CreateRetVoid();
}
}, true /*noinline*/);
}
static llvm::Constant *getInitWithTakeBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy) {
llvm::Type *argTys[] = {existContainerPointerTy, existContainerPointerTy};
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_initWithTake_boxed_opaque_existential_"
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.VoidTy, argTys, [&](IRGenFunction &IGF) {
auto &Builder = IGF.Builder;
auto it = IGF.CurFn->arg_begin();
Address dest(&*(it++), existLayout.getAlignment(IGM));
Address src(&*(it++), existLayout.getAlignment(IGM));
auto *metadata = existLayout.loadMetadataRef(IGF, src);
auto srcBuffer = existLayout.projectExistentialBuffer(IGF, src);
auto destBuffer = existLayout.projectExistentialBuffer(IGF, dest);
// Is the value stored inline?
//
llvm::Value *isInline, *flags;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
auto *inlineBB = IGF.createBasicBlock("inline");
auto *outlineBB = IGF.createBasicBlock("outline");
Builder.CreateCondBr(isInline, inlineBB, outlineBB);
Builder.emitBlock(inlineBB);
{
ConditionalDominanceScope domScope(IGF);
emitInitializeWithTakeCall(IGF, metadata,
castToOpaquePtr(IGF, destBuffer),
castToOpaquePtr(IGF, srcBuffer));
Builder.CreateRetVoid();
}
Builder.emitBlock(outlineBB);
{
ConditionalDominanceScope domScope(IGF);
// destBuffer[0] = srcBuffer[0]
auto *srcReferenceAddr = Builder.CreateBitCast(
srcBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
auto *reference =
Builder.CreateLoad(srcReferenceAddr, srcBuffer.getAlignment());
auto *destReferenceAddr = Builder.CreateBitCast(
destBuffer.getAddress(), IGM.RefCountedPtrTy->getPointerTo());
IGF.Builder.CreateStore(
reference,
Address(destReferenceAddr, existLayout.getAlignment(IGF.IGM)));
Builder.CreateRetVoid();
}
}, true /*noinline*/);
}
static llvm::Constant *getInitWithCopyBoxedOpaqueExistentialBufferFunction(
IRGenModule &IGM, OpaqueExistentialLayout existLayout,
llvm::Type *existContainerPointerTy) {
llvm::Type *argTys[] = {existContainerPointerTy, existContainerPointerTy};
llvm::SmallString<40> fnName;
llvm::raw_svector_ostream(fnName)
<< "__swift_initWithCopy_boxed_opaque_existential_"
<< existLayout.getNumTables();
return IGM.getOrCreateHelperFunction(
fnName, IGM.VoidTy, argTys,
[&](IRGenFunction &IGF) {
auto &Builder = IGF.Builder;
auto it = IGF.CurFn->arg_begin();
Address dest(&*(it++), existLayout.getAlignment(IGM));
Address src(&*(it++), existLayout.getAlignment(IGM));
auto *metadata = existLayout.loadMetadataRef(IGF, src);
auto srcBuffer = existLayout.projectExistentialBuffer(IGF, src);
auto destBuffer = existLayout.projectExistentialBuffer(IGF, dest);
// Is the value stored inline?
//
llvm::Value *isInline, *flags;
std::tie(isInline, flags) = emitLoadOfIsInline(IGF, metadata);
auto *inlineBB = IGF.createBasicBlock("inline");
auto *outlineBB = IGF.createBasicBlock("outline");
Builder.CreateCondBr(isInline, inlineBB, outlineBB);
Builder.emitBlock(inlineBB);
{
ConditionalDominanceScope domScope(IGF);
emitInitializeWithCopyCall(IGF, metadata,
castToOpaquePtr(IGF, destBuffer),
castToOpaquePtr(IGF, srcBuffer));
Builder.CreateRetVoid();
}
Builder.emitBlock(outlineBB);
{
ConditionalDominanceScope domScope(IGF);
// destBuffer[0] = srcBuffer[0]
// swift_retain(srcBuffer[0])
initBufferWithCopyOfReference(IGF, existLayout, destBuffer,
srcBuffer);
Builder.CreateRetVoid();
}
},
true /*noinline*/);
}