mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
2093 lines
81 KiB
C++
2093 lines
81 KiB
C++
//===--- GenExistential.cpp - Swift IR Generation for Existential Types ---===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements IR generation for existential types in Swift.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "GenExistential.h"
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/AST/Decl.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));
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
/// 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);
|
|
|
|
|
|
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
|
|
/// protocol<Printable, Serializable>
|
|
/// 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 {
|
|
auto objPtrTy = dest.getAddress()->getType();
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 override {
|
|
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)));
|
|
}
|
|
|
|
/// 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();
|
|
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);
|
|
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);
|
|
|
|
// 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)
|
|
const override {
|
|
// Copy the instance pointer.
|
|
llvm::Value *value = src.claimNext();
|
|
dest.add(value);
|
|
asDerived().emitValueRetain(IGF, value);
|
|
|
|
// Transfer the witness table pointers.
|
|
src.transferInto(dest, getNumStoredProtocols());
|
|
}
|
|
|
|
void consume(IRGenFunction &IGF, Explosion &src)
|
|
const override {
|
|
// Copy the instance pointer.
|
|
llvm::Value *value = src.claimNext();
|
|
asDerived().emitValueRelease(IGF, value);
|
|
|
|
// Throw out the witness table pointers.
|
|
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.
|
|
src.claim(getNumStoredProtocols());
|
|
}
|
|
|
|
void destroy(IRGenFunction &IGF, Address addr, SILType T) const override {
|
|
llvm::Value *value = asDerived().loadValue(IGF, addr);
|
|
asDerived().emitValueRelease(IGF, value);
|
|
}
|
|
|
|
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) const {
|
|
IGF.emitUnownedRetain(value, Refcounting);
|
|
}
|
|
|
|
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value) const {
|
|
IGF.emitUnownedRelease(value, Refcounting);
|
|
}
|
|
|
|
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) const {
|
|
// do nothing
|
|
}
|
|
|
|
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value) 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) const override {
|
|
IGF.emitStrongRetain(e.claimNext(), Refcounting);
|
|
e.claim(getNumStoredProtocols());
|
|
}
|
|
|
|
void strongRelease(IRGenFunction &IGF, Explosion &e) const override {
|
|
IGF.emitStrongRelease(e.claimNext(), Refcounting);
|
|
e.claim(getNumStoredProtocols());
|
|
}
|
|
|
|
void strongRetainUnowned(IRGenFunction &IGF, Explosion &e) const override {
|
|
IGF.emitStrongRetainUnowned(e.claimNext(), Refcounting);
|
|
e.claim(getNumStoredProtocols());
|
|
}
|
|
|
|
void strongRetainUnownedRelease(IRGenFunction &IGF,
|
|
Explosion &e) const override {
|
|
IGF.emitStrongRetainAndUnownedRelease(e.claimNext(), Refcounting);
|
|
e.claim(getNumStoredProtocols());
|
|
}
|
|
|
|
void unownedRetain(IRGenFunction &IGF, Explosion &e) const override {
|
|
IGF.emitUnownedRetain(e.claimNext(), Refcounting);
|
|
e.claim(getNumStoredProtocols());
|
|
}
|
|
|
|
void unownedRelease(IRGenFunction &IGF, Explosion &e) const override {
|
|
IGF.emitUnownedRelease(e.claimNext(), Refcounting);
|
|
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) const {
|
|
IGF.emitStrongRetain(value, Refcounting);
|
|
}
|
|
|
|
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value) const {
|
|
IGF.emitStrongRelease(value, Refcounting);
|
|
}
|
|
|
|
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) const {
|
|
// do nothing
|
|
}
|
|
|
|
void emitValueRelease(IRGenFunction &IGF, llvm::Value *value) 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 ErrorProtocolEntry;
|
|
ReferenceCounting Refcounting;
|
|
|
|
public:
|
|
ErrorExistentialTypeInfo(llvm::PointerType *storage,
|
|
Size size, SpareBitVector spareBits,
|
|
Alignment align,
|
|
const ProtocolEntry &errorProtocolEntry,
|
|
ReferenceCounting refcounting)
|
|
: HeapTypeInfo(storage, size, spareBits, align),
|
|
ErrorProtocolEntry(errorProtocolEntry),
|
|
Refcounting(refcounting) {}
|
|
|
|
ReferenceCounting getReferenceCounting() const {
|
|
// ErrorProtocol uses its own rc entry points.
|
|
return Refcounting;
|
|
}
|
|
|
|
ArrayRef<ProtocolEntry> getStoredProtocols() const {
|
|
return ErrorProtocolEntry;
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static const TypeInfo *createErrorExistentialTypeInfo(IRGenModule &IGM,
|
|
ArrayRef<ProtocolDecl*> protocols) {
|
|
// The ErrorProtocol existential has a special boxed representation. It has
|
|
// space only for witnesses to the ErrorProtocol protocol.
|
|
assert(protocols.size() == 1
|
|
&& *protocols[0]->getKnownProtocolKind() == KnownProtocolKind::ErrorProtocol);
|
|
|
|
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,
|
|
TypeBase *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::ErrorProtocol:
|
|
// ErrorProtocol 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 (auto *protoT = T->getAs<ProtocolType>())
|
|
type = IGM.createNominalType(protoT->getDecl());
|
|
else if (auto *compT = T->getAs<ProtocolCompositionType>())
|
|
// Protocol composition types are not nominal, but we name them anyway.
|
|
type = IGM.createNominalType(compT);
|
|
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, 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, 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();
|
|
});
|
|
}
|
|
|
|
/// 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 _isErrorProtocol(SILType baseTy) {
|
|
llvm::SmallVector<ProtocolDecl*, 1> protos;
|
|
return baseTy.getSwiftRValueType()->isExistentialType(protos)
|
|
&& protos.size() == 1
|
|
&& protos[0]->getKnownProtocolKind()
|
|
&& *protos[0]->getKnownProtocolKind() == KnownProtocolKind::ErrorProtocol;
|
|
}
|
|
#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(_isErrorProtocol(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-ErrorProtocol boxed existentials.
|
|
assert(_isErrorProtocol(destType));
|
|
|
|
auto &destTI = IGF.getTypeInfo(destType).as<ErrorExistentialTypeInfo>();
|
|
auto srcMetadata = IGF.emitTypeMetadataRef(formalSrcType);
|
|
// Should only be one conformance, for the ErrorProtocol 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-ErrorProtocol boxed existentials.
|
|
assert(_isErrorProtocol(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 ErrorProtocol 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::ErrorProtocol: {
|
|
// Bitcast the incoming class reference to ErrorProtocol.
|
|
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 =
|
|
emitProjectBufferCall(IGF, metadata, buffer);
|
|
llvm::Value *dynamicType =
|
|
IGF.Builder.CreateCall(IGF.IGM.getGetDynamicTypeFn(),
|
|
{object, metadata});
|
|
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-ErrorProtocol boxed existentials.
|
|
assert(_isErrorProtocol(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});
|
|
|
|
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;
|
|
}
|