Skip to content

Commit a95f02c

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 also 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); } Change `f128` passing to use the stack, which is the same thing GCC does, and matches `i128`. 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. That above ABI statement as well as consistency with `i128` (which is returned in xmm0) mean that it would likely be better for GCC to change its return ABI to match Clang rather than the other way around, so that portion is left as-is. 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 a95f02c

File tree

2 files changed

+7
-3
lines changed

2 files changed

+7
-3
lines changed

clang/lib/CodeGen/Targets/X86.cpp

+5
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

+2-3
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)