[gnu.gcc.bug] gcc 1.34 can lose increment/decrement operations

donn@wasatch.UUCP (Donn Seeley) (03/10/89)

In a certain very odd set of circumstances, gcc 1.34 can drop an
increment or decrement operation.  In our case, we discovered that rrn
would loop decrementing a count without incrementing a character
pointer; when the count is exhausted without finding a null byte in the
string, rrn aborts.  Here's a simplified example; comments in the
assembly listing indicate where the problem lies:

------------------------------------------------------------------------
Script started on Thu Mar  9 13:02:46 1989
% cat i2.c
/*
 * Stimulate the increment/decrement bug.
 *
 * The increment/decrement must live under a MEM.
 * The operand of the i/d must be a pseudo-register that doesn't get
 *	assigned a hard register in local or global allocation.
 * Going left to right through the RTL for an expression, the i/d must
 *	be followed by an instance of the i/d operand without i/d.
 */

x(ipppp, xp)
    int ****ipppp, *xp;
{
    int ***ippp, **ipp, *ip, i;

    while (ippp = *ipppp++) {
	while (ipp = *ippp++)
	    while (ip = *ipp++)
		while (i = *ip++)
		    y(i, ip, ipp, ippp, ipppp);
	*xp++ |= i;
    }
}
% cc -v -S -O -B./old- i2.c
gcc version 1.34
 /usr/local/lib/gcc-cpp -nostdinc -v -I/usr/include -undef -D__GNUC__ -Dmc68000 -Dhp300 -Dunix -D__mc68000__ -D__hp300__ -D__unix__ -D__OPTIMIZE__ -traditional -D__HAVE_FPU__ i2.c /tmp/cc012799.cpp
GNU CPP version 1.34
 ./old-cc1 /tmp/cc012799.cpp -quiet -dumpbase i2.c -O -traditional -fwritable-strings -fno-defer-pop -version -o i2.s
GNU C version 1.34 (68k, MIT syntax) compiled by GNU C version 1.34.
% cat i2.s
#NO_APP
gcc_compiled.:
.text
	.even
.globl _x
_x:
	link a6,#-4
	moveml #0x3c,sp@-
	movel a6@(8),a5
	movel a6@(12),a6@(-4)
	jra L2
L10:
	movel a5,sp@-
	movel a4,sp@-
	movel a3,sp@-
	movel a2,sp@-
	movel d0,sp@-
	jbsr _y
	addw #20,sp
L8:
	movel a2@+,d0
	jne L10
L6:
	movel a3@+,a2
	tstl a2
	jne L8
L4:
	movel a4@+,a3
	tstl a3
	jne L6
	movel a6@(-4),a0	/* xp never gets incremented */
	orl d0,a0@
L2:
	movel a5@+,a4
	tstl a4
	jne L4
	moveml a6@(-20),#0x3c00
	unlk a6
	rts
% 
------------------------------------------------------------------------

A study of the RTL for the example will show that the post-increment
disappears during reloading.  Here's what the RTL looks like prior to
reloading for the insn that contains the problematic increment:

	(insn 61 60 62 (set (mem:SI (post_inc:SI (reg/v:SI 57)))
	       (ior:SI (reg/v:SI 61)
		   (mem:SI (reg/v:SI 57)))) 137 (insn_list 59 (nil))
	   (expr_list:HI (reg/v:SI 57)
	       (nil)))

There are insufficiently many address registers to hold all the
pointers, so the variable 'xp' ends up without one and it has to be
reloaded.  The function find_reloads_address_1() is responsible for
taking care of side effects from increment and decrement operations
under a MEM when the operand of the increment or decrement is a
pseudo-register that needs to be reloaded; in this example, it must
handle (post_inc:SI (reg/v:SI 57)).  It calls push_reload() to schedule
the adjustments and reloading; push_reload() tucks away a copy of the
operand in the reload_in array.  Later, when push_reload() is called
for (reg/v:SI 57) without the post_inc, it correctly matches this with
the reload for the increment/decrement but clobbers the copy of the
operand in reload_in.  Still later, in choose_reload_targets(), the
inc_for_reload() function never gets called if the reload_in entry
doesn't contain an increment or decrement, and as a result the
operation gets lost.

The cheap way of solving the problem is to prevent push_reload() from
clobbering reload_in if the matching operand doesn't have an increment
or decrement; here's what I did:

------------------------------------------------------------------------
*** /tmp/,RCSt1012836	Thu Mar  9 13:07:59 1989
--- reload.c	Thu Mar  9 13:06:21 1989
***************
*** 380,386 ****
          reload_inmode[i] = inmode;
        if (outmode != VOIDmode)
          reload_outmode[i] = outmode;
!       if (in != 0)
          reload_in[i] = in;
        if (out != 0)
          reload_out[i] = out;
--- 380,386 ----
          reload_inmode[i] = inmode;
        if (outmode != VOIDmode)
          reload_outmode[i] = outmode;
!       if (in != 0 && GET_CODE (in) != REG)
          reload_in[i] = in;
        if (out != 0)
          reload_out[i] = out;
------------------------------------------------------------------------

The assembly code for the example turns into the following:

------------------------------------------------------------------------
% cc -v -S -O i2.c
gcc version 1.34
 /usr/local/lib/gcc-cpp -nostdinc -v -I/usr/include -undef -D__GNUC__ -Dmc68000 -Dhp300 -Dunix -D__mc68000__ -D__hp300__ -D__unix__ -D__OPTIMIZE__ -traditional -D__HAVE_FPU__ i2.c /tmp/cc012803.cpp
GNU CPP version 1.34
 /usr/local/lib/gcc-cc1 /tmp/cc012803.cpp -quiet -dumpbase i2.c -O -traditional -fwritable-strings -fno-defer-pop -version -o i2.s
GNU C version 1.34 (68k, MIT syntax) compiled by GNU C version 1.34.
% cat i2.s
#NO_APP
gcc_compiled.:
.text
	.even
.globl _x
_x:
	link a6,#-4
	moveml #0x3c,sp@-
	movel a6@(8),a5
	movel a6@(12),a6@(-4)
	jra L2
L10:
	movel a5,sp@-
	movel a4,sp@-
	movel a3,sp@-
	movel a2,sp@-
	movel d0,sp@-
	jbsr _y
	addw #20,sp
L8:
	movel a2@+,d0
	jne L10
L6:
	movel a3@+,a2
	tstl a2
	jne L8
L4:
	movel a4@+,a3
	tstl a3
	jne L6
	movel a6@(-4),a0
	addqw #4,a0		/* increment ... */
	movel a0,a6@(-4)	/* ... and update xp */
	subql #4,a0
	orl d0,a0@
L2:
	movel a5@+,a4
	tstl a4
	jne L4
	moveml a6@(-20),#0x3c00
	unlk a6
	rts
% exit
script done on Thu Mar  9 13:03:36 1989
------------------------------------------------------------------------

Rrn works now, and regressions didn't turn up any code changes except
for the one in rrn....

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