On 9/7/26 10:21 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: Extract 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.
Changes since v3:
- Fixed the x86 build failure reported by Jeff Law in v3 by using mode
instead of op_mode when calling CLZ_DEFINED_VALUE_AT_ZERO and
CTZ_DEFINED_VALUE_AT_ZERO.
- Bootstrapped and regression tested on x86.
2026-09-07 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.
Looking at this again after some time away. This does address the build
failure. Thanks!
However I'm concerned about the behavior for CLZ/CTZ on targets that do
not provide a well defined value for that case. It seems like we should
be making a more conservative assumption like the original
implementation did. Let's take CLZ as an example:
case CLZ:
- /* If CLZ has a known value at zero, then the nonzero bits are
- that value, plus the number of bits in the mode minus one.
- If we have a different operand mode, don't try to get nonzero
- bits as currently nonzero is not a poly_int. */
- if (op_mode == mode
- && CLZ_DEFINED_VALUE_AT_ZERO (mode, nonzero))
-nonzero
- |= (HOST_WIDE_INT_1U << (floor_log2 (mode_width))) - 1;
- else
-nonzero = -1;
- break;
Note how if the modes mismatched or CTZ_DEFINED_VALUE_AT_ZERO was false,
then we assumed any bits could be nonzero. We probably want to preserve
that kind of conservatism here for clz/ctz. I don't think it's needed
for popcount.
The old code also fell back to the conservative result when the modes
mismatched. That fallback seems to have disappeared. If it's not
needed, please indicate why.
Along the same lines, don't we need similar handling for sign bit copies?
jeff
CONFIDENTIALITY: The contents of this e-mail are confidential and
intended only for the above addressee(s). If you are not the intended
recipient, or the person responsible for delivering it to the intended
recipient, copying or delivering it to anyone else or using it in any
unauthorized manner is prohibited and may be unlawful. If you receive
this e-mail by mistake, please notify the sender and the systems
administrator at [email protected] immediately.