/// Does value A properly dominate instruction B? bool DominanceInfo::properlyDominates(SILValue a, SILInstruction *b) { if (auto *Inst = a->getDefiningInstruction()) { return properlyDominates(Inst, b); } if (auto *Arg = dyn_cast<SILArgument>(a)) { return dominates(Arg->getParent(), b->getParent()); } return false; }
/// Return true if the instruction blocks the Ptr to be moved further. bool mayBlockCodeMotion(SILInstruction *II, SILValue Ptr) override { // NOTE: If more checks are to be added, place the most expensive in the end. // This function is called many times. // // We can not move a release above the instruction that defines the // released value. if (II == Ptr->getDefiningInstruction()) return true; // Identical RC root blocks code motion, we will be able to move this release // further once we move the blocking release. if (isReleaseInstruction(II) && getRCRoot(II) == Ptr) return true; // Stop at may interfere. if (mayHaveSymmetricInterference(II, Ptr, AA)) return true; // This instruction does not block the release. return false; }
/// Find all closures that may be propagated into the given function-type value. /// /// Searches the use-def chain from the given value upward until a partial_apply /// is reached. Populates `results` with the set of partial_apply instructions. /// /// `funcVal` may be either a function type or an Optional function type. This /// might be called on a directly applied value or on a call argument, which may /// in turn be applied within the callee. void swift::findClosuresForFunctionValue( SILValue funcVal, TinyPtrVector<PartialApplyInst *> &results) { SILType funcTy = funcVal->getType(); // Handle `Optional<@convention(block) @noescape (_)->(_)>` if (auto optionalObjTy = funcTy.getOptionalObjectType()) funcTy = optionalObjTy; assert(funcTy.is<SILFunctionType>()); SmallVector<SILValue, 4> worklist; // Avoid exponential path exploration and prevent duplicate results. llvm::SmallDenseSet<SILValue, 8> visited; auto worklistInsert = [&](SILValue V) { if (visited.insert(V).second) worklist.push_back(V); }; worklistInsert(funcVal); while (!worklist.empty()) { SILValue V = worklist.pop_back_val(); if (auto *I = V->getDefiningInstruction()) { // Look through copies, borrows, and conversions. // // Handle copy_block and copy_block_without_actually_escaping before // calling findClosureStoredIntoBlock. if (SingleValueInstruction *SVI = getSingleValueCopyOrCast(I)) { worklistInsert(SVI->getOperand(0)); continue; } } // Look through Optionals. if (V->getType().getOptionalObjectType()) { auto *EI = dyn_cast<EnumInst>(V); if (EI && EI->hasOperand()) { worklistInsert(EI->getOperand()); } // Ignore the .None case. continue; } // Look through Phis. // // This should be done before calling findClosureStoredIntoBlock. if (auto *arg = dyn_cast<SILPhiArgument>(V)) { SmallVector<std::pair<SILBasicBlock *, SILValue>, 2> blockArgs; arg->getIncomingPhiValues(blockArgs); for (auto &blockAndArg : blockArgs) worklistInsert(blockAndArg.second); continue; } // Look through ObjC closures. auto fnType = V->getType().getAs<SILFunctionType>(); if (fnType && fnType->getRepresentation() == SILFunctionTypeRepresentation::Block) { if (SILValue storedClosure = findClosureStoredIntoBlock(V)) worklistInsert(storedClosure); continue; } if (auto *PAI = dyn_cast<PartialApplyInst>(V)) { SILValue thunkArg = isPartialApplyOfReabstractionThunk(PAI); if (thunkArg) { // Handle reabstraction thunks recursively. This may reabstract over // @convention(block). worklistInsert(thunkArg); continue; } results.push_back(PAI); continue; } // Ignore other unrecognized values that feed this applied argument. } }
bool ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { assert(Pointer->getType().isAddress() && "Walked through the pointer to the value?"); SILType PointeeType = Pointer->getType().getObjectType(); /// This keeps track of instructions in the use list that touch multiple tuple /// elements and should be scalarized. This is done as a second phase to /// avoid invalidating the use iterator. /// SmallVector<SILInstruction *, 4> UsesToScalarize; for (auto *UI : Pointer->getUses()) { auto *User = UI->getUser(); // struct_element_addr P, #field indexes into the current element. if (auto *SEAI = dyn_cast<StructElementAddrInst>(User)) { if (!collectStructElementUses(SEAI, BaseEltNo)) return false; continue; } // Instructions that compute a subelement are handled by a helper. if (auto *TEAI = dyn_cast<TupleElementAddrInst>(User)) { if (!collectTupleElementUses(TEAI, BaseEltNo)) return false; continue; } // Look through begin_access. if (auto I = dyn_cast<BeginAccessInst>(User)) { if (!collectUses(I, BaseEltNo)) return false; continue; } // Ignore end_access. if (isa<EndAccessInst>(User)) { continue; } // Loads are a use of the value. if (isa<LoadInst>(User)) { if (PointeeType.is<TupleType>()) UsesToScalarize.push_back(User); else addElementUses(BaseEltNo, PointeeType, User, PMOUseKind::Load); continue; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ if (isa<Load##Name##Inst>(User)) { \ Uses.push_back(PMOMemoryUse(User, PMOUseKind::Load, BaseEltNo, 1)); \ continue; \ } #include "swift/AST/ReferenceStorage.def" // Stores *to* the allocation are writes. if (isa<StoreInst>(User) && UI->getOperandNumber() == 1) { if (PointeeType.is<TupleType>()) { UsesToScalarize.push_back(User); continue; } // Coming out of SILGen, we assume that raw stores are initializations, // unless they have trivial type (which we classify as InitOrAssign). PMOUseKind Kind; if (InStructSubElement) Kind = PMOUseKind::PartialStore; else if (PointeeType.isTrivial(User->getModule())) Kind = PMOUseKind::InitOrAssign; else Kind = PMOUseKind::Initialization; addElementUses(BaseEltNo, PointeeType, User, Kind); continue; } #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ if (auto *SI = dyn_cast<Store##Name##Inst>(User)) { \ if (UI->getOperandNumber() == 1) { \ PMOUseKind Kind; \ if (InStructSubElement) \ Kind = PMOUseKind::PartialStore; \ else if (SI->isInitializationOfDest()) \ Kind = PMOUseKind::Initialization; \ else \ Kind = PMOUseKind::Assign; \ Uses.push_back(PMOMemoryUse(User, Kind, BaseEltNo, 1)); \ continue; \ } \ } #include "swift/AST/ReferenceStorage.def" if (auto *CAI = dyn_cast<CopyAddrInst>(User)) { // If this is a copy of a tuple, we should scalarize it so that we don't // have an access that crosses elements. if (PointeeType.is<TupleType>()) { UsesToScalarize.push_back(CAI); continue; } // If this is the source of the copy_addr, then this is a load. If it is // the destination, then this is an unknown assignment. Note that we'll // revisit this instruction and add it to Uses twice if it is both a load // and store to the same aggregate. PMOUseKind Kind; if (UI->getOperandNumber() == 0) Kind = PMOUseKind::Load; else if (InStructSubElement) Kind = PMOUseKind::PartialStore; else if (CAI->isInitializationOfDest()) Kind = PMOUseKind::Initialization; else Kind = PMOUseKind::Assign; addElementUses(BaseEltNo, PointeeType, User, Kind); continue; } // The apply instruction does not capture the pointer when it is passed // through 'inout' arguments or for indirect returns. InOut arguments are // treated as uses and may-store's, but an indirect return is treated as a // full store. // // Note that partial_apply instructions always close over their argument. // if (auto *Apply = dyn_cast<ApplyInst>(User)) { auto substConv = Apply->getSubstCalleeConv(); unsigned ArgumentNumber = UI->getOperandNumber() - 1; // If this is an out-parameter, it is like a store. unsigned NumIndirectResults = substConv.getNumIndirectSILResults(); if (ArgumentNumber < NumIndirectResults) { // We do not support initializing sub members. This is an old // restriction from when this code was used by Definite // Initialization. With proper code review, we can remove this, but for // now, lets be conservative. if (InStructSubElement) { return false; } addElementUses(BaseEltNo, PointeeType, User, PMOUseKind::Initialization); continue; // Otherwise, adjust the argument index. } else { ArgumentNumber -= NumIndirectResults; } auto ParamConvention = substConv.getParameters()[ArgumentNumber].getConvention(); switch (ParamConvention) { case ParameterConvention::Direct_Owned: case ParameterConvention::Direct_Unowned: case ParameterConvention::Direct_Guaranteed: llvm_unreachable("address value passed to indirect parameter"); // If this is an in-parameter, it is like a load. case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_In_Guaranteed: addElementUses(BaseEltNo, PointeeType, User, PMOUseKind::IndirectIn); continue; // If this is an @inout parameter, it is like both a load and store. case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: { // If we're in the initializer for a struct, and this is a call to a // mutating method, we model that as an escape of self. If an // individual sub-member is passed as inout, then we model that as an // inout use. addElementUses(BaseEltNo, PointeeType, User, PMOUseKind::InOutUse); continue; } } llvm_unreachable("bad parameter convention"); } // init_existential_addr is modeled as an initialization store. if (isa<InitExistentialAddrInst>(User)) { // init_existential_addr should not apply to struct subelements. if (InStructSubElement) { return false; } Uses.push_back( PMOMemoryUse(User, PMOUseKind::Initialization, BaseEltNo, 1)); continue; } // open_existential_addr is a use of the protocol value, // so it is modeled as a load. if (isa<OpenExistentialAddrInst>(User)) { Uses.push_back(PMOMemoryUse(User, PMOUseKind::Load, BaseEltNo, 1)); // TODO: Is it safe to ignore all uses of the open_existential_addr? continue; } // We model destroy_addr as a release of the entire value. if (isa<DestroyAddrInst>(User)) { Releases.push_back(User); continue; } if (isa<DeallocStackInst>(User)) { continue; } // Sanitizer instrumentation is not user visible, so it should not // count as a use and must not affect compile-time diagnostics. if (isSanitizerInstrumentation(User)) continue; // Otherwise, the use is something complicated, it escapes. addElementUses(BaseEltNo, PointeeType, User, PMOUseKind::Escape); } // Now that we've walked all of the immediate uses, scalarize any operations // working on tuples if we need to for canonicalization or analysis reasons. if (!UsesToScalarize.empty()) { SILInstruction *PointerInst = Pointer->getDefiningInstruction(); SmallVector<SILValue, 4> ElementAddrs; SILBuilderWithScope AddrBuilder(++SILBasicBlock::iterator(PointerInst), PointerInst); getScalarizedElementAddresses(Pointer, AddrBuilder, PointerInst->getLoc(), ElementAddrs); SmallVector<SILValue, 4> ElementTmps; for (auto *User : UsesToScalarize) { ElementTmps.clear(); LLVM_DEBUG(llvm::errs() << " *** Scalarizing: " << *User << "\n"); // Scalarize LoadInst if (auto *LI = dyn_cast<LoadInst>(User)) { SILValue Result = scalarizeLoad(LI, ElementAddrs); LI->replaceAllUsesWith(Result); LI->eraseFromParent(); continue; } // Scalarize StoreInst if (auto *SI = dyn_cast<StoreInst>(User)) { SILBuilderWithScope B(User, SI); getScalarizedElements(SI->getOperand(0), ElementTmps, SI->getLoc(), B); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createStore(SI->getLoc(), ElementTmps[i], ElementAddrs[i], StoreOwnershipQualifier::Unqualified); SI->eraseFromParent(); continue; } // Scalarize CopyAddrInst. auto *CAI = cast<CopyAddrInst>(User); SILBuilderWithScope B(User, CAI); // Determine if this is a copy *from* or *to* "Pointer". if (CAI->getSrc() == Pointer) { // Copy from pointer. getScalarizedElementAddresses(CAI->getDest(), B, CAI->getLoc(), ElementTmps); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createCopyAddr(CAI->getLoc(), ElementAddrs[i], ElementTmps[i], CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); } else { getScalarizedElementAddresses(CAI->getSrc(), B, CAI->getLoc(), ElementTmps); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createCopyAddr(CAI->getLoc(), ElementTmps[i], ElementAddrs[i], CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); } CAI->eraseFromParent(); } // Now that we've scalarized some stuff, recurse down into the newly created // element address computations to recursively process it. This can cause // further scalarization. if (llvm::any_of(ElementAddrs, [&](SILValue V) { return !collectTupleElementUses(cast<TupleElementAddrInst>(V), BaseEltNo); })) { return false; } } return true; }
void ElementUseCollector::collectUses(SILValue Pointer, unsigned BaseEltNo) { assert(Pointer->getType().isAddress() && "Walked through the pointer to the value?"); SILType PointeeType = Pointer->getType().getObjectType(); /// This keeps track of instructions in the use list that touch multiple tuple /// elements and should be scalarized. This is done as a second phase to /// avoid invalidating the use iterator. /// SmallVector<SILInstruction*, 4> UsesToScalarize; for (auto *UI : Pointer->getUses()) { auto *User = UI->getUser(); // struct_element_addr P, #field indexes into the current element. if (auto *SEAI = dyn_cast<StructElementAddrInst>(User)) { collectStructElementUses(SEAI, BaseEltNo); continue; } // Instructions that compute a subelement are handled by a helper. if (auto *TEAI = dyn_cast<TupleElementAddrInst>(User)) { collectTupleElementUses(TEAI, BaseEltNo); continue; } // Look through begin_access. if (auto I = dyn_cast<BeginAccessInst>(User)) { collectUses(I, BaseEltNo); continue; } // Ignore end_access. if (isa<EndAccessInst>(User)) { continue; } // Loads are a use of the value. if (isa<LoadInst>(User)) { if (PointeeType.is<TupleType>()) UsesToScalarize.push_back(User); else addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Load); continue; } if (isa<LoadWeakInst>(User)) { Uses.push_back(DIMemoryUse(User, DIUseKind::Load, BaseEltNo, 1)); continue; } // Stores *to* the allocation are writes. if ((isa<StoreInst>(User) || isa<AssignInst>(User)) && UI->getOperandNumber() == 1) { if (PointeeType.is<TupleType>()) { UsesToScalarize.push_back(User); continue; } // Coming out of SILGen, we assume that raw stores are initializations, // unless they have trivial type (which we classify as InitOrAssign). DIUseKind Kind; if (InStructSubElement) Kind = DIUseKind::PartialStore; else if (isa<AssignInst>(User)) Kind = DIUseKind::InitOrAssign; else if (PointeeType.isTrivial(User->getModule())) Kind = DIUseKind::InitOrAssign; else Kind = DIUseKind::Initialization; addElementUses(BaseEltNo, PointeeType, User, Kind); continue; } if (auto *SWI = dyn_cast<StoreWeakInst>(User)) if (UI->getOperandNumber() == 1) { DIUseKind Kind; if (InStructSubElement) Kind = DIUseKind::PartialStore; else if (SWI->isInitializationOfDest()) Kind = DIUseKind::Initialization; else Kind = DIUseKind::Assign; Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1)); continue; } if (auto *SUI = dyn_cast<StoreUnownedInst>(User)) if (UI->getOperandNumber() == 1) { DIUseKind Kind; if (InStructSubElement) Kind = DIUseKind::PartialStore; else if (SUI->isInitializationOfDest()) Kind = DIUseKind::Initialization; else Kind = DIUseKind::Assign; Uses.push_back(DIMemoryUse(User, Kind, BaseEltNo, 1)); continue; } if (auto *CAI = dyn_cast<CopyAddrInst>(User)) { // If this is a copy of a tuple, we should scalarize it so that we don't // have an access that crosses elements. if (PointeeType.is<TupleType>()) { UsesToScalarize.push_back(CAI); continue; } // If this is the source of the copy_addr, then this is a load. If it is // the destination, then this is an unknown assignment. Note that we'll // revisit this instruction and add it to Uses twice if it is both a load // and store to the same aggregate. DIUseKind Kind; if (UI->getOperandNumber() == 0) Kind = DIUseKind::Load; else if (InStructSubElement) Kind = DIUseKind::PartialStore; else if (CAI->isInitializationOfDest()) Kind = DIUseKind::Initialization; else Kind = DIUseKind::Assign; addElementUses(BaseEltNo, PointeeType, User, Kind); continue; } // The apply instruction does not capture the pointer when it is passed // through 'inout' arguments or for indirect returns. InOut arguments are // treated as uses and may-store's, but an indirect return is treated as a // full store. // // Note that partial_apply instructions always close over their argument. // if (auto *Apply = dyn_cast<ApplyInst>(User)) { auto substConv = Apply->getSubstCalleeConv(); unsigned ArgumentNumber = UI->getOperandNumber()-1; // If this is an out-parameter, it is like a store. unsigned NumIndirectResults = substConv.getNumIndirectSILResults(); if (ArgumentNumber < NumIndirectResults) { assert(!InStructSubElement && "We're initializing sub-members?"); addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Initialization); continue; // Otherwise, adjust the argument index. } else { ArgumentNumber -= NumIndirectResults; } auto ParamConvention = substConv.getParameters()[ArgumentNumber].getConvention(); switch (ParamConvention) { case ParameterConvention::Direct_Owned: case ParameterConvention::Direct_Unowned: case ParameterConvention::Direct_Guaranteed: llvm_unreachable("address value passed to indirect parameter"); // If this is an in-parameter, it is like a load. case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_In_Guaranteed: addElementUses(BaseEltNo, PointeeType, User, DIUseKind::IndirectIn); continue; // If this is an @inout parameter, it is like both a load and store. case ParameterConvention::Indirect_Inout: case ParameterConvention::Indirect_InoutAliasable: { // If we're in the initializer for a struct, and this is a call to a // mutating method, we model that as an escape of self. If an // individual sub-member is passed as inout, then we model that as an // inout use. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::InOutUse); continue; } } llvm_unreachable("bad parameter convention"); } // init_enum_data_addr is treated like a tuple_element_addr or other instruction // that is looking into the memory object (i.e., the memory object needs to // be explicitly initialized by a copy_addr or some other use of the // projected address). if (auto I = dyn_cast<InitEnumDataAddrInst>(User)) { assert(!InStructSubElement && "init_enum_data_addr shouldn't apply to struct subelements"); // Keep track of the fact that we're inside of an enum. This informs our // recursion that tuple stores are not scalarized outside, and that stores // should not be treated as partial stores. llvm::SaveAndRestore<bool> X(InEnumSubElement, true); collectUses(I, BaseEltNo); continue; } // init_existential_addr is modeled as an initialization store. if (isa<InitExistentialAddrInst>(User)) { assert(!InStructSubElement && "init_existential_addr should not apply to struct subelements"); Uses.push_back(DIMemoryUse(User, DIUseKind::Initialization, BaseEltNo, 1)); continue; } // inject_enum_addr is modeled as an initialization store. if (isa<InjectEnumAddrInst>(User)) { assert(!InStructSubElement && "inject_enum_addr the subelement of a struct unless in a ctor"); Uses.push_back(DIMemoryUse(User, DIUseKind::Initialization, BaseEltNo, 1)); continue; } // open_existential_addr is a use of the protocol value, // so it is modeled as a load. if (isa<OpenExistentialAddrInst>(User)) { Uses.push_back(DIMemoryUse(User, DIUseKind::Load, BaseEltNo, 1)); // TODO: Is it safe to ignore all uses of the open_existential_addr? continue; } // We model destroy_addr as a release of the entire value. if (isa<DestroyAddrInst>(User)) { Releases.push_back(User); continue; } if (isa<DeallocStackInst>(User)) { continue; } // Sanitizer instrumentation is not user visible, so it should not // count as a use and must not affect compile-time diagnostics. if (isSanitizerInstrumentation(User, Module.getASTContext())) continue; // Otherwise, the use is something complicated, it escapes. addElementUses(BaseEltNo, PointeeType, User, DIUseKind::Escape); } // Now that we've walked all of the immediate uses, scalarize any operations // working on tuples if we need to for canonicalization or analysis reasons. if (!UsesToScalarize.empty()) { SILInstruction *PointerInst = Pointer->getDefiningInstruction(); SmallVector<SILValue, 4> ElementAddrs; SILBuilderWithScope AddrBuilder(++SILBasicBlock::iterator(PointerInst), PointerInst); getScalarizedElementAddresses(Pointer, AddrBuilder, PointerInst->getLoc(), ElementAddrs); SmallVector<SILValue, 4> ElementTmps; for (auto *User : UsesToScalarize) { ElementTmps.clear(); DEBUG(llvm::errs() << " *** Scalarizing: " << *User << "\n"); // Scalarize LoadInst if (auto *LI = dyn_cast<LoadInst>(User)) { SILValue Result = scalarizeLoad(LI, ElementAddrs); LI->replaceAllUsesWith(Result); LI->eraseFromParent(); continue; } // Scalarize AssignInst if (auto *AI = dyn_cast<AssignInst>(User)) { SILBuilderWithScope B(User, AI); getScalarizedElements(AI->getOperand(0), ElementTmps, AI->getLoc(), B); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createAssign(AI->getLoc(), ElementTmps[i], ElementAddrs[i]); AI->eraseFromParent(); continue; } // Scalarize StoreInst if (auto *SI = dyn_cast<StoreInst>(User)) { SILBuilderWithScope B(User, SI); getScalarizedElements(SI->getOperand(0), ElementTmps, SI->getLoc(), B); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createStore(SI->getLoc(), ElementTmps[i], ElementAddrs[i], StoreOwnershipQualifier::Unqualified); SI->eraseFromParent(); continue; } // Scalarize CopyAddrInst. auto *CAI = cast<CopyAddrInst>(User); SILBuilderWithScope B(User, CAI); // Determine if this is a copy *from* or *to* "Pointer". if (CAI->getSrc() == Pointer) { // Copy from pointer. getScalarizedElementAddresses(CAI->getDest(), B, CAI->getLoc(), ElementTmps); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createCopyAddr(CAI->getLoc(), ElementAddrs[i], ElementTmps[i], CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); } else { getScalarizedElementAddresses(CAI->getSrc(), B, CAI->getLoc(), ElementTmps); for (unsigned i = 0, e = ElementAddrs.size(); i != e; ++i) B.createCopyAddr(CAI->getLoc(), ElementTmps[i], ElementAddrs[i], CAI->isTakeOfSrc(), CAI->isInitializationOfDest()); } CAI->eraseFromParent(); } // Now that we've scalarized some stuff, recurse down into the newly created // element address computations to recursively process it. This can cause // further scalarization. for (auto EltPtr : ElementAddrs) collectTupleElementUses(cast<TupleElementAddrInst>(EltPtr), BaseEltNo); } }