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]>
