On Tue, Sep 29, 2026 at 1:32 PM Stefan Schulze Frielinghaus
<[email protected]> wrote:
>
> From: Stefan Schulze Frielinghaus <[email protected]>
>
> For clz/ctz, scalar and vectorized defined-at-zero values may differ.
> Statements of the form
> B = (A != 0) ? OP : N
> may still be vectorized without a zero test in case the defined-at-zero
> value for vector modes equals N.
In light of the previous comment this doesn't seem correct iff there
would be different answers for different vector types. So I fear
this needs to be handled after vectorization as generic folding
pattern on GIMPLE (forgoing the eventual vectorization cost benefit)
Richard.
> gcc/ChangeLog:
>
> * tree-vect-patterns.cc
> (vect_recog_popcount_clz_ctz_ffs_pattern): Fold zero test for
> clz/ctz and vector modes.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/s390/clz-ctz-1.c: Remove zero test for ctzv4si_z13.
> ---
> gcc/testsuite/gcc.target/s390/clz-ctz-1.c | 8 +--
> gcc/tree-vect-patterns.cc | 67 +++++++++++++++++++++++
> 2 files changed, 69 insertions(+), 6 deletions(-)
>
> diff --git a/gcc/testsuite/gcc.target/s390/clz-ctz-1.c
> b/gcc/testsuite/gcc.target/s390/clz-ctz-1.c
> index 4d3fc148a0a..e97772143b0 100644
> --- a/gcc/testsuite/gcc.target/s390/clz-ctz-1.c
> +++ b/gcc/testsuite/gcc.target/s390/clz-ctz-1.c
> @@ -267,15 +267,11 @@ v8hi ctzv8hi_z13 (v8hi x)
> return x;
> }
>
> -/* Prior z17 we do not have a defined value for scalar zero. Although, value
> - at zero is defined for vector modes, this isn't fixed up by vectorizer.
> +/* Prior z17 we do not have a defined zero value for scalars. Verify that
> + vectorizer still recognizes that vctz has a defined zero value.
>
> ** ctzv4si_z13:
> -** vzero (%v[0-9]+)
> -** vceqf (%v[0-9]+),%v24,\1
> -** vgm (%v[0-9]+),26,26,2
> ** vctzf %v24,%v24
> -** vsel %v24,\3,%v24,\2
> ** br %r14
> */
>
> diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
> index 38f65c0dafa..41d63de0b68 100644
> --- a/gcc/tree-vect-patterns.cc
> +++ b/gcc/tree-vect-patterns.cc
> @@ -2151,6 +2151,50 @@ vect_recog_popcount_clz_ctz_ffs_pattern (vec_info
> *vinfo,
> if (!last_stmt)
> return NULL;
> tree_code code = gimple_assign_rhs_code (last_stmt);
> +
> + /* For clz/ctz, scalar and vectorized defined-at-zero values may differ.
> For
> + scalars we might end up here with LAST_STMT of the form
> + B = (A != 0) ? op_result : N
> + if there is no defined-at-zero value or the value differs from N. Still
> + we might vectorize here without a zero test, if the defined-at-zero
> value
> + equals N for the vector mode.
> +
> + Here we only look through the COND_EXPR. The test whether the
> vectorized
> + defined-at-zero value equals N is done later on. */
> + gassign *outer_cond_stmt = NULL;
> + tree outer_cond_ne_op0 = NULL;
> + if (code == COND_EXPR)
> + {
> + tree cond = gimple_assign_rhs1 (last_stmt);
> + tree true_val = gimple_assign_rhs2 (last_stmt);
> + tree false_val = gimple_assign_rhs3 (last_stmt);
> +
> + if (TREE_CODE (false_val) != INTEGER_CST)
> + return NULL;
> +
> + if (TREE_CODE (cond) != SSA_NAME)
> + return NULL;
> + gimple *cond_def = SSA_NAME_DEF_STMT (cond);
> + if (!is_gimple_assign (cond_def)
> + || gimple_assign_rhs_code (cond_def) != NE_EXPR)
> + return NULL;
> + outer_cond_ne_op0 = gimple_assign_rhs1 (cond_def);
> + tree outer_cond_ne_op1 = gimple_assign_rhs2 (cond_def);
> + if (TREE_CODE (outer_cond_ne_op0) != SSA_NAME
> + || !integer_zerop (outer_cond_ne_op1))
> + return NULL;
> +
> + if (TREE_CODE (true_val) != SSA_NAME || !has_single_use (true_val))
> + return NULL;
> + gassign *inner = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (true_val));
> + if (!inner)
> + return NULL;
> +
> + outer_cond_stmt = last_stmt;
> + last_stmt = inner;
> + code = gimple_assign_rhs_code (last_stmt);
> + }
> +
> if (!CONVERT_EXPR_CODE_P (code))
> return NULL;
>
> @@ -2172,6 +2216,10 @@ vect_recog_popcount_clz_ctz_ffs_pattern (vec_info
> *vinfo,
> {
> int val;
> CASE_CFN_POPCOUNT:
> + /* Bail out if there is an outer statement which means we are looking
> for
> + clz/ctz pattern. */
> + if (outer_cond_stmt)
> + return NULL;
> ifn = IFN_POPCOUNT;
> break;
> CASE_CFN_CLZ:
> @@ -2197,6 +2245,10 @@ vect_recog_popcount_clz_ctz_ffs_pattern (vec_info
> *vinfo,
> return NULL;
> break;
> CASE_CFN_FFS:
> + /* Bail out if there is an outer statement which means we are looking
> for
> + clz/ctz pattern. */
> + if (outer_cond_stmt)
> + return NULL;
> ifn = IFN_FFS;
> break;
> default:
> @@ -2215,6 +2267,12 @@ vect_recog_popcount_clz_ctz_ffs_pattern (vec_info
> *vinfo,
> if (!rhs_origin)
> return NULL;
>
> + /* Verify that for
> + B = (A != 0) ? .CLZ (C) : N
> + A equals C. */
> + if (outer_cond_stmt && outer_cond_ne_op0 != rhs_oprnd)
> + return NULL;
> +
> /* Input and output of .POPCOUNT should be same-precision integer. */
> if (TYPE_PRECISION (unprom_diff.type) != TYPE_PRECISION (lhs_type))
> return NULL;
> @@ -2339,6 +2397,15 @@ vect_recog_popcount_clz_ctz_ffs_pattern (vec_info
> *vinfo,
> && CTZ_DEFINED_VALUE_AT_ZERO (TYPE_MODE (vec_type),
> val) == 2)
> arg2 = build_int_cst (integer_type_node, val);
> +
> + /* For pattern clz/ctz, reaching this point we have
> + B = (A != 0) ? .CLZ (A) : N
> + Now verify that the defined-at-zero value equals N. */
> + if (outer_cond_stmt
> + && (arg2 == NULL
> + || !tree_int_cst_equal (gimple_assign_rhs3 (outer_cond_stmt),
> arg2)))
> + return NULL;
> +
> if (arg2)
> pattern_stmt = gimple_build_call_internal (ifn, 2, unprom_diff.op, arg2);
> else
> --
> 2.55.0
>