https://gcc.gnu.org/g:2a397b762827221ea5ed99d73062ae93846fa213

commit r17-1496-g2a397b762827221ea5ed99d73062ae93846fa213
Author: Georg-Johann Lay <[email protected]>
Date:   Thu Jun 11 19:35:09 2026 +0200

    AVR: target/125752 - Add 64-bit fixed point <-> 64-bit double conversions.
    
            PR target/125752
    libgcc/config/avr/libf7/
            * libf7-asm.sx (ufx64_to_d, d_to_fx64): New DEFUNs.
            (__fractdadf, __fractudadf, __fracttadf, __fractutadf)
            (__fractdqdf, __fractudqdf): New _DEFUNs.
            * libf7-common.mk (F7_ASM_PARTS) Add: fx2D, D2fx,
            dq2D, udq2D, da2D, uda2D, ta2D uta2D.
            (F7F): Add d_to_fx64, ufx64_to_d, sfx64_to_d.
            * libf7.c (d_to_ufx, d_to_sfx): New protos.
            (__fractdfda, __fractdfuda, __fractdfta, __fractdfuta)
            (__fractdfdq, __fractdfudq): New modules.
            * t-libf7 (LIBF7_DF_CONV): Add fractdfda,
            fractdfta, fractdfdq, fractdfuda, fractdfuta, fractdfudq.
            * f7-renames.h: Rebuild.
    
    gcc/testsuite/
            * gcc.target/avr/torture/fx-to-double.c: New test.

Diff:
---
 .../gcc.target/avr/torture/fx-to-double.c          |  88 +++++++++++++++
 libgcc/config/avr/libf7/f7-renames.h               |   3 +
 libgcc/config/avr/libf7/libf7-asm.sx               | 122 ++++++++++++++++++++-
 libgcc/config/avr/libf7/libf7-common.mk            |   8 +-
 libgcc/config/avr/libf7/libf7.c                    |  52 +++++++++
 libgcc/config/avr/libf7/t-libf7                    |   2 +
 6 files changed, 271 insertions(+), 4 deletions(-)

diff --git a/gcc/testsuite/gcc.target/avr/torture/fx-to-double.c 
b/gcc/testsuite/gcc.target/avr/torture/fx-to-double.c
new file mode 100644
index 000000000000..761a57a8d8a8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/torture/fx-to-double.c
@@ -0,0 +1,88 @@
+/* { dg-do run { target { ! avr_tiny } } } */
+/* { dg-additional-options "-std=gnu99 -Wno-pedantic" } */
+
+typedef long double DD;
+
+typedef long long _Fract dq_t;
+typedef long long _Accum ta_t;
+typedef long _Accum da_t;
+typedef unsigned long long _Fract udq_t;
+typedef unsigned long long _Accum uta_t;
+typedef unsigned long _Accum uda_t;
+
+#define NI __attribute__((noipa))
+
+#define MK_CONV(T)                             \
+  NI DD T##2d (T##_t x)                                \
+  {                                            \
+    __asm ("" : "+r" (x));                     \
+    return (DD) x;                             \
+  }                                            \
+                                               \
+  NI T##_t d2##T (DD x)                                \
+  {                                            \
+    __asm ("" : "+r" (x));                     \
+    return (T##_t) x;                          \
+  }
+
+
+MK_CONV (da)
+MK_CONV (ta)
+MK_CONV (dq)
+
+MK_CONV (uda)
+MK_CONV (uta)
+MK_CONV (udq)
+
+#define TEST(T)                                        \
+  do {                                         \
+    T##_t fx = d2##T (x);                      \
+    DD y = T##2d (fx);                         \
+    if (y != x)                                        \
+      __builtin_exit (__LINE__);               \
+  } while (0)
+
+
+NI void test_s (DD x)
+{
+  TEST (da);
+  TEST (ta);
+
+  if (x < 1.0L && x >= -1.0L)
+    TEST (dq);
+}
+
+
+NI void test_u (long double x)
+{
+  TEST (uda);
+  TEST (uta);
+
+  if (x < 1.0L)
+    TEST (udq);
+}
+
+void test (DD x)
+{
+  if (x >= 0)
+    test_u (x);
+
+  test_s (x);
+}
+
+int main (void)
+{
+  test (+0.0L);
+  test (-0.0L);
+
+  test (+1.0L);
+  test (-1.0L);
+
+  test (-0x0.cafe123p0);
+  test (+0x0.cafe123p0);
+
+  test (+0x1234.cafe123p0);
+  test (-0x1234.cafe123p0);
+
+  return 0;
+}
diff --git a/libgcc/config/avr/libf7/f7-renames.h 
b/libgcc/config/avr/libf7/f7-renames.h
index dc098517ada4..c9f654a032c8 100644
--- a/libgcc/config/avr/libf7/f7-renames.h
+++ b/libgcc/config/avr/libf7/f7-renames.h
@@ -222,6 +222,9 @@
 #define f7_sqrt16_floor_asm __f7_sqrt16_floor_asm
 #define f7_lshrdi3_asm __f7_lshrdi3_asm
 #define f7_ashldi3_asm __f7_ashldi3_asm
+#define f7_d_to_fx64_asm __f7_d_to_fx64_asm
+#define f7_ufx64_to_d_asm __f7_ufx64_to_d_asm
+#define f7_sfx64_to_d_asm __f7_sfx64_to_d_asm
 #define f7_class_D_asm __f7_class_D_asm
 #define f7_call_ddd_asm __f7_call_ddd_asm
 #define f7_call_xdd_asm __f7_call_xdd_asm
diff --git a/libgcc/config/avr/libf7/libf7-asm.sx 
b/libgcc/config/avr/libf7/libf7-asm.sx
index dffa87b176db..2644845d5288 100644
--- a/libgcc/config/avr/libf7/libf7-asm.sx
+++ b/libgcc/config/avr/libf7/libf7-asm.sx
@@ -2308,8 +2308,85 @@ _ENDF __fractqqdf
 #endif /* F7MOD_qq2D_ */
 
 
+#ifdef F7MOD_fx2D_
+;;; Convert an [un]signed 64-bit fixed point value R18[] to double.
+;;;    double ufx64_to_d (ufx64_t R18, uint8_t fbit R30);
+;;;    double sfx64_to_d (sfx64_t R18, uint8_t fbit R30);
+;;; FBIT accounts for the fractional bits of the fixed point type,
+;;; e.g. pass __DA_FBIT__ for long accum etc.
+DEFUN ufx64_to_d
+    push    r30
+    XCALL __floatundidf
+    rjmp  1f
+
+LABEL sfx64_to_d
+    push    r30
+    XCALL __floatdidf
+1:  pop     r30
+
+    ;; The MSB indicates a value of 0.  Notice that at this point,
+    ;; we have a normal floating point number with a biased exponent
+    ;; that won't underflow below.
+    tst     r25
+    breq 0f
+
+    ;; Subtract fbit from the double exponent.  R30[] = fbit << 4.
+    swap    r30
+    mov     r31, r30
+    andi    r30, lo8 (0x0ff0)
+    andi    r31, hi8 (0x0ff0)
+    sub     r24, r30
+    sbc     r25, r31
+
+0:  ret
+ENDF ufx64_to_d
+#endif /* F7MOD_fx2D_ */
+
+#ifdef F7MOD_da2D_
+_DEFUN __fractdadf
+    ldi     r30, __DA_FBIT__
+    F7jmp   sfx64_to_d
+_ENDF __fractdadf
+#endif /* F7MOD_da2D_ */
+
+#ifdef F7MOD_uda2D_
+_DEFUN __fractudadf
+    ldi     r30, __UDA_FBIT__
+    F7jmp   ufx64_to_d
+_ENDF __fractudadf
+#endif /* F7MOD_uda2D_ */
+
+#ifdef F7MOD_ta2D_
+_DEFUN __fracttadf
+    ldi     r30, __TA_FBIT__
+    F7jmp   sfx64_to_d
+_ENDF __fracttadf
+#endif /* F7MOD_ta2D_ */
+
+#ifdef F7MOD_uta2D_
+_DEFUN __fractutadf
+    ldi     r30, __UTA_FBIT__
+    F7jmp   ufx64_to_d
+_ENDF __fractutadf
+#endif /* F7MOD_uta2D_ */
+
+#ifdef F7MOD_dq2D_
+_DEFUN __fractdqdf
+    ldi     r30, __DQ_FBIT__
+    F7jmp   sfx64_to_d
+_ENDF __fractdqdf
+#endif /* F7MOD_dq2D_ */
+
+#ifdef F7MOD_udq2D_
+_DEFUN __fractudqdf
+    ldi     r30, __UDQ_FBIT__
+    F7jmp   ufx64_to_d
+_ENDF __fractudqdf
+#endif /* F7MOD_udq2D_ */
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Fixed-point -> double conversions.
+;;; Double -> fixed-point conversions.
 
 #ifdef F7MOD_D2qq_
 _DEFUN __fractdfqq
@@ -2389,6 +2466,49 @@ _ENDF __fractdfusa
 #endif /* F7MOD_D2usa_ */
 
 
+#ifdef F7MOD_D2fx_
+;;; Convert double R18 to a 64-bit fixed point value.
+;;; R18 d_to_fx64 (double R18, int8_t fbit R16);
+;;; |FBIT| accounts for the extra fbits compared to [u]int64_t.
+;;; FBIT < 0 indicates a signed conversion.
+;;; FBIT > 0 indicates an unsigned conversion.
+DEFUN d_to_fx64
+    do_prologue_saves  4, F7_SIZEOF
+
+    ;; Y = FramePointer + 1
+    adiw    Y,      1
+
+    ;; FP + 1 = (f7_t) arg
+    wmov    r16,    Y
+
+    ;; Convert double R18 to f7_t *R16.
+    XCALL   F7_NAME (set_double_impl)
+
+    ;; Prepare for *R24 = ldexp (*R24, R22).  ldexp() accounts for the
+    ;; fractional bits of the fixed point value, i.e. we have to <<= FBIT.
+    wmov    r24,    r16
+    ;; Zero-extend FBIT.
+    clr     r23
+    ;; R16 has been clobbered.  Fetch it from where prologue_saves put it.
+    ldd     r22,    Y + F7_SIZEOF + 3       ; Saved R16
+    tst     r22
+    brmi 1f
+
+    ;; Positive FBIT indicates an unsigned conversion.
+    XCALL   F7_NAME (Ildexp)
+    XCALL   F7_NAME (get_u64)
+    rjmp 9f
+
+1:  ;; Negative FBIT indicates a signed conversion.
+    neg     r22                 ; FBIT := |FBIT|
+    XCALL   F7_NAME (Ildexp)
+    XCALL   F7_NAME (get_s64)
+
+9:  do_epilogue_restores  4, F7_SIZEOF
+ENDF d_to_fx64
+#endif /* F7MOD_D2fx_ */
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; [u]int32_t -> double conversions.
 
diff --git a/libgcc/config/avr/libf7/libf7-common.mk 
b/libgcc/config/avr/libf7/libf7-common.mk
index e2b47409ba80..4c3091f4f437 100644
--- a/libgcc/config/avr/libf7/libf7-common.mk
+++ b/libgcc/config/avr/libf7/libf7-common.mk
@@ -29,12 +29,14 @@ F7_ASM_PARTS += D_cmp D_eq D_ne D_ge D_gt D_le D_lt D_unord 
D_fminfmax
 F7_ASM_PARTS += call_dd call_ddd
 
 # Fixed-point -> double conversions
-F7_ASM_PARTS += qq2D uqq2D            sq2D usq2D
-F7_ASM_PARTS +=            ha2D uha2D sa2D usa2D
+F7_ASM_PARTS += qq2D uqq2D            sq2D usq2D dq2D udq2D
+F7_ASM_PARTS +=            ha2D uha2D sa2D usa2D da2D uda2D ta2D uta2D
+F7_ASM_PARTS += fx2D
 
 # Double -> fixed-point conversions
 F7_ASM_PARTS += D2qq D2uqq D2hq D2uhq
 F7_ASM_PARTS +=            D2ha D2uha D2sa D2usa
+F7_ASM_PARTS += D2fx
 
 # Integer -> double conversions
 F7_ASM_PARTS += D_floatsidf D_floatunsidf
@@ -107,7 +109,7 @@ F7F_asm += set_u64 set_s64 addsub_mant_scaled mul_mant
 F7F_asm += to_integer to_unsigned clr_mant_lsbs
 F7F_asm += div sqrt_approx sqrt16_round sqrt16_floor
 F7F_asm += lshrdi3 ashldi3
-
+F7F_asm += d_to_fx64 ufx64_to_d sfx64_to_d
 F7F_asm += class_D
 
 F7F_asm += call_ddd call_xdd call_ddx
diff --git a/libgcc/config/avr/libf7/libf7.c b/libgcc/config/avr/libf7/libf7.c
index ec4f202455f0..c298b4dc479a 100644
--- a/libgcc/config/avr/libf7/libf7.c
+++ b/libgcc/config/avr/libf7/libf7.c
@@ -140,6 +140,58 @@ f7_double_t __floatdidf (int64_t x)
 #endif // F7MOD_floatdidf_
 
 
+extern uint64_t d_to_ufx (f7_double_t, int8_t) F7ASM(f7_d_to_fx64_asm);
+extern int64_t  d_to_sfx (f7_double_t, int8_t) F7ASM(f7_d_to_fx64_asm);
+
+#ifdef F7MOD_fractdfda_
+F7_WEAK
+int64_t __fractdfda (f7_double_t d)
+{
+  return d_to_sfx (d, -__DA_FBIT__);
+}
+#endif // F7MOD_fractdfda_
+
+#ifdef F7MOD_fractdfuda_
+F7_WEAK
+uint64_t __fractdfuda (f7_double_t d)
+{
+  return d_to_ufx (d, __UDA_FBIT__);
+}
+#endif // F7MOD_fractdfuda_
+
+#ifdef F7MOD_fractdfta_
+F7_WEAK
+int64_t __fractdfta (f7_double_t d)
+{
+  return d_to_sfx (d, -__TA_FBIT__);
+}
+#endif // F7MOD_fractdfta_
+
+#ifdef F7MOD_fractdfuta_
+F7_WEAK
+uint64_t __fractdfuta (f7_double_t d)
+{
+  return d_to_ufx (d, __UTA_FBIT__);
+}
+#endif // F7MOD_fractdfuta_
+
+#ifdef F7MOD_fractdfdq_
+F7_WEAK
+int64_t __fractdfdq (f7_double_t d)
+{
+  return d_to_sfx (d, -__DQ_FBIT__);
+}
+#endif // F7MOD_fractdfdq_
+
+#ifdef F7MOD_fractdfudq_
+F7_WEAK
+uint64_t __fractdfudq (f7_double_t d)
+{
+  return d_to_ufx (d, __UDQ_FBIT__);
+}
+#endif // F7MOD_fractdfudq_
+
+
 #ifdef F7MOD_init_
 f7_t* f7_init_impl (uint64_t mant, uint8_t flags, f7_t *cc, int16_t expo)
 {
diff --git a/libgcc/config/avr/libf7/t-libf7 b/libgcc/config/avr/libf7/t-libf7
index 9ec70d84ae88..d087053526ed 100644
--- a/libgcc/config/avr/libf7/t-libf7
+++ b/libgcc/config/avr/libf7/t-libf7
@@ -9,6 +9,8 @@ F7_PREFIX = __f7_
 include $(libf7)/libf7-common.mk
 
 LIBF7_DF_CONV +=  floatundidf floatdidf # floatunsidf floatsidf
+LIBF7_DF_CONV +=  fractdfda fractdfta fractdfdq
+LIBF7_DF_CONV +=  fractdfuda fractdfuta fractdfudq
 
 # Wrappers like f7_lt_impl for f7_lt etc. because the latter is inline.
 LIBF7_DF_CMP  += lt le gt ge ne eq unord

Reply via email to