[llvm] [X86] Use round-to-odd for f64->f16 with F16C (PR #230079)

Aochang Liu via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 11 22:28:24 PDT 2026


================
@@ -62,6 +112,164 @@ entry:
   ret i16 %0
 }
 
+define half @fptrunc_afn(double %d) #0 {
+; F16C-LABEL: fptrunc_afn:
+; F16C:       # %bb.0:
+; F16C-NEXT:    vcvtsd2ss %xmm0, %xmm0, %xmm0
+; F16C-NEXT:    vcvtps2ph $4, %xmm0, %xmm0
+; F16C-NEXT:    retq
+;
+; AVX-LABEL: fptrunc_afn:
+; AVX:       # %bb.0:
+; AVX-NEXT:    pushq %rax
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    popq %rax
+; AVX-NEXT:    retq
+  %r = fptrunc afn double %d to half
+  ret half %r
+}
+
+define half @fptrunc_afn_twostep(double %d) #0 {
+; F16C-LABEL: fptrunc_afn_twostep:
+; F16C:       # %bb.0:
+; F16C-NEXT:    vcvtsd2ss %xmm0, %xmm0, %xmm0
+; F16C-NEXT:    vcvtps2ph $4, %xmm0, %xmm0
+; F16C-NEXT:    retq
+;
+; AVX-LABEL: fptrunc_afn_twostep:
+; AVX:       # %bb.0:
+; AVX-NEXT:    pushq %rax
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    popq %rax
+; AVX-NEXT:    retq
+  %f = fptrunc contract afn double %d to float
+  %r = fptrunc contract afn float %f to half
+  ret half %r
+}
+
+define <4 x half> @fptrunc_afn_v4f64(<4 x double> %d) #0 {
+; F16C-LABEL: fptrunc_afn_v4f64:
+; F16C:       # %bb.0:
+; F16C-NEXT:    vextractf128 $1, %ymm0, %xmm1
+; F16C-NEXT:    vcvtsd2ss %xmm1, %xmm1, %xmm2
+; F16C-NEXT:    vcvtps2ph $4, %xmm2, %xmm2
+; F16C-NEXT:    vshufpd {{.*#+}} xmm1 = xmm1[1,0]
+; F16C-NEXT:    vcvtsd2ss %xmm1, %xmm1, %xmm1
+; F16C-NEXT:    vcvtps2ph $4, %xmm1, %xmm1
+; F16C-NEXT:    vpunpcklwd {{.*#+}} xmm1 = xmm2[0],xmm1[0],xmm2[1],xmm1[1],xmm2[2],xmm1[2],xmm2[3],xmm1[3]
+; F16C-NEXT:    vcvtsd2ss %xmm0, %xmm0, %xmm2
+; F16C-NEXT:    vcvtps2ph $4, %xmm2, %xmm2
+; F16C-NEXT:    vshufpd {{.*#+}} xmm0 = xmm0[1,0]
+; F16C-NEXT:    vcvtsd2ss %xmm0, %xmm0, %xmm0
+; F16C-NEXT:    vcvtps2ph $4, %xmm0, %xmm0
+; F16C-NEXT:    vpunpcklwd {{.*#+}} xmm0 = xmm2[0],xmm0[0],xmm2[1],xmm0[1],xmm2[2],xmm0[2],xmm2[3],xmm0[3]
+; F16C-NEXT:    vinsertps {{.*#+}} xmm0 = xmm0[0],xmm1[0],zero,zero
+; F16C-NEXT:    vzeroupper
+; F16C-NEXT:    retq
+;
+; AVX-LABEL: fptrunc_afn_v4f64:
+; AVX:       # %bb.0:
+; AVX-NEXT:    subq $88, %rsp
+; AVX-NEXT:    vmovupd %ymm0, (%rsp) # 32-byte Spill
+; AVX-NEXT:    vshufpd {{.*#+}} xmm0 = xmm0[1,0]
+; AVX-NEXT:    vzeroupper
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    vmovapd %xmm0, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; AVX-NEXT:    vmovups (%rsp), %ymm0 # 32-byte Reload
+; AVX-NEXT:    # kill: def $xmm0 killed $xmm0 killed $ymm0
+; AVX-NEXT:    vzeroupper
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    vmovaps %xmm0, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; AVX-NEXT:    vmovups (%rsp), %ymm0 # 32-byte Reload
+; AVX-NEXT:    vextractf128 $1, %ymm0, %xmm0
+; AVX-NEXT:    vmovapd %xmm0, (%rsp) # 16-byte Spill
+; AVX-NEXT:    vshufpd {{.*#+}} xmm0 = xmm0[1,0]
+; AVX-NEXT:    vzeroupper
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    vmovapd %xmm0, {{[-0-9]+}}(%r{{[sb]}}p) # 16-byte Spill
+; AVX-NEXT:    vmovdqa (%rsp), %xmm0 # 16-byte Reload
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    vpunpcklwd {{[-0-9]+}}(%r{{[sb]}}p), %xmm0, %xmm0 # 16-byte Folded Reload
+; AVX-NEXT:    # xmm0 = xmm0[0],mem[0],xmm0[1],mem[1],xmm0[2],mem[2],xmm0[3],mem[3]
+; AVX-NEXT:    vmovdqa {{[-0-9]+}}(%r{{[sb]}}p), %xmm1 # 16-byte Reload
+; AVX-NEXT:    vpunpcklwd {{[-0-9]+}}(%r{{[sb]}}p), %xmm1, %xmm1 # 16-byte Folded Reload
+; AVX-NEXT:    # xmm1 = xmm1[0],mem[0],xmm1[1],mem[1],xmm1[2],mem[2],xmm1[3],mem[3]
+; AVX-NEXT:    vinsertps {{.*#+}} xmm0 = xmm1[0],xmm0[0],zero,zero
+; AVX-NEXT:    addq $88, %rsp
+; AVX-NEXT:    retq
+  %r = fptrunc afn <4 x double> %d to <4 x half>
+  ret <4 x half> %r
+}
+
+define half @fptrunc_contract(double %d) #0 {
+; F16C-LABEL: fptrunc_contract:
+; F16C:       # %bb.0:
+; F16C-NEXT:    vcvtsd2ss %xmm0, %xmm0, %xmm1
+; F16C-NEXT:    vmovd %xmm1, %eax
+; F16C-NEXT:    leal 1(%rax), %ecx
+; F16C-NEXT:    leal -1(%rax), %edx
+; F16C-NEXT:    vmovddup {{.*#+}} xmm2 = [NaN,NaN]
+; F16C-NEXT:    # xmm2 = mem[0,0]
+; F16C-NEXT:    vandps %xmm2, %xmm0, %xmm3
+; F16C-NEXT:    vcvtss2sd %xmm1, %xmm1, %xmm1
+; F16C-NEXT:    vandps %xmm2, %xmm1, %xmm2
+; F16C-NEXT:    vucomisd %xmm2, %xmm3
+; F16C-NEXT:    cmoval %ecx, %edx
+; F16C-NEXT:    vucomisd %xmm1, %xmm0
+; F16C-NEXT:    sete %cl
+; F16C-NEXT:    testb $1, %al
+; F16C-NEXT:    setne %sil
+; F16C-NEXT:    orb %cl, %sil
+; F16C-NEXT:    cmovnel %eax, %edx
+; F16C-NEXT:    vmovd %edx, %xmm0
+; F16C-NEXT:    vcvtps2ph $4, %xmm0, %xmm0
+; F16C-NEXT:    retq
+;
+; AVX-LABEL: fptrunc_contract:
+; AVX:       # %bb.0:
+; AVX-NEXT:    pushq %rax
+; AVX-NEXT:    callq __truncdfhf2 at PLT
+; AVX-NEXT:    popq %rax
+; AVX-NEXT:    retq
+  %r = fptrunc contract double %d to half
+  ret half %r
+}
+
+define half @fptrunc_contract_twostep(double %d) #0 {
+; F16C-LABEL: fptrunc_contract_twostep:
+; F16C:       # %bb.0:
+; F16C-NEXT:    vcvtsd2ss %xmm0, %xmm0, %xmm1
+; F16C-NEXT:    vmovd %xmm1, %eax
+; F16C-NEXT:    leal 1(%rax), %ecx
+; F16C-NEXT:    leal -1(%rax), %edx
+; F16C-NEXT:    vmovddup {{.*#+}} xmm2 = [NaN,NaN]
+; F16C-NEXT:    # xmm2 = mem[0,0]
+; F16C-NEXT:    vandps %xmm2, %xmm0, %xmm3
+; F16C-NEXT:    vcvtss2sd %xmm1, %xmm1, %xmm1
+; F16C-NEXT:    vandps %xmm2, %xmm1, %xmm2
+; F16C-NEXT:    vucomisd %xmm2, %xmm3
+; F16C-NEXT:    cmoval %ecx, %edx
+; F16C-NEXT:    vucomisd %xmm1, %xmm0
+; F16C-NEXT:    sete %cl
+; F16C-NEXT:    testb $1, %al
+; F16C-NEXT:    setne %sil
+; F16C-NEXT:    orb %cl, %sil
+; F16C-NEXT:    cmovnel %eax, %edx
+; F16C-NEXT:    vmovd %edx, %xmm0
+; F16C-NEXT:    vcvtps2ph $4, %xmm0, %xmm0
+; F16C-NEXT:    retq
----------------
HelloWorldU wrote:

Yes, it's kind of like putting the cart before the horse. Since this change affects every scalar f64->f16, let me measure it against the libcall first.

https://github.com/llvm/llvm-project/pull/230079


More information about the llvm-commits mailing list