mirror of
https://github.com/apple/swift.git
synced 2026-06-27 12:25:55 +02:00
0ffb7278bc
initialization in-place on demand. Initialize parent metadata references correctly on struct and enum metadata. Also includes several minor improvements related to relative pointers that I was using before deciding to simply switch the parent reference to an absolute reference to get better access patterns. Includes a fix since the earlier commit to make enum metadata writable if they have an unfilled payload size. This didn't show up on Darwin because "constant" is currently unenforced there in global data containing relocations. This patch requires an associated LLDB change which is being submitted in parallel.
707 lines
25 KiB
C++
707 lines
25 KiB
C++
//===--- GenCast.cpp - Swift IR Generation for dynamic casts --------------===//
|
|
//
|
|
// 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 dynamic casts.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "GenCast.h"
|
|
|
|
#include "Explosion.h"
|
|
#include "GenMeta.h"
|
|
#include "GenProto.h"
|
|
#include "IRGenDebugInfo.h"
|
|
#include "IRGenFunction.h"
|
|
#include "IRGenModule.h"
|
|
#include "TypeInfo.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/Module.h"
|
|
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SIL/TypeLowering.h"
|
|
#include "swift/ABI/MetadataValues.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
/// Compute the flags to pass to swift_dynamicCast.
|
|
static DynamicCastFlags getDynamicCastFlags(CastConsumptionKind consumptionKind,
|
|
CheckedCastMode mode) {
|
|
DynamicCastFlags flags = DynamicCastFlags::Default;
|
|
|
|
if (mode == CheckedCastMode::Unconditional)
|
|
flags |= DynamicCastFlags::Unconditional;
|
|
if (shouldDestroyOnFailure(consumptionKind))
|
|
flags |= DynamicCastFlags::DestroyOnFailure;
|
|
if (shouldTakeOnSuccess(consumptionKind))
|
|
flags |= DynamicCastFlags::TakeOnSuccess;
|
|
|
|
return flags;
|
|
}
|
|
|
|
/// Emit a checked cast, starting with a value in memory.
|
|
llvm::Value *irgen::emitCheckedCast(IRGenFunction &IGF,
|
|
Address src,
|
|
CanType srcType,
|
|
Address dest,
|
|
CanType targetType,
|
|
CastConsumptionKind consumptionKind,
|
|
CheckedCastMode mode) {
|
|
// TODO: attempt to specialize this based on the known types.
|
|
|
|
DynamicCastFlags flags = getDynamicCastFlags(consumptionKind, mode);
|
|
|
|
// Cast both addresses to opaque pointer type.
|
|
dest = IGF.Builder.CreateBitCast(dest, IGF.IGM.OpaquePtrTy);
|
|
src = IGF.Builder.CreateBitCast(src, IGF.IGM.OpaquePtrTy);
|
|
|
|
// Load type metadata for the source's static type and the target type.
|
|
llvm::Value *srcMetadata = IGF.emitTypeMetadataRef(srcType);
|
|
llvm::Value *targetMetadata = IGF.emitTypeMetadataRef(targetType);
|
|
|
|
llvm::Value *args[] = {
|
|
dest.getAddress(), src.getAddress(),
|
|
srcMetadata, targetMetadata,
|
|
IGF.IGM.getSize(Size(unsigned(flags)))
|
|
};
|
|
|
|
auto call = IGF.Builder.CreateCall(IGF.IGM.getDynamicCastFn(), args);
|
|
call->setDoesNotThrow();
|
|
|
|
return call;
|
|
}
|
|
|
|
FailableCastResult irgen::emitClassIdenticalCast(IRGenFunction &IGF,
|
|
llvm::Value *from,
|
|
SILType fromType,
|
|
SILType toType) {
|
|
// Check metatype objects directly. Don't try to find their meta-metatype.
|
|
bool isMetatype = isa<MetatypeType>(fromType.getSwiftRValueType());
|
|
if (isMetatype) {
|
|
auto metaType = cast<MetatypeType>(toType.getSwiftRValueType());
|
|
assert(metaType->getRepresentation() != MetatypeRepresentation::ObjC &&
|
|
"not implemented");
|
|
toType = IGF.IGM.SILMod->Types.getLoweredType(metaType.getInstanceType());
|
|
}
|
|
// Emit a reference to the heap metadata for the target type.
|
|
const bool allowConservative = true;
|
|
|
|
// If we're allowed to do a conservative check, try to just use the
|
|
// global class symbol. If the class has been re-allocated, this
|
|
// might not be the heap metadata actually in use, and hence the
|
|
// test might fail; but it's a much faster check.
|
|
// TODO: use ObjC class references
|
|
llvm::Value *targetMetadata;
|
|
if (allowConservative &&
|
|
(targetMetadata =
|
|
tryEmitConstantHeapMetadataRef(IGF.IGM, toType.getSwiftRValueType(),
|
|
/*allowUninitialized*/ true))) {
|
|
// ok
|
|
} else {
|
|
targetMetadata
|
|
= emitClassHeapMetadataRef(IGF, toType.getSwiftRValueType(),
|
|
MetadataValueType::ObjCClass,
|
|
/*allowUninitialized*/ allowConservative);
|
|
}
|
|
|
|
// Handle checking a metatype object's type by directly comparing the address
|
|
// of the metatype value to the subclass's static metatype instance.
|
|
//
|
|
// %1 = value_metatype $Super.Type, %0 : $A
|
|
// checked_cast_br [exact] %1 : $Super.Type to $Sub.Type
|
|
// =>
|
|
// icmp eq %1, @metadata.Sub
|
|
llvm::Value *objectMetadata = isMetatype ? from :
|
|
emitHeapMetadataRefForHeapObject(IGF, from, fromType);
|
|
|
|
objectMetadata = IGF.Builder.CreateBitCast(objectMetadata,
|
|
targetMetadata->getType());
|
|
llvm::Value *cond = IGF.Builder.CreateICmpEQ(objectMetadata, targetMetadata);
|
|
return {cond, from};
|
|
}
|
|
|
|
/// Emit a checked unconditional downcast of a class value.
|
|
llvm::Value *irgen::emitClassDowncast(IRGenFunction &IGF, llvm::Value *from,
|
|
SILType toType, CheckedCastMode mode) {
|
|
// Emit the value we're casting from.
|
|
if (from->getType() != IGF.IGM.Int8PtrTy)
|
|
from = IGF.Builder.CreateBitOrPointerCast(from, IGF.IGM.Int8PtrTy);
|
|
|
|
// Emit a reference to the metadata and figure out what cast
|
|
// function to use.
|
|
llvm::Value *metadataRef;
|
|
llvm::Constant *castFn;
|
|
|
|
// Get the best known type information about the destination type.
|
|
auto destClass = toType.getSwiftRValueType().getClassBound();
|
|
assert(destClass || toType.is<ArchetypeType>());
|
|
|
|
// If the destination type is known to have a Swift-compatible
|
|
// implementation, use the most specific entrypoint.
|
|
if (destClass && hasKnownSwiftImplementation(IGF.IGM, destClass)) {
|
|
metadataRef = IGF.emitTypeMetadataRef(toType.getSwiftRValueType());
|
|
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = IGF.IGM.getDynamicCastClassUnconditionalFn();
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
castFn = IGF.IGM.getDynamicCastClassFn();
|
|
break;
|
|
}
|
|
|
|
// If the destination type is a foreign class or a non-specific
|
|
// class-bounded archetype, use the most general cast entrypoint.
|
|
} else if (toType.is<ArchetypeType>() || destClass->isForeign()) {
|
|
metadataRef = IGF.emitTypeMetadataRef(toType.getSwiftRValueType());
|
|
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = IGF.IGM.getDynamicCastUnknownClassUnconditionalFn();
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
castFn = IGF.IGM.getDynamicCastUnknownClassFn();
|
|
break;
|
|
}
|
|
|
|
// Otherwise, use the ObjC-specific entrypoint.
|
|
} else {
|
|
metadataRef = emitObjCHeapMetadataRef(IGF, destClass);
|
|
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = IGF.IGM.getDynamicCastObjCClassUnconditionalFn();
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
castFn = IGF.IGM.getDynamicCastObjCClassFn();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (metadataRef->getType() != IGF.IGM.Int8PtrTy)
|
|
metadataRef = IGF.Builder.CreateBitCast(metadataRef, IGF.IGM.Int8PtrTy);
|
|
|
|
// Call the (unconditional) dynamic cast.
|
|
auto cc = IGF.IGM.DefaultCC;
|
|
if (auto fun = dyn_cast<llvm::Function>(castFn))
|
|
cc = fun->getCallingConv();
|
|
|
|
auto call
|
|
= IGF.Builder.CreateCall(castFn, {from, metadataRef});
|
|
// FIXME: Eventually, we may want to throw.
|
|
call->setCallingConv(cc);
|
|
call->setDoesNotThrow();
|
|
|
|
llvm::Type *subTy = IGF.getTypeInfo(toType).getStorageType();
|
|
return IGF.Builder.CreateBitCast(call, subTy);
|
|
}
|
|
|
|
/// Emit a checked cast of a metatype.
|
|
void irgen::emitMetatypeDowncast(IRGenFunction &IGF,
|
|
llvm::Value *metatype,
|
|
CanMetatypeType toMetatype,
|
|
CheckedCastMode mode,
|
|
Explosion &ex) {
|
|
// Pick a runtime entry point and target metadata based on what kind of
|
|
// representation we're casting.
|
|
llvm::Value *castFn;
|
|
llvm::Value *toMetadata;
|
|
|
|
switch (toMetatype->getRepresentation()) {
|
|
case MetatypeRepresentation::Thick: {
|
|
// Get the Swift metadata for the type we're checking.
|
|
toMetadata = IGF.emitTypeMetadataRef(toMetatype.getInstanceType());
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = IGF.IGM.getDynamicCastMetatypeUnconditionalFn();
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
castFn = IGF.IGM.getDynamicCastMetatypeFn();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MetatypeRepresentation::ObjC: {
|
|
assert(IGF.IGM.ObjCInterop && "should have objc runtime");
|
|
|
|
// Get the ObjC metadata for the type we're checking.
|
|
toMetadata = emitClassHeapMetadataRef(IGF, toMetatype.getInstanceType(),
|
|
MetadataValueType::ObjCClass);
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = IGF.IGM.getDynamicCastObjCClassMetatypeUnconditionalFn();
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
castFn = IGF.IGM.getDynamicCastObjCClassMetatypeFn();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MetatypeRepresentation::Thin:
|
|
llvm_unreachable("not implemented");
|
|
}
|
|
|
|
auto cc = IGF.IGM.DefaultCC;
|
|
if (auto fun = dyn_cast<llvm::Function>(castFn))
|
|
cc = fun->getCallingConv();
|
|
|
|
auto call = IGF.Builder.CreateCall(castFn, {metatype, toMetadata});
|
|
call->setCallingConv(cc);
|
|
call->setDoesNotThrow();
|
|
ex.add(call);
|
|
}
|
|
|
|
/// Emit a Protocol* value referencing an ObjC protocol.
|
|
llvm::Value *irgen::emitReferenceToObjCProtocol(IRGenFunction &IGF,
|
|
ProtocolDecl *proto) {
|
|
assert(proto->isObjC() && "not an objc protocol");
|
|
|
|
// Get the address of the global variable the protocol reference gets
|
|
// indirected through.
|
|
llvm::Constant *protocolRefAddr
|
|
= IGF.IGM.getAddrOfObjCProtocolRef(proto, NotForDefinition);
|
|
|
|
// Load the protocol reference.
|
|
Address addr(protocolRefAddr, IGF.IGM.getPointerAlignment());
|
|
return IGF.Builder.CreateLoad(addr);
|
|
}
|
|
|
|
/// Emit a helper function to look up \c numProtocols witness tables given
|
|
/// a value and a type metadata reference.
|
|
///
|
|
/// The function's input type is (value, metadataValue, protocol...)
|
|
/// The function's output type is (value, witnessTable...)
|
|
///
|
|
/// The value is NULL if the cast failed.
|
|
static llvm::Function *emitExistentialScalarCastFn(IRGenModule &IGM,
|
|
unsigned numProtocols,
|
|
CheckedCastMode mode,
|
|
bool checkClassConstraint) {
|
|
// Build the function name.
|
|
llvm::SmallString<32> name;
|
|
{
|
|
llvm::raw_svector_ostream os(name);
|
|
os << "dynamic_cast_existential_";
|
|
os << numProtocols;
|
|
if (checkClassConstraint)
|
|
os << "_class";
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
os << "_unconditional";
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
os << "_conditional";
|
|
break;
|
|
}
|
|
}
|
|
|
|
// See if we already defined this function.
|
|
|
|
if (auto fn = IGM.Module.getFunction(name))
|
|
return fn;
|
|
|
|
// Build the function type.
|
|
|
|
llvm::SmallVector<llvm::Type *, 4> argTys;
|
|
llvm::SmallVector<llvm::Type *, 4> returnTys;
|
|
argTys.push_back(IGM.Int8PtrTy);
|
|
argTys.push_back(IGM.TypeMetadataPtrTy);
|
|
returnTys.push_back(IGM.Int8PtrTy);
|
|
for (unsigned i = 0; i < numProtocols; ++i) {
|
|
argTys.push_back(IGM.ProtocolDescriptorPtrTy);
|
|
returnTys.push_back(IGM.WitnessTablePtrTy);
|
|
}
|
|
|
|
llvm::Type *returnTy = llvm::StructType::get(IGM.getLLVMContext(), returnTys);
|
|
|
|
auto fnTy = llvm::FunctionType::get(returnTy, argTys, /*vararg*/ false);
|
|
auto fn = llvm::Function::Create(fnTy, llvm::GlobalValue::PrivateLinkage,
|
|
llvm::Twine(name), IGM.getModule());
|
|
fn->setAttributes(IGM.constructInitialAttributes());
|
|
|
|
IRGenFunction IGF(IGM, fn);
|
|
if (IGM.DebugInfo)
|
|
IGM.DebugInfo->emitArtificialFunction(IGF, fn);
|
|
Explosion args = IGF.collectParameters();
|
|
|
|
auto value = args.claimNext();
|
|
auto ref = args.claimNext();
|
|
auto failBB = IGF.createBasicBlock("fail");
|
|
auto conformsToProtocol = IGM.getConformsToProtocolFn();
|
|
|
|
Explosion rets;
|
|
rets.add(value);
|
|
|
|
// Check the class constraint if necessary.
|
|
if (checkClassConstraint) {
|
|
auto isClass = IGF.Builder.CreateCall(IGM.getIsClassTypeFn(), ref);
|
|
auto contBB = IGF.createBasicBlock("cont");
|
|
IGF.Builder.CreateCondBr(isClass, contBB, failBB);
|
|
IGF.Builder.emitBlock(contBB);
|
|
}
|
|
|
|
// Look up each protocol conformance we want.
|
|
for (unsigned i = 0; i < numProtocols; ++i) {
|
|
auto proto = args.claimNext();
|
|
auto witness = IGF.Builder.CreateCall(conformsToProtocol, {ref, proto});
|
|
auto isNull = IGF.Builder.CreateICmpEQ(witness,
|
|
llvm::ConstantPointerNull::get(IGM.WitnessTablePtrTy));
|
|
auto contBB = IGF.createBasicBlock("cont");
|
|
IGF.Builder.CreateCondBr(isNull, failBB, contBB);
|
|
|
|
IGF.Builder.emitBlock(contBB);
|
|
rets.add(witness);
|
|
}
|
|
|
|
// If we succeeded, return the witnesses.
|
|
IGF.emitScalarReturn(returnTy, rets);
|
|
|
|
// If we failed, return nil or trap.
|
|
IGF.Builder.emitBlock(failBB);
|
|
switch (mode) {
|
|
case CheckedCastMode::Conditional: {
|
|
auto null = llvm::ConstantStruct::getNullValue(returnTy);
|
|
IGF.Builder.CreateRet(null);
|
|
break;
|
|
}
|
|
|
|
case CheckedCastMode::Unconditional: {
|
|
llvm::Function *trapIntrinsic = llvm::Intrinsic::getDeclaration(&IGM.Module,
|
|
llvm::Intrinsic::ID::trap);
|
|
IGF.Builder.CreateCall(trapIntrinsic, {});
|
|
IGF.Builder.CreateUnreachable();
|
|
break;
|
|
}
|
|
}
|
|
|
|
return fn;
|
|
}
|
|
|
|
void irgen::emitMetatypeToObjectDowncast(IRGenFunction &IGF,
|
|
llvm::Value *metatypeValue,
|
|
CanAnyMetatypeType type,
|
|
CheckedCastMode mode,
|
|
Explosion &ex) {
|
|
// If ObjC interop is enabled, casting a metatype to AnyObject succeeds
|
|
// if the metatype is for a class.
|
|
auto triviallyFail = [&] {
|
|
ex.add(llvm::ConstantPointerNull::get(IGF.IGM.ObjCPtrTy));
|
|
};
|
|
|
|
if (!IGF.IGM.ObjCInterop)
|
|
return triviallyFail();
|
|
|
|
switch (type->getRepresentation()) {
|
|
case MetatypeRepresentation::ObjC:
|
|
// Metatypes that can be represented as ObjC trivially cast to AnyObject.
|
|
ex.add(IGF.Builder.CreateBitCast(metatypeValue, IGF.IGM.ObjCPtrTy));
|
|
return;
|
|
|
|
case MetatypeRepresentation::Thin:
|
|
// Metatypes that can be thin would never be classes.
|
|
// TODO: Final class metatypes could in principle be thin.
|
|
assert(!type.getInstanceType()->mayHaveSuperclass()
|
|
&& "classes should not have thin metatypes (yet)");
|
|
return triviallyFail();
|
|
|
|
case MetatypeRepresentation::Thick: {
|
|
auto instanceTy = type.getInstanceType();
|
|
// Is the type obviously a class?
|
|
if (instanceTy->mayHaveSuperclass()) {
|
|
// Get the ObjC metadata for the class.
|
|
auto heapMetadata = emitClassHeapMetadataRefForMetatype(IGF,metatypeValue,
|
|
instanceTy);
|
|
ex.add(IGF.Builder.CreateBitCast(heapMetadata, IGF.IGM.ObjCPtrTy));
|
|
return;
|
|
}
|
|
|
|
// Is the type obviously not a class?
|
|
if (!isa<ArchetypeType>(instanceTy)
|
|
&& !isa<ExistentialMetatypeType>(type))
|
|
return triviallyFail();
|
|
|
|
// Ask the runtime whether this is class metadata.
|
|
llvm::Constant *castFn;
|
|
switch (mode) {
|
|
case CheckedCastMode::Conditional:
|
|
castFn = IGF.IGM.getDynamicCastMetatypeToObjectConditionalFn();
|
|
break;
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = IGF.IGM.getDynamicCastMetatypeToObjectUnconditionalFn();
|
|
break;
|
|
}
|
|
|
|
auto cc = IGF.IGM.DefaultCC;
|
|
if (auto fun = dyn_cast<llvm::Function>(castFn))
|
|
cc = fun->getCallingConv();
|
|
|
|
auto call = IGF.Builder.CreateCall(castFn, metatypeValue);
|
|
call->setCallingConv(cc);
|
|
ex.add(call);
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// Emit a checked cast to a protocol or protocol composition.
|
|
void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
|
|
llvm::Value *value,
|
|
SILType srcType,
|
|
SILType destType,
|
|
CheckedCastMode mode,
|
|
Optional<MetatypeRepresentation> metatypeKind,
|
|
Explosion &ex) {
|
|
SmallVector<ProtocolDecl*, 4> allProtos;
|
|
destType.getSwiftRValueType().getAnyExistentialTypeProtocols(allProtos);
|
|
|
|
// Look up witness tables for the protocols that need them and get
|
|
// references to the ObjC Protocol* values for the objc protocols.
|
|
SmallVector<llvm::Value*, 4> objcProtos;
|
|
SmallVector<llvm::Value*, 4> witnessTableProtos;
|
|
|
|
bool hasClassConstraint = false;
|
|
bool hasClassConstraintByProtocol = false;
|
|
|
|
for (auto proto : allProtos) {
|
|
// If the protocol introduces a class constraint, track whether we need
|
|
// to check for it independent of protocol witnesses.
|
|
if (proto->requiresClass()) {
|
|
hasClassConstraint = true;
|
|
if (proto->getKnownProtocolKind()
|
|
&& *proto->getKnownProtocolKind() == KnownProtocolKind::AnyObject) {
|
|
// AnyObject only requires that the type be a class.
|
|
continue;
|
|
}
|
|
|
|
// If this protocol is class-constrained but not AnyObject, checking its
|
|
// conformance will check the class constraint too.
|
|
hasClassConstraintByProtocol = true;
|
|
}
|
|
|
|
if (Lowering::TypeConverter::protocolRequiresWitnessTable(proto)) {
|
|
auto descriptor = emitProtocolDescriptorRef(IGF, proto);
|
|
witnessTableProtos.push_back(descriptor);
|
|
}
|
|
|
|
if (!proto->isObjC())
|
|
continue;
|
|
|
|
objcProtos.push_back(emitReferenceToObjCProtocol(IGF, proto));
|
|
}
|
|
|
|
llvm::Type *resultType;
|
|
if (metatypeKind) {
|
|
switch (*metatypeKind) {
|
|
case MetatypeRepresentation::Thin:
|
|
llvm_unreachable("can't cast to thin metatype");
|
|
case MetatypeRepresentation::Thick:
|
|
resultType = IGF.IGM.TypeMetadataPtrTy;
|
|
break;
|
|
case MetatypeRepresentation::ObjC:
|
|
resultType = IGF.IGM.ObjCClassPtrTy;
|
|
break;
|
|
}
|
|
} else {
|
|
auto schema = IGF.getTypeInfo(destType).getSchema();
|
|
resultType = schema[0].getScalarType();
|
|
}
|
|
// We only need to check the class constraint for metatype casts where
|
|
// no protocol conformance indirectly requires the constraint for us.
|
|
bool checkClassConstraint =
|
|
(bool)metatypeKind && hasClassConstraint && !hasClassConstraintByProtocol;
|
|
|
|
llvm::Value *resultValue = value;
|
|
|
|
// If we don't have anything we really need to check, then trivially succeed.
|
|
if (objcProtos.empty() && witnessTableProtos.empty() &&
|
|
!checkClassConstraint) {
|
|
resultValue = IGF.Builder.CreateBitCast(value, resultType);
|
|
ex.add(resultValue);
|
|
return;
|
|
}
|
|
|
|
// Check the ObjC protocol conformances if there were any.
|
|
llvm::Value *objcCast = nullptr;
|
|
if (!objcProtos.empty()) {
|
|
// Get the ObjC instance or class object to check for these conformances.
|
|
llvm::Value *objcObject;
|
|
if (metatypeKind) {
|
|
switch (*metatypeKind) {
|
|
case MetatypeRepresentation::Thin:
|
|
llvm_unreachable("can't cast to thin metatype");
|
|
case MetatypeRepresentation::Thick: {
|
|
// The metadata might be for a non-class type, which wouldn't have
|
|
// an ObjC class object.
|
|
objcObject = nullptr;
|
|
break;
|
|
}
|
|
case MetatypeRepresentation::ObjC:
|
|
// Metatype is already an ObjC object.
|
|
objcObject = value;
|
|
break;
|
|
}
|
|
} else {
|
|
// Class instance is already an ObjC object.
|
|
objcObject = value;
|
|
}
|
|
if (objcObject)
|
|
objcObject = IGF.Builder.CreateBitCast(objcObject,
|
|
IGF.IGM.UnknownRefCountedPtrTy);
|
|
|
|
// Pick the cast function based on the cast mode and on whether we're
|
|
// casting a Swift metatype or ObjC object.
|
|
llvm::Value *castFn;
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
castFn = objcObject
|
|
? IGF.IGM.getDynamicCastObjCProtocolUnconditionalFn()
|
|
: IGF.IGM.getDynamicCastTypeToObjCProtocolUnconditionalFn();
|
|
break;
|
|
case CheckedCastMode::Conditional:
|
|
castFn = objcObject
|
|
? IGF.IGM.getDynamicCastObjCProtocolConditionalFn()
|
|
: IGF.IGM.getDynamicCastTypeToObjCProtocolConditionalFn();
|
|
break;
|
|
}
|
|
llvm::Value *objcCastObject = objcObject ? objcObject : value;
|
|
|
|
Address protoRefsBuf = IGF.createAlloca(
|
|
llvm::ArrayType::get(IGF.IGM.Int8PtrTy,
|
|
objcProtos.size()),
|
|
IGF.IGM.getPointerAlignment(),
|
|
"objc_protocols");
|
|
protoRefsBuf = IGF.Builder.CreateBitCast(protoRefsBuf,
|
|
IGF.IGM.Int8PtrPtrTy);
|
|
|
|
for (unsigned index : indices(objcProtos)) {
|
|
Address protoRefSlot = IGF.Builder.CreateConstArrayGEP(
|
|
protoRefsBuf, index,
|
|
IGF.IGM.getPointerSize());
|
|
IGF.Builder.CreateStore(objcProtos[index], protoRefSlot);
|
|
++index;
|
|
}
|
|
|
|
|
|
auto cc = IGF.IGM.DefaultCC;
|
|
if (auto fun = dyn_cast<llvm::Function>(castFn))
|
|
cc = fun->getCallingConv();
|
|
|
|
|
|
auto call = IGF.Builder.CreateCall(
|
|
castFn,
|
|
{objcCastObject, IGF.IGM.getSize(Size(objcProtos.size())),
|
|
protoRefsBuf.getAddress()});
|
|
call->setCallingConv(cc);
|
|
objcCast = call;
|
|
resultValue = IGF.Builder.CreateBitCast(objcCast, resultType);
|
|
}
|
|
|
|
// If we don't need to look up any witness tables, we're done.
|
|
if (witnessTableProtos.empty() && !checkClassConstraint) {
|
|
ex.add(resultValue);
|
|
return;
|
|
}
|
|
|
|
// If we're doing a conditional cast, and the ObjC protocol checks failed,
|
|
// then the cast is done.
|
|
Optional<ConditionalDominanceScope> condition;
|
|
llvm::BasicBlock *origBB = nullptr, *successBB = nullptr, *contBB = nullptr;
|
|
if (!objcProtos.empty()) {
|
|
switch (mode) {
|
|
case CheckedCastMode::Unconditional:
|
|
break;
|
|
case CheckedCastMode::Conditional: {
|
|
origBB = IGF.Builder.GetInsertBlock();
|
|
successBB = IGF.createBasicBlock("success");
|
|
contBB = IGF.createBasicBlock("cont");
|
|
auto isNull = IGF.Builder.CreateICmpEQ(objcCast,
|
|
llvm::ConstantPointerNull::get(
|
|
cast<llvm::PointerType>(objcCast->getType())));
|
|
IGF.Builder.CreateCondBr(isNull, contBB, successBB);
|
|
IGF.Builder.emitBlock(successBB);
|
|
condition.emplace(IGF);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the Swift type metadata for the type.
|
|
llvm::Value *metadataValue;
|
|
if (metatypeKind) {
|
|
switch (*metatypeKind) {
|
|
case MetatypeRepresentation::Thin:
|
|
llvm_unreachable("can't cast to thin metatype");
|
|
case MetatypeRepresentation::Thick:
|
|
// The value is already a native metatype.
|
|
metadataValue = value;
|
|
break;
|
|
case MetatypeRepresentation::ObjC:
|
|
// Get the type metadata from the ObjC class, which may be a wrapper.
|
|
metadataValue = emitObjCMetadataRefForMetadata(IGF, value);
|
|
}
|
|
} else {
|
|
// Get the type metadata for the instance.
|
|
metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType);
|
|
}
|
|
|
|
// Look up witness tables for the protocols that need them.
|
|
auto fn = emitExistentialScalarCastFn(IGF.IGM, witnessTableProtos.size(),
|
|
mode, checkClassConstraint);
|
|
|
|
llvm::SmallVector<llvm::Value *, 4> args;
|
|
|
|
if (resultValue->getType() != IGF.IGM.Int8PtrTy)
|
|
resultValue = IGF.Builder.CreateBitCast(resultValue, IGF.IGM.Int8PtrTy);
|
|
args.push_back(resultValue);
|
|
|
|
args.push_back(metadataValue);
|
|
for (auto proto : witnessTableProtos)
|
|
args.push_back(proto);
|
|
|
|
auto valueAndWitnessTables = IGF.Builder.CreateCall(fn, args);
|
|
|
|
resultValue = IGF.Builder.CreateExtractValue(valueAndWitnessTables, 0);
|
|
if (resultValue->getType() != resultType)
|
|
resultValue = IGF.Builder.CreateBitCast(resultValue, resultType);
|
|
ex.add(resultValue);
|
|
|
|
for (unsigned i = 0, e = witnessTableProtos.size(); i < e; ++i) {
|
|
auto wt = IGF.Builder.CreateExtractValue(valueAndWitnessTables, i + 1);
|
|
ex.add(wt);
|
|
}
|
|
|
|
// If we had conditional ObjC checks, join the failure paths.
|
|
if (contBB) {
|
|
condition.reset();
|
|
IGF.Builder.CreateBr(contBB);
|
|
IGF.Builder.emitBlock(contBB);
|
|
|
|
// Return null on the failure path.
|
|
Explosion successEx = std::move(ex);
|
|
ex.reset();
|
|
|
|
while (!successEx.empty()) {
|
|
auto successVal = successEx.claimNext();
|
|
auto failureVal = llvm::Constant::getNullValue(successVal->getType());
|
|
auto phi = IGF.Builder.CreatePHI(successVal->getType(), 2);
|
|
phi->addIncoming(successVal, successBB);
|
|
phi->addIncoming(failureVal, origBB);
|
|
ex.add(phi);
|
|
}
|
|
}
|
|
}
|