https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111280

--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Kyrylo Tkachov <[email protected]>:

https://gcc.gnu.org/g:37c4ef28f7e636d7fca5270d1323553eb30b8e5a

commit r17-5396-g37c4ef28f7e636d7fca5270d1323553eb30b8e5a
Author: Kyrylo Tkachov <[email protected]>
Date:   Mon Oct 19 11:57:44 2026 -0700

    internal-fn: Implement the C[LT]Z value at zero for scalar calls [PR111280]

    A two-argument .CLZ (x, V) or .CTZ (x, V) is valid only if V is the
    value of the target at zero, because the expansion ignores V.  Ranger,
    match.pd and fold_const_call use V as the result for zero.  Calls with
    another V reach the expansion in four cases:

    - The host makes such calls before offload streaming.  The C front end
      folds __builtin_clzg (x, 32) to .CLZ (x, 32), and forwprop1 creates
      table based counts.  gcn defines -1 at zero, and the amdgcn offload
      compiler expands .CTZ (x, 32) to s_ff1_i32_b32, which gives -1 for 0.
    - With x86 target_clones, a TU with -mlzcnt folds
      __builtin_clzg (x, 32) to .CLZ (x, 32).  The arch=x86-64 clone
      expands it to bsr with no zero test, so f (0) does not return 32.
    - match.pd and phiopt fold x != 0 ? .CTZ (x) : CST to .CTZ (x, CST)
      for any CST if X is a _BitInt, because bitint lowering implements
      the second argument.  It does this only for _BitInt wider than
      MAX_FIXED_MODE_SIZE.  With an unsigned _BitInt(64) X,

        return x ? tab[((x & -x) * 0x03f79d71b4cb0a89uwb) >> 58] : 32;

      does not return 32 for a zero X on aarch64 and x86_64.
    - Since r17-5342 niter analysis counts the iterations of a loop that
      shifts T before the test with .CLZ (T >> 1, PREC) or
      .CTZ (T << 1, PREC).  On x86_64 without LZCNT, .CLZ expands to bsr
      with no zero test, so gcc.dg/tree-ssa/pr111280.c fails at run time.

    Jakub Jelinek proposed in PR111280 comment 4 to compare V with the value
    of the target at expansion, and to test the argument against 0 if they
    differ.  Richard Biener agreed in comment 6.  Do this for scalar calls
    in the new function expand_clz_ctz.  If C?Z_DEFINED_VALUE_AT_ZERO
    returns 1 or 2, the instruction gives a value for zero, so count first
    and then select V.  Otherwise execute the instruction only for a
    nonzero argument.  Since r17-5047 the vectorizer creates vector calls
    only with the value of the target for the vector mode, so assert that
    for vector calls.  When the first argument is constant, fold the call
    with both arguments.

    On AArch64 at -O2,

      _1 = .CLZ (x_2(D), 5);

    now expands to

            clz     w1, w0
            cmp     w0, 0
            mov     w0, 5
            csel    w0, w1, w0, ne

    instead of a single clz.  .CLZ (x_2(D), 32) is still a single clz.

    Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.

    gcc/ChangeLog:

            PR tree-optimization/111280
            * doc/ifn.texi (IFN_CLZ, IFN_CTZ): Document the second argument
            for scalar and vector calls.
            * internal-fn.cc (expand_bitquery): Use both arguments to fold a
            two-argument .CLZ or .CTZ of a constant.
            (expand_clz_ctz): New function.
            (expand_CLZ, expand_CTZ): Use it.

    gcc/testsuite/ChangeLog:

            PR tree-optimization/111280
            * gcc.dg/torture/bitint-112.c: New test.
            * gcc.dg/torture/clz-ctz-value-at-zero-1.c: New test.
            * gcc.target/aarch64/clz-ctz-value-at-zero-1.c: New test.
            * gcc.target/i386/clzg-target-clones-1.c: New test.

    Signed-off-by: Kyrylo Tkachov <[email protected]>

Reply via email to