Skip to content

Commit 8aab9f7

Browse files
authored
Merge pull request #7974 from gottesmm/foreach_switch_enum
2 parents 6a0a199 + bf8d941 commit 8aab9f7

File tree

7 files changed

+715
-97
lines changed

7 files changed

+715
-97
lines changed

lib/SILGen/SILGenStmt.cpp

Lines changed: 109 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "SILGen.h"
14-
#include "Scope.h"
1514
#include "Condition.h"
1615
#include "Initialization.h"
1716
#include "LValue.h"
1817
#include "RValue.h"
18+
#include "Scope.h"
19+
#include "SwitchCaseFullExpr.h"
1920
#include "swift/AST/AST.h"
20-
#include "swift/SIL/SILArgument.h"
2121
#include "swift/AST/DiagnosticsSIL.h"
22+
#include "swift/SIL/SILArgument.h"
2223
#include "llvm/Support/SaveAndRestore.h"
2324

2425
using namespace swift;
@@ -745,7 +746,7 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
745746
// Emit the 'iterator' variable that we'll be using for iteration.
746747
LexicalScope OuterForScope(SGF.Cleanups, SGF, CleanupLocation(S));
747748
SGF.visitPatternBindingDecl(S->getIterator());
748-
749+
749750
// If we ever reach an unreachable point, stop emitting statements.
750751
// This will need revision if we ever add goto.
751752
if (!SGF.B.hasValidInsertionPoint()) return;
@@ -756,100 +757,130 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
756757
// will be in the buffer.
757758
auto optTy = S->getIteratorNext()->getType()->getCanonicalType();
758759
auto &optTL = SGF.getTypeLowering(optTy);
759-
SILValue nextBufOrValue;
760+
SILValue addrOnlyBuf;
761+
ManagedValue nextBufOrValue;
760762

761763
if (optTL.isAddressOnly())
762-
nextBufOrValue = SGF.emitTemporaryAllocation(S, optTL.getLoweredType());
763-
764+
addrOnlyBuf = SGF.emitTemporaryAllocation(S, optTL.getLoweredType());
765+
764766
// Create a new basic block and jump into it.
765767
JumpDest loopDest = createJumpDest(S->getBody());
766768
SGF.B.emitBlock(loopDest.getBlock(), S);
767-
769+
768770
// Set the destinations for 'break' and 'continue'.
769771
JumpDest endDest = createJumpDest(S->getBody());
770772
SGF.BreakContinueDestStack.push_back({ S, endDest, loopDest });
771773

774+
// Then emit the loop destination block.
775+
//
772776
// Advance the generator. Use a scope to ensure that any temporary stack
773777
// allocations in the subexpression are immediately released.
774778
if (optTL.isAddressOnly()) {
775779
Scope InnerForScope(SGF.Cleanups, CleanupLocation(S->getIteratorNext()));
776-
auto nextInit = SGF.useBufferAsTemporary(nextBufOrValue, optTL);
780+
auto nextInit = SGF.useBufferAsTemporary(addrOnlyBuf, optTL);
777781
SGF.emitExprInto(S->getIteratorNext(), nextInit.get());
778-
nextInit->getManagedAddress().forward(SGF);
779-
} else {
780-
Scope InnerForScope(SGF.Cleanups, CleanupLocation(S->getIteratorNext()));
781782
nextBufOrValue =
782-
SGF.emitRValueAsSingleValue(S->getIteratorNext()).forward(SGF);
783-
}
784-
785-
// Continue if the value is present.
786-
Condition Cond = SGF.emitCondition(
787-
SGF.emitDoesOptionalHaveValue(S, nextBufOrValue), S,
788-
/*hasFalseCode=*/false, /*invertValue=*/false);
789-
790-
if (Cond.hasTrue()) {
791-
Cond.enterTrue(SGF);
792-
SGF.emitProfilerIncrement(S->getBody());
793-
794-
// Emit the loop body.
795-
// The declared variable(s) for the current element are destroyed
796-
// at the end of each loop iteration.
783+
ManagedValue::forUnmanaged(nextInit->getManagedAddress().forward(SGF));
784+
} else {
785+
// SEMANTIC SIL TODO: I am doing this to match previous behavior. We need to
786+
// forward tmp below to ensure that we do not prematurely destroy the
787+
// induction variable at the end of scope. I tried to use the
788+
// CleanupRestorationScope and dormant, but it seemingly did not work and I
789+
// do not have time to look into this now = (.
790+
SILValue tmpValue;
791+
bool hasCleanup;
797792
{
798-
Scope InnerForScope(SGF.Cleanups, CleanupLocation(S->getBody()));
799-
// Emit the initialization for the pattern. If any of the bound patterns
800-
// fail (because this is a 'for case' pattern with a refutable pattern,
801-
// the code should jump to the continue block.
802-
InitializationPtr initLoopVars
803-
= SGF.emitPatternBindingInitialization(S->getPattern(), loopDest);
804-
ManagedValue val;
805-
806-
// If we had a loadable "next" generator value, we know it is present.
807-
// Get the value out of the optional, and wrap it up with a cleanup so
808-
// that any exits out of this scope properly clean it up.
809-
if (optTL.isLoadable()) {
810-
val = SGF.emitManagedRValueWithCleanup(nextBufOrValue);
811-
} else {
812-
val = SGF.emitManagedBufferWithCleanup(nextBufOrValue);
813-
}
814-
val = SGF.emitUncheckedGetOptionalValueFrom(S, val, optTL,
815-
SGFContext(initLoopVars.get()));
816-
if (!val.isInContext())
817-
RValue(SGF, S, optTy.getAnyOptionalObjectType(), val)
818-
.forwardInto(SGF, S, initLoopVars.get());
819-
820-
// Now that the pattern has been initialized, check any where condition.
821-
// If it fails, loop around as if 'continue' happened.
822-
if (auto *Where = S->getWhere()) {
823-
auto cond = SGF.emitCondition(Where, /*hasFalse*/false, /*invert*/true);
824-
// If self is null, branch to the epilog.
825-
cond.enterTrue(SGF);
826-
SGF.Cleanups.emitBranchAndCleanups(loopDest, Where, { });
827-
cond.exitTrue(SGF);
828-
cond.complete(SGF);
829-
}
830-
831-
visit(S->getBody());
793+
Scope InnerForScope(SGF.Cleanups, CleanupLocation(S->getIteratorNext()));
794+
ManagedValue tmp = SGF.emitRValueAsSingleValue(S->getIteratorNext());
795+
hasCleanup = tmp.hasCleanup();
796+
tmpValue = tmp.forward(SGF);
832797
}
833-
834-
// Loop back to the header.
835-
if (SGF.B.hasValidInsertionPoint()) {
836-
// Associate the loop body's closing brace with this branch.
837-
RegularLocation L(S->getBody());
838-
L.pointToEnd();
839-
SGF.B.createBranch(L, loopDest.getBlock());
840-
}
841-
Cond.exitTrue(SGF);
842-
}
843-
844-
// Complete the conditional execution.
845-
Cond.complete(SGF);
846-
798+
nextBufOrValue = hasCleanup ? SGF.emitManagedRValueWithCleanup(tmpValue)
799+
: ManagedValue::forUnmanaged(tmpValue);
800+
}
801+
802+
SILBasicBlock *failExitingBlock = createBasicBlock();
803+
SwitchEnumBuilder switchEnumBuilder(SGF.B, S, nextBufOrValue);
804+
805+
switchEnumBuilder.addCase(
806+
SGF.getASTContext().getOptionalSomeDecl(), createBasicBlock(),
807+
loopDest.getBlock(),
808+
[&](ManagedValue inputValue, SwitchCaseFullExpr &scope) {
809+
SGF.emitProfilerIncrement(S->getBody());
810+
811+
// Emit the loop body.
812+
// The declared variable(s) for the current element are destroyed
813+
// at the end of each loop iteration.
814+
{
815+
Scope innerForScope(SGF.Cleanups, CleanupLocation(S->getBody()));
816+
// Emit the initialization for the pattern. If any of the bound
817+
// patterns
818+
// fail (because this is a 'for case' pattern with a refutable
819+
// pattern,
820+
// the code should jump to the continue block.
821+
InitializationPtr initLoopVars =
822+
SGF.emitPatternBindingInitialization(S->getPattern(), loopDest);
823+
824+
// If we had a loadable "next" generator value, we know it is present.
825+
// Get the value out of the optional, and wrap it up with a cleanup so
826+
// that any exits out of this scope properly clean it up.
827+
//
828+
// *NOTE* If we do not have an address only value, then inputValue is
829+
// *already properly unwrapped.
830+
if (optTL.isAddressOnly()) {
831+
inputValue =
832+
SGF.emitManagedBufferWithCleanup(nextBufOrValue.getValue());
833+
inputValue = SGF.emitUncheckedGetOptionalValueFrom(
834+
S, inputValue, optTL, SGFContext(initLoopVars.get()));
835+
}
836+
837+
if (!inputValue.isInContext())
838+
RValue(SGF, S, optTy.getAnyOptionalObjectType(), inputValue)
839+
.forwardInto(SGF, S, initLoopVars.get());
840+
841+
// Now that the pattern has been initialized, check any where
842+
// condition.
843+
// If it fails, loop around as if 'continue' happened.
844+
if (auto *Where = S->getWhere()) {
845+
auto cond =
846+
SGF.emitCondition(Where, /*hasFalse*/ false, /*invert*/ true);
847+
// If self is null, branch to the epilog.
848+
cond.enterTrue(SGF);
849+
SGF.Cleanups.emitBranchAndCleanups(loopDest, Where, {});
850+
cond.exitTrue(SGF);
851+
cond.complete(SGF);
852+
}
853+
854+
visit(S->getBody());
855+
}
856+
857+
// If we emitted an unreachable in the body, we will not have a valid
858+
// insertion point. Just return early.
859+
if (!SGF.B.hasValidInsertionPoint())
860+
return;
861+
862+
// Otherwise, associate the loop body's closing brace with this branch.
863+
RegularLocation L(S->getBody());
864+
L.pointToEnd();
865+
scope.exit(L);
866+
});
867+
868+
// We add loop fail block, just to be defensive about intermediate
869+
// transformations performing cleanups at scope.exit(). We still jump to the
870+
// contBlock.
871+
switchEnumBuilder.addCase(
872+
SGF.getASTContext().getOptionalNoneDecl(), createBasicBlock(),
873+
failExitingBlock,
874+
[&](ManagedValue inputValue, SwitchCaseFullExpr &scope) {
875+
assert(!inputValue && "None should not be passed an argument!");
876+
scope.exit(S);
877+
});
878+
879+
std::move(switchEnumBuilder).emit();
880+
881+
SGF.B.emitBlock(failExitingBlock);
847882
emitOrDeleteBlock(SGF, endDest, S);
848883
SGF.BreakContinueDestStack.pop_back();
849-
850-
// We do not need to destroy the value in the 'nextBuf' slot here, because
851-
// either the 'for' loop finished naturally and the buffer contains '.None',
852-
// or we exited by 'break' and the value in the buffer was consumed.
853884
}
854885

855886
void StmtEmitter::visitBreakStmt(BreakStmt *S) {

lib/SILGen/SwitchCaseFullExpr.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ SwitchCaseFullExpr::SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc,
2323
: SGF(SGF), scope(SGF.Cleanups, loc), loc(loc),
2424
contBlock(contBlock ? *contBlock : *SGF.B.splitBlockForFallthrough()) {}
2525

26-
SwitchCaseFullExpr::~SwitchCaseFullExpr() {
27-
assert(!scope.isValid() && "Switch Case Full Expr was not popped?!");
28-
}
29-
3026
void SwitchCaseFullExpr::exit(SILLocation loc, ArrayRef<SILValue> branchArgs) {
3127
assert(SGF.B.hasValidInsertionPoint());
3228
scope.pop();

lib/SILGen/SwitchCaseFullExpr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class SwitchCaseFullExpr {
3636
explicit SwitchCaseFullExpr(SILGenFunction &SGF, CleanupLocation loc,
3737
SILBasicBlock *contBlock = nullptr);
3838

39-
~SwitchCaseFullExpr();
39+
~SwitchCaseFullExpr() = default;
4040

4141
SwitchCaseFullExpr(const SwitchCaseFullExpr &) = delete;
4242
SwitchCaseFullExpr &operator=(const SwitchCaseFullExpr &) = delete;
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s
22
for i in 0 ..< 3 {
33
// CHECK: %[[ALLOCA:[0-9]+]] = alloca %TSiSg
4+
// CHECK: %i.addr = alloca i{{32|64}}
45
// CHECK: %[[CAST:[0-9]+]] = bitcast %TSiSg* %[[ALLOCA]] to i{{32|64}}*
56
// CHECK: %[[LD:[0-9]+]] = load i{{32|64}}, i{{32|64}}* %[[CAST]]
6-
// CHECK-NEXT: call void @llvm.dbg.value(metadata i{{32|64}} %[[LD]],
7+
// CHECK: br i1 {{%.*}}, label %[[FAIL:.*]], label %[[SUCCESS:.*]],
8+
//
9+
// CHECK: ; <label>:[[SUCCESS]]:
10+
// CHECK: br label %[[NEXT_BB:.*]],
11+
//
12+
// CHECK: ; <label>:[[NEXT_BB]]:
13+
// CHECK: %[[PHI_VAL:.*]] = phi i{{32|64}} [ %[[LD]], %[[SUCCESS]] ]
14+
// CHECK: store i{{32|64}} %[[PHI_VAL]], i{{32|64}}* %i.addr
15+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata i{{32|64}}* %i.addr,
716
// CHECK-SAME: metadata ![[I:[0-9]+]],
817
// CHECK: ![[I]] = !DILocalVariable(name: "i",
918
}

0 commit comments

Comments
 (0)