Skip to content

[SPARC] Change half to use soft promotion rather than PromoteFloat #152727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

tgross35
Copy link
Contributor

@tgross35 tgross35 commented Aug 8, 2025

half currently uses the default legalization of promoting to a f32; however, this implementation implements math in a way that results in incorrect rounding. Switch to the soft promote implementation, which does not have this problem.

The SPARC ABI does not specify a _Float16 type, so there is no concern with keeping interface compatibility.

Fixes the SPARC part of #97975
Fixes the SPARC part of #97981

@llvmbot
Copy link
Member

llvmbot commented Aug 8, 2025

@llvm/pr-subscribers-backend-sparc

Author: Trevor Gross (tgross35)

Changes

half currently uses the default legalization of promoting to a f32; however, this implementation implements math in a way that results in incorrect rounding. Switch to the soft promote implementation, which does not have this problem.

The SPARC ABI does not specify a _Float16 type, so there is no concern with keeping interface compatibility.

Fixes the SPARC portion of 1.


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

4 Files Affected:

  • (modified) llvm/lib/Target/Sparc/SparcISelLowering.h (+2)
  • (modified) llvm/test/CodeGen/SPARC/fp16-promote.ll (+36-28)
  • (added) llvm/test/CodeGen/SPARC/half.ll (+805)
  • (modified) llvm/test/CodeGen/SPARC/llvm.sincos.ll (+187-148)
diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.h b/llvm/lib/Target/Sparc/SparcISelLowering.h
index 0d220f8c3d32e..3a6aaf929707d 100644
--- a/llvm/lib/Target/Sparc/SparcISelLowering.h
+++ b/llvm/lib/Target/Sparc/SparcISelLowering.h
@@ -28,6 +28,8 @@ namespace llvm {
 
     bool useSoftFloat() const override;
 
+    bool softPromoteHalfType() const override { return true; }
+
     /// computeKnownBitsForTargetNode - Determine which of the bits specified
     /// in Mask are known to be either zero or one and return them in the
     /// KnownZero/KnownOne bitsets.
diff --git a/llvm/test/CodeGen/SPARC/fp16-promote.ll b/llvm/test/CodeGen/SPARC/fp16-promote.ll
index efe67b04e8fb3..64873b744de50 100644
--- a/llvm/test/CodeGen/SPARC/fp16-promote.ll
+++ b/llvm/test/CodeGen/SPARC/fp16-promote.ll
@@ -329,13 +329,14 @@ define void @test_fadd(ptr %p, ptr %q) nounwind {
 ; V8-OPT-LABEL: test_fadd:
 ; V8-OPT:       ! %bb.0:
 ; V8-OPT-NEXT:    save %sp, -104, %sp
+; V8-OPT-NEXT:    lduh [%i0], %i2
 ; V8-OPT-NEXT:    call __extendhfsf2
-; V8-OPT-NEXT:    lduh [%i0], %o0
+; V8-OPT-NEXT:    lduh [%i1], %o0
 ; V8-OPT-NEXT:    st %f0, [%fp+-8] ! 4-byte Folded Spill
 ; V8-OPT-NEXT:    call __extendhfsf2
-; V8-OPT-NEXT:    lduh [%i1], %o0
+; V8-OPT-NEXT:    mov %i2, %o0
 ; V8-OPT-NEXT:    ld [%fp+-8], %f1 ! 4-byte Folded Reload
-; V8-OPT-NEXT:    fadds %f1, %f0, %f0
+; V8-OPT-NEXT:    fadds %f0, %f1, %f0
 ; V8-OPT-NEXT:    st %f0, [%fp+-4]
 ; V8-OPT-NEXT:    call __truncsfhf2
 ; V8-OPT-NEXT:    ld [%fp+-4], %o0
@@ -346,13 +347,14 @@ define void @test_fadd(ptr %p, ptr %q) nounwind {
 ; V8-UNOPT-LABEL: test_fadd:
 ; V8-UNOPT:       ! %bb.0:
 ; V8-UNOPT-NEXT:    save %sp, -104, %sp
-; V8-UNOPT-NEXT:    call __extendhfsf2
-; V8-UNOPT-NEXT:    lduh [%i0], %o0
-; V8-UNOPT-NEXT:    st %f0, [%fp+-8] ! 4-byte Folded Spill
+; V8-UNOPT-NEXT:    lduh [%i0], %i2
+; V8-UNOPT-NEXT:    st %i2, [%fp+-12] ! 4-byte Folded Spill
 ; V8-UNOPT-NEXT:    call __extendhfsf2
 ; V8-UNOPT-NEXT:    lduh [%i1], %o0
-; V8-UNOPT-NEXT:    fmovs %f0, %f1
-; V8-UNOPT-NEXT:    ld [%fp+-8], %f0 ! 4-byte Folded Reload
+; V8-UNOPT-NEXT:    ld [%fp+-12], %o0 ! 4-byte Folded Reload
+; V8-UNOPT-NEXT:    call __extendhfsf2
+; V8-UNOPT-NEXT:    st %f0, [%fp+-8]
+; V8-UNOPT-NEXT:    ld [%fp+-8], %f1 ! 4-byte Folded Reload
 ; V8-UNOPT-NEXT:    fadds %f0, %f1, %f0
 ; V8-UNOPT-NEXT:    st %f0, [%fp+-4]
 ; V8-UNOPT-NEXT:    call __truncsfhf2
@@ -364,13 +366,14 @@ define void @test_fadd(ptr %p, ptr %q) nounwind {
 ; V9-LABEL: test_fadd:
 ; V9:       ! %bb.0:
 ; V9-NEXT:    save %sp, -104, %sp
+; V9-NEXT:    lduh [%i0], %i2
 ; V9-NEXT:    call __extendhfsf2
-; V9-NEXT:    lduh [%i0], %o0
+; V9-NEXT:    lduh [%i1], %o0
 ; V9-NEXT:    st %f0, [%fp+-8] ! 4-byte Folded Spill
 ; V9-NEXT:    call __extendhfsf2
-; V9-NEXT:    lduh [%i1], %o0
+; V9-NEXT:    mov %i2, %o0
 ; V9-NEXT:    ld [%fp+-8], %f1 ! 4-byte Folded Reload
-; V9-NEXT:    fadds %f1, %f0, %f0
+; V9-NEXT:    fadds %f0, %f1, %f0
 ; V9-NEXT:    st %f0, [%fp+-4]
 ; V9-NEXT:    call __truncsfhf2
 ; V9-NEXT:    ld [%fp+-4], %o0
@@ -381,14 +384,15 @@ define void @test_fadd(ptr %p, ptr %q) nounwind {
 ; SPARC64-LABEL: test_fadd:
 ; SPARC64:       ! %bb.0:
 ; SPARC64-NEXT:    save %sp, -192, %sp
+; SPARC64-NEXT:    lduh [%i0], %i2
 ; SPARC64-NEXT:    call __extendhfsf2
-; SPARC64-NEXT:    lduh [%i0], %o0
+; SPARC64-NEXT:    lduh [%i1], %o0
 ; SPARC64-NEXT:    st %f0, [%fp+2043] ! 4-byte Folded Spill
 ; SPARC64-NEXT:    call __extendhfsf2
-; SPARC64-NEXT:    lduh [%i1], %o0
+; SPARC64-NEXT:    mov %i2, %o0
 ; SPARC64-NEXT:    ld [%fp+2043], %f1 ! 4-byte Folded Reload
 ; SPARC64-NEXT:    call __truncsfhf2
-; SPARC64-NEXT:    fadds %f1, %f0, %f1
+; SPARC64-NEXT:    fadds %f0, %f1, %f1
 ; SPARC64-NEXT:    sth %o0, [%i0]
 ; SPARC64-NEXT:    ret
 ; SPARC64-NEXT:    restore
@@ -403,13 +407,14 @@ define void @test_fmul(ptr %p, ptr %q) nounwind {
 ; V8-OPT-LABEL: test_fmul:
 ; V8-OPT:       ! %bb.0:
 ; V8-OPT-NEXT:    save %sp, -104, %sp
+; V8-OPT-NEXT:    lduh [%i0], %i2
 ; V8-OPT-NEXT:    call __extendhfsf2
-; V8-OPT-NEXT:    lduh [%i0], %o0
+; V8-OPT-NEXT:    lduh [%i1], %o0
 ; V8-OPT-NEXT:    st %f0, [%fp+-8] ! 4-byte Folded Spill
 ; V8-OPT-NEXT:    call __extendhfsf2
-; V8-OPT-NEXT:    lduh [%i1], %o0
+; V8-OPT-NEXT:    mov %i2, %o0
 ; V8-OPT-NEXT:    ld [%fp+-8], %f1 ! 4-byte Folded Reload
-; V8-OPT-NEXT:    fmuls %f1, %f0, %f0
+; V8-OPT-NEXT:    fmuls %f0, %f1, %f0
 ; V8-OPT-NEXT:    st %f0, [%fp+-4]
 ; V8-OPT-NEXT:    call __truncsfhf2
 ; V8-OPT-NEXT:    ld [%fp+-4], %o0
@@ -420,13 +425,14 @@ define void @test_fmul(ptr %p, ptr %q) nounwind {
 ; V8-UNOPT-LABEL: test_fmul:
 ; V8-UNOPT:       ! %bb.0:
 ; V8-UNOPT-NEXT:    save %sp, -104, %sp
-; V8-UNOPT-NEXT:    call __extendhfsf2
-; V8-UNOPT-NEXT:    lduh [%i0], %o0
-; V8-UNOPT-NEXT:    st %f0, [%fp+-8] ! 4-byte Folded Spill
+; V8-UNOPT-NEXT:    lduh [%i0], %i2
+; V8-UNOPT-NEXT:    st %i2, [%fp+-12] ! 4-byte Folded Spill
 ; V8-UNOPT-NEXT:    call __extendhfsf2
 ; V8-UNOPT-NEXT:    lduh [%i1], %o0
-; V8-UNOPT-NEXT:    fmovs %f0, %f1
-; V8-UNOPT-NEXT:    ld [%fp+-8], %f0 ! 4-byte Folded Reload
+; V8-UNOPT-NEXT:    ld [%fp+-12], %o0 ! 4-byte Folded Reload
+; V8-UNOPT-NEXT:    call __extendhfsf2
+; V8-UNOPT-NEXT:    st %f0, [%fp+-8]
+; V8-UNOPT-NEXT:    ld [%fp+-8], %f1 ! 4-byte Folded Reload
 ; V8-UNOPT-NEXT:    fmuls %f0, %f1, %f0
 ; V8-UNOPT-NEXT:    st %f0, [%fp+-4]
 ; V8-UNOPT-NEXT:    call __truncsfhf2
@@ -438,13 +444,14 @@ define void @test_fmul(ptr %p, ptr %q) nounwind {
 ; V9-LABEL: test_fmul:
 ; V9:       ! %bb.0:
 ; V9-NEXT:    save %sp, -104, %sp
+; V9-NEXT:    lduh [%i0], %i2
 ; V9-NEXT:    call __extendhfsf2
-; V9-NEXT:    lduh [%i0], %o0
+; V9-NEXT:    lduh [%i1], %o0
 ; V9-NEXT:    st %f0, [%fp+-8] ! 4-byte Folded Spill
 ; V9-NEXT:    call __extendhfsf2
-; V9-NEXT:    lduh [%i1], %o0
+; V9-NEXT:    mov %i2, %o0
 ; V9-NEXT:    ld [%fp+-8], %f1 ! 4-byte Folded Reload
-; V9-NEXT:    fmuls %f1, %f0, %f0
+; V9-NEXT:    fmuls %f0, %f1, %f0
 ; V9-NEXT:    st %f0, [%fp+-4]
 ; V9-NEXT:    call __truncsfhf2
 ; V9-NEXT:    ld [%fp+-4], %o0
@@ -455,14 +462,15 @@ define void @test_fmul(ptr %p, ptr %q) nounwind {
 ; SPARC64-LABEL: test_fmul:
 ; SPARC64:       ! %bb.0:
 ; SPARC64-NEXT:    save %sp, -192, %sp
+; SPARC64-NEXT:    lduh [%i0], %i2
 ; SPARC64-NEXT:    call __extendhfsf2
-; SPARC64-NEXT:    lduh [%i0], %o0
+; SPARC64-NEXT:    lduh [%i1], %o0
 ; SPARC64-NEXT:    st %f0, [%fp+2043] ! 4-byte Folded Spill
 ; SPARC64-NEXT:    call __extendhfsf2
-; SPARC64-NEXT:    lduh [%i1], %o0
+; SPARC64-NEXT:    mov %i2, %o0
 ; SPARC64-NEXT:    ld [%fp+2043], %f1 ! 4-byte Folded Reload
 ; SPARC64-NEXT:    call __truncsfhf2
-; SPARC64-NEXT:    fmuls %f1, %f0, %f1
+; SPARC64-NEXT:    fmuls %f0, %f1, %f1
 ; SPARC64-NEXT:    sth %o0, [%i0]
 ; SPARC64-NEXT:    ret
 ; SPARC64-NEXT:    restore
diff --git a/llvm/test/CodeGen/SPARC/half.ll b/llvm/test/CodeGen/SPARC/half.ll
new file mode 100644
index 0000000000000..5db5176fe39cd
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/half.ll
@@ -0,0 +1,805 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+
+; RUN: llc %s -o - -mtriple=sparc-unknown-linux-gnu   | FileCheck %s --check-prefixes=SPARC32
+; RUN: llc %s -o - -mtriple=sparc64-unknown-linux-gnu | FileCheck %s --check-prefixes=SPARC64
+
+; Tests for various operations on half precison float. Much of the test is
+; copied from test/CodeGen/X86/half.ll.
+
+define void @store(half %x, ptr %p) nounwind {
+; SPARC32-LABEL: store:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    sth %o0, [%o1]
+;
+; SPARC64-LABEL: store:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    sth %o0, [%o1]
+  store half %x, ptr %p
+  ret void
+}
+
+define half @return(ptr %p) nounwind {
+; SPARC32-LABEL: return:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    lduh [%o0], %o0
+;
+; SPARC64-LABEL: return:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    lduh [%o0], %o0
+  %r = load half, ptr %p
+  ret half %r
+}
+
+define dso_local double @loadd(ptr nocapture readonly %a) local_unnamed_addr nounwind {
+; SPARC32-LABEL: loadd:
+; SPARC32:       ! %bb.0: ! %entry
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __extendhfsf2
+; SPARC32-NEXT:    lduh [%i0+2], %o0
+; SPARC32-NEXT:    fstod %f0, %f0
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: loadd:
+; SPARC64:       ! %bb.0: ! %entry
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    call __extendhfsf2
+; SPARC64-NEXT:    lduh [%i0+2], %o0
+; SPARC64-NEXT:    fstod %f0, %f0
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+entry:
+  %arrayidx = getelementptr inbounds i16, ptr %a, i64 1
+  %0 = load i16, ptr %arrayidx, align 2
+  %1 = tail call double @llvm.convert.from.fp16.f64(i16 %0)
+  ret double %1
+}
+
+define dso_local float @loadf(ptr nocapture readonly %a) local_unnamed_addr nounwind {
+; SPARC32-LABEL: loadf:
+; SPARC32:       ! %bb.0: ! %entry
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __extendhfsf2
+; SPARC32-NEXT:    lduh [%i0+2], %o0
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: loadf:
+; SPARC64:       ! %bb.0: ! %entry
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    call __extendhfsf2
+; SPARC64-NEXT:    lduh [%i0+2], %o0
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+entry:
+  %arrayidx = getelementptr inbounds i16, ptr %a, i64 1
+  %0 = load i16, ptr %arrayidx, align 2
+  %1 = tail call float @llvm.convert.from.fp16.f32(i16 %0)
+  ret float %1
+}
+
+define dso_local void @stored(ptr nocapture %a, double %b) local_unnamed_addr nounwind {
+; SPARC32-LABEL: stored:
+; SPARC32:       ! %bb.0: ! %entry
+; SPARC32-NEXT:    save %sp, -112, %sp
+; SPARC32-NEXT:    mov %i2, %i3
+; SPARC32-NEXT:    mov %i1, %i2
+; SPARC32-NEXT:    std %i2, [%fp+-8]
+; SPARC32-NEXT:    ldd [%fp+-8], %f0
+; SPARC32-NEXT:    std %f0, [%fp+-16]
+; SPARC32-NEXT:    call __truncdfhf2
+; SPARC32-NEXT:    ldd [%fp+-16], %o0
+; SPARC32-NEXT:    sth %o0, [%i0]
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: stored:
+; SPARC64:       ! %bb.0: ! %entry
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    fmovd %f2, %f0
+; SPARC64-NEXT:    call __truncdfhf2
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:    sth %o0, [%i0]
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+entry:
+  %0 = tail call i16 @llvm.convert.to.fp16.f64(double %b)
+  store i16 %0, ptr %a, align 2
+  ret void
+}
+
+define dso_local void @storef(ptr nocapture %a, float %b) local_unnamed_addr nounwind {
+; SPARC32-LABEL: storef:
+; SPARC32:       ! %bb.0: ! %entry
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __truncsfhf2
+; SPARC32-NEXT:    mov %i1, %o0
+; SPARC32-NEXT:    sth %o0, [%i0]
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: storef:
+; SPARC64:       ! %bb.0: ! %entry
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    fmovs %f3, %f1
+; SPARC64-NEXT:    call __truncsfhf2
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:    sth %o0, [%i0]
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+entry:
+  %0 = tail call i16 @llvm.convert.to.fp16.f32(float %b)
+  store i16 %0, ptr %a, align 2
+  ret void
+}
+
+define void @test_load_store(ptr %in, ptr %out) nounwind {
+; SPARC32-LABEL: test_load_store:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    lduh [%o0], %o0
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    sth %o0, [%o1]
+;
+; SPARC64-LABEL: test_load_store:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    lduh [%o0], %o0
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    sth %o0, [%o1]
+  %val = load half, ptr %in
+  store half %val, ptr %out
+  ret void
+}
+
+define i16 @test_bitcast_from_half(ptr %addr) nounwind {
+; SPARC32-LABEL: test_bitcast_from_half:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    lduh [%o0], %o0
+;
+; SPARC64-LABEL: test_bitcast_from_half:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    lduh [%o0], %o0
+  %val = load half, ptr %addr
+  %val_int = bitcast half %val to i16
+  ret i16 %val_int
+}
+
+define void @test_bitcast_to_half(ptr %addr, i16 %in) nounwind {
+; SPARC32-LABEL: test_bitcast_to_half:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    sth %o1, [%o0]
+;
+; SPARC64-LABEL: test_bitcast_to_half:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    sth %o1, [%o0]
+  %val_fp = bitcast i16 %in to half
+  store half %val_fp, ptr %addr
+  ret void
+}
+
+define half @from_bits(i16 %x) nounwind {
+; SPARC32-LABEL: from_bits:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    nop
+;
+; SPARC64-LABEL: from_bits:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    nop
+  %res = bitcast i16 %x to half
+  ret half %res
+}
+
+define i16 @to_bits(half %x) nounwind {
+; SPARC32-LABEL: to_bits:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    retl
+; SPARC32-NEXT:    nop
+;
+; SPARC64-LABEL: to_bits:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    retl
+; SPARC64-NEXT:    nop
+    %res = bitcast half %x to i16
+    ret i16 %res
+}
+
+define float @test_extend32(ptr %addr) nounwind {
+; SPARC32-LABEL: test_extend32:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __extendhfsf2
+; SPARC32-NEXT:    lduh [%i0], %o0
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: test_extend32:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    call __extendhfsf2
+; SPARC64-NEXT:    lduh [%i0], %o0
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %val16 = load half, ptr %addr
+  %val32 = fpext half %val16 to float
+  ret float %val32
+}
+
+define double @test_extend64(ptr %addr) nounwind {
+; SPARC32-LABEL: test_extend64:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __extendhfsf2
+; SPARC32-NEXT:    lduh [%i0], %o0
+; SPARC32-NEXT:    fstod %f0, %f0
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: test_extend64:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    call __extendhfsf2
+; SPARC64-NEXT:    lduh [%i0], %o0
+; SPARC64-NEXT:    fstod %f0, %f0
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %val16 = load half, ptr %addr
+  %val32 = fpext half %val16 to double
+  ret double %val32
+}
+
+define void @test_trunc32(float %in, ptr %addr) nounwind {
+; SPARC32-LABEL: test_trunc32:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __truncsfhf2
+; SPARC32-NEXT:    mov %i0, %o0
+; SPARC32-NEXT:    sth %o0, [%i1]
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: test_trunc32:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    call __truncsfhf2
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:    sth %o0, [%i1]
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %val16 = fptrunc float %in to half
+  store half %val16, ptr %addr
+  ret void
+}
+
+define void @test_trunc64(double %in, ptr %addr) nounwind {
+; SPARC32-LABEL: test_trunc64:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -112, %sp
+; SPARC32-NEXT:    ! kill: def $i1 killed $i1 killed $i0_i1 def $i0_i1
+; SPARC32-NEXT:    ! kill: def $i0 killed $i0 killed $i0_i1 def $i0_i1
+; SPARC32-NEXT:    std %i0, [%fp+-8]
+; SPARC32-NEXT:    ldd [%fp+-8], %f0
+; SPARC32-NEXT:    std %f0, [%fp+-16]
+; SPARC32-NEXT:    call __truncdfhf2
+; SPARC32-NEXT:    ldd [%fp+-16], %o0
+; SPARC32-NEXT:    sth %o0, [%i2]
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: test_trunc64:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -176, %sp
+; SPARC64-NEXT:    call __truncdfhf2
+; SPARC64-NEXT:    nop
+; SPARC64-NEXT:    sth %o0, [%i1]
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %val16 = fptrunc double %in to half
+  store half %val16, ptr %addr
+  ret void
+}
+
+define i64 @test_fptosi_i64(ptr %p) nounwind {
+; SPARC32-LABEL: test_fptosi_i64:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __extendhfsf2
+; SPARC32-NEXT:    lduh [%i0], %o0
+; SPARC32-NEXT:    st %f0, [%fp+-4]
+; SPARC32-NEXT:    call __fixsfdi
+; SPARC32-NEXT:    ld [%fp+-4], %o0
+; SPARC32-NEXT:    mov %o0, %i0
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore %g0, %o1, %o1
+;
+; SPARC64-LABEL: test_fptosi_i64:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -192, %sp
+; SPARC64-NEXT:    call __extendhfsf2
+; SPARC64-NEXT:    lduh [%i0], %o0
+; SPARC64-NEXT:    fstox %f0, %f0
+; SPARC64-NEXT:    std %f0, [%fp+2039]
+; SPARC64-NEXT:    ldx [%fp+2039], %i0
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %a = load half, ptr %p, align 2
+  %r = fptosi half %a to i64
+  ret i64 %r
+}
+
+define void @test_sitofp_i64(i64 %a, ptr %p) nounwind {
+; SPARC32-LABEL: test_sitofp_i64:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    mov %i1, %o1
+; SPARC32-NEXT:    call __floatdisf
+; SPARC32-NEXT:    mov %i0, %o0
+; SPARC32-NEXT:    st %f0, [%fp+-4]
+; SPARC32-NEXT:    call __truncsfhf2
+; SPARC32-NEXT:    ld [%fp+-4], %o0
+; SPARC32-NEXT:    sth %o0, [%i2]
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: test_sitofp_i64:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -192, %sp
+; SPARC64-NEXT:    stx %i0, [%fp+2039]
+; SPARC64-NEXT:    ldd [%fp+2039], %f0
+; SPARC64-NEXT:    call __truncsfhf2
+; SPARC64-NEXT:    fxtos %f0, %f1
+; SPARC64-NEXT:    sth %o0, [%i1]
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %r = sitofp i64 %a to half
+  store half %r, ptr %p
+  ret void
+}
+
+define i64 @test_fptoui_i64(ptr %p) nounwind {
+; SPARC32-LABEL: test_fptoui_i64:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    call __extendhfsf2
+; SPARC32-NEXT:    lduh [%i0], %o0
+; SPARC32-NEXT:    st %f0, [%fp+-4]
+; SPARC32-NEXT:    call __fixunssfdi
+; SPARC32-NEXT:    ld [%fp+-4], %o0
+; SPARC32-NEXT:    mov %o0, %i0
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore %g0, %o1, %o1
+;
+; SPARC64-LABEL: test_fptoui_i64:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -192, %sp
+; SPARC64-NEXT:    call __extendhfsf2
+; SPARC64-NEXT:    lduh [%i0], %o0
+; SPARC64-NEXT:    sethi %h44(.LCPI17_0), %i0
+; SPARC64-NEXT:    add %i0, %m44(.LCPI17_0), %i0
+; SPARC64-NEXT:    sllx %i0, 12, %i0
+; SPARC64-NEXT:    ld [%i0+%l44(.LCPI17_0)], %f1
+; SPARC64-NEXT:    fsubs %f0, %f1, %f2
+; SPARC64-NEXT:    fstox %f2, %f2
+; SPARC64-NEXT:    std %f2, [%fp+2031]
+; SPARC64-NEXT:    fstox %f0, %f2
+; SPARC64-NEXT:    std %f2, [%fp+2039]
+; SPARC64-NEXT:    ldx [%fp+2031], %i0
+; SPARC64-NEXT:    sethi 0, %i1
+; SPARC64-NEXT:    or %i1, 0, %i1
+; SPARC64-NEXT:    sethi 2097152, %i2
+; SPARC64-NEXT:    or %i2, 0, %i2
+; SPARC64-NEXT:    sllx %i2, 32, %i2
+; SPARC64-NEXT:    ldx [%fp+2039], %i3
+; SPARC64-NEXT:    or %i2, %i1, %i1
+; SPARC64-NEXT:    xor %i0, %i1, %i0
+; SPARC64-NEXT:    fcmps %fcc0, %f0, %f1
+; SPARC64-NEXT:    movl %fcc0, %i3, %i0
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %a = load half, ptr %p, align 2
+  %r = fptoui half %a to i64
+  ret i64 %r
+}
+
+define void @test_uitofp_i64(i64 %a, ptr %p) nounwind {
+; SPARC32-LABEL: test_uitofp_i64:
+; SPARC32:       ! %bb.0:
+; SPARC32-NEXT:    save %sp, -96, %sp
+; SPARC32-NEXT:    mov %i1, %o1
+; SPARC32-NEXT:    call __floatundisf
+; SPARC32-NEXT:    mov %i0, %o0
+; SPARC32-NEXT:    st %f0, [%fp+-4]
+; SPARC32-NEXT:    call __truncsfhf2
+; SPARC32-NEXT:    ld [%fp+-4], %o0
+; SPARC32-NEXT:    sth %o0, [%i2]
+; SPARC32-NEXT:    ret
+; SPARC32-NEXT:    restore
+;
+; SPARC64-LABEL: test_uitofp_i64:
+; SPARC64:       ! %bb.0:
+; SPARC64-NEXT:    save %sp, -192, %sp
+; SPARC64-NEXT:    stx %i0, [%fp+2031]
+; SPARC64-NEXT:    srlx %i0, 1, %i2
+; SPARC64-NEXT:    and %i0, 1, %i3
+; SPARC64-NEXT:    or %i3, %i2, %i2
+; SPARC64-NEXT:    stx %i2, [%fp+2039]
+; SPARC64-NEXT:    ldd [%fp+2031], %f0
+; SPARC64-NEXT:    ldd [%fp+2039], %f2
+; SPARC64-NEXT:    fxtos %f0, %f1
+; SPARC64-NEXT:    fxtos %f2, %f0
+; SPARC64-NEXT:    fadds %f0, %f0, %f0
+; SPARC64-NEXT:    call __truncsfhf2
+; SPARC64-NEXT:    fmovrslz %i0, %f0, %f1
+; SPARC64-NEXT:    sth %o0, [%i1]
+; SPARC64-NEXT:    ret
+; SPARC64-NEXT:    restore
+  %r = uitofp i...
[truncated]

@tgross35
Copy link
Contributor Author

tgross35 commented Aug 8, 2025

#152723 includes the basline test, which should land first.

@rorth or @koachan, could you review this?

@tgross35 tgross35 changed the title Change half to use soft promotion rather than PromoteFloat [SPARC] Change half to use soft promotion rather than PromoteFloat Aug 8, 2025
@tgross35 tgross35 force-pushed the sparc-soft-promote-half branch 2 times, most recently from 2c77ec7 to da54e4e Compare August 8, 2025 19:19
@tgross35 tgross35 force-pushed the sparc-soft-promote-half branch from da54e4e to 2b94326 Compare August 10, 2025 23:50
`half` currently uses the default legalization of promoting to a `f32`;
however, this implementation implements math in a way that results in
incorrect rounding. Switch to the soft promote implementation, which
does not have this problem.

The SPARC ABI does not specify a `_Float16` type, so there is no concern
with keeping interface compatibility.

Fixes the SPARC portion of [1].

[1]: llvm#97975
@tgross35 tgross35 force-pushed the sparc-soft-promote-half branch from 2b94326 to 86841fa Compare August 12, 2025 07:43
@tgross35
Copy link
Contributor Author

This is ready for a review since #152723 landed @koachan

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.

3 participants