Skip to content

Commit d677b7c

Browse files
authored
Merge pull request #73595 from eeckstein/fix-compute-side-effects
ComputeSideEffects: correctly handle escaping arguments
2 parents 387580c + 4b2973a commit d677b7c

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ComputeSideEffects.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ private struct CollectedEffects {
215215
}
216216

217217
mutating func addEffectsForEscapingArgument(argument: FunctionArgument) {
218-
var escapeWalker = ArgumentEscapingWalker()
218+
var escapeWalker = ArgumentEscapingWalker(context)
219219

220220
if escapeWalker.hasUnknownUses(argument: argument) {
221221
// Worst case: we don't know anything about how the argument escapes.
@@ -414,13 +414,18 @@ private func defaultPath(for value: Value) -> SmallProjectionPath {
414414
/// Checks if an argument escapes to some unknown user.
415415
private struct ArgumentEscapingWalker : ValueDefUseWalker, AddressDefUseWalker {
416416
var walkDownCache = WalkerCache<UnusedWalkingPath>()
417+
private let calleeAnalysis: CalleeAnalysis
417418

418419
/// True if the argument escapes to a load which (potentially) "takes" the memory location.
419420
private(set) var foundTakingLoad = false
420421

421422
/// True, if the argument escapes to a closure context which might be destroyed when called.
422423
private(set) var foundConsumingPartialApply = false
423424

425+
init(_ context: FunctionPassContext) {
426+
self.calleeAnalysis = context.calleeAnalysis
427+
}
428+
424429
mutating func hasUnknownUses(argument: FunctionArgument) -> Bool {
425430
if argument.type.isAddress {
426431
return walkDownUses(ofAddress: argument, path: UnusedWalkingPath()) == .abortWalk
@@ -444,14 +449,23 @@ private struct ArgumentEscapingWalker : ValueDefUseWalker, AddressDefUseWalker {
444449
return .continueWalk
445450

446451
case let apply as ApplySite:
447-
if apply.isCallee(operand: value) {
448-
// `CollectedEffects.handleApply` only handles argument operands of an apply, but not the callee operand.
449-
return .abortWalk
450-
}
451452
if let pa = apply as? PartialApplyInst, !pa.isOnStack {
452453
foundConsumingPartialApply = true
453454
}
454-
return .continueWalk
455+
// `CollectedEffects.handleApply` only handles argument operands of an apply, but not the callee operand.
456+
if let calleeArgIdx = apply.calleeArgumentIndex(of: value),
457+
let callees = calleeAnalysis.getCallees(callee: apply.callee)
458+
{
459+
// If an argument escapes in a called function, we don't know anything about the argument's side effects.
460+
// For example, it could escape to the return value and effects might occur in the caller.
461+
for callee in callees {
462+
if callee.effects.escapeEffects.canEscape(argumentIndex: calleeArgIdx, path: SmallProjectionPath.init(.anyValueFields)) {
463+
return .abortWalk
464+
}
465+
}
466+
return .continueWalk
467+
}
468+
return .abortWalk
455469
default:
456470
return .abortWalk
457471
}

test/SILOptimizer/side_effects.sil

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ sil @$s4test1ZCfD : $@convention(method) (@owned Z) -> () {
8787
// CHECK-LABEL: sil @load_store_to_args
8888
// CHECK-NEXT: [%0: read v**]
8989
// CHECK-NEXT: [%1: write v**]
90-
// CHECK-NEXT: [%2: write c0.v**]
90+
// CHECK-NEXT: [%2: noescape **, write c0.v**]
9191
// CHECK-NEXT: [global: ]
9292
// CHECK-NEXT: {{^[^[]}}
9393
sil @load_store_to_args : $@convention(thin) (@inout Int32, @inout Int32, @guaranteed X) -> () {
94+
[%2: noescape **]
9495
bb0(%0 : $*Int32, %1 : $*Int32, %2 : $X):
9596
%l1 = load %0 : $*Int32
9697
store %l1 to %1 : $*Int32
@@ -971,11 +972,13 @@ bb0(%0 : @guaranteed $S):
971972
}
972973

973974
// CHECK-LABEL: sil [ossa] @storeToArgs
974-
// CHECK-NEXT: [%0: write c0.v**]
975-
// CHECK-NEXT: [%1: write c0.v**]
975+
// CHECK-NEXT: [%0: noescape **, write c0.v**]
976+
// CHECK-NEXT: [%1: noescape **, write c0.v**]
976977
// CHECK-NEXT: [global: ]
977978
// CHECK-NEXT: {{^[^[]}}
978979
sil [ossa] @storeToArgs : $@convention(thin) (@guaranteed List, @guaranteed List) -> () {
980+
[%0: noescape **]
981+
[%1: noescape **]
979982
bb0(%1 : @guaranteed $List, %2 : @guaranteed $List):
980983
cond_br undef, bb1, bb2
981984

@@ -1195,3 +1198,39 @@ bb0(%0 : $*T):
11951198
%r = tuple()
11961199
return %r : $()
11971200
}
1201+
1202+
sil @store_owned_to_out : $@convention(thin) (@owned SP) -> @out SP {
1203+
[%0: noescape **, write v**]
1204+
[%1: escape v** -> %0.v**]
1205+
[global: ]
1206+
}
1207+
1208+
// CHECK-LABEL: sil @test_escaping_arg1
1209+
// CHECK-NEXT: [%0: write v**.c*.v**, copy v**.c*.v**, destroy v**.c*.v**]
1210+
// CHECK-NEXT: [global: write,copy,destroy]
1211+
// CHECK-NEXT: {{^[^[]}}
1212+
sil @test_escaping_arg1 : $@convention(thin) (@owned SP) -> () {
1213+
bb0(%0 : $SP):
1214+
%1 = alloc_stack $SP
1215+
%2 = function_ref @store_owned_to_out : $@convention(thin) (@owned SP) -> @out SP
1216+
%3 = apply %2(%1, %0) : $@convention(thin) (@owned SP) -> @out SP
1217+
%4 = struct_element_addr %1 : $*SP, #SP.value
1218+
%5 = load %4 : $*X
1219+
strong_release %5 : $X
1220+
dealloc_stack %1 : $*SP
1221+
%r = tuple ()
1222+
return %r : $()
1223+
}
1224+
1225+
// CHECK-LABEL: sil @test_escaping_arg2
1226+
// CHECK-NEXT: [%0: read v**.c*.v**, write v**.c*.v**, copy v**.c*.v**, destroy v**.c*.v**]
1227+
// CHECK-NEXT: [global: read,write,copy,destroy,allocate,deinit_barrier]
1228+
// CHECK-NEXT: {{^[^[]}}
1229+
sil @test_escaping_arg2 : $@convention(thin) (@owned SP) -> () {
1230+
bb0(%0 : $SP):
1231+
%1 = function_ref @forward_to_return : $@convention(thin) (@owned SP) -> @owned SP
1232+
%2 = apply %1(%0) : $@convention(thin) (@owned SP) -> @owned SP
1233+
release_value %2 : $SP
1234+
%r = tuple ()
1235+
return %r : $()
1236+
}

0 commit comments

Comments
 (0)