| //===--- SILGenEpilog.cpp - Function epilogue emission --------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SILGen.h" |
| #include "SILGenFunction.h" |
| #include "ASTVisitor.h" |
| #include "swift/SIL/SILArgument.h" |
| |
| using namespace swift; |
| using namespace Lowering; |
| |
| void SILGenFunction::prepareEpilog(Type resultType, bool isThrowing, |
| CleanupLocation CleanupL) { |
| auto *epilogBB = createBasicBlock(); |
| |
| // If we have any direct results, receive them via BB arguments. |
| // But callers can disable this by passing a null result type. |
| if (resultType) { |
| auto fnConv = F.getConventions(); |
| // Set NeedsReturn for indirect or direct results. This ensures that SILGen |
| // emits unreachable if there is no source level return. |
| NeedsReturn = (fnConv.funcTy->getNumResults() != 0); |
| for (auto directResult : fnConv.getDirectSILResults()) { |
| SILType resultType = |
| F.mapTypeIntoContext(fnConv.getSILType(directResult)); |
| epilogBB->createPHIArgument(resultType, ValueOwnershipKind::Owned); |
| } |
| } |
| |
| ReturnDest = JumpDest(epilogBB, getCleanupsDepth(), CleanupL); |
| |
| if (isThrowing) { |
| prepareRethrowEpilog(CleanupL); |
| } |
| } |
| |
| void SILGenFunction::prepareRethrowEpilog(CleanupLocation cleanupLoc) { |
| auto exnType = SILType::getExceptionType(getASTContext()); |
| SILBasicBlock *rethrowBB = createBasicBlock(FunctionSection::Postmatter); |
| rethrowBB->createPHIArgument(exnType, ValueOwnershipKind::Owned); |
| ThrowDest = JumpDest(rethrowBB, getCleanupsDepth(), cleanupLoc); |
| } |
| |
| /// Given a list of direct results, form the direct result value. |
| /// |
| /// Note that this intentionally loses any tuple sub-structure of the |
| /// formal result type. |
| static SILValue buildReturnValue(SILGenFunction &SGF, SILLocation loc, |
| ArrayRef<SILValue> directResults) { |
| if (directResults.size() == 1) |
| return directResults[0]; |
| |
| SmallVector<TupleTypeElt, 4> eltTypes; |
| for (auto elt : directResults) |
| eltTypes.push_back(elt->getType().getSwiftRValueType()); |
| auto resultType = SILType::getPrimitiveObjectType( |
| CanType(TupleType::get(eltTypes, SGF.getASTContext()))); |
| return SGF.B.createTuple(loc, resultType, directResults); |
| } |
| |
| 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->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); |
| } |
| |
| // 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->args_begin(), epilogBB->args_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.getConventions().getNumDirectSILResults()); |
| returnValue = buildReturnValue(*this, TopLevel, directResults); |
| } |
| |
| return { returnValue, *returnLoc }; |
| } |
| |
| SILLocation SILGenFunction:: |
| emitEpilog(SILLocation TopLevel, bool UsesCustomEpilog) { |
| Optional<SILValue> maybeReturnValue; |
| SILLocation returnLoc(TopLevel); |
| std::tie(maybeReturnValue, returnLoc) = emitEpilogBB(TopLevel); |
| |
| SILBasicBlock *ResultBB = nullptr; |
| |
| if (!maybeReturnValue) { |
| // Nothing to do. |
| } else if (UsesCustomEpilog) { |
| // If the epilog is reachable, and the caller provided an epilog, just |
| // remember the block so the caller can continue it. |
| ResultBB = B.getInsertionBB(); |
| assert(ResultBB && "Didn't have an epilog block?"); |
| B.clearInsertionPoint(); |
| } else { |
| // Otherwise, if the epilog block is reachable, return the return value. |
| SILValue returnValue = *maybeReturnValue; |
| |
| // Return () if no return value was given. |
| if (!returnValue) |
| returnValue = emitEmptyTuple(CleanupLocation::get(TopLevel)); |
| |
| B.createReturn(returnLoc, returnValue); |
| } |
| |
| emitRethrowEpilog(TopLevel); |
| |
| if (ResultBB) |
| B.setInsertionPoint(ResultBB); |
| |
| return returnLoc; |
| } |
| |
| 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->args_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(); |
| } |