//===--- DiagnoseUnreachable.cpp - Diagnose unreachable code --------------===// // // 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 // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "diagnose-unreachable" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/AST/DiagnosticsSIL.h" #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" #include "swift/AST/Stmt.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILUndef.h" #include "swift/SILOptimizer/Utils/Local.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Debug.h" using namespace swift; STATISTIC(NumBlocksRemoved, "Number of unreachable basic blocks removed"); STATISTIC(NumInstructionsRemoved, "Number of unreachable instructions removed"); STATISTIC(NumTerminatorsFolded, "Number of terminators folded"); STATISTIC(NumBasicBlockArgsPropagated, "Number of basic block arguments propagated"); typedef llvm::SmallPtrSet SILBasicBlockSet; template static void diagnose(ASTContext &Context, SourceLoc loc, Diag diag, U &&...args) { Context.Diags.diagnose(loc, diag, std::forward(args)...); } enum class UnreachableKind { FoldedBranch, FoldedSwitchEnum, NoreturnCall, }; /// Information about a folded conditional branch instruction: it's location /// and whether the condition evaluated to true or false. struct UnreachableInfo { UnreachableKind Kind; /// \brief The location of the instruction that caused the unreachability. SILLocation Loc; /// \brief If this is the FoldedBranch kind, specifies if the condition is /// always true. bool CondIsAlwaysTrue; }; /// \class UnreachableUserCodeReportingState Contains extra state we need to /// communicate from condition branch folding stage to the unreachable blocks /// removal stage of the path. /// /// To report unreachable user code, we detect the blocks that contain user /// code and are not reachable (along any of the preceding paths). Note that we /// only want to report the first statement on the unreachable path. Keeping /// the info about which branch folding had produced the unreachable block makes /// it possible. class UnreachableUserCodeReportingState { public: /// \brief The set of top-level blocks that became immediately unreachable due /// to conditional branch folding, etc. /// /// This is a SetVector since several blocks may lead to the same error /// report and we iterate through these when producing the diagnostic. llvm::SetVector PossiblyUnreachableBlocks; /// \brief The set of blocks in which we reported unreachable code errors. /// These are used to ensure that we don't issue duplicate reports. /// /// Note, this set is different from the PossiblyUnreachableBlocks as these /// are the blocks that do contain user code and they might not be immediate /// successors of a folded branch. llvm::SmallPtrSet BlocksWithErrors; /// A map from the PossiblyUnreachableBlocks to the folded conditional /// branches that caused each of them to be unreachable. This extra info is /// used to enhance the diagnostics. llvm::DenseMap MetaMap; }; /// \brief Propagate/remove basic block input values when all predecessors /// supply the same arguments. static void propagateBasicBlockArgs(SILBasicBlock &BB) { // This functions would simplify the code as following: // // bb0: // br bb2(%1 : $Builtin.Int1, %2 : $Builtin.Int1) // bb1: // br bb2(%1 : $Builtin.Int1, %2 : $Builtin.Int1) // bb2(%3 : $Builtin.Int1, %4 : $Builtin.Int1): // use(%3 : $Builtin.Int1) // use(%4 : $Builtin.Int1) // => // bb0: // br bb2 // bb1: // br bb2 // bb2: // use(%1 : $Builtin.Int1) // use(%2 : $Builtin.Int1) // If there are no predecessors or no arguments, there is nothing to do. if (BB.pred_empty() || BB.args_empty()) return; // Check if all the predecessors supply the same arguments to the BB. SmallVector Args; bool checkArgs = false; for (SILBasicBlock::pred_iterator PI = BB.pred_begin(), PE = BB.pred_end(); PI != PE; ++PI) { SILBasicBlock *PredB = *PI; // We are only simplifying cases where all predecessors are // unconditional branch instructions. if (!isa(PredB->getTerminator())) return; BranchInst *BI = cast(PredB->getTerminator()); unsigned Idx = 0; assert(!BI->getArgs().empty()); for (OperandValueArrayRef::iterator AI = BI->getArgs().begin(), AE = BI->getArgs().end(); AI != AE; ++AI, ++Idx) { // When processing the first predecessor, record the arguments. if (!checkArgs) Args.push_back(*AI); else // On each subsequent predecessor, check the arguments. if (Args[Idx] != *AI) return; } // After the first branch is processed, the arguments vector is populated. assert(Args.size() > 0); checkArgs = true; } // If we've reached this point, the optimization is valid, so optimize. // We know that the incoming arguments from all predecessors are the same, // so just use them directly and remove the basic block parameters. // Drop the arguments from the branch instructions by creating a new branch // instruction and deleting the old one. llvm::SmallVector ToBeDeleted; for (SILBasicBlock::pred_iterator PI = BB.pred_begin(), PE = BB.pred_end(); PI != PE; ++PI) { SILBasicBlock *PredB = *PI; BranchInst *BI = cast(PredB->getTerminator()); SILBuilderWithScope Bldr(PredB, BI); Bldr.createBranch(BI->getLoc(), BI->getDestBB()); ToBeDeleted.push_back(BI); } // Drop the parameters from basic blocks and replace all uses with the passed // in arguments. unsigned Idx = 0; for (SILBasicBlock::arg_iterator AI = BB.args_begin(), AE = BB.args_end(); AI != AE; ++AI, ++Idx) { // FIXME: These could be further propagatable now, we might want to move // this to CCP and trigger another round of copy propagation. SILArgument *Arg = *AI; // We were able to fold, so all users should use the new folded value. Arg->replaceAllUsesWith(Args[Idx]); NumBasicBlockArgsPropagated++; } // Remove args from the block. BB.dropAllArguments(); // The old branch instructions are no longer used, erase them. recursivelyDeleteTriviallyDeadInstructions(ToBeDeleted, true); NumInstructionsRemoved += ToBeDeleted.size(); } static bool constantFoldTerminator(SILBasicBlock &BB, UnreachableUserCodeReportingState *State) { TermInst *TI = BB.getTerminator(); // Process conditional branches with constant conditions. if (CondBranchInst *CBI = dyn_cast(TI)) { SILValue V = CBI->getCondition(); SILInstruction *CondI = dyn_cast(V); SILLocation Loc = CBI->getLoc(); if (IntegerLiteralInst *ConstCond = dyn_cast_or_null(CondI)) { SILBuilderWithScope B(&BB, CBI); // Determine which of the successors is unreachable and create a new // terminator that only branches to the reachable successor. SILBasicBlock *UnreachableBlock = nullptr; bool CondIsTrue = false; if (ConstCond->getValue() == APInt(1, /*value*/ 0, false)) { B.createBranch(Loc, CBI->getFalseBB(), CBI->getFalseArgs()); UnreachableBlock = CBI->getTrueBB(); } else { assert(ConstCond->getValue() == APInt(1, /*value*/ 1, false) && "Our representation of true/false does not match."); B.createBranch(Loc, CBI->getTrueBB(), CBI->getTrueArgs()); UnreachableBlock = CBI->getFalseBB(); CondIsTrue = true; } recursivelyDeleteTriviallyDeadInstructions(TI, true); NumInstructionsRemoved++; // Produce an unreachable code warning for this basic block if it // contains user code (only if we are not within an inlined function or a // template instantiation). // FIXME: Do not report if we are within a template instantiation. // FIXME: Checking for LabeledConditionalStmt is a hack; it's meant to // catch cases where we have a #available or similar non-expression // condition that was trivially true or false. In these cases we expect // the unreachable block to be reachable on another platform and shouldn't // emit any warnings about it; if this is not the case it's Sema's // responsibility to warn about it. if (Loc.is() && State && !State->PossiblyUnreachableBlocks.count(UnreachableBlock) && !Loc.isASTNode()) { // If this is the first time we see this unreachable block, store it // along with the folded branch info. State->PossiblyUnreachableBlocks.insert(UnreachableBlock); State->MetaMap.insert( std::pair( UnreachableBlock, UnreachableInfo{UnreachableKind::FoldedBranch, Loc, CondIsTrue})); } NumTerminatorsFolded++; return true; } } // Constant fold switch enum. // %1 = enum $Bool, #Bool.false!unionelt // switch_enum %1 : $Bool, case #Bool.true!unionelt: bb1, // case #Bool.false!unionelt: bb2 // => // br bb2 if (SwitchEnumInst *SUI = dyn_cast(TI)) { if (EnumInst *TheEnum = dyn_cast(SUI->getOperand())) { const EnumElementDecl *TheEnumElem = TheEnum->getElement(); SILBasicBlock *TheSuccessorBlock = nullptr; int ReachableBlockIdx = -1; for (unsigned Idx = 0; Idx < SUI->getNumCases(); ++Idx) { const EnumElementDecl *EI; SILBasicBlock *BI; std::tie(EI, BI) = SUI->getCase(Idx); if (EI == TheEnumElem) { TheSuccessorBlock = BI; ReachableBlockIdx = Idx; break; } } if (!TheSuccessorBlock) if (SUI->hasDefault()) { SILBasicBlock *DB= SUI->getDefaultBB(); if (!isa(DB->getTerminator())) { TheSuccessorBlock = DB; ReachableBlockIdx = SUI->getNumCases(); } } // Not fully covered switches will be diagnosed later. SILGen represents // them with a Default basic block with an unreachable instruction. // We are going to produce an error on all unreachable instructions not // eliminated by DCE. if (!TheSuccessorBlock) return false; // Replace the switch with a branch to the TheSuccessorBlock. SILBuilderWithScope B(&BB, TI); SILLocation Loc = TI->getLoc(); if (!TheSuccessorBlock->args_empty()) { assert(TheEnum->hasOperand()); B.createBranch(Loc, TheSuccessorBlock, TheEnum->getOperand()); } else B.createBranch(Loc, TheSuccessorBlock); // Produce diagnostic info if we are not within an inlined function or // template instantiation. // FIXME: Do not report if we are within a template instantiation. assert(ReachableBlockIdx >= 0); if (Loc.is() && State) { // Find the first unreachable block in the switch so that we could use // it for better diagnostics. SILBasicBlock *UnreachableBlock = nullptr; if (SUI->getNumCases() > 1) { // More than one case. UnreachableBlock = (ReachableBlockIdx == 0) ? SUI->getCase(1).second: SUI->getCase(0).second; } else { if (SUI->getNumCases() == 1 && SUI->hasDefault()) { // One case and a default. UnreachableBlock = (ReachableBlockIdx == 0) ? SUI->getDefaultBB(): SUI->getCase(0).second; } } // Generate diagnostic info. if (UnreachableBlock && !State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { State->PossiblyUnreachableBlocks.insert(UnreachableBlock); State->MetaMap.insert( std::pair( UnreachableBlock, UnreachableInfo{UnreachableKind::FoldedSwitchEnum, Loc, true})); } } recursivelyDeleteTriviallyDeadInstructions(TI, true); NumTerminatorsFolded++; return true; } } // Constant fold switch int. // %1 = integer_literal $Builtin.Int64, 2 // switch_value %1 : $Builtin.Int64, case 1: bb1, case 2: bb2 // => // br bb2 if (SwitchValueInst *SUI = dyn_cast(TI)) { if (IntegerLiteralInst *SwitchVal = dyn_cast(SUI->getOperand())) { SILBasicBlock *TheSuccessorBlock = nullptr; for (unsigned Idx = 0; Idx < SUI->getNumCases(); ++Idx) { APInt AI; SILValue EI; SILBasicBlock *BI; std::tie(EI, BI) = SUI->getCase(Idx); // TODO: Check that EI is really an IntegerLiteralInst AI = dyn_cast(EI)->getValue(); if (AI == SwitchVal->getValue()) TheSuccessorBlock = BI; } if (!TheSuccessorBlock) if (SUI->hasDefault()) TheSuccessorBlock = SUI->getDefaultBB(); // Add the branch instruction with the block. if (TheSuccessorBlock) { SILBuilderWithScope B(&BB, TI); B.createBranch(TI->getLoc(), TheSuccessorBlock); recursivelyDeleteTriviallyDeadInstructions(TI, true); NumTerminatorsFolded++; return true; } // TODO: Warn on unreachable user code here as well. } } return false; } /// \brief Check if this instruction corresponds to user-written code. static bool isUserCode(const SILInstruction *I) { SILLocation Loc = I->getLoc(); if (Loc.isAutoGenerated()) return false; // Branch instructions are not user code. These could belong to the control // flow statement we are folding (ex: while loop). // Also, unreachable instructions are not user code, they are "expected" in // unreachable blocks. if ((isa(I) || isa(I)) && Loc.is()) return false; // If the instruction corresponds to user-written return or some other // statement, we know it corresponds to user code. if (Loc.is() || Loc.is()) { if (auto *E = Loc.getAsASTNode()) return !E->isImplicit(); if (auto *D = Loc.getAsASTNode()) return !D->isImplicit(); if (auto *S = Loc.getAsASTNode()) return !S->isImplicit(); if (auto *P = Loc.getAsASTNode()) return !P->isImplicit(); } return false; } static void setOutsideBlockUsesToUndef(SILInstruction *I) { if (I->use_empty()) return; SILBasicBlock *BB = I->getParent(); SILModule &Mod = BB->getModule(); // Replace all uses outside of I's basic block by undef. llvm::SmallVector Uses(I->use_begin(), I->use_end()); for (auto *Use : Uses) if (auto *User = dyn_cast(Use->getUser())) if (User->getParent() != BB) Use->set(SILUndef::get(Use->get()->getType(), Mod)); } static SILInstruction *getAsCallToNoReturn(SILInstruction *I) { if (auto *AI = dyn_cast(I)) if (AI->isCalleeNoReturn()) return AI; if (auto *BI = dyn_cast(I)) { if (BI->getModule().isNoReturnBuiltinOrIntrinsic(BI->getName())) return BI; } return nullptr; } static SILInstruction *getPrecedingCallToNoReturn(SILBasicBlock &BB) { // All the predecessors must satisfy this condition; pick the first // as a representative. SILGen doesn't actually re-use blocks for // the normal edge, but it's good to be prepared. SILInstruction *first = nullptr; for (auto i = BB.pred_begin(), e = BB.pred_end(); i != e; ++i) { SILBasicBlock *predBB = *i; // As a precaution, bail out if we have a self-loop. It's not // clear what transformations (if any) on naive SILGen output // would ever produce that, but still, don't do it. It's probably // only possible in code that's already otherwise provable to be // unreachable. if (predBB == &BB) return nullptr; // The predecessor must be the normal edge from a try_apply // that invokes a noreturn function. if (auto TAI = dyn_cast((*i)->getTerminator())) { if (TAI->isCalleeNoReturn() && TAI->isNormalSuccessorRef(i.getSuccessorRef())) { if (!first) first = TAI; continue; } } return nullptr; } return first; } 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 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()) { if (!NoReturnCall->getLoc().isASTNode()) { 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(NoReturnCall)) { if (Builtin->getName().str() == "unreachable") ToBeDeleted.push_back(NoReturnCall); } // Record the diagnostic info. if (!DiagnosedUnreachableCode && NoReturnCall->getLoc().is() && 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( 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; } /// \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 &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() || 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(); 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()) { assert(BrInfo.Loc.isASTNode()); 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 removeUnreachableBlocks(SILFunction &F, SILModule &M, UnreachableUserCodeReportingState *State) { if (F.empty()) return false; SILBasicBlockSet Reachable; SmallVector Worklist; Worklist.push_back(&F.front()); Reachable.insert(&F.front()); // Collect all reachable blocks by walking the successors. do { SILBasicBlock *BB = Worklist.pop_back_val(); for (auto SI = BB->succ_begin(), SE = BB->succ_end(); SI != SE; ++SI) { if (Reachable.insert(*SI).second) Worklist.push_back(*SI); } } while (!Worklist.empty()); assert(Reachable.size() <= F.size()); // If everything is reachable, we are done. if (Reachable.size() == F.size()) return false; // Diagnose user written unreachable code. if (State) { for (auto BI = State->PossiblyUnreachableBlocks.begin(), BE = State->PossiblyUnreachableBlocks.end(); BI != BE; ++BI) { const SILBasicBlock *BB = *BI; if (!Reachable.count(BB)) { llvm::SmallPtrSet visited; diagnoseUnreachableBlock(**BI, M, Reachable, State, BB, visited); } } } // Remove references from the dead blocks. for (auto I = F.begin(), E = F.end(); I != E; ++I) { SILBasicBlock *BB = &*I; if (Reachable.count(BB)) continue; // Drop references to other blocks. recursivelyDeleteTriviallyDeadInstructions(BB->getTerminator(), true); NumInstructionsRemoved++; } // Delete dead instructions and everything that could become dead after // their deletion. llvm::SmallVector ToBeDeleted; for (auto BI = F.begin(), BE = F.end(); BI != BE; ++BI) if (!Reachable.count(&*BI)) for (auto I = BI->begin(), E = BI->end(); I != E; ++I) ToBeDeleted.push_back(&*I); recursivelyDeleteTriviallyDeadInstructions(ToBeDeleted, true); NumInstructionsRemoved += ToBeDeleted.size(); // Delete the dead blocks. for (auto I = F.begin(), E = F.end(); I != E;) if (!Reachable.count(&*I)) { I = F.getBlocks().erase(I); NumBlocksRemoved++; } else ++I; return true; } /// Scan the function for any calls to noreturn functions. If we find one, /// change the block to have an unreachable instruction right after it, and /// diagnose any user code after it as being unreachable. This pass happens /// before the definite initialization pass so that it doesn't see infeasible /// control flow edges. static void performNoReturnFunctionProcessing(SILModule *M, SILModuleTransform *T) { for (auto &Fn : *M) { DEBUG(llvm::errs() << "*** No return function processing: " << Fn.getName() << "\n"); for (auto &BB : Fn) { // Remove instructions from the basic block after a call to a noreturn // function. simplifyBlocksWithCallsToNoReturn(BB, nullptr); } T->invalidateAnalysis(&Fn, SILAnalysis::InvalidationKind::FunctionBody); } } void swift::performSILDiagnoseUnreachable(SILModule *M, SILModuleTransform *T) { for (auto &Fn : *M) { DEBUG(llvm::errs() << "*** Diagnose Unreachable processing: " << Fn.getName() << "\n"); UnreachableUserCodeReportingState State; for (auto &BB : Fn) { // Simplify the blocks with terminators that rely on constant conditions. if (constantFoldTerminator(BB, &State)) continue; // Remove instructions from the basic block after a call to a noreturn // function. if (simplifyBlocksWithCallsToNoReturn(BB, &State)) continue; } // Remove unreachable blocks. removeUnreachableBlocks(Fn, *M, &State); for (auto &BB : Fn) { propagateBasicBlockArgs(BB); } for (auto &BB : Fn) { // Simplify the blocks with terminators that rely on constant conditions. if (constantFoldTerminator(BB, &State)) { continue; } // Remove instructions from the basic block after a call to a noreturn // function. if (simplifyBlocksWithCallsToNoReturn(BB, &State)) continue; } // Remove unreachable blocks. removeUnreachableBlocks(Fn, *M, &State); if (T) T->invalidateAnalysis(&Fn, SILAnalysis::InvalidationKind::FunctionBody); } } namespace { class NoReturnFolding : public SILModuleTransform { void run() override { performNoReturnFunctionProcessing(getModule(), this); } }; } // end anonymous namespace SILTransform *swift::createNoReturnFolding() { return new NoReturnFolding(); } namespace { class DiagnoseUnreachable : public SILModuleTransform { void run() override { performSILDiagnoseUnreachable(getModule(), this); } }; } // end anonymous namespace SILTransform *swift::createDiagnoseUnreachable() { return new DiagnoseUnreachable(); }