blob: 07a78d2747972395cff7a363b8851816da99bd85 [file] [log] [blame]
//===--- Existential.cpp - Functions analyzing existentials. -------------===//
//
// 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/Existential.h"
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/InstructionUtils.h"
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace swift;
/// Determine InitExistential from global_addr.
/// %3 = global_addr @$P : $*SomeP
/// %4 = init_existential_addr %3 : $*SomeP, $SomeC
/// %5 = alloc_ref $SomeC
/// store %5 to %4 : $*SomeC
/// %8 = alloc_stack $SomeP
/// copy_addr %3 to [initialization] %8 : $*SomeP
/// %10 = apply %9(%3) : $@convention(thin) (@in_guaranteed SomeP)
/// Assumptions: Insn is a direct user of GAI (e.g., copy_addr or
/// apply pattern shown above) and that a valid init_existential_addr
/// value is returned only if it can prove that the value it
/// initializes is the same value at the use point.
static InitExistentialAddrInst *
findInitExistentialFromGlobalAddr(GlobalAddrInst *GAI, SILInstruction *Insn) {
/// Check for a single InitExistential usage for GAI and
/// a simple dominance check: both InitExistential and Insn are in
/// the same basic block and only one InitExistential
/// occurs between GAI and Insn.
llvm::SmallPtrSet<SILInstruction *, 8> IEUses;
for (auto *Use : GAI->getUses()) {
if (auto *InitExistential =
dyn_cast<InitExistentialAddrInst>(Use->getUser())) {
IEUses.insert(InitExistential);
}
}
/// No InitExistential found in the basic block.
if (IEUses.empty())
return nullptr;
/// Walk backwards from Insn instruction till the begining of the basic block
/// looking for an InitExistential.
InitExistentialAddrInst *SingleIE = nullptr;
for (auto II = Insn->getIterator().getReverse(),
IE = Insn->getParent()->rend();
II != IE; ++II) {
if (!IEUses.count(&*II))
continue;
if (SingleIE)
return nullptr;
SingleIE = cast<InitExistentialAddrInst>(&*II);
}
return SingleIE;
}
/// Returns the instruction that initializes the given stack address. This is
/// currently either a init_existential_addr, unconditional_checked_cast_addr,
/// or copy_addr (if the instruction initializing the source of the copy cannot
/// be determined). Returns nullptr if the initializer does not dominate the
/// alloc_stack user \p ASIUser. If the value is copied from another stack
/// location, \p isCopied is set to true.
///
/// allocStackAddr may either itself be an AllocStackInst or an
/// InitEnumDataAddrInst that projects the value of an AllocStackInst.
static SILInstruction *getStackInitInst(SILValue allocStackAddr,
SILInstruction *ASIUser,
bool &isCopied) {
SILInstruction *SingleWrite = nullptr;
// Check that this alloc_stack is initialized only once.
for (auto Use : allocStackAddr->getUses()) {
auto *User = Use->getUser();
// Ignore instructions which don't write to the stack location.
// Also ignore ASIUser (only kicks in if ASIUser is the original apply).
if (isa<DeallocStackInst>(User) || isa<DebugValueAddrInst>(User) ||
isa<DestroyAddrInst>(User) || isa<WitnessMethodInst>(User) ||
isa<DeinitExistentialAddrInst>(User) ||
isa<OpenExistentialAddrInst>(User) || User == ASIUser) {
continue;
}
if (auto *CAI = dyn_cast<CopyAddrInst>(User)) {
if (CAI->getDest() == allocStackAddr) {
if (SingleWrite)
return nullptr;
SingleWrite = CAI;
isCopied = true;
}
continue;
}
// An unconditional_checked_cast_addr also copies a value into this addr.
if (auto *UCCAI = dyn_cast<UnconditionalCheckedCastAddrInst>(User)) {
if (UCCAI->getDest() == allocStackAddr) {
if (SingleWrite)
return nullptr;
SingleWrite = UCCAI;
isCopied = true;
}
continue;
}
if (isa<InitExistentialAddrInst>(User)) {
if (SingleWrite)
return nullptr;
SingleWrite = User;
continue;
}
if (isa<ApplyInst>(User) || isa<TryApplyInst>(User)) {
// Ignore function calls which do not write to the stack location.
auto Conv = FullApplySite(User).getArgumentConvention(*Use);
if (Conv != SILArgumentConvention::Indirect_In &&
Conv != SILArgumentConvention::Indirect_In_Guaranteed)
return nullptr;
continue;
}
// Bail if there is any unknown (and potentially writing) instruction.
return nullptr;
}
if (!SingleWrite)
return nullptr;
// A very simple dominance check. As ASI is an operand of ASIUser,
// SingleWrite dominates ASIUser if it is in the same block as ASI or
// ASIUser. (SingleWrite can't occur after ASIUser because the address would
// be uninitialized on use).
//
// If allocStack holds an Optional, then ASI is an InitEnumDataAddrInst
// projection and not strictly an operand of ASIUser. We rely on the guarantee
// that this InitEnumDataAddrInst must occur before the InjectEnumAddrInst
// that was the source of the existential address.
SILBasicBlock *BB = SingleWrite->getParent();
if (BB != allocStackAddr->getParentBlock() && BB != ASIUser->getParent())
return nullptr;
if (auto *IE = dyn_cast<InitExistentialAddrInst>(SingleWrite))
return IE;
if (auto *UCCA = dyn_cast<UnconditionalCheckedCastAddrInst>(SingleWrite)) {
assert(isCopied && "isCopied not set for a unconditional_checked_cast_addr");
return UCCA;
}
auto *CAI = cast<CopyAddrInst>(SingleWrite);
assert(isCopied && "isCopied not set for a copy_addr");
// Attempt to recurse to find a concrete type.
if (auto *ASI = dyn_cast<AllocStackInst>(CAI->getSrc()))
return getStackInitInst(ASI, CAI, isCopied);
// Peek through a stack location holding an Enum.
// %stack_adr = alloc_stack
// %data_adr = init_enum_data_addr %stk_adr
// %enum_adr = inject_enum_addr %stack_adr
// %copy_src = unchecked_take_enum_data_addr %enum_adr
// Replace %copy_src with %data_adr and recurse.
//
// TODO: a general Optional elimination sil-combine could
// supersede this check.
if (auto *UTEDAI = dyn_cast<UncheckedTakeEnumDataAddrInst>(CAI->getSrc())) {
if (InitEnumDataAddrInst *IEDAI = findInitAddressForTrivialEnum(UTEDAI))
return getStackInitInst(IEDAI, CAI, isCopied);
}
// Check if the CAISrc is a global_addr.
if (auto *GAI = dyn_cast<GlobalAddrInst>(CAI->getSrc()))
return findInitExistentialFromGlobalAddr(GAI, CAI);
// If the source of the copy cannot be determined, return the copy itself
// because the caller may have special handling for the source address.
return CAI;
}
/// Return the address of the value used to initialize the given stack location.
/// If the value originates from init_existential_addr, then it will be a
/// different type than \p allocStackAddr.
static SILValue getAddressOfStackInit(SILValue allocStackAddr,
SILInstruction *ASIUser, bool &isCopied) {
SILInstruction *initI = getStackInitInst(allocStackAddr, ASIUser, isCopied);
if (!initI)
return SILValue();
if (auto *IEA = dyn_cast<InitExistentialAddrInst>(initI))
return IEA;
if (auto *CAI = dyn_cast<CopyAddrInst>(initI))
return CAI->getSrc();
return SILValue();
}
/// Check if the given operand originates from a recognized OpenArchetype
/// instruction. If so, return the Opened, otherwise return nullptr.
OpenedArchetypeInfo::OpenedArchetypeInfo(Operand &use) {
SILValue openedVal = use.get();
SILInstruction *user = use.getUser();
if (auto *instance = dyn_cast<AllocStackInst>(openedVal)) {
// Handle:
// %opened = open_existential_addr
// %instance = alloc $opened
// copy_addr %opened to %stack
// <opened_use> %instance
if (auto stackInitVal =
getAddressOfStackInit(instance, user, isOpenedValueCopied)) {
openedVal = stackInitVal;
}
}
if (auto *Open = dyn_cast<OpenExistentialAddrInst>(openedVal)) {
OpenedArchetype = Open->getType().castTo<ArchetypeType>();
OpenedArchetypeValue = Open;
ExistentialValue = Open->getOperand();
return;
}
if (auto *Open = dyn_cast<OpenExistentialRefInst>(openedVal)) {
OpenedArchetype = Open->getType().castTo<ArchetypeType>();
OpenedArchetypeValue = Open;
ExistentialValue = Open->getOperand();
return;
}
if (auto *Open = dyn_cast<OpenExistentialMetatypeInst>(openedVal)) {
auto Ty = Open->getType().getASTType();
while (auto Metatype = dyn_cast<MetatypeType>(Ty))
Ty = Metatype.getInstanceType();
OpenedArchetype = cast<ArchetypeType>(Ty);
OpenedArchetypeValue = Open;
ExistentialValue = Open->getOperand();
}
}
/// Initialize ExistentialSubs from the given conformance list, using the
/// already initialized ExistentialType and ConcreteType.
void ConcreteExistentialInfo::initializeSubstitutionMap(
ArrayRef<ProtocolConformanceRef> ExistentialConformances, SILModule *M) {
// Construct a single-generic-parameter substitution map directly to the
// ConcreteType with this existential's full list of conformances.
CanGenericSignature ExistentialSig =
M->getASTContext().getExistentialSignature(ExistentialType,
M->getSwiftModule());
ExistentialSubs = SubstitutionMap::get(ExistentialSig, {ConcreteType},
ExistentialConformances);
assert(isValid());
}
/// If the ConcreteType is an opened existential, also initialize
/// ConcreteTypeDef to the definition of that type.
void ConcreteExistentialInfo::initializeConcreteTypeDef(
SILInstruction *typeConversionInst) {
if (!ConcreteType->isOpenedExistential())
return;
assert(isValid());
// If the concrete type is another existential, we're "forwarding" an
// opened existential type, so we must keep track of the original
// defining instruction.
if (!typeConversionInst->getTypeDependentOperands().empty()) {
ConcreteTypeDef = cast<SingleValueInstruction>(
typeConversionInst->getTypeDependentOperands()[0].get());
return;
}
auto typeOperand =
cast<InitExistentialMetatypeInst>(typeConversionInst)->getOperand();
assert(typeOperand->getType().hasOpenedExistential()
&& "init_existential is supposed to have a typedef operand");
ConcreteTypeDef = cast<SingleValueInstruction>(typeOperand);
}
/// Construct this ConcreteExistentialInfo based on the given existential use.
///
/// Finds the init_existential, or an address with concrete type used to
/// initialize the given \p openedUse. If the value is copied
/// from another stack location, \p isCopied is set to true.
///
/// If successful, ConcreteExistentialInfo will be valid upon return, with the
/// following fields assigned:
/// - ExistentialType
/// - isCopied
/// - ConcreteType
/// - ConcreteValue
/// - ConcreteTypeDef
/// - ExistentialSubs
ConcreteExistentialInfo::ConcreteExistentialInfo(SILValue existential,
SILInstruction *user) {
if (existential->getType().isAddress()) {
auto *ASI = dyn_cast<AllocStackInst>(existential);
if (!ASI)
return;
SILInstruction *stackInit =
getStackInitInst(ASI, user, isConcreteValueCopied);
if (!stackInit)
return;
if (auto *IE = dyn_cast<InitExistentialAddrInst>(stackInit)) {
ExistentialType = IE->getOperand()->getType().getASTType();
ConcreteType = IE->getFormalConcreteType();
ConcreteValue = IE;
initializeSubstitutionMap(IE->getConformances(), &IE->getModule());
initializeConcreteTypeDef(IE);
return;
}
// TODO: Once we have a way to introduce more constrained archetypes, handle
// any unconditional_checked_cast that wasn't already statically eliminated.
//
// Unexpected stack write.
return;
}
if (auto *IER = dyn_cast<InitExistentialRefInst>(existential)) {
ExistentialType = IER->getType().getASTType();
ConcreteType = IER->getFormalConcreteType();
ConcreteValue = IER->getOperand();
initializeSubstitutionMap(IER->getConformances(), &IER->getModule());
initializeConcreteTypeDef(IER);
return;
}
if (auto *IEM = dyn_cast<InitExistentialMetatypeInst>(existential)) {
ExistentialType = IEM->getType().getASTType();
ConcreteValue = IEM->getOperand();
ConcreteType = ConcreteValue->getType().getASTType();
while (auto InstanceType =
dyn_cast<ExistentialMetatypeType>(ExistentialType)) {
ExistentialType = InstanceType.getInstanceType();
ConcreteType = cast<MetatypeType>(ConcreteType).getInstanceType();
}
initializeSubstitutionMap(IEM->getConformances(), &IEM->getModule());
initializeConcreteTypeDef(IEM);
return;
}
// Unrecognized opened existential producer.
return;
}
/// Initialize a ConcreteExistentialInfo based on a concrete type and protocol
/// declaration that has already been computed via whole module type
/// inference. A cast instruction will be introduced to produce the concrete
/// value from the opened value.
///
/// The simpler constructor taking only the existential value is preferred
/// because it generates simpler SIL and does not require an extra
/// cast. However, if that constructor fails to produce a valid
/// ConcreteExistentialInfo, this constructor may succeed because it doesn't
/// needs to rediscover the whole-module inferred ConcreteTypeCandidate.
ConcreteExistentialInfo::ConcreteExistentialInfo(SILValue existential,
SILInstruction *user,
CanType ConcreteTypeCandidate,
ProtocolDecl *Protocol) {
SILModule *M = existential->getModule();
// We have the open_existential; we still need the conformance.
auto ConformanceRef =
M->getSwiftModule()->conformsToProtocol(ConcreteTypeCandidate, Protocol);
if (!ConformanceRef)
return;
// Assert that the conformance is complete.
auto *ConcreteConformance = ConformanceRef.getValue().getConcrete();
assert(ConcreteConformance->isComplete());
ConcreteType = ConcreteTypeCandidate;
// There is no ConcreteValue in this case.
/// Determine the ExistentialConformances and SubstitutionMap.
ExistentialType = Protocol->getDeclaredType()->getCanonicalType();
initializeSubstitutionMap(ProtocolConformanceRef(ConcreteConformance), M);
assert(isValid());
}
ConcreteOpenedExistentialInfo::ConcreteOpenedExistentialInfo(Operand &use)
: OAI(use) {
if (!OAI.isValid())
return;
CEI.emplace(OAI.ExistentialValue, OAI.OpenedArchetypeValue);
if (!CEI->isValid()) {
CEI.reset();
return;
}
CEI->isConcreteValueCopied |= OAI.isOpenedValueCopied;
}
ConcreteOpenedExistentialInfo::ConcreteOpenedExistentialInfo(
Operand &use, CanType concreteType, ProtocolDecl *protocol)
: OAI(use) {
if (!OAI.isValid())
return;
CEI.emplace(OAI.ExistentialValue, OAI.OpenedArchetypeValue, concreteType,
protocol);
if (!CEI->isValid()) {
CEI.reset();
return;
}
CEI->isConcreteValueCopied |= OAI.isOpenedValueCopied;
}