//===--- 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/ConformanceLookup.h" #include "swift/AST/LocalArchetypeRequirementCollector.h" #include "swift/AST/ProtocolConformance.h" #include "swift/Basic/Assertions.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 [init] %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 IEUses; for (auto *Use : GAI->getUses()) { if (auto *InitExistential = dyn_cast(Use->getUser())) { IEUses.insert(InitExistential); } } /// No InitExistential found in the basic block. if (IEUses.empty()) return nullptr; /// Walk backwards from Insn instruction till the beginning 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(&*II); } return SingleIE; } /// Returns the instruction that initializes the given stack address. This is /// currently either a init_existential_addr, unconditional_checked_cast_addr, /// store, 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(User) || DebugValueInst::hasAddrVal(User) || isa(User) || isa(User) || isa(User) || OpenExistentialAddrInst::isRead(User) || User == ASIUser) { continue; } if (auto *CAI = dyn_cast(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(User)) { if (UCCAI->getDest() == allocStackAddr) { if (SingleWrite) return nullptr; SingleWrite = UCCAI; isCopied = true; } continue; } if (auto *store = dyn_cast(User)) { if (store->getDest() == allocStackAddr) { if (SingleWrite) return nullptr; SingleWrite = store; } continue; } if (isa(User)) { if (SingleWrite) return nullptr; SingleWrite = User; continue; } if (isa(User) || isa(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 *store = dyn_cast(SingleWrite)) return store; if (auto *IE = dyn_cast(SingleWrite)) return IE; if (auto *UCCA = dyn_cast(SingleWrite)) { assert(isCopied && "isCopied not set for a unconditional_checked_cast_addr"); return UCCA; } auto *CAI = cast(SingleWrite); assert(isCopied && "isCopied not set for a copy_addr"); // Attempt to recurse to find a concrete type. if (auto *ASI = dyn_cast(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(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(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; } /// 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(openedVal)) { // Handle: // %opened = open_existential_addr // %instance = alloc $opened // %opened to %stack // %instance if (auto *initI = getStackInitInst(instance, user, isOpenedValueCopied)) { // init_existential_addr isn't handled here because it isn't considered an // "opened" archtype. init_existential_addr should be handled by // ConcreteExistentialInfo. if (auto *CAI = dyn_cast(initI)) openedVal = CAI->getSrc(); if (auto *store = dyn_cast(initI)) openedVal = store->getSrc(); } } if (auto *Open = dyn_cast(openedVal)) { OpenedArchetype = Open->getType().castTo(); OpenedArchetypeValue = Open; ExistentialValue = Open->getOperand(); return; } if (auto *Open = dyn_cast(openedVal)) { OpenedArchetype = Open->getType().castTo(); OpenedArchetypeValue = Open; ExistentialValue = Open->getOperand(); return; } if (auto *Open = dyn_cast(openedVal)) { auto Ty = Open->getType().getASTType(); while (auto Metatype = dyn_cast(Ty)) Ty = Metatype.getInstanceType(); OpenedArchetype = cast(Ty); OpenedArchetypeValue = Open; ExistentialValue = Open->getOperand(); } } /// Initialize ExistentialSubs from the given conformance list, using the /// already initialized ExistentialType and ConcreteType. void ConcreteExistentialInfo::initializeSubstitutionMap( ArrayRef ExistentialConformances, SILModule *M) { // Construct a single-generic-parameter substitution map directly to the // ConcreteType with this existential's full list of conformances. // // NOTE: LocalArchetypeRequirementCollector generates the signature for passing an // opened existential as a generic parameter. No opened archetypes are // actually involved here--the API is only used as a convenient way to create // a substitution map. Since opened archetypes have different conformances // than their corresponding existential, ExistentialConformances needs to be // filtered when using it with this (phony) generic signature. auto &ctx = M->getASTContext(); LocalArchetypeRequirementCollector collector(ctx, CanGenericSignature()); collector.addOpenedExistential(ExistentialType); auto ExistentialSig = buildGenericSignature( ctx, collector.OuterSig, collector.Params, collector.Requirements, /*allowInverses=*/true).getCanonicalSignature(); ExistentialSubs = SubstitutionMap::get( ExistentialSig, [&](SubstitutableType *type) { return ConcreteType; }, [&](InFlightSubstitution &, Type, ProtocolDecl *proto) -> ProtocolConformanceRef { // Directly providing ExistentialConformances to the SubstitutionMap will // fail because of the mismatch between opened archetype conformance and // existential value conformance. Instead, provide a conformance lookup // function that pulls only the necessary conformances out of // ExistentialConformances. This assumes that existential conformances // are a superset of opened archetype conformances. auto iter = llvm::find_if(ExistentialConformances, [&](const ProtocolConformanceRef &conformance) { return conformance.getProtocol() == proto; }); assert(iter != ExistentialConformances.end() && "missing conformance"); return *iter; }); assert(isValid()); } /// If the ConcreteType is an opened existential, also initialize /// ConcreteTypeDef to the definition of that type. void ConcreteExistentialInfo::initializeConcreteTypeDef( SILInstruction *typeConversionInst) { if (!isa(ConcreteType)) 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( typeConversionInst->getTypeDependentOperands()[0].get()); return; } auto typeOperand = cast(typeConversionInst)->getOperand(); assert(typeOperand->getType().hasOpenedExistential() && "init_existential is supposed to have a typedef operand"); ConcreteTypeDef = cast(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(existential); if (!ASI) return; SILInstruction *stackInit = getStackInitInst(ASI, user, isConcreteValueCopied); if (!stackInit) return; if (auto *IE = dyn_cast(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(existential)) { ExistentialType = IER->getType().getASTType(); ConcreteType = IER->getFormalConcreteType(); ConcreteValue = IER->getOperand(); initializeSubstitutionMap(IER->getConformances(), &IER->getModule()); initializeConcreteTypeDef(IER); return; } if (auto *IEM = dyn_cast(existential)) { ExistentialType = IEM->getType().getASTType(); ConcreteValue = IEM->getOperand(); ConcreteType = ConcreteValue->getType().getASTType(); while (auto InstanceType = dyn_cast(ExistentialType)) { ExistentialType = InstanceType.getInstanceType(); ConcreteType = cast(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 = checkConformance(ConcreteTypeCandidate, Protocol); if (ConformanceRef.isInvalid()) return; // Assert that the conformance is complete. auto *ConcreteConformance = ConformanceRef.getConcrete(); assert(ConcreteConformance->isComplete()); ConcreteType = ConcreteTypeCandidate; // There is no ConcreteValue in this case. /// Determine the ExistentialConformances and SubstitutionMap. ExistentialType = Protocol->getDeclaredInterfaceType()->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; } void LLVM_ATTRIBUTE_USED OpenedArchetypeInfo::dump() const { if (!isValid()) { llvm::dbgs() << "invalid OpenedArchetypeInfo\n"; return; } llvm::dbgs() << "OpendArchetype: "; OpenedArchetype->dump(llvm::dbgs()); llvm::dbgs() << "OpendArchetypeValue: "; OpenedArchetypeValue->dump(); llvm::dbgs() << (isOpenedValueCopied ? "copied " : "") << "ExistentialValue: "; ExistentialValue->dump(); } void LLVM_ATTRIBUTE_USED ConcreteExistentialInfo::dump() const { llvm::dbgs() << "ExistentialType: "; ExistentialType->dump(llvm::dbgs()); llvm::dbgs() << "ConcreteType: "; ConcreteType->dump(llvm::dbgs()); llvm::dbgs() << (isConcreteValueCopied ? "copied " : "") << "ConcreteValue: "; ConcreteValue->dump(); if (ConcreteTypeDef) { llvm::dbgs() << "ConcreteTypeDef: "; ConcreteTypeDef->dump(); } ExistentialSubs.dump(llvm::dbgs()); llvm::dbgs() << '\n'; } void LLVM_ATTRIBUTE_USED ConcreteOpenedExistentialInfo::dump() const { OAI.dump(); if (CEI) { CEI->dump(); } else { llvm::dbgs() << "no ConcreteExistentialInfo\n"; } }