Skip to content

Commit 8a65c9b

Browse files
committed
SIL: Optimizer fixes for noncopyable optional chains.
Don't attempt a SILCombine transform on `select_enum` that inserts a copy when an enum is noncopyable. Adjust the cleanup handling for a consuming optional chain to ensure a `destroy_value` still gets emitted on the `none` path; this shouldn't actually matter since `none` is a trivial case, but the memory verifier isn't that fancy during OSSA, and we can optimize it later. Fixes rdar://124426918.
1 parent e90a7e6 commit 8a65c9b

File tree

4 files changed

+26
-11
lines changed

4 files changed

+26
-11
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,9 @@ class SILBuilder {
14041404
assert(!operand->getType().isTrivial(getFunction()) &&
14051405
"Should not be passing trivial values to this api. Use instead "
14061406
"emitCopyValueOperation");
1407+
assert((getModule().getStage() == SILStage::Raw
1408+
|| !operand->getType().isMoveOnly())
1409+
&& "should not be copying move-only values in canonical SIL");
14071410
return insert(new (getModule())
14081411
CopyValueInst(getSILDebugLocation(Loc), operand));
14091412
}

lib/SILGen/SILGenLValue.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,9 @@ static ManagedValue enterAccessScope(SILGenFunction &SGF, SILLocation loc,
670670
SILAccessEnforcement enforcement,
671671
std::optional<ActorIsolation> actorIso,
672672
bool noNestedConflict = false) {
673-
return ManagedValue::forLValue(
674-
enterAccessScope(SGF, loc, base, addr.getValue(), typeData,
675-
accessKind, enforcement, actorIso, noNestedConflict));
673+
auto access = enterAccessScope(SGF, loc, base, addr.getValue(), typeData,
674+
accessKind, enforcement, actorIso, noNestedConflict);
675+
return ManagedValue::forLValue(access);
676676
}
677677

678678
// Find the base of the formal access at `address`. If the base requires an
@@ -4415,11 +4415,9 @@ LValue SILGenLValue::visitBindOptionalExpr(BindOptionalExpr *e,
44154415
SILAccessEnforcement::Static,
44164416
std::nullopt,
44174417
/*no nested conflict*/ true);
4418-
} else {
4419-
// Take ownership of the base.
4420-
optBase = SGF.emitFormalAccessManagedRValueWithCleanup(e,
4421-
optBase.getValue());
44224418
}
4419+
// Take ownership of the base.
4420+
optBase = SGF.emitManagedRValueWithCleanup(optBase.getValue());
44234421
}
44244422
}
44254423
// Bind the value, branching to the destination address if there's no
@@ -4440,11 +4438,16 @@ LValue SILGenLValue::visitBindOptionalExpr(BindOptionalExpr *e,
44404438
// Reset the insertion point at the end of hasValueBB so we can
44414439
// continue to emit code there.
44424440
SGF.B.setInsertionPoint(someBB);
4443-
4441+
44444442
// Project out the payload on the success branch. We can just use a
44454443
// naked ValueComponent here; this is effectively a separate l-value.
44464444
ManagedValue optPayload =
44474445
getPayloadOfOptionalValue(SGF, e, optBase, valueTypeData, accessKind);
4446+
// Disable the cleanup if consuming since the consumer should pull straight
4447+
// from the address we give them.
4448+
if (optBase.getType().isMoveOnly() && isConsumeAccess(baseAccessKind)) {
4449+
optPayload = ManagedValue::forLValue(optPayload.forward(SGF));
4450+
}
44484451
LValue valueLV;
44494452
valueLV.add<ValueComponent>(optPayload, std::nullopt, valueTypeData,
44504453
/*is rvalue*/optBase.getType().isObject());

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,9 +1468,15 @@ SILInstruction *SILCombiner::visitCondBranchInst(CondBranchInst *CBI) {
14681468
if (!CBI->getTrueArgs().empty() || !CBI->getFalseArgs().empty())
14691469
return nullptr;
14701470
auto EnumOperandTy = SEI->getEnumOperand()->getType();
1471-
// Type should be loadable
1472-
if (!EnumOperandTy.isLoadable(*SEI->getFunction()))
1471+
// Type should be loadable and copyable.
1472+
// TODO: Generalize to work without copying address-only or noncopyable
1473+
// values.
1474+
if (!EnumOperandTy.isLoadable(*SEI->getFunction())) {
14731475
return nullptr;
1476+
}
1477+
if (EnumOperandTy.isMoveOnly()) {
1478+
return nullptr;
1479+
}
14741480

14751481
// Result of the select_enum should be a boolean.
14761482
if (SEI->getType() != CBI->getCondition()->getType())

test/SILGen/moveonly_optional_operations_2.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func consumingSwitchSubject3(ncp: consuming NCPair) {
7474
// CHECK: [[PAIR_MARK:%.*]] = mark_unresolved_non_copyable_value [assignable_but_not_consumable] [[PAIR_ACCESS]]
7575
// CHECK: [[FIELD:%.*]] = struct_element_addr [[PAIR_MARK]]
7676
// CHECK: [[FIELD_ACCESS:%.*]] = begin_access [deinit] [static] [no_nested_conflict] [[FIELD]]
77-
// CHECK: cond_br {{.*}}, [[SOME_BB:bb[0-9]+]],
77+
// CHECK: cond_br {{.*}}, [[SOME_BB:bb[0-9]+]], [[NONE_BB:bb[0-9]+]]
7878
// CHECK: [[SOME_BB]]:
7979
// CHECK: [[PAYLOAD_ADDR:%.*]] = unchecked_take_enum_data_addr [[FIELD_ACCESS]]
8080
// CHECK: [[PAYLOAD:%.*]] = load [take] [[PAYLOAD_ADDR]]
@@ -85,6 +85,9 @@ func consumingSwitchSubject3(ncp: consuming NCPair) {
8585
// CHECK-NEXT: end_access [[PAIR_ACCESS]]
8686
// CHECK-NEXT: inject_enum_addr
8787
// CHECK-NEXT: br
88+
// CHECK: [[NONE_BB]]:
89+
// CHECK: destroy_addr [[FIELD_ACCESS]]
90+
// CHECK-NEXT: end_access [[FIELD_ACCESS]]
8891

8992
switch ncp.first?.c3() {
9093
default:

0 commit comments

Comments
 (0)