Skip to content
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

@_assemblyVision doesn't seem to see allocations from "partial apply forwarder for reabstraction thunk helper" #77820

Open
weissi opened this issue Nov 23, 2024 · 1 comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels

Comments

@weissi
Copy link
Contributor

weissi commented Nov 23, 2024

Description

The NIOThreadPool._blockingWaitForWork() function in SwiftNIO is supposed to be @inlinable, the desired type is this:

public typealias WorkItem = @Sendable (WorkItemState) -> Void
public enum WorkItemState: Sendable {
    case active
    case cancelled
}

@inline(never)  // <-- adding this adds an allocation
@inlinable
internal func _blockingWaitForWork(identifier: Int) -> (item: WorkItem, state: WorkItemState)? {

Unfortunately, this makes the function allocate each time on its return because it creates a
partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@unowned NIOPosix.NIOThreadPool.WorkItemState) -> () to @escaping @callee_guaranteed @Sendable (@in_guaranteed NIOPosix.NIOThreadPool.WorkItemState) -> (@out ()) at <compiler-generated>.

Therefore, the function currently doesn't have the @inline(never) but it should because it's a "marker function" that we want to always see in stack traces, even ones that can't properly deal with inlined functions.

Anyway, @_assemblyVision doesn't seem to show the allocation for the partial apply forwarder for reabstraction thunk helper.

Problematic assembly:

test_read_10000_chunks_from_file`NIOThreadPool._blockingWaitForWork(identifier:):
test_read_10000_chunks_from_file[0x10015d7c0] <+0>:   sub    sp, sp, #0x50
test_read_10000_chunks_from_file[0x10015d7c4] <+4>:   stp    x24, x23, [sp, #0x10]
test_read_10000_chunks_from_file[0x10015d7c8] <+8>:   stp    x22, x21, [sp, #0x20]
test_read_10000_chunks_from_file[0x10015d7cc] <+12>:  stp    x20, x19, [sp, #0x30]
test_read_10000_chunks_from_file[0x10015d7d0] <+16>:  stp    x29, x30, [sp, #0x40]
test_read_10000_chunks_from_file[0x10015d7d4] <+20>:  add    x29, sp, #0x40
test_read_10000_chunks_from_file[0x10015d7d8] <+24>:  mov    x19, x20
test_read_10000_chunks_from_file[0x10015d7dc] <+28>:  mov    x21, x0
test_read_10000_chunks_from_file[0x10015d7e0] <+32>:  ldr    x20, [x20, #0x10]
test_read_10000_chunks_from_file[0x10015d7e4] <+36>:  strb   wzr, [sp, #0xf]
test_read_10000_chunks_from_file[0x10015d7e8] <+40>:  add    x0, sp, #0xf
test_read_10000_chunks_from_file[0x10015d7ec] <+44>:  bl     0x10002d900    ; NIOConcurrencyHelpers.ConditionLock.lock(whenValue: T) -> () at lock.swift:193
test_read_10000_chunks_from_file[0x10015d7f0] <+48>:  mov    x0, x19
test_read_10000_chunks_from_file[0x10015d7f4] <+52>:  mov    x1, x21
test_read_10000_chunks_from_file[0x10015d7f8] <+56>:  bl     0x10015d89c    ; closure #1 () -> (unlockWith: NIOPosix.NIOThreadPool._WorkState, result: Swift.Optional<(item: @Sendable (NIOPosix.NIOThreadPool.WorkItemState) -> (), state: NIOPosix.NIOThreadPool.WorkItemState)>) in NIOPosix.NIOThreadPool._blockingWaitForWork(identifier: Swift.Int) -> Swift.Optional<(item: @Sendable (NIOPosix.NIOThreadPool.WorkItemState) -> (), state: NIOPosix.NIOThreadPool.WorkItemState)> at NIOThreadPool.swift:258
test_read_10000_chunks_from_file[0x10015d7fc] <+60>:  mov    x19, x1
test_read_10000_chunks_from_file[0x10015d800] <+64>:  and    w24, w0, #0x1
test_read_10000_chunks_from_file[0x10015d804] <+68>:  cbz    x1, 0x10015d86c ; <+172> [inlined] generic specialization <serialized, NIOPosix.NIOThreadPool._WorkState> of NIOConcurrencyHelpers.ConditionLock._unlock(with: Swift.Optional<τ_0_0>) -> () at NIOThreadPool.swift:533:9
test_read_10000_chunks_from_file[0x10015d808] <+72>:  mov    x21, x2
test_read_10000_chunks_from_file[0x10015d80c] <+76>:  mov    x22, x3
test_read_10000_chunks_from_file[0x10015d810] <+80>:  adrp   x0, 209
test_read_10000_chunks_from_file[0x10015d814] <+84>:  add    x0, x0, #0x188 ; block_descriptor.40 + 480
test_read_10000_chunks_from_file[0x10015d818] <+88>:  mov    w1, #0x20 ; =32 
test_read_10000_chunks_from_file[0x10015d81c] <+92>:  mov    w2, #0x7 ; =7 
test_read_10000_chunks_from_file[0x10015d820] <+96>:  bl     0x1001bd5e8    ; symbol stub for: swift_allocObject
test_read_10000_chunks_from_file[0x10015d824] <+100>: mov    x23, x0
test_read_10000_chunks_from_file[0x10015d828] <+104>: stp    x19, x21, [x0, #0x10]
test_read_10000_chunks_from_file[0x10015d82c] <+108>: and    w19, w22, #0x1
test_read_10000_chunks_from_file[0x10015d830] <+112>: strb   w24, [sp, #0xe]
test_read_10000_chunks_from_file[0x10015d834] <+116>: add    x0, sp, #0xe
test_read_10000_chunks_from_file[0x10015d838] <+120>: bl     0x10002dd64    ; NIOConcurrencyHelpers.ConditionLock.unlock(withValue: T) -> () at lock.swift:293
test_read_10000_chunks_from_file[0x10015d83c] <+124>: adrp   x0, 209
test_read_10000_chunks_from_file[0x10015d840] <+128>: add    x0, x0, #0x160 ; block_descriptor.40 + 440
test_read_10000_chunks_from_file[0x10015d844] <+132>: mov    w1, #0x20 ; =32 
test_read_10000_chunks_from_file[0x10015d848] <+136>: mov    w2, #0x7 ; =7 
test_read_10000_chunks_from_file[0x10015d84c] <+140>: bl     0x1001bd5e8    ; symbol stub for: swift_allocObject  <<<<<<< JW: PROBLEMATIC ALLOC
test_read_10000_chunks_from_file[0x10015d850] <+144>: mov    x1, x0
test_read_10000_chunks_from_file[0x10015d854] <+148>: adrp   x8, 2
test_read_10000_chunks_from_file[0x10015d858] <+152>: add    x8, x8, #0x680 ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@unowned NIOPosix.NIOThreadPool.WorkItemState) -> () to @escaping @callee_guaranteed @Sendable (@in_guaranteed NIOPosix.NIOThreadPool.WorkItemState) -> (@out ()) at <compiler-generated>
test_read_10000_chunks_from_file[0x10015d85c] <+156>: stp    x8, x23, [x0, #0x10]
test_read_10000_chunks_from_file[0x10015d860] <+160>: adrp   x0, 2
test_read_10000_chunks_from_file[0x10015d864] <+164>: add    x0, x0, #0x64c ; partial apply forwarder for reabstraction thunk helper from @escaping @callee_guaranteed @Sendable (@in_guaranteed NIOPosix.NIOThreadPool.WorkItemState) -> (@out ()) to @escaping @callee_guaranteed @Sendable (@unowned NIOPosix.NIOThreadPool.WorkItemState) -> () at <compiler-generated>
test_read_10000_chunks_from_file[0x10015d868] <+168>: b      0x10015d880    ; <+192> at NIOThreadPool.swift:300:5
test_read_10000_chunks_from_file[0x10015d86c] <+172>: strb   w24, [sp, #0xe]
test_read_10000_chunks_from_file[0x10015d870] <+176>: add    x0, sp, #0xe
test_read_10000_chunks_from_file[0x10015d874] <+180>: bl     0x10002dd64    ; NIOConcurrencyHelpers.ConditionLock.unlock(withValue: T) -> () at lock.swift:293
test_read_10000_chunks_from_file[0x10015d878] <+184>: mov    x0, #0x0 ; =0 
test_read_10000_chunks_from_file[0x10015d87c] <+188>: mov    x1, #0x0 ; =0 
test_read_10000_chunks_from_file[0x10015d880] <+192>: mov    x2, x19
test_read_10000_chunks_from_file[0x10015d884] <+196>: ldp    x29, x30, [sp, #0x40]
test_read_10000_chunks_from_file[0x10015d888] <+200>: ldp    x20, x19, [sp, #0x30]
test_read_10000_chunks_from_file[0x10015d88c] <+204>: ldp    x22, x21, [sp, #0x20]
test_read_10000_chunks_from_file[0x10015d890] <+208>: ldp    x24, x23, [sp, #0x10]
test_read_10000_chunks_from_file[0x10015d894] <+212>: add    sp, sp, #0x50
test_read_10000_chunks_from_file[0x10015d898] <+216>: ret    

Reproduction

git clone https://github.com/apple/swift-nio && cd swift-nio
git reset --hard 70dfce82b0c892c06e5730a43dc3fd717d4c1fbd

## add the `@inline(never)`
git apply <<"EOF"
diff --git a/Sources/NIOPosix/NIOThreadPool.swift b/Sources/NIOPosix/NIOThreadPool.swift
index 6d3bc08f7..524809a2c 100644
--- a/Sources/NIOPosix/NIOThreadPool.swift
+++ b/Sources/NIOPosix/NIOThreadPool.swift
@@ -253,6 +253,7 @@ public final class NIOThreadPool {
     // This function is one of those and giving it a consistent name makes it much easier to remove from the profiles
     // when only interested in on-CPU work.
     @inlinable
+    @inline(never)
     internal func _blockingWaitForWork(identifier: Int) -> (item: WorkItem, state: WorkItemState)? {
         self._conditionLock.withLock(when: .hasWork) {
             () -> (unlockWith: _WorkState, result: (WorkItem, WorkItemState)?) in
EOF

swift build -c release

Expected behavior

Does not add a swift_allocObject

Environment

Swift 5.9 through 6.0 on Linux and macOS

Additional information

No response

@weissi weissi added bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels labels Nov 23, 2024
@weissi
Copy link
Contributor Author

weissi commented Nov 23, 2024

CC @gottesmm, I assume @_assemblyVision is supposed to show the partial apply forwarder for reabstraction thunk helper, no?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. triage needed This issue needs more specific labels
Projects
None yet
Development

No branches or pull requests

1 participant