This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
Reg-stack and cmove bug
- To: Tim Prince <tprince at computer dot org>
- Subject: Reg-stack and cmove bug
- From: "Zack Weinberg" <zackw at Stanford dot EDU>
- Date: Fri, 16 Feb 2001 01:04:13 -0800
- Cc: tprinceusa at mindspring dot com, gcc-bugs at gcc dot gnu dot org, jh at suse dot cz
- References: <Springmail.105.982281121.0.18922500@www.springmail.com> <20010215161557.J814@wolery.stanford.edu> <015201c097df$a90d82b0$b50cd63f@amr.corp.intel.com>
On Thu, Feb 15, 2001 at 10:14:01PM -0800, Tim Prince wrote:
>
> It's still a problem on linux as well as cygwin. I know that cygwin is not
> a supported target, and I'm grateful to the extent that people have helped
> make it work. The problem is the same old one which started early in
> gcc-2.96 with the g77 sequence (all typed double precision). It has always
> worked correctly in the 2.95 series.
>
> if(x /= y)then
> if( x * y >=0)then
> a=abs(x)
> b=abs(y)
> c=max(a,b)
> d=min(a,b)
> w=1-d/c
> else
> w=1
> endif
> else
> w=0
> endif
>
> which, when compiled with g77-2.97 -O, -Os, or -O2, along with the
> combination -march=pentiumpro -ffast-math, sets w to 0., regardless of the
> values of x and y.
Okay, I can now reproduce this. There's a complete Fortran test case
appended (requires user input). It looks like a general problem to
me. Jan, this appears to be right in your corner: it's a problem with
reg-stack and conditional moves.
Here is the code generated for the innermost if clause by the gcc-3.0
branch as of earlier today, at -Os -march=pentiumpro -ffast-math. On
entry to this block, x and y are in %st(0) and %st(1) respectively.
fabs
fxch %st(1)
fabs
fcomi %st(1), %st
fld %st(0)
fcmovbe %st(2), %st
fxch %st(1)
*** fcmovbe %st(2), %st
fstp %st(2)
fdivrp %st, %st(1)
fsubrl .LC3 ; constant 1.0D
fstpl -24(%ebp)
Let's say x and y are 12. and 23. Top of stack is at right.
fabs 12 -- 12
fxch %st(1) 23 12 -- 12 23
fabs 23 -- 23
fcomi %st(1),%st --
fld %st(0) 12 23 -- 12 23 23
fcmovbe %st(2),%st --
fxch %st(1) --
fcmovbe %st(2),%st --
fstp %st(2) 12 23 23 -- 23 23
fdivrp %st, %st(1) 23 23 -- 1
fsubrl .LC3 1 -- 0
If I swap x and y (23 and 12), we get instead
fld %st(0) 23 12 -- 23 12 12
fcmovbe %st(2),%st 23 12 12 -- 23 12 23
fxch %st(1) 12 23 -- 23 12
fcmovbe %st(2),%st 23 23 12 -- 23 23 23
fstp %st(2) 23 23 23 -- 23 23
The intended result of the stack shuffle is to put 'd' at %st(0) and
'c' at %st(1): fdivrp %st,%st(1) means divide %st(0) by %st(1). We
can get this right by changing one instruction: the one I marked with
three stars. It needs to be 'fcmovnbe' (not bigger or equal). This
is reg-stack's fault. Right before reg-stack, the insns which will
become fcmovs are
(insn 208 228 211 {*movdfcc_1}
(set (reg:DF 10 st(2))
(if_then_else:DF (ge (reg:CCFP 17 flags) 0)
(reg:DF 8 st(0))
(reg:DF 10 st(2)))))
(insn 211 208 115 {*movdfcc_1}
(set (reg:DF 9 st(1))
(if_then_else:DF (unle (reg:CCFP 17 flags) 0)
(reg:DF 8 st(0))
(reg:DF 9 st(1)))))
After reg-stack, they are instead
(insn 208 228 270 {*movdfcc_1}
(set (reg:DF 8 st(0))
(if_then_else:DF (le (reg:CCFP 17 flags) 0)
(reg:DF 10 st(2))
(reg:DF 8 st(0)))))
(insn 211 270 271 {*movdfcc_1}
(set (reg:DF 8 st(0))
(if_then_else:DF (unle (reg:CCFP 17 flags) 0)
(reg:DF 10 st(2))
(reg:DF 8 st(0)))))
Notice how the 'ge' in the first insn has mysteriously become an 'le'
after reg-stack. It is NOT trying to reverse the sense of the
comparison here; it has got confused while trying to reorder operands
to fit the 387 register stack. fcmov can only move to top-of-stack.
N.B. 'unle' means 'UNordered less or equal' not 'not less or equal'.
That should be enough analysis for someone to find the bug on.
zw
program test
implicit none
double precision a,b,c,d,w,x,y
read *,x,y
if(x /= y)then
if( x * y >=0)then
a=abs(x)
b=abs(y)
c=max(a,b)
d=min(a,b)
w=1-d/c
else
w=1
endif
else
w=0
endif
print *,w
end