Files
swift-mirror/lib/SILGen/SILGenStmt.cpp
Joe Groff 31e761ca3e SILGen: Don't overrelease the for-each iteration value when break-ing.
We don't ever need to destroy_addr the iteration value buffer, because either the for loop exited normally because it's nil, or it exited by 'break' and it contains a stale 'Some' value that was consumed by the loop body. Fixes <rdar://problem/16687672>.

Swift SVN r16942
2014-04-27 21:39:03 +00:00

644 lines
21 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"
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)...);
}
//===--------------------------------------------------------------------===//
// SILGenFunction visit*Stmt implementation
//===--------------------------------------------------------------------===//
/// 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.
static void emitOrDeleteBlock(SILBuilder &B, SILBasicBlock *BB,
SILLocation BranchLoc) {
if (BB->pred_empty()) {
// If the block is unused, we don't need it; just delete it.
BB->eraseFromParent();
} else {
// Otherwise, continue emitting code in BB.
B.emitBlock(BB, BranchLoc);
}
}
static SILValue emitConditionValue(SILGenFunction &gen, Expr *E) {
// Sema forces conditions to have Builtin.i1 type, which guarantees this.
SILValue V;
{
FullExpr Scope(gen.Cleanups, CleanupLocation(E));
V = gen.emitRValue(E).forwardAsSingleValue(gen, E);
}
assert(V.getType().castTo<BuiltinIntegerType>()->isFixedWidth(1));
return V;
}
Condition SILGenFunction::emitCondition(Expr *E,
bool hasFalseCode, bool invertValue,
ArrayRef<SILType> contArgs) {
assert(B.hasValidInsertionPoint() &&
"emitting condition at unreachable point");
return emitCondition(emitConditionValue(*this, E), E,
hasFalseCode, invertValue, contArgs);
}
/// Information about a conditional binding.
struct ConditionalBinding {
PatternBindingDecl *PBD;
std::unique_ptr<TemporaryInitialization> OptAddr;
};
static std::unique_ptr<TemporaryInitialization>
emitConditionalBindingBuffer(SILGenFunction &gen,
StmtCondition cond) {
if (auto CB = cond.dyn_cast<PatternBindingDecl*>()) {
assert(CB->isConditional());
assert(CB->getInit());
auto &optTL = gen.getTypeLowering(CB->getInit()->getType());
return gen.emitTemporary(CB, optTL);
}
return nullptr;
}
static std::pair<Condition, Optional<ConditionalBinding>>
emitConditionalBinding(SILGenFunction &gen,
PatternBindingDecl *CB,
std::unique_ptr<TemporaryInitialization> temp,
bool hasFalseCode) {
// Emit the optional value, in its own inner scope.
{
FullExpr initScope(gen.Cleanups, CB);
gen.emitExprInto(CB->getInit(), temp.get());
}
// Test for a value in the optional.
SILValue hasValue = gen.emitDoesOptionalHaveValue(CB, temp->getAddress());
// Emit the condition on the presence of the value.
Condition C = gen.emitCondition(hasValue, CB, hasFalseCode);
return {C, ConditionalBinding{CB, std::move(temp)}};
}
static void
enterTrueConditionalBinding(SILGenFunction &gen,
const ConditionalBinding &CB) {
// Bind variables.
InitializationPtr init
= gen.emitPatternBindingInitialization(CB.PBD->getPattern());
FullExpr scope(gen.Cleanups, CB.PBD);
auto &optTL = gen.getTypeLowering(CB.PBD->getPattern()->getType());
// Take the value out of the temporary buffer into the variables.
ManagedValue mv = gen.emitGetOptionalValueFrom(CB.PBD,
ManagedValue(CB.OptAddr->getAddress(),
CB.OptAddr->getInitializedCleanup()),
optTL, SGFContext(init.get()));
if (!mv.isInContext()) {
RValue(gen, CB.PBD, CB.PBD->getPattern()->getType()->getCanonicalType(), mv)
.forwardInto(gen, init.get(), CB.PBD);
}
// FIXME: Keep the cleanup dormant so we can reactivate it on the false
// branch?
}
static void
enterFalseConditionalBinding(SILGenFunction &gen,
const ConditionalBinding &CB) {
// Destroy the value in the optional buffer.
gen.B.emitDestroyAddr(CB.PBD, CB.OptAddr->getAddress());
}
static std::pair<Condition, Optional<ConditionalBinding>>
emitStmtCondition(SILGenFunction &gen, StmtCondition C,
std::unique_ptr<TemporaryInitialization> temp,
bool hasFalseCode) {
if (auto E = C.dyn_cast<Expr*>()) {
return {gen.emitCondition(E, hasFalseCode), Nothing};
}
if (auto CB = C.dyn_cast<PatternBindingDecl*>()) {
return emitConditionalBinding(gen, CB, std::move(temp), hasFalseCode);
}
llvm_unreachable("unknown condition");
}
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();
SILBasicBlock *TrueBB = createBasicBlock();
for (SILType argTy : contArgs) {
new (F.getModule()) SILArgument(argTy, ContBB);
}
SILBasicBlock *FalseBB, *FalseDestBB;
if (hasFalseCode) {
FalseBB = FalseDestBB = createBasicBlock();
} else {
FalseBB = nullptr;
FalseDestBB = ContBB;
}
if (invertValue)
B.createCondBranch(Loc, V, FalseDestBB, TrueBB);
else
B.createCondBranch(Loc, V, TrueBB, FalseDestBB);
return Condition(TrueBB, FalseBB, ContBB, Loc);
}
void SILGenFunction::visitBraceStmt(BraceStmt *S) {
// Enter a new scope.
LexicalScope BraceScope(Cleanups, *this, CleanupLocation(S));
const unsigned ReturnStmtType = 0;
const unsigned ContinueStmtType = 1;
const unsigned UnknownStmtType = 2;
unsigned StmtType = UnknownStmtType;
// There's nothing to do if this is a configuration block.
if (S->isConfigBlock()) {
return;
}
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 (!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*>()) {
emitIgnoredExpr(E);
} else {
visit(ESD.get<Decl*>());
}
}
}
namespace {
/// IndirectReturnInitialization - represents initializing an indirect return
/// value.
class IndirectReturnInitialization : public SingleBufferInitialization {
SILValue address;
public:
IndirectReturnInitialization(SILValue address)
: address(address) {}
SILValue getAddressOrNull() const override { return address; }
};
} // end anonymous namespace
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 IndirectReturnInitialization(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 SILGenFunction::visitReturnStmt(ReturnStmt *S) {
CurrentSILLoc = S;
ReturnLocation Loc(S);
SILValue ArgV;
if (!S->hasResult())
// Void return.
Cleanups.emitBranchAndCleanups(ReturnDest, Loc);
else
emitReturnExpr(Loc, S->getResult());
}
void SILGenFunction::visitIfStmt(IfStmt *S) {
Scope condBufferScope(Cleanups, S);
auto condBuffer = emitConditionalBindingBuffer(*this, S->getCond());
// We need a false branch if we have an 'else' block or if we have a
// pattern binding, to clean up the unconsumed optional value.
bool hasBindings = condBuffer.get();
auto CondPair = emitStmtCondition(*this, S->getCond(), std::move(condBuffer),
S->getElseStmt() != nullptr
|| hasBindings);
auto &Cond = CondPair.first;
auto &CondBinding = CondPair.second;
if (Cond.hasTrue()) {
Cond.enterTrue(B);
{
// Enter a scope for pattern variables.
Scope trueScope(Cleanups, S);
if (CondBinding)
enterTrueConditionalBinding(*this, *CondBinding);
visit(S->getThenStmt());
}
Cond.exitTrue(B);
}
if (Cond.hasFalse()) {
Cond.enterFalse(B);
if (CondBinding)
enterFalseConditionalBinding(*this, *CondBinding);
if (S->getElseStmt())
visit(S->getElseStmt());
Cond.exitFalse(B);
}
Cond.complete(B);
}
void SILGenFunction::visitIfConfigStmt(IfConfigStmt *S) {
// Active members are attached to the enclosing declaration, so there's no
// need to walk anything within.
}
void SILGenFunction::visitWhileStmt(WhileStmt *S) {
Scope condBufferScope(Cleanups, S);
// Allocate a buffer for pattern binding conditions outside the loop.
auto condBuffer = emitConditionalBindingBuffer(*this, S->getCond());
// Create a new basic block and jump into it.
SILBasicBlock *LoopBB = createBasicBlock();
B.emitBlock(LoopBB, S);
// Evaluate the condition with the false edge leading directly
// to the continuation block.
auto CondPair = emitStmtCondition(*this, S->getCond(), std::move(condBuffer),
/*hasFalseCode*/ false);
auto &Cond = CondPair.first;
auto &CondBinding = CondPair.second;
// Set the destinations for 'break' and 'continue'
SILBasicBlock *EndBB = createBasicBlock();
BreakContinueDestStack.push_back({
S,
JumpDest(EndBB, getCleanupsDepth(), CleanupLocation(S->getBody())),
JumpDest(LoopBB, getCleanupsDepth(), CleanupLocation(S->getBody()))
});
// If there's a true edge, emit the body in it.
if (Cond.hasTrue()) {
Cond.enterTrue(B);
{
// Enter a scope for pattern variables.
Scope trueScope(Cleanups, S);
if (CondBinding)
enterTrueConditionalBinding(*this, *CondBinding);
visit(S->getBody());
}
if (B.hasValidInsertionPoint()) {
// Associate the loop body's closing brace with this branch.
RegularLocation L(S->getBody());
L.pointToEnd();
B.createBranch(L, LoopBB);
}
Cond.exitTrue(B);
}
// Complete the conditional execution.
Cond.complete(B);
if (CondBinding)
enterFalseConditionalBinding(*this, *CondBinding);
emitOrDeleteBlock(B, EndBB, S);
BreakContinueDestStack.pop_back();
}
void SILGenFunction::visitDoWhileStmt(DoWhileStmt *S) {
// Create a new basic block and jump into it.
SILBasicBlock *LoopBB = createBasicBlock();
B.emitBlock(LoopBB, S);
// Set the destinations for 'break' and 'continue'
SILBasicBlock *EndBB = createBasicBlock();
SILBasicBlock *CondBB = createBasicBlock();
BreakContinueDestStack.push_back({
S,
JumpDest(EndBB, getCleanupsDepth(), CleanupLocation(S->getBody())),
JumpDest(CondBB, getCleanupsDepth(), CleanupLocation(S->getBody()))
});
// 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(B, CondBB, S);
if (B.hasValidInsertionPoint()) {
// Evaluate the condition with the false edge leading directly
// to the continuation block.
Condition Cond = emitCondition(S->getCond(), /*hasFalseCode*/ false);
Cond.enterTrue(B);
if (B.hasValidInsertionPoint()) {
B.createBranch(S->getCond(), LoopBB);
}
Cond.exitTrue(B);
// Complete the conditional execution.
Cond.complete(B);
}
emitOrDeleteBlock(B, EndBB, S);
BreakContinueDestStack.pop_back();
}
void SILGenFunction::visitForStmt(ForStmt *S) {
// Enter a new scope.
Scope ForScope(Cleanups, CleanupLocation(S));
// Emit any local 'var' variables declared in the initializer.
for (auto D : S->getInitializerVarDecls())
visit(D);
if (auto *Initializer = S->getInitializer().getPtrOrNull()) {
emitIgnoredExpr(Initializer);
}
// If we ever reach an unreachable point, stop emitting statements.
// This will need revision if we ever add goto.
if (!B.hasValidInsertionPoint()) return;
// Create a new basic block and jump into it.
SILBasicBlock *LoopBB = createBasicBlock();
B.emitBlock(LoopBB, S);
// Set the destinations for 'break' and 'continue'
SILBasicBlock *IncBB = createBasicBlock();
SILBasicBlock *EndBB = createBasicBlock();
BreakContinueDestStack.push_back({
S,
JumpDest(EndBB, getCleanupsDepth(), CleanupLocation(S->getBody())),
JumpDest(IncBB, getCleanupsDepth(), CleanupLocation(S->getBody()))
});
// Evaluate the condition with the false edge leading directly
// to the continuation block.
Condition Cond = S->getCond().isNonNull() ?
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(B);
visit(S->getBody());
emitOrDeleteBlock(B, IncBB, S);
if (B.hasValidInsertionPoint() && S->getIncrement().isNonNull()) {
FullExpr Scope(Cleanups, CleanupLocation(S->getIncrement().get()));
// Don't bother loading the result of the increment expression.
emitRValue(S->getIncrement().get(), SGFContext::AllowPlusZero);
}
if (B.hasValidInsertionPoint()) {
// Accosiate the loop body's closing brace with this branch.
RegularLocation L(S->getBody());
L.pointToEnd();
B.createBranch(L, LoopBB);
}
Cond.exitTrue(B);
}
// Complete the conditional execution.
Cond.complete(B);
emitOrDeleteBlock(B, EndBB, S);
BreakContinueDestStack.pop_back();
}
namespace {
/// NextForEachValueInitialization - initialization for the 'next' value buffer
/// used during for each loop codegen.
class NextForEachValueInitialization : public SingleBufferInitialization {
SILValue address;
public:
NextForEachValueInitialization(SILValue address)
: address(address) {}
SILValue getAddressOrNull() const override { return address; }
};
} // end anonymous namespace
void SILGenFunction::visitForEachStmt(ForEachStmt *S) {
// Emit the 'generator' variable that we'll be using for iteration.
Scope OuterForScope(Cleanups, CleanupLocation(S));
visitPatternBindingDecl(S->getGenerator());
// If we ever reach an unreachable point, stop emitting statements.
// This will need revision if we ever add goto.
if (!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 = getTypeLowering(optTy);
SILValue nextBuf = emitTemporaryAllocation(S, optTL.getLoweredType());
// Create a new basic block and jump into it.
SILBasicBlock *LoopBB = createBasicBlock();
B.emitBlock(LoopBB, S);
// Set the destinations for 'break' and 'continue'.
SILBasicBlock *EndBB = createBasicBlock();
BreakContinueDestStack.push_back({
S,
JumpDest(EndBB, getCleanupsDepth(), CleanupLocation(S->getBody())),
JumpDest(LoopBB, getCleanupsDepth(), CleanupLocation(S->getBody()))
});
// Advance the generator. Use a scope to ensure that any temporary stack
// allocations in the subexpression are immediately released.
{
Scope InnerForScope(Cleanups, CleanupLocation(S->getGeneratorNext()));
InitializationPtr nextInit(new NextForEachValueInitialization(nextBuf));
emitExprInto(S->getGeneratorNext(), nextInit.get());
nextInit->finishInitialization(*this);
}
// Continue if the value is present.
Condition Cond = emitCondition(
emitDoesOptionalHaveValue(S, nextBuf), S,
/*hasFalseCode=*/false, /*invertValue=*/false);
if (Cond.hasTrue()) {
Cond.enterTrue(B);
// Emit the loop body.
// The declared variable(s) for the current element are destroyed
// at the end of each loop iteration.
{
Scope InnerForScope(Cleanups, CleanupLocation(S->getBody()));
InitializationPtr initLoopVars
= emitPatternBindingInitialization(S->getPattern());
ManagedValue val = emitGetOptionalValueFrom(S,
ManagedValue::forUnmanaged(nextBuf),
optTL,
SGFContext(initLoopVars.get()));
if (!val.isInContext())
RValue(*this, S, valTy, val).forwardInto(*this, initLoopVars.get(), S);
visit(S->getBody());
}
// Loop back to the header.
if (B.hasValidInsertionPoint()) {
// Associate the loop body's closing brace with this branch.
RegularLocation L(S->getBody());
L.pointToEnd();
B.createBranch(L, LoopBB);
}
Cond.exitTrue(B);
}
// Complete the conditional execution.
Cond.complete(B);
emitOrDeleteBlock(B, EndBB, S);
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 SILGenFunction::visitBreakStmt(BreakStmt *S) {
assert(S->getTarget() && "Sema didn't fill in break target?");
CurrentSILLoc = S;
// Find the target JumpDest based on the target that sema filled into the
// stmt.
for (auto elt : BreakContinueDestStack) {
if (S->getTarget() == std::get<0>(elt)) {
Cleanups.emitBranchAndCleanups(std::get<1>(elt), S);
return;
}
}
assert(0 && "Break has available target block.");
}
void SILGenFunction::visitContinueStmt(ContinueStmt *S) {
assert(S->getTarget() && "Sema didn't fill in continue target?");
CurrentSILLoc = S;
// Find the target JumpDest based on the target that sema filled into the
// stmt.
for (auto elt : BreakContinueDestStack) {
if (S->getTarget() == std::get<0>(elt)) {
Cleanups.emitBranchAndCleanups(std::get<2>(elt), S);
return;
}
}
assert(0 && "Break has available target block.");
}
void SILGenFunction::visitSwitchStmt(SwitchStmt *S) {
// Implemented in SILGenPattern.cpp.
emitSwitchStmt(S);
}
void SILGenFunction::visitCaseStmt(CaseStmt *S) {
llvm_unreachable("cases should be lowered as part of switch stmt");
}
void SILGenFunction::visitFallthroughStmt(FallthroughStmt *S) {
// Implemented in SILGenPattern.cpp.
emitSwitchFallthrough(S);
}
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->visit(td->getBody());
}