* [vect] Support min/max + index pattern
@ 2021-05-05 16:23 Joel Hutton
2021-05-07 11:34 ` Richard Biener
0 siblings, 1 reply; 2+ messages in thread
From: Joel Hutton @ 2021-05-05 16:23 UTC (permalink / raw)
To: gcc-patches; +Cc: Richard Biener, Richard Sandiford, Tamar Christina
[-- Attachment #1: Type: text/plain, Size: 1770 bytes --]
Hi all,
looking for some feedback on this, one thing I would like to draw attention to is the fact that this pattern requires 2 separate dependent reductions in the epilogue.
The accumulator vector for the maximum/minimum elements can be reduced to a scalar result trivially with a min/max, but getting the index from accumulator
vector of indices is more complex and requires using the position of the maximum/minimum scalar result value within the accumulator vector to create a mask.
The given solution works but it's slightly messy. vect_create_epilogue_for_reduction creates the epilogue for one vectorized scalar stmt at a time. This modification makes one
invocation create the epilogue for both related stmts and marks the other as 'done'. Alternate suggestions are welcome.
Joel
[vect] Support min/max + index pattern
Add the capability to vect-loop to support the following pattern.
for (int i = 0; i < n; i ++)
{
if (data[i] < best)
{
best = data[i];
best_i = i;
}
}
gcc/ChangeLog:
* tree-vect-loop.c (vect_reassociating_reduction_simple_p): New
function.
(vect_recog_minmax_index_pattern): New function.
(vect_is_simple_reduction): Add multi_use_reduction case.
(vect_create_epilog_for_reduction): Add minmax+index epilogue handling.
[-- Attachment #2: minmax.patch --]
[-- Type: application/octet-stream, Size: 15287 bytes --]
diff --git a/gcc/testsuite/gcc.dg/vect/vect-127.c b/gcc/testsuite/gcc.dg/vect/vect-127.c
new file mode 100644
index 0000000000000000000000000000000000000000..fc1fb6eb7bc44fdcced4e6a904e998cce845b8dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-127.c
@@ -0,0 +1,51 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fdump-tree-vect-all -fno-vect-cost-model -fdisable-tree-cunroll" } */
+#include "tree-vect.h"
+#define ARR_LEN(A) (sizeof (A)/sizeof(A[0]))
+
+void max_to_front (int *data, int n) {
+ int best_i=0, best = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (data[i] > best) {
+ best = data[i];
+ best_i = i;
+ }
+ }
+ data[best_i] = data[0];
+ data[0] = best;
+}
+
+void lastmax_to_front (int *data, int n) {
+ int best_i=0, best = 0;
+
+ for (int i = 0; i < n; i++) {
+ if (data[i] >= best) {
+ best = data[i];
+ best_i = i;
+ }
+ }
+ data[best_i] = data[0];
+ data[0] = best;
+}
+int main ()
+{
+ int a[] = {0,1,0,1,3,1,5,16,4,4,1,16,3,1};
+ max_to_front (a, ARR_LEN (a));
+ if (a[0] != 16 || a[7] != 0)
+ abort ();
+
+ int b[] = {15,0,0,0,0,0,0,16,0,15,0,0,0,0};
+ max_to_front (b, ARR_LEN (b));
+ if (b[0] != 16 || b[7] != 15)
+ abort ();
+
+ int c[] = {15,0,0,0,0,0,0,16,0,16,0,0,0,0};
+ lastmax_to_front (c, ARR_LEN (c));
+ if (c[0] != 16 || c[9] != 15)
+ abort ();
+}
+
+/* { dg-final { scan-tree-dump-times "Found minmax index pattern.*" 1 "vect" } } */
+/* { dg-final { scan-tree-dump-times "Reduce minmax index pattern.*" 1 "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-128.c b/gcc/testsuite/gcc.dg/vect/vect-128.c
new file mode 100644
index 0000000000000000000000000000000000000000..a8f403433baec5b835213fa222cd453c8c85be37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-128.c
@@ -0,0 +1,51 @@
+#include <limits.h>
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fdump-tree-vect-all -fno-vect-cost-model -fdisable-tree-cunroll" } */
+#include "tree-vect.h"
+#define ARR_LEN(A) (sizeof (A)/sizeof(A[0]))
+
+
+/* This is vectorized. */
+void min_to_front (int *data, int n) {
+ int best_i=0, best = INT_MAX;
+
+ for (int i = 0; i < n; i++) {
+ if (data[i] < best) {
+ best = data[i];
+ best_i = i;
+ }
+ }
+ data[best_i] = data[0];
+ data[0] = best;
+}
+
+/* This is not vectorized. */
+void lastmin_to_front (int *data, int n) {
+ int best_i=0, best = INT_MAX;
+
+ for (int i = 0; i < n; i++) {
+ if (data[i] <= best) {
+ best = data[i];
+ best_i = i;
+ }
+ }
+ data[best_i] = data[0];
+ data[0] = best;
+}
+
+int main ()
+{
+ int d[] = {15,-3,-2,-2,-3,-1,0,16,0,16,0,0,0,-3};
+ min_to_front (d, ARR_LEN (d));
+ if (d[0] != -3 || d[1] != 15)
+ abort ();
+
+ int e[] = {15,-3,-2,-2,-3,-1,0,16,0,16,0,0,0,-3};
+ lastmin_to_front (e, ARR_LEN (e));
+ if (e[0] != -3 || e[13] != 15)
+ abort ();
+}
+
+/* { dg-final { scan-tree-dump-times "Found minmax index pattern.*" 1 "vect" } } */
+/* { dg-final { scan-tree-dump-times "Reduce minmax index pattern.*" 1 "vect" } } */
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 2aba503fef7b195c7dd18c3ff6977add1ab73d3c..c2dc26c1f6568d1e6f068985a8e3765d69bfb27a 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -156,6 +156,112 @@ static void vect_estimate_min_profitable_iters (loop_vec_info, int *, int *);
static stmt_vec_info vect_is_simple_reduction (loop_vec_info, stmt_vec_info,
bool *, bool *);
+static bool
+vect_reassociating_reduction_simple_p (stmt_vec_info stmt_info,
+ loop_vec_info loop_info,
+ tree_code code,
+ tree *op0_out, tree *op1_out)
+{
+ gassign *assign = dyn_cast <gassign *> (stmt_info->stmt);
+ if (!assign || gimple_assign_rhs_code (assign) != code)
+ return false;
+
+ /* We don't allow changing the order of the computation in the inner-loop
+ when doing outer-loop vectorization. */
+ class loop *loop = LOOP_VINFO_LOOP (loop_info);
+ if (loop && nested_in_vect_loop_p (loop, stmt_info))
+ return false;
+
+ *op0_out = gimple_assign_rhs1 (assign);
+ *op1_out = gimple_assign_rhs2 (assign);
+ return true;
+}
+
+/*
+ Recognises a loop of the form:
+ for (int i = 0; i < n; i++) {
+ if (data[i] > best) {
+ best = data[i];
+ best_i = i;
+ }
+ }
+ Returns true if stmt_info describes the min/max assignment stmt ("best = data[i];" in the example above)
+ */
+static bool
+vect_recog_minmax_index_pattern (stmt_vec_info stmt_vinfo,
+ loop_vec_info loop_vinfo)
+{
+ tree oprnd0, oprnd1;
+ gimple *stmt = stmt_vinfo->stmt;
+ gimple *use_stmt;
+ use_operand_p use_p;
+ imm_use_iterator iter;
+
+ if (!(vect_reassociating_reduction_simple_p (stmt_vinfo, loop_vinfo,
+ MAX_EXPR,
+ &oprnd0, &oprnd1)
+ || vect_reassociating_reduction_simple_p (stmt_vinfo, loop_vinfo,
+ MIN_EXPR,
+ &oprnd0, &oprnd1)))
+ return false;
+
+ if (!((TREE_CODE (oprnd0) == SSA_NAME)
+ && (TREE_CODE (oprnd1) == SSA_NAME)
+ && (is_a <gphi *> (SSA_NAME_DEF_STMT (oprnd1))
+ || is_a <gphi *> (SSA_NAME_DEF_STMT (oprnd0)))))
+ return false;
+
+
+ tree_code tc_stmt = gimple_assign_rhs_code (stmt);
+
+ /* Starting from LAST_STMT, follow the defs of its uses in search
+ of the above pattern. */
+ FOR_EACH_IMM_USE_FAST (use_p, iter, oprnd1)
+ {
+ tree_code tc_us;
+ tree_code tc_us_cond;
+ use_stmt = USE_STMT (use_p);
+ if (stmt->bb != use_stmt->bb)
+ continue;
+ if (!is_gimple_assign (use_stmt))
+ continue;
+ tc_us = gimple_assign_rhs_code (use_stmt);
+ if (tc_us != COND_EXPR)
+ continue;
+ tree us_op0 = TREE_OPERAND (gimple_assign_rhs1 (use_stmt), 0);
+ tree us_op1 = TREE_OPERAND (gimple_assign_rhs1 (use_stmt), 1);
+ if (!((us_op0 == oprnd0 && us_op1 == oprnd1)
+ ||(us_op0 == oprnd1 && us_op1 == oprnd0)))
+ continue;
+ tree us_cond = gimple_assign_rhs1 (use_stmt);
+ tc_us_cond = TREE_CODE (us_cond);
+ tree minmax_type = TREE_TYPE (gimple_assign_lhs (stmt_vinfo->stmt));
+ tree us_type = TREE_TYPE (gimple_assign_lhs (use_stmt));
+ if (minmax_type != us_type)
+ continue;
+ if ((tc_stmt == MAX_EXPR
+ &&(tc_us_cond == GT_EXPR || tc_us_cond == LE_EXPR))
+ || (tc_stmt == MIN_EXPR
+ &&(tc_us_cond == LT_EXPR || tc_us_cond == GE_EXPR)))
+ {
+ /* stmt which saves the index corresponding to min/max */
+ stmt_vec_info ind_stmt = loop_vinfo->lookup_stmt (use_stmt);
+ ind_stmt->reduc_multi_use_related_stmt = stmt_vinfo;
+ stmt_vinfo->reduc_multi_use_related_stmt = ind_stmt;
+ ind_stmt->is_minmax_index = true;
+ stmt_vinfo->is_minmax = true;
+ if (dump_enabled_p ())
+ {
+ dump_printf (MSG_NOTE, "Found minmax index pattern:\n %G%G",
+ stmt_vinfo->stmt, ind_stmt->stmt);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Subroutine of vect_determine_vf_for_stmt that handles only one
statement. VECTYPE_MAYBE_SET_P is true if STMT_VINFO_VECTYPE
may already be set for general statements (not just data refs). */
@@ -3344,7 +3450,8 @@ needs_fold_left_reduction_p (tree type, tree_code code)
static bool
check_reduction_path (dump_user_location_t loc, loop_p loop, gphi *phi,
tree loop_arg, enum tree_code *code,
- vec<std::pair<ssa_op_iter, use_operand_p> > &path)
+ vec<std::pair<ssa_op_iter, use_operand_p> > &path,
+ bool supported_multi_use_reduction)
{
auto_bitmap visited;
tree lookfor = PHI_RESULT (phi);
@@ -3445,10 +3552,10 @@ pop:
TREE_TYPE (gimple_assign_rhs1 (use_stmt))))
;
else if (*code == ERROR_MARK)
- {
+ {
*code = use_code;
sign = TYPE_SIGN (TREE_TYPE (gimple_assign_lhs (use_stmt)));
- }
+ }
else if (use_code != *code)
{
fail = true;
@@ -3484,7 +3591,7 @@ pop:
else
cnt++;
}
- if (cnt != 1)
+ if (cnt != 1 && !supported_multi_use_reduction)
{
fail = true;
break;
@@ -3499,7 +3606,7 @@ check_reduction_path (dump_user_location_t loc, loop_p loop, gphi *phi,
{
auto_vec<std::pair<ssa_op_iter, use_operand_p> > path;
enum tree_code code_;
- return (check_reduction_path (loc, loop, phi, loop_arg, &code_, path)
+ return (check_reduction_path (loc, loop, phi, loop_arg, &code_, path, false)
&& code_ == code);
}
@@ -3638,10 +3745,14 @@ vect_is_simple_reduction (loop_vec_info loop_info, stmt_vec_info phi_info,
return def_stmt_info;
}
+ bool supported_multi_use_reduction
+ = vect_recog_minmax_index_pattern (def_stmt_info, loop_info);
+
/* If this isn't a nested cycle or if the nested cycle reduction value
is used ouside of the inner loop we cannot handle uses of the reduction
value. */
- if (nlatch_def_loop_uses > 1 || nphi_def_loop_uses > 1)
+ if (!(supported_multi_use_reduction)
+ && (nlatch_def_loop_uses > 1 || nphi_def_loop_uses > 1))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
@@ -3688,7 +3799,7 @@ vect_is_simple_reduction (loop_vec_info loop_info, stmt_vec_info phi_info,
auto_vec<std::pair<ssa_op_iter, use_operand_p> > path;
enum tree_code code;
if (check_reduction_path (vect_location, loop, phi, latch_def, &code,
- path))
+ path, supported_multi_use_reduction))
{
STMT_VINFO_REDUC_CODE (phi_info) = code;
if (code == COND_EXPR && !nested_in_vect_loop)
@@ -5312,7 +5423,124 @@ vect_create_epilog_for_reduction (loop_vec_info loop_vinfo,
else
new_phi_result = PHI_RESULT (new_phis[0]);
- if (STMT_VINFO_REDUC_TYPE (reduc_info) == COND_REDUCTION
+ if (stmt_info->is_minmax_index || stmt_info->is_minmax)
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location,
+ "Reduce minmax index pattern\n");
+
+ stmt_vec_info minmax;
+ stmt_vec_info minmax_i;
+
+ if (stmt_info->is_minmax_index)
+ {
+ minmax_i = stmt_info;
+ minmax = stmt_info->reduc_multi_use_related_stmt;
+ if (induc_val != NULL_TREE)
+ minmax_i->induc_val = induc_val;
+ if (initial_def != NULL_TREE)
+ minmax_i->initial_def = initial_def;
+ }
+ else
+ {
+ minmax = stmt_info;
+ minmax_i = stmt_info->reduc_multi_use_related_stmt;
+ }
+
+ if (!stmt_info->epilog_finished
+ && minmax->vec_stmts.length () != 0
+ && minmax_i->vec_stmts.length () != 0)
+ {
+ /* The 'accumulator' stmts - the stmts that maintain a vector of
+ minimum/maximum elements, and corresponding index respectively. */
+ gimple* minmax_accum_stmt = minmax->vec_stmts[0];
+ gimple* minmax_i_accum_stmt = minmax_i->vec_stmts[0];
+
+ tree minmax_accum_lhs = gimple_assign_lhs (minmax_accum_stmt);
+ tree minmax_i_accum_lhs = gimple_assign_lhs (minmax_i_accum_stmt);
+ tree scalar_type = TREE_TYPE (vectype);
+
+ /* Reduce the min/max accumulator to the final result by taking the min/max. */
+ if (gimple_assign_rhs_code (minmax->stmt) == MAX_EXPR)
+ reduc_fn = IFN_REDUC_MAX;
+ else
+ reduc_fn = IFN_REDUC_MIN;
+ if (minmax->scalar_result == NULL)
+ minmax->scalar_result = make_ssa_name (scalar_type);
+ gimple* maxreducstmt = gimple_build_call_internal (reduc_fn, 1,
+ minmax_accum_lhs);
+ gimple_call_set_lhs (maxreducstmt, minmax->scalar_result);
+ gsi_insert_before (&exit_gsi, maxreducstmt, GSI_NEW_STMT);
+
+ /* To reduce the index accumulator, the index corresponding to the
+ scalar result reduced above must be extracted. Start by creating a vector
+ of {maxval, maxval...}. */
+ tree maxval = minmax->scalar_result;
+ tree maxvec = make_ssa_name (vectype);
+ tree maxvec_rhs = build_vector_from_val (vectype, maxval);
+ gimple* maxvec_decl = gimple_build_assign (maxvec, maxvec_rhs);
+ gsi_insert_after (&exit_gsi, maxvec_decl, GSI_NEW_STMT);
+
+ /* Create a vector of {INT_MAX,INT_MAX,INT_MAX}. */
+ tree intmax = TYPE_MAX_VALUE (TREE_TYPE (vectype));
+ tree intmaxvec = build_vector_from_val (vectype, intmax);
+
+ /* Select the correct index from the index accumulator vector, using
+ the position in the minmax accumulator vector. Mask out the
+ incorrect indexes with INT_MAX, to allow for a MIN reduction. */
+ tree truthvectype = truth_type_for (vectype);
+ tree cond = build2 (EQ_EXPR, truthvectype, minmax_accum_lhs, maxvec);
+ tree cond_lhs = make_ssa_name(truthvectype);
+ gimple* cond_decl = gimple_build_assign (cond_lhs, cond);
+ gsi_insert_after (&exit_gsi, cond_decl, GSI_NEW_STMT);
+ tree mask_rhs = build3 (VEC_COND_EXPR, truthvectype, cond_lhs,
+ minmax_i_accum_lhs, intmaxvec);
+ tree masked_ind = make_ssa_name (vectype);
+ gimple* masked_ind_stmt = gimple_build_assign (masked_ind, mask_rhs);
+ gsi_insert_after (&exit_gsi, masked_ind_stmt, GSI_NEW_STMT);
+
+ /* Reduce the index vector to a scalar result. */
+ tree index_scalar_reduc;
+ if (minmax_i->scalar_result == NULL)
+ minmax_i->scalar_result = make_ssa_name (TREE_TYPE (vectype));
+ if (minmax_i->induc_val == NULL_TREE)
+ index_scalar_reduc = minmax_i->scalar_result;
+ else
+ index_scalar_reduc = make_ssa_name (scalar_type);
+ gimple* index_result_stmt = gimple_build_call_internal (IFN_REDUC_MIN,
+ 1, masked_ind);
+ gimple_call_set_lhs (index_result_stmt, index_scalar_reduc);
+ gsi_insert_after (&exit_gsi, index_result_stmt, GSI_NEW_STMT);
+
+ if (minmax_i->induc_val != NULL_TREE)
+ {
+ /* Earlier we set the initial value to be a vector if induc_val
+ values. Check the result and if it is induc_val then replace
+ with the original initial value, unless induc_val is
+ the same as initial_def already. */
+ tree zcompare = build2 (EQ_EXPR, boolean_type_node,
+ index_scalar_reduc, minmax_i->induc_val);
+
+ epilog_stmt = gimple_build_assign (minmax_i->scalar_result, COND_EXPR,
+ zcompare, minmax_i->initial_def,
+ index_scalar_reduc);
+ gsi_insert_after (&exit_gsi, epilog_stmt, GSI_NEW_STMT);
+ }
+
+ minmax->epilog_finished = true;
+ minmax_i->epilog_finished = true;
+ }
+ else if (stmt_info->scalar_result == NULL)
+ {
+ /* Placeholder. The vectorized forms from both the max and the index
+ are needed for the reduction. */
+ tree scalar_result = make_ssa_name (scalar_type);
+ stmt_info->scalar_result = scalar_result;
+ }
+
+ scalar_results.safe_push (stmt_info->scalar_result);
+ }
+ else if (STMT_VINFO_REDUC_TYPE (reduc_info) == COND_REDUCTION
&& reduc_fn != IFN_LAST)
{
/* For condition reductions, we have a vector (NEW_PHI_RESULT) containing
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 9861d9e88102138c0e2de8dfc34422ff65a0e9e0..8ef402e5efb19e643645b4d788997c834c5ca118 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1093,6 +1093,17 @@ public:
pattern). */
stmt_vec_info related_stmt;
+ /* For handling multi phi reductions. */
+ tree scalar_result;
+ stmt_vec_info reduc_multi_use_related_stmt;
+ gimple* reduc_multi_use_result;
+ tree induc_val;
+ tree initial_def;
+ bool is_minmax_index;
+ bool is_minmax;
+ bool epilog_finished;
+
+
/* Used to keep a sequence of def stmts of a pattern stmt if such exists.
The sequence is attached to the original statement rather than the
pattern statement. */
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [vect] Support min/max + index pattern
2021-05-05 16:23 [vect] Support min/max + index pattern Joel Hutton
@ 2021-05-07 11:34 ` Richard Biener
0 siblings, 0 replies; 2+ messages in thread
From: Richard Biener @ 2021-05-07 11:34 UTC (permalink / raw)
To: Joel Hutton; +Cc: gcc-patches, Richard Sandiford, Tamar Christina
On Wed, 5 May 2021, Joel Hutton wrote:
> Hi all,
>
> looking for some feedback on this, one thing I would like to draw
> attention to is the fact that this pattern requires 2 separate dependent
> reductions in the epilogue. The accumulator vector for the
> maximum/minimum elements can be reduced to a scalar result trivially
> with a min/max, but getting the index from accumulator vector of indices
> is more complex and requires using the position of the maximum/minimum
> scalar result value within the accumulator vector to create a mask.
>
> The given solution works but it's slightly messy.
> vect_create_epilogue_for_reduction creates the epilogue for one
> vectorized scalar stmt at a time. This modification makes one invocation
> create the epilogue for both related stmts and marks the other as
> 'done'. Alternate suggestions are welcome.
So I'm not looking at the very details in the patch but I think
a concept of "dependent" reductions might be useful (there might
be related patterns that could be vectorized). So during
reduction discovery detect unhandled multi-uses and queue those
for later re-evaluation (in case we analyze the value reduction
first). Discover the index reduction as regular reduction.
Upon re-analyzing the value reduction allow multi-uses that are
reductions themselves and note the dependence in some new stmt_vinfo
field.
Then during reduction analysis we can verify if we handle
a particular dependence and during code-gen we can ensure
to only code-gen one epilogue and have access to the other part.
That said, I'd make the thing a bit more generic in appearance
but otherwise yes, we do need to handle this together somehow.
+ /* For handling multi phi reductions. */
+ tree scalar_result;
+ stmt_vec_info reduc_multi_use_related_stmt;
+ gimple* reduc_multi_use_result;
+ tree induc_val;
+ tree initial_def;
+ bool is_minmax_index;
+ bool is_minmax;
+ bool epilog_finished;
which should ideally reduce to a single field then.
Oh, and make sure an SLP variant is supported.
for (int i = 0; i < n; i++) {
if (data[2*i] < best1) {
best1 = data[2*i];
best_i1 = 2*i;
}
if (data[2*i+1] < best2) {
best2 = data[2*i+1];
best_i2 = 2*i;
}
}
Richard.
> Joel
>
> [vect] Support min/max + index pattern
>
> Add the capability to vect-loop to support the following pattern.
>
> for (int i = 0; i < n; i ++)
> {
> if (data[i] < best)
> {
> best = data[i];
> best_i = i;
> }
> }
>
> gcc/ChangeLog:
>
> * tree-vect-loop.c (vect_reassociating_reduction_simple_p): New
> function.
> (vect_recog_minmax_index_pattern): New function.
> (vect_is_simple_reduction): Add multi_use_reduction case.
> (vect_create_epilog_for_reduction): Add minmax+index epilogue handling.
>
--
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2021-05-07 11:34 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-05 16:23 [vect] Support min/max + index pattern Joel Hutton
2021-05-07 11:34 ` Richard Biener
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).