diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 3044a730e83..93a7fbf64e7 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -30,9 +30,9 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag diag, diag, std::forward(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 @@ -48,111 +48,175 @@ static void emitOrDeleteBlock(SILBuilder &B, SILBasicBlock *BB, } } -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()->isFixedWidth(1)); - - return V; -} - Condition SILGenFunction::emitCondition(Expr *E, bool hasFalseCode, bool invertValue, ArrayRef contArgs) { assert(B.hasValidInsertionPoint() && "emitting condition at unreachable point"); - return emitCondition(emitConditionValue(*this, E), E, - hasFalseCode, invertValue, contArgs); + // 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()->isFixedWidth(1)); + + return emitCondition(V, E, hasFalseCode, invertValue, contArgs); } /// Information about a conditional binding. struct ConditionalBinding { PatternBindingDecl *PBD; std::unique_ptr OptAddr; + + ConditionalBinding(PatternBindingDecl *PBD, + std::unique_ptr &&OptAddr) + : PBD(PBD), OptAddr(std::move(OptAddr)) { + } }; -static std::unique_ptr -emitConditionalBindingBuffer(SILGenFunction &gen, - StmtCondition cond) { - assert(cond.size() == 1 && "General conditions are not handled yet"); - - if (auto CB = cond[0].getBinding()) { - assert(CB->isConditional()); - assert(CB->getInit()); +/// Emit the buffers for any pattern bindings that occur in the specified +/// condition. This is one alloc_stack per bound variable, e.g. in: +/// if let x = foo(), y = bar() { +/// you'd get an alloc_stack for 'x' and 'y'. +static std::vector +emitConditionalBindingBuffers(SILGenFunction &gen, StmtCondition cond) { + std::vector buffers; + for (auto elt : cond) { + if (auto CB = elt.getBinding()) { + assert(CB->isConditional() && CB->getInit()); - auto &optTL = gen.getTypeLowering(CB->getInit()->getType()); - return gen.emitTemporary(CB, optTL); + auto &optTL = gen.getTypeLowering(CB->getInit()->getType()); + buffers.emplace_back(ConditionalBinding(CB, gen.emitTemporary(CB,optTL))); + } } - return nullptr; -} - -static std::pair> -emitConditionalBinding(SILGenFunction &gen, - PatternBindingDecl *CB, - std::unique_ptr 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)}}; + return buffers; } 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. - // At this point we've already checked that the - ManagedValue mv = gen.emitUncheckedGetOptionalValueFrom(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); +emitConditionalPatternBindings(SILGenFunction &gen, + std::vector &condBuffers) { + for (const ConditionalBinding &CB : condBuffers) { + // 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. + // At this point we've already checked that the + ManagedValue mv = gen.emitUncheckedGetOptionalValueFrom(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? } - - // 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()); +emitConditionalBindingBufferDestroys(SILGenFunction &gen, + const std::vector &buffers) { + for (auto &elt : buffers) { + // Destroy the value in the optional buffer. + gen.B.emitDestroyAddr(elt.PBD, elt.OptAddr->getAddress()); + } } -static std::pair> +/// Emit the code to evaluate a general StmtCondition and produce a number of +/// basic blocks: +/// 1) the insertion point is the block in which all of the predicates +/// evalute to true and any patterns match and have their buffers +/// initialized. +/// 2) the returned list of blocks indicates the destruction order for any +/// contained pattern bindings. Jumping to the first block in the list +/// will destroy all of the buffers. The last block in the list will +/// continue execution after the condition fails and is fully cleaned up. +/// +static llvm::TinyPtrVector emitStmtCondition(SILGenFunction &gen, StmtCondition C, - std::unique_ptr temp, - bool hasFalseCode) { - assert(C.size() == 1 && "General conditions are not handled yet"); - - auto elt = C[0]; - if (auto E = elt.getCondition()) - return {gen.emitCondition(E, hasFalseCode), None}; + std::vector &buffers) { + assert(gen.B.hasValidInsertionPoint() && + "emitting condition at unreachable point"); - auto CB = elt.getBinding(); - return emitConditionalBinding(gen, CB, std::move(temp), hasFalseCode); + // Create the block for overall failure of the condition. We build the + // CleanupBlocks list backwards then reverse it before returning it (for + // algorithmic efficiency, which admittedly probably doesn't matter). + llvm::TinyPtrVector CleanupBlocks; + CleanupBlocks.push_back(gen.createBasicBlock()); + + unsigned nextBuffer = 0; + + // Evaluate each condition/pattern in sequence. Any patterns are optional + // unwraps that can fail, so they need control flow of their own. + for (auto &elt : C) { + // If this is a simple expression, just emit it and continue. + if (auto *expr = elt.getCondition()) { + SILValue V; + { + FullExpr Scope(gen.Cleanups, CleanupLocation(expr)); + V = gen.emitRValue(expr).forwardAsSingleValue(gen, expr); + } + assert(V.getType().castTo()->isFixedWidth(1) && + "Sema forces conditions to have Builtin.i1 type"); + + SILBasicBlock *ContBB = gen.createBasicBlock(); + gen.B.createCondBranch(expr, V, ContBB, CleanupBlocks.back()); + gen.B.emitBlock(ContBB); + continue; + } + + // Otherwise, we have a pattern initialized by an optional. Emit the + // optional expression and test its presence. + auto *binding = elt.getBinding(); + assert(binding && "Unknown condition case"); + assert(nextBuffer < buffers.size() && buffers[nextBuffer].PBD == binding && + "Buffer mismatch"); + + auto &buffer = buffers[nextBuffer++]; + + // Emit the optional value, in its own inner scope. + { + FullExpr initScope(gen.Cleanups, binding); + gen.emitExprInto(binding->getInit(), buffer.OptAddr.get()); + } + + // Test for a value in the optional. + SILValue hasValue = gen.emitDoesOptionalHaveValue(binding, + buffer.OptAddr->getAddress()); + + // Now that we evaluated some thing into the optional buffer, we need to + // clean it up on failure paths. If this is the first condition, we can + // insert the cleanup code directly in the false block. Otherwise, we need + // to create a new block that branches to the cleanup code we already + // created. We can tell this based on whether FalseBB already has + // predecessors. + SILBasicBlock *FalseDest = CleanupBlocks.back(); + if (!FalseDest->pred_empty()) { + // Something is using it, create a new block. + FalseDest = gen.createBasicBlock(); + SILBuilder(FalseDest).createBranch(binding, CleanupBlocks.back()); + CleanupBlocks.push_back(FalseDest); + } + SILBuilder(FalseDest, FalseDest->begin()) + .createDestroyAddr(binding, buffer.OptAddr->getAddress()); + + // Finally, emit the continuation block and the conditional branch. + SILBasicBlock *ContBB = gen.createBasicBlock(); + gen.B.createCondBranch(binding, hasValue, ContBB, CleanupBlocks.back()); + + // Continue on the success path as the current block. + gen.B.emitBlock(ContBB); + } + + std::reverse(CleanupBlocks.begin(), CleanupBlocks.end()); + return CleanupBlocks; } Condition SILGenFunction::emitCondition(SILValue V, SILLocation Loc, @@ -281,41 +345,79 @@ void SILGenFunction::visitReturnStmt(ReturnStmt *S) { void SILGenFunction::visitIfStmt(IfStmt *S) { Scope condBufferScope(Cleanups, S); - // 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 needsFalseBranch = S->getElseStmt() != nullptr; - // If there is any PBD, the first one will be. The only case without a - // pattern is the simple "if cond" case. - needsFalseBranch |= S->getCond()[0].getBinding() != nullptr; + std::vector condBuffers = + emitConditionalBindingBuffers(*this, S->getCond()); + auto CleanupBlocks = emitStmtCondition(*this, S->getCond(), condBuffers); - auto condBuffer = emitConditionalBindingBuffer(*this, S->getCond()); - auto CondPair = emitStmtCondition(*this, S->getCond(), std::move(condBuffer), - needsFalseBranch); - 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()); + // Emit the 'true' side of the branch code. + { + // Enter a scope for pattern variables. + Scope trueScope(Cleanups, S); + + // In the true block, we extract the element value of the optional buffer + // into a temporary that the pattern is bound to, and consume the buffers. + emitConditionalPatternBindings(*this, condBuffers); + + // Then we emit the code for the "then" part of the if. + visit(S->getThenStmt()); + } + + // If there is no else, just branch to the start of the cleanup list for + // continuation. + if (!S->getElseStmt()) { + if (B.hasValidInsertionPoint()) { + // If the final cleanup block has a destroy in it, then we need another + // cleanup block to jump to which doesn't do that. + if (!CleanupBlocks.back()->empty()) { + SILBasicBlock *LastBlock = CleanupBlocks.back(); + SILBasicBlock *NewCont = createBasicBlock(); + CleanupBlocks.push_back(NewCont); + SILBuilder(LastBlock).createBranch(S, NewCont); + } + + RegularLocation L(S->getThenStmt()); + L.pointToEnd(); + B.createBranch(L, CleanupBlocks.back()); } - Cond.exitTrue(B); + + // Move all of the cleanup blocks into reasonable spots, leaving the + // insertion point in the continuation block. + for (auto BB : CleanupBlocks) { + B.clearInsertionPoint(); + B.emitBlock(BB); + } + return; } - - if (Cond.hasFalse()) { - Cond.enterFalse(B); - if (CondBinding) - enterFalseConditionalBinding(*this, *CondBinding); - if (S->getElseStmt()) - visit(S->getElseStmt()); - Cond.exitFalse(B); + + // If there is 'else' logic, create a new ContBB to be the merge point and + // jump to it from the true case. + auto *ContBB = createBasicBlock(); + if (B.hasValidInsertionPoint()) { + RegularLocation L(S->getThenStmt()); + L.pointToEnd(); + B.createBranch(L, ContBB); } - - Cond.complete(B); + + // With the true side done, work on the 'else' logic. Start by moving all of + // the cleanup blocks into reasonable spots, leaving the insertion point in + // the continuation block. + for (auto BB : CleanupBlocks) { + B.clearInsertionPoint(); + B.emitBlock(BB); + } + + visit(S->getElseStmt()); + if (B.hasValidInsertionPoint()) { + RegularLocation L(S->getElseStmt()); + L.pointToEnd(); + B.createBranch(L, ContBB); + } + + // Leave things in the continuation block if it is live, remove it if not. + if (ContBB->pred_empty()) + ContBB->eraseFromParent(); + else + B.emitBlock(ContBB); } void SILGenFunction::visitIfConfigStmt(IfConfigStmt *S) { @@ -326,53 +428,53 @@ void SILGenFunction::visitIfConfigStmt(IfConfigStmt *S) { 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()); + std::vector condBuffers = + emitConditionalBindingBuffers(*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; + // Evaluate the condition, leaving the insertion point in the "true" block + // and getting the cleanup blocks. + auto CleanupBlocks = emitStmtCondition(*this, S->getCond(), condBuffers); - // Set the destinations for 'break' and 'continue' - SILBasicBlock *EndBB = createBasicBlock(); + // Set the destinations for 'break' and 'continue'. + // FIXME: this is incorrect for continue: + // "while let" + continue silgen's incorrect code BreakContinueDestStack.push_back(std::make_tuple( S, - JumpDest(EndBB, getCleanupsDepth(), CleanupLocation(S->getBody())), + JumpDest(CleanupBlocks.front(), + 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); + // Continue to emit the true case, the loop body. + { + // Enter a scope for pattern variables. + Scope trueScope(Cleanups, S); + + // In the true block, we extract the element values of the optional buffers + // into temporaries that the pattern is bound to, and consume the buffers. + emitConditionalPatternBindings(*this, condBuffers); + + visit(S->getBody()); } - - // Complete the conditional execution. - Cond.complete(B); - if (CondBinding) - enterFalseConditionalBinding(*this, *CondBinding); - - emitOrDeleteBlock(B, EndBB, S); + if (B.hasValidInsertionPoint()) { + // Associate the loop body's closing brace with this branch. + RegularLocation L(S->getBody()); + L.pointToEnd(); + B.createBranch(L, LoopBB); + } + BreakContinueDestStack.pop_back(); + + // With the loop done, work on the continuation logic. Start by moving all of + // the cleanup blocks into reasonable spots, leaving the insertion point in + // the last continuation block. + for (auto BB : CleanupBlocks) { + B.clearInsertionPoint(); + B.emitBlock(BB); + } } void SILGenFunction::visitDoWhileStmt(DoWhileStmt *S) { diff --git a/test/SILGen/if_while_binding.swift b/test/SILGen/if_while_binding.swift index 3067b8afbb6..eb519494066 100644 --- a/test/SILGen/if_while_binding.swift +++ b/test/SILGen/if_while_binding.swift @@ -94,8 +94,6 @@ func while_loop() { } // CHECK: [[LOOP_EXIT]]: // CHECK: destroy_addr [[OPT_BUF]] - // CHECK: br [[LOOP_END]] - // CHECK: [[LOOP_END]]: // CHECK: dealloc_stack [[OPT_BUF]] } @@ -114,3 +112,37 @@ func while_loop_generic(source: () -> T?) { while let x = source() { } } + +// Improve 'if let' to avoid optional pyramid of doom +// CHECK-LABEL: sil hidden @_TF16if_while_binding16while_loop_multiFT_T_ +func while_loop_multi() { + // CHECK: [[OPT_BUF1:%.*]] = alloc_stack $Optional + // CHECK: [[OPT_BUF2:%.*]] = alloc_stack $Optional + // CHECK: br [[LOOP_ENTRY:bb[0-9]+]] + // CHECK: [[LOOP_ENTRY]]: + // CHECK: [[HAS_VALUE1:%.*]] = select_enum_addr [[OPT_BUF1]]#1 + // CHECK: cond_br [[HAS_VALUE1]], [[CHECKBUF2:bb[0-9]+]], [[LOOP_EXIT1:bb[0-9]+]] + // CHECK: [[CHECKBUF2]]: + // CHECK: [[HAS_VALUE2:%.*]] = select_enum_addr [[OPT_BUF2]]#1 + // CHECK: cond_br [[HAS_VALUE2]], [[LOOP_BODY:bb[0-9]+]], [[LOOP_EXIT2:bb[0-9]+]] + // CHECK: [[LOOP_BODY]]: + while let a = foo(), b = bar() { + // CHECK: [[VAL_BUF1:%.*]] = unchecked_take_enum_data_addr [[OPT_BUF1]]#1 + // CHECK: debug_value {{.*}} : $String // let a + // CHECK: [[VAL_BUF2:%.*]] = unchecked_take_enum_data_addr [[OPT_BUF2]]#1 + // CHECK: debug_value {{.*}} : $String // let b + // CHECK: debug_value {{.*}} : $String // let c + // CHECK: br [[LOOP_ENTRY]] + let c = a + } + // CHECK: [[LOOP_EXIT2]]: + // CHECK: destroy_addr [[OPT_BUF2]]#1 + // CHECK: br [[LOOP_EXIT1]] + // CHECK: [[LOOP_EXIT1]]: + // CHECK: destroy_addr [[OPT_BUF1]]#1 + // CHECK: dealloc_stack [[OPT_BUF2]]#0 + // CHECK: dealloc_stack [[OPT_BUF1]]#0 +} + + + diff --git a/test/SILGen/sil_locations.swift b/test/SILGen/sil_locations.swift index 80b3cf2b664..b9b5cb73954 100644 --- a/test/SILGen/sil_locations.swift +++ b/test/SILGen/sil_locations.swift @@ -11,7 +11,7 @@ func ifexpr() -> Int { // CHECK-LABEL: sil hidden @_TF13sil_locations6ifexprFT_Si // CHECK: apply {{.*}} line:[[@LINE-5]]:6 // CHECK: cond_br {{%.*}}, [[TRUE_BB:bb[0-9]+]], [[FALSE_BB:bb[0-9]+]] // {{.*}} line:[[@LINE-6]]:6 - // CHECK: br [[FALSE_BB]] // {{.*}} line:[[@LINE-6]]:6 + // CHECK: br [[FALSE_BB]] // {{.*}} line:[[@LINE-5]]:3 // CHECK: return {{.*}} // {{.*}} line:[[@LINE-5]]:3:return } @@ -26,9 +26,9 @@ func ifelseexpr() -> Int { // CHECK-LABEL: sil hidden @_TF13sil_locations10ifelseexprFT_Si // CHECK: cond_br {{%.*}}, [[TRUE_BB:bb[0-9]+]], [[FALSE_BB:bb[0-9]+]] // {{.*}} line:[[@LINE-7]]:6 // CHECK: [[TRUE_BB]]: - // CHECK: br bb{{[0-9]+}} // {{.*}} line:[[@LINE-8]]:6 + // CHECK: br bb{{[0-9]+}} // {{.*}} line:[[@LINE-7]]:3 // CHECK: [[FALSE_BB]]: - // CHECK: br bb{{[0-9]+}} // {{.*}} line:[[@LINE-8]]:6 + // CHECK: br bb{{[0-9]+}} // {{.*}} line:[[@LINE-7]]:3 // CHECK: return {{.*}} // {{.*}} line:[[@LINE-7]]:3:return }