https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127239
Bug ID: 127239
Summary: Missed optimization: same-width unsigned casts prevent
folding `X % Y < Y`
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: mikaseianatsu at proton dot me
Target Milestone: ---
GCC fails to simplify a remainder comparison when two known-nonnegative values
are converted from int to unsigned before the comparison. In the testcase
below, every defined execution satisfies
0 <= aa % bb < bb
so the function can be folded to return 1.
Testcase:
int
f (unsigned short a, unsigned short b)
{
int aa = a;
int bb = b;
int c = aa % bb;
return (unsigned) c < (unsigned) bb;
}
The issue reproduces with GCC 17 trunk at -O1, -O2, and -O3.
For example:
gcc -O2 -Wall -Wextra -S -fdump-tree-optimized test.c
Actual result:
GCC keeps the remainder and comparison:
aa_5 = (int) a_4(D);
bb_7 = (int) b_6(D);
c_8 = aa_5 % bb_7;
c.0_1 = (unsigned int) c_8;
bb.1_2 = (unsigned int) b_6(D);
_3 = c.0_1 < bb.1_2;
_9 = (int) _3;
return _9;
On x86-64 this produces:
movzwl %di, %eax
movzwl %si, %esi
cltd
idivl %esi
xorl %eax, %eax
cmpl %esi, %edx
setb %al
ret
Expected result:
For every defined execution, bb != 0 and both operands are nonnegative.
Therefore,
0 <= aa % bb < bb
Converting them to unsigned does not change their values or ordering, so the
expression can be folded to:
return 1;
On x86-64, this would eliminate the integer division and reduce to:
movl $1, %eax
ret
GCC already has a match.pd optimization for the direct nonnegative X % Y < Y
form, but the intervening same-width signed-to-unsigned conversions prevent
that fold from matching here.
Compiler:
Target: x86_64-pc-linux-gnu
Configure options: --enable-languages=c,c++ --disable-bootstrap
--disable-multilib --enable-checking=release
gcc version 17.0.0 20260827 (experimental) (GCC)
GCC source: g:d38b7b3