This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/12308] New: '387 mode switching clobbers flags
- From: "stuart at apple dot com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: 17 Sep 2003 00:45:05 -0000
- Subject: [Bug c/12308] New: '387 mode switching clobbers flags
- Reply-to: gcc-bugzilla at gcc dot gnu dot org
PLEASE REPLY TO gcc-bugzilla@gcc.gnu.org ONLY, *NOT* gcc-bugs@gcc.gnu.org.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12308
Summary: '387 mode switching clobbers flags
Product: gcc
Version: 3.4
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: c
AssignedTo: unassigned at gcc dot gnu dot org
ReportedBy: stuart at apple dot com
CC: gcc-bugs at gcc dot gnu dot org
GCC build triplet: i686-apple-darwin7.0.0b1
GCC host triplet: i686-apple-darwin7.0.0b1
GCC target triplet: i686-apple-darwin7.0.0b1
We found this problem on Darwin/x86, but we believe it will reproduce on GNU/Linux/x86.
Pre-PNI '387s need to switch into "truncate towards zero" mode in order to convert a double into
an integral type. The testcase is a pathological instance where the five-instruction mode-
switching idiom lands between a compare and a subsequent conditional jump ("if (x)" in the
testcase). Subsequent passes notice the flags set by the compare are lost, and delete the
compare instruction.
Here is the '387 mode-switching idiom:
fnstcw -6(%ebp) store '387 Control Word register
movzwl -6(%ebp), %eax
orb $12, %ah compute "truncate towards zero" mode
movw %ax, -8(%ebp)
fldcw -8(%ebp) set '387 to "truncate towards zero" mode
Theoretically, the compiler could issue the idiom once in the prologue, and the "fldcw -6(%ebp)" to
switch modes for zero-trunc mode and "fldcw -6(%ebp)" to restore "round nearest" mode. Instead,
it seems to re-issue the entire idiom every time it needs to issue an fp->int conversion.
The short-term hack is to mark the mode-switching idiom (the "orb") as clobbering the flags. (We
have such a patch, if you like.)
The better long-term fix is to fully apply the lcm.c:optimize_mode_switching() feature.
Getting this working correctly may be difficult; mistakes will cause subtle rounding errors that may
or may not be noticed. I humbly suggest the fix include support for correct code without the
optimize_mode_switching() pass; perhaps a flag "-fno-optimize-mode-switching" could cause an
fldcw-fistp-fldcw sequence. This should be a significant help when a mode-switching bug is
suspected.
If your P4 supports PNI (P4 "Prescott New Instructions"), there is a new instruction ("fisttp") that
will convert st(0) into an integral type, truncating towards zero, regardless of the current rounding
mode set in CW. This is clearly the preferred (modeless) solution, but it doesn't work on every P4,
and won't work on anything older.
Here is the commandline used to compile the testcase:
gcc -g -Wall -O2 -o test test.c -save-temps
You need -O2 or equivalent to see the problem.
Here is the testcase:
----------------------------------------------------
/* this #include is only for the abort() decl; may be omitted */
#include <stdlib.h>
static unsigned char x = 0;
static unsigned char y = 0;
float
width(void)
{
return 0.0f;
}
int
foo(void)
{
float w = width();
int index;
if (x) {
index = y ? 2 : 1;
} else {
index = y ? 2 : 0;
}
return index + (int)w;
}
int
main (void)
{
int x = foo() ;
if (x)
abort();
return 0;
}
------------------------------------------------