From: bde@runx.oz.AU (Bruce Evans)
Subject: 386 gcc-2.3.1: sin (x) wrong for large x
Organization: RUNX Un*x Timeshare.  Sydney, Australia.
Distribution: gnu
Date: Tue, 17 Nov 1992 17:13:03 GMT

gcc-2.3.1 for the 386 generates incorrect code for sin (x).  It is necessary to
check the C2 bit in the FP status word to handle the case abs (x) >= 2**64.
cos (x) requires the same treatment.

I think that the C2 bit should be checked even for -ffast-math.  It does not
cause an exception (the arg is not changed) so the error will be discarded
silently if C2 is not checked.

I don't think it is worth inlining the transcendental functions for 486's
(and probably not for 386/387's).  The speedup is only 10-20% compared
with an extern function and zero compared with an inline function.  There
is another correctness issue.  The inline code will trap if the arg is a
NaN or infinity and the exception masks say to trap.  I think the ANSI
standard says that sin () should not trap.  The function implementation
could easily change the exception masks to avoid the trap (or better
keep track of the exception masks and change them only when necessary).
This is a bit much for automatically inlined versions.

See the comments in the assembler output for more remarks.  The comment
starting with a single / shows suitable correct code.  Comments starting
with // are about side issues.

---
command
---
gcc -v -S -O -m80387 -mfp-ret-in-387 -mieee-fp z.c
---

---
error output
---
Reading specs from /usr/gnu/gcc/lib/gcc-lib/i386-minix/2.3.1/specs
gcc version 2.3.1
 /usr/gnu/gcc/lib/gcc-lib/i386-minix/2.3.1/cpp -lang-c -v -undef -D__GNUC__=2 -D__OPTIMIZE__ z.c /usr/tmp/cca02026.i
GNU CPP version 2.3.1 (80386, BSD syntax)
 /usr/gnu/gcc/lib/gcc-lib/i386-minix/2.3.1/cc1 /usr/tmp/cca02026.i -quiet -dumpbase z.c -m80387 -mfp-ret-in-387 -mieee-fp -O -version -o z.s
GNU C version 2.3.1 (80386, BSD syntax) compiled by GNU C version 2.3.1.

---
input file z.c
---
double sin(double x);

extern double x;

double g(void)
{
    return sin(x);
}
---

---
commented output file
---
	.file	"z.c"
gcc2_compiled.:
.text
	.align 2		// I want .align 2,0x90 - need to use a macro
				// more like ASM_OUTPUT_ALIGN_CODE than
				// ASM_OUTPUT_ALIGN at the start of a function
.globl _g
_g:
	pushl %ebp
	movl %esp,%ebp
	fldl _x
	fld %st(0)		// inefficient
	fsin
		/ Must test C2 set by fsin now since fucom will clobber it.
		/ Add this:
		/     fnstsw %ax
		/     testb $4,%ah	/ 4 = C2
		/     jne Lnew
		/ Or delete the old 5 instructions starting at fucom and
		/ add this:
		/     fnstsw %ax
		/     testl $0x4zz,%eax	/ 0x400 = C2, zz = relevant exceptions
		/     jne Lnew
		/ The second version calls the sin () function if the fsin
		/ set an exception bit or if an exception bit was already
		/ set.  This version is better unless there are a lot of
		/ (masked) exceptions and the sin () function is much slower.
	fucom %st(0)
	fnstsw %ax
	andb $69,%ah
	cmpb $64,%ah
	je L3			// inefficient - branch usually taken
		/ Lnew:
	fstp %st(0)
	subl $8,%esp		// inefficient
	fstpl (%esp)		// inefficient
				// It was not good to dup the value of x in
				// loaded into %st for fsin, since we want
				// it on the x86 and not the x87 stack for
				// the function call.
	call _sin
	leave
	ret
	.align 2,0x90
L3:
	fstp %st(1)
	leave
	ret
---
-- 
Bruce Evans  (bde@runx.oz.au)

Newsgroups: gnu.gcc.bug