mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
SILGen unconditional dynamic casts using the new
unconditional_dynamic_cast_addr instruction. Also, fix some major semantic problems with the existing specialization of unconditional dynamic casts by handling optional types and being much more conservative about deciding that a cast is infeasible. This commit regresses specialization slightly by failing to turn indirect dynamic casts into scalar ones when possible; we can fix that easily enough in a follow-up. Swift SVN r19044
This commit is contained in:
462
lib/SIL/DynamicCasts.cpp
Normal file
462
lib/SIL/DynamicCasts.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
//===--- DynamicCasts.cpp - Utilities for dynamic casts -------------------===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2015 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/AST/Types.h"
|
||||
#include "swift/SIL/SILArgument.h"
|
||||
#include "swift/SIL/SILBuilder.h"
|
||||
|
||||
#include "swift/SIL/DynamicCasts.h"
|
||||
|
||||
using namespace swift;
|
||||
using namespace Lowering;
|
||||
|
||||
static DynamicCastFeasibility weakenSuccess(DynamicCastFeasibility v) {
|
||||
if (v == DynamicCastFeasibility::WillSucceed)
|
||||
return DynamicCastFeasibility::MaySucceed;
|
||||
return v;
|
||||
}
|
||||
|
||||
static unsigned getAnyMetatypeDepth(CanType type) {
|
||||
unsigned depth = 0;
|
||||
while (auto metatype = dyn_cast<AnyMetatypeType>(type))
|
||||
type = metatype.getInstanceType();
|
||||
return depth;
|
||||
}
|
||||
|
||||
/// Try to classify the dynamic-cast relationship between two types.
|
||||
DynamicCastFeasibility
|
||||
swift::classifyDynamicCast(Module *M, CanType source, CanType target) {
|
||||
if (source == target) return DynamicCastFeasibility::WillSucceed;
|
||||
|
||||
auto sourceObject = source.getAnyOptionalObjectType();
|
||||
auto targetObject = target.getAnyOptionalObjectType();
|
||||
|
||||
// A common level of optionality doesn't affect the feasibility.
|
||||
if (sourceObject && targetObject) {
|
||||
return classifyDynamicCast(M, sourceObject, targetObject);
|
||||
|
||||
// Nor does casting to a more optional type.
|
||||
} else if (targetObject) {
|
||||
return classifyDynamicCast(M, source, targetObject);
|
||||
|
||||
// Casting to a less-optional type can always fail.
|
||||
} else if (sourceObject) {
|
||||
return weakenSuccess(classifyDynamicCast(M, sourceObject, target));
|
||||
}
|
||||
assert(!sourceObject && !targetObject);
|
||||
|
||||
// Assume that casts to or from existential types or involving
|
||||
// dependent types can always succeed. This is over-conservative.
|
||||
if (source->hasArchetype() || source.isExistentialType() ||
|
||||
target->hasArchetype() || target.isExistentialType())
|
||||
return DynamicCastFeasibility::MaySucceed;
|
||||
|
||||
// Metatype casts.
|
||||
while (auto sourceMetatype = dyn_cast<AnyMetatypeType>(source)) {
|
||||
auto targetMetatype = dyn_cast<AnyMetatypeType>(target);
|
||||
if (!targetMetatype) return DynamicCastFeasibility::WillFail;
|
||||
|
||||
source = sourceMetatype.getInstanceType();
|
||||
target = targetMetatype.getInstanceType();
|
||||
|
||||
// TODO: prove that some conversions to existential metatype will
|
||||
// obviously succeed/fail.
|
||||
// TODO: prove that some conversions from class existential metatype
|
||||
// to a concrete non-class metatype will obviously fail.
|
||||
if (isa<ExistentialMetatypeType>(sourceMetatype) ||
|
||||
isa<ExistentialMetatypeType>(targetMetatype))
|
||||
return (getAnyMetatypeDepth(source) == getAnyMetatypeDepth(target)
|
||||
? DynamicCastFeasibility::MaySucceed
|
||||
: DynamicCastFeasibility::WillFail);
|
||||
}
|
||||
|
||||
// Class casts.
|
||||
auto sourceClass = source.getClassOrBoundGenericClass();
|
||||
auto targetClass = target.getClassOrBoundGenericClass();
|
||||
if (sourceClass && targetClass) {
|
||||
if (target->isSuperclassOf(source, nullptr))
|
||||
return DynamicCastFeasibility::WillSucceed;
|
||||
if (source->isSuperclassOf(target, nullptr))
|
||||
return DynamicCastFeasibility::MaySucceed;
|
||||
|
||||
// FIXME: bridged types, e.g. CF <-> NS (but not for metatypes).
|
||||
return DynamicCastFeasibility::WillFail;
|
||||
}
|
||||
|
||||
// FIXME: tuple conversions?
|
||||
|
||||
// FIXME: bridged types, e.g. NSString <-> String (but not for metatypes).
|
||||
return DynamicCastFeasibility::WillFail;
|
||||
}
|
||||
|
||||
static unsigned getOptionalDepth(CanType type) {
|
||||
unsigned depth = 0;
|
||||
while (CanType objectType = type.getAnyOptionalObjectType()) {
|
||||
depth++;
|
||||
type = objectType;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct Source {
|
||||
SILValue Value;
|
||||
CanType FormalType;
|
||||
CastConsumptionKind Consumption;
|
||||
|
||||
bool isAddress() const { return Value.getType().isAddress(); }
|
||||
IsTake_t shouldTake() const {
|
||||
return IsTake_t(Consumption != CastConsumptionKind::CopyOnSuccess);
|
||||
}
|
||||
|
||||
Source() = default;
|
||||
Source(SILValue value, CanType formalType, CastConsumptionKind consumption)
|
||||
: Value(value), FormalType(formalType), Consumption(consumption) {}
|
||||
};
|
||||
|
||||
struct Target {
|
||||
SILValue Address;
|
||||
SILType LoweredType;
|
||||
CanType FormalType;
|
||||
|
||||
bool isAddress() const { return (bool) Address; }
|
||||
|
||||
Source asAddressSource() const {
|
||||
assert(isAddress());
|
||||
return { Address, FormalType, CastConsumptionKind::TakeAlways };
|
||||
}
|
||||
Source asScalarSource(SILValue value) const {
|
||||
assert(!isAddress());
|
||||
assert(!value.getType().isAddress());
|
||||
return { value, FormalType, CastConsumptionKind::TakeAlways };
|
||||
}
|
||||
|
||||
Target() = default;
|
||||
Target(SILValue address, CanType formalType)
|
||||
: Address(address), LoweredType(address.getType()),
|
||||
FormalType(formalType) {}
|
||||
Target(SILType loweredType, CanType formalType)
|
||||
: Address(), LoweredType(loweredType), FormalType(formalType) {}
|
||||
};
|
||||
|
||||
class CastEmitter {
|
||||
SILBuilder &B;
|
||||
SILModule &M;
|
||||
ASTContext &Ctx;
|
||||
SILLocation Loc;
|
||||
public:
|
||||
CastEmitter(SILBuilder &B, Module *swiftModule, SILLocation loc)
|
||||
: B(B), M(B.getModule()), Ctx(M.getASTContext()), Loc(loc) {}
|
||||
|
||||
Source emitTopLevel(Source source, Target target) {
|
||||
unsigned sourceOptDepth = getOptionalDepth(source.FormalType);
|
||||
unsigned targetOptDepth = getOptionalDepth(target.FormalType);
|
||||
|
||||
assert(sourceOptDepth <= targetOptDepth);
|
||||
return emitAndInjectIntoOptionals(source, target,
|
||||
targetOptDepth - sourceOptDepth);
|
||||
}
|
||||
|
||||
private:
|
||||
const TypeLowering &getTypeLowering(SILType type) {
|
||||
return M.Types.getTypeLowering(type);
|
||||
}
|
||||
|
||||
SILValue getOwnedScalar(Source source, const TypeLowering &srcTL) {
|
||||
assert(!source.isAddress());
|
||||
if (!source.shouldTake())
|
||||
srcTL.emitRetainValue(B, Loc, source.Value);
|
||||
return source.Value;
|
||||
}
|
||||
|
||||
Source putOwnedScalar(SILValue scalar, Target target) {
|
||||
assert(scalar.getType() == target.LoweredType.getObjectType());
|
||||
if (!target.isAddress())
|
||||
return target.asScalarSource(scalar);
|
||||
|
||||
auto &targetTL = getTypeLowering(target.LoweredType);
|
||||
targetTL.emitStoreOfCopy(B, Loc, scalar, target.Address,
|
||||
IsInitialization);
|
||||
return target.asAddressSource();
|
||||
}
|
||||
|
||||
Source emitSameType(Source source, Target target) {
|
||||
assert(source.FormalType == target.FormalType);
|
||||
|
||||
auto &srcTL = getTypeLowering(source.Value.getType());
|
||||
|
||||
// The destination always wants a +1 value, so make the source
|
||||
// +1 if it's a scalar.
|
||||
if (!source.isAddress()) {
|
||||
source.Value = getOwnedScalar(source, srcTL);
|
||||
source.Consumption = CastConsumptionKind::TakeAlways;
|
||||
}
|
||||
|
||||
// If we've got a scalar and want a scalar, the source is
|
||||
// exactly right.
|
||||
if (!target.isAddress() && !source.isAddress())
|
||||
return source;
|
||||
|
||||
// If the destination wants a non-address value, load
|
||||
if (!target.isAddress()) {
|
||||
SILValue value = srcTL.emitLoadOfCopy(B, Loc, source.Value,
|
||||
source.shouldTake());
|
||||
return target.asScalarSource(value);
|
||||
}
|
||||
|
||||
if (source.isAddress()) {
|
||||
srcTL.emitCopyInto(B, Loc, source.Value, target.Address,
|
||||
source.shouldTake(), IsInitialization);
|
||||
} else {
|
||||
srcTL.emitStoreOfCopy(B, Loc, source.Value, target.Address,
|
||||
IsInitialization);
|
||||
}
|
||||
return target.asAddressSource();
|
||||
}
|
||||
|
||||
Source emit(Source source, Target target) {
|
||||
if (source.FormalType == target.FormalType)
|
||||
return emitSameType(source, target);
|
||||
|
||||
// Handle subtype conversions involving optionals.
|
||||
OptionalTypeKind sourceOptKind;
|
||||
if (auto sourceObjectType =
|
||||
source.FormalType.getAnyOptionalObjectType(sourceOptKind)) {
|
||||
return emitOptionalToOptional(source, sourceOptKind, sourceObjectType,
|
||||
target);
|
||||
}
|
||||
assert(!target.FormalType.getAnyOptionalObjectType());
|
||||
|
||||
// The only other thing we return WillSucceed for currently is
|
||||
// an upcast.
|
||||
auto &srcTL = getTypeLowering(source.Value.getType());
|
||||
SILValue value;
|
||||
if (source.isAddress()) {
|
||||
value = srcTL.emitLoadOfCopy(B, Loc, source.Value, source.shouldTake());
|
||||
} else {
|
||||
value = getOwnedScalar(source, srcTL);
|
||||
}
|
||||
value = B.createUpcast(Loc, value, target.LoweredType.getObjectType());
|
||||
return putOwnedScalar(value, target);
|
||||
}
|
||||
|
||||
Source emitAndInjectIntoOptionals(Source source, Target target,
|
||||
unsigned depth) {
|
||||
if (depth == 0)
|
||||
return emit(source, target);
|
||||
|
||||
// Recurse.
|
||||
EmitSomeState state;
|
||||
Target objectTarget = prepareForEmitSome(target, state);
|
||||
Source objectSource =
|
||||
emitAndInjectIntoOptionals(source, objectTarget, depth - 1);
|
||||
return emitSome(objectSource, target, state);
|
||||
}
|
||||
|
||||
Source emitOptionalToOptional(Source source,
|
||||
OptionalTypeKind sourceOptKind,
|
||||
CanType sourceObjectType,
|
||||
Target target) {
|
||||
// Switch on the incoming value.
|
||||
SILBasicBlock *contBB = B.splitBlockForFallthrough();
|
||||
SILBasicBlock *noneBB = B.splitBlockForFallthrough();
|
||||
SILBasicBlock *someBB = B.splitBlockForFallthrough();
|
||||
|
||||
// Emit the switch.
|
||||
std::pair<EnumElementDecl*, SILBasicBlock*> cases[] = {
|
||||
{ Ctx.getOptionalSomeDecl(sourceOptKind), someBB },
|
||||
{ Ctx.getOptionalNoneDecl(sourceOptKind), noneBB },
|
||||
};
|
||||
if (source.isAddress()) {
|
||||
B.createSwitchEnumAddr(Loc, source.Value, /*default*/ nullptr, cases);
|
||||
} else {
|
||||
B.createSwitchEnum(Loc, source.Value, /*default*/ nullptr, cases);
|
||||
}
|
||||
|
||||
// Create the Some block, which recurses.
|
||||
B.setInsertionPoint(someBB);
|
||||
{
|
||||
auto sourceSomeDecl = Ctx.getOptionalSomeDecl(sourceOptKind);
|
||||
|
||||
SILType loweredSourceObjectType =
|
||||
source.Value.getType().getEnumElementType(sourceSomeDecl, M);
|
||||
|
||||
// Form the target for the optional object.
|
||||
EmitSomeState state;
|
||||
Target objectTarget = prepareForEmitSome(target, state);
|
||||
|
||||
// Form the source value.
|
||||
AllocStackInst *sourceTemp = nullptr;
|
||||
Source objectSource;
|
||||
if (source.isAddress()) {
|
||||
// TODO: add an instruction for non-destructively getting a
|
||||
// specific element's data.
|
||||
SILValue sourceAddr = source.Value;
|
||||
if (!source.shouldTake()) {
|
||||
sourceTemp = B.createAllocStack(Loc,
|
||||
sourceAddr.getType().getObjectType());
|
||||
sourceAddr = sourceTemp->getAddressResult();
|
||||
B.createCopyAddr(Loc, source.Value, sourceAddr, IsNotTake,
|
||||
IsInitialization);
|
||||
}
|
||||
sourceAddr = B.createUncheckedTakeEnumDataAddr(Loc, sourceAddr,
|
||||
sourceSomeDecl, loweredSourceObjectType);
|
||||
objectSource = Source(sourceAddr, sourceObjectType,
|
||||
CastConsumptionKind::TakeAlways);
|
||||
} else {
|
||||
SILValue sourceObjectValue =
|
||||
new (M) SILArgument(loweredSourceObjectType, someBB);
|
||||
objectSource = Source(sourceObjectValue, sourceObjectType,
|
||||
source.Consumption);
|
||||
}
|
||||
|
||||
Source resultObject = emit(objectSource, objectTarget);
|
||||
|
||||
// Deallocate the source temporary if we needed one.
|
||||
if (sourceTemp) {
|
||||
B.createDeallocStack(Loc, sourceTemp->getContainerResult());
|
||||
}
|
||||
|
||||
Source result = emitSome(resultObject, target, state);
|
||||
assert(result.isAddress() == target.isAddress());
|
||||
if (target.isAddress()) {
|
||||
B.createBranch(Loc, contBB);
|
||||
} else {
|
||||
B.createBranch(Loc, contBB, { result.Value });
|
||||
}
|
||||
}
|
||||
|
||||
// Create the None block.
|
||||
B.setInsertionPoint(noneBB);
|
||||
{
|
||||
Source result = emitNone(target);
|
||||
assert(result.isAddress() == target.isAddress());
|
||||
if (target.isAddress()) {
|
||||
B.createBranch(Loc, contBB);
|
||||
} else {
|
||||
B.createBranch(Loc, contBB, { result.Value });
|
||||
}
|
||||
}
|
||||
|
||||
// Continuation block.
|
||||
B.setInsertionPoint(contBB);
|
||||
if (target.isAddress()) {
|
||||
return target.asAddressSource();
|
||||
} else {
|
||||
SILValue result = new (M) SILArgument(target.LoweredType, contBB);
|
||||
return target.asScalarSource(result);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmitSomeState {
|
||||
EnumElementDecl *SomeDecl;
|
||||
};
|
||||
|
||||
Target prepareForEmitSome(Target target, EmitSomeState &state) {
|
||||
OptionalTypeKind optKind;
|
||||
auto objectType = target.FormalType.getAnyOptionalObjectType(optKind);
|
||||
assert(objectType && "emitting Some into non-optional type");
|
||||
|
||||
auto someDecl = Ctx.getOptionalSomeDecl(optKind);
|
||||
state.SomeDecl = someDecl;
|
||||
|
||||
SILType loweredObjectType =
|
||||
target.LoweredType.getEnumElementType(someDecl, M);
|
||||
|
||||
if (target.isAddress()) {
|
||||
SILValue objectAddr =
|
||||
B.createInitEnumDataAddr(Loc, target.Address, someDecl,
|
||||
loweredObjectType);
|
||||
return { objectAddr, objectType };
|
||||
} else {
|
||||
return { loweredObjectType, objectType };
|
||||
}
|
||||
}
|
||||
|
||||
Source emitSome(Source source, Target target, EmitSomeState &state) {
|
||||
// If our target is an address, prepareForEmitSome should have set this
|
||||
// up so that we emitted directly into
|
||||
if (target.isAddress()) {
|
||||
B.createInjectEnumAddr(Loc, target.Address, state.SomeDecl);
|
||||
return target.asAddressSource();
|
||||
} else {
|
||||
auto &srcTL = getTypeLowering(source.Value.getType());
|
||||
auto sourceObject = getOwnedScalar(source, srcTL);
|
||||
auto source = B.createEnum(Loc, sourceObject, state.SomeDecl,
|
||||
target.LoweredType);
|
||||
return target.asScalarSource(source);
|
||||
}
|
||||
}
|
||||
|
||||
Source emitNone(Target target) {
|
||||
OptionalTypeKind optKind;
|
||||
auto objectType = target.FormalType.getAnyOptionalObjectType(optKind);
|
||||
assert(objectType && "emitting None into non-optional type");
|
||||
(void) objectType;
|
||||
|
||||
auto noneDecl = Ctx.getOptionalNoneDecl(optKind);
|
||||
|
||||
if (target.isAddress()) {
|
||||
B.createInjectEnumAddr(Loc, target.Address, noneDecl);
|
||||
return target.asAddressSource();
|
||||
} else {
|
||||
SILValue res = B.createEnum(Loc, nullptr, noneDecl, target.LoweredType);
|
||||
return target.asScalarSource(res);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Emit an unconditional scalar cast that's known to succeed.
|
||||
SILValue
|
||||
swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, Module *M,
|
||||
SILLocation loc, SILValue value,
|
||||
SILType loweredTargetType,
|
||||
CanType sourceType,
|
||||
CanType targetType) {
|
||||
assert(classifyDynamicCast(M, sourceType, targetType)
|
||||
== DynamicCastFeasibility::WillSucceed);
|
||||
|
||||
// Fast path changes that don't change the type.
|
||||
if (sourceType == targetType)
|
||||
return value;
|
||||
|
||||
Source source(value, sourceType, CastConsumptionKind::TakeAlways);
|
||||
Target target(loweredTargetType, targetType);
|
||||
Source result = CastEmitter(B, M, loc).emitTopLevel(source, target);
|
||||
assert(!result.isAddress());
|
||||
assert(result.Value.getType() == loweredTargetType);
|
||||
assert(result.Consumption == CastConsumptionKind::TakeAlways);
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
void swift::emitSuccessfulIndirectUnconditionalCast(SILBuilder &B, Module *M,
|
||||
SILLocation loc,
|
||||
CastConsumptionKind consumption,
|
||||
SILValue src,
|
||||
CanType sourceType,
|
||||
SILValue dest,
|
||||
CanType targetType) {
|
||||
assert(classifyDynamicCast(M, sourceType, targetType)
|
||||
== DynamicCastFeasibility::WillSucceed);
|
||||
|
||||
assert(src.getType().isAddress());
|
||||
assert(dest.getType().isAddress());
|
||||
|
||||
Source source(src, sourceType, consumption);
|
||||
Target target(dest, targetType);
|
||||
Source result = CastEmitter(B, M, loc).emitTopLevel(source, target);
|
||||
assert(result.isAddress());
|
||||
assert(result.Value == dest);
|
||||
assert(result.Consumption == CastConsumptionKind::TakeAlways);
|
||||
(void) result;
|
||||
}
|
||||
Reference in New Issue
Block a user