/// Returns true if FirstIV is a SILArgument or SILInstruction in a BB that /// dominates the BB of A. static bool dominatesArgument(DominanceInfo *DI, SILArgument *A, SILValue FirstIV) { SILBasicBlock *OtherBB = FirstIV->getParentBlock(); if (!OtherBB || OtherBB == A->getParent()) return false; return DI->dominates(OtherBB, A->getParent()); }
/// TODO: Refactor this code so the decision on whether or not to accept an /// instruction. bool swift::getFinalReleasesForValue(SILValue V, ReleaseTracker &Tracker) { llvm::SmallPtrSet<SILBasicBlock *, 16> LiveIn; llvm::SmallPtrSet<SILBasicBlock *, 16> UseBlocks; // First attempt to get the BB where this value resides. auto *DefBB = V->getParentBlock(); if (!DefBB) return false; bool seenRelease = false; SILInstruction *OneRelease = nullptr; // We'll treat this like a liveness problem where the value is the def. Each // block that has a use of the value has the value live-in unless it is the // block with the value. for (auto *UI : V->getUses()) { auto *User = UI->getUser(); auto *BB = User->getParent(); if (!Tracker.isUserAcceptable(User)) return false; Tracker.trackUser(User); if (BB != DefBB) LiveIn.insert(BB); // Also keep track of the blocks with uses. UseBlocks.insert(BB); // Try to speed up the trivial case of single release/dealloc. if (isa<StrongReleaseInst>(User) || isa<DeallocBoxInst>(User)) { if (!seenRelease) OneRelease = User; else OneRelease = nullptr; seenRelease = true; } } // Only a single release/dealloc? We're done! if (OneRelease) { Tracker.trackLastRelease(OneRelease); return true; } propagateLiveness(LiveIn, DefBB); // Now examine each block we saw a use in. If it has no successors // that are in LiveIn, then the last use in the block is the final // release/dealloc. for (auto *BB : UseBlocks) if (!successorHasLiveIn(BB, LiveIn)) if (!addLastUse(V, BB, Tracker)) return false; return true; }
/// Copy the array load to the insert point. static SILValue copyArrayLoad(SILValue ArrayStructValue, SILInstruction *InsertBefore, DominanceInfo *DT) { if (DT->dominates(ArrayStructValue->getParentBlock(), InsertBefore->getParent())) return ArrayStructValue; auto *LI = cast<LoadInst>(ArrayStructValue); // Recursively move struct_element_addr. ValueBase *Val = LI->getOperand(); auto *InsertPt = InsertBefore; while (!DT->dominates(Val->getParentBlock(), InsertBefore->getParent())) { auto *Inst = cast<StructElementAddrInst>(Val); Inst->moveBefore(InsertPt); Val = Inst->getOperand(); InsertPt = Inst; } return cast<LoadInst>(LI->clone(InsertBefore)); }
// Find the final releases of the alloc_box along any given path. // These can include paths from a release back to the alloc_box in a // loop. static bool getFinalReleases(SILValue Box, llvm::SmallVectorImpl<SILInstruction *> &Releases) { llvm::SmallPtrSet<SILBasicBlock*, 16> LiveIn; llvm::SmallPtrSet<SILBasicBlock*, 16> UseBlocks; auto *DefBB = Box->getParentBlock(); auto seenRelease = false; SILInstruction *OneRelease = nullptr; // We'll treat this like a liveness problem where the alloc_box is // the def. Each block that has a use of the owning pointer has the // value live-in unless it is the block with the alloc_box. llvm::SmallVector<Operand *, 32> Worklist(Box->use_begin(), Box->use_end()); while (!Worklist.empty()) { auto *Op = Worklist.pop_back_val(); auto *User = Op->getUser(); auto *BB = User->getParent(); if (isa<ProjectBoxInst>(User)) continue; if (BB != DefBB) LiveIn.insert(BB); // Also keep track of the blocks with uses. UseBlocks.insert(BB); // If we have a copy value or a mark_uninitialized, add its uses to the work // list and continue. if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User)) { copy(cast<SingleValueInstruction>(User)->getUses(), std::back_inserter(Worklist)); continue; } // Try to speed up the trivial case of single release/dealloc. if (isa<StrongReleaseInst>(User) || isa<DeallocBoxInst>(User) || isa<DestroyValueInst>(User)) { if (!seenRelease) OneRelease = User; else OneRelease = nullptr; seenRelease = true; } } // Only a single release/dealloc? We're done! if (OneRelease) { Releases.push_back(OneRelease); return true; } propagateLiveness(LiveIn, DefBB); // Now examine each block we saw a use in. If it has no successors // that are in LiveIn, then the last use in the block is the final // release/dealloc. for (auto *BB : UseBlocks) if (!successorHasLiveIn(BB, LiveIn)) if (!addLastRelease(Box, BB, Releases)) return false; return true; }
LinearLifetimeError swift::valueHasLinearLifetime( SILValue value, ArrayRef<BranchPropagatedUser> consumingUses, ArrayRef<BranchPropagatedUser> nonConsumingUses, SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks, DeadEndBlocks &deBlocks, ErrorBehaviorKind errorBehavior, SmallVectorImpl<SILBasicBlock *> *leakingBlocks) { assert(!consumingUses.empty() && "Must have at least one consuming user?!"); State state(value, visitedBlocks, errorBehavior, leakingBlocks); // First add our non-consuming uses and their blocks to the // blocksWithNonConsumingUses map. While we do this, if we have multiple uses // in the same block, we only accept the last use since from a liveness // perspective that is all we care about. state.initializeAllNonConsumingUses(nonConsumingUses); // Then, we go through each one of our consuming users performing the // following operation: // // 1. Verifying that no two consuming users are in the same block. This // is accomplished by adding the user blocks to the blocksWithConsumingUsers // list. This avoids double consumes. // // 2. Verifying that no predecessor is a block with a consuming use. The // reason why this is necessary is because we wish to not add elements to the // worklist twice. Thus we want to check if we have already visited a // predecessor. SmallVector<BrPropUserAndBlockPair, 32> predsToAddToWorklist; state.initializeAllConsumingUses(consumingUses, predsToAddToWorklist); // If we have a singular consuming use and it is in the same block as value's // def, we bail early. Any use-after-frees due to non-consuming uses would // have been detected by initializing our consuming uses. So we are done. if (consumingUses.size() == 1 && consumingUses[0].getParent() == value->getParentBlock()) { return state.error; } // Ok, we may have multiple consuming uses. Add the user block of each of our // consuming users to the visited list since we do not want them to be added // to the successors to visit set. for (const auto &i : consumingUses) { state.visitedBlocks.insert(i.getParent()); } // Now that we have marked all of our producing blocks, we go through our // predsToAddToWorklist list and add our preds, making sure that none of these // preds are in blocksWithConsumingUses. This is important so that we do not // need to re-process. for (auto pair : predsToAddToWorklist) { BranchPropagatedUser user = pair.first; SILBasicBlock *predBlock = pair.second; // Make sure that the predecessor is not in our blocksWithConsumingUses // list. state.checkPredsForDoubleConsume(user, predBlock); if (!state.visitedBlocks.insert(predBlock).second) continue; state.worklist.push_back(predBlock); } // Now that our algorithm is completely prepared, run the // dataflow... If we find a failure, return false. state.performDataflow(deBlocks); // ...and then check that the end state shows that we have a valid linear // typed value. state.checkDataflowEndState(deBlocks); return state.error; }
bool RCIdentityFunctionInfo:: findDominatingNonPayloadedEdge(SILBasicBlock *IncomingEdgeBB, SILValue RCIdentity) { // First grab the NonPayloadedEnumBB and RCIdentityBB. If we cannot find // either of them, return false. SILBasicBlock *RCIdentityBB = RCIdentity->getParentBlock(); if (!RCIdentityBB) return false; // Make sure that the incoming edge bb is not the RCIdentityBB. We are not // trying to handle this case here, so simplify by just bailing if we detect // it. // // I think the only way this can happen is if we have a switch_enum of some // sort with multiple incoming values going into the destination BB. We are // not interested in handling that case anyways. // // FIXME: If we ever split all critical edges, this should be relooked at. if (IncomingEdgeBB == RCIdentityBB) return false; // Now we know that RCIdentityBB and IncomingEdgeBB are different. Prove that // RCIdentityBB dominates IncomingEdgeBB. SILFunction *F = RCIdentityBB->getParent(); // First make sure that IncomingEdgeBB dominates NonPayloadedEnumBB. If not, // return false. DominanceInfo *DI = DA->get(F); if (!DI->dominates(RCIdentityBB, IncomingEdgeBB)) return false; // Now walk up the dominator tree from IncomingEdgeBB to RCIdentityBB and see // if we can find a use of RCIdentity that dominates IncomingEdgeBB and // enables us to know that RCIdentity must be a no-payload enum along // IncomingEdge. We don't care if the case or enum of RCIdentity match the // case or enum along RCIdentityBB since a pairing of retain, release of two // non-payloaded enums can always be eliminated (since we can always eliminate // ref count operations on non-payloaded enums). // RCIdentityBB must have a valid dominator tree node. auto *EndDomNode = DI->getNode(RCIdentityBB); if (!EndDomNode) return false; for (auto *Node = DI->getNode(IncomingEdgeBB); Node; Node = Node->getIDom()) { // Search for uses of RCIdentity in Node->getBlock() that will enable us to // know that it has a non-payloaded enum case. SILBasicBlock *DominatingBB = Node->getBlock(); llvm::Optional<bool> Result = proveNonPayloadedEnumCase(DominatingBB, RCIdentity); // If we found either a signal of a payloaded or a non-payloaded enum, // return that value. if (Result.hasValue()) return Result.getValue(); // If we didn't reach RCIdentityBB, keep processing up the DomTree. if (DominatingBB != RCIdentityBB) continue; // Otherwise, we failed to find any interesting information, return false. return false; } return false; }