Skip to content

Commit 44b4316

Browse files
committed
SILGen: Materialize addressor bases for the formal access scope of the entire access.
The return pointer may point into the materialized base value, so if the base needs materialization, ensure that materialization covers any futher projection of the value.
1 parent 96862b4 commit 44b4316

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)