Skip to content

Commit b46eddb

Browse files
authored
Merge pull request #84564 from eeckstein/fix-constant-capture-propagation
ConstantCapturePropagation: don't propagate keypaths with multiple uses in non-OSSA
2 parents df17eea + 273874c commit b46eddb

File tree

2 files changed

+104
-15
lines changed

2 files changed

+104
-15
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ConstantCapturePropagation.swift

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ let constantCapturePropagation = FunctionPass(name: "constant-capture-propagatio
6666
continue
6767
}
6868

69+
if !context.continueWithNextSubpassRun(for: partialApply) {
70+
return
71+
}
72+
6973
optimizeClosureWithDeadCaptures(of: partialApply, context)
7074

7175
if partialApply.isDeleted {
@@ -301,9 +305,15 @@ private extension PartialApplyInst {
301305
var nonConstArgs = [Operand]()
302306
var hasKeypath = false
303307
for argOp in argumentOperands {
304-
if argOp.value.isConstant(hasKeypath: &hasKeypath) {
308+
// In non-OSSA we don't know where to insert the compensating release for a propagated keypath.
309+
// Therefore bail if a keypath has multiple uses.
310+
switch argOp.value.isConstant(requireSingleUse: !parentFunction.hasOwnership && !isOnStack) {
311+
case .constant:
312+
constArgs.append(argOp)
313+
case .constantWithKeypath:
305314
constArgs.append(argOp)
306-
} else {
315+
hasKeypath = true
316+
case .notConstant:
307317
nonConstArgs.append(argOp)
308318
}
309319
}
@@ -333,33 +343,49 @@ private extension FullApplySite {
333343
}
334344
}
335345

346+
private enum ConstantKind {
347+
case notConstant
348+
case constant
349+
case constantWithKeypath
350+
351+
func merge(with other: ConstantKind) -> ConstantKind {
352+
switch (self, other) {
353+
case (.notConstant, _): return .notConstant
354+
case (_, .notConstant): return .notConstant
355+
case (.constant, .constant): return .constant
356+
default: return .constantWithKeypath
357+
}
358+
}
359+
}
360+
336361
private extension Value {
337-
func isConstant(hasKeypath: inout Bool) -> Bool {
362+
func isConstant(requireSingleUse: Bool) -> ConstantKind {
338363
// All instructions handled here must also be handled in
339364
// `FunctionSignatureSpecializationMangler::mangleConstantProp`.
365+
let result: ConstantKind
340366
switch self {
341367
case let si as StructInst:
342-
for op in si.operands {
343-
if !op.value.isConstant(hasKeypath: &hasKeypath) {
344-
return false
345-
}
346-
}
347-
return true
368+
result = si.operands.reduce(.constant, {
369+
$0.merge(with: $1.value.isConstant(requireSingleUse: requireSingleUse))
370+
})
348371
case is ThinToThickFunctionInst, is ConvertFunctionInst, is UpcastInst, is OpenExistentialRefInst:
349-
return (self as! UnaryInstruction).operand.value.isConstant(hasKeypath: &hasKeypath)
372+
result = (self as! UnaryInstruction).operand.value.isConstant(requireSingleUse: requireSingleUse)
350373
case is StringLiteralInst, is IntegerLiteralInst, is FloatLiteralInst, is FunctionRefInst, is GlobalAddrInst:
351-
return true
374+
result = .constant
352375
case let keyPath as KeyPathInst:
353-
hasKeypath = true
354376
guard keyPath.operands.isEmpty,
355377
keyPath.hasPattern,
356378
!keyPath.substitutionMap.hasAnySubstitutableParams
357379
else {
358-
return false
380+
return .notConstant
359381
}
360-
return true
382+
result = .constantWithKeypath
361383
default:
362-
return false
384+
return .notConstant
385+
}
386+
if requireSingleUse, result == .constantWithKeypath, !uses.ignoreDebugUses.isSingleUse {
387+
return .notConstant
363388
}
389+
return result
364390
}
365391
}

test/SILOptimizer/capture_propagation.sil

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,29 @@ bb0:
558558
return %7 : $()
559559
}
560560

561+
// CHECK-LABEL: sil @dontPropagateMulitUseKeyPathInNonOSSA :
562+
// CHECK: %0 = keypath
563+
// CHECK: [[U:%.*]] = upcast %0
564+
// CHECK: [[C:%.*]] = function_ref @closureWithKeypath
565+
// CHECK: partial_apply [callee_guaranteed] [[C]]([[U]])
566+
// CHECK: } // end sil function 'dontPropagateMulitUseKeyPathInNonOSSA'
567+
sil @dontPropagateMulitUseKeyPathInNonOSSA : $@convention(thin) () -> () {
568+
bb0:
569+
%0 = keypath $WritableKeyPath<Str, Int>, (root $Str; stored_property #Str.a : $Int)
570+
%c = upcast %0 to $KeyPath<Str, Int>
571+
%1 = function_ref @closureWithKeypath : $@convention(thin) (Str, @guaranteed KeyPath<Str, Int>) -> Int
572+
%2 = partial_apply [callee_guaranteed] %1(%c) : $@convention(thin) (Str, @guaranteed KeyPath<Str, Int>) -> Int
573+
strong_retain %0
574+
%3 = convert_escape_to_noescape %2 : $@callee_guaranteed (Str) -> Int to $@noescape @callee_guaranteed (Str) -> Int
575+
%4 = function_ref @calleeWithKeypath : $@convention(thin) (@noescape @callee_guaranteed (Str) -> Int) -> ()
576+
%5 = apply %4(%3) : $@convention(thin) (@noescape @callee_guaranteed (Str) -> Int) -> ()
577+
strong_release %2 : $@callee_guaranteed (Str) -> Int
578+
strong_release %0
579+
%7 = tuple ()
580+
return %7 : $()
581+
}
582+
583+
561584
// CHECK-LABEL: sil shared @$s18closureWithKeypath{{.*}}main3StrVSiTf3npk_n : $@convention(thin) (Str) -> Int {
562585
// CHECK: [[K:%[0-9]+]] = keypath
563586
// CHECK: [[F:%[0-9]+]] = function_ref @swift_getAtKeyPath
@@ -824,6 +847,46 @@ bb0:
824847
return %12 : $()
825848
}
826849

850+
// CHECK-LABEL: sil [ossa] @testNonConstStruct1 :
851+
// CHECK: [[S:%.*]] = struct $S
852+
// CHECK: partial_apply [callee_guaranteed] {{%[0-9]+}}([[S]])
853+
// CHECK: } // end sil function 'testNonConstStruct1'
854+
sil [ossa] @testNonConstStruct1 : $@convention(thin) (Int32) -> () {
855+
bb0(%0 : $Int32):
856+
%2 = integer_literal $Builtin.Int1, 0
857+
%3 = struct $Bool (%2)
858+
%4 = struct $S (%0, %3)
859+
%5 = function_ref @closureWithStruct : $@convention(thin) (Str, S) -> Builtin.Int32
860+
%6 = partial_apply [callee_guaranteed] %5(%4) : $@convention(thin) (Str, S) -> Builtin.Int32
861+
%7 = convert_escape_to_noescape %6 to $@noescape @callee_guaranteed (Str) -> Builtin.Int32
862+
%8 = function_ref @useIntClosure : $@convention(thin) (@noescape @callee_guaranteed (Str) -> Builtin.Int32) -> ()
863+
%9 = apply %8(%7) : $@convention(thin) (@noescape @callee_guaranteed (Str) -> Builtin.Int32) -> ()
864+
destroy_value %7
865+
destroy_value %6
866+
%12 = tuple ()
867+
return %12 : $()
868+
}
869+
870+
// CHECK-LABEL: sil [ossa] @testNonConstStruct2 :
871+
// CHECK: [[S:%.*]] = struct $S
872+
// CHECK: partial_apply [callee_guaranteed] {{%[0-9]+}}([[S]])
873+
// CHECK: } // end sil function 'testNonConstStruct2'
874+
sil [ossa] @testNonConstStruct2 : $@convention(thin) (Bool) -> () {
875+
bb0(%0 : $Bool):
876+
%1 = integer_literal $Builtin.Int32, 3
877+
%2 = struct $Int32 (%1)
878+
%4 = struct $S (%2, %0)
879+
%5 = function_ref @closureWithStruct : $@convention(thin) (Str, S) -> Builtin.Int32
880+
%6 = partial_apply [callee_guaranteed] %5(%4) : $@convention(thin) (Str, S) -> Builtin.Int32
881+
%7 = convert_escape_to_noescape %6 to $@noescape @callee_guaranteed (Str) -> Builtin.Int32
882+
%8 = function_ref @useIntClosure : $@convention(thin) (@noescape @callee_guaranteed (Str) -> Builtin.Int32) -> ()
883+
%9 = apply %8(%7) : $@convention(thin) (@noescape @callee_guaranteed (Str) -> Builtin.Int32) -> ()
884+
destroy_value %7
885+
destroy_value %6
886+
%12 = tuple ()
887+
return %12 : $()
888+
}
889+
827890
// CHECK-LABEL: sil shared [ossa] @$s17closureWithStruct4main1SVs5Int32VSbTf3npSSi3Si0_n : $@convention(thin) (Str) -> Builtin.Int32 {
828891
// CHECK: bb0(%0 : $Str):
829892
// CHECK: %1 = integer_literal $Builtin.Int32, 3

0 commit comments

Comments
 (0)