static bool constantFoldTerminator(SILBasicBlock &BB, UnreachableUserCodeReportingState *State) { TermInst *TI = BB.getTerminator(); // Process conditional branches with constant conditions. if (CondBranchInst *CBI = dyn_cast<CondBranchInst>(TI)) { SILValue V = CBI->getCondition(); SILInstruction *CondI = dyn_cast<SILInstruction>(V); SILLocation Loc = CBI->getLoc(); if (IntegerLiteralInst *ConstCond = dyn_cast_or_null<IntegerLiteralInst>(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. if (Loc.is<RegularLocation>() && State && !State->PossiblyUnreachableBlocks.count(UnreachableBlock)) { // 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<const SILBasicBlock*, UnreachableInfo>( 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<SwitchEnumInst>(TI)) { if (EnumInst *TheEnum = dyn_cast<EnumInst>(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<UnreachableInst>(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 unrechable 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->bbarg_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<RegularLocation>() && 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<const SILBasicBlock*, UnreachableInfo>( 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<SwitchValueInst>(TI)) { if (IntegerLiteralInst *SwitchVal = dyn_cast<IntegerLiteralInst>(SUI->getOperand())) { SILBasicBlock *TheSuccessorBlock = 0; 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<IntegerLiteralInst>(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 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.bbarg_empty()) return; // Check if all the predecessors supply the same arguments to the BB. SmallVector<SILValue, 4> 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<BranchInst>(PredB->getTerminator())) return; BranchInst *BI = cast<BranchInst>(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<SILInstruction*, 32> ToBeDeleted; for (SILBasicBlock::pred_iterator PI = BB.pred_begin(), PE = BB.pred_end(); PI != PE; ++PI) { SILBasicBlock *PredB = *PI; BranchInst *BI = cast<BranchInst>(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::bbarg_iterator AI = BB.bbarg_begin(), AE = BB.bbarg_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. assert(Arg->getTypes().size() == 1 && "Currently, we only support single result instructions."); SILValue(Arg).replaceAllUsesWith(Args[Idx]); NumBasicBlockArgsPropagated++; } // Remove args from the block. BB.dropAllBBArgs(); // The old branch instructions are no longer used, erase them. recursivelyDeleteTriviallyDeadInstructions(ToBeDeleted, true); NumInstructionsRemoved += ToBeDeleted.size(); }
std::pair<Optional<SILValue>, SILLocation> SILGenFunction::emitEpilogBB(SILLocation TopLevel) { assert(ReturnDest.getBlock() && "no epilog bb prepared?!"); SILBasicBlock *epilogBB = ReturnDest.getBlock(); SILLocation ImplicitReturnFromTopLevel = ImplicitReturnLocation::getImplicitReturnLoc(TopLevel); SILValue returnValue; Optional<SILLocation> returnLoc = None; // If the current BB isn't terminated, and we require a return, then we // are not allowed to fall off the end of the function and can't reach here. if (NeedsReturn && B.hasValidInsertionPoint()) B.createUnreachable(ImplicitReturnFromTopLevel); if (epilogBB->pred_empty()) { bool hadArg = !epilogBB->bbarg_empty(); // If the epilog was not branched to at all, kill the BB and // just emit the epilog into the current BB. while (!epilogBB->empty()) epilogBB->back().eraseFromParent(); eraseBasicBlock(epilogBB); // If the current bb is terminated then the epilog is just unreachable. if (!B.hasValidInsertionPoint()) return { None, TopLevel }; // We emit the epilog at the current insertion point. assert(!hadArg && "NeedsReturn is false but epilog had argument?!"); (void)hadArg; returnLoc = ImplicitReturnFromTopLevel; } else if (std::next(epilogBB->pred_begin()) == epilogBB->pred_end() && !B.hasValidInsertionPoint()) { // If the epilog has a single predecessor and there's no current insertion // point to fall through from, then we can weld the epilog to that // predecessor BB. bool needsArg = false; if (!epilogBB->bbarg_empty()) { assert(epilogBB->bbarg_size() == 1 && "epilog should take 0 or 1 args"); needsArg = true; } // Steal the branch argument as the return value if present. SILBasicBlock *pred = *epilogBB->pred_begin(); BranchInst *predBranch = cast<BranchInst>(pred->getTerminator()); assert(predBranch->getArgs().size() == (needsArg ? 1 : 0) && "epilog predecessor arguments does not match block params"); if (needsArg) { returnValue = predBranch->getArgs()[0]; // RAUW the old BB argument (if any) with the new value. SILValue(*epilogBB->bbarg_begin(),0).replaceAllUsesWith(returnValue); } // If we are optimizing, we should use the return location from the single, // previously processed, return statement if any. if (predBranch->getLoc().is<ReturnLocation>()) { returnLoc = predBranch->getLoc(); } else { returnLoc = ImplicitReturnFromTopLevel; } // Kill the branch to the now-dead epilog BB. pred->erase(predBranch); // Move any instructions from the EpilogBB to the end of the 'pred' block. pred->spliceAtEnd(epilogBB); // Finally we can erase the epilog BB. eraseBasicBlock(epilogBB); // Emit the epilog into its former predecessor. B.setInsertionPoint(pred); } else { // Move the epilog block to the end of the ordinary section. auto endOfOrdinarySection = (StartOfPostmatter ? SILFunction::iterator(StartOfPostmatter) : F.end()); B.moveBlockTo(epilogBB, endOfOrdinarySection); // Emit the epilog into the epilog bb. Its argument is the return value. if (!epilogBB->bbarg_empty()) { assert(epilogBB->bbarg_size() == 1 && "epilog should take 0 or 1 args"); returnValue = epilogBB->bbarg_begin()[0]; } // If we are falling through from the current block, the return is implicit. B.emitBlock(epilogBB, ImplicitReturnFromTopLevel); } // Emit top-level cleanups into the epilog block. assert(!Cleanups.hasAnyActiveCleanups(getCleanupsDepth(), ReturnDest.getDepth()) && "emitting epilog in wrong scope"); auto cleanupLoc = CleanupLocation::get(TopLevel); Cleanups.emitCleanupsForReturn(cleanupLoc); // If the return location is known to be that of an already // processed return, use it. (This will get triggered when the // epilog logic is simplified.) // // Otherwise make the ret instruction part of the cleanups. if (!returnLoc) returnLoc = cleanupLoc; return { returnValue, *returnLoc }; }