Skip to content

Commit a9c4f15

Browse files
Merge pull request #34979 from varungandhi-apple/vg-fix-silgen-for-loop
[SILGen] Branch on result of next() before conversion.
2 parents eb296f2 + 02c4ad3 commit a9c4f15

File tree

4 files changed

+70
-30
lines changed

4 files changed

+70
-30
lines changed

lib/SILGen/SILGenStmt.cpp

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -972,20 +972,18 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
972972
// to hold the results. This will be initialized on every entry into the loop
973973
// header and consumed by the loop body. On loop exit, the terminating value
974974
// will be in the buffer.
975-
CanType optTy;
976-
if (S->getConvertElementExpr()) {
977-
optTy = S->getConvertElementExpr()->getType()->getCanonicalType();
978-
} else {
979-
optTy = OptionalType::get(S->getSequenceConformance().getTypeWitnessByName(
980-
S->getSequence()->getType(),
981-
SGF.getASTContext().Id_Element))
982-
->getCanonicalType();
983-
}
975+
CanType optTy =
976+
OptionalType::get(
977+
S->getSequenceConformance().getTypeWitnessByName(
978+
S->getSequence()->getType(), SGF.getASTContext().Id_Element))
979+
->getCanonicalType();
984980
auto &optTL = SGF.getTypeLowering(optTy);
981+
985982
SILValue addrOnlyBuf;
986-
ManagedValue nextBufOrValue;
983+
bool nextResultTyIsAddressOnly =
984+
optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses();
987985

988-
if (optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses())
986+
if (nextResultTyIsAddressOnly)
989987
addrOnlyBuf = SGF.emitTemporaryAllocation(S, optTL.getLoweredType());
990988

991989
// Create a new basic block and jump into it.
@@ -1028,25 +1026,22 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
10281026
SGFContext().withFollowingSideEffects()));
10291027
};
10301028

1029+
bool hasElementConversion = S->getElementExpr();
10311030
auto buildElementRValue = [&](SILLocation loc, SGFContext ctx) {
10321031
RValue result;
10331032
result = SGF.emitApplyMethod(
10341033
loc, iteratorNextRef, buildArgumentSource(),
10351034
PreparedArguments(ArrayRef<AnyFunctionType::Param>({})),
1036-
S->getElementExpr() ? SGFContext() : ctx);
1037-
if (S->getElementExpr()) {
1038-
SILGenFunction::OpaqueValueRAII pushOpaqueValue(
1039-
SGF, S->getElementExpr(),
1040-
std::move(result).getAsSingleValue(SGF, loc));
1041-
result = SGF.emitRValue(S->getConvertElementExpr(), ctx);
1042-
}
1035+
hasElementConversion ? SGFContext() : ctx);
10431036
return result;
10441037
};
1038+
1039+
ManagedValue nextBufOrElement;
10451040
// Then emit the loop destination block.
10461041
//
10471042
// Advance the generator. Use a scope to ensure that any temporary stack
10481043
// allocations in the subexpression are immediately released.
1049-
if (optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses()) {
1044+
if (nextResultTyIsAddressOnly) {
10501045
// Create the initialization outside of the innerForScope so that the
10511046
// innerForScope doesn't clean it up.
10521047
auto nextInit = SGF.useBufferAsTemporary(addrOnlyBuf, optTL);
@@ -1061,16 +1056,23 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
10611056
}
10621057
innerForScope.pop();
10631058
}
1064-
nextBufOrValue = nextInit->getManagedAddress();
1059+
nextBufOrElement = nextInit->getManagedAddress();
10651060
} else {
10661061
ArgumentScope innerForScope(SGF, SILLocation(S));
1067-
nextBufOrValue = innerForScope.popPreservingValue(
1062+
nextBufOrElement = innerForScope.popPreservingValue(
10681063
buildElementRValue(SILLocation(S), SGFContext())
10691064
.getAsSingleValue(SGF, SILLocation(S)));
10701065
}
10711066

10721067
SILBasicBlock *failExitingBlock = createBasicBlock();
1073-
SwitchEnumBuilder switchEnumBuilder(SGF.B, S, nextBufOrValue);
1068+
SwitchEnumBuilder switchEnumBuilder(SGF.B, S, nextBufOrElement);
1069+
1070+
auto convertElementRValue = [&](ManagedValue inputValue, SGFContext ctx) -> ManagedValue {
1071+
SILGenFunction::OpaqueValueRAII pushOpaqueValue(SGF, S->getElementExpr(),
1072+
inputValue);
1073+
return SGF.emitRValue(S->getConvertElementExpr(), ctx)
1074+
.getAsSingleValue(SGF, SILLocation(S));
1075+
};
10741076

10751077
switchEnumBuilder.addOptionalSomeCase(
10761078
createBasicBlock(), loopDest.getBlock(),
@@ -1096,13 +1098,22 @@ void StmtEmitter::visitForEachStmt(ForEachStmt *S) {
10961098
//
10971099
// *NOTE* If we do not have an address only value, then inputValue is
10981100
// *already properly unwrapped.
1099-
if (optTL.isAddressOnly() && SGF.silConv.useLoweredAddresses()) {
1101+
SGFContext loopVarCtx{initLoopVars.get()};
1102+
if (nextResultTyIsAddressOnly) {
11001103
inputValue = SGF.emitUncheckedGetOptionalValueFrom(
1101-
S, inputValue, optTL, SGFContext(initLoopVars.get()));
1104+
S, inputValue, optTL,
1105+
hasElementConversion ? SGFContext() : loopVarCtx);
11021106
}
11031107

1108+
CanType optConvertedTy = optTy;
1109+
if (hasElementConversion) {
1110+
inputValue = convertElementRValue(inputValue, loopVarCtx);
1111+
optConvertedTy =
1112+
OptionalType::get(S->getConvertElementExpr()->getType())
1113+
->getCanonicalType();
1114+
}
11041115
if (!inputValue.isInContext())
1105-
RValue(SGF, S, optTy.getOptionalObjectType(), inputValue)
1116+
RValue(SGF, S, optConvertedTy.getOptionalObjectType(), inputValue)
11061117
.forwardInto(SGF, S, initLoopVars.get());
11071118

11081119
// Now that the pattern has been initialized, check any where

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8039,13 +8039,13 @@ static Optional<SolutionApplicationTarget> applySolutionToForEachStmt(
80398039
// Convert that Optional<Element> value to the type of the pattern.
80408040
auto optPatternType = OptionalType::get(forEachStmtInfo.initType);
80418041
if (!optPatternType->isEqual(nextResultType)) {
8042-
OpaqueValueExpr *elementExpr =
8043-
new (ctx) OpaqueValueExpr(stmt->getInLoc(), nextResultType,
8044-
/*isPlaceholder=*/true);
8042+
OpaqueValueExpr *elementExpr = new (ctx) OpaqueValueExpr(
8043+
stmt->getInLoc(), nextResultType->getOptionalObjectType(),
8044+
/*isPlaceholder=*/true);
80458045
Expr *convertElementExpr = elementExpr;
80468046
if (TypeChecker::typeCheckExpression(
80478047
convertElementExpr, dc,
8048-
/*contextualInfo=*/{optPatternType, CTP_CoerceOperand})
8048+
/*contextualInfo=*/{forEachStmtInfo.initType, CTP_CoerceOperand})
80498049
.isNull()) {
80508050
return None;
80518051
}

test/Profiler/pgo_foreach.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public func guessForEach1(x: Int32) -> Int32 {
4343
// IR-OPT-LABEL: define swiftcc i32 @$s9pgo_foreach10guessWhiles5Int32VAD1x_tF
4444

4545
public func guessForEach2(x: Int32) -> Int32 {
46-
// SIL: switch_enum {{.*}} : $Optional<(String, Int32)>, case #Optional.some!enumelt: {{.*}} !case_count(168), case #Optional.none!enumelt: {{.*}} !case_count(42)
46+
// SIL: switch_enum {{.*}} : $Optional<(key: String, value: Int32)>, case #Optional.some!enumelt: {{.*}} !case_count(168), case #Optional.none!enumelt: {{.*}} !case_count(42)
4747

4848
var ret : Int32 = 0
4949
let names = ["John" : Int32(1), "Paul" : Int32(2), "George" : Int32(3), "Ringo" : Int32(x)]

test/SILGen/foreach.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,3 +603,32 @@ func genericFuncWithConversion<T: C>(list : [T]) {
603603
print(item)
604604
}
605605
}
606+
607+
// SR-8688: Check that branch on result of next() precedes optional injection.
608+
// If we branch on the converted result of next(), the loop won't terminate.
609+
//
610+
// CHECK-LABEL: sil hidden [ossa] @$s7foreach32injectForEachElementIntoOptionalyySaySiGF
611+
// CHECK: [[NEXT_RESULT:%.*]] = load [trivial] {{.*}} : $*Optional<Int>
612+
// CHECK: switch_enum [[NEXT_RESULT]] : $Optional<Int>, case #Optional.some!enumelt: [[BB_SOME:bb.*]], case
613+
// CHECK: [[BB_SOME]]([[X_PRE_BINDING:%.*]] : $Int):
614+
// CHECK: [[X_BINDING:%.*]] = enum $Optional<Int>, #Optional.some!enumelt, [[X_PRE_BINDING]] : $Int
615+
// CHECK: debug_value [[X_BINDING]] : $Optional<Int>, let, name "x"
616+
func injectForEachElementIntoOptional(_ xs: [Int]) {
617+
for x : Int? in xs {}
618+
}
619+
620+
// SR-8688: Check that branch on result of next() precedes optional injection.
621+
// If we branch on the converted result of next(), the loop won't terminate.
622+
//
623+
// CHECK-LABEL: sil hidden [ossa] @$s7foreach32injectForEachElementIntoOptionalyySayxGlF
624+
// CHECK: copy_addr [take] [[NEXT_RESULT:%.*]] to [initialization] [[NEXT_RESULT_COPY:%.*]] : $*Optional<T>
625+
// CHECK: switch_enum_addr [[NEXT_RESULT_COPY]] : $*Optional<T>, case #Optional.some!enumelt: [[BB_SOME:bb.*]], case
626+
// CHECK: [[BB_SOME]]:
627+
// CHECK: [[X_BINDING:%.*]] = alloc_stack $Optional<T>, let, name "x"
628+
// CHECK: [[ADDR:%.*]] = unchecked_take_enum_data_addr [[NEXT_RESULT_COPY]] : $*Optional<T>, #Optional.some!enumelt
629+
// CHECK: [[X_ADDR:%.*]] = init_enum_data_addr [[X_BINDING]] : $*Optional<T>, #Optional.some!enumelt
630+
// CHECK: copy_addr [take] [[ADDR]] to [initialization] [[X_ADDR]] : $*T
631+
// CHECK: inject_enum_addr [[X_BINDING]] : $*Optional<T>, #Optional.some!enumelt
632+
func injectForEachElementIntoOptional<T>(_ xs: [T]) {
633+
for x : T? in xs {}
634+
}

0 commit comments

Comments
 (0)