// !!! 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) \
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[] =
--- /dev/null
+/* { 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;
+}
--- /dev/null
+// !!! 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
push __tmp_reg__
;; A = |A|
- .call_if_neg A7, __ssneg_8
+ .call_if_neg A7, __negdi2
;; Stash away |A|
wmov r26, A6
;; A = |B|
mov8 A0, B0
- .call_if_neg A7, __ssneg_8
+ .call_if_neg A7, __negdi2
;; Restore B = |A|
wmov B6, r26
;; 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 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 */