https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109236

            Bug ID: 109236
           Summary: [avr] Invalid code of signed 16-bit compare
                    optimization
           Product: gcc
           Version: 12.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: gandalf at winds dot org
  Target Milestone: ---

I'm seeing incorrect code when -O1 or higher is used on AVR (atmega1284p). The
compiler generates code that compares "x" to "y", instead of performing the
subtraction and comparing against 0. This subtraction matters since "x" and "y"
are signed 16-bit variables, and the math in the expression "x-y <= 0" stays as
16-bit in AVR (as opposed to being promoted to 32-bit ints in e.g. x86).

gcc -v:

Using built-in specs.
Reading specs from /usr/local/avr/lib/gcc/avr/12.2.0/device-specs/specs-avr2
COLLECT_GCC=avr-gcc
COLLECT_LTO_WRAPPER=/usr/local/avr/libexec/gcc/avr/12.2.0/lto-wrapper
Target: avr
Configured with: ../configure --target=avr --prefix=/usr/local/avr
--disable-nls --enable-languages=c --disable-bootstrap --disable-libssp
Thread model: single
Supported LTO compression algorithms: zlib zstd
gcc version 12.2.0 (GCC)

Sample code:

_Bool compare(short x, short y)
{
  return (x-y <= 0);
}

Compiled using: gcc -O1 -mmcu=atmega1284p -c -o test.o test.c

ASM result:

00000000 <compare>:
   0:   9c 01           movw    r18, r24
   2:   81 e0           ldi     r24, 0x01       ; 1
   4:   62 17           cp      r22, r18        <---- X compared directly to Y;
no subtraction
   6:   73 07           cpc     r23, r19
   8:   04 f4           brge    .+0             ; 0xa <compare+0xa>
   a:   80 e0           ldi     r24, 0x00       ; 0

0000000c <.L2>:
   c:   08 95           ret

I instead expect to see a subtraction between r22:23 and r18:19, then a compare
against r1 (zero).

The following testcase causes an incorrect computation:

compare(-30737, 24799) returns 1 on AVR; expected 0.  (-30737 - (24799)) as
signed 16-bit = 10000, which is not <= 0.

For the record, changing the function to "return ((short)(x-y) <= 0)" produces
the same incorrect code.

Compiling the function without optimization correctly returns 0.

It's possible other signed variable sizes besides 16-bit have the same problem,
but I did not test this.

Reply via email to