[gnu.gcc.bug] bug in strength reduction for gcc 1.36 / 1.36.9

donn@CS.UTAH.EDU (Donn Seeley) (11/29/89)

Someone on our system (HP 9000 series 300 running 4.3 BSD) noticed a
few weeks ago that 'rn' was doing odd things with their Teleray
terminal.  This didn't seem very important at the time, but a few weeks
later I looked into the problem and found that it was really a problem
in 'tset' that caused the termcap processing to screw up.
Specifically, if 'tset' was compiled with -fstrength-reduce, a routine
called cancelled() was compiled incorrectly.  I was able to extract the
code from 'tset' and reproduce the problem in a small example:

Script started on Tue Nov 28 18:46:15 1989
% cat cancelled.c
int ncap;
char delcap[128][2];

cancelled(cap)
	char *cap;
{
	register int i;

	for (i = 0; i < ncap; i++)
		if (cap[0] == delcap[i][0] && cap[1] == delcap[i][1])
			return 1;
	++ncap;
	return cap[2] == '@';
}
% cc -v -O -S -fstrength-reduce cancelled.c
gcc version 1.36.9
 /usr/src/gnu/gcc-1.36.9.utah/cpp -nostdinc -v -I/usr/include -undef -D__GNUC__ -Dmc68000 -Dmc68020 -Dhp300 -Dhp9000 -Dunix -D__mc68000__ -D__mc68020__ -D__hp300__ -D__hp9000__ -D__unix__ -D__OPTIMIZE__ -traditional -D__HAVE_FPU__ cancelled.c /usr/tmp/cc000624.cpp
GNU CPP version 1.36.9
 /usr/src/gnu/gcc-1.36.9.utah/cc1 /usr/tmp/cc000624.cpp -quiet -dumpbase cancelled.c -fstrength-reduce -O -traditional -fwritable-strings -fno-defer-pop -version -o cancelled.s
GNU C version 1.36.9 (68k, MIT syntax) compiled by GNU C version 1.36.9.
default target switches: -m68020 -mc68020 -m68881 -mbitfield
% cat cancelled.s
#NO_APP
gcc_compiled.:
.text
	.even
.globl _cancelled
_cancelled:
	link a6,#0
	moveml #0x30,sp@-
	movel a6@(8),a3
	clrl d1			# loop prologue
	cmpl _ncap,d1
	jge L7
	lea _delcap,a0
	movel a0,a1		# initialize GIV: &delcap[i][0]
	moveb a3@(1),d0
	extbl d0
	lea a0@(d0:l:2),a2	# bizarreness: &delcap[cap[1]][0] (!)
	movel _ncap,d0
	asll #1,d0
	addl a0,d0
L6:				# body of loop
	moveb a3@,d1
	cmpb a1@,d1		# correctly test using first GIV
	jne L4
	cmpl a2,a1		# test GIV against bizarreness
	jne L4
	moveq #1,d0
	jra L1
L4:
	addqw #2,a1
	cmpl a1,d0
	jgt L6
L7:				# end of loop
	addql #1,_ncap
	cmpb #64,a3@(2)
	seq d0
	moveq #1,d1
	andl d1,d0
L1:
	moveml a6@(-8),#0xc00
	unlk a6
	rts
.comm _delcap,256
.comm _ncap,4
% exit
script done on Tue Nov 28 18:47:05 1989

Here's my analysis of the situation...  The strength reduction code did
find that &delcap[i][0] was a GIV, but failed to identify a GIV in
&delcap[i][1].  Subsequent code assumes that if a comparison contains a
reference to the BIV 'i' (reg_mentioned_p()), one operand of the
comparison must literally be that BIV; this assumption probably works
most of the time because most occurrences of BIVs are either naked or
in GIVs which in turn are replaced by register references.  In the
comparison 'cap[1] == delcap[i][1]', 'cap[1]' gets treated as the BIV
and 'delcap[i][1]' is apparently treated as an invariant, hence the
bizarre substitution seen in the loop prologue.

If can_eliminate_biv_p() is hacked so that it punts when a compare does
not have a BIV as a direct operand, eliminate_biv() won't mangle the
code trying to eliminate it, so I made the following change in loop.c:

------------------------------------------------------------------------
*** /tmp/,RCSt1000773	Tue Nov 28 20:18:59 1989
--- loop.c	Tue Nov 28 20:18:45 1989
***************
*** 4821,4831 ****
  	  arg = XEXP (src, 1);
  	  arg_operand = 1;
  	}
!       else
  	{
  	  arg = XEXP (src, 0);
  	  arg_operand = 0;
  	}
  
        if (GET_CODE (arg) == CONST_INT)
  	{
--- 4821,4834 ----
  	  arg = XEXP (src, 1);
  	  arg_operand = 1;
  	}
!       else if ((GET_CODE (XEXP (src, 1)) == REG)
! 	       && (REGNO (XEXP (src, 1)) == bl->regno))
  	{
  	  arg = XEXP (src, 0);
  	  arg_operand = 0;
  	}
+       else
+ 	return 0;
  
        if (GET_CODE (arg) == CONST_INT)
  	{
------------------------------------------------------------------------

This change makes 'tset' work right, and shows no effect on any of the
regressions I've tried on other sources, including GCC itself.

I'm still a little puzzled by a few aspects of this example, though.
Why wasn't '&delcap[i][0]' pulled out by CSE from '&delcap[i][1]'?  Why
wasn't '&delcap[i][1]' considered a suitable GIV candidate?  Oh well...

Donn Seeley    University of Utah CS Dept    donn@cs.utah.edu
40 46' 6"N 111 50' 34"W    (801) 581-5668    utah-cs!donn