Skip to content

Commit e6c9288

Browse files
authored
Merge pull request #37166 from eeckstein/fix-keypath-optimization
SILCombine: fix keypath optimization with optional chaining and classes
2 parents 1bdf006 + bd5d427 commit e6c9288

File tree

2 files changed

+55
-6
lines changed

2 files changed

+55
-6
lines changed

lib/SILOptimizer/Utils/KeyPathProjector.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ class GettablePropertyProjector : public ComponentProjector {
230230
auto addr = builder.createAllocStack(loc, type);
231231

232232
assertHasNoContext();
233-
assert(getter->getArguments().size() == 2);
233+
assert(getter->getConventions().getNumSILArguments());
234234

235235
auto ref = builder.createFunctionRef(loc, getter);
236236
builder.createApply(loc, ref, subs, {addr, parentValue});
@@ -308,8 +308,8 @@ class SettablePropertyProjector : public GettablePropertyProjector {
308308
auto addr = builder.createAllocStack(loc, type);
309309

310310
assertHasNoContext();
311-
assert(getter->getArguments().size() == 2);
312-
assert(setter->getArguments().size() == 2);
311+
assert(getter->getConventions().getNumSILArguments());
312+
assert(setter->getConventions().getNumSILArguments());
313313

314314
// If this is a modify, we need to call the getter and
315315
// store the result in the writeback buffer.
@@ -468,10 +468,11 @@ class OptionalChainProjector : public ComponentProjector {
468468
public:
469469
OptionalChainProjector(const KeyPathPatternComponent &component,
470470
std::unique_ptr<KeyPathProjector> parent,
471+
BeginAccessInst *&beginAccess,
471472
SILValue optionalChainResult,
472473
SILLocation loc, SILBuilder &builder)
473474
: ComponentProjector(component, std::move(parent), loc, builder),
474-
optionalChainResult(optionalChainResult) {}
475+
optionalChainResult(optionalChainResult), beginAccess(beginAccess) {}
475476

476477
void project(AccessType accessType,
477478
std::function<void(SILValue addr)> callback) override {
@@ -506,6 +507,7 @@ class OptionalChainProjector : public ComponentProjector {
506507

507508
// Unwrap the optional.
508509
auto objAddr = builder.createUncheckedTakeEnumDataAddr(loc, tempAddr, someDecl, objType);
510+
BeginAccessInst *origBeginAccess = beginAccess;
509511

510512
// at the end of the projection, callback will store a value in optionalChainResult
511513
callback(objAddr);
@@ -516,6 +518,12 @@ class OptionalChainProjector : public ComponentProjector {
516518
builder.createBranch(loc, continuation);
517519
// else, store nil in the result
518520
builder.setInsertionPoint(ifNone);
521+
522+
// If the sub-projection ended the access in the some-branch, we also
523+
// have to end the access in the none-branch.
524+
if (origBeginAccess && origBeginAccess != beginAccess)
525+
builder.createEndAccess(loc, origBeginAccess, /*aborted*/ false);
526+
519527
builder.createInjectEnumAddr(loc, optionalChainResult, noneDecl);
520528

521529
builder.createBranch(loc, continuation);
@@ -526,6 +534,7 @@ class OptionalChainProjector : public ComponentProjector {
526534

527535
private:
528536
SILValue optionalChainResult;
537+
BeginAccessInst *&beginAccess;
529538
};
530539

531540
/// A projector to handle a complete key path.
@@ -646,7 +655,8 @@ class CompleteKeyPathProjector : public KeyPathProjector {
646655
break;
647656
case KeyPathPatternComponent::Kind::OptionalChain:
648657
projector = std::make_unique<OptionalChainProjector>
649-
(comp, std::move(parent), optionalChainResult, loc, builder);
658+
(comp, std::move(parent), beginAccess, optionalChainResult, loc,
659+
builder);
650660
break;
651661
}
652662

test/SILOptimizer/sil_combine.sil

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -enable-objc-interop -enforce-exclusivity=none -enable-sil-verify-all %s -sil-combine | %FileCheck %s
1+
// RUN: %target-sil-opt -enable-objc-interop -enable-sil-verify-all %s -sil-combine | %FileCheck %s
22

33
// Declare this SIL to be canonical because some tests break raw SIL
44
// conventions. e.g. address-type block args. -enforce-exclusivity=none is also
@@ -4226,3 +4226,42 @@ bb0:
42264226
return %16 : $Int
42274227
} // end sil function 'test_global_value'
42284228

4229+
final class Kp1 {
4230+
@_hasStorage @_hasInitialValue final var b: Kp2? { get set }
4231+
@objc deinit
4232+
init()
4233+
}
4234+
4235+
class Kp2 {
4236+
@_hasStorage @_hasInitialValue var s: String { get set }
4237+
@objc deinit
4238+
init()
4239+
}
4240+
4241+
sil @kpgetter : $@convention(thin) (@in_guaranteed Kp2) -> @out String
4242+
sil @kpsetter : $@convention(thin) (@in_guaranteed String, @in_guaranteed Kp2) -> ()
4243+
sil @swift_getAtKeyPath : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @guaranteed KeyPath<τ_0_0, τ_0_1>) -> @out τ_0_1
4244+
4245+
// Test keypath optimization for optional chaining with accesses scopes.
4246+
// E.g. \.a?.b (on a class)
4247+
//
4248+
// CHECK-LABEL: sil @test_optional_chaining_keypath
4249+
// CHECK: [[BA:%[0-9]+]] = begin_access
4250+
// CHECK: switch_enum {{.*}} case #Optional.none!enumelt: bb2, case #Optional.some!enumelt: bb1
4251+
// CHECK: bb1:
4252+
// CHECK: apply
4253+
// CHECK: end_access [[BA]]
4254+
// CHECK: bb2:
4255+
// CHECK: end_access [[BA]]
4256+
// CHECK: bb3:
4257+
// CHECK: } // end sil function 'test_optional_chaining_keypath'
4258+
sil @test_optional_chaining_keypath : $@convention(thin) (@in_guaranteed Kp1) -> @out Optional<String> {
4259+
bb0(%0 : $*Optional<String>, %1 : $*Kp1):
4260+
%kp = keypath $KeyPath<Kp1, Optional<String>>, (root $Kp1; stored_property #Kp1.b : $Optional<Kp2>; optional_chain : $Kp2; settable_property $String, id #Kp2.s!getter : (Kp2) -> () -> String, getter @kpgetter : $@convention(thin) (@in_guaranteed Kp2) -> @out String, setter @kpsetter : $@convention(thin) (@in_guaranteed String, @in_guaranteed Kp2) -> (); optional_wrap : $Optional<String>)
4261+
%f = function_ref @swift_getAtKeyPath : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @guaranteed KeyPath<τ_0_0, τ_0_1>) -> @out τ_0_1
4262+
%a = apply %f<Kp1, Optional<String>>(%0, %1, %kp) : $@convention(thin) <τ_0_0, τ_0_1> (@in_guaranteed τ_0_0, @guaranteed KeyPath<τ_0_0, τ_0_1>) -> @out τ_0_1
4263+
strong_release %kp : $KeyPath<Kp1, Optional<String>>
4264+
%r = tuple ()
4265+
return %r : $()
4266+
}
4267+

0 commit comments

Comments
 (0)