Reg-stack and cmove bug
Zack Weinberg
zackw@Stanford.EDU
Fri Feb 16 01:04:00 GMT 2001
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
More information about the Gcc-bugs
mailing list