/// Return true if this copy can be eliminated through Named Return Value /// Optimization (NRVO). /// /// Simple NRVO cases are handled naturally via backwardPropagateCopy. However, /// general NRVO is not handled via local propagation without global data /// flow. Nonetheless, NRVO is a simple pattern that can be detected using a /// different technique from propagation. /// /// Example: /// func nrvo<T : P>(z : Bool) -> T { /// var rvo : T /// if (z) { /// rvo = T(10) /// } /// else { /// rvo = T(1) /// } /// return rvo /// } /// /// Because of the control flow, backward propagation with a block will fail to /// find the initializer for the copy at "return rvo". Instead, we directly /// check for an NRVO pattern by observing a copy in a return block that is the /// only use of the copy's dest, which must be an @out arg. If there are no /// instructions between the copy and the return that may write to the copy's /// source, we simply replace the source's local stack address with the @out /// address. /// /// The following SIL pattern will be detected: /// /// sil @foo : $@convention(thin) <T> (@out T) -> () { /// bb0(%0 : $*T): /// %2 = alloc_stack $T /// ... // arbitrary control flow, but no other uses of %0 /// bbN: /// copy_addr [take] %2#1 to [initialization] %0 : $*T /// ... // no writes /// return static bool canNRVO(CopyAddrInst *CopyInst) { if (!isa<AllocStackInst>(CopyInst->getSrc())) return false; // The copy's dest must be an indirect SIL argument. Otherwise, it may not // dominate all uses of the source. Worse, it may be aliased. This // optimization will early-initialize the copy dest, so we can't allow aliases // to be accessed between the initialization and the return. auto OutArg = dyn_cast<SILArgument>(CopyInst->getDest()); if (!OutArg || !OutArg->getParameterInfo().isIndirect()) return false; SILBasicBlock *BB = CopyInst->getParent(); if (!isa<ReturnInst>(BB->getTerminator())) return false; SILValue CopyDest = CopyInst->getDest(); if (!hasOneNonDebugUse(CopyDest)) return false; auto SI = CopyInst->getIterator(), SE = BB->end(); for (++SI; SI != SE; ++SI) { if (SI->mayWriteToMemory() && !isa<DeallocationInst>(SI)) return false; } return true; }
/// \brief Splits a basic block into two at the specified instruction. /// /// Note that all the instructions BEFORE the specified iterator /// stay as part of the original basic block. The old basic block is left /// without a terminator. SILBasicBlock *SILBasicBlock::split(iterator I) { SILBasicBlock *New = new (Parent->getModule()) SILBasicBlock(Parent, this, /*after*/true); // Move all of the specified instructions from the original basic block into // the new basic block. New->InstList.splice(New->end(), InstList, I, end()); return New; }
/// \brief Splits a basic block into two at the specified instruction. /// /// Note that all the instructions BEFORE the specified iterator /// stay as part of the original basic block. The old basic block is left /// without a terminator. SILBasicBlock *SILBasicBlock::splitBasicBlock(iterator I) { SILBasicBlock *New = new (Parent->getModule()) SILBasicBlock(Parent); SILFunction::iterator Where = std::next(SILFunction::iterator(this)); SILFunction::iterator First = SILFunction::iterator(New); if (Where != First) Parent->getBlocks().splice(Where, Parent->getBlocks(), First); // Move all of the specified instructions from the original basic block into // the new basic block. New->InstList.splice(New->end(), InstList, I, end()); return New; }
/// Walk backwards from an unsafeGuaranteedEnd builtin instruction looking for a /// release on the reference returned by the matching unsafeGuaranteed builtin /// ignoring releases on the way. /// /// %4 = builtin "unsafeGuaranteed"<Foo>(%0 : $Foo) : $(Foo, Builtin.Int8) /// %5 = tuple_extract %4 : $(Foo, Builtin.Int8), 0 /// %6 = tuple_extract %4 : $(Foo, Builtin.Int8), 1 /// strong_release %5 : $Foo // <-- Matching release. /// strong_release %6 : $Foo // Ignore. /// %12 = builtin "unsafeGuaranteedEnd"(%6 : $Builtin.Int8) : $() /// static SILBasicBlock::iterator findReleaseToMatchUnsafeGuaranteedValue(SILInstruction *UnsafeGuaranteedEndI, SILValue UnsafeGuaranteedValue, SILBasicBlock &BB) { auto UnsafeGuaranteedEndIIt = SILBasicBlock::iterator(UnsafeGuaranteedEndI); if (UnsafeGuaranteedEndIIt == BB.begin()) return BB.end(); auto LastReleaseIt = std::prev(UnsafeGuaranteedEndIIt); while (LastReleaseIt != BB.begin() && (isa<StrongReleaseInst>(*LastReleaseIt) || isa<ReleaseValueInst>(*LastReleaseIt)) && LastReleaseIt->getOperand(0) != UnsafeGuaranteedValue) --LastReleaseIt; if ((!isa<StrongReleaseInst>(*LastReleaseIt) && !isa<ReleaseValueInst>(*LastReleaseIt)) || LastReleaseIt->getOperand(0) != UnsafeGuaranteedValue) { return BB.end(); } return LastReleaseIt; }
bool StackPromoter::promote() { llvm::SetVector<SILBasicBlock *> ReachableBlocks; // First step: find blocks which end up in a no-return block (terminated by // an unreachable instruction). // Search for function-exiting blocks, i.e. return and throw. for (SILBasicBlock &BB : *F) { TermInst *TI = BB.getTerminator(); if (TI->isFunctionExiting()) ReachableBlocks.insert(&BB); } // Propagate the reachability up the control flow graph. unsigned Idx = 0; while (Idx < ReachableBlocks.size()) { SILBasicBlock *BB = ReachableBlocks[Idx++]; for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) ReachableBlocks.insert(Pred); } bool Changed = false; // Search the whole function for stack promotable allocations. for (SILBasicBlock &BB : *F) { // Don't stack promote any allocation inside a code region which ends up in // a no-return block. Such allocations may missing their final release. // We would insert the deallocation too early, which may result in a // use-after-free problem. if (ReachableBlocks.count(&BB) == 0) continue; for (auto Iter = BB.begin(); Iter != BB.end();) { // The allocation instruction may be moved, so increment Iter prior to // doing the optimization. SILInstruction *I = &*Iter++; if (auto *ARI = dyn_cast<AllocRefInst>(I)) { Changed |= tryPromoteAlloc(ARI); } } } return Changed; }
void ARCRegionState::addInterestingInst(SILInstruction *TargetInst) { // Insert I into its location in the interesting instruction list. SILBasicBlock *BB = getRegion()->getBlock(); assert(TargetInst->getParent() == BB); auto II = BB->begin(); auto IE = BB->end(); assert(II != IE && "I can not be an element of an empty block"); auto SI = SummarizedInterestingInsts.begin(); auto SE = SummarizedInterestingInsts.end(); while (II != IE) { if (SI == SE) { // Ok, TargetInst is after all of the interesting insts. Append it to the // list. SummarizedInterestingInsts.push_back(TargetInst); return; } // Move II down the block until it hits TargetInst or the first // SummarizedInterestingInst. while (&*II != *SI && &*II != TargetInst) { ++II; } // If II == SI and TargetInst == II then there is nothing further to do. if (&*II == TargetInst) { assert(&*II != *SI); SummarizedInterestingInsts.insert(SI, TargetInst); return; } // If we reach this point, then we know that II == SI and we have not found // TargetInst yet. So we move to the next II, SI. ++II; ++SI; } llvm_unreachable("Could not find Inst in the block?!"); }
/// Perform outlining on the function and return any newly created outlined /// functions. bool tryOutline(SILOptFunctionBuilder &FuncBuilder, SILFunction *Fun, SmallVectorImpl<SILFunction *> &FunctionsAdded) { SmallPtrSet<SILBasicBlock *, 32> Visited; SmallVector<SILBasicBlock *, 128> Worklist; OutlinePatterns patterns(FuncBuilder); // Traverse the function. Worklist.push_back(&*Fun->begin()); while (!Worklist.empty()) { SILBasicBlock *CurBlock = Worklist.pop_back_val(); if (!Visited.insert(CurBlock).second) continue; SILBasicBlock::iterator CurInst = CurBlock->begin(); // Go over the instructions trying to match and replace patterns. while (CurInst != CurBlock->end()) { if (OutlinePattern *match = patterns.tryToMatch(CurInst)) { SILFunction *F; SILBasicBlock::iterator LastInst; std::tie(F, LastInst) = match->outline(Fun->getModule()); if (F) FunctionsAdded.push_back(F); CurInst = LastInst; assert(LastInst->getParent() == CurBlock); } else if (isa<TermInst>(CurInst)) { std::copy(CurBlock->succ_begin(), CurBlock->succ_end(), std::back_inserter(Worklist)); ++CurInst; } else { ++CurInst; } } } return false; }
void AllocOptimize:: computeAvailableValuesFrom(SILBasicBlock::iterator StartingFrom, SILBasicBlock *BB, llvm::SmallBitVector &RequiredElts, SmallVectorImpl<std::pair<SILValue, unsigned>> &Result, llvm::SmallDenseMap<SILBasicBlock*, llvm::SmallBitVector, 32> &VisitedBlocks, llvm::SmallBitVector &ConflictingValues) { assert(!RequiredElts.none() && "Scanning with a goal of finding nothing?"); // If there is a potential modification in the current block, scan the block // to see if the store or escape is before or after the load. If it is // before, check to see if it produces the value we are looking for. if (HasLocalDefinition.count(BB)) { for (SILBasicBlock::iterator BBI = StartingFrom; BBI != BB->begin();) { SILInstruction *TheInst = &*std::prev(BBI); // If this instruction is unrelated to the element, ignore it. if (!NonLoadUses.count(TheInst)) { --BBI; continue; } // Given an interesting instruction, incorporate it into the set of // results, and filter down the list of demanded subelements that we still // need. updateAvailableValues(TheInst, RequiredElts, Result, ConflictingValues); // If this satisfied all of the demanded values, we're done. if (RequiredElts.none()) return; // Otherwise, keep scanning the block. If the instruction we were looking // at just got exploded, don't skip the next instruction. if (&*std::prev(BBI) == TheInst) --BBI; } } // Otherwise, we need to scan up the CFG looking for available values. for (auto PI = BB->pred_begin(), E = BB->pred_end(); PI != E; ++PI) { SILBasicBlock *PredBB = *PI; // If the predecessor block has already been visited (potentially due to a // cycle in the CFG), don't revisit it. We can do this safely because we // are optimistically assuming that all incoming elements in a cycle will be // the same. If we ever detect a conflicting element, we record it and do // not look at the result. auto Entry = VisitedBlocks.insert({PredBB, RequiredElts}); if (!Entry.second) { // If we are revisiting a block and asking for different required elements // then anything that isn't agreeing is in conflict. const auto &PrevRequired = Entry.first->second; if (PrevRequired != RequiredElts) { ConflictingValues |= (PrevRequired ^ RequiredElts); RequiredElts &= ~ConflictingValues; if (RequiredElts.none()) return; } continue; } // Make sure to pass in the same set of required elements for each pred. llvm::SmallBitVector Elts = RequiredElts; computeAvailableValuesFrom(PredBB->end(), PredBB, Elts, Result, VisitedBlocks, ConflictingValues); // If we have any conflicting values, don't bother searching for them. RequiredElts &= ~ConflictingValues; if (RequiredElts.none()) return; } }
void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) { DEBUG(llvm::dbgs() << "*** Promoting in-block: " << *ASI); SILBasicBlock *BB = ASI->getParent(); // The default value of the AllocStack is NULL because we don't have // uninitialized variables in Swift. SILValue RunningVal = SILValue(); // For all instructions in the block. for (auto BBI = BB->begin(), E = BB->end(); BBI != E;) { SILInstruction *Inst = &*BBI; ++BBI; // Remove instructions that we are loading from. Replace the loaded value // with our running value. if (isLoadFromStack(Inst, ASI)) { if (!RunningVal) { assert(ASI->getElementType().isVoid() && "Expected initialization of non-void type!"); RunningVal = SILUndef::get(ASI->getElementType(), ASI->getModule()); } replaceLoad(cast<LoadInst>(Inst), RunningVal, ASI); NumInstRemoved++; continue; } // Remove stores and record the value that we are saving as the running // value. if (auto *SI = dyn_cast<StoreInst>(Inst)) { if (SI->getDest() == ASI) { RunningVal = SI->getSrc(); Inst->eraseFromParent(); NumInstRemoved++; continue; } } // Replace debug_value_addr with debug_value of the promoted value. if (auto *DVAI = dyn_cast<DebugValueAddrInst>(Inst)) { if (DVAI->getOperand() == ASI) { if (RunningVal) { promoteDebugValueAddr(DVAI, RunningVal, B); } else { // Drop debug_value_addr of uninitialized void values. assert(ASI->getElementType().isVoid() && "Expected initialization of non-void type!"); DVAI->eraseFromParent(); } } continue; } // Replace destroys with a release of the value. if (auto *DAI = dyn_cast<DestroyAddrInst>(Inst)) { if (DAI->getOperand() == ASI) { replaceDestroy(DAI, RunningVal); } continue; } // Remove deallocation. if (auto *DSI = dyn_cast<DeallocStackInst>(Inst)) { if (DSI->getOperand() == ASI) { Inst->eraseFromParent(); NumInstRemoved++; // No need to continue scanning after deallocation. break; } } SILValue InstVal = Inst; // Remove dead address instructions that may be uses of the allocation. while (InstVal->use_empty() && (isa<StructElementAddrInst>(InstVal) || isa<TupleElementAddrInst>(InstVal))) { SILInstruction *I = cast<SILInstruction>(InstVal); InstVal = I->getOperand(0); I->eraseFromParent(); NumInstRemoved++; } } }
bool StackPromoter::canPromoteAlloc(SILInstruction *AI, SILInstruction *&AllocInsertionPoint, SILInstruction *&DeallocInsertionPoint) { AllocInsertionPoint = nullptr; DeallocInsertionPoint = nullptr; auto *Node = ConGraph->getNodeOrNull(AI, EA); if (!Node) return false; // The most important check: does the object escape the current function? if (Node->escapes()) return false; // Now we have to determine the lifetime of the allocated object in its // function. // Get all interesting uses of the object (e.g. release instructions). This // includes uses of objects where the allocation is stored to. int NumUsePointsToFind = ConGraph->getNumUsePoints(Node); if (NumUsePointsToFind == 0) { // There should always be at least one release for an allocated object. // But in case all paths from this block end in unreachable then the // final release of the object may be optimized away. We bail out in this // case. return false; } // In the following we check two requirements for stack promotion: // 1) Are all uses in the same control region as the alloc? E.g. if the // allocation is in a loop then there may not be any uses of the object // outside the loop. // 2) We need to find an insertion place for the deallocation so that it // preserves a properly nested stack allocation-deallocation structure. SILBasicBlock *StartBlock = AI->getParent(); // The block where we assume we can insert the deallocation. SILBasicBlock *EndBlock = StartBlock; // We visit all instructions starting at the allocation instruction. WorkListType WorkList; // It's important that the EndBlock is at the head of the WorkList so that // we handle it after all other blocks. WorkList.insert(EndBlock, -1); WorkList.insert(StartBlock, 0); for (;;) { SILBasicBlock *BB = WorkList.pop_back_val(); int StackDepth = 0; SILBasicBlock::iterator Iter; if (BB == StartBlock) { // In the first block we start at the allocation instruction and not at // the begin of the block. Iter = AI->getIterator(); } else { // Track all uses in the block arguments. for (SILArgument *BBArg : BB->getBBArgs()) { if (ConGraph->isUsePoint(BBArg, Node)) NumUsePointsToFind--; } // Make sure that the EndBlock is not inside a loop (which does not // contain the StartBlock). // E.g.: // %obj = alloc_ref // the allocation // br loop // loop: // the_only_use_of_obj(%obj) // cond_br ..., loop, exit // exit: // ... // this is the new EndBlock for (SILBasicBlock *Pred : BB->getPreds()) { // Extend the lifetime region until the EndBlock post dominates the // StartBlock. while (!strictlyPostDominates(EndBlock, Pred)) { EndBlock = getImmediatePostDom(EndBlock); if (!EndBlock) return false; } } Iter = BB->begin(); StackDepth = WorkList.getStackDepth(BB); } // Visit all instructions of the current block. while (Iter != BB->end()) { SILInstruction &I = *Iter++; if (BB == EndBlock && StackDepth == 0 && NumUsePointsToFind == 0) { // We found a place to insert the stack deallocation. DeallocInsertionPoint = &I; return true; } if (I.isAllocatingStack()) { StackDepth++; } else if (I.isDeallocatingStack()) { if (StackDepth == 0) { // The allocation is inside a stack alloc-dealloc region and we are // now leaving this region without having found a place for the // deallocation. E.g. // E.g.: // %1 = alloc_stack // %obj = alloc_ref // the allocation // dealloc_stack %1 // use_of_obj(%obj) // // In this case we can move the alloc_ref before the alloc_stack // to fix the nesting. if (!isa<AllocRefInst>(AI)) return false; auto *Alloc = dyn_cast<SILInstruction>(I.getOperand(0)); if (!Alloc) return false; // This should always be the case, but let's be on the safe side. if (!PDT->dominates(StartBlock, Alloc->getParent())) return false; AllocInsertionPoint = Alloc; StackDepth++; } StackDepth--; } // Track a use. if (ConGraph->isUsePoint(&I, Node) != 0) NumUsePointsToFind--; } if (WorkList.empty()) { if (EndBlock == BB) { // We reached the EndBlock but didn't find a place for the deallocation // so far (because we didn't find all uses yet or we entered another // stack alloc-dealloc region). Let's extend our lifetime region. // E.g.: // %obj = alloc_ref // the allocation // %1 = alloc_stack // use_of_obj(%obj) // can't insert the deallocation in this block // cond_br ..., bb1, bb2 // bb1: // ... // br bb2 // bb2: // dealloc_stack %1 // this is the new EndBlock EndBlock = getImmediatePostDom(EndBlock); if (!EndBlock) return false; } // Again, it's important that the EndBlock is the first in the WorkList. WorkList.insert(EndBlock, -1); } // Push the successor blocks to the WorkList. for (SILBasicBlock *Succ : BB->getSuccessors()) { if (!strictlyDominates(StartBlock, Succ)) { // The StartBlock is inside a loop but we couldn't find a deallocation // place in this loop, e.g. because there are uses outside the loop. // E.g.: // %container = alloc_ref // br loop // loop: // %obj = alloc_ref // the allocation // store %obj to %some_field_in_container // cond_br ..., loop, exit // exit: // use(%container) return false; } WorkList.insert(Succ, StackDepth); } } }
/// \brief Issue an "unreachable code" diagnostic if the blocks contains or /// leads to another block that contains user code. /// /// Note, we rely on SILLocation information to determine if SILInstructions /// correspond to user code. static bool diagnoseUnreachableBlock(const SILBasicBlock &B, SILModule &M, const SILBasicBlockSet &Reachable, UnreachableUserCodeReportingState *State, const SILBasicBlock *TopLevelB, llvm::SmallPtrSetImpl<const SILBasicBlock*> &Visited){ if (Visited.count(&B)) return false; Visited.insert(&B); assert(State); for (auto I = B.begin(), E = B.end(); I != E; ++I) { SILLocation Loc = I->getLoc(); // If we've reached an implicit return, we have not found any user code and // can stop searching for it. if (Loc.is<ImplicitReturnLocation>() || Loc.isAutoGenerated()) return false; // Check if the instruction corresponds to user-written code, also make // sure we don't report an error twice for the same instruction. if (isUserCode(&*I) && !State->BlocksWithErrors.count(&B)) { // Emit the diagnostic. auto BrInfoIter = State->MetaMap.find(TopLevelB); assert(BrInfoIter != State->MetaMap.end()); auto BrInfo = BrInfoIter->second; switch (BrInfo.Kind) { case (UnreachableKind::FoldedBranch): // Emit the diagnostic on the unreachable block and emit the // note on the branch responsible for the unreachable code. diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::unreachable_code_branch, BrInfo.CondIsAlwaysTrue); break; case (UnreachableKind::FoldedSwitchEnum): { // If we are warning about a switch condition being a constant, the main // emphasis should be on the condition (to ensure we have a single // message per switch). const SwitchStmt *SS = BrInfo.Loc.getAsASTNode<SwitchStmt>(); if (!SS) break; assert(SS); const Expr *SE = SS->getSubjectExpr(); diagnose(M.getASTContext(), SE->getLoc(), diag::switch_on_a_constant); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code_note); break; } case (UnreachableKind::NoreturnCall): { // Specialcase when we are warning about unreachable code after a call // to a noreturn function. if (!BrInfo.Loc.isASTNode<ExplicitCastExpr>()) { assert(BrInfo.Loc.isASTNode<ApplyExpr>()); diagnose(M.getASTContext(), Loc.getSourceLoc(), diag::unreachable_code); diagnose(M.getASTContext(), BrInfo.Loc.getSourceLoc(), diag::call_to_noreturn_note); } break; } } // Record that we've reported this unreachable block to avoid duplicates // in the future. State->BlocksWithErrors.insert(&B); return true; } } // This block could be empty if it's terminator has been folded. if (B.empty()) return false; // If we have not found user code in this block, inspect it's successors. // Check if at least one of the successors contains user code. for (auto I = B.succ_begin(), E = B.succ_end(); I != E; ++I) { SILBasicBlock *SB = *I; bool HasReachablePred = false; for (auto PI = SB->pred_begin(), PE = SB->pred_end(); PI != PE; ++PI) { if (Reachable.count(*PI)) HasReachablePred = true; } // If all of the predecessors of this successor are unreachable, check if // it contains user code. if (!HasReachablePred && diagnoseUnreachableBlock(*SB, M, Reachable, State, TopLevelB, Visited)) return true; } return false; }
static bool simplifyBlocksWithCallsToNoReturn(SILBasicBlock &BB, UnreachableUserCodeReportingState *State) { auto I = BB.begin(), E = BB.end(); bool DiagnosedUnreachableCode = false; SILInstruction *NoReturnCall = nullptr; // Collection of all instructions that should be deleted. llvm::SmallVector<SILInstruction*, 32> ToBeDeleted; // If all of the predecessor blocks end in a try_apply to a noreturn // function, the entire block is dead. NoReturnCall = getPrecedingCallToNoReturn(BB); // Does this block contain a call to a noreturn function? while (I != E) { auto *CurrentInst = &*I; // Move the iterator before we remove instructions to avoid iterator // invalidation issues. ++I; // Remove all instructions following the noreturn call. if (NoReturnCall) { // We will need to delete the instruction later on. ToBeDeleted.push_back(CurrentInst); // Diagnose the unreachable code within the same block as the call to // noreturn. if (isUserCode(CurrentInst) && !DiagnosedUnreachableCode) { if (NoReturnCall->getLoc().is<RegularLocation>()) { if (!NoReturnCall->getLoc().isASTNode<ExplicitCastExpr>()) { diagnose(BB.getModule().getASTContext(), CurrentInst->getLoc().getSourceLoc(), diag::unreachable_code); diagnose(BB.getModule().getASTContext(), NoReturnCall->getLoc().getSourceLoc(), diag::call_to_noreturn_note); DiagnosedUnreachableCode = true; } } } // We are going to bluntly remove these instructions. Change uses in // different basic blocks to undef. This is safe because all control flow // created by transparent inlining of functions applications after a call // to a 'noreturn' function is control dependent on the call to the // noreturn function and therefore dead. setOutsideBlockUsesToUndef(CurrentInst); NumInstructionsRemoved++; continue; } // Check if this instruction is the first call to noreturn in this block. if (!NoReturnCall) { NoReturnCall = getAsCallToNoReturn(CurrentInst); } } if (!NoReturnCall) return false; // If the call is to the 'unreachable' builtin, then remove the call, // as it is redundant with the actual unreachable terminator. if (auto Builtin = dyn_cast<BuiltinInst>(NoReturnCall)) { if (Builtin->getName().str() == "unreachable") ToBeDeleted.push_back(NoReturnCall); } // Record the diagnostic info. if (!DiagnosedUnreachableCode && NoReturnCall->getLoc().is<RegularLocation>() && State){ for (auto SI = BB.succ_begin(), SE = BB.succ_end(); SI != SE; ++SI) { SILBasicBlock *UnreachableBlock = *SI; if (!State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { // If this is the first time we see this unreachable block, store it // along with the noreturn call info. State->PossiblyUnreachableBlocks.insert(UnreachableBlock); State->MetaMap.insert( std::pair<const SILBasicBlock*, UnreachableInfo>( UnreachableBlock, UnreachableInfo{UnreachableKind::NoreturnCall, NoReturnCall->getLoc(), true })); } } } recursivelyDeleteTriviallyDeadInstructions(ToBeDeleted, true); NumInstructionsRemoved += ToBeDeleted.size(); // Add an unreachable terminator. The terminator has an invalid source // location to signal to the DataflowDiagnostic pass that this code does // not correspond to user code. SILBuilder B(&BB); B.createUnreachable(ArtificialUnreachableLocation()); return true; }
bool CSE::processNode(DominanceInfoNode *Node) { SILBasicBlock *BB = Node->getBlock(); bool Changed = false; // See if any instructions in the block can be eliminated. If so, do it. If // not, add them to AvailableValues. for (SILBasicBlock::iterator I = BB->begin(), E = BB->end(); I != E;) { SILInstruction *Inst = &*I; ++I; DEBUG(llvm::dbgs() << "SILCSE VISITING: " << *Inst << "\n"); // Dead instructions should just be removed. if (isInstructionTriviallyDead(Inst)) { DEBUG(llvm::dbgs() << "SILCSE DCE: " << *Inst << '\n'); eraseFromParentWithDebugInsts(Inst, I); Changed = true; ++NumSimplify; continue; } // If the instruction can be simplified (e.g. X+0 = X) then replace it with // its simpler value. if (SILValue V = simplifyInstruction(Inst)) { DEBUG(llvm::dbgs() << "SILCSE SIMPLIFY: " << *Inst << " to: " << *V << '\n'); Inst->replaceAllUsesWith(V); Inst->eraseFromParent(); Changed = true; ++NumSimplify; continue; } // If this is not a simple instruction that we can value number, skip it. if (!canHandle(Inst)) continue; // If an instruction can be handled here, then it must also be handled // in isIdenticalTo, otherwise looking up a key in the map with fail to // match itself. assert(Inst->isIdenticalTo(Inst) && "Inst must match itself for map to work"); // Now that we know we have an instruction we understand see if the // instruction has an available value. If so, use it. if (ValueBase *V = AvailableValues->lookup(Inst)) { DEBUG(llvm::dbgs() << "SILCSE CSE: " << *Inst << " to: " << *V << '\n'); // Instructions producing a new opened archetype need a special handling, // because replacing these instructions may require a replacement // of the opened archetype type operands in some of the uses. if (!isa<OpenExistentialRefInst>(Inst) || processOpenExistentialRef(Inst, V, I)) { Inst->replaceAllUsesWith(V); Inst->eraseFromParent(); Changed = true; ++NumCSE; continue; } } // Otherwise, just remember that this value is available. AvailableValues->insert(Inst, Inst); DEBUG(llvm::dbgs() << "SILCSE Adding to value table: " << *Inst << " -> " << *Inst << "\n"); } return Changed; }
/// Remove redundant checks in a basic block. This pass will reset the state /// after an instruction that may modify any array allowing removal of redundant /// checks up to that point and after that point. static bool removeRedundantChecksInBlock(SILBasicBlock &BB, ArraySet &Arrays, RCIdentityFunctionInfo *RCIA) { ABCAnalysis ABC(false, Arrays, RCIA); IndexedArraySet RedundantChecks; bool Changed = false; DEBUG(llvm::dbgs() << "Removing in BB\n"); DEBUG(BB.dump()); // Process all instructions in the current block. for (auto Iter = BB.begin(); Iter != BB.end();) { auto Inst = &*Iter; ++Iter; ABC.analyse(Inst); if (ABC.clearArraysUnsafeFlag()) { // Any array may be modified -> forget everything. This is just a // shortcut to the isUnsafe test for a specific array below. RedundantChecks.clear(); continue; } // Is this a check_bounds. ArraySemanticsCall ArrayCall(Inst); auto Kind = ArrayCall.getKind(); if (Kind != ArrayCallKind::kCheckSubscript && Kind != ArrayCallKind::kCheckIndex) { DEBUG(llvm::dbgs() << " not a check_bounds call " << *Inst); continue; } auto Array = ArrayCall.getSelf(); // Get the underlying array pointer. Array = getArrayStructPointer(Kind, Array); // Is this an unsafe array whose size could have been changed? if (ABC.isUnsafe(Array)) { DEBUG(llvm::dbgs() << " not a safe array argument " << *Array); continue; } // Get the array index. auto ArrayIndex = ArrayCall.getIndex(); if (!ArrayIndex) continue; auto IndexedArray = getArrayIndexPair(Array, ArrayIndex, Kind); DEBUG(llvm::dbgs() << " IndexedArray: " << *Array << " and " << *ArrayIndex); // Saw a check for the first time. if (!RedundantChecks.count(IndexedArray)) { DEBUG(llvm::dbgs() << " first time: " << *Inst << " with array argument: " << *Array); RedundantChecks.insert(IndexedArray); continue; } // Remove the bounds check. ArrayCall.removeCall(); Changed = true; } return Changed; }
SILInstruction *StackPromoter::findDeallocPoint(SILInstruction *StartInst, SILInstruction *&RestartPoint, EscapeAnalysis::CGNode *Node, int NumUsePointsToFind) { // In the following we check two requirements for stack promotion: // 1) Are all uses in the same control region as the alloc? E.g. if the // allocation is in a loop then there may not be any uses of the object // outside the loop. // 2) We need to find an insertion place for the deallocation so that it // preserves a properly nested stack allocation-deallocation structure. SILBasicBlock *StartBlock = StartInst->getParent(); // The block where we assume we can insert the deallocation. SILBasicBlock *EndBlock = StartBlock; // We visit all instructions starting at the allocation instruction. WorkListType WorkList; // It's important that the EndBlock is at the head of the WorkList so that // we handle it after all other blocks. WorkList.insert(EndBlock, -1); WorkList.insert(StartBlock, 0); for (;;) { SILBasicBlock *BB = WorkList.pop_back_val(); int StackDepth = 0; SILBasicBlock::iterator Iter; if (BB == StartBlock) { // In the first block we start at the allocation instruction and not at // the begin of the block. Iter = StartInst->getIterator(); } else { // Track all uses in the block arguments. for (SILArgument *BBArg : BB->getArguments()) { if (ConGraph->isUsePoint(BBArg, Node)) NumUsePointsToFind--; } // Make sure that the EndBlock is not inside a loop (which does not // contain the StartBlock). // E.g.: // %obj = alloc_ref // the allocation // br loop // loop: // the_only_use_of_obj(%obj) // cond_br ..., loop, exit // exit: // ... // this is the new EndBlock EndBlock = updateEndBlock(BB, EndBlock, WorkList); if (!EndBlock) return nullptr; Iter = BB->begin(); StackDepth = WorkList.getStackDepth(BB); } // Visit all instructions of the current block. while (Iter != BB->end()) { SILInstruction &I = *Iter++; if (BB == EndBlock && StackDepth == 0 && NumUsePointsToFind == 0) { // We found a place to insert the stack deallocation. return &I; } if (I.isAllocatingStack()) { StackDepth++; } else if (I.isDeallocatingStack()) { if (StackDepth == 0) { // The allocation is inside a stack alloc-dealloc region and we are // now leaving this region without having found a place for the // deallocation. E.g. // E.g.: // %1 = alloc_stack // %obj = alloc_ref // the allocation // dealloc_stack %1 // use_of_obj(%obj) // // In this case we can move the alloc_ref before the alloc_stack // to fix the nesting. auto *Alloc = dyn_cast<SILInstruction>(I.getOperand(0)); if (!Alloc) return nullptr; // This should always be the case, but let's be on the safe side. if (!postDominates(StartBlock, Alloc->getParent())) return nullptr; // Trigger another iteration with a new start point; RestartPoint = Alloc; return nullptr; } StackDepth--; } // Track a use. if (ConGraph->isUsePoint(&I, Node) != 0) NumUsePointsToFind--; } if (WorkList.empty()) { if (EndBlock == BB) { // We reached the EndBlock but didn't find a place for the deallocation // so far (because we didn't find all uses yet or we entered another // stack alloc-dealloc region). Let's extend our lifetime region. // E.g.: // %obj = alloc_ref // the allocation // %1 = alloc_stack // use_of_obj(%obj) // can't insert the deallocation in this block // cond_br ..., bb1, bb2 // bb1: // ... // br bb2 // bb2: // dealloc_stack %1 // this is the new EndBlock EndBlock = getImmediatePostDom(EndBlock); if (!EndBlock) return nullptr; } // Again, it's important that the EndBlock is the first in the WorkList. WorkList.insert(EndBlock, -1); } // Push the successor blocks to the WorkList. for (SILBasicBlock *Succ : BB->getSuccessors()) { if (!strictlyDominates(StartBlock, Succ)) { // The StartBlock is inside a loop but we couldn't find a deallocation // place in this loop, e.g. because there are uses outside the loop. // E.g.: // %container = alloc_ref // br loop // loop: // %obj = alloc_ref // the allocation // store %obj to %some_field_in_container // cond_br ..., loop, exit // exit: // use(%container) return nullptr; } WorkList.insert(Succ, StackDepth); } } }
/// Insert monomorphic inline caches for a specific class or metatype /// type \p SubClassTy. static FullApplySite speculateMonomorphicTarget(FullApplySite AI, SILType SubType, CheckedCastBranchInst *&CCBI) { CCBI = nullptr; // Bail if this class_method cannot be devirtualized. if (!canDevirtualizeClassMethod(AI, SubType)) return FullApplySite(); if (SubType.getSwiftRValueType()->hasDynamicSelfType()) return FullApplySite(); // Create a diamond shaped control flow and a checked_cast_branch // instruction that checks the exact type of the object. // This cast selects between two paths: one that calls the slow dynamic // dispatch and one that calls the specific method. auto It = AI.getInstruction()->getIterator(); SILFunction *F = AI.getFunction(); SILBasicBlock *Entry = AI.getParent(); // Iden is the basic block containing the direct call. SILBasicBlock *Iden = F->createBasicBlock(); // Virt is the block containing the slow virtual call. SILBasicBlock *Virt = F->createBasicBlock(); Iden->createPHIArgument(SubType, ValueOwnershipKind::Owned); SILBasicBlock *Continue = Entry->split(It); SILBuilderWithScope Builder(Entry, AI.getInstruction()); // Create the checked_cast_branch instruction that checks at runtime if the // class instance is identical to the SILType. ClassMethodInst *CMI = cast<ClassMethodInst>(AI.getCallee()); CCBI = Builder.createCheckedCastBranch(AI.getLoc(), /*exact*/ true, CMI->getOperand(), SubType, Iden, Virt); It = CCBI->getIterator(); SILBuilderWithScope VirtBuilder(Virt, AI.getInstruction()); SILBuilderWithScope IdenBuilder(Iden, AI.getInstruction()); // This is the class reference downcasted into subclass SubType. SILValue DownCastedClassInstance = Iden->getArgument(0); // Copy the two apply instructions into the two blocks. FullApplySite IdenAI = CloneApply(AI, IdenBuilder); FullApplySite VirtAI = CloneApply(AI, VirtBuilder); // See if Continue has a release on self as the instruction right after the // apply. If it exists, move it into position in the diamond. SILBasicBlock::iterator next = next_or_end(Continue->begin(), Continue->end()); auto *Release = (next == Continue->end()) ? nullptr : dyn_cast<StrongReleaseInst>(next); if (Release && Release->getOperand() == CMI->getOperand()) { VirtBuilder.createStrongRelease(Release->getLoc(), CMI->getOperand(), Release->getAtomicity()); IdenBuilder.createStrongRelease(Release->getLoc(), DownCastedClassInstance, Release->getAtomicity()); Release->eraseFromParent(); } // Create a PHInode for returning the return value from both apply // instructions. SILArgument *Arg = Continue->createPHIArgument(AI.getType(), ValueOwnershipKind::Owned); if (!isa<TryApplyInst>(AI)) { if (AI.getSubstCalleeType()->isNoReturnFunction()) { IdenBuilder.createUnreachable(AI.getLoc()); VirtBuilder.createUnreachable(AI.getLoc()); } else { IdenBuilder.createBranch(AI.getLoc(), Continue, { cast<ApplyInst>(IdenAI) }); VirtBuilder.createBranch(AI.getLoc(), Continue, { cast<ApplyInst>(VirtAI) }); } } // Remove the old Apply instruction. assert(AI.getInstruction() == &Continue->front() && "AI should be the first instruction in the split Continue block"); if (isa<TryApplyInst>(AI)) { AI.getInstruction()->eraseFromParent(); assert(Continue->empty() && "There should not be an instruction after try_apply"); Continue->eraseFromParent(); } else { auto apply = cast<ApplyInst>(AI); apply->replaceAllUsesWith(Arg); apply->eraseFromParent(); assert(!Continue->empty() && "There should be at least a terminator after AI"); } // Update the stats. NumTargetsPredicted++; // Devirtualize the apply instruction on the identical path. auto NewInstPair = devirtualizeClassMethod(IdenAI, DownCastedClassInstance); assert(NewInstPair.first && "Expected to be able to devirtualize apply!"); replaceDeadApply(IdenAI, NewInstPair.first); // Split critical edges resulting from VirtAI. if (auto *TAI = dyn_cast<TryApplyInst>(VirtAI)) { auto *ErrorBB = TAI->getFunction()->createBasicBlock(); ErrorBB->createPHIArgument(TAI->getErrorBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); Builder.setInsertionPoint(ErrorBB); Builder.createBranch(TAI->getLoc(), TAI->getErrorBB(), {ErrorBB->getArgument(0)}); auto *NormalBB = TAI->getFunction()->createBasicBlock(); NormalBB->createPHIArgument(TAI->getNormalBB()->getArgument(0)->getType(), ValueOwnershipKind::Owned); Builder.setInsertionPoint(NormalBB); Builder.createBranch(TAI->getLoc(), TAI->getNormalBB(), {NormalBB->getArgument(0)}); Builder.setInsertionPoint(VirtAI.getInstruction()); SmallVector<SILValue, 4> Args; for (auto Arg : VirtAI.getArguments()) { Args.push_back(Arg); } FullApplySite NewVirtAI = Builder.createTryApply(VirtAI.getLoc(), VirtAI.getCallee(), VirtAI.getSubstitutions(), Args, NormalBB, ErrorBB); VirtAI.getInstruction()->eraseFromParent(); VirtAI = NewVirtAI; } return VirtAI; }