Skip to content

Fix escape analysis: addressable parameters. #80628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -641,11 +641,13 @@ fileprivate struct EscapeWalker<V: EscapeVisitor> : ValueDefUseWalker,

// Indirect arguments cannot escape the function, but loaded values from such can.
if !followLoads(at: path) {
guard let beginApply = apply as? BeginApplyInst else {
return .continueWalk
}
// Except for begin_apply: it can yield an address value.
if !indirectResultEscapes(of: beginApply, path: path) {
if let beginApply = apply as? BeginApplyInst {
// begin_apply can yield an address value.
if !indirectResultEscapes(of: beginApply, path: path) {
return .continueWalk
}
} else if !apply.isAddressable(operand: argOp) {
// The result does not depend on the argument's address.
return .continueWalk
}
}
Expand Down
7 changes: 7 additions & 0 deletions SwiftCompilerSources/Sources/SIL/ApplySite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ extension ApplySite {
functionConvention.resultDependencies != nil
}

public func isAddressable(operand: Operand) -> Bool {
if let dep = resultDependence(on: operand) {
return dep.isAddressable(for: operand.value)
}
return false
}

public var hasLifetimeDependence: Bool {
functionConvention.hasLifetimeDependencies()
}
Expand Down
96 changes: 95 additions & 1 deletion test/SILOptimizer/addr_escape_info.sil
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// RUN: %target-sil-opt %s -dump-addr-escape-info -o /dev/null | %FileCheck %s
// RUN: %target-sil-opt %s -dump-addr-escape-info -o /dev/null \
// RUN: -enable-experimental-feature LifetimeDependence \
// RUN: -enable-experimental-feature AddressableTypes \
// RUN: | %FileCheck %s

// REQUIRES: swift_feature_LifetimeDependence
// REQUIRES: swift_feature_AddressableTypes

// REQUIRES: swift_in_compiler

Expand Down Expand Up @@ -37,6 +43,13 @@ protocol P {}

extension Int : P {}

@_addressableForDependencies
struct Addressable {
@_hasStorage var a: Int
}

struct NE : ~Escapable {}

sil @no_arguments : $@convention(thin) () -> ()
sil @indirect_argument : $@convention(thin) (@in Int) -> ()
sil @indirect_struct_argument : $@convention(thin) (@in Str) -> ()
Expand Down Expand Up @@ -69,6 +82,14 @@ sil @modifyStr : $@convention(method) (@inout Str) -> ()
sil @guaranteed_yield_coroutine : $@yield_once @convention(thin) (@inout X) -> @yields @inout X
sil @in_ptr : $@convention(thin) (@in Builtin.RawPointer) -> ()

sil @addressable_independent_arg : $@convention(thin) (@in_guaranteed Addressable) -> Builtin.RawPointer

sil @addressable_dependent_arg : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE

sil @addressable_noescape_arg : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE {
[%0: noescape]
}

// CHECK-LABEL: Address escape information for test_simple:
// CHECK: value: %1 = struct_element_addr %0 : $*Str, #Str.a
// CHECK-NEXT: ==> %7 = apply %6(%5) : $@convention(thin) (@in Int) -> ()
Expand Down Expand Up @@ -862,3 +883,76 @@ bb0(%0 : @guaranteed $X):
return %3 : $()
}

// CHECK-LABEL: Address escape information for noescape_via_independent_addressable:
// CHECK: pair 0 - 1
// CHECK-NEXT: apply %{{.*}}(%{{.*}}) : $@convention(thin) (@in_guaranteed Addressable) -> Builtin.RawPointer
// CHECK-NEXT: alloc_stack $Addressable
// CHECK-NEXT: no alias
// CHECK-LABEL: End function noescape_via_independent_addressable
sil [ossa] @noescape_via_independent_addressable : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_stack $Addressable
%2 = struct_element_addr %1 : $*Addressable, #Addressable.a
store %0 to [trivial] %2

%f = function_ref @addressable_independent_arg : $@convention(thin) (@in_guaranteed Addressable) -> Builtin.RawPointer
%a = apply %f(%1) : $@convention(thin) (@in_guaranteed Addressable) -> Builtin.RawPointer

fix_lifetime %a
fix_lifetime %1
destroy_addr %1
dealloc_stack %1

%9 = tuple ()
return %9 : $()
}

// CHECK-LABEL: Address escape information for escape_via_dependent_addressable:
// CHECK: pair 0 - 1
// CHECK-NEXT: %{{.*}} = apply %{{.*}}(%{{.*}}) : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE
// CHECK-NEXT: %{{.*}} = alloc_stack $Addressable
// CHECK-NEXT: may alias
// CHECK-LABEL: End function escape_via_dependent_addressable
sil [ossa] @escape_via_dependent_addressable : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_stack $Addressable
%2 = struct_element_addr %1 : $*Addressable, #Addressable.a
store %0 to [trivial] %2

%f = function_ref @addressable_dependent_arg : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE
%a = apply %f(%1) : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE

fix_lifetime %a
fix_lifetime %1
destroy_value %a
destroy_addr %1
dealloc_stack %1

%9 = tuple ()
return %9 : $()
}

// CHECK-LABEL: Address escape information for escape_via_noescape_addressable:
// CHECK: pair 0 - 1
// CHECK-NEXT: %{{.*}} = apply %{{.*}}(%{{.*}}) : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE
// CHECK-NEXT: %{{.*}} = alloc_stack $Addressable
// CHECK-NEXT: no alias
// CHECK-LABEL: End function escape_via_noescape_addressable
sil [ossa] @escape_via_noescape_addressable : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
%1 = alloc_stack $Addressable
%2 = struct_element_addr %1 : $*Addressable, #Addressable.a
store %0 to [trivial] %2

%f = function_ref @addressable_noescape_arg : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE
%a = apply %f(%1) : $@convention(thin) (@in_guaranteed Addressable) -> @lifetime(borrow address_for_deps 0) @owned NE

fix_lifetime %a
fix_lifetime %1
destroy_value %a
destroy_addr %1
dealloc_stack %1

%9 = tuple ()
return %9 : $()
}