This is the mail archive of the gcc@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]

x86 float conditional move is buggy


Hi,

Here is the original bug report on the PPro float conditional move. The
problem is x86 uses register stack for float. The operands of the float
conditional moves have to be the float registers on the register stack.
For some reason, gcc turns

(insn 170 157 45 (set (reg/v:DF 10 %st(2))
        (if_then_else:DF (ne:SI (reg/v:SI 4 %esi)
                (reg/v:SI 3 %ebx))
            (reg/v:DF 9 %st(1))
            (reg:DF 12 %st(4)))) 396 {movdfcc+2} (insn_list 33 (nil))
    (nil))

where (reg/v:DF 10 %st(2)) is not on the stack at the time into

(insn:QI 178 167 47 (set (reg:DF 8 %st(0))
        (if_then_else:DF (ne:SI (reg/v:SI 4 %esi)
                (reg/v:SI 3 %ebx))
            (reg:DF 8 %st(0))
            (reg:DF 9 %st(1)))) 396 {movdfcc+2} (insn_list 33 (nil))
    (nil))

It was done around line 2359 in subst_stack_regs_pat in reg-stack.c.
Before subst_stack_regs_pat, the stack looks like

stack	virtual	hard
top	DF 9	DF 8
top-1	DF 12	DF 9
empty

the instuction tries to get

stack	virtual	hard
top	DF 10	DF 8
top-1	DF 9	DF 9
top-2	DF 12	DF 10
empty

Instead it gets

stack	virtual	hard
top	DF 10	st8
top-1	DF 12	st9
empty

I am not sure how it gets

(insn 170 157 45 (set (reg/v:DF 10 %st(2))
        (if_then_else:DF (ne:SI (reg/v:SI 4 %esi)
                (reg/v:SI 3 %ebx))
            (reg/v:DF 9 %st(1))
            (reg:DF 12 %st(4)))) 396 {movdfcc+2} (insn_list 33 (nil))
    (nil))

where (reg/v:DF 10 %st(2)) is not on the stack. For x86, when the
destination is a float register, movxfcc is diffenerent from movxf.
movxf is a push while movxfcc is not. gcc may get confused on that.


-- 
H.J. Lu (hjl@gnu.org)
---
/*
Dear gcc maintainers,

I believe I have found a bug in the PentiumPro optimizer of

 gcc version egcs-2.91.57 19980901 (egcs-1.1 release)

built for i686-pc-linux-gnulibc1 without any options or modifications.

The program below should multipliy a matrix with its inverse and compare
elementwise the result to the unity matrix, recording the maximum absolute
difference between the expected result and the unity matrix.

I have simplified the program as much as possible, using unity matrices as
operands. The result should be 0.

Here is the program:
*/

#include <stdio.h>
#include <math.h>
#define SIZE 10

double a [SIZE * SIZE] =
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
  0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
  0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
double ainv [SIZE * SIZE] =
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 
  0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 
  0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};

int
main (void)
{
  int i, j, k;
  double diff, sum, soll, maxd;

  /* compute the product a*ainv element by element and compare to
     unity matrix */

  maxd = 0;			/* maximum of difference */
  for (i = 0; i < SIZE; i++)
    for (j = 0; j < SIZE; j++)
      /* compute element (i,j) of product */
      {
/*1*/	sum = 0;   
/*2*/	soll = ((i == j) ? 1 : 0);	/* this should be the result */
	for (k = 0; k < SIZE; k++)
	  sum += a[i * SIZE + k] * ainv[k * SIZE + j];
	diff = fabs (sum - soll);	/* measure difference */
	if (diff > maxd)	/* record maximum difference */
	  maxd = diff;
      }
  printf ("maxd=%g \n", maxd);
  return 0;
}

#if 0
"If I compile it with

 gcc -g -O -march=i686 err.c -o err    /* BUG */

I get the wrong result

 maxd=NaN 

The bug appears only with the combination of '-O' and '-march=i686'.
The following produce correct results:

 gcc -g  -O3  -march=i586 err.c -o err   /* NO BUG */
 gcc -g  -march=i686 err.c -o err        /* NO BUG */

Furthermore, the bug disappears if the lines marked /*1*/ and /*2*/
in the source are interchanged.

"
#endif


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