On Tue, Jul 14, 2026 at 7:36 PM Richard Biener <[email protected]> wrote:
>
> Sorry for the delay responding ...
it's OK.
> Hmm, so this probes ahead that we can likely successfully build
> the swapped operand zero.
>
> Of course the first vect_build_slp_tree_1 is redundant (we'll
> do it again during vect_build_slp_tree before swapping).
>
> So what I had in mind was sth like (just a quick sketch, not even
> compile tested):
>
> diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
> index ff11b392305..c1d82a8907e 100644
> --- a/gcc/tree-vect-slp.cc
> +++ b/gcc/tree-vect-slp.cc
> @@ -3018,17 +3018,22 @@ out:
> }
> }
>
> - old_swap_distance = least_upthread_swappable_op_distance;
> - if (can_swap_nonmatching)
> - least_upthread_swappable_op_distance = 1;
> - else if (least_upthread_swappable_op_distance != -1U)
> - least_upthread_swappable_op_distance++;
> child = vect_build_slp_tree (vinfo, oprnd_info->def_stmts,
> &this_max_nunits,
> matches, limit,
> &this_tree_size, bst_map);
> - least_upthread_swappable_op_distance = old_swap_distance;
> - if (child != NULL)
> +
> + slp_tree saved_child = NULL;
> + if (child
> + && is_a <bb_vec_info> (vinfo)
> + && SLP_TREE_DEF_TYPE (child) == vect_external_def
> + && oprnd_info->first_dt != vect_external_def
> + && oprnd_info->first_dt != vect_constant_def
> + && can_swap_nonmatching)
> + /* Check if matches[] from above is still populated correctly.
> + I think it is. */
> + saved_child = child;
> + else if (child != NULL)
> {
> oprnd_info->def_stmts = vNULL;
> children.safe_push (child);
> @@ -3094,6 +3099,14 @@ out:
> children.safe_push (child);
> continue;
> }
> + if (saved_child)
> + {
> + /* Now un-swap stmts. matches[] should be still available
> + from above. Important so we get operand 1 back. */
> + oprnd_info->def_stmts = vNULL;
> + children.safe_push (saved_child);
> + continue;
> + }
> }
> fail:
>
I tried your idea, with some changes.
1. The returned op0 child isn't itself external in PR125800:
current commutative node
`-- op0: internal child
`-- external operand
So the `SLP_TREE_DEF_TYPE (child) == vect_external_def` check doesn't
catch it. I use the matches[] to detect the fallback and decide
whether to save child and retry swap.
2. besides saved_child, I also save tree_size, max_nunits, and the
swapped operand state, so a failed retry can be rolled back.
all these changes make the code a little complicated and feel somewhat
repetitive. do you see a cleaner way?
Both PR125567 and PR125800 tests pass. haven't run other tests.
Does this look OK?
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index 2250f6f74a1..f993589aa06 100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -1873,12 +1873,6 @@ vect_slp_linearize_chain (vec_info *vinfo,
}
}
-/* Distance from the node currently being discovered to the closest upthread
- commutative operation whose operand-zero discovery may still be fixed by
- retrying with swapped operands, or -1U if there is none. */
-
-static unsigned least_upthread_swappable_op_distance = -1U;
-
static slp_tree
vect_build_slp_tree_2 (vec_info *vinfo, slp_tree node,
vec<stmt_vec_info> stmts, unsigned int group_size,
@@ -2821,10 +2815,13 @@ out:
{
slp_tree child = nullptr;
unsigned int j;
- unsigned old_swap_distance;
bool can_swap;
bool can_swap_nonmatching;
bool *stmt_can_swap;
+ slp_tree saved_child = NULL;
+ unsigned pre_child_tree_size;
+ poly_uint64 pre_child_max_nunits;
+ bool child_from_cache;
/* We're skipping certain operands from processing, for example
outer loop reduction initial defs. */
@@ -3014,22 +3011,37 @@ out:
can_swap_nonmatching = false;
}
}
-
- old_swap_distance = least_upthread_swappable_op_distance;
- if (can_swap_nonmatching)
- least_upthread_swappable_op_distance = 1;
- else if (least_upthread_swappable_op_distance != -1U)
- least_upthread_swappable_op_distance++;
+ pre_child_tree_size = this_tree_size;
+ pre_child_max_nunits = this_max_nunits;
+ /* A successful cache hit leaves matches unchanged. */
+ child_from_cache = bst_map->get (oprnd_info->def_stmts) != NULL;
child = vect_build_slp_tree (vinfo, oprnd_info->def_stmts,
group_size, &this_max_nunits,
matches, limit,
&this_tree_size, bst_map);
- least_upthread_swappable_op_distance = old_swap_distance;
+
if (child != NULL)
{
- oprnd_info->def_stmts = vNULL;
- children.safe_push (child);
- continue;
+ if (is_a <bb_vec_info> (vinfo)
+ && !child_from_cache
+ && can_swap_nonmatching
+ && matches[0])
+ /* Use matches to detect an external operand built from scalars.
+ Save the child and retry later with swapped operands to see if
+ the build succeeds. */
+ for (j = 1; j < group_size; ++j)
+ if (!matches[j])
+ {
+ saved_child = child;
+ break;
+ }
+
+ if (!saved_child)
+ {
+ oprnd_info->def_stmts = vNULL;
+ children.safe_push (child);
+ continue;
+ }
}
/* If the SLP build for operand zero failed and operand zero
@@ -3060,6 +3072,17 @@ out:
}
while (j != group_size);
+ if (saved_child)
+ oprnd_info->def_stmts = oprnd_info->def_stmts.copy ();
+
+ bool saved_any_pattern[2]
+ = { oprnds_info[0]->any_pattern,
+ oprnds_info[1]->any_pattern };
+ unsigned retry_tree_size
+ = saved_child ? pre_child_tree_size : this_tree_size;
+ poly_uint64 retry_max_nunits
+ = saved_child ? pre_child_max_nunits : this_max_nunits;
+
/* Swap mismatched definition stmts. */
if (dump_enabled_p ())
dump_printf_loc (MSG_NOTE, vect_location,
@@ -3082,10 +3105,37 @@ out:
oprnds_info[0]->any_pattern = oprnds_info[1]->any_pattern = true;
/* And try again with scratch 'matches' ... */
bool *tem = XALLOCAVEC (bool, group_size);
- if ((child = vect_build_slp_tree (vinfo, oprnd_info->def_stmts,
- group_size, &this_max_nunits,
- tem, limit,
- &this_tree_size, bst_map)) != NULL)
+ child = vect_build_slp_tree (vinfo, oprnd_info->def_stmts,
+ group_size, &retry_max_nunits,
+ tem, limit,
+ &retry_tree_size, bst_map);
+ /* This is a speculative swap retry. Use child if it works;
+ otherwise restore the operand state and go back to saved_child. */
+ if (saved_child)
+ {
+ if (!child)
+ {
+ for (j = 0; j < group_size; ++j)
+ if (matches[j] == !swap_not_matching)
+ {
+ std::swap (oprnds_info[0]->def_stmts[j],
+ oprnds_info[1]->def_stmts[j]);
+ std::swap (oprnds_info[0]->ops[j],
+ oprnds_info[1]->ops[j]);
+ }
+ oprnds_info[0]->any_pattern = saved_any_pattern[0];
+ oprnds_info[1]->any_pattern = saved_any_pattern[1];
+ oprnd_info->def_stmts.release ();
+ children.safe_push (saved_child);
+ continue;
+ }
+
+ vect_free_slp_tree (saved_child);
+ }
+
+ this_tree_size = retry_tree_size;
+ this_max_nunits = retry_max_nunits;
+ if (child)
{
oprnd_info->def_stmts = vNULL;
children.safe_push (child);
@@ -3106,12 +3156,6 @@ fail:
/* ??? Rejecting patterns this way doesn't work. We'd have to
do extra work to cancel the pattern so the uses see the
scalar version. */
- /* Skip building vector operands from scalars while operand
- discovery may still be fixed by retrying with swapped operands. */
- && (least_upthread_swappable_op_distance != 1
- /* A first scalar stmt mismatch signals a fatal mismatch
- that the parent commutative retry cannot recover. */
- || !matches[0])
&& !is_pattern_stmt_p (stmt_info)
&& !oprnd_info->any_pattern)
{
--
Regards,
Zhongyao