Skip to content

Conversation

@kateinoigakukun
Copy link
Member

@kateinoigakukun kateinoigakukun commented Nov 6, 2025

The Swift calling convention on Wasm has historically returned aggregate values directly at the LLVM IR level due to the use of the generic SwiftABIInfo implementation. The direct return at LLVM IR level will cause unnecessary stack allocation and memory copies for each aggregate return value like below:

target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasip1"

define hidden swiftcc { i32, i32, i32 } @thunk() #0 {
entry:
  %call = tail call swiftcc { i32, i32, i32 } @f(ptr noundef nonnull @thunk) #2
  ret { i32, i32, i32 } %call
}

declare swiftcc { i32, i32, i32 } @f(ptr noundef)
(func $thunk (type 0) (param i32 i32 i32)
    (local i32 i32 i32)
    global.get 0
    i32.const 16
    i32.sub
    local.tee 3
    global.set 0
    local.get 3
    i32.const 1
    local.get 3
    local.get 3
    call 0
    local.get 3
    i32.load offset=4
    local.set 4
    local.get 3
    i32.load offset=8
    local.set 5
    local.get 0
    local.get 3
    i32.load
    i32.store
    local.get 0
    local.get 5
    i32.store offset=8
    local.get 0
    local.get 4
    i32.store offset=4
    local.get 3
    i32.const 16
    i32.add
    global.set 0)
  (elem (;0;) (i32.const 1) func $thunk)

This explicit indirection at LLVM IR generation stage is important for Swift because we have a lot of call-forwarding thunk functions (like protocol witness method and merged function thunks).

After this patch, the IR and the lowered code look like below:

target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-wasip1"

%struct.foo = type { i32, i32, i32 }

define hidden swiftcc void @thunk(ptr dead_on_unwind noalias writable sret(%struct.foo) align 4 %agg.result) #0 {
entry:
  call swiftcc void @f(ptr dead_on_unwind writable sret(%struct.foo) align 4 %agg.result, ptr noundef @thunk)
  ret void
}

declare swiftcc void @f(ptr dead_on_unwind writable sret(%struct.foo) align 4, ptr noundef) #1
  (func $thunk (type 0) (param i32 i32 i32)
    local.get 0
    i32.const 1
    local.get 0
    local.get 0
    call 0)
  (elem (;0;) (i32.const 1) func $thunk)

The Swift calling convention on Wasm has historically returned aggregate
values directly at the LLVM IR level due to the use of the generic
`SwiftABIInfo` implementation. The direct return at LLVM IR level will
cause unnecessary stack allocation and memory copies for each aggregate
return value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant