blob: 8891d3bb6438164b89b202b0fb9956fb130a0695 [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/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/CFGOptUtils.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.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;
//===----------------------------------------------------------------------===//
// ObjC -> Swift Bridging Cast Optimization
//===----------------------------------------------------------------------===//
static SILFunction *
getObjCToSwiftBridgingFunction(SILOptFunctionBuilder &funcBuilder,
SILDynamicCastInst dynamicCast) {
// inline constructor.
auto *bridgeFuncDecl = [&]() -> FuncDecl * {
auto &astContext = dynamicCast.getModule().getASTContext();
if (dynamicCast.isConditional()) {
return astContext.getConditionallyBridgeFromObjectiveCBridgeable();
}
return astContext.getForceBridgeFromObjectiveCBridgeable();
}();
assert(bridgeFuncDecl && "Bridging function doesn't exist?!");
SILDeclRef funcDeclRef(bridgeFuncDecl, SILDeclRef::Kind::Func);
// Lookup a function from the stdlib.
return funcBuilder.getOrCreateFunction(dynamicCast.getLocation(), funcDeclRef,
ForDefinition_t::NotForDefinition);
}
static SubstitutionMap lookupBridgeToObjCProtocolSubs(SILModule &mod,
CanType target) {
auto bridgedProto =
mod.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
auto conf = *mod.getSwiftModule()->lookupConformance(target, bridgedProto);
return SubstitutionMap::getProtocolSubstitutions(conf.getRequirement(),
target, conf);
}
/// Given that our insertion point is at the cast that we are trying to
/// optimize, convert our incoming value to something that can be passed to the
/// bridge call.
static std::pair<SILValue, SILInstruction *>
convertObjectToLoadableBridgeableType(SILBuilderWithScope &builder,
SILDynamicCastInst dynamicCast,
SILValue src) {
auto *f = dynamicCast.getFunction();
auto loc = dynamicCast.getLocation();
bool isConditional = dynamicCast.isConditional();
SILValue load =
builder.emitLoadValueOperation(loc, src, LoadOwnershipQualifier::Take);
SILType silBridgedTy = *dynamicCast.getLoweredBridgedTargetObjectType();
// If we are not conditional...
if (!isConditional) {
// and our loaded type is our bridged type, just return the load as our
// SILValue and signal to our caller that we did not create a new cast
// instruction by returning nullptr as second.
if (load->getType() == silBridgedTy) {
return {load, nullptr};
}
// Otherwise, just perform an unconditional checked cast to the sil bridged
// ty. We return the cast as our value and as our new cast instruction.
auto *cast =
builder.createUnconditionalCheckedCast(loc, load, silBridgedTy);
return {cast, cast};
}
SILBasicBlock *castSuccessBB =
f->createBasicBlockAfter(dynamicCast.getInstruction()->getParent());
castSuccessBB->createPhiArgument(silBridgedTy, ValueOwnershipKind::Owned);
// If we /are/ conditional and we do not need to bridge the load to the sil,
// then we just create our cast success block and branch from the end of the
// cast instruction block to the cast success block. We leave our insertion
// point in the cast success block since when we return, we are going to
// insert the bridge call/switch there. We return the argument of the cast
// success block as the value to be passed to the bridging function.
if (load->getType() == silBridgedTy) {
castSuccessBB->moveAfter(dynamicCast.getInstruction()->getParent());
builder.createBranch(loc, castSuccessBB, load);
builder.setInsertionPoint(castSuccessBB);
return {castSuccessBB->getArgument(0), nullptr};
}
auto *castFailBB = ([&]() -> SILBasicBlock * {
auto *failureBB = dynamicCast.getFailureBlock();
SILBuilderWithScope failureBBBuilder(&(*failureBB->begin()), builder);
return splitBasicBlockAndBranch(failureBBBuilder, &(*failureBB->begin()),
nullptr, nullptr);
}());
// Now that we have created the failure bb, move our cast success block right
// after the checked_cast_br bb.
castSuccessBB->moveAfter(dynamicCast.getInstruction()->getParent());
// Ok, we need to perform the full cast optimization. This means that we are
// going to replace the cast terminator in inst_block with a checked_cast_br.
auto *ccbi = builder.createCheckedCastBranch(loc, false, load, silBridgedTy,
castSuccessBB, castFailBB);
splitEdge(ccbi, /* EdgeIdx to CastFailBB */ 1);
// Now that we have split the edge to cast fail bb, add the default argument
// for the checked_cast_br. Then we need to handle our error conditions,
// namely we destroy on take_always and otherwise store the value back into
// the memory location that we took it out of.
{
auto *newFailureBlock = ccbi->getFailureBB();
SILValue defaultArg;
if (builder.hasOwnership()) {
defaultArg = newFailureBlock->createPhiArgument(
load->getType(), ValueOwnershipKind::Owned);
} else {
defaultArg = ccbi->getOperand();
}
// This block should be properly terminated already due to our method of
// splitting the failure block, so we can use begin() safely.
SILBuilderWithScope failureBuilder(newFailureBlock->begin());
switch (dynamicCast.getBridgedConsumptionKind()) {
case CastConsumptionKind::TakeAlways:
failureBuilder.emitDestroyValueOperation(loc, defaultArg);
break;
case CastConsumptionKind::TakeOnSuccess:
case CastConsumptionKind::CopyOnSuccess:
// Without ownership, we do not need to consume the taken value.
if (failureBuilder.hasOwnership()) {
failureBuilder.emitStoreValueOperation(loc, defaultArg, src,
StoreOwnershipQualifier::Init);
}
break;
case CastConsumptionKind::BorrowAlways:
llvm_unreachable("this should never occur here");
}
}
builder.setInsertionPoint(castSuccessBB);
return {castSuccessBB->getArgument(0), ccbi};
}
/// Create a call of _forceBridgeFromObjectiveC_bridgeable or
/// _conditionallyBridgeFromObjectiveC_bridgeable which converts an ObjC
/// instance into a corresponding Swift type, conforming to
/// _ObjectiveCBridgeable.
///
/// Control Flow Modification Model
/// ===============================
///
/// NOTE: In the following we assume that our src type is not address only. We
/// do not support optimizing such source types today.
///
/// Unconditional Casts
/// -------------------
///
/// In the case of unconditional casts, we do not touch the CFG at all. We
/// perform the following optimizations:
///
/// 1. If the bridged type and the src type equal, we replace the cast with the
/// apply.
///
/// 2. If src is an address and bridged type has the matching object type to
/// src, just load the value and again replace the cast with the apply.
///
/// 3. If src is an address and after loading still doesn't match bridged type,
/// insert an unconditional_checked_cast before calling the apply.
///
/// Conditional Casts
/// -----------------
///
/// In the case of a conditional const (i.e. checked_cast_addr_br), we transform
/// the following CFG:
///
/// ```
/// InstBlock (checked_cast_addr_br) -> FailureBB -> FailureSucc
/// \
/// \----------------------------> SuccessBB -> SuccessSucc
/// ```
///
/// to a CFG of the following form:
///
/// ```
/// InstBlock (checked_cast_br) -> CastFailBB -> FailureBB -> FailureSucc
/// | ^
/// \-> CastSuccessBB (bridge call + switch) --|
/// |
/// \-> BridgeSuccessBB -> SuccessBB -> SuccessSucc
/// ```
///
/// NOTE: That if the underlying src type matches the type of the underlying
/// bridge source object, we can omit the initial checked_cast_br and just load
/// the value + branch to the CastSuccessBB. This results instead in the
/// following CFG:
///
/// ```
/// InstBlock (br) FailureBB -> FailureSucc
/// | ^
/// \-> CastSuccessBB (bridge call + switch) --|
/// |
/// \-> BridgeSuccessBB -> SuccessBB -> SuccessSucc
/// ```
///
SILInstruction *
CastOptimizer::optimizeBridgedObjCToSwiftCast(SILDynamicCastInst dynamicCast) {
auto kind = dynamicCast.getKind();
(void)kind;
assert(((kind == SILDynamicCastKind::CheckedCastAddrBranchInst) ||
(kind == SILDynamicCastKind::UnconditionalCheckedCastAddrInst)) &&
"Unsupported dynamic cast kind");
CanType target = dynamicCast.getTargetType();
auto &mod = dynamicCast.getModule();
// AnyHashable is a special case that we do not handle since we only handle
// objc targets in this function. Bailout early.
if (auto dt = target.getNominalOrBoundGenericNominal()) {
if (dt == mod.getASTContext().getAnyHashableDecl()) {
return nullptr;
}
}
SILValue src = dynamicCast.getSource();
SILInstruction *Inst = dynamicCast.getInstruction();
auto *F = Inst->getFunction();
// Check if we have a source type that is address only. We do not support that
// today.
if (src->getType().isAddressOnly(*F)) {
return nullptr;
}
bool isConditional = dynamicCast.isConditional();
SILValue Dest = dynamicCast.getDest();
SILBasicBlock *SuccessBB = dynamicCast.getSuccessBlock();
SILBasicBlock *FailureBB = dynamicCast.getFailureBlock();
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.
SILFunction *bridgingFunc =
getObjCToSwiftBridgingFunction(functionBuilder, dynamicCast);
if (!bridgingFunc)
return nullptr;
auto paramTypes = bridgingFunc->getLoweredFunctionType()->getParameters();
(void)paramTypes;
assert(paramTypes[0].getConvention() ==
ParameterConvention::Direct_Guaranteed &&
"Parameter should be @guaranteed");
SILBuilderWithScope Builder(Inst, builderContext);
// Generate a load for the source argument since as part of our optimization
// we are going to promote the cast to work with objects instead of
// addresses. Additionally, if we have an objc object that is not bridgeable,
// but that could be converted to something that is bridgeable, we try to
// convert to the bridgeable type.
SILValue srcOp;
SILInstruction *newI;
std::tie(srcOp, newI) =
convertObjectToLoadableBridgeableType(Builder, dynamicCast, 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.
auto *funcRef = Builder.createFunctionRefFor(Loc, bridgingFunc);
SubstitutionMap subMap = lookupBridgeToObjCProtocolSubs(mod, target);
auto MetaTy = MetatypeType::get(target, MetatypeRepresentation::Thick);
auto SILMetaTy = F->getTypeLowering(MetaTy).getLoweredType();
auto *MetaTyVal = Builder.createMetatype(Loc, SILMetaTy);
// Temporary to hold the intermediate result.
AllocStackInst *Tmp = nullptr;
CanType OptionalTy;
SILValue outOptionalParam;
if (isConditional) {
// Create a temporary
OptionalTy = OptionalType::get(Dest->getType().getASTType())
->getImplementationType()
->getCanonicalType();
Tmp = Builder.createAllocStack(Loc,
SILType::getPrimitiveObjectType(OptionalTy));
outOptionalParam = Tmp;
} else {
outOptionalParam = Dest;
}
// Emit a retain.
SILValue srcArg = Builder.emitCopyValueOperation(Loc, srcOp);
SmallVector<SILValue, 1> Args;
Args.push_back(outOptionalParam);
Args.push_back(srcArg);
Args.push_back(MetaTyVal);
auto *AI = Builder.createApply(Loc, funcRef, subMap, Args);
// If we have guaranteed normal arguments, insert the destroy.
//
// TODO: Is it safe to just eliminate the initial retain?
Builder.emitDestroyOperation(Loc, srcArg);
// If we have an unconditional_checked_cast_addr, return early. We do not need
// to handle any conditional code.
if (isa<UnconditionalCheckedCastAddrInst>(Inst)) {
// Destroy the source value as unconditional_checked_cast_addr would.
Builder.emitDestroyOperation(Loc, srcOp);
eraseInstAction(Inst);
return (newI) ? newI : AI;
}
auto *CCABI = cast<CheckedCastAddrBranchInst>(Inst);
switch (CCABI->getConsumptionKind()) {
case CastConsumptionKind::TakeAlways:
Builder.emitDestroyOperation(Loc, srcOp);
break;
case CastConsumptionKind::TakeOnSuccess: {
{
// Insert a release in the success BB.
SILBuilderWithScope successBuilder(SuccessBB->begin());
successBuilder.emitDestroyOperation(Loc, srcOp);
}
{
// And a store in the failure BB.
if (Builder.hasOwnership()) {
SILBuilderWithScope failureBuilder(FailureBB->begin());
SILValue writeback = srcOp;
SILType srcType = src->getType().getObjectType();
if (writeback->getType() != srcType) {
writeback =
failureBuilder.createUncheckedRefCast(Loc, writeback, srcType);
}
failureBuilder.emitStoreValueOperation(Loc, writeback, src,
StoreOwnershipQualifier::Init);
}
}
break;
}
case CastConsumptionKind::BorrowAlways:
llvm_unreachable("checked_cast_addr_br never has BorrowAlways");
case CastConsumptionKind::CopyOnSuccess:
// If we are performing copy_on_success, store the value back into memory
// here since we loaded it. We may need to cast back to the actual
// underlying type.
if (Builder.hasOwnership()) {
SILValue writeback = srcOp;
SILType srcType = src->getType().getObjectType();
if (writeback->getType() != srcType) {
writeback = Builder.createUncheckedRefCast(Loc, writeback, srcType);
}
Builder.emitStoreValueOperation(Loc, writeback, src,
StoreOwnershipQualifier::Init);
}
break;
}
// 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();
auto *BridgeSuccessBB =
Inst->getFunction()->createBasicBlockAfter(Builder.getInsertionBB());
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 2> CaseBBs;
CaseBBs.emplace_back(SomeDecl, BridgeSuccessBB);
CaseBBs.emplace_back(mod.getASTContext().getOptionalNoneDecl(), FailureBB);
Builder.createSwitchEnumAddr(Loc, outOptionalParam, nullptr, CaseBBs);
Builder.setInsertionPoint(FailureBB->begin());
Builder.createDeallocStack(Loc, Tmp);
Builder.setInsertionPoint(BridgeSuccessBB);
auto Addr = Builder.createUncheckedTakeEnumDataAddr(Loc, outOptionalParam,
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;
}
//===----------------------------------------------------------------------===//
// Swift -> ObjC Bridging Cast Optimization
//===----------------------------------------------------------------------===//
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;
}
static Optional<std::pair<SILFunction *, SubstitutionMap>>
findBridgeToObjCFunc(SILOptFunctionBuilder &functionBuilder,
SILDynamicCastInst dynamicCast) {
CanType sourceType = dynamicCast.getSourceType();
auto loc = dynamicCast.getLocation();
auto &mod = dynamicCast.getModule();
auto bridgedProto =
mod.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
auto conf = mod.getSwiftModule()->lookupConformance(sourceType, bridgedProto);
assert(conf && "_ObjectiveCBridgeable conformance should exist");
(void)conf;
// Generate code to invoke _bridgeToObjectiveC
ModuleDecl *modDecl =
mod.getASTContext().getLoadedModule(mod.getASTContext().Id_Foundation);
if (!modDecl)
return None;
SmallVector<ValueDecl *, 2> results;
modDecl->lookupMember(results, sourceType.getNominalOrBoundGenericNominal(),
mod.getASTContext().Id_bridgeToObjectiveC,
Identifier());
ArrayRef<ValueDecl *> resultsRef(results);
if (resultsRef.empty()) {
mod.getSwiftModule()->lookupMember(
results, sourceType.getNominalOrBoundGenericNominal(),
mod.getASTContext().Id_bridgeToObjectiveC, Identifier());
resultsRef = results;
}
if (resultsRef.size() != 1)
return None;
auto *resultDecl = results.front();
auto memberDeclRef = SILDeclRef(resultDecl);
auto *bridgedFunc = functionBuilder.getOrCreateFunction(
loc, memberDeclRef, ForDefinition_t::NotForDefinition);
// Get substitutions, if source is a bound generic type.
auto subMap = sourceType->getContextSubstitutionMap(
mod.getSwiftModule(), resultDecl->getDeclContext());
// Implementation of _bridgeToObjectiveC could not be found.
if (!bridgedFunc)
return None;
if (dynamicCast.getFunction()->isSerialized() &&
!bridgedFunc->hasValidLinkageForFragileRef())
return None;
if (bridgedFunc->getLoweredFunctionType()
->getSingleResult()
.isFormalIndirect())
return None;
return std::make_pair(bridgedFunc, subMap);
}
static SILValue computeFinalCastedValue(SILBuilderWithScope &builder,
SILDynamicCastInst dynamicCast,
ApplyInst *newAI) {
SILValue dest = dynamicCast.getDest();
auto loc = dynamicCast.getLocation();
auto convTy = newAI->getType();
bool isConditional = dynamicCast.isConditional();
auto destTy = dest->getType().getObjectType();
assert(destTy == dynamicCast.getLoweredBridgedTargetObjectType() &&
"Expected Dest Type to be the same as BridgedTargetTy");
auto &m = dynamicCast.getModule();
if (convTy == destTy) {
return newAI;
}
if (destTy.isExactSuperclassOf(convTy)) {
return builder.createUpcast(loc, newAI, destTy);
}
if (convTy.isExactSuperclassOf(destTy)) {
// If we are not conditional, we are ok with the downcast via checked cast
// fails since we will trap.
if (!isConditional) {
return builder.createUnconditionalCheckedCast(loc, newAI, destTy);
}
// Otherwise if we /are/ emitting a conditional cast, make sure that we
// handle the failure gracefully.
//
// Since we are being returned the value at +1, we need to destroy the
// newAI on failure.
auto *failureBB = dynamicCast.getFailureBlock();
{
SILBuilderWithScope innerBuilder(&*failureBB->begin(), builder);
auto valueToDestroy = ([&]() -> SILValue {
if (!innerBuilder.hasOwnership())
return newAI;
return failureBB->createPhiArgument(newAI->getType(),
ValueOwnershipKind::Owned);
}());
innerBuilder.emitDestroyOperation(loc, valueToDestroy);
}
auto *condBrSuccessBB =
newAI->getFunction()->createBasicBlockAfter(newAI->getParent());
condBrSuccessBB->createPhiArgument(destTy, ValueOwnershipKind::Owned);
builder.createCheckedCastBranch(loc, /* isExact*/ false, newAI, destTy,
condBrSuccessBB, failureBB);
builder.setInsertionPoint(condBrSuccessBB, condBrSuccessBB->begin());
return condBrSuccessBB->getArgument(0);
}
if (convTy.getASTType() ==
getNSBridgedClassOfCFClass(m.getSwiftModule(), destTy.getASTType()) ||
destTy.getASTType() ==
getNSBridgedClassOfCFClass(m.getSwiftModule(), convTy.getASTType())) {
// Handle NS <-> CF toll-free bridging here.
return SILValue(builder.createUncheckedRefCast(loc, newAI, destTy));
}
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.");
}
/// Create a call of _bridgeToObjectiveC which converts an _ObjectiveCBridgeable
/// instance into a bridged ObjC type.
SILInstruction *
CastOptimizer::optimizeBridgedSwiftToObjCCast(SILDynamicCastInst dynamicCast) {
SILInstruction *Inst = dynamicCast.getInstruction();
const SILFunction *F = Inst->getFunction();
CastConsumptionKind ConsumptionKind = dynamicCast.getBridgedConsumptionKind();
bool isConditional = dynamicCast.isConditional();
SILValue Src = dynamicCast.getSource();
SILValue Dest = dynamicCast.getDest();
CanType BridgedTargetTy = dynamicCast.getBridgedTargetType();
SILBasicBlock *SuccessBB = dynamicCast.getSuccessBlock();
SILBasicBlock *FailureBB = dynamicCast.getFailureBlock();
auto &M = Inst->getModule();
auto Loc = Inst->getLoc();
bool AddressOnlyType = false;
if (!Src->getType().isLoadable(*F) || !Dest->getType().isLoadable(*F)) {
AddressOnlyType = true;
}
// Find the _BridgedToObjectiveC protocol.
SILFunction *bridgedFunc = nullptr;
SubstitutionMap subMap;
{
auto result = findBridgeToObjCFunc(functionBuilder, dynamicCast);
if (!result)
return nullptr;
std::tie(bridgedFunc, subMap) = result.getValue();
}
SILType SubstFnTy = bridgedFunc->getLoweredType().substGenericArgs(M, subMap);
SILFunctionConventions substConv(SubstFnTy.castTo<SILFunctionType>(), M);
// Check that this is a case that the authors of this code thought it could
// handle.
if (!canOptimizeCast(BridgedTargetTy, M, substConv)) {
return nullptr;
}
SILBuilderWithScope Builder(Inst, builderContext);
auto FnRef = Builder.createFunctionRefFor(Loc, bridgedFunc);
auto ParamTypes = SubstFnTy.castTo<SILFunctionType>()->getParameters();
SILValue oldSrc;
if (Src->getType().isAddress() && !substConv.isSILIndirect(ParamTypes[0])) {
// Create load
oldSrc = Src;
Src =
Builder.emitLoadValueOperation(Loc, Src, LoadOwnershipQualifier::Take);
}
// Compensate different owning conventions of the replaced cast instruction
// and the inserted conversion function.
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::BorrowAlways:
llvm_unreachable("Should never hit this");
case CastConsumptionKind::CopyOnSuccess:
// We assume that our caller is correct and will treat our argument as
// being immutable, so we do not need to do anything here.
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;
}
// Generate a code to invoke the bridging function.
auto *NewAI = Builder.createApply(Loc, FnRef, subMap, Src);
// First if we are going to destroy the value unconditionally, just insert the
// destroy right after the call. This handles some of the conditional cases
// and /all/ of the consuming unconditional cases.
if (needReleaseAfterCall) {
Builder.emitDestroyOperation(Loc, Src);
} else {
if (SuccessBB) {
SILBuilderWithScope succBuilder(&*SuccessBB->begin(), Builder);
if (needReleaseInSuccess) {
succBuilder.emitDestroyOperation(Loc, Src);
} else {
if (oldSrc) {
succBuilder.emitStoreValueOperation(Loc, Src, oldSrc,
StoreOwnershipQualifier::Init);
}
}
SILBuilderWithScope failBuilder(&*FailureBB->begin(), Builder);
if (oldSrc) {
failBuilder.emitStoreValueOperation(Loc, Src, oldSrc,
StoreOwnershipQualifier::Init);
}
} else {
if (oldSrc) {
Builder.emitStoreValueOperation(Loc, Src, oldSrc,
StoreOwnershipQualifier::Init);
}
}
}
if (!Dest)
return NewAI;
// If it is addr cast then store the result into the dest.
//
// NOTE: We assume that dest was uninitialized when passed to us.
SILValue castedValue = computeFinalCastedValue(Builder, dynamicCast, NewAI);
auto qual = Builder.hasOwnership() ? StoreOwnershipQualifier::Init
: StoreOwnershipQualifier::Unqualified;
SILInstruction *NewI = Builder.createStore(Loc, castedValue, Dest, qual);
if (isConditional && NewI->getParent() != NewAI->getParent()) {
Builder.createBranch(Loc, SuccessBB);
}
eraseInstAction(Inst);
return NewI;
}
//===----------------------------------------------------------------------===//
// Top Level Bridge Cast Optimization Entrypoint
//===----------------------------------------------------------------------===//
/// 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(SILDynamicCastInst dynamicCast) {
CanType source = dynamicCast.getSourceType();
CanType target = dynamicCast.getTargetType();
auto &M = dynamicCast.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;
CanType CanBridgedSourceTy = dynamicCast.getBridgedSourceType();
CanType CanBridgedTargetTy = dynamicCast.getBridgedTargetType();
// If we were unable to bridge either of our source/target types, return
// nullptr.
if (!CanBridgedSourceTy || !CanBridgedTargetTy)
return nullptr;
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;
}
// Check what kind of conversion it is? ObjC->Swift or Swift-ObjC?
if (CanBridgedTargetTy != target) {
// This is an ObjC to Swift cast.
return optimizeBridgedObjCToSwiftCast(dynamicCast);
} else {
// This is a Swift to ObjC cast
return optimizeBridgedSwiftToObjCCast(dynamicCast);
}
llvm_unreachable("Unknown kind of bridging");
}
//===----------------------------------------------------------------------===//
// Cast Optimizer Public API
//===----------------------------------------------------------------------===//
SILInstruction *CastOptimizer::simplifyCheckedCastAddrBranchInst(
CheckedCastAddrBranchInst *Inst) {
if (auto *I = optimizeCheckedCastAddrBranchInst(Inst))
Inst = dyn_cast<CheckedCastAddrBranchInst>(I);
if (!Inst)
return nullptr;
SILDynamicCastInst dynamicCast(Inst);
auto Loc = dynamicCast.getLocation();
auto Src = dynamicCast.getSource();
auto Dest = dynamicCast.getDest();
auto *SuccessBB = dynamicCast.getSuccessBlock();
auto *FailureBB = dynamicCast.getFailureBlock();
SILBuilderWithScope Builder(Inst, builderContext);
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
dynamicCast.classifyFeasibility(true /*allow whole module*/);
if (Feasibility == DynamicCastFeasibility::WillFail) {
if (shouldDestroyOnFailure(Inst->getConsumptionKind())) {
auto &srcTL = Builder.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(dynamicCast);
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.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.
switch (Inst->getConsumptionKind()) {
case CastConsumptionKind::BorrowAlways:
llvm_unreachable("checked_cast_addr_br never has BorrowAlways");
case CastConsumptionKind::CopyOnSuccess:
return nullptr;
case CastConsumptionKind::TakeAlways:
case CastConsumptionKind::TakeOnSuccess:
break;
}
if (!emitSuccessfulIndirectUnconditionalCast(Builder, Loc, dynamicCast)) {
// 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()) {
SILDynamicCastInst dynamicCast(Inst);
auto *ARI = dyn_cast<AllocRefInst>(stripUpCasts(dynamicCast.getSource()));
if (!ARI)
return nullptr;
// We know the dynamic type of the operand.
SILBuilderWithScope Builder(Inst, builderContext);
auto Loc = dynamicCast.getLocation();
if (ARI->getType() == dynamicCast.getLoweredTargetType()) {
// This exact cast will succeed.
SmallVector<SILValue, 1> Args;
Args.push_back(ARI);
auto *NewI =
Builder.createBranch(Loc, dynamicCast.getSuccessBlock(), Args);
eraseInstAction(Inst);
willSucceedAction();
return NewI;
}
// This exact cast will fail. With ownership enabled, we pass a copy of the
// original casts value to the failure block.
TinyPtrVector<SILValue> Args;
if (Builder.hasOwnership())
Args.push_back(dynamicCast.getSource());
auto *NewI = Builder.createBranch(Loc, dynamicCast.getFailureBlock(), Args);
eraseInstAction(Inst);
willFailAction();
return NewI;
}
if (auto *I = optimizeCheckedCastBranchInst(Inst))
Inst = dyn_cast<CheckedCastBranchInst>(I);
if (!Inst)
return nullptr;
SILDynamicCastInst dynamicCast(Inst);
auto LoweredTargetType = dynamicCast.getLoweredTargetType();
auto Loc = dynamicCast.getLocation();
auto *SuccessBB = dynamicCast.getSuccessBlock();
auto Op = dynamicCast.getSource();
auto *F = dynamicCast.getFunction();
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
dynamicCast.classifyFeasibility(false /*allow whole module*/);
SILBuilderWithScope Builder(Inst, builderContext);
if (Feasibility == DynamicCastFeasibility::WillFail) {
TinyPtrVector<SILValue> Args;
if (Builder.hasOwnership())
Args.push_back(Inst->getOperand());
auto *NewI = Builder.createBranch(Loc, dynamicCast.getFailureBlock(), Args);
eraseInstAction(Inst);
willFailAction();
return NewI;
}
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
SILValue CastedValue;
if (Op->getType() != LoweredTargetType) {
// 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(dynamicCast);
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 (!dynamicCast.canUseScalarCheckedCastInstructions())
return nullptr;
CastedValue =
emitSuccessfulScalarUnconditionalCast(Builder, Loc, dynamicCast);
} else {
CastedValue = SILUndef::get(LoweredTargetType, *F);
}
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;
SILDynamicCastInst dynamicCast(Inst);
auto LoweredTargetType = dynamicCast.getLoweredTargetType();
auto Loc = dynamicCast.getLocation();
auto *SuccessBB = dynamicCast.getSuccessBlock();
auto *FailureBB = dynamicCast.getFailureBlock();
auto Op = dynamicCast.getSource();
auto *F = dynamicCast.getFunction();
// Check if we can statically predict the outcome of the cast.
auto Feasibility = dynamicCast.classifyFeasibility(false /*allow wmo opts*/);
SILBuilderWithScope Builder(Inst, builderContext);
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) {
// 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(dynamicCast);
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 (!dynamicCast.canUseScalarCheckedCastInstructions())
return nullptr;
if (!ResultNotUsed) {
CastedValue =
emitSuccessfulScalarUnconditionalCast(Builder, Loc, dynamicCast);
} else {
CastedValue = SILUndef::get(LoweredTargetType, *F);
}
}
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, builderContext);
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;
// InstOptUtils.helper we use to simplify replacing a checked_cast_branch with
// an optimized checked cast branch.
auto replaceCastHelper = [](SILBuilderWithScope &B,
SILDynamicCastInst dynamicCast,
MetatypeInst *mi) -> SILInstruction * {
// Make sure that the failure block has the new metatype type for
// its default argument as required when we are in ossa
// mode. Without ossa, failure blocks do not have args, so we do
// not need to do anything.
auto *fBlock = dynamicCast.getFailureBlock();
if (B.hasOwnership()) {
fBlock->replacePhiArgumentAndReplaceAllUses(0, mi->getType(),
ValueOwnershipKind::Any);
}
return B.createCheckedCastBranch(
dynamicCast.getLocation(), false /*isExact*/, mi,
dynamicCast.getLoweredTargetType(), dynamicCast.getSuccessBlock(),
fBlock, *dynamicCast.getSuccessBlockCount(),
*dynamicCast.getFailureBlockCount());
};
SILDynamicCastInst dynamicCast(Inst);
auto Op = dynamicCast.getSource();
// 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, builderContext);
auto *NewI = replaceCastHelper(B, dynamicCast, MI);
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, builderContext);
B.getOpenedArchetypes().addOpenedArchetypeOperands(
FoundIEI->getTypeDependentOperands());
auto *MI = B.createMetatype(FoundIEI->getLoc(), SILMetaTy);
auto *NewI = replaceCastHelper(B, dynamicCast, MI);
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, builderContext);
B.getOpenedArchetypes().addOpenedArchetypeOperands(
FoundIERI->getTypeDependentOperands());
auto *MI = B.createMetatype(FoundIERI->getLoc(), SILMetaTy);
auto *NewI = replaceCastHelper(B, dynamicCast, MI);
eraseInstAction(Inst);
return NewI;
}
}
}
return nullptr;
}
ValueBase *CastOptimizer::optimizeUnconditionalCheckedCastInst(
UnconditionalCheckedCastInst *Inst) {
SILDynamicCastInst dynamicCast(Inst);
auto Loc = dynamicCast.getLocation();
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
dynamicCast.classifyFeasibility(false /*allowWholeModule*/);
if (Feasibility == DynamicCastFeasibility::WillFail) {
// Remove the cast and insert a trap, followed by an
// unreachable instruction.
SILBuilderWithScope Builder(Inst, builderContext);
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, builderContext);
// Try to apply the bridged casts optimizations
auto NewI = optimizeBridgedCasts(dynamicCast);
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 (dynamicCast.isBridgingCast())
return nullptr;
auto Result =
emitSuccessfulScalarUnconditionalCast(Builder, Loc, dynamicCast);
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)
return false;
// SourceType is a non-existential type with a non-conditional
// conformance to a protocol represented by the TargetType.
//
// TypeChecker::conformsToProtocol checks any conditional conformances. If
// they depend on information not known until runtime, the conformance
// will not be returned. 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`), in which case a
// valid conformance will be returned.
auto Conformance = SM->conformsToProtocol(SourceType, Proto);
if (!Conformance)
return false;
SILBuilderWithScope B(Inst);
SmallVector<ProtocolConformanceRef, 1> NewConformances;
NewConformances.push_back(Conformance.getValue());
ArrayRef<ProtocolConformanceRef> Conformances =
Ctx.AllocateCopy(NewConformances);
auto ExistentialRepr =
Dest->getType().getPreferredExistentialRepresentation(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.emitLoadValueOperation(Loc, Src, LoadOwnershipQualifier::Take);
auto Existential =
B.createInitExistentialRef(Loc, Dest->getType().getObjectType(),
SourceType, Value, Conformances);
B.emitStoreValueOperation(Loc, Existential, Dest,
StoreOwnershipQualifier::Init);
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.emitStoreValueOperation(Loc, AllocBox, Dest,
StoreOwnershipQualifier::Init);
break;
}
};
return true;
}
// Not a concrete -> existential cast.
return false;
}
SILInstruction *CastOptimizer::optimizeUnconditionalCheckedCastAddrInst(
UnconditionalCheckedCastAddrInst *Inst) {
SILDynamicCastInst dynamicCast(Inst);
auto Loc = dynamicCast.getLocation();
// Check if we can statically predict the outcome of the cast.
auto Feasibility =
dynamicCast.classifyFeasibility(false /*allow whole module*/);
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
// Forced bridged casts can be still simplified here.
// If they fail, they fail inside the conversion function.
if (!dynamicCast.isBridgingCast())
return nullptr;
}
if (Feasibility == DynamicCastFeasibility::WillFail) {
// Remove the cast and insert a trap, followed by an
// unreachable instruction.
SILBuilderWithScope Builder(Inst, builderContext);
// mem2reg's invariants get unhappy if we don't try to
// initialize a loadable result.
if (!dynamicCast.getLoweredTargetType().isAddressOnly(
Builder.getFunction())) {
auto undef = SILValue(
SILUndef::get(dynamicCast.getLoweredTargetType().getObjectType(),
Builder.getFunction()));
Builder.emitStoreValueOperation(Loc, undef, dynamicCast.getDest(),
StoreOwnershipQualifier::Init);
}
Builder.emitDestroyAddr(Loc, Inst->getSrc());
auto *TrapI = Builder.createBuiltinTrap(Loc);
eraseInstAction(Inst);
Builder.setInsertionPoint(std::next(TrapI->getIterator()));
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.
SILValue dest = dynamicCast.getDest();
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, builderContext);
B.createDestroyAddr(Loc, dynamicCast.getSource());
if (DestroyDestInst)
eraseInstAction(DestroyDestInst);
eraseInstAction(Inst);
willSucceedAction();
return nullptr;
}
// Try to apply the bridged casts optimizations.
auto NewI = optimizeBridgedCasts(dynamicCast);
if (NewI) {
willSucceedAction();
return nullptr;
}
if (Feasibility == DynamicCastFeasibility::MaySucceed)
return nullptr;
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
if (optimizeStaticallyKnownProtocolConformance(Inst)) {
eraseInstAction(Inst);
willSucceedAction();
return nullptr;
}
if (dynamicCast.isBridgingCast())
return nullptr;
SILBuilderWithScope Builder(Inst, builderContext);
if (!emitSuccessfulIndirectUnconditionalCast(Builder, Loc, dynamicCast)) {
// No optimization was possible.
return nullptr;
}
eraseInstAction(Inst);
willSucceedAction();
}
return nullptr;
}
/// Simplify conversions between thick and objc metatypes.
SILValue CastOptimizer::optimizeMetatypeConversion(
ConversionInst *mci, MetatypeRepresentation representation) {
SILValue op = mci->getOperand(0);
// Instruction has a proper target type already.
SILType ty = mci->getType();
auto metatypeTy = op->getType().getAs<AnyMetatypeType>();
if (metatypeTy->getRepresentation() != representation)
return SILValue();
auto loc = mci->getLoc();
// Rematerialize the incoming metatype instruction with the outgoing type.
auto replaceCast = [&](SILValue newValue) -> SILValue {
assert(ty.getAs<AnyMetatypeType>()->getRepresentation() ==
newValue->getType().getAs<AnyMetatypeType>()->getRepresentation());
replaceValueUsesAction(mci, newValue);
eraseInstAction(mci);
return newValue;
};
if (auto *mi = dyn_cast<MetatypeInst>(op)) {
return replaceCast(
SILBuilderWithScope(mci, builderContext).createMetatype(loc, ty));
}
// For metatype instructions that require an operand, generate the new
// metatype at the same position as the original to avoid extending the
// lifetime of `op` past its destroy.
if (auto *vmi = dyn_cast<ValueMetatypeInst>(op)) {
return replaceCast(SILBuilderWithScope(vmi, builderContext)
.createValueMetatype(loc, ty, vmi->getOperand()));
}
if (auto *emi = dyn_cast<ExistentialMetatypeInst>(op)) {
return replaceCast(
SILBuilderWithScope(emi, builderContext)
.createExistentialMetatype(loc, ty, emi->getOperand()));
}
return SILValue();
}