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.

Reply via email to