jk@UUNET.UU.NET (Juergen Keil) (12/17/89)
I am using a SUN3/50, SunOS 3.5 and gcc 1.36 configured for sun3-os3. gcc 1.36 produces incorrect assembly code when compiling the following program with -O and -fstrength-reduce (reduced from a much larger program to one function): -- error1.c ------------ unsigned short x; unsigned short y; int arr[100]; main() { int n; int s; arr[x] = 1; n = x; s = y; while (n < y) { s += arr[n]; n++; } f(s); } ------------------------ $ gcc -v -S -O -fstrength-reduce error1.c /usr/local/lib/gcc-cpp -v -undef -D__GNUC__ -Dmc68000 -Dsun -Dunix -D__mc68000__ -D__sun__ -D__unix__ -D__OPTIMIZE__ -D__HAVE_68881__ -Dmc68020 error1.c /tmp/cca11466.cpp GNU CPP version 1.36 /usr/local/lib/gcc-cc1 /tmp/cca11466.cpp -quiet -dumpbase error1.c -fstrength-reduce -O -version -o error1.s GNU C version 1.36 (68k, MIT syntax) compiled by GNU C version 1.36. default target switches: -m68020 -mc68020 -m68881 -mbitfield The following assembly code is generated: #NO_APP gcc_compiled.: .text .even .globl _main _main: link a6,#0 moveml #0x3800,sp@- clrl d0 movew _x,d0 lea _arr,a0 moveq #1,d4 movel d4,a0@(d0:l:4) clrl d1 movew _x,d1 clrl d2 movew _y,d2 clrl d3 lea _arr,a0 ; <-- $a0 = &arr[0] instead of $a0 = &arr[n] !! jra L2 L4: addl a0@+,d2 movel a0,d0 addql #1,d1 L2: movew _y,d3 cmpl d1,d3 jgt L4 movel d2,sp@- jbsr _f moveml a6@(-12),#0x1c unlk a6 rts .comm _arr,400 .comm _y,2 .comm _x,2 When gcc searches the last assignment to a biv before a loop it misses assignments where only parts of the register containg the biv are modified. In this case the initialisation of n is done by first setting n to 0 and then loading the lower half of n with (the unsigned short) x. gcc misses the insn where the lower half of n is loaded and instead finds the insn where n is set to 0! I used the following patch to fix the problem: *** loop.c Sat Dec 16 18:27:47 1989 --- loop.c.orig Sat Dec 16 18:23:22 1989 *************** *** 2553,2572 **** call_seen = 1; if (GET_CODE (p) == INSN ! && GET_CODE (PATTERN (p)) == SET) { ! rtx dest = SET_DEST (PATTERN (p)); - while (GET_CODE (dest) == SUBREG - || GET_CODE (dest) == ZERO_EXTRACT - || GET_CODE (dest) == SIGN_EXTRACT - || GET_CODE (dest) == STRICT_LOW_PART) - dest = XEXP (dest, 0); - - if (GET_CODE (dest) == REG) - { - int dest_regno = REGNO (dest); - if (induct_var[dest_regno] == BASIC_INDUCT && class_struct[dest_regno]->init_insn == 0) { --- 2553,2563 ---- call_seen = 1; if (GET_CODE (p) == INSN ! && GET_CODE (PATTERN (p)) == SET ! && GET_CODE (SET_DEST (PATTERN (p))) == REG) { ! int dest_regno = REGNO (SET_DEST (PATTERN (p))); if (induct_var[dest_regno] == BASIC_INDUCT && class_struct[dest_regno]->init_insn == 0) { *************** *** 2617,2623 **** } biv_found--; - } } } else if (GET_CODE (p) == CODE_LABEL) --- 2608,2613 ---- Having fixed this bug I tried my hacked gcc on the original program but it still didn't get the assembly code right. The second bug can be reproduced by the following program: -- error2.c ---------- unsigned short x; unsigned short y; int arr[100]; main() { int n; int m; int s; for (m = 1; m < 10; m++) { arr[m] = m; arr[x] = 1; n = x; s = y; while (n < y) { s += arr[n]; n++; } f(s); } } ----------------------- $ gcc -v -S -O -fstrength-reduce error2.c gcc version 1.36 /usr/local/lib/gcc-cpp -v -undef -D__GNUC__ -Dmc68000 -Dsun -Dunix -D__mc68000__ -D__sun__ -D__unix__ -D__OPTIMIZE__ -D__HAVE_68881__ -Dmc68020 error2.c /tmp/cca11525.cpp GNU CPP version 1.36 /usr/local/lib/gcc-cc1 /tmp/cca11525.cpp -quiet -dumpbase error2.c -fstrength-reduce -O -version -o error2.s GNU C version 1.36 (68k, MIT syntax) compiled by GNU C version 1.36. default target switches: -m68020 -mc68020 -m68881 -mbitfield This time, the following assembly code is generated: #NO_APP gcc_compiled.: .text .even .globl _main _main: link a6,#0 moveml #0x3e20,sp@- moveq #1,d5 lea _arr,a2 clrl d2 L8: movel d5,a2@(d5:l:4) movew _x,d2 ; x loaded into $d2 moveq #1,d6 movel d6,a2@(d2:l:4) movel d2,d3 ; n = x (@) movew _y,d2 ; Now $d2 contains y movel d2,d4 clrl d1 lea a2@(d2:l:4),a0 ; $a0 = &arr[y] instead of &arr[x] ; should use $d3 instead of $d2 here! jra L5 L7: addl a0@+,d4 movel a0,d0 addql #1,d3 L5: movew _y,d1 cmpl d3,d1 jgt L7 movel d4,sp@- jbsr _f addqw #4,sp addql #1,d5 moveq #9,d6 cmpl d5,d6 jge L8 moveml a6@(-24),#0x47c unlk a6 rts .comm _arr,400 .comm _y,2 .comm _x,2 The problem in this case is, that the strength reduction of the inner loop possibly extends the lifetime of a register that is used to initialize the biv ($d2) up to the beginning of the loop, but the information in the array regno_last_uid is not updated. The strength reduction of the outer loop considers $d2 as unused after (@) und re-uses $d2 for optimizing another load of an unsigned short variable. This can be fixed by applying the following patch to loop.c (a better patch would probably update the lifetime information for the register involved but I haven't figured out yet how to do this): *** loop2.c Sat Dec 16 19:30:54 1989 --- loop.c Sat Dec 16 18:27:47 1989 *************** *** 2584,2590 **** /* Save value if it is a constant or register. */ if (CONSTANT_P (src) - #if 0 || (GET_CODE (src) == REG /* Don't try to use a value in a hard reg across a call which clobbers it. */ --- 2584,2589 ---- *************** *** 2591,2599 **** && ! (REGNO (src) < FIRST_PSEUDO_REGISTER && call_used_regs[REGNO (src)] && call_seen) ! && ! reg_set_between_p (src, p, loop_start)) ! #endif ! ) { class_struct[dest_regno]->initial_value = src; --- 2590,2596 ---- && ! (REGNO (src) < FIRST_PSEUDO_REGISTER && call_used_regs[REGNO (src)] && call_seen) ! && ! reg_set_between_p (src, p, loop_start))) { class_struct[dest_regno]->initial_value = src; -- Juergen Keil jk@tools.uucp ...!{uunet,mcsun}!unido!tools!jk