static bool prepareExtraEpilog(SILGenFunction &SGF, JumpDest &dest, SILLocation &loc, SILValue *arg) { assert(!SGF.B.hasValidInsertionPoint()); // If we don't have a destination, we don't need to emit the epilog. if (!dest.isValid()) return false; // If the destination isn't used, we don't need to emit the epilog. SILBasicBlock *epilogBB = dest.getBlock(); auto pi = epilogBB->pred_begin(), pe = epilogBB->pred_end(); if (pi == pe) { dest = JumpDest::invalid(); SGF.eraseBasicBlock(epilogBB); return false; } assert(epilogBB->getNumArguments() <= 1); assert((epilogBB->getNumArguments() == 1) == (arg != nullptr)); if (arg) *arg = epilogBB->args_begin()[0]; bool reposition = true; // If the destination has a single branch predecessor, // consider emitting the epilog into it. SILBasicBlock *predBB = *pi; if (++pi == pe) { if (auto branch = dyn_cast<BranchInst>(predBB->getTerminator())) { assert(branch->getArgs().size() == epilogBB->getNumArguments()); // Save the location and operand information from the branch, // then destroy it. loc = branch->getLoc(); if (arg) *arg = branch->getArgs()[0]; predBB->erase(branch); // Erase the rethrow block. SGF.eraseBasicBlock(epilogBB); epilogBB = predBB; reposition = false; } } // Reposition the block to the end of the postmatter section // unless we're emitting into a single predecessor. if (reposition) { SGF.B.moveBlockTo(epilogBB, SGF.F.end()); } SGF.B.setInsertionPoint(epilogBB); return true; }
void SILGenFunction::emitRethrowEpilog(SILLocation topLevel) { assert(!B.hasValidInsertionPoint()); // If we don't have a rethrow destination, we're done. if (!ThrowDest.isValid()) return; // If the rethrow destination isn't used, we're done. SILBasicBlock *rethrowBB = ThrowDest.getBlock(); if (rethrowBB->pred_empty()) { ThrowDest = JumpDest::invalid(); eraseBasicBlock(rethrowBB); return; } SILLocation throwLoc = topLevel; SILValue exn = rethrowBB->bbarg_begin()[0]; bool reposition = true; // If the rethrow destination has a single branch predecessor, // consider emitting the rethrow into it. SILBasicBlock *predBB = *rethrowBB->pred_begin(); if (std::next(rethrowBB->pred_begin()) == rethrowBB->pred_end()) { if (auto branch = dyn_cast<BranchInst>(predBB->getTerminator())) { assert(branch->getArgs().size() == 1); // Save the location and operand information from the branch, // then destroy it. throwLoc = branch->getLoc(); exn = branch->getArgs()[0]; predBB->erase(branch); // Erase the rethrow block. eraseBasicBlock(rethrowBB); rethrowBB = predBB; reposition = false; } } // Reposition the rethrow block to the end of the postmatter section // unless we're emitting into a single predecessor. if (reposition) { B.moveBlockTo(rethrowBB, F.end()); } B.setInsertionPoint(rethrowBB); Cleanups.emitCleanupsForReturn(ThrowDest.getCleanupLocation()); B.createThrow(throwLoc, exn); ThrowDest = JumpDest::invalid(); }
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); SmallVector<SILValue, 4> directResults; 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()) { // 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. 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. // 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() == epilogBB->bbarg_size() && "epilog predecessor arguments does not match block params"); for (auto index : indices(predBranch->getArgs())) { SILValue result = predBranch->getArgs()[index]; directResults.push_back(result); epilogBB->getBBArg(index)->replaceAllUsesWith(result); } // 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; B.moveBlockTo(epilogBB, endOfOrdinarySection); // Emit the epilog into the epilog bb. Its arguments are the // direct results. directResults.append(epilogBB->bbarg_begin(), epilogBB->bbarg_end()); // 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; // Build the return value. We don't do this if there are no direct // results; this can happen for void functions, but also happens when // prepareEpilog was asked to not add result arguments to the epilog // block. SILValue returnValue; if (!directResults.empty()) { assert(directResults.size() == F.getLoweredFunctionType()->getNumDirectResults()); returnValue = buildReturnValue(*this, TopLevel, directResults); } return { returnValue, *returnLoc }; }
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 }; }
static Optional<SILLocation> prepareForEpilogBlockEmission(SILGenFunction &SGF, SILLocation topLevel, SILBasicBlock *epilogBB, SmallVectorImpl<SILValue> &directResults) { SILLocation implicitReturnFromTopLevel = ImplicitReturnLocation::getImplicitReturnLoc(topLevel); // If the current BB we are inserting into 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 (SGF.NeedsReturn && SGF.B.hasValidInsertionPoint()) SGF.B.createUnreachable(implicitReturnFromTopLevel); if (epilogBB->pred_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(); SGF.eraseBasicBlock(epilogBB); // If the current bb is terminated then the epilog is just unreachable. if (!SGF.B.hasValidInsertionPoint()) return None; // We emit the epilog at the current insertion point. return implicitReturnFromTopLevel; } if (std::next(epilogBB->pred_begin()) == epilogBB->pred_end() && !SGF.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. // 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() == epilogBB->args_size() && "epilog predecessor arguments does not match block params"); for (auto index : indices(predBranch->getArgs())) { SILValue result = predBranch->getArgs()[index]; directResults.push_back(result); epilogBB->getArgument(index)->replaceAllUsesWith(result); } Optional<SILLocation> returnLoc; // 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. SGF.eraseBasicBlock(epilogBB); // Emit the epilog into its former predecessor. SGF.B.setInsertionPoint(pred); return returnLoc; } // Move the epilog block to the end of the ordinary section. auto endOfOrdinarySection = SGF.StartOfPostmatter; SGF.B.moveBlockTo(epilogBB, endOfOrdinarySection); // Emit the epilog into the epilog bb. Its arguments are the // direct results. directResults.append(epilogBB->args_begin(), epilogBB->args_end()); // If we are falling through from the current block, the return is implicit. SGF.B.emitBlock(epilogBB, implicitReturnFromTopLevel); // 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. auto cleanupLoc = CleanupLocation::get(topLevel); return cleanupLoc; }