Skip to content

[X86][Windows] use LowerWin64_INT128_TO_FP for u128 to f128 conversion#207285

Merged
folkertdev merged 1 commit into
llvm:mainfrom
folkertdev:windows-f128-libcall-fix
Jul 7, 2026
Merged

[X86][Windows] use LowerWin64_INT128_TO_FP for u128 to f128 conversion#207285
folkertdev merged 1 commit into
llvm:mainfrom
folkertdev:windows-f128-libcall-fix

Conversation

@folkertdev

@folkertdev folkertdev commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This change fixes an ABI issue where the signed conversion passes a i128 different from a u128, and crucially different from GCC.

https://godbolt.org/z/x3daY3rej

--- <unnamed>
+++ <unnamed>
@@ -1,15 +1,15 @@
-conv_from_i128:
+conv_from_u128:
         push    rsi
-        sub     rsp, 64
+        sub     rsp, 48
         mov     rsi, rcx
-        movaps  xmm0, xmmword ptr [rdx]
-        movaps  xmmword ptr [rsp + 48], xmm0
+        mov     rax, qword ptr [rdx]
+        mov     r8, qword ptr [rdx + 8]
         lea     rcx, [rsp + 32]
-        lea     rdx, [rsp + 48]
-        call    __floattitf
+        mov     rdx, rax
+        call    __floatuntitf
         movaps  xmm0, xmmword ptr [rsp + 32]
         movaps  xmmword ptr [rsi], xmm0
         mov     rax, rsi
-        add     rsp, 64
+        add     rsp, 48
         pop     rsi
         ret

The signed case being correct is apparently due to a special windows-specific path being chosen. The unsigned version bailed out too early. So, I've changed LowerUINT_TO_FP to be more like LowerSINT_TO_FP. In particular this moves the "Bail out when we don't have native conversion instructions." down a bit, so that LowerWin64_INT128_TO_FP is actually used.

I've then also added some testing for win64 SSE. I'm not sure if the other target features are as relevant but I can add those too if helpful.

This was found by running the rust-lang/compiler-builtins tests with a build that includes #204887.

Comment on lines +1216 to +1227
; WIN64-SSE-LABEL: TestUIToFPU128_F128:
; WIN64-SSE: # %bb.0: # %entry
; WIN64-SSE-NEXT: subq $72, %rsp
; WIN64-SSE-NEXT: movaps vu128(%rip), %xmm0
; WIN64-SSE-NEXT: movaps %xmm0, {{[0-9]+}}(%rsp)
; WIN64-SSE-NEXT: leaq {{[0-9]+}}(%rsp), %rcx
; WIN64-SSE-NEXT: leaq {{[0-9]+}}(%rsp), %rdx
; WIN64-SSE-NEXT: callq __floatuntitf
; WIN64-SSE-NEXT: movaps {{[0-9]+}}(%rsp), %xmm0
; WIN64-SSE-NEXT: movaps %xmm0, vf128(%rip)
; WIN64-SSE-NEXT: addq $72, %rsp
; WIN64-SSE-NEXT: retq

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This generated code is now equivalent to the signed version

--- <unnamed>
+++ <unnamed>
@@ -1,11 +1,11 @@
-; WIN64-SSE-LABEL: TestSIToFPI128_F128:
+; WIN64-SSE-LABEL: TestUIToFPU128_F128:
 ; WIN64-SSE:       # %bb.0: # %entry
 ; WIN64-SSE-NEXT:    subq $72, %rsp
-; WIN64-SSE-NEXT:    movaps vi128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps vu128(%rip), %xmm0
 ; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
 ; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
 ; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
-; WIN64-SSE-NEXT:    callq __floattitf
+; WIN64-SSE-NEXT:    callq __floatuntitf
 ; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
 ; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
 ; WIN64-SSE-NEXT:    addq $72, %rsp

@folkertdev
folkertdev requested review from RKSimon and rnk July 2, 2026 21:46
@folkertdev
folkertdev marked this pull request as ready for review July 2, 2026 21:46
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-x86

Author: Folkert de Vries (folkertdev)

Changes

This change fixes an ABI issue where the signed conversion passes a i128 different from a u128, and crucially different from GCC.

https://godbolt.org/z/x3daY3rej

--- &lt;unnamed&gt;
+++ &lt;unnamed&gt;
@@ -1,15 +1,15 @@
-conv_from_i128:
+conv_from_u128:
         push    rsi
-        sub     rsp, 64
+        sub     rsp, 48
         mov     rsi, rcx
-        movaps  xmm0, xmmword ptr [rdx]
-        movaps  xmmword ptr [rsp + 48], xmm0
+        mov     rax, qword ptr [rdx]
+        mov     r8, qword ptr [rdx + 8]
         lea     rcx, [rsp + 32]
-        lea     rdx, [rsp + 48]
-        call    __floattitf
+        mov     rdx, rax
+        call    __floatuntitf
         movaps  xmm0, xmmword ptr [rsp + 32]
         movaps  xmmword ptr [rsi], xmm0
         mov     rax, rsi
-        add     rsp, 64
+        add     rsp, 48
         pop     rsi
         ret

The signed case being correct is apparently due to a special windows-specific path being chosen. The unsigned version bailed out too early. So, I've changed LowerUINT_TO_FP to be more like LowerSINT_TO_FP. In particular this moves the "Bail out when we don't have native conversion instructions." down a bit, so that LowerWin64_INT128_TO_FP is actually used.

I've then also added some testing for win64 SSE. I'm not sure if the other target features are as relevant but I can add those too if helpful.

This was found by running the rust-lang/compiler-builtins tests with a build that includes #204887.


Patch is 22.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/207285.diff

2 Files Affected:

  • (modified) llvm/lib/Target/X86/X86ISelLowering.cpp (+11-11)
  • (modified) llvm/test/CodeGen/X86/fp128-cast.ll (+366)
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 97a406d176a55..b4352527b3cc1 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -21439,32 +21439,32 @@ SDValue X86TargetLowering::LowerUINT_TO_FP(SDValue Op,
   bool IsStrict = Op->isStrictFPOpcode();
   unsigned OpNo = IsStrict ? 1 : 0;
   SDValue Src = Op.getOperand(OpNo);
-  SDLoc dl(Op);
+  SDValue Chain = IsStrict ? Op.getOperand(0) : DAG.getEntryNode();
   auto PtrVT = getPointerTy(DAG.getDataLayout());
   MVT SrcVT = Src.getSimpleValueType();
   MVT DstVT = Op->getSimpleValueType(0);
-  SDValue Chain = IsStrict ? Op.getOperand(0) : DAG.getEntryNode();
-
-  // Bail out when we don't have native conversion instructions.
-  if (DstVT == MVT::f128)
-    return SDValue();
+  SDLoc dl(Op);
 
   if (isBF16orSoftF16(DstVT, Subtarget))
     return promoteXINT_TO_FP(Op, dl, DAG);
   else if (isLegalConversion(SrcVT, DstVT, false, Subtarget))
     return Op;
 
+  if (Subtarget.isTargetWin64() && SrcVT == MVT::i128)
+    return LowerWin64_INT128_TO_FP(Op, DAG);
+
+  if (SDValue Extract = vectorizeExtractedCast(Op, dl, DAG, Subtarget))
+    return Extract;
+
   if (SDValue V = lowerFPToIntToFP(Op, dl, DAG, Subtarget))
     return V;
 
   if (DstVT.isVector())
     return lowerUINT_TO_FP_vec(Op, dl, DAG, Subtarget);
 
-  if (Subtarget.isTargetWin64() && SrcVT == MVT::i128)
-    return LowerWin64_INT128_TO_FP(Op, DAG);
-
-  if (SDValue Extract = vectorizeExtractedCast(Op, dl, DAG, Subtarget))
-    return Extract;
+  // Bail out when we don't have native conversion instructions.
+  if (DstVT == MVT::f128)
+    return SDValue();
 
   if (Subtarget.hasAVX512() && isScalarFPTypeInSSEReg(DstVT) &&
       (SrcVT == MVT::i32 || (SrcVT == MVT::i64 && Subtarget.is64Bit()))) {
diff --git a/llvm/test/CodeGen/X86/fp128-cast.ll b/llvm/test/CodeGen/X86/fp128-cast.ll
index 6d4ec063ccd46..2fef3746729c1 100644
--- a/llvm/test/CodeGen/X86/fp128-cast.ll
+++ b/llvm/test/CodeGen/X86/fp128-cast.ll
@@ -1,6 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc < %s -O2 -mtriple=x86_64-linux-android -mattr=+sse | FileCheck %s --check-prefix=X64-SSE
 ; RUN: llc < %s -O2 -mtriple=x86_64-linux-gnu -mattr=+sse | FileCheck %s --check-prefix=X64-SSE
+; RUN: llc < %s -O2 -mtriple=x86_64-pc-windows-gnu -mattr=+sse | FileCheck %s --check-prefix=WIN64-SSE
 ; RUN: llc < %s -O2 -mtriple=i686-linux-gnu -mattr=+mmx | FileCheck %s --check-prefix=X86
 ; RUN: llc < %s -O2 -mtriple=x86_64-linux-android -mattr=+avx | FileCheck %s --check-prefix=X64-AVX
 ; RUN: llc < %s -O2 -mtriple=x86_64-linux-gnu -mattr=+avx | FileCheck %s --check-prefix=X64-AVX
@@ -31,6 +32,17 @@ define dso_local void @TestFPExtF32_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPExtF32_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movss {{.*#+}} xmm1 = mem[0],zero,zero,zero
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __extendsftf2
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPExtF32_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -78,6 +90,17 @@ define dso_local void @TestFPExtF64_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPExtF64_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movsd {{.*#+}} xmm1 = mem[0],zero
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __extenddftf2
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPExtF64_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -126,6 +149,19 @@ define dso_local void @TestFPExtF80_F128() nounwind {
 ; X64-SSE-NEXT:    addq $24, %rsp
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPExtF80_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $72, %rsp
+; WIN64-SSE-NEXT:    fldt vf80(%rip)
+; WIN64-SSE-NEXT:    fstpt {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    callq __extendxftf2
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $72, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPExtF80_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -174,6 +210,17 @@ define dso_local void @TestFPToSIF128_I16() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToSIF128_I16:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixtfsi
+; WIN64-SSE-NEXT:    movw %ax, vi16(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToSIF128_I16:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -212,6 +259,17 @@ define dso_local void @TestFPToUIF128_I16() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToUIF128_I16:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixtfsi
+; WIN64-SSE-NEXT:    movw %ax, vi16(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToUIF128_I16:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -250,6 +308,17 @@ define dso_local void @TestFPToSIF128_I32() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToSIF128_I32:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixtfsi
+; WIN64-SSE-NEXT:    movl %eax, vi32(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToSIF128_I32:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -288,6 +357,17 @@ define dso_local void @TestFPToUIF128_U32() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToUIF128_U32:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixunstfsi
+; WIN64-SSE-NEXT:    movl %eax, vu32(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToUIF128_U32:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -327,6 +407,18 @@ define dso_local void @TestFPToSIF128_I64() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToSIF128_I64:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixtfsi
+; WIN64-SSE-NEXT:    cltq
+; WIN64-SSE-NEXT:    movq %rax, vi64(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToSIF128_I64:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -370,6 +462,18 @@ define dso_local void @TestFPToUIF128_U64() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToUIF128_U64:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixunstfsi
+; WIN64-SSE-NEXT:    movl %eax, %eax
+; WIN64-SSE-NEXT:    movq %rax, vu64(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToUIF128_U64:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -412,6 +516,17 @@ define dso_local void @TestFPToSIF128_I128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToSIF128_I128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixtfti
+; WIN64-SSE-NEXT:    movaps %xmm0, vi128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToSIF128_I128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -467,6 +582,17 @@ define dso_local void @TestFPToUIF128_U128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPToUIF128_U128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __fixunstfti
+; WIN64-SSE-NEXT:    movaps %xmm0, vu128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPToUIF128_U128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -521,6 +647,17 @@ define dso_local void @TestFPTruncF128_F32() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPTruncF128_F32:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __trunctfsf2
+; WIN64-SSE-NEXT:    movss %xmm0, vf32(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPTruncF128_F32:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -559,6 +696,17 @@ define dso_local void @TestFPTruncF128_F64() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPTruncF128_F64:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __trunctfdf2
+; WIN64-SSE-NEXT:    movsd %xmm0, vf64(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPTruncF128_F64:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -597,6 +745,19 @@ define dso_local void @TestFPTruncF128_F80() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestFPTruncF128_F80:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $72, %rsp
+; WIN64-SSE-NEXT:    movaps vf128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    callq __trunctfxf2
+; WIN64-SSE-NEXT:    fldt {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    fstpt vf80(%rip)
+; WIN64-SSE-NEXT:    addq $72, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestFPTruncF128_F80:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -635,6 +796,17 @@ define dso_local void @TestSIToFPI16_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestSIToFPI16_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movswl vi16(%rip), %edx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __floatsitf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestSIToFPI16_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -683,6 +855,17 @@ define dso_local void @TestSIToFPU16_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestSIToFPU16_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movzwl vi16(%rip), %edx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __floatsitf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestSIToFPU16_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -731,6 +914,17 @@ define dso_local void @TestSIToFPI32_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestSIToFPI32_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movl vi32(%rip), %edx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __floatsitf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestSIToFPI32_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -777,6 +971,17 @@ define dso_local void @TestUIToFPU32_F128() #2 {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestUIToFPU32_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movl vu32(%rip), %edx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __floatunsitf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestUIToFPU32_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -823,6 +1028,17 @@ define dso_local void @TestSIToFPI64_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestSIToFPI64_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movq vi64(%rip), %rdx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __floatditf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestSIToFPI64_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -870,6 +1086,17 @@ define dso_local void @TestUIToFPU64_F128() #2 {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestUIToFPU64_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $56, %rsp
+; WIN64-SSE-NEXT:    movq vu64(%rip), %rdx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    callq __floatunditf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $56, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestUIToFPU64_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -918,6 +1145,19 @@ define dso_local void @TestSIToFPI128_F128() nounwind {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestSIToFPI128_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $72, %rsp
+; WIN64-SSE-NEXT:    movaps vi128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    callq __floattitf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $72, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestSIToFPI128_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -973,6 +1213,19 @@ define dso_local void @TestUIToFPU128_F128() #2 {
 ; X64-SSE-NEXT:    popq %rax
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestUIToFPU128_F128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $72, %rsp
+; WIN64-SSE-NEXT:    movaps vu128(%rip), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    callq __floatuntitf
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, vf128(%rip)
+; WIN64-SSE-NEXT:    addq $72, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestUIToFPU128_F128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -1030,6 +1283,23 @@ define dso_local i32 @TestConst128(fp128 %v) nounwind {
 ; X64-SSE-NEXT:    popq %rcx
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestConst128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $72, %rsp
+; WIN64-SSE-NEXT:    movaps (%rcx), %xmm0
+; WIN64-SSE-NEXT:    movaps {{.*#+}} xmm1 = [1.0E+0]
+; WIN64-SSE-NEXT:    movaps %xmm1, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    callq __gttf2
+; WIN64-SSE-NEXT:    xorl %ecx, %ecx
+; WIN64-SSE-NEXT:    testl %eax, %eax
+; WIN64-SSE-NEXT:    setg %cl
+; WIN64-SSE-NEXT:    movl %ecx, %eax
+; WIN64-SSE-NEXT:    addq $72, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestConst128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -1081,6 +1351,23 @@ define dso_local i32 @TestConst128Zero(fp128 %v) nounwind {
 ; X64-SSE-NEXT:    popq %rcx
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestConst128Zero:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $72, %rsp
+; WIN64-SSE-NEXT:    movaps (%rcx), %xmm0
+; WIN64-SSE-NEXT:    xorps %xmm1, %xmm1
+; WIN64-SSE-NEXT:    movaps %xmm1, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    callq __gttf2
+; WIN64-SSE-NEXT:    xorl %ecx, %ecx
+; WIN64-SSE-NEXT:    testl %eax, %eax
+; WIN64-SSE-NEXT:    setg %cl
+; WIN64-SSE-NEXT:    movl %ecx, %eax
+; WIN64-SSE-NEXT:    addq $72, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestConst128Zero:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    subl $12, %esp
@@ -1148,6 +1435,27 @@ define dso_local i32 @TestBits128(fp128 %ld) nounwind {
 ; X64-SSE-NEXT:    addq $24, %rsp
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LABEL: TestBits128:
+; WIN64-SSE:       # %bb.0: # %entry
+; WIN64-SSE-NEXT:    subq $104, %rsp
+; WIN64-SSE-NEXT:    movaps (%rcx), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %rdx
+; WIN64-SSE-NEXT:    leaq {{[0-9]+}}(%rsp), %r8
+; WIN64-SSE-NEXT:    callq __multf3
+; WIN64-SSE-NEXT:    movaps {{[0-9]+}}(%rsp), %xmm0
+; WIN64-SSE-NEXT:    movaps %xmm0, {{[0-9]+}}(%rsp)
+; WIN64-SSE-NEXT:    movq {{[0-9]+}}(%rsp), %rcx
+; WIN64-SSE-NEXT:    movq %rcx, %rdx
+; WIN64-SSE-NEXT:    shrq $32, %rdx
+; WIN64-SSE-NEXT:    xorl %eax, %eax
+; WIN64-SSE-NEXT:    orl %ecx, %edx
+; WIN64-SSE-NEXT:    sete %al
+; WIN64-SSE-NEXT:    addq $104, %rsp
+; WIN64-SSE-NEXT:    retq
+;
 ; X86-LABEL: TestBits128:
 ; X86:       # %bb.0: # %entry
 ; X86-NEXT:    pushl %esi
@@ -1225,6 +1533,15 @@ define fp128 @TestPair128(i64 %a, i64 %b) nounwind {
 ; X64-SSE-NEXT:    movaps -{{[0-9]+}}(%rsp), %xmm0
 ; X64-SSE-NEXT:    retq
 ;
+; WIN64-SSE-LA...
[truncated]

@rnk rnk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@folkertdev
folkertdev merged commit 57a7e75 into llvm:main Jul 7, 2026
15 checks passed
aobolensk pushed a commit to aobolensk/llvm-project that referenced this pull request Jul 8, 2026
…version (llvm#207285)

This change fixes an ABI issue where the signed conversion passes a
`i128` different from a `u128`, and crucially different from GCC.

https://godbolt.org/z/x3daY3rej

```diff
--- <unnamed>
+++ <unnamed>
@@ -1,15 +1,15 @@
-conv_from_i128:
+conv_from_u128:
         push    rsi
-        sub     rsp, 64
+        sub     rsp, 48
         mov     rsi, rcx
-        movaps  xmm0, xmmword ptr [rdx]
-        movaps  xmmword ptr [rsp + 48], xmm0
+        mov     rax, qword ptr [rdx]
+        mov     r8, qword ptr [rdx + 8]
         lea     rcx, [rsp + 32]
-        lea     rdx, [rsp + 48]
-        call    __floattitf
+        mov     rdx, rax
+        call    __floatuntitf
         movaps  xmm0, xmmword ptr [rsp + 32]
         movaps  xmmword ptr [rsi], xmm0
         mov     rax, rsi
-        add     rsp, 64
+        add     rsp, 48
         pop     rsi
         ret
```

The signed case being correct is apparently due to a special
windows-specific path being chosen. The unsigned version bailed out too
early. So, I've changed `LowerUINT_TO_FP` to be more like
`LowerSINT_TO_FP`. In particular this moves the "Bail out when we don't
have native conversion instructions." down a bit, so that
`LowerWin64_INT128_TO_FP` is actually used.

I've then also added some testing for win64 SSE. I'm not sure if the
other target features are as relevant but I can add those too if
helpful.

This was found by running the `rust-lang/compiler-builtins` tests with a
build that includes llvm#204887.
gandhi56 pushed a commit that referenced this pull request Jul 9, 2026
…version (#207285)

This change fixes an ABI issue where the signed conversion passes a
`i128` different from a `u128`, and crucially different from GCC.

https://godbolt.org/z/x3daY3rej

```diff
--- <unnamed>
+++ <unnamed>
@@ -1,15 +1,15 @@
-conv_from_i128:
+conv_from_u128:
         push    rsi
-        sub     rsp, 64
+        sub     rsp, 48
         mov     rsi, rcx
-        movaps  xmm0, xmmword ptr [rdx]
-        movaps  xmmword ptr [rsp + 48], xmm0
+        mov     rax, qword ptr [rdx]
+        mov     r8, qword ptr [rdx + 8]
         lea     rcx, [rsp + 32]
-        lea     rdx, [rsp + 48]
-        call    __floattitf
+        mov     rdx, rax
+        call    __floatuntitf
         movaps  xmm0, xmmword ptr [rsp + 32]
         movaps  xmmword ptr [rsi], xmm0
         mov     rax, rsi
-        add     rsp, 64
+        add     rsp, 48
         pop     rsi
         ret
```

The signed case being correct is apparently due to a special
windows-specific path being chosen. The unsigned version bailed out too
early. So, I've changed `LowerUINT_TO_FP` to be more like
`LowerSINT_TO_FP`. In particular this moves the "Bail out when we don't
have native conversion instructions." down a bit, so that
`LowerWin64_INT128_TO_FP` is actually used.

I've then also added some testing for win64 SSE. I'm not sure if the
other target features are as relevant but I can add those too if
helpful.

This was found by running the `rust-lang/compiler-builtins` tests with a
build that includes #204887.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants