On 8/13/2026 4:03 AM, Milan Tripkovic wrote:
This patch fixes PR123905, where the compiler generates redundant sign
Extensions and bit masks on RISC-V when using __builtin_clz, __builtin_ctz
and __builtin_popcount.
To solve this, the patch introduces the following changes based on
Jeff's suggestions:
ext-dce.cc: Extracted the redundant sign extension check into a new helper
function is_trivially_redundant_extension. This uses num_sign_bit_copies
evaluated in the outer mode to check if the extension is already
redundant,
which allows combine to eliminate it instead of ext-dce leaving SUBREGs
that are harder to optimize.
rtlanal.cc: Improve nonzero_bits1 and num_sign_bit_copies1 so that
they recognize that the results of CLZ, CTZ, and POPCOUNT are
bounded by floor_log2 (bitwidth) + 1 bits, meaning that all higher
bits are known to be zero.
ASM Before:
clzw a0,a0
andn t0,a0,a1
sext.w a0,t0
ret
ASM After:
clzw a0,a0
andn a0,a0,a1
ret
Additionally, this patch includes a new test case
gcc.target/riscv/pr123905.c
which covers several scenarios: AND, ANDN, and combinations like clz & ctz
and clz & ctz & popcount.
2026-08-13 Milan Tripkovic <[email protected]>
gcc/ChangeLog:
* ext-dce.cc (is_trivially_redundant_extension): New helper
function.
(ext_dce_try_optimize_extension): Keep trivially redundant
SIGN_EXTENDs.
* rtlanal.cc (nonzero_bits1): Handle POPCOUNT/CLZ/CTZ.
(num_sign_bit_copies1): Add CLZ/CTZ/POPCOUNT handling.
gcc/testsuite/ChangeLog:
* gcc.target/riscv/pr123905.c: New test case.
Conceptually it looks good. Since this is a generic change, it needs to
be bootstrapped and regression tested on at least one major platform.
Most folks use x86_64 or aarch64.
I threw it into my tester to do some broader testing and it failed to
build on x86_64:
x86_64-linux-gnu-g++ -std=c++14 -fno-PIE -c -DIN_GCC
-fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall
-Wno-error=narrowing -Wwrite-strings -Wcast-qual -Wno-format
-Wmissing-format-attribute -Wconditionally-supported
-Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros
-Wno-overlength-strings -DHAVE_CONFIG_H -fno-PIE -I. -I.
-I../../../gcc/gcc -I../../../gcc/gcc/. -I../../../gcc/gcc/../include
-I../../../gcc/gcc/../libcpp/include -I../../../gcc/gcc/../libcody
-I../../../gcc/gcc/../libdecnumber
-I../../../gcc/gcc/../libdecnumber/bid -I../libdecnumber
-I../../../gcc/gcc/../libbacktrace -o rtlanal.o -MT rtlanal.o -MMD
-MP -MF ./.deps/rtlanal.TPo ../../../gcc/gcc/rtlanal.cc
In file included from ./tm.h:29,
from ../../../gcc/gcc/backend.h:28,
from ../../../gcc/gcc/rtlanal.cc:24:
../../../gcc/gcc/rtlanal.cc: In function 'long unsigned int
nonzero_bits1(const_rtx, scalar_int_mode, const_rtx, machine_mode,
long unsigned int)':
../../../gcc/gcc/config/i386/i386.h:3059:37: error: cannot convert
'poly_uint16' {aka 'poly_int<1, short unsigned int>'} to 'long int' in
assignment
3059 | ((VALUE) = GET_MODE_BITSIZE (MODE), TARGET_LZCNT ? 2 : 0)
| ~~~~~~~~~~~~~~~~~^~~~~~
| |
| poly_uint16 {aka
poly_int<1, short unsigned int>}
../../../gcc/gcc/rtlanal.cc:5238:32: note: in expansion of macro
'CLZ_DEFINED_VALUE_AT_ZERO'
5238 | if (code == CLZ && CLZ_DEFINED_VALUE_AT_ZERO
(op_mode, val_at_zero))
| ^~~~~~~~~~~~~~~~~~~~~~~~~
../../../gcc/gcc/config/i386/i386.h:3057:37: error: cannot convert
'poly_uint16' {aka 'poly_int<1, short unsigned int>'} to 'long int' in
assignment
3057 | ((VALUE) = GET_MODE_BITSIZE (MODE), TARGET_BMI ? 2 : 0)
| ~~~~~~~~~~~~~~~~~^~~~~~
| |
| poly_uint16 {aka
poly_int<1, short unsigned int>}
../../../gcc/gcc/rtlanal.cc:5241:20: note: in expansion of macro
'CTZ_DEFINED_VALUE_AT_ZERO'
5241 | && CTZ_DEFINED_VALUE_AT_ZERO (op_mode,
val_at_zero))
| ^~~~~~~~~~~~~~~~~~~~~~~~~
make: *** [Makefile:1218: rtlanal.o] Error 1
So there's still a bit of work to do, but it looks to be on the right
track to me.
jeff