Files
swift-mirror/lib/SILOptimizer/Utils/CastOptimizer.cpp
Michael Gottesman dfd40e4443 [cast-optimizer] Move the cast optimizer into its own .cpp file.
Local.cpp was ~3k lines of which 1.5k (i.e. 1/2) was the cast optimizer. This
commit extracts the cast optimizer into its own .cpp and .h file. It is large
enough to stand on its own and allows for Local.cpp to return to being a small
group of helper functions.

I am making some changes in this area due to the change in certain function
conventions caused by the +0-normal-arg work. I am just trying to leave the area
a little cleaner than before.
2018-01-13 18:26:22 -08:00

1558 lines
55 KiB
C++

//===--- CastOptimizer.cpp ------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This file contains local cast optimizations and simplifications.
///
//===----------------------------------------------------------------------===//
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/DynamicCasts.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SIL/TypeLowering.h"
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/SILOptimizer/Utils/CFG.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include <deque>
using namespace swift;
/// Check if is a bridging cast, i.e. one of the sides is
/// a bridged type.
static bool isBridgingCast(CanType SourceType, CanType TargetType) {
// Bridging casts cannot be further simplified.
auto TargetIsBridgeable = TargetType->isBridgeableObjectType();
auto SourceIsBridgeable = SourceType->isBridgeableObjectType();
if (TargetIsBridgeable != SourceIsBridgeable)
return true;
return false;
}
/// If target is a Swift type bridging to an ObjC type,
/// return the ObjC type it bridges to.
/// If target is an ObjC type, return this type.
static Type getCastFromObjC(SILModule &M, CanType source, CanType target) {
return M.getASTContext().getBridgedToObjC(M.getSwiftModule(), target);
}
/// Create a call of _forceBridgeFromObjectiveC_bridgeable or
/// _conditionallyBridgeFromObjectiveC_bridgeable which converts an ObjC
/// instance into a corresponding Swift type, conforming to
/// _ObjectiveCBridgeable.
SILInstruction *CastOptimizer::optimizeBridgedObjCToSwiftCast(
SILInstruction *Inst, bool isConditional, SILValue Src, SILValue Dest,
CanType Source, CanType Target, Type BridgedSourceTy, Type BridgedTargetTy,
SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) {
auto &M = Inst->getModule();
auto Loc = Inst->getLoc();
// The conformance to _BridgedToObjectiveC is statically known.
// Retrieve the bridging operation to be used if a static conformance
// to _BridgedToObjectiveC can be proven.
FuncDecl *BridgeFuncDecl =
isConditional
? M.getASTContext().getConditionallyBridgeFromObjectiveCBridgeable(
nullptr)
: M.getASTContext().getForceBridgeFromObjectiveCBridgeable(nullptr);
assert(BridgeFuncDecl && "_forceBridgeFromObjectiveC should exist");
SILDeclRef FuncDeclRef(BridgeFuncDecl, SILDeclRef::Kind::Func);
// Lookup a function from the stdlib.
SILFunction *BridgedFunc = M.getOrCreateFunction(
Loc, FuncDeclRef, ForDefinition_t::NotForDefinition);
if (!BridgedFunc)
return nullptr;
CanType CanBridgedTy = BridgedTargetTy->getCanonicalType();
SILType SILBridgedTy = SILType::getPrimitiveObjectType(CanBridgedTy);
SILBuilderWithScope Builder(Inst);
SILValue SrcOp;
SILInstruction *NewI = nullptr;
assert(Src->getType().isAddress() && "Source should have an address type");
assert(Dest->getType().isAddress() && "Source should have an address type");
// AnyHashable is a special case - it does not conform to NSObject -
// If AnyHashable - Bail out of the optimization
if (auto DT = Target.getNominalOrBoundGenericNominal()) {
if (DT == M.getASTContext().getAnyHashableDecl()) {
return nullptr;
}
}
// If this is a conditional cast:
// We need a new fail BB in order to add a dealloc_stack to it
SILBasicBlock *ConvFailBB = nullptr;
if (isConditional) {
auto CurrInsPoint = Builder.getInsertionPoint();
ConvFailBB = splitBasicBlockAndBranch(Builder, &(*FailureBB->begin()),
nullptr, nullptr);
Builder.setInsertionPoint(CurrInsPoint);
}
if (SILBridgedTy != Src->getType()) {
// Check if we can simplify a cast into:
// - ObjCTy to _ObjectiveCBridgeable._ObjectiveCType.
// - then convert _ObjectiveCBridgeable._ObjectiveCType to
// a Swift type using _forceBridgeFromObjectiveC.
if (!Src->getType().isLoadable(M)) {
// This code path is never reached in current test cases
// If reached, we'd have to convert from an ObjC Any* to a loadable type
// Should use check_addr / make a source we can actually load
return nullptr;
}
// Generate a load for the source argument.
auto *Load =
Builder.createLoad(Loc, Src, LoadOwnershipQualifier::Unqualified);
// Try to convert the source into the expected ObjC type first.
if (Load->getType() == SILBridgedTy) {
// If type of the source and the expected ObjC type are
// equal, there is no need to generate the conversion
// from ObjCTy to _ObjectiveCBridgeable._ObjectiveCType.
if (isConditional) {
SILBasicBlock *CastSuccessBB = Inst->getFunction()->createBasicBlock();
CastSuccessBB->createPHIArgument(SILBridgedTy,
ValueOwnershipKind::Owned);
Builder.createBranch(Loc, CastSuccessBB, SILValue(Load));
Builder.setInsertionPoint(CastSuccessBB);
SrcOp = CastSuccessBB->getArgument(0);
} else {
SrcOp = Load;
}
} else if (isConditional) {
SILBasicBlock *CastSuccessBB = Inst->getFunction()->createBasicBlock();
CastSuccessBB->createPHIArgument(SILBridgedTy, ValueOwnershipKind::Owned);
NewI = Builder.createCheckedCastBranch(Loc, false, Load, SILBridgedTy,
CastSuccessBB, ConvFailBB);
Builder.setInsertionPoint(CastSuccessBB);
SrcOp = CastSuccessBB->getArgument(0);
} else {
auto cast =
Builder.createUnconditionalCheckedCast(Loc, Load, SILBridgedTy);
NewI = cast;
SrcOp = cast;
}
} else {
SrcOp = Src;
}
// Now emit the a cast from the casted ObjC object into a target type.
// This is done by means of calling _forceBridgeFromObjectiveC or
// _conditionallyBridgeFromObjectiveC_bridgeable from the Target type.
// Lookup the required function in the Target type.
// Lookup the _ObjectiveCBridgeable protocol.
auto BridgedProto =
M.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
auto Conf = *M.getSwiftModule()->lookupConformance(Target, BridgedProto);
auto ParamTypes = BridgedFunc->getLoweredFunctionType()->getParameters();
auto *FuncRef = Builder.createFunctionRef(Loc, BridgedFunc);
auto MetaTy = MetatypeType::get(Target, MetatypeRepresentation::Thick);
auto SILMetaTy = M.Types.getTypeLowering(MetaTy).getLoweredType();
auto *MetaTyVal = Builder.createMetatype(Loc, SILMetaTy);
SmallVector<SILValue, 1> Args;
// Add substitutions
auto SubMap = SubstitutionMap::getProtocolSubstitutions(Conf.getRequirement(),
Target, Conf);
auto SILFnTy = FuncRef->getType();
SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap);
SILFunctionConventions substConv(SubstFnTy.castTo<SILFunctionType>(), M);
// Temporary to hold the intermediate result.
AllocStackInst *Tmp = nullptr;
CanType OptionalTy;
OptionalTypeKind OTK;
SILValue InOutOptionalParam;
if (isConditional) {
// Create a temporary
OptionalTy = OptionalType::get(Dest->getType().getSwiftRValueType())
->getImplementationType()
->getCanonicalType();
OptionalTy.getAnyOptionalObjectType(OTK);
Tmp = Builder.createAllocStack(Loc,
SILType::getPrimitiveObjectType(OptionalTy));
InOutOptionalParam = Tmp;
} else {
InOutOptionalParam = Dest;
}
(void)ParamTypes;
assert(ParamTypes[0].getConvention() == ParameterConvention::Direct_Owned &&
"Parameter should be @owned");
// Emit a retain.
Builder.createRetainValue(Loc, SrcOp, Builder.getDefaultAtomicity());
Args.push_back(InOutOptionalParam);
Args.push_back(SrcOp);
Args.push_back(MetaTyVal);
SmallVector<Substitution, 4> Subs;
Conf.getRequirement()->getGenericSignature()->getSubstitutions(SubMap, Subs);
auto *AI = Builder.createApply(Loc, FuncRef, Subs, Args, false);
// If the source of a cast should be destroyed, emit a release.
if (isa<UnconditionalCheckedCastAddrInst>(Inst)) {
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
}
if (auto *CCABI = dyn_cast<CheckedCastAddrBranchInst>(Inst)) {
if (CCABI->getConsumptionKind() == CastConsumptionKind::TakeAlways) {
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
} else if (CCABI->getConsumptionKind() ==
CastConsumptionKind::TakeOnSuccess) {
// Insert a release in the success BB.
Builder.setInsertionPoint(SuccessBB->begin());
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
}
}
// Results should be checked in case we process a conditional
// case. E.g. casts from NSArray into [SwiftType] may fail, i.e. return .None.
if (isConditional) {
// Copy the temporary into Dest.
// Load from the optional.
auto *SomeDecl = Builder.getASTContext().getOptionalSomeDecl(OTK);
SILBasicBlock *ConvSuccessBB = Inst->getFunction()->createBasicBlock();
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 1> CaseBBs;
CaseBBs.push_back(
std::make_pair(M.getASTContext().getOptionalNoneDecl(), FailureBB));
Builder.createSwitchEnumAddr(Loc, InOutOptionalParam, ConvSuccessBB,
CaseBBs);
Builder.setInsertionPoint(FailureBB->begin());
Builder.createDeallocStack(Loc, Tmp);
Builder.setInsertionPoint(ConvSuccessBB);
auto Addr = Builder.createUncheckedTakeEnumDataAddr(Loc, InOutOptionalParam,
SomeDecl);
Builder.createCopyAddr(Loc, Addr, Dest, IsTake, IsInitialization);
Builder.createDeallocStack(Loc, Tmp);
SmallVector<SILValue, 1> SuccessBBArgs;
Builder.createBranch(Loc, SuccessBB, SuccessBBArgs);
}
EraseInstAction(Inst);
return (NewI) ? NewI : AI;
}
static bool canOptimizeCast(const swift::Type &BridgedTargetTy,
swift::SILModule &M,
swift::SILFunctionConventions &substConv) {
// DestTy is the type which we want to convert to
SILType DestTy =
SILType::getPrimitiveObjectType(BridgedTargetTy->getCanonicalType());
// ConvTy is the return type of the _bridgeToObjectiveCImpl()
auto ConvTy = substConv.getSILResultType().getObjectType();
if (ConvTy == DestTy) {
// Destination is the same type
return true;
}
// Check if a superclass/subclass of the source operand
if (DestTy.isExactSuperclassOf(ConvTy)) {
return true;
}
if (ConvTy.isExactSuperclassOf(DestTy)) {
return true;
}
// check if it is a bridgeable CF type
if (ConvTy.getSwiftRValueType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
DestTy.getSwiftRValueType())) {
return true;
}
if (DestTy.getSwiftRValueType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
ConvTy.getSwiftRValueType())) {
return true;
}
// All else failed - can't optimize this case
return false;
}
/// Create a call of _bridgeToObjectiveC which converts an _ObjectiveCBridgeable
/// instance into a bridged ObjC type.
SILInstruction *CastOptimizer::optimizeBridgedSwiftToObjCCast(
SILInstruction *Inst, CastConsumptionKind ConsumptionKind,
bool isConditional, SILValue Src, SILValue Dest, CanType Source,
CanType Target, Type BridgedSourceTy, Type BridgedTargetTy,
SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) {
auto &M = Inst->getModule();
auto Loc = Inst->getLoc();
bool AddressOnlyType = false;
if (!Src->getType().isLoadable(M) || !Dest->getType().isLoadable(M)) {
AddressOnlyType = true;
}
// Find the _BridgedToObjectiveC protocol.
auto BridgedProto =
M.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
auto Conf = M.getSwiftModule()->lookupConformance(Source, BridgedProto);
assert(Conf && "_ObjectiveCBridgeable conformance should exist");
(void)Conf;
// Generate code to invoke _bridgeToObjectiveC
SILBuilderWithScope Builder(Inst);
auto *NTD = Source.getNominalOrBoundGenericNominal();
assert(NTD);
SmallVector<ValueDecl *, 4> FoundMembers;
ArrayRef<ValueDecl *> Members;
Members = NTD->lookupDirect(M.getASTContext().Id_bridgeToObjectiveC);
if (Members.empty()) {
if (NTD->getDeclContext()->lookupQualified(
NTD->getDeclaredType(), M.getASTContext().Id_bridgeToObjectiveC,
NLOptions::NL_ProtocolMembers, nullptr, FoundMembers)) {
Members = FoundMembers;
// Returned members are starting with the most specialized ones.
// Thus, the first element is what we are looking for.
Members = Members.take_front(1);
}
}
// There should be exactly one implementation of _bridgeToObjectiveC.
if (Members.size() != 1)
return nullptr;
auto BridgeFuncDecl = Members.front();
auto BridgeFuncDeclRef = SILDeclRef(BridgeFuncDecl);
ModuleDecl *Mod =
M.getASTContext().getLoadedModule(M.getASTContext().Id_Foundation);
if (!Mod)
return nullptr;
SmallVector<ValueDecl *, 2> Results;
Mod->lookupMember(Results, Source.getNominalOrBoundGenericNominal(),
M.getASTContext().Id_bridgeToObjectiveC, Identifier());
ArrayRef<ValueDecl *> ResultsRef(Results);
if (ResultsRef.empty()) {
M.getSwiftModule()->lookupMember(
Results, Source.getNominalOrBoundGenericNominal(),
M.getASTContext().Id_bridgeToObjectiveC, Identifier());
ResultsRef = Results;
}
if (ResultsRef.size() != 1)
return nullptr;
auto *resultDecl = Results.front();
auto MemberDeclRef = SILDeclRef(resultDecl);
auto *BridgedFunc = M.getOrCreateFunction(Loc, MemberDeclRef,
ForDefinition_t::NotForDefinition);
// Implementation of _bridgeToObjectiveC could not be found.
if (!BridgedFunc)
return nullptr;
if (Inst->getFunction()->isSerialized() &&
!BridgedFunc->hasValidLinkageForFragileRef())
return nullptr;
auto ParamTypes = BridgedFunc->getLoweredFunctionType()->getParameters();
auto SILFnTy = SILType::getPrimitiveObjectType(
M.Types.getConstantFunctionType(BridgeFuncDeclRef));
// TODO: Handle return from witness function.
if (BridgedFunc->getLoweredFunctionType()
->getSingleResult()
.isFormalIndirect())
return nullptr;
// Get substitutions, if source is a bound generic type.
auto SubMap = Source->getContextSubstitutionMap(
M.getSwiftModule(), BridgeFuncDecl->getDeclContext());
SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap);
SILFunctionConventions substConv(SubstFnTy.castTo<SILFunctionType>(), M);
// check that we can go through with the optimization
if (!canOptimizeCast(BridgedTargetTy, M, substConv)) {
return nullptr;
}
auto FnRef = Builder.createFunctionRef(Loc, BridgedFunc);
if (Src->getType().isAddress() && !substConv.isSILIndirect(ParamTypes[0])) {
// Create load
Src = Builder.createLoad(Loc, Src, LoadOwnershipQualifier::Unqualified);
}
// Compensate different owning conventions of the replaced cast instruction
// and the inserted conversion function.
bool needRetainBeforeCall = false;
bool needReleaseAfterCall = false;
bool needReleaseInSuccess = false;
switch (ParamTypes[0].getConvention()) {
case ParameterConvention::Direct_Guaranteed:
case ParameterConvention::Indirect_In_Guaranteed:
switch (ConsumptionKind) {
case CastConsumptionKind::TakeAlways:
needReleaseAfterCall = true;
break;
case CastConsumptionKind::TakeOnSuccess:
needReleaseInSuccess = true;
break;
case CastConsumptionKind::CopyOnSuccess:
// Conservatively insert a retain/release pair around the conversion
// function because the conversion function could decrement the
// (global) reference count of the source object.
//
// %src = load %global_var
// apply %conversion_func(@guaranteed %src)
//
// sil conversion_func {
// %old_value = load %global_var
// store %something_else, %global_var
// strong_release %old_value
// }
needRetainBeforeCall = true;
needReleaseAfterCall = true;
break;
}
break;
case ParameterConvention::Direct_Owned:
case ParameterConvention::Indirect_In:
case ParameterConvention::Indirect_In_Constant:
// Currently this
// cannot appear, because the _bridgeToObjectiveC protocol witness method
// always receives the this pointer (= the source) as guaranteed.
// If it became possible (perhaps with the advent of ownership and
// explicit +1 annotations), the implementation should look something
// like this:
/*
switch (ConsumptionKind) {
case CastConsumptionKind::TakeAlways:
break;
case CastConsumptionKind::TakeOnSuccess:
needRetainBeforeCall = true;
needReleaseInSuccess = true;
break;
case CastConsumptionKind::CopyOnSuccess:
needRetainBeforeCall = true;
break;
}
break;
*/
llvm_unreachable("this should never happen so is currently untestable");
case ParameterConvention::Direct_Unowned:
assert(!AddressOnlyType &&
"AddressOnlyType with Direct_Unowned is not supported");
break;
case ParameterConvention::Indirect_Inout:
case ParameterConvention::Indirect_InoutAliasable:
// TODO handle remaining indirect argument types
return nullptr;
}
bool needStackAllocatedTemporary = false;
if (needRetainBeforeCall) {
if (AddressOnlyType) {
needStackAllocatedTemporary = true;
auto NewSrc = Builder.createAllocStack(Loc, Src->getType());
Builder.createCopyAddr(Loc, Src, NewSrc, IsNotTake, IsInitialization);
Src = NewSrc;
} else {
Builder.createRetainValue(Loc, Src, Builder.getDefaultAtomicity());
}
}
SmallVector<Substitution, 4> Subs;
if (auto *Sig = Source->getAnyNominal()->getGenericSignature())
Sig->getSubstitutions(SubMap, Subs);
// Generate a code to invoke the bridging function.
auto *NewAI = Builder.createApply(Loc, FnRef, Subs, Src, false);
auto releaseSrc = [&](SILBuilder &Builder) {
if (AddressOnlyType) {
Builder.createDestroyAddr(Loc, Src);
} else {
Builder.createReleaseValue(Loc, Src, Builder.getDefaultAtomicity());
}
};
Optional<SILBuilder> SuccBuilder;
if (needReleaseInSuccess || needStackAllocatedTemporary)
SuccBuilder.emplace(SuccessBB->begin());
if (needReleaseAfterCall) {
releaseSrc(Builder);
} else if (needReleaseInSuccess) {
if (SuccessBB) {
releaseSrc(*SuccBuilder);
} else {
// For an unconditional cast, success is the only defined path
releaseSrc(Builder);
}
}
// Pop the temporary stack slot for a copied temporary.
if (needStackAllocatedTemporary) {
assert((bool)SuccessBB == (bool)FailureBB);
if (SuccessBB) {
SuccBuilder->createDeallocStack(Loc, Src);
SILBuilder FailBuilder(FailureBB->begin());
FailBuilder.createDeallocStack(Loc, Src);
} else {
Builder.createDeallocStack(Loc, Src);
}
}
SILInstruction *NewI = NewAI;
if (Dest) {
// If it is addr cast then store the result.
auto ConvTy = NewAI->getType();
auto DestTy = Dest->getType().getObjectType();
assert(DestTy == SILType::getPrimitiveObjectType(
BridgedTargetTy->getCanonicalType()) &&
"Expected Dest Type to be the same as BridgedTargetTy");
SILValue CastedValue;
if (ConvTy == DestTy) {
CastedValue = NewAI;
} else if (DestTy.isExactSuperclassOf(ConvTy)) {
CastedValue = Builder.createUpcast(Loc, NewAI, DestTy);
} else if (ConvTy.isExactSuperclassOf(DestTy)) {
// The downcast from a base class to derived class may fail.
if (isConditional) {
// In case of a conditional cast, we should handle it gracefully.
auto CondBrSuccessBB =
NewAI->getFunction()->createBasicBlock(NewAI->getParent());
CondBrSuccessBB->createPHIArgument(DestTy, ValueOwnershipKind::Owned,
nullptr);
Builder.createCheckedCastBranch(Loc, /* isExact*/ false, NewAI, DestTy,
CondBrSuccessBB, FailureBB);
Builder.setInsertionPoint(CondBrSuccessBB, CondBrSuccessBB->begin());
CastedValue = CondBrSuccessBB->getArgument(0);
} else {
CastedValue = SILValue(
Builder.createUnconditionalCheckedCast(Loc, NewAI, DestTy));
}
} else if (ConvTy.getSwiftRValueType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
DestTy.getSwiftRValueType()) ||
DestTy.getSwiftRValueType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
ConvTy.getSwiftRValueType())) {
// Handle NS <-> CF toll-free bridging here.
CastedValue =
SILValue(Builder.createUncheckedRefCast(Loc, NewAI, DestTy));
} else {
llvm_unreachable("optimizeBridgedSwiftToObjCCast: should never reach "
"this condition: if the Destination does not have the "
"same type, is not a bridgeable CF type and isn't a "
"superclass/subclass of the source operand we should "
"have bailed earlier");
}
NewI = Builder.createStore(Loc, CastedValue, Dest,
StoreOwnershipQualifier::Unqualified);
if (isConditional && NewI->getParent() != NewAI->getParent()) {
Builder.createBranch(Loc, SuccessBB);
}
}
if (Dest) {
EraseInstAction(Inst);
}
return NewI;
}
/// Make use of the fact that some of these casts cannot fail.
/// For example, if the ObjC type is exactly the expected
/// _ObjectiveCType type, then it would always succeed for
/// NSString, NSNumber, etc.
/// Casts from NSArray, NSDictionary and NSSet may fail.
///
/// If ObjC class is not exactly _ObjectiveCType, then
/// its conversion to a required _ObjectiveCType may fail.
SILInstruction *CastOptimizer::optimizeBridgedCasts(
SILInstruction *Inst, CastConsumptionKind ConsumptionKind,
bool isConditional, SILValue Src, SILValue Dest, CanType source,
CanType target, SILBasicBlock *SuccessBB, SILBasicBlock *FailureBB) {
auto &M = Inst->getModule();
// To apply the bridged optimizations, we should ensure that types are not
// existential (and keep in mind that generic parameters can be existentials),
// and that one of the types is a class and another one is a struct.
if (source.isAnyExistentialType() || target.isAnyExistentialType() ||
source->is<ArchetypeType>() || target->is<ArchetypeType>() ||
(source.getClassOrBoundGenericClass() &&
!target.getStructOrBoundGenericStruct()) ||
(target.getClassOrBoundGenericClass() &&
!source.getStructOrBoundGenericStruct()))
return nullptr;
// Casts involving non-bound generic types cannot be optimized.
if (source->hasArchetype() || target->hasArchetype())
return nullptr;
auto BridgedTargetTy = getCastFromObjC(M, source, target);
if (!BridgedTargetTy)
return nullptr;
auto BridgedSourceTy = getCastFromObjC(M, target, source);
if (!BridgedSourceTy)
return nullptr;
CanType CanBridgedTargetTy = BridgedTargetTy->getCanonicalType();
CanType CanBridgedSourceTy = BridgedSourceTy->getCanonicalType();
if (CanBridgedSourceTy == source && CanBridgedTargetTy == target) {
// Both source and target type are ObjC types.
return nullptr;
}
if (CanBridgedSourceTy != source && CanBridgedTargetTy != target) {
// Both source and target type are Swift types.
return nullptr;
}
if ((CanBridgedSourceTy && CanBridgedSourceTy->getAnyNominal() ==
M.getASTContext().getNSErrorDecl()) ||
(CanBridgedTargetTy && CanBridgedSourceTy->getAnyNominal() ==
M.getASTContext().getNSErrorDecl())) {
// FIXME: Can't optimize bridging with NSError.
return nullptr;
}
if (CanBridgedSourceTy || CanBridgedTargetTy) {
// Check what kind of conversion it is? ObjC->Swift or Swift-ObjC?
if (CanBridgedTargetTy != target) {
// This is an ObjC to Swift cast.
return optimizeBridgedObjCToSwiftCast(
Inst, isConditional, Src, Dest, source, target, BridgedSourceTy,
BridgedTargetTy, SuccessBB, FailureBB);
} else {
// This is a Swift to ObjC cast
return optimizeBridgedSwiftToObjCCast(
Inst, ConsumptionKind, isConditional, Src, Dest, source, target,
BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB);
}
}
llvm_unreachable("Unknown kind of bridging");
}
SILInstruction *CastOptimizer::simplifyCheckedCastAddrBranchInst(
CheckedCastAddrBranchInst *Inst) {
if (auto *I = optimizeCheckedCastAddrBranchInst(Inst))
Inst = dyn_cast<CheckedCastAddrBranchInst>(I);
if (!Inst)
return nullptr;
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto &Mod = Inst->getModule();
SILBuilderWithScope Builder(Inst);
// Try to determine the outcome of the cast from a known type
// to a protocol type at compile-time.
bool isSourceTypeExact = isa<MetatypeInst>(Inst->getSrc());
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
classifyDynamicCast(Mod.getSwiftModule(), SourceType, TargetType,
isSourceTypeExact, Mod.isWholeModule());
if (Feasibility == DynamicCastFeasibility::WillFail) {
if (shouldDestroyOnFailure(Inst->getConsumptionKind())) {
auto &srcTL = Builder.getModule().getTypeLowering(Src->getType());
srcTL.emitDestroyAddress(Builder, Loc, Src);
}
auto NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
bool ResultNotUsed = isa<AllocStackInst>(Dest);
if (ResultNotUsed) {
for (auto Use : Dest->getUses()) {
auto *User = Use->getUser();
if (isa<DeallocStackInst>(User) || User == Inst)
continue;
ResultNotUsed = false;
break;
}
}
auto *BB = Inst->getParent();
SILInstruction *BridgedI = nullptr;
// To apply the bridged optimizations, we should
// ensure that types are not existential,
// and that not both types are classes.
BridgedI = optimizeBridgedCasts(
Inst, Inst->getConsumptionKind(),
/* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed,
Src, Dest, SourceType, TargetType, SuccessBB, FailureBB);
if (!BridgedI) {
// If the cast may succeed or fail, and it can't be optimized into a
// bridging operation, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
// Replace by unconditional_addr_cast, followed by a branch.
// The unconditional_addr_cast can be skipped, if the result of a cast
// is not used afterwards.
if (ResultNotUsed) {
if (shouldTakeOnSuccess(Inst->getConsumptionKind())) {
auto &srcTL = Builder.getModule().getTypeLowering(Src->getType());
srcTL.emitDestroyAddress(Builder, Loc, Src);
}
EraseInstAction(Inst);
Builder.setInsertionPoint(BB);
auto *NewI = Builder.createBranch(Loc, SuccessBB);
WillSucceedAction();
return NewI;
}
// Since it is an addr cast, only address types are handled here.
if (!Src->getType().isAddress() || !Dest->getType().isAddress()) {
return nullptr;
}
// For CopyOnSuccess casts, we could insert an explicit copy here, but this
// case does not happen in practice.
// Both TakeOnSuccess and TakeAlways can be reduced to an
// UnconditionalCheckedCast, since the failure path is irrelevant.
if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess)
return nullptr;
if (!emitSuccessfulIndirectUnconditionalCast(Builder, Mod.getSwiftModule(),
Loc, Src, SourceType, Dest,
TargetType, Inst)) {
// No optimization was possible.
return nullptr;
}
EraseInstAction(Inst);
}
SILInstruction *NewI = &BB->back();
if (!isa<TermInst>(NewI)) {
Builder.setInsertionPoint(BB);
NewI = Builder.createBranch(Loc, SuccessBB);
}
WillSucceedAction();
return NewI;
}
SILInstruction *
CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
if (Inst->isExact()) {
auto *ARI = dyn_cast<AllocRefInst>(stripUpCasts(Inst->getOperand()));
if (!ARI)
return nullptr;
// We know the dynamic type of the operand.
SILBuilderWithScope Builder(Inst);
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
if (ARI->getType() == Inst->getCastType()) {
// This exact cast will succeed.
SmallVector<SILValue, 1> Args;
Args.push_back(ARI);
auto *NewI = Builder.createBranch(Loc, SuccessBB, Args);
EraseInstAction(Inst);
WillSucceedAction();
return NewI;
}
// This exact cast will fail.
auto *NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
if (auto *I = optimizeCheckedCastBranchInst(Inst))
Inst = dyn_cast<CheckedCastBranchInst>(I);
if (!Inst)
return nullptr;
auto LoweredTargetType = Inst->getCastType();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto Op = Inst->getOperand();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Op);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
TargetType, isSourceTypeExact);
SILBuilderWithScope Builder(Inst);
if (Feasibility == DynamicCastFeasibility::WillFail) {
auto *NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
SILValue CastedValue;
if (Op->getType() != LoweredTargetType) {
auto Src = Inst->getOperand();
auto Dest = SILValue();
// Apply the bridged cast optimizations.
// TODO: Bridged casts cannot be expressed by checked_cast_br yet.
// Should we ever support it, please review this code.
auto BridgedI = optimizeBridgedCasts(
Inst, CastConsumptionKind::CopyOnSuccess,
/* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed,
Src, Dest, SourceType, TargetType, nullptr, nullptr);
if (BridgedI) {
llvm_unreachable(
"Bridged casts cannot be expressed by checked_cast_br yet");
} else {
// If the cast may succeed or fail and can't be turned into a bridging
// call, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
// Replace by unconditional_cast, followed by a branch.
// The unconditional_cast can be skipped, if the result of a cast
// is not used afterwards.
if (!ResultNotUsed) {
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
return nullptr;
CastedValue = emitSuccessfulScalarUnconditionalCast(
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
SourceType, TargetType, Inst);
} else {
CastedValue = SILUndef::get(LoweredTargetType, Mod);
}
if (!CastedValue)
CastedValue =
Builder.createUnconditionalCheckedCast(Loc, Op, LoweredTargetType);
}
} else {
// No need to cast.
CastedValue = Op;
}
auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue);
EraseInstAction(Inst);
WillSucceedAction();
return NewI;
}
SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst(
CheckedCastValueBranchInst *Inst) {
if (auto *I = optimizeCheckedCastValueBranchInst(Inst))
Inst = dyn_cast<CheckedCastValueBranchInst>(I);
if (!Inst)
return nullptr;
auto LoweredTargetType = Inst->getCastType();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto Op = Inst->getOperand();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Op);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
TargetType, isSourceTypeExact);
SILBuilderWithScope Builder(Inst);
if (Feasibility == DynamicCastFeasibility::WillFail) {
auto *NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
// Casting will succeed.
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
SILValue CastedValue;
if (Op->getType() != LoweredTargetType) {
auto Src = Inst->getOperand();
auto Dest = SILValue();
// Apply the bridged cast optimizations.
// TODO: Bridged casts cannot be expressed by checked_cast_value_br yet.
// Once the support for opaque values has landed, please review this
// code.
auto BridgedI = optimizeBridgedCasts(
Inst, CastConsumptionKind::CopyOnSuccess,
/* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed,
Src, Dest, SourceType, TargetType, nullptr, nullptr);
if (BridgedI) {
llvm_unreachable(
"Bridged casts cannot be expressed by checked_cast_value_br yet");
} else {
// If the cast may succeed or fail and can't be turned into a bridging
// call, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
// Replace by unconditional_cast, followed by a branch.
// The unconditional_cast can be skipped, if the result of a cast
// is not used afterwards.
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
return nullptr;
if (!ResultNotUsed) {
CastedValue = emitSuccessfulScalarUnconditionalCast(
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
SourceType, TargetType, Inst);
} else {
CastedValue = SILUndef::get(LoweredTargetType, Mod);
}
}
if (!CastedValue)
CastedValue = Builder.createUnconditionalCheckedCastValue(
Loc, Op, LoweredTargetType);
} else {
// No need to cast.
CastedValue = Op;
}
auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue);
EraseInstAction(Inst);
WillSucceedAction();
return NewI;
}
SILInstruction *CastOptimizer::optimizeCheckedCastAddrBranchInst(
CheckedCastAddrBranchInst *Inst) {
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
// If there is an unbound generic type involved in the cast, bail.
if (Src->getType().hasArchetype() || Dest->getType().hasArchetype())
return nullptr;
// %1 = metatype $A.Type
// [%2 = init_existential_metatype %1 ...]
// %3 = alloc_stack
// store %1 to %3 or store %2 to %3
// checked_cast_addr_br %3 to ...
// ->
// %1 = metatype $A.Type
// %c = checked_cast_br %1 to ...
// store %c to %3 (if successful)
if (auto *ASI = dyn_cast<AllocStackInst>(Src)) {
// Check if the value of this alloc_stack is set only once by a store
// instruction, used only by CCABI and then deallocated.
bool isLegal = true;
StoreInst *Store = nullptr;
for (auto Use : ASI->getUses()) {
auto *User = Use->getUser();
if (isa<DeallocStackInst>(User) || User == Inst)
continue;
if (auto *SI = dyn_cast<StoreInst>(User)) {
if (!Store) {
Store = SI;
continue;
}
}
isLegal = false;
break;
}
if (isLegal && Store) {
// Check what was the value stored in the allocated stack slot.
auto Src = Store->getSrc();
MetatypeInst *MI = nullptr;
if (auto *IEMI = dyn_cast<InitExistentialMetatypeInst>(Src)) {
MI = dyn_cast<MetatypeInst>(IEMI->getOperand());
}
if (!MI)
MI = dyn_cast<MetatypeInst>(Src);
if (MI) {
if (SuccessBB->getSinglePredecessorBlock() &&
canUseScalarCheckedCastInstructions(
Inst->getModule(), MI->getType().getSwiftRValueType(),
Inst->getTargetType())) {
SILBuilderWithScope B(Inst);
auto NewI = B.createCheckedCastBranch(
Loc, false /*isExact*/, MI, Dest->getType().getObjectType(),
SuccessBB, FailureBB, Inst->getTrueBBCount(),
Inst->getFalseBBCount());
SuccessBB->createPHIArgument(Dest->getType().getObjectType(),
ValueOwnershipKind::Owned);
B.setInsertionPoint(SuccessBB->begin());
// Store the result
B.createStore(Loc, SuccessBB->getArgument(0), Dest,
StoreOwnershipQualifier::Unqualified);
EraseInstAction(Inst);
return NewI;
}
}
}
}
return nullptr;
}
SILInstruction *CastOptimizer::optimizeCheckedCastValueBranchInst(
CheckedCastValueBranchInst *Inst) {
// TODO
return nullptr;
}
SILInstruction *
CastOptimizer::optimizeCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
if (Inst->isExact())
return nullptr;
auto LoweredTargetType = Inst->getCastType();
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto Op = Inst->getOperand();
// Try to simplify checked_cond_br instructions using existential
// metatypes by propagating a concrete type whenever it can be
// determined statically.
// %0 = metatype $A.Type
// %1 = init_existential_metatype ..., %0: $A
// checked_cast_br %1, ....
// ->
// %0 = metatype $A.Type
// checked_cast_br %0 to ...
if (auto *IEMI = dyn_cast<InitExistentialMetatypeInst>(Op)) {
if (auto *MI = dyn_cast<MetatypeInst>(IEMI->getOperand())) {
SILBuilderWithScope B(Inst);
auto *NewI = B.createCheckedCastBranch(
Loc, /* isExact */ false, MI, LoweredTargetType, SuccessBB, FailureBB,
Inst->getTrueBBCount(), Inst->getFalseBBCount());
EraseInstAction(Inst);
return NewI;
}
}
if (auto *EMI = dyn_cast<ExistentialMetatypeInst>(Op)) {
// Operand of the existential_metatype instruction.
auto Op = EMI->getOperand();
auto EmiTy = EMI->getType();
// %0 = alloc_stack $T
// %1 = init_existential_addr %0: $*T, $A
// %2 = existential_metatype $T.Type, %0: $*T
// checked_cast_br %2 to ...
// ->
// %1 = metatype $A.Type
// checked_cast_br %1 to ...
if (auto *ASI = dyn_cast<AllocStackInst>(Op)) {
// Should be in the same BB.
if (ASI->getParent() != EMI->getParent())
return nullptr;
// Check if this alloc_stack is only initialized once by means of
// single init_existential_addr.
bool isLegal = true;
// init_existential instruction used to initialize this alloc_stack.
InitExistentialAddrInst *FoundIEI = nullptr;
for (auto Use : getNonDebugUses(ASI)) {
auto *User = Use->getUser();
if (isa<ExistentialMetatypeInst>(User) || isa<DestroyAddrInst>(User) ||
isa<DeallocStackInst>(User))
continue;
if (auto *IEI = dyn_cast<InitExistentialAddrInst>(User)) {
if (!FoundIEI) {
FoundIEI = IEI;
continue;
}
}
isLegal = false;
break;
}
if (isLegal && FoundIEI) {
// Should be in the same BB.
if (FoundIEI->getParent() != EMI->getParent())
return nullptr;
// Get the type used to initialize the existential.
auto LoweredConcreteTy = FoundIEI->getLoweredConcreteType();
// We don't know enough at compile time about existential
// and generic type parameters.
if (LoweredConcreteTy.isAnyExistentialType() ||
LoweredConcreteTy.is<ArchetypeType>())
return nullptr;
// Get the metatype of this type.
auto EMT = EmiTy.castTo<AnyMetatypeType>();
auto *MetaTy = MetatypeType::get(LoweredConcreteTy.getSwiftRValueType(),
EMT->getRepresentation());
auto CanMetaTy = CanTypeWrapper<MetatypeType>(MetaTy);
auto SILMetaTy = SILType::getPrimitiveObjectType(CanMetaTy);
SILBuilderWithScope B(Inst);
B.getOpenedArchetypes().addOpenedArchetypeOperands(
FoundIEI->getTypeDependentOperands());
auto *MI = B.createMetatype(FoundIEI->getLoc(), SILMetaTy);
auto *NewI = B.createCheckedCastBranch(
Loc, /* isExact */ false, MI, LoweredTargetType, SuccessBB,
FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount());
EraseInstAction(Inst);
return NewI;
}
}
// %0 = alloc_ref $A
// %1 = init_existential_ref %0: $A, $...
// %2 = existential_metatype ..., %1 : ...
// checked_cast_br %2, ....
// ->
// %1 = metatype $A.Type
// checked_cast_br %1, ....
if (auto *FoundIERI = dyn_cast<InitExistentialRefInst>(Op)) {
auto *ASRI = dyn_cast<AllocRefInst>(FoundIERI->getOperand());
if (!ASRI)
return nullptr;
// Should be in the same BB.
if (ASRI->getParent() != EMI->getParent())
return nullptr;
// Check if this alloc_stack is only initialized once by means of
// a single init_existential_ref.
bool isLegal = true;
for (auto Use : getNonDebugUses(ASRI)) {
auto *User = Use->getUser();
if (isa<ExistentialMetatypeInst>(User) || isa<StrongReleaseInst>(User))
continue;
if (auto *IERI = dyn_cast<InitExistentialRefInst>(User)) {
if (IERI == FoundIERI) {
continue;
}
}
isLegal = false;
break;
}
if (isLegal && FoundIERI) {
// Should be in the same BB.
if (FoundIERI->getParent() != EMI->getParent())
return nullptr;
// Get the type used to initialize the existential.
auto ConcreteTy = FoundIERI->getFormalConcreteType();
// We don't know enough at compile time about existential
// and generic type parameters.
if (ConcreteTy.isAnyExistentialType() ||
ConcreteTy->is<ArchetypeType>())
return nullptr;
// Get the SIL metatype of this type.
auto EMT = EMI->getType().castTo<AnyMetatypeType>();
auto *MetaTy = MetatypeType::get(ConcreteTy, EMT->getRepresentation());
auto CanMetaTy = CanTypeWrapper<MetatypeType>(MetaTy);
auto SILMetaTy = SILType::getPrimitiveObjectType(CanMetaTy);
SILBuilderWithScope B(Inst);
B.getOpenedArchetypes().addOpenedArchetypeOperands(
FoundIERI->getTypeDependentOperands());
auto *MI = B.createMetatype(FoundIERI->getLoc(), SILMetaTy);
auto *NewI = B.createCheckedCastBranch(
Loc, /* isExact */ false, MI, LoweredTargetType, SuccessBB,
FailureBB, Inst->getTrueBBCount(), Inst->getFalseBBCount());
EraseInstAction(Inst);
return NewI;
}
}
}
return nullptr;
}
ValueBase *CastOptimizer::optimizeUnconditionalCheckedCastInst(
UnconditionalCheckedCastInst *Inst) {
auto LoweredSourceType = Inst->getOperand()->getType();
auto LoweredTargetType = Inst->getType();
auto Loc = Inst->getLoc();
auto Op = Inst->getOperand();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Op);
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
classifyDynamicCast(Mod.getSwiftModule(), Inst->getSourceType(),
Inst->getTargetType(), isSourceTypeExact);
if (Feasibility == DynamicCastFeasibility::WillFail) {
// Remove the cast and insert a trap, followed by an
// unreachable instruction.
SILBuilderWithScope Builder(Inst);
auto *Trap = Builder.createBuiltinTrap(Loc);
Inst->replaceAllUsesWithUndef();
EraseInstAction(Inst);
Builder.setInsertionPoint(std::next(SILBasicBlock::iterator(Trap)));
auto *UnreachableInst =
Builder.createUnreachable(ArtificialUnreachableLocation());
// Delete everything after the unreachable except for dealloc_stack which we
// move before the trap.
deleteInstructionsAfterUnreachable(UnreachableInst, Trap);
WillFailAction();
return Trap;
}
if (Feasibility == DynamicCastFeasibility::WillSucceed) {
if (Inst->use_empty()) {
EraseInstAction(Inst);
WillSucceedAction();
return nullptr;
}
}
SILBuilderWithScope Builder(Inst);
// Try to apply the bridged casts optimizations
auto SourceType = LoweredSourceType.getSwiftRValueType();
auto TargetType = LoweredTargetType.getSwiftRValueType();
auto Src = Inst->getOperand();
auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess,
false, Src, SILValue(), SourceType,
TargetType, nullptr, nullptr);
if (NewI) {
// FIXME: I'm not sure why this is true!
auto newValue = cast<SingleValueInstruction>(NewI);
ReplaceInstUsesAction(Inst, newValue);
EraseInstAction(Inst);
WillSucceedAction();
return newValue;
}
// If the cast may succeed or fail and can't be optimized into a bridging
// call, let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
if (isBridgingCast(SourceType, TargetType))
return nullptr;
auto Result = emitSuccessfulScalarUnconditionalCast(
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
LoweredSourceType.getSwiftRValueType(),
LoweredTargetType.getSwiftRValueType(), Inst);
if (!Result) {
// No optimization was possible.
return nullptr;
}
ReplaceInstUsesAction(Inst, Result);
EraseInstAction(Inst);
WillSucceedAction();
return Result;
}
/// Deletes all instructions after \p UnreachableInst except dealloc_stack
/// instructions are moved before \p TrapInst.
void CastOptimizer::deleteInstructionsAfterUnreachable(
SILInstruction *UnreachableInst, SILInstruction *TrapInst) {
auto UnreachableInstIt = std::next(SILBasicBlock::iterator(UnreachableInst));
auto *Block = TrapInst->getParent();
while (UnreachableInstIt != Block->end()) {
SILInstruction *CurInst = &*UnreachableInstIt;
++UnreachableInstIt;
if (auto *DeallocStack = dyn_cast<DeallocStackInst>(CurInst))
if (!isa<SILUndef>(DeallocStack->getOperand())) {
DeallocStack->moveBefore(TrapInst);
continue;
}
CurInst->replaceAllUsesOfAllResultsWithUndef();
EraseInstAction(CurInst);
}
}
/// TODO: Move to emitSuccessfulIndirectUnconditionalCast?
///
/// Peephole to avoid runtime calls:
/// unconditional_checked_cast_addr T in %0 : $*T to P in %1 : $*P
/// ->
/// %addr = init_existential_addr %1 : $*P, T
/// copy_addr %0 to %addr
///
/// where T is a type statically known to conform to P.
///
/// In caase P is a class existential type, it generates:
/// %val = load %0 : $*T
/// %existential = init_existential_ref %val : $T, $T, P
/// store %existential to %1 : $*P
///
/// Returns true if the optimization was possible and false otherwise.
static bool optimizeStaticallyKnownProtocolConformance(
UnconditionalCheckedCastAddrInst *Inst) {
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto &Mod = Inst->getModule();
if (TargetType->isAnyExistentialType() &&
!SourceType->isAnyExistentialType()) {
auto &Ctx = Mod.getASTContext();
auto *SM = Mod.getSwiftModule();
auto Proto = dyn_cast<ProtocolDecl>(TargetType->getAnyNominal());
if (Proto) {
auto Conformance = SM->lookupConformance(SourceType, Proto);
if (Conformance.hasValue()) {
// SourceType is a non-existential type conforming to a
// protocol represented by the TargetType.
SILBuilder B(Inst);
SmallVector<ProtocolConformanceRef, 1> NewConformances;
NewConformances.push_back(Conformance.getValue());
ArrayRef<ProtocolConformanceRef> Conformances =
Ctx.AllocateCopy(NewConformances);
auto ExistentialRepr =
Dest->getType().getPreferredExistentialRepresentation(Mod,
SourceType);
switch (ExistentialRepr) {
default:
return false;
case ExistentialRepresentation::Opaque: {
auto ExistentialAddr = B.createInitExistentialAddr(
Loc, Dest, SourceType, Src->getType().getObjectType(),
Conformances);
B.createCopyAddr(Loc, Src, ExistentialAddr, IsTake_t::IsTake,
IsInitialization_t::IsInitialization);
break;
}
case ExistentialRepresentation::Class: {
auto Value = B.createLoad(Loc, Src,
swift::LoadOwnershipQualifier::Unqualified);
auto Existential =
B.createInitExistentialRef(Loc, Dest->getType().getObjectType(),
SourceType, Value, Conformances);
B.createStore(Loc, Existential, Dest,
swift::StoreOwnershipQualifier::Unqualified);
break;
}
case ExistentialRepresentation::Boxed: {
auto AllocBox = B.createAllocExistentialBox(Loc, Dest->getType(),
SourceType, Conformances);
auto Projection =
B.createProjectExistentialBox(Loc, Src->getType(), AllocBox);
auto Value = B.createLoad(Loc, Src,
swift::LoadOwnershipQualifier::Unqualified);
B.createStore(Loc, Value, Projection,
swift::StoreOwnershipQualifier::Unqualified);
B.createStore(Loc, AllocBox, Dest,
swift::StoreOwnershipQualifier::Unqualified);
break;
}
};
return true;
}
}
}
return false;
}
SILInstruction *CastOptimizer::optimizeUnconditionalCheckedCastAddrInst(
UnconditionalCheckedCastAddrInst *Inst) {
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Src);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
TargetType, isSourceTypeExact);
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
// Forced bridged casts can be still simplified here.
// If they fail, they fail inside the conversion function.
if (!isBridgingCast(SourceType, TargetType))
return nullptr;
}
if (Feasibility == DynamicCastFeasibility::WillFail) {
// Remove the cast and insert a trap, followed by an
// unreachable instruction.
SILBuilderWithScope Builder(Inst);
// mem2reg's invariants get unhappy if we don't try to
// initialize a loadable result.
auto DestType = Dest->getType();
auto &resultTL = Mod.Types.getTypeLowering(DestType);
if (!resultTL.isAddressOnly()) {
auto undef = SILValue(
SILUndef::get(DestType.getObjectType(), Builder.getModule()));
Builder.createStore(Loc, undef, Dest,
StoreOwnershipQualifier::Unqualified);
}
auto *TrapI = Builder.createBuiltinTrap(Loc);
EraseInstAction(Inst);
Builder.setInsertionPoint(std::next(SILBasicBlock::iterator(TrapI)));
auto *UnreachableInst =
Builder.createUnreachable(ArtificialUnreachableLocation());
// Delete everything after the unreachable except for dealloc_stack which we
// move before the trap.
deleteInstructionsAfterUnreachable(UnreachableInst, TrapI);
WillFailAction();
}
if (Feasibility == DynamicCastFeasibility::WillSucceed ||
Feasibility == DynamicCastFeasibility::MaySucceed) {
// Check if a result of a cast is unused. If this is the case, the cast can
// be removed even if the cast may fail at runtime.
// Swift optimizer does not claim to be crash-preserving.
bool ResultNotUsed = isa<AllocStackInst>(Dest);
DestroyAddrInst *DestroyDestInst = nullptr;
if (ResultNotUsed) {
for (auto Use : Dest->getUses()) {
auto *User = Use->getUser();
if (isa<DeallocStackInst>(User) || User == Inst)
continue;
if (isa<DestroyAddrInst>(User) && !DestroyDestInst) {
DestroyDestInst = cast<DestroyAddrInst>(User);
continue;
}
ResultNotUsed = false;
DestroyDestInst = nullptr;
break;
}
}
if (ResultNotUsed) {
SILBuilderWithScope B(Inst);
B.createDestroyAddr(Inst->getLoc(), Inst->getSrc());
if (DestroyDestInst)
EraseInstAction(DestroyDestInst);
EraseInstAction(Inst);
WillSucceedAction();
return nullptr;
}
// Try to apply the bridged casts optimizations.
auto NewI =
optimizeBridgedCasts(Inst, CastConsumptionKind::TakeAlways, false, Src,
Dest, SourceType, TargetType, nullptr, nullptr);
if (NewI) {
WillSucceedAction();
return nullptr;
}
if (Feasibility == DynamicCastFeasibility::MaySucceed)
return nullptr;
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
if (optimizeStaticallyKnownProtocolConformance(Inst)) {
EraseInstAction(Inst);
WillSucceedAction();
return nullptr;
}
if (isBridgingCast(SourceType, TargetType))
return nullptr;
SILBuilderWithScope Builder(Inst);
if (!emitSuccessfulIndirectUnconditionalCast(Builder, Mod.getSwiftModule(),
Loc, Src, SourceType, Dest,
TargetType, Inst)) {
// No optimization was possible.
return nullptr;
}
EraseInstAction(Inst);
WillSucceedAction();
}
return nullptr;
}