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?

Johann

--

libgcc/
        * config/avr/t-avr (FUNCS_notiny): Add: _umulditi3_raw,
        _muluda3,  _muluta3,  _muludq3, _mulda3 _multa3 _muldq3,
        _muluQ64_tail, _mulQ64_work.
        * config/avr/asm-defs.h (mov8, ENTRY): New .macro's.
        * config/avr/lib1funcs-fixed.S (__umulditi3_raw, __muluda3)
        (__muluta3, __muludq3, __muluQ64_tail, __muldq3, __mulda3)
        (__multa3, __mulQ64_work): New DEFUN's.

gcc/testsuite/
        * gcc.target/avr/fx24-mul.c: New test.
diff --git a/gcc/testsuite/gcc.target/avr/fx24-mul.c b/gcc/testsuite/gcc.target/avr/fx24-mul.c
new file mode 100644
index 00000000000..6cc0d75b8cb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/avr/fx24-mul.c
@@ -0,0 +1,140 @@
+/* { dg-do run { target { ! avr_tiny } } } */
+/* { dg-additional-options { -std=gnu99 -Os -mcall-prologues } } */
+
+// !!! 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
+
+#define MK_TEST(fx)				    \
+  NI bool in_range_##fx (float x)		    \
+  {						    \
+    return x < fmax_##fx && x >= fmin_##fx;	    \
+  }						    \
+						    \
+  NI void test_mul_##fx (float a, float b)	    \
+  {						    \
+    if (!in_range_##fx (a))			    \
+      return;					    \
+    if (!in_range_##fx (b))			    \
+      return;					    \
+    float f = a * b;				    \
+    __asm ("" : "+r" (f));			    \
+    fx##_t ab = ((fx##_t) a) * (fx##_t) b;	    \
+    if (f < fmin_##fx)				    \
+      {						    \
+	if (ab != min_##fx)			    \
+	  exit (id_##fx + 1);			    \
+	return;					    \
+      }						    \
+    if (f > fmax_##fx)				    \
+      {						    \
+	if (ab != max_##fx)			    \
+	  exit (id_##fx + 2);			    \
+	return;					    \
+      }						    \
+    if (f != (float) ab)			    \
+      exit (id_##fx + 3);			    \
+  }
+
+MK_TEST (lk)
+MK_TEST (ulk)
+MK_TEST (llk)
+MK_TEST (ullk)
+MK_TEST (llr)
+MK_TEST (ullr)
+
+NI void test_mul (float a, float b)
+{
+  test_mul_lk (a, b);
+  test_mul_ulk (a, b);
+
+  test_mul_llk (a, b);
+  test_mul_ullk (a, b);
+
+  test_mul_llr (a, 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[] =
+  {
+    0.0,
+    +1.0, +2.0, +0.5, +0xff.0p0, +0xf.fp0, +0x0.ffp0, +0xcd.0p12, +0x0.0a1p0,
+    -1.0, -2.0, -0.5, -0xff.0p0, -0xf.fp0, -0x0.ffp0, -0xcd.0p12, -0x0.0a1p0,
+  };
+
+NI void test (void)
+{
+  for (uint8_t a = 0; a < ARRAY_SIZE (fvals); ++a)
+    for (uint8_t b = 0; b < ARRAY_SIZE (fvals); ++b)
+      {
+	float fa = pgm_read_float (&fvals[a]);
+	float fb = pgm_read_float (&fvals[b]);
+	test_mul (fa, fb);
+      }	
+}
+
+int main (void)
+{
+  test ();
+  return 0;
+}
diff --git a/libgcc/config/avr/asm-defs.h b/libgcc/config/avr/asm-defs.h
index 62879464f9d..970428f01ce 100644
--- a/libgcc/config/avr/asm-defs.h
+++ b/libgcc/config/avr/asm-defs.h
@@ -179,6 +179,19 @@
 .endm
 
 
+.macro  mov8  dst, src
+    REGNO ..mov8.dst, \dst
+    REGNO ..mov8.src, \src
+    .if ..mov8.dst < ..mov8.src
+        mov4    ..mov8.dst+0, ..mov8.src+0
+        mov4    ..mov8.dst+4, ..mov8.src+4
+    .else
+        mov4    ..mov8.dst+4, ..mov8.src+4
+        mov4    ..mov8.dst+0, ..mov8.src+0
+    .endif
+.endm
+
+
 ;; Negate a 2-byte value held in consecutive registers.
 .macro  NEG2  reg
     com     \reg+1
@@ -230,6 +243,12 @@
     .endfunc
 .endm
 
+.macro ENTRY name
+    .global \name
+    .type \name, @function
+    .size \name, 0
+    \name:
+.endm
 
 #ifndef __AVR_TINY__
 
diff --git a/libgcc/config/avr/lib1funcs-fixed.S b/libgcc/config/avr/lib1funcs-fixed.S
index 4b77ee2dd56..8deb21096fd 100644
--- a/libgcc/config/avr/lib1funcs-fixed.S
+++ b/libgcc/config/avr/lib1funcs-fixed.S
@@ -906,6 +906,382 @@ DEFUN __ssmulsa3
 #undef C7
 #undef SS
 
+/*******************************************************
+    Fixed 64 x 64 saturated Multiplication
+*******************************************************/
+
+.pushsection .text.libgcc.mul64, "ax", @progbits
+
+#define A0  18
+#define A1  A0+1
+#define A2  A0+2
+#define A3  A0+3
+#define A4  A0+4
+#define A5  A0+5
+#define A6  A0+6
+#define A7  A0+7
+
+#define B0  10
+#define B1  B0+1
+#define B2  B0+2
+#define B3  B0+3
+#define B4  B0+4
+#define B5  B0+5
+#define B6  B0+6
+#define B7  B0+7
+
+#define C0  10
+#define C1  C0+1
+#define C2  C0+2
+#define C3  C0+3
+#define C4  C0+4
+#define C5  C0+5
+#define C6  C0+6
+#define C7  C0+7
+#define C8  2
+#define C9  C8+1
+#define Ca  C8+2
+#define Cb  C8+3
+#define Cc  C8+4
+#define Cd  C8+5
+#define Ce  C8+6
+#define Cf  C8+7
+
+#define M0  18
+#define M1  M0+1
+#define M2  M0+2
+#define M3  M0+3
+#define M4  M0+4
+#define M5  M0+5
+#define M6  M0+6
+#define M7  M0+7
+
+#if defined (L_umulditi3_raw)
+
+.macro .Mul a, b
+    wmov    r24, Y
+    adiw    r24, \a
+    wmov    r30, Y
+    adiw    r30, \b
+    rcall   __umulsidi3_pBE
+.endm
+
+;;; uint128_t R10[16] = (uint128_t) R10[8] * R18[8]
+;;; No regs are restored except Y.
+DEFUN __umulditi3_raw
+    push    A0
+    push    A1
+    push    A2
+    push    A3
+    push    A4
+    push    A5
+    push    A6
+    push    A7
+    do_prologue_saves 10
+    ;; Now we have the following stack layout:
+    ;; Y+1..Y+2    saved Y
+    ;; Y+3..Y+10   B[8] as big endian bytes
+    ;; Y+11..Y+18  A[8] as big endian bytes
+    A.hi = 11
+    A.lo = A.hi + 4
+    B.hi = 3
+    B.lo = B.hi + 4
+
+    ;; In terms of uint32_t we have to perform 4 multiplications:
+
+    .Mul    A.lo, B.lo
+    mov8    C0, M0
+
+    .Mul    A.hi, B.hi
+    mov8    C8, M0
+
+    .Mul    A.hi, B.lo
+    rcall   __umulditi3_12
+
+    .Mul    A.lo, B.hi
+    rcall   __umulditi3_12
+
+    ;; Move result into place.
+    mov8    C0 + 0x8, C8
+
+    ;; Epilogue:  We pushed 18 bytes.  Let epilogue_restores pop
+    ;; them by adjusting SP accordingly.
+    ldi     r30, 18
+    ;; Pretend we pushed only 2 bytes (Y) so that the low part of
+    ;; the return value in R10..R17 won't be overridden by LDDs.
+    XJMP    __epilogue_restores__ + ((18 - 2) * 2)
+
+__umulditi3_12:
+    ADD     C4, M0
+    adc     C5, M1
+    adc     C6, M2
+    adc     C7, M3
+    adc     C8, M4
+    adc     C9, M5
+    adc     Ca, M6
+    adc     Cb, M7
+    adc     Cc, __zero_reg__
+    adc     Cd, __zero_reg__
+    adc     Ce, __zero_reg__
+    adc     Cf, __zero_reg__
+    ret
+
+;;; uint64_t R18 = uint32_t BE *Z  *  uint32_t BE *W
+__umulsidi3_pBE:
+    ldd     A0, Z + 3
+    ldd     A1, Z + 2
+    ldd     A2, Z + 1
+    ldd     A3, Z + 0
+    wmov    r30, r24
+    ldd     A4, Z + 3
+    ldd     A5, Z + 2
+    ldd     A6, Z + 1
+    ldd     A7, Z + 0
+    XJMP    __umulsidi3
+ENDF __umulditi3_raw
+#endif /* L_umulditi3_raw */
+
+#if defined (L_muluda3)
+;;; uint128_t R10 = umulsidi3 (uint32_t R22, uint32_t R18)
+FALIAS __usmuluda3
+DEFUN __muluda3
+    clt
+ENTRY __muluda3.2
+    do_prologue_saves 18
+    bld     __tmp_reg__, 7
+    push    __tmp_reg__
+    XCALL __umulditi3_raw
+    or      A7, A6
+    or      A7, A5
+    or      A7, A4
+    mov8    A0, r14
+    mov     r27, r13
+    XJMP    __muluQ64_tail
+ENDF __muluda3
+#endif /* L_muluda3 */
+
+#if defined (L_muluta3)
+FALIAS __usmuluta3
+DEFUN __muluta3
+    clt
+ENTRY __muluta3.2
+    do_prologue_saves 18
+    bld     __tmp_reg__, 7
+    push    __tmp_reg__
+    XCALL __umulditi3_raw
+    or      A7, A6
+    mov8    A0, r16
+    mov     r27, r15
+    XJMP    __muluQ64_tail
+ENDF __muluta3
+#endif /* L_muluta3 */
+
+#if defined (L_muludq3)
+FALIAS __usmuludq3
+DEFUN __muludq3
+    clt
+ENTRY __muludq3.2
+    do_prologue_saves 18
+    bld     __tmp_reg__, 7
+    push    __tmp_reg__
+    XCALL __umulditi3_raw
+    sez
+    mov     r27, r17
+    XJMP    __muluQ64_tail
+ENDF __muludq3
+#endif /* L_muludq3 */
+
+#if defined (L_muluQ64_tail)
+;;; Inputs:
+;;;    R27.7 = LSB (bit -1)
+;;;    Z = 0 -> Overflow
+;;;    Stack.7: On behalf of signed routines?
+DEFUN __muluQ64_tail
+    pop     __tmp_reg__
+    brne 1f
+    ;; tmp.7 = for signed?
+    ;; For signed: return LSB in T, no rounding
+    bst     r27, 7
+    tst     __tmp_reg__
+    brmi 9f
+
+    ;; For unsigned: round
+    lsl     r27
+    brcc 9f
+    ldi     r26, 1
+    XCALL   __adddi3_s8
+    brcs 1f
+9:  do_epilogue_restores 18
+
+1:  ;; Unsigned overflow: Saturate to 0xff...
+    sec
+    XCALL   __sbc_8
+    set
+    rjmp    9b
+ENDF __muluQ64_tail
+#endif /* L_muluQ64_tail */
+
+#if defined (L_muldq3)
+FALIAS __ssmuldq3
+DEFUN __muldq3
+    ldi     r30, lo8(gs(__muludq3.2))
+    ldi     r31, hi8(gs(__muludq3.2))
+    XJMP    __mulQ64_work
+ENDF  __muldq3
+#endif /* L_muldq3 */
+
+#if defined (L_mulda3)
+FALIAS __ssmulda3
+DEFUN __mulda3
+    ldi     r30, lo8(gs(__muluda3.2))
+    ldi     r31, hi8(gs(__muluda3.2))
+    XJMP    __mulQ64_work
+ENDF  __mulda3
+#endif /* L_mulda3 */
+
+#if defined (L_multa3)
+FALIAS __ssmulta3
+DEFUN __multa3
+    ldi     r30, lo8(gs(__muluta3.2))
+    ldi     r31, hi8(gs(__muluta3.2))
+    XJMP    __mulQ64_work
+ENDF  __multa3
+#endif /* L_multa3 */
+
+#if defined (L_mulQ64_work)
+DEFUN __mulQ64_work
+    mov     __tmp_reg__, A7
+    eor     __tmp_reg__, B7
+    push    __tmp_reg__
+
+    ;; A = |A|
+    tst     A7
+    brpl 1f
+    XCALL   __ssneg_8
+1:  ;; Stash away |A|
+    wmov    r26, A6
+    push    A5
+    push    A4
+    push    A3
+    push    A2
+    push    A1
+    push    A0
+
+    ;; A = |B|
+    mov8    A0, B0
+    tst     A7
+    brpl 2f
+    XCALL   __ssneg_8
+2:  ;; Restore B = |A|
+    wmov    B6, r26
+    pop     B0
+    pop     B1
+    pop     B2
+    pop     B3
+    pop     B4
+    pop     B5
+
+    XICALL
+
+    ;; 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
+    rol     A2
+    rol     A3
+    rol     A4
+    rol     A5
+    rol     A6
+    rol     A7
+    brts .Lneg
+
+    ;; Result must be is >= 0
+    ;; C = 1: Positive overflow
+    brcs .Lsaturate
+    ;; A < 0: Positive overflow
+    sec
+    brmi .Lsaturate
+    ret
+
+.L0x80:
+    ;; Return 0x80..
+    clc
+.Lsaturate:
+    ;; 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 */
+
+#undef A0
+#undef A1
+#undef A2
+#undef A3
+#undef A4
+#undef A5
+#undef A6
+#undef A7
+
+#undef B0
+#undef B1
+#undef B2
+#undef B3
+#undef B4
+#undef B5
+#undef B6
+#undef B7
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+#undef C8
+#undef C9
+#undef Ca
+#undef Cb
+#undef Cc
+#undef Cd
+#undef Ce
+#undef Cf
+
+#undef M0
+#undef M1
+#undef M2
+#undef M3
+#undef M4
+#undef M5
+#undef M6
+#undef M7
+
+.popsection ; .text.libgcc.mul64
+
 #endif /* ! AVR_TINY */
 
 /*******************************************************
diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr
index 9b05c8575b6..a1651b9692f 100644
--- a/libgcc/config/avr/t-avr
+++ b/libgcc/config/avr/t-avr
@@ -94,6 +94,10 @@ FUNCS_notiny += \
 	_fractsfsq _fractsfusq _fractsfsa _fractsfusa \
 	_mulsa3 _mulusa3 \
 	_usmulusa3 _ssmulsa3 \
+	_umulditi3_raw \
+	_muluda3 _muluta3 _muludq3 \
+	_mulda3 _multa3 _muldq3 \
+	_muluQ64_tail _mulQ64_work \
 	_divsa3 _udivusa3 \
 	_clr_8 \
 	_ssneg_4 _ssneg_8 \

Reply via email to