--- /dev/null
+/* { dg-do run { target { ! avr_tiny } } } */
+/* { dg-additional-options { -std=gnu99 -Os -mcall-prologues } } */
+
+typedef __INT8_TYPE__ int8_t;
+typedef __UINT8_TYPE__ uint8_t;
+typedef __UINT16_TYPE__ uint16_t;
+typedef __UINT64_TYPE__ uint64_t;
+typedef __INT64_TYPE__ int64_t;
+
+#define ARRAY_SIZE(X) (sizeof(X) / sizeof(*X))
+
+const __flash uint64_t vals[] =
+ {
+ // NaNs
+ 0xffffffffffffffff,
+ 0x7fffffffffffffff,
+ 0xfff0000000000001,
+ 0x7ff0000000000001,
+
+ // Some non-NaN doubles, increasing in magnitude.
+ 0xfff0000000000000, // -Inf
+ 0xffefffffffffffff,
+ 0xffe0000000000000,
+ 0x8010000000000000,
+ 0x800fffffffffffff,
+ 0x800ffffffffffffe,
+ 0x8007fffffffffffe,
+ 0x8000000000000001,
+ 0x0000000000000000,
+ 0x0000000000000001,
+ 0x0007fffffffffffe,
+ 0x000ffffffffffffe,
+ 0x000fffffffffffff,
+ 0x0010000000000000,
+ 0x7fe0000000000000,
+ 0x7fefffffffffffff,
+ 0x7ff0000000000000 // +Inf
+ };
+
+#define SMASK ((uint64_t) 1 << 63)
+
+char d64_nan_p (uint64_t a)
+{
+ return (a & ~SMASK) > (uint64_t) 0x7ff << 52;
+}
+
+extern uint64_t xmin (uint64_t, uint64_t) __asm("__fmin");
+extern uint64_t xmax (uint64_t, uint64_t) __asm("__fmax");
+
+void test_fmin (uint8_t i, uint8_t j)
+{
+ uint64_t a = vals[i];
+ uint64_t b = vals[j];
+ uint64_t m = xmin (a, b);
+
+ char a_nan_p = d64_nan_p (a);
+ char b_nan_p = d64_nan_p (b);
+
+ if (a_nan_p + b_nan_p == 2)
+ {
+ if (!d64_nan_p (m))
+ __builtin_exit (__LINE__);
+ }
+ else
+ {
+ uint64_t r = 0?0
+ : a_nan_p ? b
+ : b_nan_p ? a
+ : i < j ? a : b;
+ if (r != m)
+ __builtin_exit (__LINE__);
+ }
+}
+
+
+void test_fmax (uint8_t i, uint8_t j)
+{
+ uint64_t a = vals[i];
+ uint64_t b = vals[j];
+ uint64_t m = xmax (a, b);
+
+ char a_nan_p = d64_nan_p (a);
+ char b_nan_p = d64_nan_p (b);
+
+ if (a_nan_p + b_nan_p == 2)
+ {
+ if (!d64_nan_p (m))
+ __builtin_exit (__LINE__);
+ }
+ else
+ {
+ uint64_t r = 0?0
+ : a_nan_p ? b
+ : b_nan_p ? a
+ : i > j ? a : b;
+ if (r != m)
+ __builtin_exit (__LINE__);
+ }
+}
+
+
+void tests (void)
+{
+ for (uint8_t i = 0; i < ARRAY_SIZE (vals); ++i)
+ for (uint8_t j = 0; j < ARRAY_SIZE (vals); ++j)
+ {
+ test_fmin (i, j);
+ }
+}
+
+
+int main (void)
+{
+ tests ();
+ return 0;
+}
;; Functions that usually live in libm: Depending on [long] double layout,
;; define <name> and <name>l as weak alias(es) of __<name> for <name> in:
-;; pow fmin fmax fmod hypot atan2 fdim
+;; pow fmod hypot atan2 fdim
;; double __pow (double, double)
#ifdef F7MOD_D_pow_
_ENDF __pow
#endif /* F7MOD_D_pow_ */
-;; double __fmin (double, double)
-#ifdef F7MOD_D_fmin_
-_DEFUN __fmin
- DALIAS fmin
- LALIAS fminl
- .global F7_NAME(fmin)
- ldi ZH, hi8(gs(F7_NAME(fmin)))
- ldi ZL, lo8(gs(F7_NAME(fmin)))
- F7jmp call_ddd
-_ENDF __fmin
-#endif /* F7MOD_D_fmin_ */
-
-;; double __fmax (double, double)
-#ifdef F7MOD_D_fmax_
-_DEFUN __fmax
- DALIAS fmax
- LALIAS fmaxl
- .global F7_NAME(fmax)
- ldi ZH, hi8(gs(F7_NAME(fmax)))
- ldi ZL, lo8(gs(F7_NAME(fmax)))
- F7jmp call_ddd
-_ENDF __fmax
-#endif /* F7MOD_D_fmax_ */
-
;; double __fmod (double, double)
#ifdef F7MOD_D_fmod_
_DEFUN __fmod
;;; T = 0: Comparison is ordered, and Z, N, C, S flags are set according
;;; to compare (double A, double B) as if set by a signed int comparison.
;;; Note that f(+0) = f(-0) = 0.
-;;; In any case, return R24 = 1.
+;;; In any case:
+;;; - return R24 = 1.
+;;; - return R25.0 = isNaN (A)
+;;; - return R25.1 = isNaN (B)
DEFUN D_cmp
rcall D_cmp.map_i64
- brts 9f
+ bld __tmp_reg__, 0
+ push __tmp_reg__
;; Save A somewhere else...
wmov AA6, A6
mov AA5, A5
cpc AA5, BB5
cpc AA6, BB6
cpc AA7, BB7
-9: ldi r24, 1
+ pop r25
+ ;; R25.0 <=> A is NaN
+ ;; R25.1 <=> B is NaN
+ ;; T <=> comparison is unordered
+ bld r25, 1
+ sbrc r25, 0
+ set
+ ldi r24, 1
ret
;;; A is NaN: Set T=1.
_ENDF __unorddf2
#endif /* F7MOD_D_unord_ */
+#ifdef F7MOD_D_fminfmax_
+_DEFUN __fmin
+DALIAS fmin
+LALIAS fminl
+ inc __zero_reg__
+
+_LABEL __fmax
+DALIAS fmax
+LALIAS fmaxl
+ ;; Push A[].
+ push r25
+ push r24
+ push r23
+ push r22
+ push r21
+ push r20
+ push r19
+ push r18
+ ;; fmin or fmax
+ push __zero_reg__
+ clr __zero_reg__
+
+ XCALL __gedf2
+
+ pop __tmp_reg__
+ andi r25, 0x3 ; NaNs?
+ brne .Lnan
+ ;; No NaNs involved.
+ eor __tmp_reg__, r24 ; (f == fmin) ^ (A >= B)
+ brne 1f
+2:
+ ;; Return B since the cases are:
+ ;; fmax && A < B
+ ;; fmin && A >= B
+#ifdef __AVR_XMEGA__
+ in XL, __SP_L__
+ in XH, __SP_H__
+ adiw XL, 8
+ out __SP_L__, XL
+ out __SP_H__, XH
+#else
+ pop r0 $ pop r0 $ pop r0 $ pop r0
+ pop r0 $ pop r0 $ pop r0 $ pop r0
+#endif
+ wmov r24, r16
+ wmov r22, r14
+ wmov r20, r12
+ wmov r18, r10
+ ret
+1:
+ ;; Return A since the cases are:
+ ;; fmax && A >= B
+ ;; fmin && A < B
+ pop r18
+ pop r19
+ pop r20
+ pop r21
+ pop r22
+ pop r23
+ pop r24
+ pop r25
+ ret
+
+.Lnan:
+ ;; There are NaNs.
+ ;; When only the 1st argument is a NaN, then return the 2nd argument
+ cpi r25, 0x1
+ breq 2b
+ ;; When the 2nd argument is a NaN, then return the 1st argument.
+ ;; When both arguments are NaNs, then return NaN (e.g. the 1st argument).
+ rjmp 1b
+_ENDF __fmax
+#endif /* F7MOD_D_fminfmax_ */
#ifdef F7MOD_call_dd_
F7_ASM_PARTS += D_class D_fma D_powi
F7_ASM_PARTS += D_isnan D_isinf D_isfinite D_signbit D_copysign D_neg D_fabs
-F7_ASM_PARTS += D_cmp D_eq D_ne D_ge D_gt D_le D_lt D_unord
+F7_ASM_PARTS += D_cmp D_eq D_ne D_ge D_gt D_le D_lt D_unord D_fminfmax
F7_ASM_PARTS += call_dd call_ddd
g_dx += floatunsidf floatsidf extendsfdf2
g_xd += fixdfsi fixdfdi fixunsdfdi fixunsdfsi truncdfsf2
-m_ddd += pow fmin fmax fmod hypot atan2 fdim
+m_ddd += pow fmod hypot atan2 fdim
m_ddx += ldexp frexp
m_dd += sqrt cbrt exp exp10 pow10 log log10 log2 sin cos tan cotan asin acos atan
m_dd += ceil floor trunc round sinh cosh tanh