From 4b0d43ae80fa14818dc81d64335cadfa8d55a7a4 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 6 Nov 2025 01:04:56 +0000 Subject: [PATCH] [clang][WebAssembly] Return aggregate values indirectly in swiftcc 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. --- clang/lib/CodeGen/Targets/WebAssembly.cpp | 26 +++++++++- .../CodeGen/WebAssembly/wasm-return-swiftcc.c | 50 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 clang/test/CodeGen/WebAssembly/wasm-return-swiftcc.c diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp b/clang/lib/CodeGen/Targets/WebAssembly.cpp index 9217c78a540a3..3d16ea66253ca 100644 --- a/clang/lib/CodeGen/Targets/WebAssembly.cpp +++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp @@ -45,13 +45,35 @@ class WebAssemblyABIInfo final : public ABIInfo { AggValueSlot Slot) const override; }; +class WebAssemblySwiftABIInfo final : public SwiftABIInfo { + WebAssemblyABIKind K; + +public: + explicit WebAssemblySwiftABIInfo(CodeGen::CodeGenTypes &CGT, + WebAssemblyABIKind K) + : SwiftABIInfo(CGT, /*SwiftErrorInRegister=*/false), K(K) {} + + bool shouldPassIndirectly(ArrayRef ComponentTys, + bool AsReturnValue) const override { + if (AsReturnValue) { + if (K == WebAssemblyABIKind::ExperimentalMV) { + // If the MV ABI is enabled, return all values directly. + return false; + } + // Otherwise, check if the value occupies more than 1 scalar slot. + return SwiftABIInfo::occupiesMoreThan(ComponentTys, /*total=*/1); + } + // For arguments, just follow the default implementation. + return SwiftABIInfo::shouldPassIndirectly(ComponentTys, AsReturnValue); + } +}; + class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { public: explicit WebAssemblyTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, WebAssemblyABIKind K) : TargetCodeGenInfo(std::make_unique(CGT, K)) { - SwiftInfo = - std::make_unique(CGT, /*SwiftErrorInRegister=*/false); + SwiftInfo = std::make_unique(CGT, K); } void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, diff --git a/clang/test/CodeGen/WebAssembly/wasm-return-swiftcc.c b/clang/test/CodeGen/WebAssembly/wasm-return-swiftcc.c new file mode 100644 index 0000000000000..631883c7c5284 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-return-swiftcc.c @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-unknown %s -emit-llvm -o - | FileCheck %s + +typedef struct { + int aa; + int bb; +} s1; + +// Multiple-element structs should be returned through sret. +// CHECK: define swiftcc void @return_s1(ptr dead_on_unwind noalias writable sret(%struct.s1) align 4 %agg.result) +__attribute__((swiftcall)) +s1 return_s1(void) { + s1 foo; + return foo; +} + +typedef struct { + int cc; +} s2; + +// Single-element structs should be returned directly. +// CHECK: define swiftcc i32 @return_s2() +__attribute__((swiftcall)) +s2 return_s2(void) { + s2 foo; + return foo; +} + +typedef struct { + char c1[4]; +} s3; + +// CHECK: define swiftcc i32 @return_s3() +__attribute__((swiftcall)) +s3 return_s3(void) { + s3 foo; + return foo; +} + +typedef struct { + int bf1 : 4; + int bf2 : 3; + int bf3 : 8; +} s4; + +// CHECK: define swiftcc i16 @return_s4() +__attribute__((swiftcall)) +s4 return_s4(void) { + s4 foo; + return foo; +} \ No newline at end of file