Added one more addendum that tweaks __mulQ64_work's overflow handling:
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/fx64-mul-1.c: Rename from fx24-mul.c.
* gcc.target/avr/fx64-mul-2.c: New test.
* gcc.target/avr/fx64-mul.h: New file.
Johann
Am 21.07.26 um 11:16 schrieb Denis Chertykov:
пн, 20 июл. 2026 г. в 20:48, Georg-Johann Lay <[email protected]>:
This patch adds (un)saturated 64-bit fixed-point multiplications
to libgcc. The saturating functions are just aliased of the
vanilla versions, which are also saturating.
Ok for trunk?
Ok. Please apply.
Denis
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 <stdfix.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <avr/pgmspace.h>
-
-#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 <stdfix.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <avr/pgmspace.h>
+
+#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 */