From: Georg-Johann Lay Date: Wed, 22 Jul 2026 10:25:30 +0000 (+0200) Subject: AVR: Tweak lib1funcs-fixed.S::__mulQ64_work. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=65b50ebf4bbdf39388cd83030d9441111663604b;p=thirdparty%2Fgcc.git AVR: Tweak lib1funcs-fixed.S::__mulQ64_work. This patch - Tweaks the signed overflow handling in __mulQ64_work. - Rename misnomed fx24-mul.c to fx64-mul-1.c. - Adds more test cases in fx64-mul-2.c. libgcc/ * config/avr/lib1funcs-fixed.S (__mulQ64_work): Tweak overflow handling. Use __negsi2 (non-saturating negation) to negate. gcc/testsuite/ * gcc.target/avr/fx24-mul.c: Renamed to... * gcc.target/avr/fx64-mul-1.c: ...this. * gcc.target/avr/fx64-mul-2.c: New test. * gcc.target/avr/fx64-mul.h: New file. --- diff --git a/gcc/testsuite/gcc.target/avr/fx24-mul.c b/gcc/testsuite/gcc.target/avr/fx64-mul-1.c similarity index 59% rename from gcc/testsuite/gcc.target/avr/fx24-mul.c rename to gcc/testsuite/gcc.target/avr/fx64-mul-1.c index 6cc0d75b8cb..fb342b3cce7 100644 --- a/gcc/testsuite/gcc.target/avr/fx24-mul.c +++ b/gcc/testsuite/gcc.target/avr/fx64-mul-1.c @@ -3,63 +3,7 @@ // !!! Requires the fx64 <-> float conversions from AVR-LibC. -#include -#include -#include -#include - -#define NI __attribute((noipa)) - -typedef long accum lk_t; -typedef long long accum llk_t; -typedef long long fract llr_t; - -typedef unsigned long accum ulk_t; -typedef unsigned long long accum ullk_t; -typedef unsigned long long fract ullr_t; - -// Values are in fmin <= x < fmax. -#define fmax_llr 1.0f -#define fmin_llr (-fmax_llr) -#define fmax_ullr fmax_llr -#define fmin_ullr 0.0f - -#define fmax_lk 0x1.0p32f -#define fmin_lk (-fmax_lk) -#define fmax_ulk fmax_lk -#define fmin_ulk 0.0f - -#define fmax_llk 0x1.0p16f -#define fmin_llk (-fmax_llk) -#define fmax_ullk fmax_llk -#define fmin_ullk 0.0f - -#define UMAX 0xffffffffffffffff -#define SMAX 0x7fffffffffffffff -#define SMIN 0x8000000000000000 - -// Values are in min <= x <= max. -#define max_lk lkbits (SMAX) -#define min_lk lkbits (SMIN) -#define max_ulk ulkbits (UMAX) -#define min_ulk ulkbits (0) - -#define max_llk llkbits (SMAX) -#define min_llk llkbits (SMIN) -#define max_ullk ullkbits (UMAX) -#define min_ullk ullkbits (0) - -#define max_llr llrbits (SMAX) -#define min_llr llrbits (SMIN) -#define max_ullr ullrbits (UMAX) -#define min_ullr ullrbits (0) - -#define id_lk 10 -#define id_ulk 20 -#define id_llk 30 -#define id_ullk 40 -#define id_llr 50 -#define id_ullr 60 +#include "fx64-mul.h" #define MK_TEST(fx) \ NI bool in_range_##fx (float x) \ @@ -111,8 +55,6 @@ NI void test_mul (float a, float b) test_mul_ullr (a, b); } -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - // Results / arguments must be representable as float, so no rounding occurs. // No-overflow results must be representable as fixed, so no rounding occurs. const PROGMEM float fvals[] = diff --git a/gcc/testsuite/gcc.target/avr/fx64-mul-2.c b/gcc/testsuite/gcc.target/avr/fx64-mul-2.c new file mode 100644 index 00000000000..a679c010928 --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/fx64-mul-2.c @@ -0,0 +1,65 @@ +/* { dg-do run { target { ! avr_tiny } } } */ +/* { dg-additional-options { -std=gnu99 -Os -mcall-prologues } } */ + +// !!! Requires the fx64 <-> float conversions from AVR-LibC. + +#include "fx64-mul.h" + +NI void test_mul (float fa, float fb, uint64_t res) +{ + lk_t a = (lk_t) fa; + lk_t b = (lk_t) fb; + if (a * b != lkbits (res)) + exit (id_lk + 4); +} + +void test (void) +{ + const float e16 = 0x1p-16f; + const float e15 = 0x1p-15f; + + const float p0 = 0x1.0p+0f; + const float p1 = 0x1.0p+1f; + const float p16 = 0x1.0p+16f; + const float p17 = 0x1.0p+17f; + const float p31 = 0x1.0p+31f; + const float p32 = 0x1.0p+32f; + + test_mul (-p16, -p16, SMAX); + test_mul (+p16, +p16, SMAX); + test_mul (+p16, -p16, SMIN); + test_mul (-p16, +p16, SMIN); + + test_mul (-p16, -p17, SMAX); + test_mul (+p16, +p17, SMAX); + test_mul (+p16, -p17, SMIN); + test_mul (-p16, +p17, SMIN); + + test_mul (-p17, -p16, SMAX); + test_mul (+p17, +p16, SMAX); + test_mul (+p17, -p16, SMIN); + test_mul (-p17, +p16, SMIN); + + test_mul (+e16, +e15, 1); + test_mul (-e16, -e15, 1); + test_mul (-e16, +e15, -1ull); + test_mul (+e16, -e15, -1ull); + + test_mul (+e16, +e16, 0); + test_mul (-e16, +e16, 0); + test_mul (-e16, -e16, 0); + + test_mul (-p32, -p0, SMAX); + test_mul (-p32, +p0, SMIN); + + test_mul (-p31, -p1, SMAX); + test_mul (-p31, +p1, SMIN); + test_mul (+p31, +p1, SMAX); + test_mul (+p31, -p1, SMIN); +} + +int main (void) +{ + test (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/avr/fx64-mul.h b/gcc/testsuite/gcc.target/avr/fx64-mul.h new file mode 100644 index 00000000000..5c935d58c1a --- /dev/null +++ b/gcc/testsuite/gcc.target/avr/fx64-mul.h @@ -0,0 +1,60 @@ +// !!! Requires the fx64 <-> float conversions from AVR-LibC. + +#include +#include +#include +#include + +#define NI __attribute((noipa)) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +typedef long accum lk_t; +typedef long long accum llk_t; +typedef long long fract llr_t; + +typedef unsigned long accum ulk_t; +typedef unsigned long long accum ullk_t; +typedef unsigned long long fract ullr_t; + +// Values are in fmin <= x < fmax. +#define fmax_llr 1.0f +#define fmin_llr (-fmax_llr) +#define fmax_ullr fmax_llr +#define fmin_ullr 0.0f + +#define fmax_lk 0x1.0p32f +#define fmin_lk (-fmax_lk) +#define fmax_ulk fmax_lk +#define fmin_ulk 0.0f + +#define fmax_llk 0x1.0p16f +#define fmin_llk (-fmax_llk) +#define fmax_ullk fmax_llk +#define fmin_ullk 0.0f + +#define UMAX 0xffffffffffffffff +#define SMAX 0x7fffffffffffffff +#define SMIN 0x8000000000000000 + +// Values are in min <= x <= max. +#define max_lk lkbits (SMAX) +#define min_lk lkbits (SMIN) +#define max_ulk ulkbits (UMAX) +#define min_ulk ulkbits (0) + +#define max_llk llkbits (SMAX) +#define min_llk llkbits (SMIN) +#define max_ullk ullkbits (UMAX) +#define min_ullk ullkbits (0) + +#define max_llr llrbits (SMAX) +#define min_llr llrbits (SMIN) +#define max_ullr ullrbits (UMAX) +#define min_ullr ullrbits (0) + +#define id_lk 10 +#define id_ulk 20 +#define id_llk 30 +#define id_ullk 40 +#define id_llr 50 +#define id_ullr 60 diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S index 52e32a059ef..e7f21ceca17 100644 --- a/libgcc/config/avr/lib1funcs-fixed.S +++ b/libgcc/config/avr/lib1funcs-fixed.S @@ -1155,7 +1155,7 @@ DEFUN __mulQ64_work push __tmp_reg__ ;; A = |A| - .call_if_neg A7, __ssneg_8 + .call_if_neg A7, __negdi2 ;; Stash away |A| wmov r26, A6 @@ -1172,7 +1172,7 @@ DEFUN __mulQ64_work ;; A = |B| mov8 A0, B0 - .call_if_neg A7, __ssneg_8 + .call_if_neg A7, __negdi2 ;; Restore B = |A| wmov B6, r26 @@ -1195,9 +1195,6 @@ DEFUN __mulQ64_work ;; C = T = LSB (bit -1) from the unsigned mult. bld __tmp_reg__, 7 rol __tmp_reg__ - ;; T = result sign - pop __tmp_reg__ - bst __tmp_reg__, 7 ;; Adjust for signed Q formats that have one FBIT less. rol A0 rol A1 @@ -1207,41 +1204,30 @@ DEFUN __mulQ64_work rol A5 rol A6 rol A7 - brts .Lneg - ;; Result must be is >= 0 - ;; C = 1: Positive overflow + ;; r30.7 = result sign + pop r30 + + ;; Handle overflow. brcs .Lsaturate - ;; A < 0: Positive overflow - sec + ;; In the negative result case, A = 0x80.. is no overflow, + ;; but treating it as such keeps the value unchanged. brmi .Lsaturate + + ;; Handle negation. + sbrs r30, 7 ret + XJMP __negdi2 -.L0x80: - ;; Return 0x80.. - clc .Lsaturate: + ;; r30.7 = 0 -> 0x7f... + ;; r30.7 = 1 -> 0x80... + cpi r30, 0x80 ;; C = 1 -> 0x7f... ;; C = 0 -> 0x80... XCALL __sbc_8 subi A7, 0x80 ret - -.Lneg: - ;; Result must be <= 0 - brcs .L0x80 - XCALL __negdi2 - brmi 9f - ;; Values that are > 0 after the negation are overflow. - sbiw A6, 0 - sbci A5, 0 - sbci A4, 0 - sbci A3, 0 - sbci A2, 0 - sbci A1, 0 - sbci A0, 0 - brne .L0x80 -9: ret ENDF __mulQ64_work #endif /* L_mulQ64_work */