Skip to content

Commit 8b646bf

Browse files
committed
[clang] Always pass fp128 arguments indirectly on Windows
Clang currently passes and returns `__float128` in vector registers on MinGW targets. However, the Windows x86-64 calling convention [1] states the following: __m128 types, arrays, and strings are never passed by immediate value. Instead, a pointer is passed to memory allocated by the caller. Structs and unions of size 8, 16, 32, or 64 bits, and __m64 types, are passed as if they were integers of the same size. Structs or unions of other sizes are passed as a pointer to memory allocated by the caller. For these aggregate types passed as a pointer, including __m128, the caller-allocated temporary memory must be 16-byte aligned. Based on the above it sounds like `__float128` should be passed indirectly; this is what MinGW GCC already does, so change Clang to match. Passing by value causes problems with varargs. E.g. the below completes successfully when built with GCC but has a runtime crash when built with Clang: void va_f128(int count, ...) { va_list args; va_start(args, count); __float128 val = va_arg(args, __float128); va_end(args); } int main() { va_f128(0, 0.0); } This patch fixes the above. It also resolves crashes when calling GCC-built f128 libcalls. Regarding return values, the documentation states: A scalar return value that can fit into 64 bits, including the __m64 type, is returned through RAX. Non-scalar types including floats, doubles, and vector types such as __m128, __m128i, __m128d are returned in XMM0. This makes it sound like it should be acceptable to return `__float128` in XMM0. However, GCC returns `__float128` on the stack, so do the same here to be consistent. Clang's MSVC targets do not support `__float128` or `_Float128`, but these changes would also apply there if it is eventually enabled. [1]: https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170
1 parent 60b0716 commit 8b646bf

File tree

2 files changed

+7
-3
lines changed

2 files changed

+7
-3
lines changed

clang/lib/CodeGen/Targets/X86.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3403,6 +3403,11 @@ ABIArgInfo WinX86_64ABIInfo::classify(QualType Ty, unsigned &FreeSSERegs,
34033403
return ABIArgInfo::getDirect(llvm::FixedVectorType::get(
34043404
llvm::Type::getInt64Ty(getVMContext()), 2));
34053405

3406+
case BuiltinType::Float128:
3407+
// f128 is too large to fit in integer registers so the Windows ABI
3408+
// require it be passed on the stack. GCC does the same.
3409+
return ABIArgInfo::getIndirect(Align, /*ByVal=*/false);
3410+
34063411
default:
34073412
break;
34083413
}

clang/test/CodeGen/win64-fp128.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@
33
// __float128 is unsupported on MSVC
44

55
__float128 fp128_ret(void) { return 0; }
6-
// GNU64: define dso_local fp128 @fp128_ret()
6+
// GNU64: define dso_local void @fp128_ret(ptr dead_on_unwind noalias writable sret(fp128) align 16 %agg.result)
77

88
__float128 fp128_args(__float128 a, __float128 b) { return a * b; }
9-
// GNU64: define dso_local fp128 @fp128_args(fp128 noundef %a, fp128 noundef %b)
9+
// GNU64: define dso_local void @fp128_args(ptr dead_on_unwind noalias writable sret(fp128) align 16 %agg.result, ptr noundef %0, ptr noundef %1)
1010

1111
void fp128_vararg(int a, ...) {
1212
// GNU64-LABEL: define dso_local void @fp128_vararg
1313
__builtin_va_list ap;
1414
__builtin_va_start(ap, a);
1515
__float128 i = __builtin_va_arg(ap, __float128);
16-
// movaps xmm0, xmmword ptr [rax]
1716
// GNU64: load ptr, ptr
1817
// GNU64: load fp128, ptr
1918
__builtin_va_end(ap);

0 commit comments

Comments
 (0)