On Tue, 23 Jun 2026, Victor Do Nascimento wrote:
> Consider the following dot-product computation:
>
> uint32_t
> tcp_checksum(int n, uint8_t* data)
> {
> uint32_t sum = 0;
> for (int i=0; i<n; i+=1)
> sum += data[i] * data[i];
> return sum;
> }
>
> At present, following vectorization and the subsequent optimization
> passes, we will end up with the following GIMPLE code:
>
> vect__1 = .MASK_LOAD (vectp_data, 8B, loop_mask_1, { 0, ... });
> masked_op1_1 = VEC_COND_EXPR <loop_mask_1, vect__1, { 0, ... }>;
> vect_patt_1 = DOT_PROD_EXPR <vect__1, masked_op1_1, vectt_sum_1>;
>
> By looking at the input data going into the VEC_COND_EXPR, its mask
> and its else value, we can walk back up the USE-DEF chain to see
> whether the source of the input data shares the same mask and else
> values. If so, we can safely remove the VEC_COND_EXPR statement from
> the cfg, thus resulting in the more optimal variant:
>
> vect__1 = .MASK_LOAD (vectp_data, 8B, loop_mask_1, { 0, ... });
> vect_patt_1 = DOT_PROD_EXPR <vect__1, vect__1, vectt_sum_1>;
>
> Regression testes on AArch64 & x86_64, no new regressions.
I think this belongs into forwprop. We might be able to do
a match.pd pattern as well, sth like
(simplify
(vec_cond @0 @1 @2)
(if (same_mask_and_else_value (@0, @1, @2))
@1))
where an external same_mask_and_else_value function would look
at the def of @1. Similar situations can occur with
.COND_<op>
Richard.
> PR tree-optimization/111770
>
> gcc/ChangeLog:
>
> * gimple-isel.cc (stmt_is_any_masked_load_p): New.
> (stmt_is_vec_cond_expr_zeroing): Likewise.
> (redundant_condexpr_removal): Likewise.
> (pass_gimple_isel::execute): Add call to
> `redundant_condexpr_removal'.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/vect/vect-cond-dot.c: New.
> ---
> gcc/gimple-isel.cc | 78 +++++++++++++++++++++++
> gcc/testsuite/gcc.dg/vect/vect-cond-dot.c | 21 ++++++
> 2 files changed, 99 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/vect/vect-cond-dot.c
>
> diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
> index b193e27b183..aa020f402e0 100644
> --- a/gcc/gimple-isel.cc
> +++ b/gcc/gimple-isel.cc
> @@ -1343,6 +1343,82 @@ gimple_isel_builtin_call (gcall *call,
> gimple_stmt_iterator *gsi)
> }
> }
>
> +static internal_fn
> +stmt_is_any_masked_load_p (gimple *stmt)
> +{
> + if (stmt && is_gimple_call (stmt) && gimple_call_internal_p (stmt))
> + {
> + internal_fn ifn = gimple_call_internal_fn (stmt);
> + if (internal_load_fn_p (gimple_call_internal_fn (stmt))
> + && internal_fn_mask_index (ifn) >= 0)
> + return ifn;
> + }
> + return IFN_LAST;
> +}
> +
> +/* Ensure we have a statement of the form:
> + VEC_COND_EXPR <mask, then_val, { 0, ... }>
> + If the else value of STMT is a zero initializer, return the then value,
> + returning NULL_TREE otherwise. */
> +static tree
> +stmt_is_vec_cond_expr_zeroing (gimple *stmt)
> +{
> + if (is_gimple_assign (stmt))
> + {
> + enum tree_code code = gimple_assign_rhs_code (stmt);
> + if (code == VEC_COND_EXPR)
> + {
> + tree then_val = gimple_assign_rhs2 (stmt);
> + tree else_val = gimple_assign_rhs3 (stmt);
> + if (initializer_zerop (else_val))
> + return then_val;
> + }
> + }
> + return NULL_TREE;
> +}
> +
> + /* Remove redundant VEC_COND_EXPR statments.
> + Consider the following set of statements:
> +
> + vect__1 = .MASK_LOAD (vectp_data, 8B, loop_mask_1, { 0, ... });
> + masked_op1_1 = VEC_COND_EXPR <loop_mask_1, vect__1, { 0, ... }>;
> + vect_patt_1 = DOT_PROD_EXPR <vect__1, masked_op1_1 vect
> +
> + By looking at the input data going into the VEC_COND_EXPR, we can walk
> + back up the USE-DEF chain to see whether the source of the input data
> + shares the same mask and else values. If so, we can safely remove the
> + VEC_COND_EXPR statement from the cfg. */
> +static void
> +redundant_condexpr_removal (gimple_stmt_iterator &gsi)
> +{
> +
> + gimple *vcond_stmt = *gsi;
> + tree then_val = stmt_is_vec_cond_expr_zeroing (vcond_stmt);
> + if (then_val && TREE_CODE (then_val) == SSA_NAME)
> + {
> + gimple *then_defn = SSA_NAME_DEF_STMT (then_val);
> + internal_fn then_ifn = stmt_is_any_masked_load_p (then_defn);
> + if (then_ifn != IFN_LAST)
> + {
> + tree vcond_mask, load_mask, load_else;
> + vcond_mask = gimple_assign_rhs1 (vcond_stmt);
> + load_mask = gimple_call_arg (then_defn,
> + internal_fn_mask_index (then_ifn));
> + load_else = gimple_call_arg (then_defn,
> + internal_fn_else_index (then_ifn));
> + if (vcond_mask == load_mask && initializer_zerop (load_else))
> + {
> + tree dest = gimple_assign_lhs (gsi_stmt (gsi));
> + replace_uses_by (dest, then_val);
> + gimple *def_stmt = gsi_stmt (gsi);
> + gsi_remove (&gsi, true);
> + release_defs (def_stmt);
> + }
> + }
> + }
> +}
> +
> +
> /* Iterate all gimple statements and perform pre RTL expansion
> GIMPLE massaging to improve instruction selection. */
>
> @@ -1360,6 +1436,8 @@ pass_gimple_isel::execute (struct function *fun)
> /* Give the target first try at replacing the instruction. */
> cfg_changed |= targetm.instruction_selection (fun, &gsi);
>
> + redundant_condexpr_removal (gsi);
> +
> /* Pre-expand VEC_COND_EXPRs to .VCOND* internal function
> calls mapping to supported optabs. */
> gimple *g = gimple_expand_vec_cond_expr (&gsi);
> diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-dot.c
> b/gcc/testsuite/gcc.dg/vect/vect-cond-dot.c
> new file mode 100644
> index 00000000000..7b6d920c9e5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/vect/vect-cond-dot.c
> @@ -0,0 +1,21 @@
> +/* { dg-do compile } */
> +/* { dg-additional-options "-fdump-tree-optimized" } */
> +/* { dg-require-effective-target vect_masked_load } */
> +#include <stdint.h>
> +
> +#define CHECK_DOT(IN, OUT) \
> +OUT check_dot_##OUT(int n, IN* data) { \
> + OUT sum = 0; \
> + for (int i=0; i<n; i+=1) { \
> + sum += data[i] * data[i]; \
> + } \
> + return sum; \
> +}
> +
> +CHECK_DOT (uint8_t, uint32_t);
> +CHECK_DOT (int8_t, int32_t);
> +CHECK_DOT (int16_t, int64_t);
> +
> +/* { dg-final { scan-tree-dump-times {vectorized 1 loops} 3 "vect" } } */
> +/* { dg-final { scan-tree-dump-times {[\n\r]\s*(vect__[^ \t=]+) =
> \.MASK_LOAD \([a-zA-Z0-9_.]+, [0-9]+B, (loop_mask_[0-9]+), \{ 0, \.\.\.
> \}\);.*[\n\r]\s+masked_op[^ \t=]+ = VEC_COND_EXPR <\2, \1, \{ 0, \.\.\. \}} 9
> "vect" { target aarch64*-*-* } } } */
> +/* { dg-final { scan-tree-dump-not {VEC_COND_EXPR} "optimized" } } */
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)