This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c/12308] New: '387 mode switching clobbers flags


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;
}
------------------------------------------------


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]