mirror of git://gcc.gnu.org/git/gcc.git
AVR/LibF7: target/122177 - fix fmin / fmax return value for one NaN arg.
fmin and fmax should return the non-NaN argument in the case where exactly one argument is a NaN. Moreover, IEEE double fmin and fmax can be performed without first converting the args to the internal representation and then converting back again. PR target/122177 libgcc/config/avr/libf7/ * libf7-common.mk (m_ddd): Remove: fmin, fmax. (F7_ASM_PARTS): Add: D_fminfmax. * libf7-asm.sx (D_fmanfmax): New module. * f7-wraps.h: Rebuild. gcc/testsuite/ * gcc.target/avr/fminfmax-1.c: New test.
This commit is contained in:
parent
e5731a4bc5
commit
efb3cd64fd
|
@ -0,0 +1,116 @@
|
||||||
|
/* { 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;
|
||||||
|
}
|
|
@ -169,7 +169,7 @@ _ENDF __extendsfdf2
|
||||||
|
|
||||||
;; Functions that usually live in libm: Depending on [long] double layout,
|
;; 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:
|
;; 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)
|
;; double __pow (double, double)
|
||||||
#ifdef F7MOD_D_pow_
|
#ifdef F7MOD_D_pow_
|
||||||
|
@ -183,30 +183,6 @@ _DEFUN __pow
|
||||||
_ENDF __pow
|
_ENDF __pow
|
||||||
#endif /* F7MOD_D_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)
|
;; double __fmod (double, double)
|
||||||
#ifdef F7MOD_D_fmod_
|
#ifdef F7MOD_D_fmod_
|
||||||
_DEFUN __fmod
|
_DEFUN __fmod
|
||||||
|
|
|
@ -1765,10 +1765,14 @@ ENDF class_D
|
||||||
;;; T = 0: Comparison is ordered, and Z, N, C, S flags are set according
|
;;; 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.
|
;;; to compare (double A, double B) as if set by a signed int comparison.
|
||||||
;;; Note that f(+0) = f(-0) = 0.
|
;;; 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
|
DEFUN D_cmp
|
||||||
rcall D_cmp.map_i64
|
rcall D_cmp.map_i64
|
||||||
brts 9f
|
bld __tmp_reg__, 0
|
||||||
|
push __tmp_reg__
|
||||||
;; Save A somewhere else...
|
;; Save A somewhere else...
|
||||||
wmov AA6, A6
|
wmov AA6, A6
|
||||||
mov AA5, A5
|
mov AA5, A5
|
||||||
|
@ -1794,7 +1798,14 @@ DEFUN D_cmp
|
||||||
cpc AA5, BB5
|
cpc AA5, BB5
|
||||||
cpc AA6, BB6
|
cpc AA6, BB6
|
||||||
cpc AA7, BB7
|
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
|
ret
|
||||||
|
|
||||||
;;; A is NaN: Set T=1.
|
;;; A is NaN: Set T=1.
|
||||||
|
@ -1904,6 +1915,79 @@ _DEFUN __unorddf2
|
||||||
_ENDF __unorddf2
|
_ENDF __unorddf2
|
||||||
#endif /* F7MOD_D_unord_ */
|
#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_
|
#ifdef F7MOD_call_dd_
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ F7_ASM_PARTS += store_expo sqrt16 sqrt_approx div
|
||||||
|
|
||||||
F7_ASM_PARTS += D_class D_fma D_powi
|
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_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
|
F7_ASM_PARTS += call_dd call_ddd
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ g_xdd_cmp +=
|
||||||
g_dx += floatunsidf floatsidf extendsfdf2
|
g_dx += floatunsidf floatsidf extendsfdf2
|
||||||
g_xd += fixdfsi fixdfdi fixunsdfdi fixunsdfsi truncdfsf2
|
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_ddx += ldexp frexp
|
||||||
m_dd += sqrt cbrt exp exp10 pow10 log log10 log2 sin cos tan cotan asin acos atan
|
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
|
m_dd += ceil floor trunc round sinh cosh tanh
|
||||||
|
|
Loading…
Reference in New Issue