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

            Bug ID: 128155
           Summary: arm: wrong code: _Sat unsigned _Accum multiplication
                    returns 0 instead of saturating to the maximum
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: yuki.tanaka at defaultcase dot de
  Target Milestone: ---

typedef _Sat unsigned _Accum T;

__attribute__((noipa)) T a(T x, T y)
{
  return x * y;
}

int main(void)
{
  if (a(50000uk, 50000uk) != __UACCUM_MAX__)
    __builtin_abort();
  return 0;
}

$ arm-none-eabi-gcc -O2 -march=armv7-a -mthumb --specs=rdimon.specs
-Wl,-Ttext-segment=0x400000 usmul.c -o usmul
$ qemu-arm ./usmul; echo $?
1

50000 * 50000 is larger than the maximum of _Sat unsigned _Accum
(65535.9999847412109375), so the result must saturate to the maximum. Instead a
returns 0 and main aborts.

Compiler Explorer:
https://godbolt.org/z/W4o3Tfjav

GCC 17:
a:
        umull   r2, r3, r0, r1
        mov     r1, #0
        msr     APSR_nzcvq, r1
        usat    r3, #16, r3
        mrs     r1, APSR
        tst     r1, #1<<27
        it      ne
        sbfxne  r2, r3, #15, #1
        lsr     r0, r2, #16
        orr     r0, r0, r3, asl #16
        bx      lr

The usat applied to the high word of the 64-bit umull product treats it as
signed, so a high word of 0x80000000 or more gives 0 instead of 0xffff. GCC
16.1 emits the same code. usat is also used at -O0 and with -mcpu=cortex-m4 or
-march=armv7-a.

Reply via email to