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
