blob: a32a92743698d131fe8727db271f8617c50576e0 [file] [log] [blame]
//===--- 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/Module.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/SILOptimizer/Utils/SILOptFunctionBuilder.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 = FunctionBuilder.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);
auto *CCBI = Builder.createCheckedCastBranch(Loc, false, Load,
SILBridgedTy, CastSuccessBB, ConvFailBB);
NewI = CCBI;
splitEdge(CCBI, /* EdgeIdx to ConvFailBB */ 1);
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;
SILValue InOutOptionalParam;
if (isConditional) {
// Create a temporary
OptionalTy = OptionalType::get(Dest->getType().getASTType())
->getImplementationType()
->getCanonicalType();
Tmp = Builder.createAllocStack(Loc,
SILType::getPrimitiveObjectType(OptionalTy));
InOutOptionalParam = Tmp;
} else {
InOutOptionalParam = Dest;
}
(void)ParamTypes;
assert(ParamTypes[0].getConvention() == ParameterConvention::Direct_Guaranteed &&
"Parameter should be @guaranteed");
// Emit a retain.
Builder.createRetainValue(Loc, SrcOp, Builder.getDefaultAtomicity());
Args.push_back(InOutOptionalParam);
Args.push_back(SrcOp);
Args.push_back(MetaTyVal);
auto *AI = Builder.createApply(Loc, FuncRef, SubMap, Args, false);
// If we have guaranteed normal arguments, insert the destroy.
//
// TODO: Is it safe to just eliminate the initial retain?
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
// 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();
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.getASTType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
DestTy.getASTType())) {
return true;
}
if (DestTy.getASTType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
ConvTy.getASTType())) {
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, M.getASTContext().Id_bridgeToObjectiveC,
NLOptions::NL_ProtocolMembers, 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 = FunctionBuilder.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());
}
}
// Generate a code to invoke the bridging function.
auto *NewAI = Builder.createApply(Loc, FnRef, SubMap, 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.getASTType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
DestTy.getASTType()) ||
DestTy.getASTType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
ConvTy.getASTType())) {
// 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().getASTType(),
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.getASTType(),
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.getASTType();
auto TargetType = LoweredTargetType.getASTType();
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.getASTType(),
LoweredTargetType.getASTType(), 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() &&
Conformance->getConditionalRequirements().empty()) {
// SourceType is a non-existential type with a non-conditional
// conformance to a protocol represented by the TargetType.
//
// Conditional conformances are complicated: they may depend on
// information not known until runtime. For instance, if `X: P` where `T
// == Int` in `func foo<T>(_: T) { ... X<T>() as? P ... }`, the cast
// will succeed for `foo(0)` but not for `foo("string")`. There are many
// cases where everything is completely static (`X<Int>() as? P`), but
// we don't try to handle that at the moment.
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);
// This needs to be a copy_addr (for now) because we must handle
// address-only types.
B.createCopyAddr(Loc, Src, Projection, IsTake, IsInitialization);
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;
}