"%~call __<code><mode>3"
[(set_attr "type" "xcall")])
-;; "divhq3" "udivuhq3"
-;; "divha3" "udivuha3"
-(define_expand "<code><mode>3"
+;; "divhq3" "udivuhq3" "ssdivhq3" "usdivuhq3"
+;; "divha3" "udivuha3" "ssdivha3" "usdivuha3"
+(define_expand "<code_stdname><mode>3"
[(set (reg:ALL2QA 26)
(match_operand:ALL2QA 1 "register_operand" ""))
(set (reg:ALL2QA 22)
(match_operand:ALL2QA 2 "register_operand" ""))
(parallel [(set (reg:ALL2QA 24)
- (usdiv:ALL2QA (reg:ALL2QA 26)
- (reg:ALL2QA 22)))
+ (alldiv:ALL2QA (reg:ALL2QA 26)
+ (reg:ALL2QA 22)))
(clobber (reg:HI 26))
(clobber (reg:QI 21))])
(set (match_operand:ALL2QA 0 "register_operand" "")
(reg:ALL2QA 24))]
- ""
+ "SIGNED_FIXED_POINT_MODE_P (<MODE>mode)
+ == (<CODE> == DIV || <CODE> == SS_DIV)"
{
avr_fix_inputs (operands, 1 << 2, regmask (<MODE>mode, 26));
})
-;; "*divhq3.call" "*udivuhq3.call"
-;; "*divha3.call" "*udivuha3.call"
-(define_insn_and_split "*<code><mode>3.call_split"
+;; "*divhq3.split" "*udivuhq3.split" "*ssdivhq3.split" "*usdivuhq3.split"
+;; "*divha3.split" "*udivuha3.split" "*ssdivha3.split" "*usdivuha3.split"
+(define_insn_and_split "*<code_stdname><mode>3.split"
[(set (reg:ALL2QA 24)
- (usdiv:ALL2QA (reg:ALL2QA 26)
- (reg:ALL2QA 22)))
+ (alldiv:ALL2QA (reg:ALL2QA 26)
+ (reg:ALL2QA 22)))
(clobber (reg:HI 26))
(clobber (reg:QI 21))]
- ""
+ "SIGNED_FIXED_POINT_MODE_P (<MODE>mode)
+ == (<CODE> == DIV || <CODE> == SS_DIV)"
"#"
"&& reload_completed"
[(scratch)]
{ DONE_ADD_CCC })
-(define_insn "*<code><mode>3.call"
+;; "*divhq3.call" "*udivuhq3.call" "*ssdivhq3.call" "*usdivuhq3.call"
+;; "*divha3.call" "*udivuha3.call" "*ssdivha3.call" "*usdivuha3.call"
+(define_insn "*<code_stdname><mode>3.call"
[(set (reg:ALL2QA 24)
- (usdiv:ALL2QA (reg:ALL2QA 26)
- (reg:ALL2QA 22)))
+ (alldiv:ALL2QA (reg:ALL2QA 26)
+ (reg:ALL2QA 22)))
(clobber (reg:HI 26))
(clobber (reg:QI 21))
(clobber (reg:CC REG_CC))]
- "reload_completed"
- "%~call __<code><mode>3"
+ "reload_completed
+ && SIGNED_FIXED_POINT_MODE_P (<MODE>mode)
+ == (<CODE> == DIV || <CODE> == SS_DIV)"
+ "%~call __<code_stdname><mode>3"
[(set_attr "type" "xcall")])
;; Note the first parameter gets passed in already offset by 2 bytes
--- /dev/null
+/* { dg-do run { target { ! avr_tiny } } } */
+/* { dg-additional-options { -std=gnu99 -Os -mcall-prologues } } */
+
+#include "fx16.h"
+
+#define MK_TEST(fx) \
+ NI bool in_range_##fx (float x) \
+ { \
+ return x < fmax_##fx && x >= fmin_##fx; \
+ } \
+ \
+ NI void test_div_##fx (float a, float b) \
+ { \
+ if (!in_range_##fx (a)) \
+ return; \
+ if (!in_range_##fx (b)) \
+ return; \
+ float f = b ? a / b : 0.0f; \
+ __asm volatile ("" : "+r" (f)); \
+ fx##_t ax = (fx##_t) a; \
+ fx##_t bx = (fx##_t) b; \
+ fx##_t ab = ax / bx; \
+ if ((b == 0 && a < 0) || (b && f < fmin_##fx)) \
+ { \
+ if (ab != min_##fx) \
+ exit (id_##fx + 1); \
+ return; \
+ } \
+ if ((b == 0 && a >= 0) || (b && f > fmax_##fx)) \
+ { \
+ if (ab != max_##fx) \
+ exit (id_##fx + 2); \
+ return; \
+ } \
+ if (f != (float) ab) \
+ exit (id_##fx + 3); \
+ }
+
+MK_TEST (hk)
+MK_TEST (uhk)
+MK_TEST (r)
+MK_TEST (ur)
+
+NI void test_div (float a, float b)
+{
+ test_div_hk (a, b);
+ test_div_uhk (a, b);
+}
+
+// Results / args must be representable as float, so no rounding occurs.
+// Non-overflow results must be representable as fixed, so no rounding occurs.
+const PROGMEM float fvals[] =
+ {
+ 0.0,
+ +1.0, +2.0, +0.5, +0x1p2, +0x1p3, +0x1p-3,
+ -1.0, -2.0, -0.5, -0x1p2, -0x1p3, -0x1p-3,
+ };
+
+NI void test1 (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_div (fa, fb);
+ }
+}
+
+int main (void)
+{
+ test1 ();
+ return 0;
+}
--- /dev/null
+/* { dg-do run { target { ! avr_tiny } } } */
+/* { dg-additional-options { -std=gnu99 -Os -mcall-prologues } } */
+
+#include "fx16.h"
+
+NI void test_hk (uint16_t a, float fb, uint16_t res)
+{
+ hk_t b = (hk_t) fb;
+ if (hkbits (a) / b != hkbits (res))
+ exit (id_hk + 4);
+}
+
+NI void test_uhk (uint16_t a, float fb, uint16_t res)
+{
+ uhk_t b = (hk_t) fb;
+ if (uhkbits (a) / b != uhkbits (res))
+ exit (id_uhk + 4);
+}
+
+NI void test2_uhk (uint16_t a, uint16_t b, uint16_t res)
+{
+ if (uhkbits (a) / uhkbits (b) != uhkbits (res))
+ exit (id_uhk + 5);
+}
+
+NI void test2_hk (uint16_t a, uint16_t b, uint16_t res)
+{
+ if (hkbits (a) / hkbits (b) != hkbits (res))
+ exit (id_hk + 5);
+}
+
+void test2 (void)
+{
+ test_uhk (+0xaabb, +0x11.0p0f, +0x0a0b);
+ test_hk (-0x77bb, +0x11.0p0f, -0x070b);
+ test_hk (+0x77bb, -0x11.0p0f, -0x070b);
+ test_hk (-0x77bb, -0x11.0p0f, +0x070b);
+ test_hk (-0x7ffe, 2.0f, -0x3fff);
+ test_uhk (+0x7ffe, 2.0f, +0x3fff);
+ test_uhk (+0xa955, 2.0f, +0x54aa);
+ test_hk (+0x5955, 2.0f, +0x2caa);
+ test_hk (-0x5955, 2.0f, -0x2caa);
+ test_uhk (+0xffff, 2.0f, +0x7fff);
+ test_hk (-0x7fff, 2.0f, -0x3fff);
+ test_hk (+0, 0.0f, SMAX);
+ test_hk (+1, 0.0f, SMAX);
+ test_hk (-1, 0.0f, SMIN);
+ test_uhk (+0, 0.0f, UMAX);
+ test_uhk (+1, 0.0f, UMAX);
+ test_hk (SMAX - 100, +0x0.f8p0f, SMAX);
+ test_hk (SMIN + 100, +0x0.f8p0f, SMIN);
+ test_hk (SMAX - 100, -0x0.f8p0f, SMIN);
+ test_hk (SMIN + 100, -0x0.f8p0f, SMAX);
+ test_uhk (UMAX - 100, +0x0.f8p0f, UMAX);
+ test_uhk (UMAX - 100, +0x0.f8p0f, UMAX);
+
+ test2_uhk (X80 + 1, X80, uhk_1);
+ test2_uhk (X80, X80 + 1, uhk_1 - 1);
+ test2_uhk (UMAX - 1, UMAX, uhk_1 - 1);
+}
+
+int main (void)
+{
+ test2 ();
+ return 0;
+}
--- /dev/null
+/* { dg-do run { target { ! avr_tiny } } } */
+/* { dg-additional-options { -std=gnu99 -Os -mcall-prologues } } */
+
+#include "fx16.h"
+
+#define MK_TEST(fx) \
+ NI void test_div_##fx (uint16_t a, float fb, uint16_t r) \
+ { \
+ if (fb < fmax_##fx) \
+ { \
+ fx##_t b = (fx##_t) fb; \
+ fx##_t ab = fx##bits (a) / b; \
+ if (ab != fx##bits (r)) \
+ exit (id_##fx + 6); \
+ } \
+ }
+
+MK_TEST (hk)
+MK_TEST (uhk)
+MK_TEST (r)
+MK_TEST (ur)
+
+NI void test1_div (uint16_t a, float fb, uint16_t r)
+{
+ test_div_uhk (a, fb, r);
+ test_div_ur (a, fb, r);
+ if ((a & X80) == 0)
+ {
+ test_div_hk (a, fb, r);
+ test_div_r (a, fb, r);
+ }
+}
+
+NI void test2_div (uint16_t a, float fb, uint16_t r)
+{
+ test_div_uhk (a, fb, r);
+ test_div_ur (a, fb, r);
+ if ((r & X80) == 0)
+ {
+ test_div_hk (a, fb, r);
+ test_div_r (a, fb, r);
+ }
+ else
+ {
+ test_div_hk (a, fb, SMAX);
+ test_div_r (a, fb, SMAX);
+ }
+}
+
+NI void test3 (void)
+{
+ for (uint16_t a = (uint16_t) 0xff01; a; a >>= 1)
+ {
+ test1_div (a, 2.0f, a >> 1);
+ if ((a & X80) == 0)
+ test2_div (a, 0.5f, a << 1);
+ }
+}
+
+int main (void)
+{
+ test3 ();
+ return 0;
+}
--- /dev/null
+#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 short accum hk_t;
+typedef unsigned short accum uhk_t;
+typedef fract r_t;
+typedef unsigned fract ur_t;
+
+typedef short sat accum sat_hk_t;
+typedef short sat unsigned accum sat_uhk_t;
+typedef sat fract sat_r_t;
+typedef sat unsigned fract sat_ur_t;
+
+// Values are in fmin <= x < fmax.
+#define fmax_hk 0x1.0p8f
+#define fmin_hk (-fmax_hk)
+#define fmax_uhk fmax_hk
+#define fmin_uhk 0.0f
+#define fmax_r 0x1.0p0f
+#define fmin_r (-fmax_r)
+#define fmax_ur fmax_r
+#define fmin_ur 0.0f
+
+#define UMAX 0xffff
+#define SMAX 0x7fff
+#define SMIN 0x8000
+#define X80 0x8000
+
+// Values are in min <= x <= max.
+#define max_hk hkbits (SMAX)
+#define min_hk hkbits (SMIN)
+#define max_uhk uhkbits (UMAX)
+#define min_uhk uhkbits (0)
+#define max_r rbits (SMAX)
+#define min_r rbits (SMIN)
+#define max_ur urbits (UMAX)
+#define min_ur urbits (0)
+
+#define hk_1 (1u << __HA_FBIT__)
+#define uhk_1 (1u << __UHA_FBIT__)
+
+#define id_hk 10
+#define id_uhk 20
+#define id_r 30
+#define id_ur 40
/*******************************************************
Fractional Division 16 / 16
*******************************************************/
-#define r_divdL 26 /* dividend Low */
-#define r_divdH 27 /* dividend Hig */
-#define r_quoL 24 /* quotient Low */
-#define r_quoH 25 /* quotient High */
-#define r_divL 22 /* divisor */
-#define r_divH 23 /* divisor */
-#define r_cnt 21
+;;; Argument 1: Dividend and remainder
+#define A0 26
+#define A1 A0+1
+
+;;; Argument 2: Divisor
+#define B0 22
+#define B1 B0+1
+
+;;; Return value: Quotient
+#define Q0 24
+#define Q1 Q0+1
+
+#define Cnt 21
+#define Sign __tmp_reg__
#if defined (L_divhq3)
-DEFUN __divhq3
- mov __tmp_reg__, r_divdH
- eor __tmp_reg__, r_divH
- sbrs r_divH, 7
- rjmp 1f
- NEG2 r_divL
+;;; (set (reg:HQ 24)
+;;; (ss_div:HQ (reg:HQ 26)
+;;; (reg:HQ 22)))
+;;; (clobber (reg:HI 26))
+;;; (clobber (reg:QI 21))
+;;; Rounds towards zero. Overflow saturates.
+DEFUN __divhq3
+ENTRY __ssdivhq3
+ mov Sign, A1
+ tst B1
+ brpl 1f
+ com Sign
+ NEG2 B0
1:
- sbrs r_divdH, 7
- rjmp 2f
- NEG2 r_divdL
+ tst A1
+ brpl 2f
+ NEG2 A0
2:
- cp r_divdL, r_divL
- cpc r_divdH, r_divH
- breq __divhq3_minus1 ; if equal return -1
- XCALL __udivuhq3
- lsr r_quoH
- ror r_quoL
- brpl 9f
- ;; negate result if needed
- NEG2 r_quoL
-9:
- ret
-__divhq3_minus1:
- ldi r_quoH, 0x80
- clr r_quoL
- ret
+ lsr A1 ; Adjust for FBIT = 15.
+ ror A0 ; Pass LSB in C.
+ clr Q1
+ XCALL __udivuhq3.2
+ XJMP __divQ16_tail
ENDF __divhq3
#endif /* defined (L_divhq3) */
+#if defined (L_divQ16_tail)
+DEFUN __divQ16_tail
+ ;; In the negative result case, Q = 0x80.. is no overflow,
+ ;; but treating it as such keeps the value unchanged.
+ brmi .Lsaturate ; N = sign (Q)
+ ;; Negate result as needed.
+ sbrs Sign, 7
+ ret
+ NEG2 Q0
+ ret
+.Lsaturate:
+ ;; Sign.7 = 0 -> 0x7f..
+ ;; Sign.7 = 1 -> 0x80..
+#ifndef __AVR_TINY__
+ ldi Q0, lo8(0x7fff)
+ ldi Q1, hi8(0x7fff)
+ sbrc Sign, 7
+ adiw Q0, 1
+#else
+ cpi Sign, 0x80
+ sbc Q0, Q0
+ sbc Q1, Q1
+ subi Q1, 0x80
+#endif
+ ret
+ENDF __divQ16_tail
+#endif /* L_divQ16_tail */
+
#if defined (L_udivuhq3)
-DEFUN __udivuhq3
- sub r_quoH,r_quoH ; clear quotient and carry
- ;; FALLTHRU
+;;; (set (reg:UHQ 24)
+;;; (us_div:UHQ (reg:UHQ 26)
+;;; (reg:UHQ 22)))
+;;; (clobber (reg:HI 26))
+;;; (clobber (reg:QI 21))
+;;; Rounds towards zero. Overflow saturates.
+DEFUN __udivuhq3
+ENTRY __usdivuhq3
+ sub Q1, Q1 ; CLC
+ENTRY __udivuhq3.2
+ clr Q0 ; Clear quotient
+ ldi Cnt, 16 ; Init loop counter.
+ ;; C = 0 (unsigned).
+ ;; C = LSB from the caller (signed).
+.Loop:
+ rol A0 ; Shift one more dividend bit into the remainder.
+ rol A1
+ brcs .Lsub ; Divisor always fits in remainder?
+ cp A0, B0 ; Remainder >= B?
+ cpc A1, B1
+ brcc .Lsub
+ rol Q0 ; No: Shift quotient with C = 1 (0 in result).
+ rjmp .Lcont
+.Lsub:
+ sub A0, B0 ; Yes: Restore remainder by subtracting B...
+ sbc A1, B1
+ lsl Q0 ; ...and shift quotient with C = 0 (1 in result).
+.Lcont:
+ rol Q1 ; Shift quotient.
+ dec Cnt ; Decrement loop counter.
+ brne .Loop
+ com Q0 ; Complement result because C was
+ com Q1 ; complemented in the loop.
+ ret ; N = sign (Q).
ENDF __udivuhq3
-
-DEFUN __udivuha3_common
- clr r_quoL ; clear quotient
- ldi r_cnt,16 ; init loop counter
-__udivuhq3_loop:
- rol r_divdL ; shift dividend (with CARRY)
- rol r_divdH
- brcs __udivuhq3_ep ; dividend overflow
- cp r_divdL,r_divL ; compare dividend & divisor
- cpc r_divdH,r_divH
- brcc __udivuhq3_ep ; dividend >= divisor
- rol r_quoL ; shift quotient (with CARRY)
- rjmp __udivuhq3_cont
-__udivuhq3_ep:
- sub r_divdL,r_divL ; restore dividend
- sbc r_divdH,r_divH
- lsl r_quoL ; shift quotient (without CARRY)
-__udivuhq3_cont:
- rol r_quoH ; shift quotient
- dec r_cnt ; decrement loop counter
- brne __udivuhq3_loop
- com r_quoL ; complement result
- com r_quoH ; because C flag was complemented in loop
- ret
-ENDF __udivuha3_common
#endif /* defined (L_udivuhq3) */
/*******************************************************
Fixed Division 8.8 / 8.8
*******************************************************/
#if defined (L_divha3)
-DEFUN __divha3
- mov __tmp_reg__, r_divdH
- eor __tmp_reg__, r_divH
- sbrs r_divH, 7
- rjmp 1f
- NEG2 r_divL
+;;; (set (reg:HA 24)
+;;; (ss_div:HA (reg:HA 26)
+;;; (reg:HA 22)))
+;;; (clobber (reg:HI 26))
+;;; (clobber (reg:QI 21))
+;;; Rounds towards zero. Overflow saturates.
+DEFUN __divha3
+ENTRY __ssdivha3
+ mov Sign, A1
+ tst B1
+ brpl 1f
+ com Sign
+ NEG2 B0
1:
- sbrs r_divdH, 7
- rjmp 2f
- NEG2 r_divdL
+ tst A1
+ brpl 2f
+ NEG2 A0
2:
- XCALL __udivuha3
- lsr r_quoH ; adjust to 7 fractional bits
- ror r_quoL
- sbrs __tmp_reg__, 7 ; negate result if needed
- ret
- NEG2 r_quoL
- ret
+ lsr A1 ; Adjust to FBIT = 7.
+ ror A0 ; Pass LSB in C.
+ XCALL __udivuha3.2 ; N = sign (Q)
+ XJMP __divQ16_tail
ENDF __divha3
#endif /* defined (L_divha3) */
#if defined (L_udivuha3)
-DEFUN __udivuha3
- mov r_quoH, r_divdL
- mov r_divdL, r_divdH
- clr r_divdH
- lsl r_quoH ; shift quotient into carry
- XJMP __udivuha3_common ; same as fractional after rearrange
+;;; (set (reg:UHA 24)
+;;; (us_div:UHA (reg:UHA 26)
+;;; (reg:UHA 22)))
+;;; (clobber (reg:HI 26))
+;;; (clobber (reg:QI 21))
+;;; Rounds towards zero. Overflow saturates.
+DEFUN __udivuha3
+ENTRY __usdivuha3
+ clc
+ENTRY __udivuha3.2
+ mov Q1, A0 ; Put dividend in A0:Q1.
+ mov A0, A1
+ clr A1
+ rol Q1 ; Shift quotient into carry.
+ XJMP __udivuhq3.2 ; Same as fractional after rearrange.
ENDF __udivuha3
#endif /* defined (L_udivuha3) */
-#undef r_divdL
-#undef r_divdH
-#undef r_quoL
-#undef r_quoH
-#undef r_divL
-#undef r_divH
-#undef r_cnt
+#undef A0
+#undef A1
+#undef Q0
+#undef Q1
+#undef B0
+#undef B1
+#undef Cnt
+#undef Sign
+
/*********************************************************
Fixed 16.16 and s16.15 saturated [U]SAmode Divisions
_mulha3 _muluha3 _muluha3_round \
_usmuluha3 _ssmulha3 \
_divqq3 _udivuqq3 _divqq_helper \
- _divhq3 _udivuhq3 \
+ _divhq3 _udivuhq3 _divQ16_tail \
_divha3 _udivuha3 \
_divsa3 _udivusa3 \
_ssneg_2 \