Skip to content

Commit 9e586f4

Browse files
authored
Merge pull request #79084 from jckarter/addressor-projection-lifetime
SILGen: Materialize addressor bases for the formal access scope of the entire access.
2 parents a399709 + 44b4316 commit 9e586f4

File tree

8 files changed

+125
-47
lines changed

8 files changed

+125
-47
lines changed

lib/SILGen/RValue.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,10 +409,6 @@ static void verifyHelper(ArrayRef<ManagedValue> values,
409409
ValueOwnershipKind result = OwnershipKind::None;
410410
std::optional<bool> sameHaveCleanups;
411411
for (ManagedValue v : values) {
412-
assert((!SGF || !v.getType().isLoadable(SGF.get()->F) ||
413-
v.getType().isObject()) &&
414-
"All loadable values in an RValue must be an object");
415-
416412
ValueOwnershipKind kind = v.getOwnershipKind();
417413
if (kind == OwnershipKind::None)
418414
continue;

lib/SILGen/SILGenApply.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7012,13 +7012,20 @@ bool AccessorBaseArgPreparer::shouldLoadBaseAddress() const {
70127012
// base isn't a temporary. We aren't allowed to pass aliased
70137013
// memory to 'in', and we have pass at +1.
70147014
case ParameterConvention::Indirect_In:
7015-
case ParameterConvention::Indirect_In_Guaranteed:
70167015
case ParameterConvention::Indirect_In_CXX:
70177016
// TODO: We shouldn't be able to get an lvalue here, but the AST
70187017
// sometimes produces an inout base for non-mutating accessors.
70197018
// rdar://problem/19782170
70207019
// assert(!base.isLValue());
7021-
return base.isLValue() || base.isPlusZeroRValueOrTrivial();
7020+
return base.isLValue() || base.isPlusZeroRValueOrTrivial()
7021+
|| !SGF.silConv.useLoweredAddresses();
7022+
7023+
case ParameterConvention::Indirect_In_Guaranteed:
7024+
// We can pass the memory we already have at +0. The memory referred to
7025+
// by the base should be already immutable unless it was lowered as an
7026+
// lvalue.
7027+
return base.isLValue()
7028+
|| !SGF.silConv.useLoweredAddresses();
70227029

70237030
// If the accessor wants the value directly, we definitely have to
70247031
// load.
@@ -7077,7 +7084,7 @@ ArgumentSource AccessorBaseArgPreparer::prepareAccessorAddressBaseArg() {
70777084

70787085
// Otherwise, we have a value that we can forward without any additional
70797086
// handling.
7080-
return ArgumentSource(loc, RValue(SGF, loc, baseFormalType, base));
7087+
return ArgumentSource(loc, RValue(SGF, {base}, baseFormalType));
70817088
}
70827089

70837090
ArgumentSource AccessorBaseArgPreparer::prepareAccessorObjectBaseArg() {

lib/SILGen/SILGenLValue.cpp

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,10 +1405,25 @@ namespace {
14051405
if (base) {
14061406
// Borrow the base, because we may need it again to invoke other
14071407
// accessors.
1408-
result.base = SGF.prepareAccessorBaseArg(loc,
1409-
base.formalAccessBorrow(SGF, loc),
1410-
BaseFormalType,
1408+
base = base.formalAccessBorrow(SGF, loc);
1409+
// If the base needs to be materialized, do so in
1410+
// the outer formal evaluation scope, since an addressor or
1411+
// other dependent value may want to point into the materialization.
1412+
auto &baseInfo = SGF.getConstantInfo(SGF.getTypeExpansionContext(),
14111413
accessor);
1414+
1415+
if (!baseInfo.FormalPattern.isForeign()) {
1416+
auto baseFnTy = baseInfo.SILFnType;
1417+
1418+
if (baseFnTy->getSelfParameter().isFormalIndirect()
1419+
&& base.getType().isObject()
1420+
&& SGF.silConv.useLoweredAddresses()) {
1421+
base = base.formallyMaterialize(SGF, loc);
1422+
}
1423+
}
1424+
1425+
result.base = SGF.prepareAccessorBaseArg(loc, base, BaseFormalType,
1426+
accessor);
14121427
}
14131428

14141429
if (!Indices.isNull())
@@ -2086,16 +2101,12 @@ namespace {
20862101
"offsetting l-value for modification without writeback scope");
20872102

20882103
ManagedValue addr;
2089-
{
2090-
FormalEvaluationScope scope(SGF);
2091-
2092-
auto args =
2093-
std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor);
2094-
addr = SGF.emitAddressorAccessor(
2095-
loc, Accessor, Substitutions, std::move(args.base), IsSuper,
2096-
IsDirectAccessorUse, std::move(args.Indices),
2097-
SubstFieldType, IsOnSelfParameter);
2098-
}
2104+
auto args =
2105+
std::move(*this).prepareAccessorArgs(SGF, loc, base, Accessor);
2106+
addr = SGF.emitAddressorAccessor(
2107+
loc, Accessor, Substitutions, std::move(args.base), IsSuper,
2108+
IsDirectAccessorUse, std::move(args.Indices),
2109+
SubstFieldType, IsOnSelfParameter);
20992110

21002111
// Enter an unsafe access scope for the access.
21012112
addr =

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.cpp

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -475,11 +475,11 @@ void MoveOnlyObjectCheckerPImpl::check(
475475
if (markedInst->getCheckKind() ==
476476
MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign) {
477477
if (auto *cvi = dyn_cast<CopyValueInst>(markedInst->getOperand())) {
478-
SingleValueInstruction *i = cvi;
478+
auto replacement = cvi->getOperand();
479+
auto orig = replacement;
479480
if (auto *copyToMoveOnly =
480-
dyn_cast<CopyableToMoveOnlyWrapperValueInst>(
481-
cvi->getOperand())) {
482-
i = copyToMoveOnly;
481+
dyn_cast<CopyableToMoveOnlyWrapperValueInst>(orig)) {
482+
orig = copyToMoveOnly->getOperand();
483483
}
484484

485485
// TODO: Instead of pattern matching specific code generation patterns,
@@ -492,14 +492,14 @@ void MoveOnlyObjectCheckerPImpl::check(
492492
// bb(%arg : @guaranteed $Type):
493493
// %copy = copy_value %arg
494494
// %mark = mark_unresolved_non_copyable_value [no_consume_or_assign] %copy
495-
if (auto *arg = dyn_cast<SILArgument>(i->getOperand(0))) {
495+
if (auto *arg = dyn_cast<SILArgument>(orig)) {
496496
if (arg->getOwnershipKind() == OwnershipKind::Guaranteed) {
497497
for (auto *use : markedInst->getConsumingUses()) {
498498
destroys.push_back(cast<DestroyValueInst>(use->getUser()));
499499
}
500500
while (!destroys.empty())
501501
destroys.pop_back_val()->eraseFromParent();
502-
markedInst->replaceAllUsesWith(arg);
502+
markedInst->replaceAllUsesWith(replacement);
503503
markedInst->eraseFromParent();
504504
cvi->eraseFromParent();
505505
continue;
@@ -511,13 +511,13 @@ void MoveOnlyObjectCheckerPImpl::check(
511511
// %1 = load_borrow %0
512512
// %2 = copy_value %1
513513
// %3 = mark_unresolved_non_copyable_value [no_consume_or_assign] %2
514-
if (auto *lbi = dyn_cast<LoadBorrowInst>(i->getOperand(0))) {
514+
if (auto *lbi = dyn_cast<LoadBorrowInst>(orig)) {
515515
for (auto *use : markedInst->getConsumingUses()) {
516516
destroys.push_back(cast<DestroyValueInst>(use->getUser()));
517517
}
518518
while (!destroys.empty())
519519
destroys.pop_back_val()->eraseFromParent();
520-
markedInst->replaceAllUsesWith(lbi);
520+
markedInst->replaceAllUsesWith(replacement);
521521
markedInst->eraseFromParent();
522522
cvi->eraseFromParent();
523523
continue;
@@ -527,15 +527,14 @@ void MoveOnlyObjectCheckerPImpl::check(
527527
// (%yield, ..., %handle) = begin_apply
528528
// %copy = copy_value %yield
529529
// %mark = mark_unresolved_noncopyable_value [no_consume_or_assign] %copy
530-
if (isa_and_nonnull<BeginApplyInst>(
531-
i->getOperand(0)->getDefiningInstruction())) {
532-
if (i->getOperand(0)->getOwnershipKind() == OwnershipKind::Guaranteed) {
530+
if (isa_and_nonnull<BeginApplyInst>(orig->getDefiningInstruction())) {
531+
if (orig->getOwnershipKind() == OwnershipKind::Guaranteed) {
533532
for (auto *use : markedInst->getConsumingUses()) {
534533
destroys.push_back(cast<DestroyValueInst>(use->getUser()));
535534
}
536535
while (!destroys.empty())
537536
destroys.pop_back_val()->eraseFromParent();
538-
markedInst->replaceAllUsesWith(i->getOperand(0));
537+
markedInst->replaceAllUsesWith(replacement);
539538
markedInst->eraseFromParent();
540539
cvi->eraseFromParent();
541540
continue;

test/Constraints/keypath_dynamic_member_lookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ _ = lens.topLeft.x
4848
// CHECK: function_ref @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig
4949
// CHECK-NEXT: apply %68<Rectangle, Point>({{.*}})
5050
// CHECK: function_ref @$s29keypath_dynamic_member_lookup4LensV0B6MemberACyqd__Gs15WritableKeyPathCyxqd__G_tcluig
51-
// CHECK-NEXT: apply %75<Point, Int>({{.*}})
51+
// CHECK-NEXT: apply {{%[0-9]+}}<Point, Int>({{.*}})
5252
_ = lens.topLeft.y
5353

5454
lens.topLeft = Lens(Point(x: 1, y: 2)) // Ok

test/IRGen/opaque_result_type_linkage.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ struct G<T>: P {
1010
lazy var lazyVar: A = a
1111

1212
var a: some Any {
13+
b()
14+
}
15+
16+
consuming func b() -> some Any {
1317
"hello"
1418
}
1519
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
2+
3+
struct Foo: ~Copyable {
4+
var bar: Bar<Baz>
5+
}
6+
7+
struct Bar<T: ~Copyable>: ~Copyable {
8+
var elements: T
9+
10+
subscript(i: Int) -> T {
11+
unsafeAddress {
12+
fatalError()
13+
}
14+
}
15+
}
16+
17+
struct Baz: ~Copyable {
18+
var storage: Int
19+
20+
borrowing func test() {}
21+
}
22+
23+
// CHECK-LABEL: sil {{.*}} @${{.*}}4test1x
24+
func test(x: borrowing Foo) {
25+
// The materialization of x.bar into a temporary to invoke the subscript
26+
// addressor needs to cover the entire formal evaluation scope of the
27+
// projection, since the pointer may depend on the materialized
28+
// representation
29+
30+
// CHECK: [[BAR_MAT:%.*]] = alloc_stack $Bar<Baz>
31+
// CHECK: [[BAR_MAT_BORROW:%.*]] = store_borrow {{%.*}} to [[BAR_MAT]]
32+
// CHECK: [[TEST_FN:%.*]] = function_ref @${{.*}}3BazV4test
33+
// CHECK: apply [[TEST_FN]]
34+
// CHECK: end_borrow [[BAR_MAT_BORROW]]
35+
// CHECK: dealloc_stack [[BAR_MAT]]
36+
x.bar[0].test()
37+
}
38+
39+
// Value borrows should also cover the scope of the projected value, at
40+
// least until it's copied out if copyable.
41+
42+
struct CopyableLoadable {
43+
var x: AnyObject
44+
45+
subscript(i: Int) -> CopyableResult {
46+
unsafeAddress {
47+
fatalError()
48+
}
49+
}
50+
}
51+
52+
struct CopyableResult {
53+
var x: AnyObject
54+
55+
func test() {}
56+
}
57+
58+
func copyableLoadable() -> CopyableLoadable { fatalError() }
59+
60+
// CHECK-LABEL: sil {{.*}} @${{.*}}5test2
61+
func test2() {
62+
63+
// CHECK: [[BASE:%.*]] = begin_borrow
64+
// CHECK: [[ADDRESSOR:%.*]] = function_ref @${{.*}}16CopyableLoadableVy{{.*}}
65+
// CHECK: [[PTR:%.*]] = apply [[ADDRESSOR]]({{.*}}, [[BASE]])
66+
// CHECK: [[RAWPTR:%.*]] = struct_extract [[PTR]]
67+
// CHECK: [[ADDR:%.*]] = pointer_to_address [[RAWPTR]]
68+
// CHECK: [[DEP_ADDR:%.*]] = mark_dependence [unresolved] [[ADDR]] on [[BASE]]
69+
// CHECK: [[DEP_ADDR_ACCESS:%.*]] = begin_access [read] [unsafe] [[DEP_ADDR]]
70+
// CHECK: load [copy] [[DEP_ADDR_ACCESS]]
71+
// CHECK: end_access [[DEP_ADDR_ACCESS]]
72+
// CHECK: end_borrow [[BASE]]
73+
copyableLoadable()[0].test()
74+
}

test/SILGen/class_bound_protocols.swift

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +181,8 @@ func takesInheritsMutatingMethod(x: inout InheritsMutatingMethod,
181181
// CHECK-NEXT: [[X_PAYLOAD:%.*]] = open_existential_ref [[X_VALUE]] : $any InheritsMutatingMethod to $@opened("{{.*}}", any InheritsMutatingMethod) Self
182182
// CHECK-NEXT: [[TEMPORARY:%.*]] = alloc_stack $@opened("{{.*}}", any InheritsMutatingMethod) Self
183183
// CHECK-NEXT: store [[X_PAYLOAD]] to [init] [[TEMPORARY]] : $*@opened("{{.*}}", any InheritsMutatingMethod) Self
184-
// CHECK-NEXT: [[X_PAYLOAD_RELOADED:%.*]] = load_borrow [[TEMPORARY]]
185-
//
186-
// ** *NOTE* This extra copy is here since RValue invariants enforce that all
187-
// ** loadable objects are actually loaded. So we form the RValue and
188-
// ** load... only to then need to store the value back in a stack location to
189-
// ** pass to an in_guaranteed method. PredictableMemOpts is able to handle this
190-
// ** type of temporary codegen successfully.
191-
// CHECK-NEXT: [[TEMPORARY_2:%.*]] = alloc_stack $@opened("{{.*}}", any InheritsMutatingMethod) Self
192-
// CHECK-NEXT: [[SB:%.*]] = store_borrow [[X_PAYLOAD_RELOADED:%.*]] to [[TEMPORARY_2]]
193-
//
194184
// CHECK-NEXT: [[METHOD:%.*]] = witness_method $@opened("{{.*}}", any InheritsMutatingMethod) Self, #HasMutatingMethod.mutatingCounter!getter : <Self where Self : HasMutatingMethod> (Self) -> () -> Value, [[X_PAYLOAD]] : $@opened("{{.*}}", any InheritsMutatingMethod) Self : $@convention(witness_method: HasMutatingMethod) <τ_0_0 where τ_0_0 : HasMutatingMethod> (@in_guaranteed τ_0_0) -> Value
195-
// CHECK-NEXT: [[RESULT_VALUE:%.*]] = apply [[METHOD]]<@opened("{{.*}}", any InheritsMutatingMethod) Self>([[SB]]) : $@convention(witness_method: HasMutatingMethod) <τ_0_0 where τ_0_0 : HasMutatingMethod> (@in_guaranteed τ_0_0) -> Value
196-
// CHECK-NEXT: end_borrow [[SB]]
197-
// CHECK-NEXT: dealloc_stack [[TEMPORARY_2]]
198-
// CHECK-NEXT: end_borrow
185+
// CHECK-NEXT: [[RESULT_VALUE:%.*]] = apply [[METHOD]]<@opened("{{.*}}", any InheritsMutatingMethod) Self>([[TEMPORARY]]) : $@convention(witness_method: HasMutatingMethod) <τ_0_0 where τ_0_0 : HasMutatingMethod> (@in_guaranteed τ_0_0) -> Value
199186
// CHECK-NEXT: destroy_addr
200187
// CHECK-NEXT: end_access [[X_ADDR]] : $*any InheritsMutatingMethod
201188
// CHECK-NEXT: ignored_use [[RESULT_VALUE]]

0 commit comments

Comments
 (0)