https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126555
Bug ID: 126555
Summary: [AArch64] wrong-code: SUBP used for subtraction of
zero-extended 56-bit integers
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: kareem.h.omar at gmail dot com
Target Milestone: ---
Keywords: AArch64, wrong-code, memtag, subp, backend
The AArch64 backend incorrectly emits SUBP for subtraction of two unsigned
64-bit values after each value has been masked to 56 bits when the 'memtag'
instruction set extensions are enabled (i.e. -mcpu=cortex-a150 or
-mcpu=neoverse-n2).
Minimal repro:
unsigned long f(unsigned long a, unsigned long b)
{
return (a & 0xFFFFFFFFFFFFFF) - (b & 0xFFFFFFFFFFFFFF);
}
Compile:
gcc -O3 -mcpu=neoverse-n2
Codegen:
subp x0, x0, x1
ret
View it live:
https://godbolt.org/z/6vxYz37de
The bug:
SUBP performs *sign*-extension of its operands from 56 to 64 bits, not
*zero*-extension, producing an incorrect result for some inputs.
This is of particular importance in cryptographic code which somewhat commonly
manipulates 56-bit limbs of multiprecision integers, and is how this bug was
encountered in the wild.
Expected behavior:
The subtraction must be performed on zero-extended, not sign-extended,
operands.
Versions:
All versions from gcc10.3 to current gcc trunk (10.3 is earliest I can find
with support for -mcpu=neoverse-n2).
Workaround:
Appending +nomemtag to the -mcpu string prevents the generation of the SUBP
instruction.