Files
swift-mirror/lib/SILOptimizer/Utils/Local.cpp
John McCall ab3f77baf2 Make SILInstruction no longer a subclass of ValueBase and
introduce a common superclass, SILNode.

This is in preparation for allowing instructions to have multiple
results.  It is also a somewhat more elegant representation for
instructions that have zero results.  Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction.  Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.

A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.

Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
2017-09-25 02:06:26 -04:00

3052 lines
108 KiB
C++

//===--- Local.cpp - Functions that perform local SIL transformations. ----===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "swift/SILOptimizer/Utils/Local.h"
#include "swift/SILOptimizer/Utils/CFG.h"
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/SIL/DynamicCasts.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/SIL/DebugUtils.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SIL/BasicBlockUtils.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;
static llvm::cl::opt<bool> EnableExpandAll("enable-expand-all",
llvm::cl::init(false));
/// Creates an increment on \p Ptr before insertion point \p InsertPt that
/// creates a strong_retain if \p Ptr has reference semantics itself or a
/// retain_value if \p Ptr is a non-trivial value without reference-semantics.
NullablePtr<SILInstruction>
swift::createIncrementBefore(SILValue Ptr, SILInstruction *InsertPt) {
// If we have a trivial type, just bail, there is no work to do.
if (Ptr->getType().isTrivial(InsertPt->getModule()))
return nullptr;
// Set up the builder we use to insert at our insertion point.
SILBuilder B(InsertPt);
auto Loc = RegularLocation(SourceLoc());
// If Ptr is refcounted itself, create the strong_retain and
// return.
if (Ptr->getType().isReferenceCounted(B.getModule()))
return B.createStrongRetain(Loc, Ptr, B.getDefaultAtomicity());
// Otherwise, create the retain_value.
return B.createRetainValue(Loc, Ptr, B.getDefaultAtomicity());
}
/// Creates a decrement on \p Ptr before insertion point \p InsertPt that
/// creates a strong_release if \p Ptr has reference semantics itself or
/// a release_value if \p Ptr is a non-trivial value without reference-semantics.
NullablePtr<SILInstruction>
swift::createDecrementBefore(SILValue Ptr, SILInstruction *InsertPt) {
if (Ptr->getType().isTrivial(InsertPt->getModule()))
return nullptr;
// Setup the builder we will use to insert at our insertion point.
SILBuilder B(InsertPt);
auto Loc = RegularLocation(SourceLoc());
// If Ptr has reference semantics itself, create a strong_release.
if (Ptr->getType().isReferenceCounted(B.getModule()))
return B.createStrongRelease(Loc, Ptr, B.getDefaultAtomicity());
// Otherwise create a release value.
return B.createReleaseValue(Loc, Ptr, B.getDefaultAtomicity());
}
/// \brief Perform a fast local check to see if the instruction is dead.
///
/// This routine only examines the state of the instruction at hand.
bool
swift::isInstructionTriviallyDead(SILInstruction *I) {
// At Onone, consider all uses, including the debug_info.
// This way, debug_info is preserved at Onone.
if (I->hasUsesOfAnyResult() &&
I->getModule().getOptions().Optimization <= SILOptions::SILOptMode::None)
return false;
if (!onlyHaveDebugUsesOfAllResults(I) || isa<TermInst>(I))
return false;
if (auto *BI = dyn_cast<BuiltinInst>(I)) {
// Although the onFastPath builtin has no side-effects we don't want to
// remove it.
if (BI->getBuiltinInfo().ID == BuiltinValueKind::OnFastPath)
return false;
return !BI->mayHaveSideEffects();
}
// condfail instructions that obviously can't fail are dead.
if (auto *CFI = dyn_cast<CondFailInst>(I))
if (auto *ILI = dyn_cast<IntegerLiteralInst>(CFI->getOperand()))
if (!ILI->getValue())
return true;
// mark_uninitialized is never dead.
if (isa<MarkUninitializedInst>(I))
return false;
if (isa<MarkUninitializedBehaviorInst>(I))
return false;
if (isa<DebugValueInst>(I) || isa<DebugValueAddrInst>(I))
return false;
// These invalidate enums so "write" memory, but that is not an essential
// operation so we can remove these if they are trivially dead.
if (isa<UncheckedTakeEnumDataAddrInst>(I))
return true;
if (!I->mayHaveSideEffects())
return true;
return false;
}
/// \brief Return true if this is a release instruction and the released value
/// is a part of a guaranteed parameter.
bool swift::isIntermediateRelease(SILInstruction *I,
EpilogueARCFunctionInfo *EAFI) {
// Check whether this is a release instruction.
if (!isa<StrongReleaseInst>(I) && !isa<ReleaseValueInst>(I))
return false;
// OK. we have a release instruction.
// Check whether this is a release on part of a guaranteed function argument.
SILValue Op = stripValueProjections(I->getOperand(0));
auto *Arg = dyn_cast<SILFunctionArgument>(Op);
if (!Arg)
return false;
// This is a release on a guaranteed parameter. Its not the final release.
if (Arg->hasConvention(SILArgumentConvention::Direct_Guaranteed))
return true;
// This is a release on an owned parameter and its not the epilogue release.
// Its not the final release.
auto Rel = EAFI->computeEpilogueARCInstructions(
EpilogueARCContext::EpilogueARCKind::Release, Arg);
if (Rel.size() && !Rel.count(I))
return true;
// Failed to prove anything.
return false;
}
namespace {
using CallbackTy = std::function<void(SILInstruction *)>;
} // end anonymous namespace
void swift::
recursivelyDeleteTriviallyDeadInstructions(ArrayRef<SILInstruction *> IA,
bool Force, CallbackTy Callback) {
// Delete these instruction and others that become dead after it's deleted.
llvm::SmallPtrSet<SILInstruction *, 8> DeadInsts;
for (auto I : IA) {
// If the instruction is not dead and force is false, do nothing.
if (Force || isInstructionTriviallyDead(I))
DeadInsts.insert(I);
}
llvm::SmallPtrSet<SILInstruction *, 8> NextInsts;
while (!DeadInsts.empty()) {
for (auto I : DeadInsts) {
// Call the callback before we mutate the to be deleted instruction in any
// way.
Callback(I);
// Check if any of the operands will become dead as well.
MutableArrayRef<Operand> Ops = I->getAllOperands();
for (Operand &Op : Ops) {
SILValue OpVal = Op.get();
if (!OpVal)
continue;
// Remove the reference from the instruction being deleted to this
// operand.
Op.drop();
// If the operand is an instruction that is only used by the instruction
// being deleted, delete it.
if (auto *OpValInst = OpVal->getDefiningInstruction())
if (!DeadInsts.count(OpValInst) &&
isInstructionTriviallyDead(OpValInst))
NextInsts.insert(OpValInst);
}
// If we have a function ref inst, we need to especially drop its function
// argument so that it gets a proper ref decrement.
auto *FRI = dyn_cast<FunctionRefInst>(I);
if (FRI && FRI->getReferencedFunction())
FRI->dropReferencedFunction();
}
for (auto I : DeadInsts) {
// This will remove this instruction and all its uses.
eraseFromParentWithDebugInsts(I);
}
NextInsts.swap(DeadInsts);
NextInsts.clear();
}
}
/// \brief If the given instruction is dead, delete it along with its dead
/// operands.
///
/// \param I The instruction to be deleted.
/// \param Force If Force is set, don't check if the top level instruction is
/// considered dead - delete it regardless.
void swift::recursivelyDeleteTriviallyDeadInstructions(SILInstruction *I,
bool Force,
CallbackTy Callback) {
ArrayRef<SILInstruction *> AI = ArrayRef<SILInstruction *>(I);
recursivelyDeleteTriviallyDeadInstructions(AI, Force, Callback);
}
void swift::eraseUsesOfInstruction(SILInstruction *Inst,
CallbackTy Callback) {
for (auto result : Inst->getResults()) {
while (!result->use_empty()) {
auto UI = result->use_begin();
auto *User = UI->getUser();
assert(User && "User should never be NULL!");
// If the instruction itself has any uses, recursively zap them so that
// nothing uses this instruction.
eraseUsesOfInstruction(User, Callback);
// Walk through the operand list and delete any random instructions that
// will become trivially dead when this instruction is removed.
for (auto &Op : User->getAllOperands()) {
if (auto *OpI = Op.get()->getDefiningInstruction()) {
// Don't recursively delete the instruction we're working on.
// FIXME: what if we're being recursively invoked?
if (OpI != Inst) {
Op.drop();
recursivelyDeleteTriviallyDeadInstructions(OpI, false, Callback);
}
}
}
Callback(User);
User->eraseFromParent();
}
}
}
void swift::
collectUsesOfValue(SILValue V, llvm::SmallPtrSetImpl<SILInstruction *> &Insts) {
for (auto UI = V->use_begin(), E = V->use_end(); UI != E; UI++) {
auto *User = UI->getUser();
// Instruction has been processed.
if (!Insts.insert(User).second)
continue;
// Collect the users of this instruction.
for (auto result : User->getResults())
collectUsesOfValue(result, Insts);
}
}
void swift::eraseUsesOfValue(SILValue V) {
llvm::SmallPtrSet<SILInstruction *, 4> Insts;
// Collect the uses.
collectUsesOfValue(V, Insts);
// Erase the uses, we can have instructions that become dead because
// of the removal of these instructions, leave to DCE to cleanup.
// Its not safe to do recursively delete here as some of the SILInstruction
// maybe tracked by this set.
for (auto I : Insts) {
I->replaceAllUsesOfAllResultsWithUndef();
I->eraseFromParent();
}
}
// Devirtualization of functions with covariant return types produces
// a result that is not an apply, but takes an apply as an
// argument. Attempt to dig the apply out from this result.
FullApplySite swift::findApplyFromDevirtualizedResult(SILValue V) {
if (auto Apply = FullApplySite::isa(V))
return Apply;
if (isa<UpcastInst>(V) || isa<EnumInst>(V) || isa<UncheckedRefCastInst>(V))
return findApplyFromDevirtualizedResult(
cast<SingleValueInstruction>(V)->getOperand(0));
return FullApplySite();
}
SILValue swift::isPartialApplyOfReabstractionThunk(PartialApplyInst *PAI) {
if (PAI->getNumArguments() != 1)
return SILValue();
auto *Fun = PAI->getReferencedFunction();
if (!Fun)
return SILValue();
// Make sure we have a reabstraction thunk.
if (Fun->isThunk() != IsReabstractionThunk)
return SILValue();
// The argument should be a closure.
auto Arg = PAI->getArgument(0);
if (!Arg->getType().is<SILFunctionType>() ||
!Arg->getType().isReferenceCounted(PAI->getFunction()->getModule()))
return SILValue();
return Arg;
}
// Replace a dead apply with a new instruction that computes the same
// value, and delete the old apply.
void swift::replaceDeadApply(ApplySite Old, ValueBase *New) {
auto *OldApply = Old.getInstruction();
if (!isa<TryApplyInst>(OldApply))
cast<SingleValueInstruction>(OldApply)->replaceAllUsesWith(New);
recursivelyDeleteTriviallyDeadInstructions(OldApply, true);
}
bool swift::hasArchetypes(SubstitutionList Subs) {
// Check whether any of the substitutions are dependent.
return llvm::any_of(Subs, [](const Substitution &S) {
return S.getReplacement()->hasArchetype();
});
}
bool swift::mayBindDynamicSelf(SILFunction *F) {
if (!F->hasSelfMetadataParam())
return false;
SILValue MDArg = F->getSelfMetadataArgument();
for (Operand *MDUse : F->getSelfMetadataArgument()->getUses()) {
SILInstruction *MDUser = MDUse->getUser();
for (Operand &TypeDepOp : MDUser->getTypeDependentOperands()) {
if (TypeDepOp.get() == MDArg)
return true;
}
}
return false;
}
/// Find a new position for an ApplyInst's FuncRef so that it dominates its
/// use. Not that FunctionRefInsts may be shared by multiple ApplyInsts.
void swift::placeFuncRef(ApplyInst *AI, DominanceInfo *DT) {
FunctionRefInst *FuncRef = cast<FunctionRefInst>(AI->getCallee());
SILBasicBlock *DomBB =
DT->findNearestCommonDominator(AI->getParent(), FuncRef->getParent());
if (DomBB == AI->getParent() && DomBB != FuncRef->getParent())
// Prefer to place the FuncRef immediately before the call. Since we're
// moving FuncRef up, this must be the only call to it in the block.
FuncRef->moveBefore(AI);
else
// Otherwise, conservatively stick it at the beginning of the block.
FuncRef->moveBefore(&*DomBB->begin());
}
/// \brief Add an argument, \p val, to the branch-edge that is pointing into
/// block \p Dest. Return a new instruction and do not erase the old
/// instruction.
TermInst *swift::addArgumentToBranch(SILValue Val, SILBasicBlock *Dest,
TermInst *Branch) {
SILBuilderWithScope Builder(Branch);
if (auto *CBI = dyn_cast<CondBranchInst>(Branch)) {
SmallVector<SILValue, 8> TrueArgs;
SmallVector<SILValue, 8> FalseArgs;
for (auto A : CBI->getTrueArgs())
TrueArgs.push_back(A);
for (auto A : CBI->getFalseArgs())
FalseArgs.push_back(A);
if (Dest == CBI->getTrueBB()) {
TrueArgs.push_back(Val);
assert(TrueArgs.size() == Dest->getNumArguments());
} else {
FalseArgs.push_back(Val);
assert(FalseArgs.size() == Dest->getNumArguments());
}
return Builder.createCondBranch(CBI->getLoc(), CBI->getCondition(),
CBI->getTrueBB(), TrueArgs,
CBI->getFalseBB(), FalseArgs);
}
if (auto *BI = dyn_cast<BranchInst>(Branch)) {
SmallVector<SILValue, 8> Args;
for (auto A : BI->getArgs())
Args.push_back(A);
Args.push_back(Val);
assert(Args.size() == Dest->getNumArguments());
return Builder.createBranch(BI->getLoc(), BI->getDestBB(), Args);
}
llvm_unreachable("unsupported terminator");
}
SILLinkage swift::getSpecializedLinkage(SILFunction *F, SILLinkage L) {
if (hasPrivateVisibility(L) &&
!F->isSerialized()) {
// Specializations of private symbols should remain so, unless
// they were serialized, which can only happen when specializing
// definitions from a standard library built with -sil-serialize-all.
return SILLinkage::Private;
}
// Treat stdlib_binary_only specially. We don't serialize the body of
// stdlib_binary_only functions so we can't mark them as Shared (making
// their visibility in the dylib hidden).
return F->hasSemanticsAttr("stdlib_binary_only") ? SILLinkage::Public
: SILLinkage::Shared;
}
/// Remove all instructions in the body of \p BB in safe manner by using
/// undef.
void swift::clearBlockBody(SILBasicBlock *BB) {
// Instructions in the dead block may be used by other dead blocks. Replace
// any uses of them with undef values.
while (!BB->empty()) {
// Grab the last instruction in the BB.
auto *Inst = &BB->back();
// Replace any still-remaining uses with undef values and erase.
Inst->replaceAllUsesOfAllResultsWithUndef();
Inst->eraseFromParent();
}
}
// Handle the mechanical aspects of removing an unreachable block.
void swift::removeDeadBlock(SILBasicBlock *BB) {
// Clear the body of BB.
clearBlockBody(BB);
// Now that the BB is empty, eliminate it.
BB->eraseFromParent();
}
/// Cast a value into the expected, ABI compatible type if necessary.
/// This may happen e.g. when:
/// - a type of the return value is a subclass of the expected return type.
/// - actual return type and expected return type differ in optionality.
/// - both types are tuple-types and some of the elements need to be casted.
///
/// If CheckOnly flag is set, then this function only checks if the
/// required casting is possible. If it is not possible, then None
/// is returned.
///
/// If CheckOnly is not set, then a casting code is generated and the final
/// casted value is returned.
///
/// NOTE: We intentionally combine the checking of the cast's handling possibility
/// and the transformation performing the cast in the same function, to avoid
/// any divergence between the check and the implementation in the future.
///
/// NOTE: The implementation of this function is very closely related to the
/// rules checked by SILVerifier::requireABICompatibleFunctionTypes.
SILValue swift::castValueToABICompatibleType(SILBuilder *B, SILLocation Loc,
SILValue Value,
SILType SrcTy, SILType DestTy) {
// No cast is required if types are the same.
if (SrcTy == DestTy)
return Value;
assert(SrcTy.isAddress() == DestTy.isAddress() &&
"Addresses aren't compatible with values");
if (SrcTy.isAddress() && DestTy.isAddress()) {
// Cast between two addresses and that's it.
return B->createUncheckedAddrCast(Loc, Value, DestTy);
}
// If both types are classes and dest is the superclass of src,
// simply perform an upcast.
if (DestTy.isExactSuperclassOf(SrcTy)) {
return B->createUpcast(Loc, Value, DestTy);
}
if (SrcTy.isHeapObjectReferenceType() &&
DestTy.isHeapObjectReferenceType()) {
return B->createUncheckedRefCast(Loc, Value, DestTy);
}
if (auto mt1 = SrcTy.getAs<AnyMetatypeType>()) {
if (auto mt2 = DestTy.getAs<AnyMetatypeType>()) {
if (mt1->getRepresentation() == mt2->getRepresentation()) {
// If B.Type needs to be casted to A.Type and
// A is a superclass of B, then it can be done by means
// of a simple upcast.
if (mt2.getInstanceType()->isExactSuperclassOf(
mt1.getInstanceType())) {
return B->createUpcast(Loc, Value, DestTy);
}
// Cast between two metatypes and that's it.
return B->createUncheckedBitCast(Loc, Value, DestTy);
}
}
}
// Check if src and dest types are optional.
auto OptionalSrcTy = SrcTy.getAnyOptionalObjectType();
auto OptionalDestTy = DestTy.getAnyOptionalObjectType();
// Both types are optional.
if (OptionalDestTy && OptionalSrcTy) {
// If both wrapped types are classes and dest is the superclass of src,
// simply perform an upcast.
if (OptionalDestTy.isExactSuperclassOf(OptionalSrcTy)) {
// Insert upcast.
return B->createUpcast(Loc, Value, DestTy);
}
// Unwrap the original optional value.
auto *SomeDecl = B->getASTContext().getOptionalSomeDecl();
auto *NoneBB = B->getFunction().createBasicBlock();
auto *SomeBB = B->getFunction().createBasicBlock();
auto *CurBB = B->getInsertionPoint()->getParent();
auto *ContBB = CurBB->split(B->getInsertionPoint());
ContBB->createPHIArgument(DestTy, ValueOwnershipKind::Owned);
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock *>, 1> CaseBBs;
CaseBBs.push_back(std::make_pair(SomeDecl, SomeBB));
B->setInsertionPoint(CurBB);
B->createSwitchEnum(Loc, Value, NoneBB, CaseBBs);
// Handle the Some case.
B->setInsertionPoint(SomeBB);
SILValue UnwrappedValue = B->createUncheckedEnumData(Loc, Value,
SomeDecl);
// Cast the unwrapped value.
auto CastedUnwrappedValue =
castValueToABICompatibleType(B, Loc, UnwrappedValue,
OptionalSrcTy,
OptionalDestTy);
// Wrap into optional.
auto CastedValue = B->createOptionalSome(Loc, CastedUnwrappedValue, DestTy);
B->createBranch(Loc, ContBB, {CastedValue});
// Handle the None case.
B->setInsertionPoint(NoneBB);
CastedValue = B->createOptionalNone(Loc, DestTy);
B->createBranch(Loc, ContBB, {CastedValue});
B->setInsertionPoint(ContBB->begin());
return ContBB->getArgument(0);
}
// Src is not optional, but dest is optional.
if (!OptionalSrcTy && OptionalDestTy) {
auto OptionalSrcCanTy = OptionalType::get(SrcTy.getSwiftRValueType())
->getCanonicalType();
auto LoweredOptionalSrcType = SILType::getPrimitiveObjectType(
OptionalSrcCanTy);
// Wrap the source value into an optional first.
SILValue WrappedValue = B->createOptionalSome(Loc, Value,
LoweredOptionalSrcType);
// Cast the wrapped value.
return castValueToABICompatibleType(B, Loc, WrappedValue,
WrappedValue->getType(),
DestTy);
}
// Handle tuple types.
// Extract elements, cast each of them, create a new tuple.
if (auto SrcTupleTy = SrcTy.getAs<TupleType>()) {
SmallVector<SILValue, 8> ExpectedTuple;
for (unsigned i = 0, e = SrcTupleTy->getNumElements(); i < e; i++) {
SILValue Element = B->createTupleExtract(Loc, Value, i);
// Cast the value if necessary.
Element = castValueToABICompatibleType(B, Loc, Element,
SrcTy.getTupleElementType(i),
DestTy.getTupleElementType(i));
ExpectedTuple.push_back(Element);
}
return B->createTuple(Loc, DestTy, ExpectedTuple);
}
// Function types are interchangeable if they're also ABI-compatible.
if (SrcTy.is<SILFunctionType>()) {
if (DestTy.is<SILFunctionType>()) {
// Insert convert_function.
return B->createConvertFunction(Loc, Value, DestTy);
}
}
llvm::errs() << "Source type: " << SrcTy << "\n";
llvm::errs() << "Destination type: " << DestTy << "\n";
llvm_unreachable("Unknown combination of types for casting");
}
ProjectBoxInst *swift::getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index){
SILBasicBlock::iterator Iter(ABI);
Iter++;
assert(Iter != ABI->getParent()->end() &&
"alloc_box cannot be the last instruction of a block");
SILInstruction *NextInst = &*Iter;
if (auto *PBI = dyn_cast<ProjectBoxInst>(NextInst)) {
if (PBI->getOperand() == ABI && PBI->getFieldIndex() == Index)
return PBI;
}
SILBuilder B(NextInst);
return B.createProjectBox(ABI->getLoc(), ABI, Index);
}
//===----------------------------------------------------------------------===//
// String Concatenation Optimizer
//===----------------------------------------------------------------------===//
namespace {
/// This is a helper class that performs optimization of string literals
/// concatenation.
class StringConcatenationOptimizer {
/// Apply instruction being optimized.
ApplyInst *AI;
/// Builder to be used for creation of new instructions.
SILBuilder &Builder;
/// Left string literal operand of a string concatenation.
StringLiteralInst *SLILeft = nullptr;
/// Right string literal operand of a string concatenation.
StringLiteralInst *SLIRight = nullptr;
/// Function used to construct the left string literal.
FunctionRefInst *FRILeft = nullptr;
/// Function used to construct the right string literal.
FunctionRefInst *FRIRight = nullptr;
/// Apply instructions used to construct left string literal.
ApplyInst *AILeft = nullptr;
/// Apply instructions used to construct right string literal.
ApplyInst *AIRight = nullptr;
/// String literal conversion function to be used.
FunctionRefInst *FRIConvertFromBuiltin = nullptr;
/// Result type of a function producing the concatenated string literal.
SILValue FuncResultType;
/// Internal helper methods
bool extractStringConcatOperands();
void adjustEncodings();
APInt getConcatenatedLength();
bool isAscii() const;
public:
StringConcatenationOptimizer(ApplyInst *AI, SILBuilder &Builder)
: AI(AI), Builder(Builder) {}
/// Tries to optimize a given apply instruction if it is a
/// concatenation of string literals.
///
/// Returns a new instruction if optimization was possible.
SingleValueInstruction *optimize();
};
} // end anonymous namespace
/// Checks operands of a string concatenation operation to see if
/// optimization is applicable.
///
/// Returns false if optimization is not possible.
/// Returns true and initializes internal fields if optimization is possible.
bool StringConcatenationOptimizer::extractStringConcatOperands() {
auto *Fn = AI->getReferencedFunction();
if (!Fn)
return false;
if (AI->getNumArguments() != 3 || !Fn->hasSemanticsAttr("string.concat"))
return false;
assert(Fn->getRepresentation() == SILFunctionTypeRepresentation::Method);
// Left and right operands of a string concatenation operation.
AILeft = dyn_cast<ApplyInst>(AI->getOperand(1));
AIRight = dyn_cast<ApplyInst>(AI->getOperand(2));
if (!AILeft || !AIRight)
return false;
FRILeft = dyn_cast<FunctionRefInst>(AILeft->getCallee());
FRIRight = dyn_cast<FunctionRefInst>(AIRight->getCallee());
if (!FRILeft || !FRIRight)
return false;
auto *FRILeftFun = FRILeft->getReferencedFunction();
auto *FRIRightFun = FRIRight->getReferencedFunction();
if (FRILeftFun->getEffectsKind() >= EffectsKind::ReadWrite ||
FRIRightFun->getEffectsKind() >= EffectsKind::ReadWrite)
return false;
if (!FRILeftFun->hasSemanticsAttrs() || !FRIRightFun->hasSemanticsAttrs())
return false;
auto AILeftOperandsNum = AILeft->getNumOperands();
auto AIRightOperandsNum = AIRight->getNumOperands();
// makeUTF16 should have following parameters:
// (start: RawPointer, utf16CodeUnitCount: Word)
// makeUTF8 should have following parameters:
// (start: RawPointer, utf8CodeUnitCount: Word, isASCII: Int1)
if (!((FRILeftFun->hasSemanticsAttr("string.makeUTF16") &&
AILeftOperandsNum == 4) ||
(FRILeftFun->hasSemanticsAttr("string.makeUTF8") &&
AILeftOperandsNum == 5) ||
(FRIRightFun->hasSemanticsAttr("string.makeUTF16") &&
AIRightOperandsNum == 4) ||
(FRIRightFun->hasSemanticsAttr("string.makeUTF8") &&
AIRightOperandsNum == 5)))
return false;
assert(FRILeftFun->getRepresentation() ==
SILFunctionTypeRepresentation::Method);
assert(FRIRightFun->getRepresentation() ==
SILFunctionTypeRepresentation::Method);
SLILeft = dyn_cast<StringLiteralInst>(AILeft->getOperand(1));
SLIRight = dyn_cast<StringLiteralInst>(AIRight->getOperand(1));
if (!SLILeft || !SLIRight)
return false;
// Only UTF-8 and UTF-16 encoded string literals are supported by this
// optimization.
if (SLILeft->getEncoding() != StringLiteralInst::Encoding::UTF8 &&
SLILeft->getEncoding() != StringLiteralInst::Encoding::UTF16)
return false;
if (SLIRight->getEncoding() != StringLiteralInst::Encoding::UTF8 &&
SLIRight->getEncoding() != StringLiteralInst::Encoding::UTF16)
return false;
return true;
}
/// Ensures that both string literals to be concatenated use the same
/// UTF encoding. Converts UTF-8 into UTF-16 if required.
void StringConcatenationOptimizer::adjustEncodings() {
if (SLILeft->getEncoding() == SLIRight->getEncoding()) {
FRIConvertFromBuiltin = FRILeft;
if (SLILeft->getEncoding() == StringLiteralInst::Encoding::UTF8) {
FuncResultType = AILeft->getOperand(4);
} else {
FuncResultType = AILeft->getOperand(3);
}
return;
}
Builder.setCurrentDebugScope(AI->getDebugScope());
// If one of the string literals is UTF8 and another one is UTF16,
// convert the UTF8-encoded string literal into UTF16-encoding first.
if (SLILeft->getEncoding() == StringLiteralInst::Encoding::UTF8 &&
SLIRight->getEncoding() == StringLiteralInst::Encoding::UTF16) {
FuncResultType = AIRight->getOperand(3);
FRIConvertFromBuiltin = FRIRight;
// Convert UTF8 representation into UTF16.
SLILeft = Builder.createStringLiteral(AI->getLoc(), SLILeft->getValue(),
StringLiteralInst::Encoding::UTF16);
}
if (SLIRight->getEncoding() == StringLiteralInst::Encoding::UTF8 &&
SLILeft->getEncoding() == StringLiteralInst::Encoding::UTF16) {
FuncResultType = AILeft->getOperand(3);
FRIConvertFromBuiltin = FRILeft;
// Convert UTF8 representation into UTF16.
SLIRight = Builder.createStringLiteral(AI->getLoc(), SLIRight->getValue(),
StringLiteralInst::Encoding::UTF16);
}
// It should be impossible to have two operands with different
// encodings at this point.
assert(SLILeft->getEncoding() == SLIRight->getEncoding() &&
"Both operands of string concatenation should have the same encoding");
}
/// Computes the length of a concatenated string literal.
APInt StringConcatenationOptimizer::getConcatenatedLength() {
// Real length of string literals computed based on its contents.
// Length is in code units.
auto SLILenLeft = SLILeft->getCodeUnitCount();
(void) SLILenLeft;
auto SLILenRight = SLIRight->getCodeUnitCount();
(void) SLILenRight;
// Length of string literals as reported by string.make functions.
auto *LenLeft = dyn_cast<IntegerLiteralInst>(AILeft->getOperand(2));
auto *LenRight = dyn_cast<IntegerLiteralInst>(AIRight->getOperand(2));
// Real and reported length should be the same.
assert(SLILenLeft == LenLeft->getValue() &&
"Size of string literal in @_semantics(string.make) is wrong");
assert(SLILenRight == LenRight->getValue() &&
"Size of string literal in @_semantics(string.make) is wrong");
// Compute length of the concatenated literal.
return LenLeft->getValue() + LenRight->getValue();
}
/// Computes the isAscii flag of a concatenated UTF8-encoded string literal.
bool StringConcatenationOptimizer::isAscii() const{
// Add the isASCII argument in case of UTF8.
// IsASCII is true only if IsASCII of both literals is true.
auto *AsciiLeft = dyn_cast<IntegerLiteralInst>(AILeft->getOperand(3));
auto *AsciiRight = dyn_cast<IntegerLiteralInst>(AIRight->getOperand(3));
auto IsAsciiLeft = AsciiLeft->getValue() == 1;
auto IsAsciiRight = AsciiRight->getValue() == 1;
return IsAsciiLeft && IsAsciiRight;
}
SingleValueInstruction *StringConcatenationOptimizer::optimize() {
// Bail out if string literals concatenation optimization is
// not possible.
if (!extractStringConcatOperands())
return nullptr;
// Perform string literal encodings adjustments if needed.
adjustEncodings();
// Arguments of the new StringLiteralInst to be created.
SmallVector<SILValue, 4> Arguments;
// Encoding to be used for the concatenated string literal.
auto Encoding = SLILeft->getEncoding();
// Create a concatenated string literal.
Builder.setCurrentDebugScope(AI->getDebugScope());
auto LV = SLILeft->getValue();
auto RV = SLIRight->getValue();
auto *NewSLI =
Builder.createStringLiteral(AI->getLoc(), LV + Twine(RV), Encoding);
Arguments.push_back(NewSLI);
// Length of the concatenated literal according to its encoding.
auto *Len = Builder.createIntegerLiteral(
AI->getLoc(), AILeft->getOperand(2)->getType(), getConcatenatedLength());
Arguments.push_back(Len);
// isAscii flag for UTF8-encoded string literals.
if (Encoding == StringLiteralInst::Encoding::UTF8) {
bool IsAscii = isAscii();
auto ILType = AILeft->getOperand(3)->getType();
auto *Ascii =
Builder.createIntegerLiteral(AI->getLoc(), ILType, intmax_t(IsAscii));
Arguments.push_back(Ascii);
}
// Type.
Arguments.push_back(FuncResultType);
return Builder.createApply(AI->getLoc(), FRIConvertFromBuiltin,
SubstitutionList(), Arguments,
false);
}
/// Top level entry point
SingleValueInstruction *
swift::tryToConcatenateStrings(ApplyInst *AI, SILBuilder &B) {
return StringConcatenationOptimizer(AI, B).optimize();
}
//===----------------------------------------------------------------------===//
// Closure Deletion
//===----------------------------------------------------------------------===//
static bool useDoesNotKeepClosureAlive(const SILInstruction *I) {
switch (I->getKind()) {
case SILInstructionKind::StrongRetainInst:
case SILInstructionKind::StrongReleaseInst:
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::DestroyValueInst:
case SILInstructionKind::RetainValueInst:
case SILInstructionKind::ReleaseValueInst:
case SILInstructionKind::DebugValueInst:
return true;
default:
return false;
}
}
static SILValue createLifetimeExtendedAllocStack(
SILBuilder &Builder, SILLocation Loc, SILValue Arg,
ArrayRef<SILBasicBlock *> ExitingBlocks, InstModCallbacks Callbacks) {
AllocStackInst *ASI = nullptr;
{
// Save our insert point and create a new alloc_stack in the initial BB and
// dealloc_stack in all exit blocks.
auto *OldInsertPt = &*Builder.getInsertionPoint();
Builder.setInsertionPoint(Builder.getFunction().begin()->begin());
ASI = Builder.createAllocStack(Loc, Arg->getType());
Callbacks.CreatedNewInst(ASI);
for (auto *BB : ExitingBlocks) {
Builder.setInsertionPoint(BB->getTerminator());
Callbacks.CreatedNewInst(Builder.createDeallocStack(Loc, ASI));
}
Builder.setInsertionPoint(OldInsertPt);
}
assert(ASI != nullptr);
// Then perform a copy_addr [take] [init] right after the partial_apply from
// the original address argument to the new alloc_stack that we have
// created.
Callbacks.CreatedNewInst(
Builder.createCopyAddr(Loc, Arg, ASI, IsTake, IsInitialization));
// Return the new alloc_stack inst that has the appropriate live range to
// destroy said values.
return ASI;
}
static bool shouldDestroyPartialApplyCapturedArg(SILValue Arg,
SILParameterInfo PInfo,
SILModule &M) {
// If we have a non-trivial type and the argument is passed in @inout, we do
// not need to destroy it here. This is something that is implicit in the
// partial_apply design that will be revisited when partial_apply is
// redesigned.
if (PInfo.isIndirectMutating())
return false;
// If we have a trivial type, we do not need to put in any extra releases.
if (Arg->getType().isTrivial(M))
return false;
// We handle all other cases.
return true;
}
// *HEY YOU, YES YOU, PLEASE READ*. Even though a textual partial apply is
// printed with the convention of the closed over function upon it, all
// non-inout arguments to a partial_apply are passed at +1. This includes
// arguments that will eventually be passed as guaranteed or in_guaranteed to
// the closed over function. This is because the partial apply is building up a
// boxed aggregate to send off to the closed over function. Of course when you
// call the function, the proper conventions will be used.
void swift::releasePartialApplyCapturedArg(SILBuilder &Builder, SILLocation Loc,
SILValue Arg, SILParameterInfo PInfo,
InstModCallbacks Callbacks) {
if (!shouldDestroyPartialApplyCapturedArg(Arg, PInfo, Builder.getModule()))
return;
// Otherwise, we need to destroy the argument. If we have an address, we
// insert a destroy_addr and return. Any live range issues must have been
// dealt with by our caller.
if (Arg->getType().isAddress()) {
// Then emit the destroy_addr for this arg
SILInstruction *NewInst = Builder.emitDestroyAddrAndFold(Loc, Arg);
Callbacks.CreatedNewInst(NewInst);
return;
}
// Otherwise, we have an object. We emit the most optimized form of release
// possible for that value.
// If we have qualified ownership, we should just emit a destroy value.
if (Arg->getFunction()->hasQualifiedOwnership()) {
Callbacks.CreatedNewInst(Builder.createDestroyValue(Loc, Arg));
return;
}
if (Arg->getType().hasReferenceSemantics()) {
auto U = Builder.emitStrongRelease(Loc, Arg);
if (U.isNull())
return;
if (auto *SRI = U.dyn_cast<StrongRetainInst *>()) {
Callbacks.DeleteInst(SRI);
return;
}
Callbacks.CreatedNewInst(U.get<StrongReleaseInst *>());
return;
}
auto U = Builder.emitReleaseValue(Loc, Arg);
if (U.isNull())
return;
if (auto *RVI = U.dyn_cast<RetainValueInst *>()) {
Callbacks.DeleteInst(RVI);
return;
}
Callbacks.CreatedNewInst(U.get<ReleaseValueInst *>());
}
/// For each captured argument of PAI, decrement the ref count of the captured
/// argument as appropriate at each of the post dominated release locations
/// found by Tracker.
static bool releaseCapturedArgsOfDeadPartialApply(PartialApplyInst *PAI,
ReleaseTracker &Tracker,
InstModCallbacks Callbacks) {
SILBuilderWithScope Builder(PAI);
SILLocation Loc = PAI->getLoc();
CanSILFunctionType PAITy =
PAI->getCallee()->getType().getAs<SILFunctionType>();
ArrayRef<SILParameterInfo> Params = PAITy->getParameters();
llvm::SmallVector<SILValue, 8> Args;
for (SILValue v : PAI->getArguments()) {
// If any of our arguments contain open existentials, bail. We do not
// support this for now so that we can avoid having to re-order stack
// locations (a larger change).
if (v->getType().hasOpenedExistential())
return false;
Args.emplace_back(v);
}
unsigned Delta = Params.size() - Args.size();
assert(Delta <= Params.size() && "Error, more Args to partial apply than "
"params in its interface.");
Params = Params.drop_front(Delta);
llvm::SmallVector<SILBasicBlock *, 2> ExitingBlocks;
PAI->getFunction()->findExitingBlocks(ExitingBlocks);
// Go through our argument list and create new alloc_stacks for each
// non-trivial address value. This ensures that the memory location that we
// are cleaning up has the same live range as the partial_apply. Otherwise, we
// may be inserting destroy_addr of alloc_stack that have already been passed
// to a dealloc_stack.
for (unsigned i : reversed(indices(Args))) {
SILValue Arg = Args[i];
SILParameterInfo PInfo = Params[i];
// If we are not going to destroy this partial_apply, continue.
if (!shouldDestroyPartialApplyCapturedArg(Arg, PInfo, Builder.getModule()))
continue;
// If we have an object, we will not have live range issues, just continue.
if (Arg->getType().isObject())
continue;
// Now that we know that we have a non-argument address, perform a take-init
// of Arg into a lifetime extended alloc_stack
Args[i] = createLifetimeExtendedAllocStack(Builder, Loc, Arg, ExitingBlocks,
Callbacks);
}
// Emit a destroy for each captured closure argument at each final release
// point.
for (auto *FinalRelease : Tracker.getFinalReleases()) {
Builder.setInsertionPoint(FinalRelease);
for (unsigned i : indices(Args)) {
SILValue Arg = Args[i];
SILParameterInfo Param = Params[i];
releasePartialApplyCapturedArg(Builder, Loc, Arg, Param, Callbacks);
}
}
return true;
}
/// TODO: Generalize this to general objects.
bool swift::tryDeleteDeadClosure(SingleValueInstruction *Closure,
InstModCallbacks Callbacks) {
// We currently only handle locally identified values that do not escape. We
// also assume that the partial apply does not capture any addresses.
if (!isa<PartialApplyInst>(Closure) && !isa<ThinToThickFunctionInst>(Closure))
return false;
// We only accept a user if it is an ARC object that can be removed if the
// object is dead. This should be expanded in the future. This also ensures
// that we are locally identified and non-escaping since we only allow for
// specific ARC users.
ReleaseTracker Tracker([](const SILInstruction *I) -> bool {
return useDoesNotKeepClosureAlive(I);
});
// Find the ARC Users and the final retain, release.
if (!getFinalReleasesForValue(SILValue(Closure), Tracker))
return false;
// If we have a partial_apply, release each captured argument at each one of
// the final release locations of the partial apply.
if (auto *PAI = dyn_cast<PartialApplyInst>(Closure)) {
// If we can not decrement the ref counts of the dead partial apply for any
// reason, bail.
if (!releaseCapturedArgsOfDeadPartialApply(PAI, Tracker, Callbacks))
return false;
}
// Then delete all user instructions.
for (auto *User : Tracker.getTrackedUsers()) {
assert(User->getResults().empty()
&& "We expect only ARC operations without "
"results. This is true b/c of "
"isARCOperationRemovableIfObjectIsDead");
Callbacks.DeleteInst(User);
}
// Finally delete the closure.
Callbacks.DeleteInst(Closure);
return true;
}
//===----------------------------------------------------------------------===//
// Value Lifetime
//===----------------------------------------------------------------------===//
void ValueLifetimeAnalysis::propagateLiveness() {
assert(LiveBlocks.empty() && "frontier computed twice");
auto DefBB = DefValue->getParentBlock();
llvm::SmallVector<SILBasicBlock *, 64> Worklist;
int NumUsersBeforeDef = 0;
// Find the initial set of blocks where the value is live, because
// it is used in those blocks.
for (SILInstruction *User : UserSet) {
SILBasicBlock *UserBlock = User->getParent();
if (LiveBlocks.insert(UserBlock))
Worklist.push_back(UserBlock);
// A user in the DefBB could potentially be located before the DefValue.
if (UserBlock == DefBB)
NumUsersBeforeDef++;
}
// Don't count any users in the DefBB which are actually located _after_
// the DefValue.
auto InstIter = DefValue->getIterator();
while (NumUsersBeforeDef > 0 && ++InstIter != DefBB->end()) {
if (UserSet.count(&*InstIter))
NumUsersBeforeDef--;
}
// Now propagate liveness backwards until we hit the block that defines the
// value.
while (!Worklist.empty()) {
auto *BB = Worklist.pop_back_val();
// Don't go beyond the definition.
if (BB == DefBB && NumUsersBeforeDef == 0)
continue;
for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) {
// If it's already in the set, then we've already queued and/or
// processed the predecessors.
if (LiveBlocks.insert(Pred))
Worklist.push_back(Pred);
}
}
}
SILInstruction *ValueLifetimeAnalysis:: findLastUserInBlock(SILBasicBlock *BB) {
// Walk backwards in BB looking for last use of the value.
for (auto II = BB->rbegin(); II != BB->rend(); ++II) {
assert(DefValue != &*II && "Found def before finding use!");
if (UserSet.count(&*II))
return &*II;
}
llvm_unreachable("Expected to find use of value in block!");
}
bool ValueLifetimeAnalysis::computeFrontier(Frontier &Fr, Mode mode,
DeadEndBlocks *DEBlocks) {
assert(!isAliveAtBeginOfBlock(DefValue->getFunction()->getEntryBlock()) &&
"Can't compute frontier for def which does not dominate all uses");
bool NoCriticalEdges = true;
// Exit-blocks from the lifetime region. The value is live at the end of
// a predecessor block but not in the frontier block itself.
llvm::SmallSetVector<SILBasicBlock *, 16> FrontierBlocks;
// Blocks where the value is live at the end of the block and which have
// a frontier block as successor.
llvm::SmallSetVector<SILBasicBlock *, 16> LiveOutBlocks;
/// The lifetime ends if we have a live block and a not-live successor.
for (SILBasicBlock *BB : LiveBlocks) {
if (DEBlocks && DEBlocks->isDeadEnd(BB))
continue;
bool LiveInSucc = false;
bool DeadInSucc = false;
for (const SILSuccessor &Succ : BB->getSuccessors()) {
if (isAliveAtBeginOfBlock(Succ)) {
LiveInSucc = true;
} else if (!DEBlocks || !DEBlocks->isDeadEnd(Succ)) {
DeadInSucc = true;
}
}
if (!LiveInSucc) {
// The value is not live in any of the successor blocks. This means the
// block contains a last use of the value. The next instruction after
// the last use is part of the frontier.
SILInstruction *LastUser = findLastUserInBlock(BB);
if (!isa<TermInst>(LastUser)) {
Fr.push_back(&*std::next(LastUser->getIterator()));
continue;
}
// In case the last user is a TermInst we add all successor blocks to the
// frontier (see below).
assert(DeadInSucc && "The final using TermInst must have successors");
}
if (DeadInSucc) {
if (mode == UsersMustPostDomDef)
return false;
// The value is not live in some of the successor blocks.
LiveOutBlocks.insert(BB);
for (const SILSuccessor &Succ : BB->getSuccessors()) {
if (!isAliveAtBeginOfBlock(Succ)) {
// It's an "exit" edge from the lifetime region.
FrontierBlocks.insert(Succ);
}
}
}
}
// Handle "exit" edges from the lifetime region.
llvm::SmallPtrSet<SILBasicBlock *, 16> UnhandledFrontierBlocks;
for (SILBasicBlock *FrontierBB: FrontierBlocks) {
assert(mode != UsersMustPostDomDef);
bool needSplit = false;
// If the value is live only in part of the predecessor blocks we have to
// split those predecessor edges.
for (SILBasicBlock *Pred : FrontierBB->getPredecessorBlocks()) {
if (!LiveOutBlocks.count(Pred)) {
needSplit = true;
break;
}
}
if (needSplit) {
if (mode == DontModifyCFG)
return false;
// We need to split the critical edge to create a frontier instruction.
UnhandledFrontierBlocks.insert(FrontierBB);
} else {
// The first instruction of the exit-block is part of the frontier.
Fr.push_back(&*FrontierBB->begin());
}
}
// Split critical edges from the lifetime region to not yet handled frontier
// blocks.
for (SILBasicBlock *FrontierPred : LiveOutBlocks) {
assert(mode != UsersMustPostDomDef);
auto *T = FrontierPred->getTerminator();
// Cache the successor blocks because splitting critical edges invalidates
// the successor list iterator of T.
llvm::SmallVector<SILBasicBlock *, 4> SuccBlocks;
for (const SILSuccessor &Succ : T->getSuccessors())
SuccBlocks.push_back(Succ);
for (unsigned i = 0, e = SuccBlocks.size(); i != e; ++i) {
if (UnhandledFrontierBlocks.count(SuccBlocks[i])) {
assert(mode == AllowToModifyCFG);
assert(isCriticalEdge(T, i) && "actually not a critical edge?");
SILBasicBlock *NewBlock = splitEdge(T, i);
// The single terminator instruction is part of the frontier.
Fr.push_back(&*NewBlock->begin());
NoCriticalEdges = false;
}
}
}
return NoCriticalEdges;
}
bool ValueLifetimeAnalysis::isWithinLifetime(SILInstruction *Inst) {
SILBasicBlock *BB = Inst->getParent();
// Check if the value is not live anywhere in Inst's block.
if (!LiveBlocks.count(BB))
return false;
for (const SILSuccessor &Succ : BB->getSuccessors()) {
// If the value is live at the beginning of any successor block it is also
// live at the end of BB and therefore Inst is definitely in the lifetime
// region (Note that we don't check in upward direction against the value's
// definition).
if (isAliveAtBeginOfBlock(Succ))
return true;
}
// The value is live in the block but not at the end of the block. Check if
// Inst is located before (or at) the last use.
for (auto II = BB->rbegin(); II != BB->rend(); ++II) {
if (UserSet.count(&*II)) {
return true;
}
if (Inst == &*II)
return false;
}
llvm_unreachable("Expected to find use of value in block!");
}
void ValueLifetimeAnalysis::dump() const {
llvm::errs() << "lifetime of def: " << *DefValue;
for (SILInstruction *Use : UserSet) {
llvm::errs() << " use: " << *Use;
}
llvm::errs() << " live blocks:";
for (SILBasicBlock *BB : LiveBlocks) {
llvm::errs() << ' ' << BB->getDebugID();
}
llvm::errs() << '\n';
}
//===----------------------------------------------------------------------===//
// Casts Optimization and Simplification
//===----------------------------------------------------------------------===//
/// Check if is a bridging cast, i.e. one of the sides is
/// a bridged type.
static bool isBridgingCast(CanType SourceType, CanType TargetType) {
// Bridging casts cannot be further simplified.
auto TargetIsBridgeable = TargetType->isBridgeableObjectType();
auto SourceIsBridgeable = SourceType->isBridgeableObjectType();
if (TargetIsBridgeable != SourceIsBridgeable)
return true;
return false;
}
/// If target is a Swift type bridging to an ObjC type,
/// return the ObjC type it bridges to.
/// If target is an ObjC type, return this type.
static Type getCastFromObjC(SILModule &M, CanType source, CanType target) {
return M.getASTContext().getBridgedToObjC(M.getSwiftModule(), target);
}
/// Create a call of _forceBridgeFromObjectiveC_bridgeable or
/// _conditionallyBridgeFromObjectiveC_bridgeable which converts an ObjC
/// instance into a corresponding Swift type, conforming to
/// _ObjectiveCBridgeable.
SILInstruction *
CastOptimizer::
optimizeBridgedObjCToSwiftCast(SILInstruction *Inst,
bool isConditional,
SILValue Src,
SILValue Dest,
CanType Source,
CanType Target,
Type BridgedSourceTy,
Type BridgedTargetTy,
SILBasicBlock *SuccessBB,
SILBasicBlock *FailureBB) {
auto &M = Inst->getModule();
auto Loc = Inst->getLoc();
// The conformance to _BridgedToObjectiveC is statically known.
// Retrieve the bridging operation to be used if a static conformance
// to _BridgedToObjectiveC can be proven.
FuncDecl *BridgeFuncDecl =
isConditional
? M.getASTContext().getConditionallyBridgeFromObjectiveCBridgeable(nullptr)
: M.getASTContext().getForceBridgeFromObjectiveCBridgeable(nullptr);
assert(BridgeFuncDecl && "_forceBridgeFromObjectiveC should exist");
SILDeclRef FuncDeclRef(BridgeFuncDecl, SILDeclRef::Kind::Func);
// Lookup a function from the stdlib.
SILFunction *BridgedFunc = M.getOrCreateFunction(
Loc, FuncDeclRef, ForDefinition_t::NotForDefinition);
if (!BridgedFunc)
return nullptr;
CanType CanBridgedTy = BridgedTargetTy->getCanonicalType();
SILType SILBridgedTy = SILType::getPrimitiveObjectType(CanBridgedTy);
SILBuilderWithScope Builder(Inst);
SILValue SrcOp;
SILInstruction *NewI = nullptr;
assert(Src->getType().isAddress() && "Source should have an address type");
assert(Dest->getType().isAddress() && "Source should have an address type");
// AnyHashable is a special case - it does not conform to NSObject -
// If AnyHashable - Bail out of the optimization
if (auto DT = Target.getNominalOrBoundGenericNominal()) {
if (DT == M.getASTContext().getAnyHashableDecl()) {
return nullptr;
}
}
// If this is a conditional cast:
// We need a new fail BB in order to add a dealloc_stack to it
SILBasicBlock *ConvFailBB = nullptr;
if (isConditional) {
auto CurrInsPoint = Builder.getInsertionPoint();
ConvFailBB = splitBasicBlockAndBranch(Builder, &(*FailureBB->begin()),
nullptr, nullptr);
Builder.setInsertionPoint(CurrInsPoint);
}
if (SILBridgedTy != Src->getType()) {
// Check if we can simplify a cast into:
// - ObjCTy to _ObjectiveCBridgeable._ObjectiveCType.
// - then convert _ObjectiveCBridgeable._ObjectiveCType to
// a Swift type using _forceBridgeFromObjectiveC.
if (!Src->getType().isLoadable(M)) {
// This code path is never reached in current test cases
// If reached, we'd have to convert from an ObjC Any* to a loadable type
// Should use check_addr / make a source we can actually load
return nullptr;
}
// Generate a load for the source argument.
auto *Load =
Builder.createLoad(Loc, Src, LoadOwnershipQualifier::Unqualified);
// Try to convert the source into the expected ObjC type first.
if (Load->getType() == SILBridgedTy) {
// If type of the source and the expected ObjC type are
// equal, there is no need to generate the conversion
// from ObjCTy to _ObjectiveCBridgeable._ObjectiveCType.
if (isConditional) {
SILBasicBlock *CastSuccessBB = Inst->getFunction()->createBasicBlock();
CastSuccessBB->createPHIArgument(SILBridgedTy,
ValueOwnershipKind::Owned);
Builder.createBranch(Loc, CastSuccessBB, SILValue(Load));
Builder.setInsertionPoint(CastSuccessBB);
SrcOp = CastSuccessBB->getArgument(0);
} else {
SrcOp = Load;
}
} else if (isConditional) {
SILBasicBlock *CastSuccessBB = Inst->getFunction()->createBasicBlock();
CastSuccessBB->createPHIArgument(SILBridgedTy, ValueOwnershipKind::Owned);
NewI = Builder.createCheckedCastBranch(Loc, false, Load, SILBridgedTy,
CastSuccessBB, ConvFailBB);
Builder.setInsertionPoint(CastSuccessBB);
SrcOp = CastSuccessBB->getArgument(0);
} else {
auto cast = Builder.createUnconditionalCheckedCast(Loc, Load,
SILBridgedTy);
NewI = cast;
SrcOp = cast;
}
} else {
SrcOp = Src;
}
// Now emit the a cast from the casted ObjC object into a target type.
// This is done by means of calling _forceBridgeFromObjectiveC or
// _conditionallyBridgeFromObjectiveC_bridgeable from the Target type.
// Lookup the required function in the Target type.
// Lookup the _ObjectiveCBridgeable protocol.
auto BridgedProto =
M.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
auto Conf =
*M.getSwiftModule()->lookupConformance(Target, BridgedProto);
auto ParamTypes = BridgedFunc->getLoweredFunctionType()->getParameters();
auto *FuncRef = Builder.createFunctionRef(Loc, BridgedFunc);
auto MetaTy = MetatypeType::get(Target, MetatypeRepresentation::Thick);
auto SILMetaTy = M.Types.getTypeLowering(MetaTy).getLoweredType();
auto *MetaTyVal = Builder.createMetatype(Loc, SILMetaTy);
SmallVector<SILValue, 1> Args;
// Add substitutions
auto SubMap = SubstitutionMap::getProtocolSubstitutions(
Conf.getRequirement(), Target, Conf);
auto SILFnTy = FuncRef->getType();
SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap);
SILFunctionConventions substConv(SubstFnTy.castTo<SILFunctionType>(), M);
// Temporary to hold the intermediate result.
AllocStackInst *Tmp = nullptr;
CanType OptionalTy;
OptionalTypeKind OTK;
SILValue InOutOptionalParam;
if (isConditional) {
// Create a temporary
OptionalTy = OptionalType::get(Dest->getType().getSwiftRValueType())
->getImplementationType()
->getCanonicalType();
OptionalTy.getAnyOptionalObjectType(OTK);
Tmp = Builder.createAllocStack(Loc,
SILType::getPrimitiveObjectType(OptionalTy));
InOutOptionalParam = Tmp;
} else {
InOutOptionalParam = Dest;
}
(void) ParamTypes;
assert(ParamTypes[0].getConvention() == ParameterConvention::Direct_Owned &&
"Parameter should be @owned");
// Emit a retain.
Builder.createRetainValue(Loc, SrcOp, Builder.getDefaultAtomicity());
Args.push_back(InOutOptionalParam);
Args.push_back(SrcOp);
Args.push_back(MetaTyVal);
SmallVector<Substitution, 4> Subs;
Conf.getRequirement()->getGenericSignature()->getSubstitutions(SubMap, Subs);
auto *AI = Builder.createApply(Loc, FuncRef, Subs, Args, false);
// If the source of a cast should be destroyed, emit a release.
if (isa<UnconditionalCheckedCastAddrInst>(Inst)) {
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
}
if (auto *CCABI = dyn_cast<CheckedCastAddrBranchInst>(Inst)) {
if (CCABI->getConsumptionKind() == CastConsumptionKind::TakeAlways) {
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
} else if (CCABI->getConsumptionKind() ==
CastConsumptionKind::TakeOnSuccess) {
// Insert a release in the success BB.
Builder.setInsertionPoint(SuccessBB->begin());
Builder.createReleaseValue(Loc, SrcOp, Builder.getDefaultAtomicity());
}
}
// Results should be checked in case we process a conditional
// case. E.g. casts from NSArray into [SwiftType] may fail, i.e. return .None.
if (isConditional) {
// Copy the temporary into Dest.
// Load from the optional.
auto *SomeDecl = Builder.getASTContext().getOptionalSomeDecl(OTK);
SILBasicBlock *ConvSuccessBB = Inst->getFunction()->createBasicBlock();
SmallVector<std::pair<EnumElementDecl *, SILBasicBlock*>, 1> CaseBBs;
CaseBBs.push_back(std::make_pair(M.getASTContext().getOptionalNoneDecl(), FailureBB));
Builder.createSwitchEnumAddr(Loc, InOutOptionalParam, ConvSuccessBB, CaseBBs);
Builder.setInsertionPoint(FailureBB->begin());
Builder.createDeallocStack(Loc, Tmp);
Builder.setInsertionPoint(ConvSuccessBB);
auto Addr = Builder.createUncheckedTakeEnumDataAddr(Loc, InOutOptionalParam,
SomeDecl);
Builder.createCopyAddr(Loc, Addr, Dest, IsTake, IsInitialization);
Builder.createDeallocStack(Loc, Tmp);
SmallVector<SILValue, 1> SuccessBBArgs;
Builder.createBranch(Loc, SuccessBB, SuccessBBArgs);
}
EraseInstAction(Inst);
return (NewI) ? NewI : AI;
}
/// Create a call of _bridgeToObjectiveC which converts an _ObjectiveCBridgeable
/// instance into a bridged ObjC type.
SILInstruction *
CastOptimizer::
optimizeBridgedSwiftToObjCCast(SILInstruction *Inst,
CastConsumptionKind ConsumptionKind,
bool isConditional,
SILValue Src,
SILValue Dest,
CanType Source,
CanType Target,
Type BridgedSourceTy,
Type BridgedTargetTy,
SILBasicBlock *SuccessBB,
SILBasicBlock *FailureBB) {
auto &M = Inst->getModule();
auto Loc = Inst->getLoc();
bool AddressOnlyType = false;
if (!Src->getType().isLoadable(M) || !Dest->getType().isLoadable(M)) {
AddressOnlyType = true;
}
// Find the _BridgedToObjectiveC protocol.
auto BridgedProto =
M.getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);
auto Conf =
M.getSwiftModule()->lookupConformance(Source, BridgedProto);
assert(Conf && "_ObjectiveCBridgeable conformance should exist");
(void) Conf;
// Generate code to invoke _bridgeToObjectiveC
SILBuilderWithScope Builder(Inst);
auto *NTD = Source.getNominalOrBoundGenericNominal();
assert(NTD);
SmallVector<ValueDecl *, 4> FoundMembers;
ArrayRef<ValueDecl *> Members;
Members = NTD->lookupDirect(M.getASTContext().Id_bridgeToObjectiveC);
if (Members.empty()) {
if (NTD->getDeclContext()->lookupQualified(
NTD->getDeclaredType(), M.getASTContext().Id_bridgeToObjectiveC,
NLOptions::NL_ProtocolMembers, nullptr, FoundMembers)) {
Members = FoundMembers;
// Returned members are starting with the most specialized ones.
// Thus, the first element is what we are looking for.
Members = Members.take_front(1);
}
}
// There should be exactly one implementation of _bridgeToObjectiveC.
if (Members.size() != 1)
return nullptr;
auto BridgeFuncDecl = Members.front();
auto BridgeFuncDeclRef = SILDeclRef(BridgeFuncDecl);
ModuleDecl *Mod = M.getASTContext().getLoadedModule(
M.getASTContext().Id_Foundation);
if (!Mod)
return nullptr;
SmallVector<ValueDecl *, 2> Results;
Mod->lookupMember(Results, Source.getNominalOrBoundGenericNominal(),
M.getASTContext().Id_bridgeToObjectiveC, Identifier());
ArrayRef<ValueDecl *> ResultsRef(Results);
if (ResultsRef.empty()) {
M.getSwiftModule()->lookupMember(Results, Source.getNominalOrBoundGenericNominal(),
M.getASTContext().Id_bridgeToObjectiveC, Identifier());
ResultsRef = Results;
}
if (ResultsRef.size() != 1)
return nullptr;
auto MemberDeclRef = SILDeclRef(Results.front());
auto *BridgedFunc = M.getOrCreateFunction(Loc, MemberDeclRef,
ForDefinition_t::NotForDefinition);
// Implementation of _bridgeToObjectiveC could not be found.
if (!BridgedFunc)
return nullptr;
if (Inst->getFunction()->isSerialized() &&
!BridgedFunc->hasValidLinkageForFragileRef())
return nullptr;
auto ParamTypes = BridgedFunc->getLoweredFunctionType()->getParameters();
auto SILFnTy = SILType::getPrimitiveObjectType(
M.Types.getConstantFunctionType(BridgeFuncDeclRef));
// TODO: Handle return from witness function.
if (BridgedFunc->getLoweredFunctionType()
->getSingleResult()
.isFormalIndirect())
return nullptr;
// Get substitutions, if source is a bound generic type.
auto SubMap =
Source->getContextSubstitutionMap(M.getSwiftModule(),
BridgeFuncDecl->getDeclContext());
SILType SubstFnTy = SILFnTy.substGenericArgs(M, SubMap);
SILFunctionConventions substConv(SubstFnTy.castTo<SILFunctionType>(), M);
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 needReleaseInSucc = false;
switch (ParamTypes[0].getConvention()) {
case ParameterConvention::Direct_Guaranteed:
assert(!AddressOnlyType &&
"AddressOnlyType with Direct_Guaranteed is not supported");
switch (ConsumptionKind) {
case CastConsumptionKind::TakeAlways:
needReleaseAfterCall = true;
break;
case CastConsumptionKind::TakeOnSuccess:
needReleaseInSucc = 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:
// The Direct_Owned case is only handled for completeness. Currently this
// cannot appear, because the _bridgeToObjectiveC protocol witness method
// always receives the this pointer (= the source) as guaranteed.
assert(!AddressOnlyType &&
"AddressOnlyType with Direct_Owned is not supported");
switch (ConsumptionKind) {
case CastConsumptionKind::TakeAlways:
break;
case CastConsumptionKind::TakeOnSuccess:
needRetainBeforeCall = true;
needReleaseInSucc = true;
break;
case CastConsumptionKind::CopyOnSuccess:
needRetainBeforeCall = true;
break;
}
break;
case ParameterConvention::Direct_Unowned:
assert(!AddressOnlyType &&
"AddressOnlyType with Direct_Unowned is not supported");
break;
case ParameterConvention::Indirect_In_Guaranteed:
// Source as-is, we don't need to copy it due to guarantee
break;
case ParameterConvention::Indirect_In_Constant:
case ParameterConvention::Indirect_In: {
assert(substConv.isSILIndirect(ParamTypes[0])
&& "unsupported convention for bridging conversion");
// Need to make a copy of the source, can be changed in ObjC
auto BridgeStack = Builder.createAllocStack(Loc, Src->getType());
Builder.createCopyAddr(Loc, Src, BridgeStack, IsNotTake,
IsInitialization);
break;
}
case ParameterConvention::Indirect_Inout:
case ParameterConvention::Indirect_InoutAliasable:
// TODO handle remaining indirect argument types
return nullptr;
}
if (needRetainBeforeCall)
Builder.createRetainValue(Loc, Src, Builder.getDefaultAtomicity());
SmallVector<Substitution, 4> Subs;
if (auto *Sig = Source->getAnyNominal()->getGenericSignature())
Sig->getSubstitutions(SubMap, Subs);
// Generate a code to invoke the bridging function.
auto *NewAI = Builder.createApply(Loc, FnRef, Subs, Src, false);
if (needReleaseAfterCall) {
Builder.createReleaseValue(Loc, Src, Builder.getDefaultAtomicity());
} else if (needReleaseInSucc) {
SILBuilder SuccBuilder(SuccessBB->begin());
SuccBuilder.createReleaseValue(Loc, Src, SuccBuilder.getDefaultAtomicity());
}
SILInstruction *NewI = NewAI;
if (Dest) {
// If it is addr cast then store the result.
auto ConvTy = NewAI->getType();
auto DestTy = Dest->getType().getObjectType();
SILValue CastedValue;
if (ConvTy == DestTy) {
CastedValue = NewAI;
} else if (DestTy.isExactSuperclassOf(ConvTy)) {
CastedValue = Builder.createUpcast(Loc, NewAI, DestTy);
} else if (ConvTy.isExactSuperclassOf(DestTy)) {
// The downcast from a base class to derived class may fail.
if (isConditional) {
// In case of a conditional cast, we should handle it gracefully.
auto CondBrSuccessBB =
NewAI->getFunction()->createBasicBlock(NewAI->getParent());
CondBrSuccessBB->createPHIArgument(DestTy, ValueOwnershipKind::Owned,
nullptr);
Builder.createCheckedCastBranch(Loc, /* isExact*/ false, NewAI, DestTy,
CondBrSuccessBB, FailureBB);
Builder.setInsertionPoint(CondBrSuccessBB, CondBrSuccessBB->begin());
CastedValue = CondBrSuccessBB->getArgument(0);
} else {
CastedValue = SILValue(
Builder.createUnconditionalCheckedCast(Loc, NewAI, DestTy));
}
} else if (ConvTy.getSwiftRValueType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
DestTy.getSwiftRValueType()) ||
DestTy.getSwiftRValueType() ==
getNSBridgedClassOfCFClass(M.getSwiftModule(),
ConvTy.getSwiftRValueType())) {
// Handle NS <-> CF toll-free bridging here.
CastedValue =
SILValue(Builder.createUncheckedRefCast(Loc, NewAI, DestTy));
} else {
llvm_unreachable(
"Destination should have the same type, be bridgeable CF "
"type or be a superclass/subclass of the source operand");
}
NewI = Builder.createStore(Loc, CastedValue, Dest,
StoreOwnershipQualifier::Unqualified);
if (isConditional && NewI->getParent() != NewAI->getParent()) {
Builder.createBranch(Loc, SuccessBB);
}
}
if (Dest) {
EraseInstAction(Inst);
}
return NewI;
}
/// Make use of the fact that some of these casts cannot fail.
/// For example, if the ObjC type is exactly the expected
/// _ObjectiveCType type, then it would always succeed for
/// NSString, NSNumber, etc.
/// Casts from NSArray, NSDictionary and NSSet may fail.
///
/// If ObjC class is not exactly _ObjectiveCType, then
/// its conversion to a required _ObjectiveCType may fail.
SILInstruction *
CastOptimizer::
optimizeBridgedCasts(SILInstruction *Inst,
CastConsumptionKind ConsumptionKind,
bool isConditional,
SILValue Src,
SILValue Dest,
CanType source,
CanType target,
SILBasicBlock *SuccessBB,
SILBasicBlock *FailureBB) {
auto &M = Inst->getModule();
// To apply the bridged optimizations, we should ensure that types are not
// existential (and keep in mind that generic parameters can be existentials),
// and that one of the types is a class and another one is a struct.
if (source.isAnyExistentialType() ||
target.isAnyExistentialType() ||
source->is<ArchetypeType>() ||
target->is<ArchetypeType>() ||
(source.getClassOrBoundGenericClass() &&
!target.getStructOrBoundGenericStruct()) ||
(target.getClassOrBoundGenericClass() &&
!source.getStructOrBoundGenericStruct()))
return nullptr;
// Casts involving non-bound generic types cannot be optimized.
if (source->hasArchetype() || target->hasArchetype())
return nullptr;
auto BridgedTargetTy = getCastFromObjC(M, source, target);
if (!BridgedTargetTy)
return nullptr;
auto BridgedSourceTy = getCastFromObjC(M, target, source);
if (!BridgedSourceTy)
return nullptr;
CanType CanBridgedTargetTy = BridgedTargetTy->getCanonicalType();
CanType CanBridgedSourceTy = BridgedSourceTy->getCanonicalType();
if (CanBridgedSourceTy == source && CanBridgedTargetTy == target) {
// Both source and target type are ObjC types.
return nullptr;
}
if (CanBridgedSourceTy != source && CanBridgedTargetTy != target) {
// Both source and target type are Swift types.
return nullptr;
}
if ((CanBridgedSourceTy &&
CanBridgedSourceTy->getAnyNominal() ==
M.getASTContext().getNSErrorDecl()) ||
(CanBridgedTargetTy &&
CanBridgedSourceTy->getAnyNominal() ==
M.getASTContext().getNSErrorDecl())) {
// FIXME: Can't optimize bridging with NSError.
return nullptr;
}
if (CanBridgedSourceTy || CanBridgedTargetTy) {
// Check what kind of conversion it is? ObjC->Swift or Swift-ObjC?
if (CanBridgedTargetTy != target) {
// This is an ObjC to Swift cast.
return optimizeBridgedObjCToSwiftCast(Inst, isConditional, Src, Dest, source,
target, BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB);
} else {
// This is a Swift to ObjC cast
return optimizeBridgedSwiftToObjCCast(Inst, ConsumptionKind,
isConditional, Src, Dest, source,
target, BridgedSourceTy, BridgedTargetTy, SuccessBB, FailureBB);
}
}
llvm_unreachable("Unknown kind of bridging");
}
SILInstruction *
CastOptimizer::
simplifyCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *Inst) {
if (auto *I = optimizeCheckedCastAddrBranchInst(Inst))
Inst = dyn_cast<CheckedCastAddrBranchInst>(I);
if (!Inst)
return nullptr;
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto &Mod = Inst->getModule();
SILBuilderWithScope Builder(Inst);
// Try to determine the outcome of the cast from a known type
// to a protocol type at compile-time.
bool isSourceTypeExact = isa<MetatypeInst>(Inst->getSrc());
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(),
SourceType,
TargetType,
isSourceTypeExact,
Mod.isWholeModule());
if (Feasibility == DynamicCastFeasibility::WillFail) {
if (shouldDestroyOnFailure(Inst->getConsumptionKind())) {
auto &srcTL = Builder.getModule().getTypeLowering(Src->getType());
srcTL.emitDestroyAddress(Builder, Loc, Src);
}
auto NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
bool ResultNotUsed = isa<AllocStackInst>(Dest);
if (ResultNotUsed) {
for (auto Use : Dest->getUses()) {
auto *User = Use->getUser();
if (isa<DeallocStackInst>(User) || User == Inst)
continue;
ResultNotUsed = false;
break;
}
}
auto *BB = Inst->getParent();
SILInstruction *BridgedI = nullptr;
// To apply the bridged optimizations, we should
// ensure that types are not existential,
// and that not both types are classes.
BridgedI = optimizeBridgedCasts(
Inst, Inst->getConsumptionKind(),
/* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed,
Src, Dest, SourceType, TargetType, SuccessBB, FailureBB);
if (!BridgedI) {
// If the cast may succeed or fail, and it can't be optimized into a
// bridging operation, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
// Replace by unconditional_addr_cast, followed by a branch.
// The unconditional_addr_cast can be skipped, if the result of a cast
// is not used afterwards.
if (ResultNotUsed) {
if (shouldTakeOnSuccess(Inst->getConsumptionKind())) {
auto &srcTL = Builder.getModule().getTypeLowering(Src->getType());
srcTL.emitDestroyAddress(Builder, Loc, Src);
}
EraseInstAction(Inst);
Builder.setInsertionPoint(BB);
auto *NewI = Builder.createBranch(Loc, SuccessBB);
WillSucceedAction();
return NewI;
}
// Since it is an addr cast, only address types are handled here.
if (!Src->getType().isAddress() || !Dest->getType().isAddress()) {
return nullptr;
}
// For CopyOnSuccess casts, we could insert an explicit copy here, but this
// case does not happen in practice.
// Both TakeOnSuccess and TakeAlways can be reduced to an
// UnconditionalCheckedCast, since the failure path is irrelevant.
if (Inst->getConsumptionKind() == CastConsumptionKind::CopyOnSuccess)
return nullptr;
if (!emitSuccessfulIndirectUnconditionalCast(
Builder, Mod.getSwiftModule(), Loc, Src, SourceType, Dest,
TargetType, Inst)) {
// No optimization was possible.
return nullptr;
}
EraseInstAction(Inst);
}
SILInstruction *NewI = &BB->back();
if (!isa<TermInst>(NewI)) {
Builder.setInsertionPoint(BB);
NewI = Builder.createBranch(Loc, SuccessBB);
}
WillSucceedAction();
return NewI;
}
SILInstruction *
CastOptimizer::simplifyCheckedCastBranchInst(CheckedCastBranchInst *Inst) {
if (Inst->isExact()) {
auto *ARI = dyn_cast<AllocRefInst>(stripUpCasts(Inst->getOperand()));
if (!ARI)
return nullptr;
// We know the dynamic type of the operand.
SILBuilderWithScope Builder(Inst);
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
if (ARI->getType() == Inst->getCastType()) {
// This exact cast will succeed.
SmallVector<SILValue, 1> Args;
Args.push_back(ARI);
auto *NewI = Builder.createBranch(Loc, SuccessBB, Args);
EraseInstAction(Inst);
WillSucceedAction();
return NewI;
}
// This exact cast will fail.
auto *NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
if (auto *I = optimizeCheckedCastBranchInst(Inst))
Inst = dyn_cast<CheckedCastBranchInst>(I);
if (!Inst)
return nullptr;
auto LoweredTargetType = Inst->getCastType();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto Op = Inst->getOperand();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Op);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(),
SourceType,
TargetType,
isSourceTypeExact);
SILBuilderWithScope Builder(Inst);
if (Feasibility == DynamicCastFeasibility::WillFail) {
auto *NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
SILValue CastedValue;
if (Op->getType() != LoweredTargetType) {
auto Src = Inst->getOperand();
auto Dest = SILValue();
// Apply the bridged cast optimizations.
// TODO: Bridged casts cannot be expressed by checked_cast_br yet.
// Should we ever support it, please review this code.
auto BridgedI = optimizeBridgedCasts(
Inst, CastConsumptionKind::CopyOnSuccess,
/* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed,
Src, Dest, SourceType, TargetType, nullptr, nullptr);
if (BridgedI) {
llvm_unreachable(
"Bridged casts cannot be expressed by checked_cast_br yet");
} else {
// If the cast may succeed or fail and can't be turned into a bridging
// call, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
// Replace by unconditional_cast, followed by a branch.
// The unconditional_cast can be skipped, if the result of a cast
// is not used afterwards.
if (!ResultNotUsed) {
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
return nullptr;
CastedValue = emitSuccessfulScalarUnconditionalCast(
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
SourceType, TargetType, Inst);
} else {
CastedValue = SILUndef::get(LoweredTargetType, Mod);
}
if (!CastedValue)
CastedValue =
Builder.createUnconditionalCheckedCast(Loc, Op, LoweredTargetType);
}
} else {
// No need to cast.
CastedValue = Op;
}
auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue);
EraseInstAction(Inst);
WillSucceedAction();
return NewI;
}
SILInstruction *CastOptimizer::simplifyCheckedCastValueBranchInst(
CheckedCastValueBranchInst *Inst) {
if (auto *I = optimizeCheckedCastValueBranchInst(Inst))
Inst = dyn_cast<CheckedCastValueBranchInst>(I);
if (!Inst)
return nullptr;
auto LoweredTargetType = Inst->getCastType();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto Loc = Inst->getLoc();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
auto Op = Inst->getOperand();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Op);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
TargetType, isSourceTypeExact);
SILBuilderWithScope Builder(Inst);
if (Feasibility == DynamicCastFeasibility::WillFail) {
auto *NewI = Builder.createBranch(Loc, FailureBB);
EraseInstAction(Inst);
WillFailAction();
return NewI;
}
// Casting will succeed.
bool ResultNotUsed = SuccessBB->getArgument(0)->use_empty();
SILValue CastedValue;
if (Op->getType() != LoweredTargetType) {
auto Src = Inst->getOperand();
auto Dest = SILValue();
// Apply the bridged cast optimizations.
// TODO: Bridged casts cannot be expressed by checked_cast_value_br yet.
// Once the support for opaque values has landed, please review this
// code.
auto BridgedI = optimizeBridgedCasts(
Inst, CastConsumptionKind::CopyOnSuccess,
/* isConditional */ Feasibility == DynamicCastFeasibility::MaySucceed,
Src, Dest, SourceType, TargetType, nullptr, nullptr);
if (BridgedI) {
llvm_unreachable(
"Bridged casts cannot be expressed by checked_cast_value_br yet");
} else {
// If the cast may succeed or fail and can't be turned into a bridging
// call, then let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
// Replace by unconditional_cast, followed by a branch.
// The unconditional_cast can be skipped, if the result of a cast
// is not used afterwards.
if (!canUseScalarCheckedCastInstructions(Mod, SourceType, TargetType))
return nullptr;
if (!ResultNotUsed) {
CastedValue = emitSuccessfulScalarUnconditionalCast(
Builder, Mod.getSwiftModule(), Loc, Op, LoweredTargetType,
SourceType, TargetType, Inst);
} else {
CastedValue = SILUndef::get(LoweredTargetType, Mod);
}
}
if (!CastedValue)
CastedValue = Builder.createUnconditionalCheckedCastValue(
Loc, Op, LoweredTargetType);
} else {
// No need to cast.
CastedValue = Op;
}
auto *NewI = Builder.createBranch(Loc, SuccessBB, CastedValue);
EraseInstAction(Inst);
WillSucceedAction();
return NewI;
}
SILInstruction *CastOptimizer::optimizeCheckedCastAddrBranchInst(
CheckedCastAddrBranchInst *Inst) {
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto *SuccessBB = Inst->getSuccessBB();
auto *FailureBB = Inst->getFailureBB();
// If there is an unbound generic type involved in the cast, bail.
if (Src->getType().hasArchetype() || Dest->getType().hasArchetype())
return nullptr;
// %1 = metatype $A.Type
// [%2 = init_existential_metatype %1 ...]
// %3 = alloc_stack
// store %1 to %3 or store %2 to %3
// checked_cast_addr_br %3 to ...
// ->
// %1 = metatype $A.Type
// %c = checked_cast_br %1 to ...
// store %c to %3 (if successful)
if (auto *ASI = dyn_cast<AllocStackInst>(Src)) {
// Check if the value of this alloc_stack is set only once by a store
// instruction, used only by CCABI and then deallocated.
bool isLegal = true;
StoreInst *Store = nullptr;
for (auto Use : ASI->getUses()) {
auto *User = Use->getUser();
if (isa<DeallocStackInst>(User) || User == Inst)
continue;
if (auto *SI = dyn_cast<StoreInst>(User)) {
if (!Store) {
Store = SI;
continue;
}
}
isLegal = false;
break;
}
if (isLegal && Store) {
// Check what was the value stored in the allocated stack slot.
auto Src = Store->getSrc();
MetatypeInst *MI = nullptr;
if (auto *IEMI = dyn_cast<InitExistentialMetatypeInst>(Src)) {
MI = dyn_cast<MetatypeInst>(IEMI->getOperand());
}
if (!MI)
MI = dyn_cast<MetatypeInst>(Src);
if (MI) {
if (SuccessBB->getSinglePredecessorBlock() &&
canUseScalarCheckedCastInstructions(
Inst->getModule(), MI->getType().getSwiftRValueType(),
Inst->getTargetType())) {
SILBuilderWithScope B(Inst);
auto NewI = B.createCheckedCastBranch(
Loc, false /*isExact*/, MI,
Dest->getType().getObjectType(), SuccessBB, FailureBB);
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);
EraseInstAction(Inst);
return NewI;
}
}
if (auto *EMI = dyn_cast<ExistentialMetatypeInst>(Op)) {
// Operand of the existential_metatype instruction.
auto Op = EMI->getOperand();
auto EmiTy = EMI->getType();
// %0 = alloc_stack $T
// %1 = init_existential_addr %0: $*T, $A
// %2 = existential_metatype $T.Type, %0: $*T
// checked_cast_br %2 to ...
// ->
// %1 = metatype $A.Type
// checked_cast_br %1 to ...
if (auto *ASI = dyn_cast<AllocStackInst>(Op)) {
// Should be in the same BB.
if (ASI->getParent() != EMI->getParent())
return nullptr;
// Check if this alloc_stack is only initialized once by means of
// single init_existential_addr.
bool isLegal = true;
// init_existential instruction used to initialize this alloc_stack.
InitExistentialAddrInst *FoundIEI = nullptr;
for (auto Use: getNonDebugUses(ASI)) {
auto *User = Use->getUser();
if (isa<ExistentialMetatypeInst>(User) ||
isa<DestroyAddrInst>(User) ||
isa<DeallocStackInst>(User))
continue;
if (auto *IEI = dyn_cast<InitExistentialAddrInst>(User)) {
if (!FoundIEI) {
FoundIEI = IEI;
continue;
}
}
isLegal = false;
break;
}
if (isLegal && FoundIEI) {
// Should be in the same BB.
if (FoundIEI->getParent() != EMI->getParent())
return nullptr;
// Get the type used to initialize the existential.
auto LoweredConcreteTy = FoundIEI->getLoweredConcreteType();
// We don't know enough at compile time about existential
// and generic type parameters.
if (LoweredConcreteTy.isAnyExistentialType() ||
LoweredConcreteTy.is<ArchetypeType>())
return nullptr;
// Get the metatype of this type.
auto EMT = EmiTy.castTo<AnyMetatypeType>();
auto *MetaTy = MetatypeType::get(LoweredConcreteTy.getSwiftRValueType(),
EMT->getRepresentation());
auto CanMetaTy = CanTypeWrapper<MetatypeType>(MetaTy);
auto SILMetaTy = SILType::getPrimitiveObjectType(CanMetaTy);
SILBuilderWithScope B(Inst);
B.getOpenedArchetypes().addOpenedArchetypeOperands(
FoundIEI->getTypeDependentOperands());
auto *MI = B.createMetatype(FoundIEI->getLoc(), SILMetaTy);
auto *NewI = B.createCheckedCastBranch(Loc, /* isExact */ false, MI,
LoweredTargetType,
SuccessBB,
FailureBB);
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);
EraseInstAction(Inst);
return NewI;
}
}
}
return nullptr;
}
ValueBase *
CastOptimizer::
optimizeUnconditionalCheckedCastInst(UnconditionalCheckedCastInst *Inst) {
auto LoweredSourceType = Inst->getOperand()->getType();
auto LoweredTargetType = Inst->getType();
auto Loc = Inst->getLoc();
auto Op = Inst->getOperand();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Op);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(),
Inst->getSourceType(),
Inst->getTargetType(),
isSourceTypeExact);
if (Feasibility == DynamicCastFeasibility::WillFail) {
// Remove the cast and insert a trap, followed by an
// unreachable instruction.
SILBuilderWithScope Builder(Inst);
auto *Trap = Builder.createBuiltinTrap(Loc);
Inst->replaceAllUsesWithUndef();
EraseInstAction(Inst);
Builder.setInsertionPoint(std::next(SILBasicBlock::iterator(Trap)));
auto *UnreachableInst =
Builder.createUnreachable(ArtificialUnreachableLocation());
// Delete everything after the unreachable except for dealloc_stack which we
// move before the trap.
deleteInstructionsAfterUnreachable(UnreachableInst, Trap);
WillFailAction();
return Trap;
}
if (Feasibility == DynamicCastFeasibility::WillSucceed) {
if (Inst->use_empty()) {
EraseInstAction(Inst);
WillSucceedAction();
return nullptr;
}
}
SILBuilderWithScope Builder(Inst);
// Try to apply the bridged casts optimizations
auto SourceType = LoweredSourceType.getSwiftRValueType();
auto TargetType = LoweredTargetType.getSwiftRValueType();
auto Src = Inst->getOperand();
auto NewI = optimizeBridgedCasts(Inst, CastConsumptionKind::CopyOnSuccess,
false, Src, SILValue(), SourceType,
TargetType, nullptr, nullptr);
if (NewI) {
// FIXME: I'm not sure why this is true!
auto newValue = cast<SingleValueInstruction>(NewI);
ReplaceInstUsesAction(Inst, newValue);
EraseInstAction(Inst);
WillSucceedAction();
return newValue;
}
// If the cast may succeed or fail and can't be optimized into a bridging
// call, let it be.
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
return nullptr;
}
assert(Feasibility == DynamicCastFeasibility::WillSucceed);
if (isBridgingCast(SourceType, TargetType))
return nullptr;
auto Result = emitSuccessfulScalarUnconditionalCast(Builder,
Mod.getSwiftModule(), Loc, Op,
LoweredTargetType,
LoweredSourceType.getSwiftRValueType(),
LoweredTargetType.getSwiftRValueType(),
Inst);
if (!Result) {
// No optimization was possible.
return nullptr;
}
ReplaceInstUsesAction(Inst, Result);
EraseInstAction(Inst);
WillSucceedAction();
return Result;
}
/// Deletes all instructions after \p UnreachableInst except dealloc_stack
/// instructions are moved before \p TrapInst.
void CastOptimizer::deleteInstructionsAfterUnreachable(
SILInstruction *UnreachableInst, SILInstruction *TrapInst) {
auto UnreachableInstIt =
std::next(SILBasicBlock::iterator(UnreachableInst));
auto *Block = TrapInst->getParent();
while (UnreachableInstIt != Block->end()) {
SILInstruction *CurInst = &*UnreachableInstIt;
++UnreachableInstIt;
if (auto *DeallocStack = dyn_cast<DeallocStackInst>(CurInst))
if (!isa<SILUndef>(DeallocStack->getOperand())) {
DeallocStack->moveBefore(TrapInst);
continue;
}
CurInst->replaceAllUsesOfAllResultsWithUndef();
EraseInstAction(CurInst);
}
}
/// TODO: Move to emitSuccessfulIndirectUnconditionalCast?
///
/// Peephole to avoid runtime calls:
/// unconditional_checked_cast_addr T in %0 : $*T to P in %1 : $*P
/// ->
/// %addr = init_existential_addr %1 : $*P, T
/// copy_addr %0 to %addr
///
/// where T is a type statically known to conform to P.
///
/// In caase P is a class existential type, it generates:
/// %val = load %0 : $*T
/// %existential = init_existential_ref %val : $T, $T, P
/// store %existential to %1 : $*P
///
/// Returns true if the optimization was possible and false otherwise.
static bool optimizeStaticallyKnownProtocolConformance(
UnconditionalCheckedCastAddrInst *Inst) {
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto &Mod = Inst->getModule();
if (TargetType->isAnyExistentialType() &&
!SourceType->isAnyExistentialType()) {
auto &Ctx = Mod.getASTContext();
auto *SM = Mod.getSwiftModule();
auto Proto = dyn_cast<ProtocolDecl>(TargetType->getAnyNominal());
if (Proto) {
auto Conformance = SM->lookupConformance(SourceType, Proto);
if (Conformance.hasValue()) {
// SourceType is a non-existential type conforming to a
// protocol represented by the TargetType.
SILBuilder B(Inst);
SmallVector<ProtocolConformanceRef, 1> NewConformances;
NewConformances.push_back(Conformance.getValue());
ArrayRef<ProtocolConformanceRef> Conformances =
Ctx.AllocateCopy(NewConformances);
auto ExistentialRepr =
Dest->getType().getPreferredExistentialRepresentation(Mod,
SourceType);
switch (ExistentialRepr) {
default:
return false;
case ExistentialRepresentation::Opaque: {
auto ExistentialAddr = B.createInitExistentialAddr(
Loc, Dest, SourceType, Src->getType().getObjectType(),
Conformances);
B.createCopyAddr(
Loc, Src, ExistentialAddr,
IsTake_t::IsTake,
IsInitialization_t::IsInitialization);
break;
}
case ExistentialRepresentation::Class: {
auto Value = B.createLoad(Loc, Src,
swift::LoadOwnershipQualifier::Unqualified);
auto Existential =
B.createInitExistentialRef(Loc, Dest->getType().getObjectType(),
SourceType, Value, Conformances);
B.createStore(Loc, Existential, Dest,
swift::StoreOwnershipQualifier::Unqualified);
break;
}
case ExistentialRepresentation::Boxed: {
auto AllocBox = B.createAllocExistentialBox(Loc, Dest->getType(),
SourceType, Conformances);
auto Projection =
B.createProjectExistentialBox(Loc, Src->getType(), AllocBox);
auto Value = B.createLoad(Loc, Src,
swift::LoadOwnershipQualifier::Unqualified);
B.createStore(Loc, Value, Projection,
swift::StoreOwnershipQualifier::Unqualified);
B.createStore(Loc, AllocBox, Dest,
swift::StoreOwnershipQualifier::Unqualified);
break;
}
};
return true;
}
}
}
return false;
}
SILInstruction *
CastOptimizer::
optimizeUnconditionalCheckedCastAddrInst(UnconditionalCheckedCastAddrInst *Inst) {
auto Loc = Inst->getLoc();
auto Src = Inst->getSrc();
auto Dest = Inst->getDest();
auto SourceType = Inst->getSourceType();
auto TargetType = Inst->getTargetType();
auto &Mod = Inst->getModule();
bool isSourceTypeExact = isa<MetatypeInst>(Src);
// Check if we can statically predict the outcome of the cast.
auto Feasibility = classifyDynamicCast(Mod.getSwiftModule(), SourceType,
TargetType, isSourceTypeExact);
if (Feasibility == DynamicCastFeasibility::MaySucceed) {
// Forced bridged casts can be still simplified here.
// If they fail, they fail inside the conversion function.
if (!isBridgingCast(SourceType, TargetType))
return nullptr;
}
if (Feasibility == DynamicCastFeasibility::WillFail) {
// Remove the cast and insert a trap, followed by an
// unreachable instruction.
SILBuilderWithScope Builder(Inst);
// mem2reg's invariants get unhappy if we don't try to
// initialize a loadable result.
auto DestType = Dest->getType();
auto &resultTL = Mod.Types.getTypeLowering(DestType);
if (!resultTL.isAddressOnly()) {
auto undef = SILValue(SILUndef::get(DestType.getObjectType(),
Builder.getModule()));
Builder.createStore(Loc, undef, Dest,
StoreOwnershipQualifier::Unqualified);
}
auto *TrapI = Builder.createBuiltinTrap(Loc);
EraseInstAction(Inst);
Builder.setInsertionPoint(std::next(SILBasicBlock::iterator(TrapI)));
auto *UnreachableInst =
Builder.createUnreachable(ArtificialUnreachableLocation());
// Delete everything after the unreachable except for dealloc_stack which we
// move before the trap.
deleteInstructionsAfterUnreachable(UnreachableInst, TrapI);
WillFailAction();
}
if (Feasibility == DynamicCastFeasibility::WillSucceed ||
Feasibility == DynamicCastFeasibility::MaySucceed) {
// Check if a result of a cast is unused. If this is the case, the cast can
// be removed even if the cast may fail at runtime.
// Swift optimizer does not claim to be crash-preserving.
bool ResultNotUsed = isa<AllocStackInst>(Dest);
DestroyAddrInst *DestroyDestInst = nullptr;
if (ResultNotUsed) {
for (auto Use : Dest->getUses()) {
auto *User = Use->getUser();
if (isa<DeallocStackInst>(User) || User == Inst)
continue;
if (isa<DestroyAddrInst>(User) && !DestroyDestInst) {
DestroyDestInst = cast<DestroyAddrInst>(User);
continue;
}
ResultNotUsed = false;
DestroyDestInst = nullptr;
break;
}
}
if (ResultNotUsed) {
SILBuilder 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;
}
bool swift::simplifyUsers(SingleValueInstruction *I) {
bool Changed = false;
for (auto UI = I->use_begin(), UE = I->use_end(); UI != UE; ) {
SILInstruction *User = UI->getUser();
++UI;
auto SVI = dyn_cast<SingleValueInstruction>(User);
if (!SVI) continue;
SILValue S = simplifyInstruction(SVI);
if (!S)
continue;
SVI->replaceAllUsesWith(S);
SVI->eraseFromParent();
Changed = true;
}
return Changed;
}
/// True if a type can be expanded
/// without a significant increase to code size.
bool swift::shouldExpand(SILModule &Module, SILType Ty) {
if (EnableExpandAll) {
return true;
}
if (Ty.isAddressOnly(Module)) {
return false;
}
unsigned numFields = Module.Types.countNumberOfFields(Ty);
if (numFields > 6) {
return false;
}
return true;
}
/// Some support functions for the global-opt and let-properties-opts
/// Check if a given type is a simple type, i.e. a builtin
/// integer or floating point type or a struct/tuple whose members
/// are of simple types.
/// TODO: Cache the "simple" flag for types to avoid repeating checks.
bool swift::isSimpleType(SILType SILTy, SILModule& Module) {
// Classes can never be initialized statically at compile-time.
if (SILTy.getClassOrBoundGenericClass()) {
return false;
}
if (!SILTy.isTrivial(Module))
return false;
return true;
}
/// Check if the value of V is computed by means of a simple initialization.
/// Store the actual SILValue into Val and the reversed list of instructions
/// initializing it in Insns.
/// The check is performed by recursively walking the computation of the
/// SIL value being analyzed.
/// TODO: Move into utils.
bool
swift::analyzeStaticInitializer(SILValue V,
SmallVectorImpl<SILInstruction *> &Insns) {
// Save every instruction we see.
// TODO: MultiValueInstruction?
if (auto I = dyn_cast<SingleValueInstruction>(V))
Insns.push_back(I);
if (auto *SI = dyn_cast<StructInst>(V)) {
// If it is not a struct which is a simple type, bail.
if (!isSimpleType(SI->getType(), SI->getModule()))
return false;
for (auto &Op: SI->getAllOperands()) {
// If one of the struct instruction operands is not
// a simple initializer, bail.
if (!analyzeStaticInitializer(Op.get(), Insns))
return false;
}
return true;
} else if (auto *TI = dyn_cast<TupleInst>(V)) {
// If it is not a tuple which is a simple type, bail.
if (!isSimpleType(TI->getType(), TI->getModule()))
return false;
for (auto &Op: TI->getAllOperands()) {
// If one of the struct instruction operands is not
// a simple initializer, bail.
if (!analyzeStaticInitializer(Op.get(), Insns))
return false;
}
return true;
} else if (auto *bi = dyn_cast<BuiltinInst>(V)) {
switch (bi->getBuiltinInfo().ID) {
case BuiltinValueKind::FPTrunc:
if (auto *LI = dyn_cast<LiteralInst>(bi->getArguments()[0])) {
return analyzeStaticInitializer(LI, Insns);
}
return false;
default:
return false;
}
} else if (isa<IntegerLiteralInst>(V)
|| isa<FloatLiteralInst>(V)
|| isa<StringLiteralInst>(V)) {
return true;
} else {
return false;
}
}
/// Replace load sequence which may contain
/// a chain of struct_element_addr followed by a load.
/// The sequence is traversed inside out, i.e.
/// starting with the innermost struct_element_addr
/// Move into utils.
void swift::replaceLoadSequence(SILInstruction *I,
SILValue Value,
SILBuilder &B) {
if (auto *LI = dyn_cast<LoadInst>(I)) {
LI->replaceAllUsesWith(Value);
return;
}
// It is a series of struct_element_addr followed by load.
if (auto *SEAI = dyn_cast<StructElementAddrInst>(I)) {
auto *SEI = B.createStructExtract(SEAI->getLoc(), Value, SEAI->getField());
for (auto SEAIUse : SEAI->getUses()) {
replaceLoadSequence(SEAIUse->getUser(), SEI, B);
}
return;
}
if (auto *TEAI = dyn_cast<TupleElementAddrInst>(I)) {
auto *TEI = B.createTupleExtract(TEAI->getLoc(), Value, TEAI->getFieldNo());
for (auto TEAIUse : TEAI->getUses()) {
replaceLoadSequence(TEAIUse->getUser(), TEI, B);
}
return;
}
llvm_unreachable("Unknown instruction sequence for reading from a global");
}
/// Are the callees that could be called through Decl statically
/// knowable based on the Decl and the compilation mode?
bool swift::calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl) {
if (Decl.isForeign)
return false;
const DeclContext *AssocDC = M.getAssociatedContext();
if (!AssocDC)
return false;
auto *AFD = Decl.getAbstractFunctionDecl();
assert(AFD && "Expected abstract function decl!");
// Only handle members defined within the SILModule's associated context.
if (!AFD->isChildContextOf(AssocDC))
return false;
if (AFD->isDynamic())
return false;
if (!AFD->hasAccess())
return false;
// Only consider 'private' members, unless we are in whole-module compilation.
switch (AFD->getEffectiveAccess()) {
case AccessLevel::Open:
return false;
case AccessLevel::Public:
if (isa<ConstructorDecl>(AFD)) {
// Constructors are special: a derived class in another module can
// "override" a constructor if its class is "open", although the
// constructor itself is not open.
auto *ND = AFD->getDeclContext()
->getAsNominalTypeOrNominalTypeExtensionContext();
if (ND->getEffectiveAccess() == AccessLevel::Open)
return false;
}
LLVM_FALLTHROUGH;
case AccessLevel::Internal:
return M.isWholeModule();
case AccessLevel::FilePrivate:
case AccessLevel::Private:
return true;
}
llvm_unreachable("Unhandled access level in switch.");
}
void swift::hoistAddressProjections(Operand &Op, SILInstruction *InsertBefore,
DominanceInfo *DomTree) {
SILValue V = Op.get();
SILInstruction *Prev = nullptr;
auto *InsertPt = InsertBefore;
while (true) {
SILValue Incoming = stripSinglePredecessorArgs(V);
// Forward the incoming arg from a single predecessor.
if (V != Incoming) {
if (V == Op.get()) {
// If we are the operand itself set the operand to the incoming
// argument.
Op.set(Incoming);
V = Incoming;
} else {
// Otherwise, set the previous projections operand to the incoming
// argument.
assert(Prev && "Must have seen a projection");
Prev->setOperand(0, Incoming);
V = Incoming;
}
}
switch (V->getKind()) {
case ValueKind::StructElementAddrInst:
case ValueKind::TupleElementAddrInst:
case ValueKind::RefElementAddrInst:
case ValueKind::RefTailAddrInst:
case ValueKind::UncheckedTakeEnumDataAddrInst: {
auto *Inst = cast<SingleValueInstruction>(V);
// We are done once the current projection dominates the insert point.
if (DomTree->dominates(Inst->getParent(), InsertBefore->getParent()))
return;
// Move the current projection and memorize it for the next iteration.
Prev = Inst;
Inst->moveBefore(InsertPt);
InsertPt = Inst;
V = Inst->getOperand(0);
continue;
}
default:
assert(DomTree->dominates(V->getParentBlock(), InsertBefore->getParent()) &&
"The projected value must dominate the insertion point");
return;
}
}
}