Bug 39386

Summary: [avr] different computation results for O1 and O0 executables
Product: gcc Reporter: Xuejun Yang <jxyang>
Component: targetAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal CC: anitha.boyapati, eric.weddington, gcc-bugs, gjl, hutchinsonandy, regehr, wvangulik
Priority: P3 Keywords: wrong-code
Version: 4.3.3   
Target Milestone: 4.6.2   
Host: Target: avr-*-*
Build: Known to work:
Known to fail: 4.1.2, 4.2.2, 4.3.0, 4.3.2, 4.3.3, 4.4.0, 4.5.0 Last reconfirmed: 2009-08-10 13:40:54
Attachments: mis-calculated program
Reduced testcase
Minimal testcase

Description Xuejun Yang 2009-03-05 21:17:13 UTC
This is seen on the version of avr-gcc 4.3.3 that gets built by the script that
comes with FemtoOS 0.88.

The program performs simple arithmatic and logical operations on a global variable, and store the result in register R30:R31.

If the program is compiled with "avr-gcc -mmcu=atmega128 -O0", the final result is 00. On the other hand, if the program is compiled with "avr-gcc -mmcu=atmega128 -O1", the final result is 0xFF. Compiling at O2 gives me 0xFF too.

Obviously, avr-gcc compiled the program wrong at one of the these optimization levels.

This bug is observed in avr studio 4.15.
Comment 1 Xuejun Yang 2009-03-05 21:22:12 UTC
Created attachment 17400 [details]
mis-calculated program
Comment 2 Eric Weddington 2009-03-18 17:47:31 UTC
Confirmed with gcc 4.3.2.
Comment 3 Eric Weddington 2009-03-21 16:52:47 UTC
Please try using gcc 4.4 (HEAD). Anatoly Sokolov (AVR port maintainer) has indicated to me that he does not see this bug when using HEAD/4.4.
Comment 4 Eric Weddington 2009-04-07 15:18:30 UTC
*** Bug 39635 has been marked as a duplicate of this bug. ***
Comment 5 Eric Weddington 2009-04-07 15:23:25 UTC
Adding additional version numbers in the known-to-fail field from comment in duplicate bug #39635. Still waiting to hear if bug is present in 4.4.
Comment 6 Anitha Boyapati 2009-08-10 13:27:27 UTC
Confirmed with gcc 4.4.0. Using switch -O0 with avr-gcc 4.4.0 (-mmcu=atmega128) gave the result 0 while -O1 gave 0xFF
Comment 7 Anitha Boyapati 2010-08-23 13:08:58 UTC
Created attachment 21547 [details]
Reduced testcase

Reduced testcase to reproduce the bug.
Comment 8 Anitha Boyapati 2010-08-23 13:25:18 UTC
When -O2 is enabled result is 0xFF (255) which is incorrect. The code generated for the following shift operation:

13:       	 return  (left << right);
+000000AB:   C004        RJMP      PC+0x0005      Relative jump
+000000AC:   0F22        LSL       R18            Logical Shift Left
+000000AD:   1F33        ROL       R19            Rotate Left Through Carry
+000000AE:   1F44        ROL       R20            Rotate Left Through Carry
+000000AF:   1F55        ROL       R21            Rotate Left Through Carry
+000000B0:   952A        DEC       R18            Decrement
+000000B1:   F7D2        BRPL      PC-0x05        Branch if plus
19:          g_6= func_1 (g_6, g_6);
+000000B2:   93200100    STS       0x0100,R18     Store direct to data space

'left' is stored in registers R21:R20:R19:R18 as it is typecasted to uint32_t type while 'right' is stored in R18. Because of shift logic, R18 becomes 0xFF in first iteration which is then stored.
Comment 9 Wouter van Gulik 2010-08-23 13:36:08 UTC
I already generated a simple testcase (although different)in http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39635. The comments from Andy in the linked report might be worth reproducing here
Comment 10 Georg-Johann Lay 2011-07-23 10:08:33 UTC
The problem arises from the following insns:

*ashlqi3
ashlhi3
ashlsi3

ashrqi3
ashrhi3
ashrsi3

*lshrqi3
lshrhi3
lshrsi3

For variable shifts the first constraint alternative is "=r,0,r"

avr.c:out_shift_with_cnt() reads 

  else if (register_operand (operands[2], QImode))
    {
      if (reg_unused_after (insn, operands[2]))
	op[3] = op[2];
      else
	{
	  op[3] = tmp_reg_rtx;
	  if (!len)
	    strcat (str, (AS2 (mov,%3,%2) CR_TAB));
	}
    }

so that in the very rare, pathological case of op0 = op1 = op2 and op2 is unused after the insn, wrong code gets generated. Fix is:

      if (reg_unused_after (insn, operands[2])
          && !reg_overlap_mentioned_p (operands[0], operands[2]))

or something like that.
Comment 11 Georg-Johann Lay 2011-07-23 13:48:14 UTC
Created attachment 24814 [details]
Minimal testcase

Minimal testcase that also miscompiles for newer versions of the compiler like 4.5.2 or 4.6.1.

Compiles with -Os to

shift:
	rjmp 2f
1:	lsl r24
	rol r25
2:	dec r24
	brpl 1b
	ret
Comment 12 Georg-Johann Lay 2011-07-25 15:42:00 UTC
Author: gjl
Date: Mon Jul 25 15:41:55 2011
New Revision: 176756

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=176756
Log:
	
	PR target/39386
	* config/avr/avr.c (out_shift_with_cnt): Use tmp_reg as
	shift counter for x << x and x >> x shifts.


Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/avr/avr.c
Comment 13 Georg-Johann Lay 2011-07-25 15:45:51 UTC
Author: gjl
Date: Mon Jul 25 15:45:47 2011
New Revision: 176757

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=176757
Log:
	
	PR target/39386
	Backport from mainline r176756
	2011-07-25  Georg-Johann Lay
	* config/avr/avr.c (out_shift_with_cnt): Use tmp_reg as
	shift counter for x << x and x >> x shifts.


Modified:
    branches/gcc-4_6-branch/gcc/ChangeLog
    branches/gcc-4_6-branch/gcc/config/avr/avr.c
Comment 14 Georg-Johann Lay 2011-07-25 16:08:04 UTC
Closed as FIXED.