mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
868 lines
29 KiB
C++
868 lines
29 KiB
C++
//===--- SILGenStmt.cpp - Implements Lowering of ASTs -> SIL for Stmts ----===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SILGen.h"
|
|
#include "Scope.h"
|
|
#include "Condition.h"
|
|
#include "Initialization.h"
|
|
#include "LValue.h"
|
|
#include "RValue.h"
|
|
#include "swift/AST/AST.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/AST/DiagnosticsSIL.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
|
|
using namespace swift;
|
|
using namespace Lowering;
|
|
|
|
template<typename...T, typename...U>
|
|
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
|
|
U &&...args) {
|
|
Context.Diags.diagnose(loc,
|
|
diag, std::forward<U>(args)...);
|
|
}
|
|
|
|
SILBasicBlock *SILGenFunction::createBasicBlock(SILBasicBlock *afterBB) {
|
|
// Honor an explicit placement if given.
|
|
if (afterBB) {
|
|
return new (F.getModule()) SILBasicBlock(&F, afterBB);
|
|
|
|
// If we don't have a requested placement, but we do have a current
|
|
// insertion point, insert there.
|
|
} else if (B.hasValidInsertionPoint()) {
|
|
return new (F.getModule()) SILBasicBlock(&F, B.getInsertionBB());
|
|
|
|
// Otherwise, insert at the end of the current section.
|
|
} else {
|
|
return createBasicBlock(CurFunctionSection);
|
|
}
|
|
}
|
|
|
|
SILBasicBlock *SILGenFunction::createBasicBlock(FunctionSection section) {
|
|
switch (section) {
|
|
case FunctionSection::Ordinary: {
|
|
// The end of the ordinary section is just the end of the function
|
|
// unless postmatter blocks exist.
|
|
SILBasicBlock *afterBB =
|
|
(StartOfPostmatter ? StartOfPostmatter->getPrevNode() : nullptr);
|
|
return new (F.getModule()) SILBasicBlock(&F, afterBB);
|
|
}
|
|
|
|
case FunctionSection::Postmatter: {
|
|
// The end of the postmatter section is always the end of the function.
|
|
// Register the new block as the start of the postmatter if needed.
|
|
SILBasicBlock *newBB = new (F.getModule()) SILBasicBlock(&F, nullptr);
|
|
if (!StartOfPostmatter) StartOfPostmatter = newBB;
|
|
return newBB;
|
|
}
|
|
|
|
}
|
|
llvm_unreachable("bad function section");
|
|
}
|
|
|
|
void SILGenFunction::eraseBasicBlock(SILBasicBlock *block) {
|
|
assert(block->pred_empty() && "erasing block with predecessors");
|
|
assert(block->empty() && "erasing block with content");
|
|
if (block == StartOfPostmatter) {
|
|
StartOfPostmatter = block->getNextNode();
|
|
}
|
|
block->eraseFromParent();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// SILGenFunction emitStmt implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
class StmtEmitter : public Lowering::ASTVisitor<StmtEmitter> {
|
|
SILGenFunction &SGF;
|
|
public:
|
|
StmtEmitter(SILGenFunction &sgf) : SGF(sgf) {}
|
|
#define STMT(ID, BASE) void visit##ID##Stmt(ID##Stmt *S);
|
|
#include "swift/AST/StmtNodes.def"
|
|
|
|
ASTContext &getASTContext() { return SGF.getASTContext(); }
|
|
|
|
SILBasicBlock *createBasicBlock() { return SGF.createBasicBlock(); }
|
|
|
|
template <class... Args>
|
|
JumpDest createJumpDest(Stmt *cleanupLoc, Args... args) {
|
|
return JumpDest(SGF.createBasicBlock(args...),
|
|
SGF.getCleanupsDepth(),
|
|
CleanupLocation(cleanupLoc));
|
|
}
|
|
};
|
|
}
|
|
|
|
void SILGenFunction::emitStmt(Stmt *S) {
|
|
StmtEmitter(*this).visit(S);
|
|
}
|
|
|
|
/// emitOrDeleteBlock - If there are branches to the specified basic block,
|
|
/// emit it per emitBlock. If there aren't, then just delete the block - it
|
|
/// turns out to have not been needed. Returns true if the block was emitted.
|
|
static bool emitOrDeleteBlock(SILGenFunction &SGF, SILBasicBlock *BB,
|
|
SILLocation BranchLoc) {
|
|
// If we ever add a single-use optimization here (to just continue
|
|
// the predecessor instead of branching to a separate block), we'll
|
|
// need to update visitDoCatchStmt so that code like:
|
|
// try { throw x } catch _ { }
|
|
// doesn't leave us emitting the rest of the function in the
|
|
// postmatter section.
|
|
|
|
if (BB->pred_empty()) {
|
|
// If the block is unused, we don't need it; just delete it.
|
|
SGF.eraseBasicBlock(BB);
|
|
return false;
|
|
}
|
|
// Otherwise, continue emitting code in BB.
|
|
SGF.B.emitBlock(BB, BranchLoc);
|
|
return true;
|
|
}
|
|
|
|
Condition SILGenFunction::emitCondition(Expr *E,
|
|
bool hasFalseCode, bool invertValue,
|
|
ArrayRef<SILType> contArgs) {
|
|
assert(B.hasValidInsertionPoint() &&
|
|
"emitting condition at unreachable point");
|
|
|
|
// Sema forces conditions to have Builtin.i1 type, which guarantees this.
|
|
SILValue V;
|
|
{
|
|
FullExpr Scope(Cleanups, CleanupLocation(E));
|
|
V = emitRValue(E).forwardAsSingleValue(*this, E);
|
|
}
|
|
assert(V.getType().castTo<BuiltinIntegerType>()->isFixedWidth(1));
|
|
|
|
return emitCondition(V, E, hasFalseCode, invertValue, contArgs);
|
|
}
|
|
|
|
|
|
|
|
Condition SILGenFunction::emitCondition(SILValue V, SILLocation Loc,
|
|
bool hasFalseCode, bool invertValue,
|
|
ArrayRef<SILType> contArgs) {
|
|
assert(B.hasValidInsertionPoint() &&
|
|
"emitting condition at unreachable point");
|
|
|
|
SILBasicBlock *ContBB = createBasicBlock();
|
|
|
|
for (SILType argTy : contArgs) {
|
|
new (F.getModule()) SILArgument(ContBB, argTy);
|
|
}
|
|
|
|
SILBasicBlock *FalseBB, *FalseDestBB;
|
|
if (hasFalseCode) {
|
|
FalseBB = FalseDestBB = createBasicBlock();
|
|
} else {
|
|
FalseBB = nullptr;
|
|
FalseDestBB = ContBB;
|
|
}
|
|
|
|
SILBasicBlock *TrueBB = createBasicBlock();
|
|
|
|
if (invertValue)
|
|
B.createCondBranch(Loc, V, FalseDestBB, TrueBB);
|
|
else
|
|
B.createCondBranch(Loc, V, TrueBB, FalseDestBB);
|
|
|
|
return Condition(TrueBB, FalseBB, ContBB, Loc);
|
|
}
|
|
|
|
void StmtEmitter::visitBraceStmt(BraceStmt *S) {
|
|
// Enter a new scope.
|
|
LexicalScope BraceScope(SGF.Cleanups, SGF, CleanupLocation(S));
|
|
const unsigned ReturnStmtType = 0;
|
|
const unsigned ContinueStmtType = 1;
|
|
const unsigned UnknownStmtType = 2;
|
|
unsigned StmtType = UnknownStmtType;
|
|
|
|
for (auto &ESD : S->getElements()) {
|
|
|
|
if (auto S = ESD.dyn_cast<Stmt*>()) {
|
|
if (isa<IfConfigStmt>(S)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If we ever reach an unreachable point, stop emitting statements and issue
|
|
// an unreachable code diagnostic. This will need revision if we ever add
|
|
// goto.
|
|
if (!SGF.B.hasValidInsertionPoint()) {
|
|
if (StmtType != UnknownStmtType) {
|
|
diagnose(getASTContext(), ESD.getStartLoc(),
|
|
diag::unreachable_code_after_stmt, StmtType);
|
|
} else {
|
|
diagnose(getASTContext(), ESD.getStartLoc(),
|
|
diag::unreachable_code);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Process children.
|
|
if (Stmt *S = ESD.dyn_cast<Stmt*>()) {
|
|
visit(S);
|
|
if (isa<ContinueStmt>(S))
|
|
StmtType = ContinueStmtType;
|
|
if (isa<ReturnStmt>(S))
|
|
StmtType = ReturnStmtType;
|
|
|
|
} else if (Expr *E = ESD.dyn_cast<Expr*>()) {
|
|
SGF.emitIgnoredExpr(E);
|
|
} else {
|
|
SGF.visit(ESD.get<Decl*>());
|
|
}
|
|
}
|
|
}
|
|
|
|
void SILGenFunction::emitReturnExpr(SILLocation branchLoc,
|
|
Expr *ret) {
|
|
SILValue result;
|
|
if (IndirectReturnAddress) {
|
|
// Indirect return of an address-only value.
|
|
FullExpr scope(Cleanups, CleanupLocation(ret));
|
|
InitializationPtr returnInit(
|
|
new KnownAddressInitialization(IndirectReturnAddress));
|
|
emitExprInto(ret, returnInit.get());
|
|
} else {
|
|
// SILValue return.
|
|
FullExpr scope(Cleanups, CleanupLocation(ret));
|
|
RValue resultRValue = emitRValue(ret);
|
|
if (!resultRValue.getType()->isVoid()) {
|
|
result = std::move(resultRValue).forwardAsSingleValue(*this, ret);
|
|
}
|
|
}
|
|
Cleanups.emitBranchAndCleanups(ReturnDest, branchLoc,
|
|
result ? result : ArrayRef<SILValue>{});
|
|
}
|
|
|
|
void StmtEmitter::visitReturnStmt(ReturnStmt *S) {
|
|
SGF.CurrentSILLoc = S;
|
|
SILLocation Loc = S->isImplicit() ?
|
|
(SILLocation)ImplicitReturnLocation(S) :
|
|
(SILLocation)ReturnLocation(S);
|
|
|
|
SILValue ArgV;
|
|
if (!S->hasResult())
|
|
// Void return.
|
|
SGF.Cleanups.emitBranchAndCleanups(SGF.ReturnDest, Loc);
|
|
else
|
|
SGF.emitReturnExpr(Loc, S->getResult());
|
|
}
|
|
|
|
namespace {
|
|
// This is a little cleanup that ensures that there are no jumps out of a
|
|
// defer body. The cleanup is only active and installed when emitting the
|
|
// body of a defer, and it is disabled at the end. If it ever needs to be
|
|
// emitted, it crashes the compiler because Sema missed something.
|
|
class DeferEscapeCheckerCleanup : public Cleanup {
|
|
SourceLoc deferLoc;
|
|
public:
|
|
DeferEscapeCheckerCleanup(SourceLoc deferLoc) : deferLoc(deferLoc) {}
|
|
void emit(SILGenFunction &SGF, CleanupLocation l) override {
|
|
SGF.SGM.diagnose(deferLoc, diag::defer_cannot_be_exited);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
namespace {
|
|
class DeferCleanup : public Cleanup {
|
|
SourceLoc deferLoc;
|
|
Expr *call;
|
|
public:
|
|
DeferCleanup(SourceLoc deferLoc, Expr *call)
|
|
: deferLoc(deferLoc), call(call) {}
|
|
void emit(SILGenFunction &SGF, CleanupLocation l) override {
|
|
SGF.Cleanups.pushCleanup<DeferEscapeCheckerCleanup>(deferLoc);
|
|
auto TheCleanup = SGF.Cleanups.getTopCleanup();
|
|
|
|
SGF.emitIgnoredExpr(call);
|
|
|
|
if (SGF.B.hasValidInsertionPoint())
|
|
SGF.Cleanups.setCleanupState(TheCleanup, CleanupState::Dead);
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
void StmtEmitter::visitDeferStmt(DeferStmt *S) {
|
|
// Emit the closure for the defer, along with its binding.
|
|
SGF.visitPatternBindingDecl(S->getPatternBinding());
|
|
|
|
// Register a cleanup to invoke the closure on any exit paths.
|
|
SGF.Cleanups.pushCleanup<DeferCleanup>(S->getDeferLoc(), S->getCallExpr());
|
|
}
|
|
|
|
|
|
|
|
|
|
void StmtEmitter::visitIfStmt(IfStmt *S) {
|
|
Scope condBufferScope(SGF.Cleanups, S);
|
|
|
|
// Create a continuation block. We need it if there is a labeled break out
|
|
// of the if statement or if there is an if/then/else.
|
|
JumpDest contDest = createJumpDest(S->getThenStmt());
|
|
auto contBB = contDest.getBlock();
|
|
|
|
// Set the destinations for any 'break' and 'continue' statements inside the
|
|
// body. Note that "continue" is not valid out of a labeled 'if'.
|
|
SGF.BreakContinueDestStack.push_back(
|
|
{ S, contDest, JumpDest(CleanupLocation(S)) });
|
|
|
|
// Set up the block for the false case. If there is an 'else' block, we make
|
|
// a new one, otherwise it is our continue block.
|
|
JumpDest falseDest = contDest;
|
|
if (S->getElseStmt())
|
|
falseDest = createJumpDest(S);
|
|
|
|
// Emit the condition, along with the "then" part of the if properly guarded
|
|
// by the condition and a jump to ContBB. If the condition fails, jump to
|
|
// the CondFalseBB.
|
|
{
|
|
// Enter a scope for any bound pattern variables.
|
|
LexicalScope trueScope(SGF.Cleanups, SGF, S);
|
|
|
|
SGF.emitStmtCondition(S->getCond(), falseDest, S);
|
|
|
|
// In the success path, emit the 'then' part if the if.
|
|
SGF.emitProfilerIncrement(S->getThenStmt());
|
|
SGF.emitStmt(S->getThenStmt());
|
|
|
|
// Finish the "true part" by cleaning up any temporaries and jumping to the
|
|
// continuation block.
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
RegularLocation L(S->getThenStmt());
|
|
L.pointToEnd();
|
|
SGF.Cleanups.emitBranchAndCleanups(contDest, L);
|
|
}
|
|
}
|
|
|
|
// If there is 'else' logic, then emit it.
|
|
if (S->getElseStmt()) {
|
|
SGF.B.emitBlock(falseDest.getBlock());
|
|
visit(S->getElseStmt());
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
RegularLocation L(S->getElseStmt());
|
|
L.pointToEnd();
|
|
SGF.B.createBranch(L, contBB);
|
|
}
|
|
}
|
|
|
|
// If the continuation block was used, emit it now, otherwise remove it.
|
|
if (contBB->pred_empty()) {
|
|
SGF.eraseBasicBlock(contBB);
|
|
} else {
|
|
RegularLocation L(S->getThenStmt());
|
|
L.pointToEnd();
|
|
SGF.B.emitBlock(contBB, L);
|
|
}
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
}
|
|
|
|
void StmtEmitter::visitGuardStmt(GuardStmt *S) {
|
|
// Create a block for the body and emit code into it before processing any of
|
|
// the patterns, because none of the bound variables will be in scope in the
|
|
// 'body' context.
|
|
JumpDest bodyBB =
|
|
JumpDest(createBasicBlock(), SGF.getCleanupsDepth(), CleanupLocation(S));
|
|
|
|
{
|
|
// Move the insertion point to the 'body' block temporarily and emit it.
|
|
// Note that we don't push break/continue locations since they aren't valid
|
|
// in this statement.
|
|
SavedInsertionPoint savedIP(SGF, bodyBB.getBlock());
|
|
SGF.emitStmt(S->getBody());
|
|
|
|
// The body block must end in a noreturn call, return, break etc. It
|
|
// isn't valid to fall off into the normal flow. To model this, we emit
|
|
// an unreachable instruction and then have SIL diagnostic check this.
|
|
if (SGF.B.hasValidInsertionPoint())
|
|
SGF.B.createUnreachable(S);
|
|
}
|
|
|
|
// Emit the condition bindings, branching to the bodyBB if they fail. Since
|
|
// we didn't push a scope, the bound variables are live after this statement.
|
|
SGF.emitStmtCondition(S->getCond(), bodyBB, S);
|
|
}
|
|
|
|
|
|
void StmtEmitter::visitIfConfigStmt(IfConfigStmt *S) {
|
|
// Active members are attached to the enclosing declaration, so there's no
|
|
// need to walk anything within.
|
|
}
|
|
|
|
void StmtEmitter::visitWhileStmt(WhileStmt *S) {
|
|
Scope condBufferScope(SGF.Cleanups, S);
|
|
|
|
// Create a new basic block and jump into it.
|
|
JumpDest loopDest = createJumpDest(S->getBody());
|
|
SGF.B.emitBlock(loopDest.getBlock(), S);
|
|
|
|
// Create a break target (at this level in the cleanup stack) in case it is
|
|
// needed.
|
|
JumpDest breakDest = createJumpDest(S->getBody());
|
|
|
|
// Set the destinations for any 'break' and 'continue' statements inside the
|
|
// body.
|
|
SGF.BreakContinueDestStack.push_back({S, breakDest, loopDest});
|
|
|
|
// Evaluate the condition, the body, and a branch back to LoopBB when the
|
|
// condition is true. On failure, jump to BreakBB.
|
|
{
|
|
// Enter a scope for any bound pattern variables.
|
|
Scope conditionScope(SGF.Cleanups, S);
|
|
|
|
SGF.emitStmtCondition(S->getCond(), breakDest, S);
|
|
|
|
// In the success path, emit the body of the while.
|
|
SGF.emitProfilerIncrement(S->getBody());
|
|
SGF.emitStmt(S->getBody());
|
|
|
|
// Finish the "true part" by cleaning up any temporaries and jumping to the
|
|
// continuation block.
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
RegularLocation L(S->getBody());
|
|
L.pointToEnd();
|
|
SGF.Cleanups.emitBranchAndCleanups(loopDest, L);
|
|
}
|
|
}
|
|
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
|
|
// Handle break block. If it was used, we link it up with the cleanup chain,
|
|
// otherwise we just remove it.
|
|
SILBasicBlock *breakBB = breakDest.getBlock();
|
|
if (breakBB->pred_empty()) {
|
|
SGF.eraseBasicBlock(breakBB);
|
|
} else {
|
|
SGF.B.emitBlock(breakBB);
|
|
}
|
|
}
|
|
|
|
void StmtEmitter::visitDoStmt(DoStmt *S) {
|
|
// We don't need to do anything fancy if we don't have a label.
|
|
// Otherwise, assume we might break or continue.
|
|
bool hasLabel = (bool) S->getLabelInfo();
|
|
|
|
JumpDest endDest = JumpDest::invalid();
|
|
if (hasLabel) {
|
|
// Create the end dest first so that the loop dest comes in-between.
|
|
endDest = createJumpDest(S->getBody());
|
|
|
|
// Create a new basic block and jump into it.
|
|
JumpDest loopDest = createJumpDest(S->getBody());
|
|
SGF.B.emitBlock(loopDest.getBlock(), S);
|
|
|
|
// Set the destinations for 'break' and 'continue'.
|
|
SGF.BreakContinueDestStack.push_back({S, endDest, loopDest});
|
|
}
|
|
|
|
// Emit the body.
|
|
visit(S->getBody());
|
|
|
|
if (hasLabel) {
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
emitOrDeleteBlock(SGF, endDest.getBlock(), S);
|
|
}
|
|
}
|
|
|
|
void StmtEmitter::visitDoCatchStmt(DoCatchStmt *S) {
|
|
Type formalExnType =
|
|
S->getCatches().front()->getErrorPattern()->getType();
|
|
auto &exnTL = SGF.getTypeLowering(formalExnType);
|
|
|
|
// Create the throw destination at the end of the function.
|
|
JumpDest throwDest = createJumpDest(S->getBody(),
|
|
FunctionSection::Postmatter);
|
|
SILArgument *exnArg =
|
|
throwDest.getBlock()->createBBArg(exnTL.getLoweredType());
|
|
|
|
// We always need an continuation block because we might fall out of
|
|
// a catch block. But we don't need a loop block unless the 'do'
|
|
// statement is labeled.
|
|
JumpDest endDest = createJumpDest(S->getBody());
|
|
|
|
// We don't need to do anything too fancy about emission if we don't
|
|
// have a label. Otherwise, assume we might break or continue.
|
|
bool hasLabel = (bool) S->getLabelInfo();
|
|
if (hasLabel) {
|
|
// Create a new basic block and jump into it.
|
|
JumpDest loopDest = createJumpDest(S->getBody());
|
|
SGF.B.emitBlock(loopDest.getBlock(), S);
|
|
|
|
// Set the destinations for 'break' and 'continue'.
|
|
SGF.BreakContinueDestStack.push_back({S, endDest, loopDest});
|
|
}
|
|
|
|
// Emit the body.
|
|
{
|
|
// Push the new throw destination.
|
|
llvm::SaveAndRestore<JumpDest> savedThrowDest(SGF.ThrowDest, throwDest);
|
|
|
|
visit(S->getBody());
|
|
}
|
|
|
|
// Emit the catch clauses.
|
|
{
|
|
// Move the insertion point to the throw destination.
|
|
SavedInsertionPoint savedIP(SGF, throwDest.getBlock(),
|
|
FunctionSection::Postmatter);
|
|
|
|
// The exception cleanup should be getting forwarded around
|
|
// correctly anyway, but push a scope to ensure it gets popped.
|
|
Scope exnScope(SGF.Cleanups, CleanupLocation(S));
|
|
|
|
// Take ownership of the exception.
|
|
ManagedValue exn = SGF.emitManagedRValueWithCleanup(exnArg, exnTL);
|
|
|
|
// Emit all the catch clauses, branching to the end destination if
|
|
// we fall out of one.
|
|
SGF.emitCatchDispatch(S, exn, S->getCatches(), endDest);
|
|
}
|
|
|
|
if (hasLabel) {
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
}
|
|
|
|
// Handle falling out of the do-block.
|
|
//
|
|
// It's important for good code layout that the insertion point be
|
|
// left in the original function section after this. So if
|
|
// emitOrDeleteBlock ever learns to just continue in the
|
|
// predecessor, we'll need to suppress that here.
|
|
if (emitOrDeleteBlock(SGF, endDest.getBlock(), S))
|
|
SGF.emitProfilerIncrement(S);
|
|
}
|
|
|
|
void StmtEmitter::visitCatchStmt(CatchStmt *S) {
|
|
llvm_unreachable("catch statement outside of context?");
|
|
}
|
|
|
|
void StmtEmitter::visitRepeatWhileStmt(RepeatWhileStmt *S) {
|
|
// Create a new basic block and jump into it.
|
|
SILBasicBlock *loopBB = createBasicBlock();
|
|
SGF.B.emitBlock(loopBB, S);
|
|
|
|
// Set the destinations for 'break' and 'continue'
|
|
JumpDest endDest = createJumpDest(S->getBody());
|
|
JumpDest condDest = createJumpDest(S->getBody());
|
|
SGF.BreakContinueDestStack.push_back({ S, endDest, condDest });
|
|
|
|
// Emit the body, which is always evaluated the first time around.
|
|
visit(S->getBody());
|
|
|
|
// Let's not differ from C99 6.8.5.2: "The evaluation of the controlling
|
|
// expression takes place after each execution of the loop body."
|
|
emitOrDeleteBlock(SGF, condDest.getBlock(), S);
|
|
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
// Evaluate the condition with the false edge leading directly
|
|
// to the continuation block.
|
|
Condition Cond = SGF.emitCondition(S->getCond(), /*hasFalseCode*/ false);
|
|
|
|
Cond.enterTrue(SGF);
|
|
SGF.emitProfilerIncrement(S->getBody());
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
SGF.B.createBranch(S->getCond(), loopBB);
|
|
}
|
|
|
|
Cond.exitTrue(SGF);
|
|
// Complete the conditional execution.
|
|
Cond.complete(SGF);
|
|
}
|
|
|
|
emitOrDeleteBlock(SGF, endDest.getBlock(), S);
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
}
|
|
|
|
void StmtEmitter::visitForStmt(ForStmt *S) {
|
|
// Enter a new scope.
|
|
Scope ForScope(SGF.Cleanups, CleanupLocation(S));
|
|
|
|
// Emit any local 'var' variables declared in the initializer.
|
|
for (auto D : S->getInitializerVarDecls()) {
|
|
SGF.visit(D);
|
|
}
|
|
|
|
if (auto *Initializer = S->getInitializer().getPtrOrNull()) {
|
|
SGF.emitIgnoredExpr(Initializer);
|
|
}
|
|
|
|
// If we ever reach an unreachable point, stop emitting statements.
|
|
// This will need revision if we ever add goto.
|
|
if (!SGF.B.hasValidInsertionPoint()) return;
|
|
|
|
// Create a new basic block and jump into it.
|
|
SILBasicBlock *loopBB = createBasicBlock();
|
|
SGF.B.emitBlock(loopBB, S);
|
|
|
|
JumpDest endDest = createJumpDest(S->getBody());
|
|
|
|
// Evaluate the condition with the false edge leading directly
|
|
// to the continuation block.
|
|
Condition Cond = S->getCond().isNonNull() ?
|
|
SGF.emitCondition(S->getCond().get(), /*hasFalseCode*/ false) :
|
|
Condition(loopBB, 0, 0, S); // Infinite loop.
|
|
|
|
// If there's a true edge, emit the body in it.
|
|
if (Cond.hasTrue()) {
|
|
Cond.enterTrue(SGF);
|
|
SGF.emitProfilerIncrement(S->getBody());
|
|
|
|
// Set the destinations for 'break' and 'continue'.
|
|
JumpDest incDest = createJumpDest(S->getBody());
|
|
SGF.BreakContinueDestStack.push_back({S, endDest, incDest});
|
|
|
|
visit(S->getBody());
|
|
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
|
|
emitOrDeleteBlock(SGF, incDest.getBlock(), S);
|
|
|
|
if (SGF.B.hasValidInsertionPoint() && S->getIncrement().isNonNull()) {
|
|
FullExpr Scope(SGF.Cleanups, CleanupLocation(S->getIncrement().get()));
|
|
// Ignore the result of the increment expression.
|
|
SGF.emitIgnoredExpr(S->getIncrement().get());
|
|
}
|
|
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
// Associate the loop body's closing brace with this branch.
|
|
RegularLocation L(S->getBody());
|
|
L.pointToEnd();
|
|
SGF.B.createBranch(L, loopBB);
|
|
}
|
|
Cond.exitTrue(SGF);
|
|
}
|
|
|
|
// Complete the conditional execution.
|
|
Cond.complete(SGF);
|
|
|
|
emitOrDeleteBlock(SGF, endDest.getBlock(), S);
|
|
}
|
|
|
|
void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
|
|
// Emit the 'generator' variable that we'll be using for iteration.
|
|
Scope OuterForScope(SGF.Cleanups, CleanupLocation(S));
|
|
SGF.visitPatternBindingDecl(S->getGenerator());
|
|
|
|
// If we ever reach an unreachable point, stop emitting statements.
|
|
// This will need revision if we ever add goto.
|
|
if (!SGF.B.hasValidInsertionPoint()) return;
|
|
|
|
// Create a stack allocation to hold values out of the generator.
|
|
// This will be initialized on every entry into the loop header and consumed
|
|
// by the loop body. On loop exit, the terminating value will be in the
|
|
// buffer.
|
|
auto optTy = S->getGeneratorNext()->getType()->getCanonicalType();
|
|
auto valTy = optTy.getAnyOptionalObjectType();
|
|
auto &optTL = SGF.getTypeLowering(optTy);
|
|
SILValue nextBuf = SGF.emitTemporaryAllocation(S, optTL.getLoweredType());
|
|
|
|
// Create a new basic block and jump into it.
|
|
JumpDest loopDest = createJumpDest(S->getBody());
|
|
SGF.B.emitBlock(loopDest.getBlock(), S);
|
|
|
|
// Set the destinations for 'break' and 'continue'.
|
|
JumpDest endDest = createJumpDest(S->getBody());
|
|
SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest });
|
|
|
|
// Advance the generator. Use a scope to ensure that any temporary stack
|
|
// allocations in the subexpression are immediately released.
|
|
{
|
|
Scope InnerForScope(SGF.Cleanups, CleanupLocation(S->getGeneratorNext()));
|
|
InitializationPtr nextInit(new KnownAddressInitialization(nextBuf));
|
|
SGF.emitExprInto(S->getGeneratorNext(), nextInit.get());
|
|
nextInit->finishInitialization(SGF);
|
|
}
|
|
|
|
// Continue if the value is present.
|
|
Condition Cond = SGF.emitCondition(
|
|
SGF.emitDoesOptionalHaveValue(S, nextBuf), S,
|
|
/*hasFalseCode=*/false, /*invertValue=*/false);
|
|
|
|
if (Cond.hasTrue()) {
|
|
Cond.enterTrue(SGF);
|
|
SGF.emitProfilerIncrement(S->getBody());
|
|
|
|
// Emit the loop body.
|
|
// The declared variable(s) for the current element are destroyed
|
|
// at the end of each loop iteration.
|
|
{
|
|
Scope InnerForScope(SGF.Cleanups, CleanupLocation(S->getBody()));
|
|
InitializationPtr initLoopVars
|
|
= SGF.emitPatternBindingInitialization(S->getPattern());
|
|
auto managedNext = SGF.emitManagedBufferWithCleanup(nextBuf);
|
|
ManagedValue val = SGF.emitUncheckedGetOptionalValueFrom(S,
|
|
managedNext,
|
|
optTL,
|
|
SGFContext(initLoopVars.get()));
|
|
if (!val.isInContext())
|
|
RValue(SGF, S, valTy, val).forwardInto(SGF, initLoopVars.get(), S);
|
|
visit(S->getBody());
|
|
}
|
|
|
|
// Loop back to the header.
|
|
if (SGF.B.hasValidInsertionPoint()) {
|
|
// Associate the loop body's closing brace with this branch.
|
|
RegularLocation L(S->getBody());
|
|
L.pointToEnd();
|
|
SGF.B.createBranch(L, loopDest.getBlock());
|
|
}
|
|
Cond.exitTrue(SGF);
|
|
}
|
|
|
|
// Complete the conditional execution.
|
|
Cond.complete(SGF);
|
|
|
|
emitOrDeleteBlock(SGF, endDest.getBlock(), S);
|
|
SGF.BreakContinueDestStack.pop_back();
|
|
|
|
// We do not need to destroy the value in the 'nextBuf' slot here, because
|
|
// either the 'for' loop finished naturally and the buffer contains '.None',
|
|
// or we exited by 'break' and the value in the buffer was consumed.
|
|
}
|
|
|
|
void StmtEmitter::visitBreakStmt(BreakStmt *S) {
|
|
assert(S->getTarget() && "Sema didn't fill in break target?");
|
|
SGF.emitBreakOutOf(S, S->getTarget());
|
|
}
|
|
|
|
void SILGenFunction::emitBreakOutOf(SILLocation loc, Stmt *target) {
|
|
CurrentSILLoc = loc;
|
|
|
|
// Find the target JumpDest based on the target that sema filled into the
|
|
// stmt.
|
|
for (auto &elt : BreakContinueDestStack) {
|
|
if (target == elt.Target) {
|
|
Cleanups.emitBranchAndCleanups(elt.BreakDest, loc);
|
|
return;
|
|
}
|
|
}
|
|
llvm_unreachable("Break has available target block.");
|
|
}
|
|
|
|
void StmtEmitter::visitContinueStmt(ContinueStmt *S) {
|
|
assert(S->getTarget() && "Sema didn't fill in continue target?");
|
|
|
|
SGF.CurrentSILLoc = S;
|
|
|
|
// Find the target JumpDest based on the target that sema filled into the
|
|
// stmt.
|
|
for (auto &elt : SGF.BreakContinueDestStack) {
|
|
if (S->getTarget() == elt.Target) {
|
|
SGF.Cleanups.emitBranchAndCleanups(elt.ContinueDest, S);
|
|
return;
|
|
}
|
|
}
|
|
llvm_unreachable("Break has available target block.");
|
|
}
|
|
|
|
void StmtEmitter::visitSwitchStmt(SwitchStmt *S) {
|
|
// Implemented in SILGenPattern.cpp.
|
|
SGF.emitSwitchStmt(S);
|
|
}
|
|
|
|
void StmtEmitter::visitCaseStmt(CaseStmt *S) {
|
|
llvm_unreachable("cases should be lowered as part of switch stmt");
|
|
}
|
|
|
|
void StmtEmitter::visitFallthroughStmt(FallthroughStmt *S) {
|
|
// Implemented in SILGenPattern.cpp.
|
|
SGF.emitSwitchFallthrough(S);
|
|
}
|
|
|
|
void StmtEmitter::visitFailStmt(FailStmt *S) {
|
|
// Jump to the failure block.
|
|
assert(SGF.FailDest.isValid() && "too big to fail");
|
|
SGF.Cleanups.emitBranchAndCleanups(SGF.FailDest, S);
|
|
}
|
|
|
|
/// Return a basic block suitable to be the destination block of a
|
|
/// try_apply instruction. The block is implicitly emitted and filled in.
|
|
SILBasicBlock *
|
|
SILGenFunction::getTryApplyErrorDest(SILLocation loc,
|
|
SILResultInfo exnResult) {
|
|
assert(exnResult.getConvention() == ResultConvention::Owned);
|
|
|
|
// For now, don't try to re-use destination blocks for multiple
|
|
// failure sites.
|
|
SILBasicBlock *destBB = createBasicBlock(FunctionSection::Postmatter);
|
|
SILValue exn = destBB->createBBArg(exnResult.getSILType());
|
|
|
|
assert(B.hasValidInsertionPoint() && B.insertingAtEndOfBlock());
|
|
SavedInsertionPoint savedIP(*this, destBB, FunctionSection::Postmatter);
|
|
emitTryApplyErrorBranch(loc, emitManagedRValueWithCleanup(exn));
|
|
|
|
return destBB;
|
|
}
|
|
|
|
/// Emit an error-propagation branch with a managed error value as if
|
|
/// from a try_apply instruction.
|
|
void SILGenFunction::emitTryApplyErrorBranch(SILLocation loc,
|
|
ManagedValue exn) {
|
|
if (ThrowDest.isValid()) {
|
|
FullExpr scope(Cleanups, CleanupLocation::get(loc));
|
|
emitThrow(loc, exn);
|
|
} else {
|
|
// Claim the exception so that the cleanup doesn't end being
|
|
// emitted in the normal control flow (which we're likely to
|
|
// resume after this).
|
|
exn.forward(*this);
|
|
|
|
SGM.diagnose(loc, diag::unhandled_throw_from_apply);
|
|
B.createUnreachable(loc);
|
|
}
|
|
}
|
|
|
|
void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
|
|
bool emitWillThrow) {
|
|
assert(ThrowDest.isValid() &&
|
|
"calling emitThrow with invalid throw destination!");
|
|
|
|
// Claim the exception value. If we need to handle throwing
|
|
// cleanups, the correct thing to do here is to recreate the
|
|
// exception's cleanup when emitting each cleanup we branch through.
|
|
// But for now we aren't bothering.
|
|
SILValue exn = exnMV.forward(*this);
|
|
|
|
if (emitWillThrow) {
|
|
// Generate a call to the 'swift_willThrow' runtime function to allow the
|
|
// debugger to catch the throw event.
|
|
B.createBuiltin(loc, SGM.getASTContext().getIdentifier("willThrow"),
|
|
SGM.Types.getEmptyTupleType(), {}, {exn});
|
|
}
|
|
|
|
// Branch to the cleanup destination.
|
|
Cleanups.emitBranchAndCleanups(ThrowDest, loc, exn);
|
|
}
|
|
|
|
void SILGenModule::visitIfConfigDecl(IfConfigDecl *ICD) {
|
|
// Nothing to do for these kinds of decls - anything active has been added
|
|
// to the enclosing declaration.
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// SILGenModule visitTopLevelCodeDecl implementation
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
void SILGenModule::visitTopLevelCodeDecl(TopLevelCodeDecl *td) {
|
|
// Emit top-level statements and expressions into the toplevel function until
|
|
// we hit an unreachable point.
|
|
assert(TopLevelSGF && "top-level code in a non-main source file!");
|
|
// TODO:
|
|
if (!TopLevelSGF->B.hasValidInsertionPoint())
|
|
return;
|
|
|
|
TopLevelSGF->emitStmt(td->getBody());
|
|
}
|