//===--- 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/SILOptimizer/Utils/CFG.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/BasicBlockUtils.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 SILValue 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 SILValue(); /// Walk backwards from Insn instruction till the begining of the basic block /// looking for an InitExistential. SILValue SingleIE; for (auto II = Insn->getIterator().getReverse(), IE = Insn->getParent()->rend(); II != IE; ++II) { if (!IEUses.count(&*II)) continue; if (SingleIE) return SILValue(); SingleIE = cast(&*II); } return SingleIE; } /// Determine InitExistential from global_addr and copy_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 SILValue swift::findInitExistentialFromGlobalAddrAndCopyAddr(GlobalAddrInst *GAI, CopyAddrInst *CAI) { assert(CAI->getSrc() == SILValue(GAI) && "Broken Assumption! Global Addr is not the source of the passed in " "copy_addr?!"); return findInitExistentialFromGlobalAddr(GAI, cast(CAI)); } /// Determine InitExistential from global_addr and an apply argument. /// Pattern 1 /// %3 = global_addr @$P : $*SomeP /// %4 = init_existential_addr %3 : $*SomeP, $SomeC /// %5 = alloc_ref $SomeC /// store %5 to %4 : $*SomeC /// %10 = apply %9(%3) : $@convention(thin) (@in_guaranteed SomeP) /// Pattern 2 /// %3 = global_addr @$P : $*SomeP /// %9 = open_existential_addr mutable_access %3 : $*SomeP to $*@opened SomeP /// %15 = apply %11(%9) : $@convention(thin) (@in_guaranteed SomeP) SILValue swift::findInitExistentialFromGlobalAddrAndApply(GlobalAddrInst *GAI, ApplySite Apply, int ArgIdx) { /// Code to ensure that we are calling only in two pattern matching scenarios. bool isArg = false; auto Arg = Apply.getArgument(ArgIdx); if (auto *ApplyGAI = dyn_cast(Arg)) { if (ApplyGAI->isIdenticalTo(GAI)) { isArg = true; } } else if (auto Open = dyn_cast(Arg)) { auto Op = Open->getOperand(); if (auto *OpGAI = dyn_cast(Op)) { if (OpGAI->isIdenticalTo(GAI)) { isArg = true; } } } assert(isArg && "Broken Assumption! Global Addr is not an argument to " "apply?!"); return findInitExistentialFromGlobalAddr(GAI, Apply.getInstruction()); } /// Returns the address of an object with which the stack location \p ASI is /// initialized. This is either a init_existential_addr or the destination of a /// copy_addr. Returns a null value if the address does not dominate the /// alloc_stack user \p ASIUser. /// If the value is copied from another stack location, \p isCopied is set to /// true. SILValue swift::getAddressOfStackInit(AllocStackInst *ASI, SILInstruction *ASIUser, bool &isCopied) { SILInstruction *SingleWrite = nullptr; // Check that this alloc_stack is initialized only once. for (auto Use : ASI->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) || isa(User) || isa(User) || isa(User) || isa(User) || isa(User) || User == ASIUser) { continue; } if (auto *CAI = dyn_cast(User)) { if (CAI->getDest() == ASI) { if (SingleWrite) return SILValue(); SingleWrite = CAI; isCopied = true; } continue; } if (isa(User)) { if (SingleWrite) return SILValue(); 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 SILValue(); continue; } // Bail if there is any unknown (and potentially writing) instruction. return SILValue(); } if (!SingleWrite) return SILValue(); // 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. SILBasicBlock *BB = SingleWrite->getParent(); if (BB != ASI->getParent() && BB != ASIUser->getParent()) return SILValue(); if (auto *CAI = dyn_cast(SingleWrite)) { // Try to derive the type from the copy_addr that was used to // initialize the alloc_stack. assert(isCopied && "isCopied not set for a copy_addr"); SILValue CAISrc = CAI->getSrc(); if (auto *ASI = dyn_cast(CAISrc)) return getAddressOfStackInit(ASI, CAI, isCopied); // Check if the CAISrc is a global_addr. if (auto *GAI = dyn_cast(CAISrc)) { return findInitExistentialFromGlobalAddrAndCopyAddr(GAI, CAI); } return CAISrc; } return cast(SingleWrite); } /// Find the init_existential, which could be used to determine a concrete /// type of the \p Self. /// If the value is copied from another stack location, \p isCopied is set to /// true. SILInstruction *swift::findInitExistential(Operand &openedUse, ArchetypeType *&OpenedArchetype, SILValue &OpenedArchetypeDef, bool &isCopied) { SILValue Self = openedUse.get(); SILInstruction *User = openedUse.getUser(); isCopied = false; if (auto *Instance = dyn_cast(Self)) { // In case the Self operand is an alloc_stack where a copy_addr copies the // result of an open_existential_addr to this stack location. if (SILValue Src = getAddressOfStackInit(Instance, User, isCopied)) Self = Src; } if (auto *Open = dyn_cast(Self)) { auto Op = Open->getOperand(); auto *ASI = dyn_cast(Op); if (!ASI) return nullptr; SILValue StackWrite = getAddressOfStackInit(ASI, Open, isCopied); if (!StackWrite) return nullptr; auto *IE = dyn_cast(StackWrite); if (!IE) return nullptr; OpenedArchetype = Open->getType().castTo(); OpenedArchetypeDef = Open; return IE; } if (auto *Open = dyn_cast(Self)) { if (auto *IE = dyn_cast(Open->getOperand())) { OpenedArchetype = Open->getType().castTo(); OpenedArchetypeDef = Open; return IE; } return nullptr; } if (auto *Open = dyn_cast(Self)) { if (auto *IE = dyn_cast(Open->getOperand())) { auto Ty = Open->getType().getASTType(); while (auto Metatype = dyn_cast(Ty)) Ty = Metatype.getInstanceType(); OpenedArchetype = cast(Ty); OpenedArchetypeDef = Open; return IE; } return nullptr; } return nullptr; } /// Derive a concrete type of self and conformance from the init_existential /// instruction. /// If successful, initializes a valid ConformanceAndConcreteType. ConcreteExistentialInfo::ConcreteExistentialInfo(Operand &openedUse) { // Try to find the init_existential, which could be used to // determine a concrete type of the self. // Returns: InitExistential, OpenedArchetype, OpenedArchetypeDef, isCopied. InitExistential = findInitExistential(openedUse, OpenedArchetype, OpenedArchetypeDef, isCopied); if (!InitExistential) return; ArrayRef ExistentialConformances; if (auto IE = dyn_cast(InitExistential)) { ExistentialType = IE->getOperand()->getType().getASTType(); ExistentialConformances = IE->getConformances(); ConcreteType = IE->getFormalConcreteType(); ConcreteValue = IE; } else if (auto IER = dyn_cast(InitExistential)) { ExistentialType = IER->getType().getASTType(); ExistentialConformances = IER->getConformances(); ConcreteType = IER->getFormalConcreteType(); ConcreteValue = IER->getOperand(); } else if (auto IEM = dyn_cast(InitExistential)) { ExistentialType = IEM->getType().getASTType(); ExistentialConformances = IEM->getConformances(); ConcreteValue = IEM->getOperand(); ConcreteType = ConcreteValue->getType().getASTType(); while (auto InstanceType = dyn_cast(ExistentialType)) { ExistentialType = InstanceType.getInstanceType(); ConcreteType = cast(ConcreteType).getInstanceType(); } } else { assert(!isValid()); return; } // Construct a single-generic-parameter substitution map directly to the // ConcreteType with this existential's full list of conformances. SILModule &M = InitExistential->getModule(); CanGenericSignature ExistentialSig = M.getASTContext().getExistentialSignature(ExistentialType, M.getSwiftModule()); ExistentialSubs = SubstitutionMap::get(ExistentialSig, {ConcreteType}, ExistentialConformances); // 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 (ConcreteType->isOpenedExistential()) { if (InitExistential->getTypeDependentOperands().empty()) { auto op = InitExistential->getOperand(0); assert(op->getType().hasOpenedExistential() && "init_existential is supposed to have a typedef operand"); ConcreteTypeDef = cast(op); } else { ConcreteTypeDef = cast( InitExistential->getTypeDependentOperands()[0].get()); } } assert(isValid()); }