Skip to content

Commit 487b917

Browse files
[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.
1 parent 18ffb87 commit 487b917

File tree

5 files changed

+112
-13
lines changed

5 files changed

+112
-13
lines changed

clang/lib/CodeGen/ABIInfo.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ ABIInfo::getOptimalVectorMemoryType(llvm::FixedVectorType *T,
247247
// Pin the vtable to this file.
248248
SwiftABIInfo::~SwiftABIInfo() = default;
249249

250+
void SwiftABIInfo::countOccupiedRegisters(ArrayRef<llvm::Type *> scalarTypes,
251+
unsigned &intCount, unsigned &fpCount,
252+
unsigned maxIntRegisterBitWidth) {
253+
for (llvm::Type *type : scalarTypes) {
254+
if (type->isPointerTy()) {
255+
intCount++;
256+
} else if (auto *intTy = dyn_cast<llvm::IntegerType>(type)) {
257+
intCount += (intTy->getBitWidth() + maxIntRegisterBitWidth - 1) / maxIntRegisterBitWidth;
258+
} else {
259+
assert(type->isVectorTy() || type->isFloatingPointTy());
260+
fpCount++;
261+
}
262+
}
263+
}
264+
250265
/// Does the given lowering require more than the given number of
251266
/// registers when expanded?
252267
///
@@ -262,18 +277,10 @@ SwiftABIInfo::~SwiftABIInfo() = default;
262277
/// return registers.
263278
bool SwiftABIInfo::occupiesMoreThan(ArrayRef<llvm::Type *> scalarTypes,
264279
unsigned maxAllRegisters) const {
280+
// Use the pointer width as the maximum integer register bit width by default.
281+
unsigned ptrWidth = CGT.getTarget().getPointerWidth(LangAS::Default);
265282
unsigned intCount = 0, fpCount = 0;
266-
for (llvm::Type *type : scalarTypes) {
267-
if (type->isPointerTy()) {
268-
intCount++;
269-
} else if (auto intTy = dyn_cast<llvm::IntegerType>(type)) {
270-
auto ptrWidth = CGT.getTarget().getPointerWidth(LangAS::Default);
271-
intCount += (intTy->getBitWidth() + ptrWidth - 1) / ptrWidth;
272-
} else {
273-
assert(type->isVectorTy() || type->isFloatingPointTy());
274-
fpCount++;
275-
}
276-
}
283+
countOccupiedRegisters(scalarTypes, intCount, fpCount, ptrWidth);
277284

278285
return (intCount + fpCount > maxAllRegisters);
279286
}

clang/lib/CodeGen/ABIInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ class SwiftABIInfo {
141141
CodeGenTypes &CGT;
142142
bool SwiftErrorInRegister;
143143

144+
static void countOccupiedRegisters(ArrayRef<llvm::Type *> scalarTypes,
145+
unsigned &intCount, unsigned &fpCount,
146+
unsigned maxIntRegisterBitWidth);
144147
bool occupiesMoreThan(ArrayRef<llvm::Type *> scalarTypes,
145148
unsigned maxAllRegisters) const;
146149

clang/lib/CodeGen/Targets/WebAssembly.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,40 @@ class WebAssemblyABIInfo final : public ABIInfo {
4545
AggValueSlot Slot) const override;
4646
};
4747

48+
class WebAssemblySwiftABIInfo final : public SwiftABIInfo {
49+
WebAssemblyABIKind Kind;
50+
public:
51+
explicit WebAssemblySwiftABIInfo(CodeGen::CodeGenTypes &CGT, WebAssemblyABIKind K)
52+
: SwiftABIInfo(CGT, /*SwiftErrorInRegister=*/false), Kind(K) {}
53+
54+
bool shouldPassIndirectly(ArrayRef<llvm::Type *> ComponentTys,
55+
bool AsReturnValue) const override {
56+
unsigned maxIntRegisterBitWidth = 64;
57+
unsigned intCount = 0, fpCount = 0;
58+
countOccupiedRegisters(ComponentTys, intCount, fpCount, maxIntRegisterBitWidth);
59+
60+
if (AsReturnValue) {
61+
if (Kind == WebAssemblyABIKind::ExperimentalMV) {
62+
// If the experimental multivalue ABI is enabled, try to return up to 2 values for each
63+
// of int and fp, which is a very conservative value based on the number of available
64+
// physical gp return registers used in the major engines to minimize stack spills
65+
// at JIT time.
66+
return intCount > 2 || fpCount > 2;
67+
}
68+
// By default, limit to 1 total register.
69+
return (intCount + fpCount > 1);
70+
}
71+
// For an argument, limit to 4 total registers.
72+
return (intCount + fpCount > 4);
73+
}
74+
};
75+
4876
class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo {
4977
public:
5078
explicit WebAssemblyTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT,
5179
WebAssemblyABIKind K)
5280
: TargetCodeGenInfo(std::make_unique<WebAssemblyABIInfo>(CGT, K)) {
53-
SwiftInfo =
54-
std::make_unique<SwiftABIInfo>(CGT, /*SwiftErrorInRegister=*/false);
81+
SwiftInfo = std::make_unique<WebAssemblySwiftABIInfo>(CGT, K);
5582
}
5683

5784
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %clang_cc1 -triple wasm32-unknown-unknown %s -emit-llvm -o - | FileCheck %s
2+
3+
typedef struct {
4+
int aa;
5+
int bb;
6+
} s1;
7+
8+
// Multiple-element structs should be returned through sret.
9+
// CHECK: define swiftcc void @return_s1(ptr dead_on_unwind noalias writable sret(%struct.s1) align 4 %agg.result)
10+
__attribute__((swiftcall))
11+
s1 return_s1(void) {
12+
s1 foo;
13+
return foo;
14+
}
15+
16+
typedef struct {
17+
int cc;
18+
} s2;
19+
20+
// Single-element structs should be returned directly.
21+
// CHECK: define swiftcc i32 @return_s2()
22+
__attribute__((swiftcall))
23+
s2 return_s2(void) {
24+
s2 foo;
25+
return foo;
26+
}
27+
28+
typedef struct {
29+
char c1[4];
30+
} s3;
31+
32+
// CHECK: define swiftcc i32 @return_s3()
33+
__attribute__((swiftcall))
34+
s3 return_s3(void) {
35+
s3 foo;
36+
return foo;
37+
}
38+
39+
typedef struct {
40+
int bf1 : 4;
41+
int bf2 : 3;
42+
int bf3 : 8;
43+
} s4;
44+
45+
// CHECK: define swiftcc i16 @return_s4()
46+
__attribute__((swiftcall))
47+
s4 return_s4(void) {
48+
s4 foo;
49+
return foo;
50+
}
51+
52+
typedef struct {
53+
long long v;
54+
} s5;
55+
56+
// CHECK: define swiftcc i64 @return_s5()
57+
__attribute__((swiftcall))
58+
s5 return_s5(void) {
59+
s5 foo;
60+
return foo;
61+
}

llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "WebAssemblyUtilities.h"
2121
#include "llvm/CodeGen/CallingConvLower.h"
2222
#include "llvm/CodeGen/MachineFrameInfo.h"
23+
#include "llvm/CodeGen/MachineFunction.h"
2324
#include "llvm/CodeGen/MachineInstrBuilder.h"
2425
#include "llvm/CodeGen/MachineJumpTableInfo.h"
2526
#include "llvm/CodeGen/MachineModuleInfo.h"

0 commit comments

Comments
 (0)