Skip to content

Commit 4476088

Browse files
committed
[ComputeSideEffects] Track deinit-barrier-ness.
Functions "are deinit barriers" (more pedantically, applies of functions are deinit barriers) if any of their instructions are deinit barriers. During side-effect analysis, when walking a function's instructions for other global effects, also check for the deinit-barrier effect. If an instruction is found to be a deinit barrier, mark the function's global effects accordingly. Add SILFunction::isDeinitBarrier to conveniently access the effects computed during ComputeSideEffects. Update the isBarrierApply predicate to iterate over the list of callees, if complete, to check whether any is a deinit barrier. If none is, then the apply is not a deinit barrier.
1 parent 7ea3363 commit 4476088

File tree

7 files changed

+111
-22
lines changed

7 files changed

+111
-22
lines changed

SwiftCompilerSources/Sources/Optimizer/Analysis/CalleeAnalysis.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,19 @@ public struct CalleeAnalysis {
5454
}
5555
}
5656

57+
extension FullApplySite {
58+
fileprivate func isBarrier(_ analysis: CalleeAnalysis) -> Bool {
59+
guard let callees = analysis.getCallees(callee: callee) else {
60+
return true
61+
}
62+
return callees.contains { $0.isDeinitBarrier }
63+
}
64+
}
65+
5766
extension Instruction {
5867
public final func maySynchronize(_ analysis: CalleeAnalysis) -> Bool {
5968
if let site = self as? FullApplySite {
60-
return true
69+
return site.isBarrier(analysis)
6170
}
6271
return maySynchronizeNotConsideringSideEffects
6372
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ private struct CollectedEffects {
8888
}
8989

9090
mutating func addInstructionEffects(_ inst: Instruction) {
91+
var checkedIfDeinitBarrier = false
9192
switch inst {
9293
case is CopyValueInst, is RetainValueInst, is StrongRetainInst:
9394
addEffects(.copy, to: inst.operands[0].value, fromInitialPath: SmallProjectionPath(.anyValueFields))
@@ -131,12 +132,14 @@ private struct CollectedEffects {
131132
addDestroyEffects(of: calleeValue)
132133
}
133134
handleApply(apply)
135+
checkedIfDeinitBarrier = true
134136

135137
case let pa as PartialApplyInst:
136138
if pa.canBeAppliedInFunction(context) {
137139
// Only if the created closure can actually be called in the function
138140
// we have to consider side-effects within the closure.
139141
handleApply(pa)
142+
checkedIfDeinitBarrier = true
140143
}
141144

142145
case let fl as FixLifetimeInst:
@@ -178,6 +181,13 @@ private struct CollectedEffects {
178181
globalEffects.allocates = true
179182
}
180183
}
184+
// If we didn't already, check whether the instruction could be a deinit
185+
// barrier. If it's an apply of some sort, that was already done in
186+
// handleApply.
187+
if !checkedIfDeinitBarrier,
188+
inst.mayBeDeinitBarrierNotConsideringSideEffects {
189+
globalEffects.isDeinitBarrier = true
190+
}
181191
}
182192

183193
mutating func addEffectsForEcapingArgument(argument: FunctionArgument) {

SwiftCompilerSources/Sources/SIL/Effects.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,19 +395,29 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
395395
/// are not observable form the outside and are therefore not considered.
396396
public var allocates: Bool
397397

398+
/// If true, destroys of lexical values may not be hoisted over applies of
399+
/// the function.
400+
///
401+
/// This is true when the function (or a callee, transitively) contains a
402+
/// deinit barrier instruction.
403+
public var isDeinitBarrier: Bool
404+
398405
/// When called with default arguments, it creates an "effect-free" GlobalEffects.
399406
public init(memory: Memory = Memory(read: false, write: false),
400407
ownership: Ownership = Ownership(copy: false, destroy: false),
401-
allocates: Bool = false) {
408+
allocates: Bool = false,
409+
isDeinitBarrier: Bool = false) {
402410
self.memory = memory
403411
self.ownership = ownership
404412
self.allocates = allocates
413+
self.isDeinitBarrier = isDeinitBarrier
405414
}
406415

407416
public mutating func merge(with other: GlobalEffects) {
408417
memory.merge(with: other.memory)
409418
ownership.merge(with: other.ownership)
410419
allocates = allocates || other.allocates
420+
isDeinitBarrier = isDeinitBarrier || other.isDeinitBarrier
411421
}
412422

413423
/// Removes effects, which cannot occur for an `argument` value with a given `convention`.
@@ -444,12 +454,13 @@ public struct SideEffects : CustomStringConvertible, NoReflectionChildren {
444454
}
445455

446456
public static var worstEffects: GlobalEffects {
447-
GlobalEffects(memory: .worstEffects, ownership: .worstEffects, allocates: true)
457+
GlobalEffects(memory: .worstEffects, ownership: .worstEffects, allocates: true, isDeinitBarrier: true)
448458
}
449459

450460
public var description: String {
451461
var res: [String] = [memory.description, ownership.description].filter { !$0.isEmpty }
452462
if allocates { res += ["allocate"] }
463+
if isDeinitBarrier { res += ["deinit_barrier"] }
453464
return res.joined(separator: ",")
454465
}
455466
}
@@ -652,6 +663,7 @@ extension StringParser {
652663
else if consume("copy") { globalEffects.ownership.copy = true }
653664
else if consume("destroy") { globalEffects.ownership.destroy = true }
654665
else if consume("allocate") { globalEffects.allocates = true }
666+
else if consume("deinit_barrier") { globalEffects.isDeinitBarrier = true }
655667
else {
656668
break
657669
}

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
186186
SILFunction_needsStackProtection(bridged) != 0
187187
}
188188

189+
public var isDeinitBarrier: Bool {
190+
effects.sideEffects?.global.isDeinitBarrier ?? true
191+
}
192+
189193
// Only to be called by PassContext
190194
public func _modifyEffects(_ body: (inout FunctionEffects) -> ()) {
191195
body(&effects)

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include "swift/SIL/DynamicCasts.h"
2222
#include "swift/SIL/InstructionUtils.h"
2323
#include "swift/SIL/SILArgument.h"
24+
#include "swift/SIL/SILBridging.h"
25+
#include "swift/SIL/SILBridgingUtils.h"
2426
#include "swift/SIL/SILBuilder.h"
2527
#include "swift/SIL/SILDebugInfoExpression.h"
2628
#include "swift/SIL/SILModule.h"
@@ -30,7 +32,9 @@
3032
#include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
3133
#include "swift/SILOptimizer/Analysis/Analysis.h"
3234
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
35+
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
3336
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
37+
#include "swift/SILOptimizer/OptimizerBridging.h"
3438
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
3539
#include "swift/SILOptimizer/Utils/DebugOptUtils.h"
3640
#include "swift/SILOptimizer/Utils/ValueLifetime.h"

test/SILOptimizer/cross-module-effects.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public func inlineWithEffects(_ x: X) -> Int {
7979
return internalCallee(x)
8080
}
8181

82+
// CHECK-LE-MODULE-LABEL: sil [serialized] [noinline] [canonical] @loadWeakX_from : {{.*}} {
83+
// CHECK-LE-MODULE: {{^[^[]}}
84+
// CHECK-LE-MODULE-LABEL: } // end sil function 'loadWeakX_from'
85+
8286
// CHECK-FR-MODULE-LABEL: sil [serialized] [noinline] [canonical] @$s6Module12simpleInlineySiAA1XCF : $@convention(thin) (@guaranteed X) -> Int {
8387
// CHECK-FR-MODULE-NEXT: [%0: noescape **, read c0.v**]
8488
// CHECK-FR-MODULE-NEXT: [global: ]
@@ -91,6 +95,21 @@ public func simpleInline(_ x: X) -> Int {
9195
return x.i
9296
}
9397

98+
public struct S {
99+
public init(_ x: X) { self.x = x }
100+
public weak var x: X?
101+
}
102+
103+
// CHECK-FR-MODULE-LABEL: sil [serialized] [noinline] [canonical] @loadWeakX_from : {{.*}} {
104+
// CHECK-FR-MODULE: [global: deinit_barrier]
105+
// CHECK-FR-MODULE-LABEL: } // end sil function 'loadWeakX_from'
106+
@inlinable
107+
@inline(never)
108+
@_silgen_name("loadWeakX_from")
109+
public func loadWeakX(from s: S) -> X? {
110+
return s.x
111+
}
112+
94113
#else
95114

96115
import Module
@@ -176,6 +195,27 @@ public func callit5() -> Int {
176195
// CHECK-LE-MAIN-LABEL: sil public_external [noinline] @$s6Module12simpleInlineySiAA1XCF : $@convention(thin) (@guaranteed X) -> Int {
177196
// CHECK-LE-MAIN-NEXT: {{^[^[]}}
178197

198+
// CHECK-MAIN-LABEL: sil [noinline] @callit6 : {{.*}} {
199+
// CHECK-MAIN: [[GET_X:%[^,]+]] = function_ref @$s6Module1XCACycfc
200+
// CHECK-MAIN: [[X:%[^,]+]] = apply [[GET_X]]({{%[^,]+}})
201+
// CHECK-MAIN: [[LOAD_WEAK_X_FROM:%[^,]+]] = function_ref @loadWeakX_from
202+
// CHECK-MAIN: apply [[LOAD_WEAK_X_FROM]]({{%[^,]+}})
203+
// CHECK-MAIN: strong_release [[X]]
204+
// CHECK-MAIN-LABEL: } // end sil function 'callit6'
205+
@_silgen_name("callit6")
206+
@inline(never)
207+
public func callit6() -> X? {
208+
let x = X()
209+
let s = S(x)
210+
let out = loadWeakX(from: s)
211+
return out
212+
}
213+
214+
// CHECK-MAIN-LABEL: sil public_external [noinline] @loadWeakX_from : {{.*}} {
215+
// CHECK-FR-MAIN: [global: deinit_barrier]
216+
// CHECK-LE-MAIN: {{^[^[]}}
217+
// CHECK-MAIN-LABEL: } // end sil function 'loadWeakX_from'
218+
179219
// CHECK-FR-MAIN-LABEL: sil @$s6Module14internalCalleeySiAA1XCF : $@convention(thin) (@guaranteed X) -> Int {
180220
// CHECK-FR-MAIN-NEXT: [%0: noescape **, read c0.v**]
181221
// CHECK-FR-MAIN-NEXT: [global: ]

test/SILOptimizer/side_effects.sil

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ bb0(%0 : $X, %1 : $*Int32):
129129
// CHECK-NEXT: [%0: read v**]
130130
// CHECK-NEXT: [%1: write v**]
131131
// CHECK-NEXT: [%2: write c0.v**, destroy c*.v**]
132-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
132+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
133133
// CHECK-NEXT: {{^[^[]}}
134134
sil @partial_apply_load_store_to_args : $@convention(thin) (@inout Int32, @inout Int32, @guaranteed X) -> () {
135135
bb0(%0 : $*Int32, %1 : $*Int32, %2 : $X):
@@ -220,7 +220,7 @@ bb0(%0 : $X):
220220
}
221221

222222
// CHECK-LABEL: sil @call_unknown
223-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
223+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
224224
// CHECK-NEXT: {{^[^[]}}
225225
sil @call_unknown : $@convention(thin) () -> () {
226226
bb0:
@@ -233,7 +233,7 @@ bb0:
233233

234234
// CHECK-LABEL: sil @call_unknown_func_with_trivial_args
235235
// CHECK-NEXT: [%0: read v**]
236-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
236+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
237237
// CHECK-NEXT: {{^[^[]}}
238238
sil @call_unknown_func_with_trivial_args : $@convention(thin) (@in Int32, Int32) -> () {
239239
bb0(%0 : $*Int32, %1 : $Int32):
@@ -293,7 +293,7 @@ bb0(%0 : $Builtin.NativeObject):
293293
sil @checkedaddr : $@convention(thin) (Builtin.NativeObject, X) -> () {
294294
// CHECK-NEXT: [%0: read v**.c*.v**, write v**.c*.v**, copy v**.c*.v**, destroy v**.c*.v**]
295295
// CHECK-NEXT: [%1: read c*.v**, write c*.v**, copy c*.v**, destroy c*.v**]
296-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
296+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
297297
// CHECK-NEXT: {{^[^[]}}
298298
bb0(%0 : $Builtin.NativeObject, %1 : $X):
299299
unconditional_checked_cast_addr Builtin.NativeObject in %0 : $Builtin.NativeObject to X in %1 : $X
@@ -450,7 +450,7 @@ bb0(%0 : $*X, %1 : @owned $X):
450450
// CHECK-LABEL: sil [ossa] @unknown_destructor_effects
451451
// CHECK-NEXT: [%0: write v**, destroy v**]
452452
// CHECK-NEXT: [%1: read c*.v**, write c*.v**, copy c*.v**, destroy c*.v**]
453-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
453+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
454454
// CHECK-NEXT: {{^[^[]}}
455455
sil [ossa] @unknown_destructor_effects : $@convention(thin) (@inout Y, @owned Y) -> () {
456456
bb0(%0 : $*Y, %1 : @owned $Y):
@@ -496,7 +496,7 @@ bb0(%0 : $*X, %1 : $*X):
496496

497497
// CHECK-LABEL: sil [ossa] @destroy_value_effects1
498498
// CHECK-NEXT: [%0: destroy v**.c*.v**]
499-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
499+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
500500
// CHECK-NEXT: {{^[^[]}}
501501
sil [ossa] @destroy_value_effects1 : $@convention(thin) (@owned SP) -> () {
502502
bb0(%0 : @owned $SP):
@@ -628,7 +628,7 @@ bb0(%0 : $*X):
628628

629629
// CHECK-LABEL: sil [ossa] @pass_arg_to_unknown_function
630630
// CHECK-NEXT: [%0: read s1.v**, write s1.v**, copy s1.v**, destroy s1.v**]
631-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
631+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
632632
// CHECK-NEXT: {{^[^[]}}
633633
sil [ossa] @pass_arg_to_unknown_function : $@convention(thin) (@inout TwoSPs) -> () {
634634
bb0(%0 : $*TwoSPs):
@@ -641,7 +641,7 @@ bb0(%0 : $*TwoSPs):
641641

642642
// CHECK-LABEL: sil [ossa] @copy_addr_and_consume_by_unknown_func
643643
// CHECK-NEXT: [%0: read v**, copy v**, destroy v**]
644-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
644+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
645645
// CHECK-NEXT: {{^[^[]}}
646646
sil [ossa] @copy_addr_and_consume_by_unknown_func : $@convention(thin) (@in X) -> () {
647647
bb0(%0 : $*X):
@@ -672,7 +672,7 @@ bb2:
672672
}
673673

674674
// CHECK-LABEL: sil @call_readnone
675-
// CHECK-NEXT: [global: copy]
675+
// CHECK-NEXT: [global: copy,deinit_barrier]
676676
// CHECK-NEXT: {{^[^[]}}
677677
sil @call_readnone : $@convention(thin) () -> () {
678678
bb0:
@@ -683,7 +683,7 @@ bb0:
683683
}
684684

685685
// CHECK-LABEL: sil @call_releasenone
686-
// CHECK-NEXT: [global: read,write,copy,allocate]
686+
// CHECK-NEXT: [global: read,write,copy,allocate,deinit_barrier]
687687
// CHECK-NEXT: {{^[^[]}}
688688
sil @call_releasenone : $@convention(thin) () -> () {
689689
bb0:
@@ -696,7 +696,7 @@ bb0:
696696

697697
// CHECK-LABEL: sil @call_readonly_owned
698698
// CHECK-NEXT: [%0: read c*.v**, copy c*.v**]
699-
// CHECK-NEXT: [global: read,copy]
699+
// CHECK-NEXT: [global: read,copy,deinit_barrier]
700700
// CHECK-NEXT: {{^[^[]}}
701701
sil @call_readonly_owned : $@convention(thin) (@owned X) -> () {
702702
bb0(%0 : $X):
@@ -708,7 +708,7 @@ bb0(%0 : $X):
708708

709709
// CHECK-LABEL: sil [ossa] @call_readnone_owned
710710
// CHECK-NEXT: [%0: copy c*.v**]
711-
// CHECK-NEXT: [global: copy]
711+
// CHECK-NEXT: [global: copy,deinit_barrier]
712712
// CHECK-NEXT: {{^[^[]}}
713713
sil [ossa] @call_readnone_owned : $@convention(thin) (@owned X) -> @owned X {
714714
bb0(%0 : @owned $X):
@@ -719,7 +719,7 @@ bb0(%0 : @owned $X):
719719

720720
// CHECK-LABEL: sil @call_readonly_guaranteed
721721
// CHECK-NEXT: [%0: read c*.v**, copy c*.v**]
722-
// CHECK-NEXT: [global: read,copy]
722+
// CHECK-NEXT: [global: read,copy,deinit_barrier]
723723
// CHECK-NEXT: {{^[^[]}}
724724
sil @call_readonly_guaranteed : $@convention(thin) (@guaranteed X) -> () {
725725
bb0(%0 : $X):
@@ -733,7 +733,7 @@ bb0(%0 : $X):
733733
// CHECK-NEXT: [%0: write v**]
734734
// CHECK-NEXT: [%1: read v**, copy v**]
735735
// CHECK-NEXT: [%2: read v**, copy v**]
736-
// CHECK-NEXT: [global: read,copy]
736+
// CHECK-NEXT: [global: read,copy,deinit_barrier]
737737
// CHECK-NEXT: {{^[^[]}}
738738
sil @call_readonly_indirect_params : $@convention(thin) (@in_guaranteed X, @inout X) -> @out X {
739739
bb0(%0 : $*X, %1 : $*X, %2 : $*X):
@@ -747,7 +747,7 @@ bb0(%0 : $*X, %1 : $*X, %2 : $*X):
747747
// CHECK-NEXT: [%0: write v**]
748748
// CHECK-NEXT: [%1: read v**, copy v**]
749749
// CHECK-NEXT: [%2: read v**, copy v**]
750-
// CHECK-NEXT: [global: read,copy]
750+
// CHECK-NEXT: [global: read,copy,deinit_barrier]
751751
// CHECK-NEXT: {{^[^[]}}
752752
sil @call_readnone_indirect_params : $@convention(thin) (@in_guaranteed X, @inout X) -> @out X {
753753
bb0(%0 : $*X, %1 : $*X, %2 : $*X):
@@ -759,7 +759,7 @@ bb0(%0 : $*X, %1 : $*X, %2 : $*X):
759759

760760
// CHECK-LABEL: sil @call_unknown_func_with_in_guaranteed_arg
761761
// CHECK-NEXT: [%0: read v**, copy v**]
762-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
762+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
763763
// CHECK-NEXT: {{^[^[]}}
764764
sil @call_unknown_func_with_in_guaranteed_arg : $@convention(thin) (@in_guaranteed X) -> () {
765765
bb0(%0 : $*X):
@@ -771,7 +771,7 @@ bb0(%0 : $*X):
771771

772772
// CHECK-LABEL: sil @test_escaping_in_argument
773773
// CHECK-NEXT: [%0: read v**, copy v**]
774-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
774+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
775775
// CHECK-NEXT: {{^[^[]}}
776776
sil @test_escaping_in_argument : $@convention(thin) (@in_guaranteed X) -> () {
777777
bb0(%0 : $*X):
@@ -859,7 +859,7 @@ bb0:
859859
}
860860

861861
// CHECK-LABEL: sil @call_public_external_func
862-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
862+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
863863
// CHECK-NEXT: {{^[^[]}}
864864
sil @call_public_external_func : $@convention(thin) () -> () {
865865
bb0:
@@ -969,7 +969,7 @@ bb3:
969969

970970
// CHECK-LABEL: sil @storeMaybeLocalPhi
971971
// CHECK-NEXT: [%0: read c*.v**, write c*.v**, copy **, destroy c*.v**]
972-
// CHECK-NEXT: [global: read,write,copy,destroy,allocate]
972+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
973973
// CHECK-NEXT: {{^[^[]}}
974974
sil @storeMaybeLocalPhi : $@convention(thin) (@guaranteed List) -> () {
975975
bb0(%1 : $List):
@@ -1069,3 +1069,13 @@ bb0(%0 : @guaranteed ${ var Int32 }):
10691069
end_access %5 : $*Int32
10701070
return %6 : $Int32
10711071
}
1072+
1073+
// CHECK-LABEL: sil [ossa] @callUnknownIsDeinitBarrier : {{.*}} {
1074+
// CHECK-NEXT: [global: {{.*}}deinit_barrier]
1075+
// CHECK-LABEL: } // end sil function 'callUnknownIsDeinitBarrier'
1076+
sil [ossa] @callUnknownIsDeinitBarrier : $@convention(thin) () -> () {
1077+
%callee = function_ref @unknown_func : $@convention(thin) () -> ()
1078+
apply %callee() : $@convention(thin) () -> ()
1079+
%retval = tuple ()
1080+
return %retval : $()
1081+
}

0 commit comments

Comments
 (0)