[1/2] Introduce gcc_sort_r

Message ID alpine.LNX.2.20.13.1908011840570.685@monopod.intra.ispras.ru
State New
Headers show
Series
  • [1/2] Introduce gcc_sort_r
Related show

Commit Message

Alexander Monakov Aug. 1, 2019, 3:54 p.m.
Hi,

this patch adds gcc_sort_r, a function similar to Glibc qsort_r that takes an
extra 'user data' pointer and passes it to comparators.  Richi asked about it
in https://gcc.gnu.org/ml/gcc-patches/2019-07/msg01383.html , see followups in
that thread for other approaches that were considered.

Patch 2/2, unchanged from Richard's version, uses the new function in domwalk.

Bootstrapped/regtested on x86-64.

Thanks.
Alexander

	* sort.cc (sort_r_ctx): New struct.
	(reorder23): Make templated on context type.
	(reorder45): Ditto.
	(cmp1): Ditto.  Adjust signature.
	(netsort): Ditto.
	(mergesort): Ditto.
	[CHECKING_P] (cmp2to3): New static function.  Use it...
	(gcc_qsort) [CHECKING_P]: ...here.
	(gcc_sort_r): New function.
	* system.h (sort_r_cmp_fn): New function typedef.
	(qsort_chk): Adjust signature.
	(gcc_sort_r): Declare.
	* vec.c (qsort_chk_error): Adjust.
	(qsort_chk): Adjust.

Comments

Richard Biener Aug. 1, 2019, 3:56 p.m. | #1
On Thu, Aug 1, 2019 at 5:54 PM Alexander Monakov <amonakov@ispras.ru> wrote:
>

> Hi,

>

> this patch adds gcc_sort_r, a function similar to Glibc qsort_r that takes an

> extra 'user data' pointer and passes it to comparators.  Richi asked about it

> in https://gcc.gnu.org/ml/gcc-patches/2019-07/msg01383.html , see followups in

> that thread for other approaches that were considered.

>

> Patch 2/2, unchanged from Richard's version, uses the new function in domwalk.

>

> Bootstrapped/regtested on x86-64.


OK.

Thanks!
Richard.

> Thanks.

> Alexander

>

>         * sort.cc (sort_r_ctx): New struct.

>         (reorder23): Make templated on context type.

>         (reorder45): Ditto.

>         (cmp1): Ditto.  Adjust signature.

>         (netsort): Ditto.

>         (mergesort): Ditto.

>         [CHECKING_P] (cmp2to3): New static function.  Use it...

>         (gcc_qsort) [CHECKING_P]: ...here.

>         (gcc_sort_r): New function.

>         * system.h (sort_r_cmp_fn): New function typedef.

>         (qsort_chk): Adjust signature.

>         (gcc_sort_r): Declare.

>         * vec.c (qsort_chk_error): Adjust.

>         (qsort_chk): Adjust.

>

> diff --git a/gcc/sort.cc b/gcc/sort.cc

> index 3e9c032c462..73a9f7ed7c5 100644

> --- a/gcc/sort.cc

> +++ b/gcc/sort.cc

> @@ -58,8 +58,25 @@ struct sort_ctx

>    size_t nlim; // limit for network sort

>  };

>

> +/* Like sort_ctx, but for use with qsort_r-style comparators.  Several

> +   functions in this file are templates that work with either context type.  */

> +struct sort_r_ctx

> +{

> +  void          *data;

> +  sort_r_cmp_fn *cmp_;

> +  char   *out;

> +  size_t n;

> +  size_t size;

> +  size_t nlim;

> +  int cmp (const void *a, const void *b)

> +  {

> +    return cmp_ (a, b, data);

> +  }

> +};

> +

>  /* Helper for netsort. Permute, possibly in-place, 2 or 3 elements,

>     placing E0 to C->OUT, E1 to C->OUT + C->SIZE, and so on.  */

> +template<typename sort_ctx>

>  static void

>  reorder23 (sort_ctx *c, char *e0, char *e1, char *e2)

>  {

> @@ -90,6 +107,7 @@ do {                                                     \

>  }

>

>  /* Like reorder23, but permute 4 or 5 elements.  */

> +template<typename sort_ctx>

>  static void

>  reorder45 (sort_ctx *c, char *e0, char *e1, char *e2, char *e3, char *e4)

>  {

> @@ -127,21 +145,23 @@ do {                                                     \

>     Return E0^E1 if E0 compares less than E1, zero otherwise.

>     This is noinline to avoid code growth and confine invocation

>     to a single call site, assisting indirect branch prediction.  */

> +template<typename sort_ctx>

>  noinline static intptr_t

> -cmp1 (char *e0, char *e1, cmp_fn *cmp)

> +cmp1 (char *e0, char *e1, sort_ctx *c)

>  {

>    intptr_t x = (intptr_t)e0 ^ (intptr_t)e1;

> -  return x & (cmp (e0, e1) >> 31);

> +  return x & (c->cmp (e0, e1) >> 31);

>  }

>

>  /* Execute network sort on 2 to 5 elements from IN, placing them into C->OUT.

>     IN may be equal to C->OUT, in which case elements are sorted in place.  */

> +template<typename sort_ctx>

>  static void

>  netsort (char *in, sort_ctx *c)

>  {

>  #define CMP(e0, e1)                   \

>  do {                                  \

> -  intptr_t x = cmp1 (e1, e0, c->cmp); \

> +  intptr_t x = cmp1 (e1, e0, c);      \

>    e0 = (char *)((intptr_t)e0 ^ x);    \

>    e1 = (char *)((intptr_t)e1 ^ x);    \

>  } while (0)

> @@ -176,6 +196,7 @@ do {                                  \

>  /* Execute merge sort on N elements from IN, placing them into OUT,

>     using TMP as temporary storage if IN is equal to OUT.

>     This is a stable sort if netsort is used only for 2 or 3 elements.  */

> +template<typename sort_ctx>

>  static void

>  mergesort (char *in, sort_ctx *c, size_t n, char *out, char *tmp)

>  {

> @@ -217,6 +238,17 @@ do {                                            \

>    memcpy (out, l, r - out);

>  }

>

> +#if CHECKING_P

> +/* Adapter for using two-argument comparators in functions expecting the

> +   three-argument sort_r_cmp_fn type.  */

> +static int

> +cmp2to3 (const void *a, const void *b, void *c)

> +{

> +  return ((cmp_fn *)c) (a, b);

> +}

> +#endif

> +

> +/* Replacement for C qsort.  */

>  void

>  gcc_qsort (void *vbase, size_t n, size_t size, cmp_fn *cmp)

>  {

> @@ -235,10 +267,30 @@ gcc_qsort (void *vbase, size_t n, size_t size, cmp_fn *cmp)

>    if (buf != scratch)

>      free (buf);

>  #if CHECKING_P

> -  qsort_chk (vbase, n, size, cmp);

> +  qsort_chk (vbase, n, size, cmp2to3, (void*)cmp);

>  #endif

>  }

>

> +/* Substitute for Glibc qsort_r.  */

> +void

> +gcc_sort_r (void *vbase, size_t n, size_t size, sort_r_cmp_fn *cmp, void *data)

> +{

> +  if (n < 2)

> +    return;

> +  char *base = (char *)vbase;

> +  sort_r_ctx c = {data, cmp, base, n, size, 5};

> +  long long scratch[32];

> +  size_t bufsz = (n / 2) * size;

> +  void *buf = bufsz <= sizeof scratch ? scratch : xmalloc (bufsz);

> +  mergesort (base, &c, n, base, (char *)buf);

> +  if (buf != scratch)

> +    free (buf);

> +#if CHECKING_P

> +  qsort_chk (vbase, n, size, cmp, data);

> +#endif

> +}

> +

> +/* Stable sort, signature-compatible to C qsort.  */

>  void

>  gcc_stablesort (void *vbase, size_t n, size_t size, cmp_fn *cmp)

>  {

> diff --git a/gcc/system.h b/gcc/system.h

> index d04f8fd3360..56af544b70b 100644

> --- a/gcc/system.h

> +++ b/gcc/system.h

> @@ -1197,13 +1197,14 @@ helper_const_non_const_cast (const char *p)

>  /* Get definitions of HOST_WIDE_INT.  */

>  #include "hwint.h"

>

> -/* GCC qsort API-compatible functions: except in release-checking compilers,

> -   redirect 4-argument qsort calls to gcc_qsort; keep 1-argument invocations

> -   corresponding to vec::qsort (cmp): they use C qsort internally anyway.  */

> -void qsort_chk (void *, size_t, size_t, int (*)(const void *, const void *));

> +typedef int sort_r_cmp_fn (const void *, const void *, void *);

> +void qsort_chk (void *, size_t, size_t, sort_r_cmp_fn *, void *);

> +void gcc_sort_r (void *, size_t, size_t, sort_r_cmp_fn *, void *);

>  void gcc_qsort (void *, size_t, size_t, int (*)(const void *, const void *));

>  void gcc_stablesort (void *, size_t, size_t,

>                      int (*)(const void *, const void *));

> +/* Redirect four-argument qsort calls to gcc_qsort; one-argument invocations

> +   correspond to vec::qsort, and use C qsort internally.  */

>  #define PP_5th(a1, a2, a3, a4, a5, ...) a5

>  #undef qsort

>  #define qsort(...) PP_5th (__VA_ARGS__, gcc_qsort, 3, 2, qsort, 0) (__VA_ARGS__)

> diff --git a/gcc/vec.c b/gcc/vec.c

> index cbd2db010f9..bac5eb753a3 100644

> --- a/gcc/vec.c

> +++ b/gcc/vec.c

> @@ -192,21 +192,23 @@ dump_vec_loc_statistics (void)

>  ATTRIBUTE_NORETURN ATTRIBUTE_COLD

>  static void

>  qsort_chk_error (const void *p1, const void *p2, const void *p3,

> -                int (*cmp) (const void *, const void *))

> +                sort_r_cmp_fn *cmp, void *data)

>  {

>    if (!p3)

>      {

> -      int r1 = cmp (p1, p2), r2 = cmp (p2, p1);

> -      error ("qsort comparator not anti-commutative: %d, %d", r1, r2);

> +      int r1 = cmp (p1, p2, data), r2 = cmp (p2, p1, data);

> +      error ("qsort comparator not anti-symmetric: %d, %d", r1, r2);

>      }

>    else if (p1 == p2)

>      {

> -      int r = cmp (p1, p3);

> +      int r = cmp (p1, p3, data);

>        error ("qsort comparator non-negative on sorted output: %d", r);

>      }

>    else

>      {

> -      int r1 = cmp (p1, p2), r2 = cmp (p2, p3), r3 = cmp (p1, p3);

> +      int r1 = cmp (p1, p2, data);

> +      int r2 = cmp (p2, p3, data);

> +      int r3 = cmp (p1, p3, data);

>        error ("qsort comparator not transitive: %d, %d, %d", r1, r2, r3);

>      }

>    internal_error ("qsort checking failed");

> @@ -215,8 +217,7 @@ qsort_chk_error (const void *p1, const void *p2, const void *p3,

>  /* Verify anti-symmetry and transitivity for comparator CMP on sorted array

>     of N SIZE-sized elements pointed to by BASE.  */

>  void

> -qsort_chk (void *base, size_t n, size_t size,

> -          int (*cmp)(const void *, const void *))

> +qsort_chk (void *base, size_t n, size_t size, sort_r_cmp_fn *cmp, void *data)

>  {

>  #if 0

>  #define LIM(n) (n)

> @@ -225,9 +226,9 @@ qsort_chk (void *base, size_t n, size_t size,

>  #define LIM(n) ((n) <= 16 ? (n) : 12 + floor_log2 (n))

>  #endif

>  #define ELT(i) ((const char *) base + (i) * size)

> -#define CMP(i, j) cmp (ELT (i), ELT (j))

> -#define ERR2(i, j) qsort_chk_error (ELT (i), ELT (j), NULL, cmp)

> -#define ERR3(i, j, k) qsort_chk_error (ELT (i), ELT (j), ELT (k), cmp)

> +#define CMP(i, j) cmp (ELT (i), ELT (j), data)

> +#define ERR2(i, j) qsort_chk_error (ELT (i), ELT (j), NULL, cmp, data)

> +#define ERR3(i, j, k) qsort_chk_error (ELT (i), ELT (j), ELT (k), cmp, data)

>    size_t i1, i2, i, j;

>    /* This outer loop iterates over maximum spans [i1, i2) such that

>       elements within each span compare equal to each other.  */
Alexander Monakov Aug. 1, 2019, 4:01 p.m. | #2
I used this patch by Richard to check gcc_sort_r in bootstrap.

	* domwalk.c (bb_postorder): Remove static variable.
	(cmp_bb_postorder): Adjust.
	(sort_bbs_postorder): Adjust and use gcc_sort_r.
	(dom_walker::walk): Adjust.

diff --git a/gcc/domwalk.c b/gcc/domwalk.c
index 8c0fdecb462..42c5127695b 100644
--- a/gcc/domwalk.c
+++ b/gcc/domwalk.c
@@ -128,14 +128,12 @@ along with GCC; see the file COPYING3.  If not see
     which is currently an abstraction over walking tree statements.  Thus
     the dominator walker is currently only useful for trees.  */
 
-/* Reverse postorder index of each basic block.  */
-static int *bb_postorder;
-
 static int
-cmp_bb_postorder (const void *a, const void *b)
+cmp_bb_postorder (const void *a, const void *b, void *data)
 {
   basic_block bb1 = *(const basic_block *)(a);
   basic_block bb2 = *(const basic_block *)(b);
+  int *bb_postorder = (int *)data;
   /* Place higher completion number first (pop off lower number first).  */
   return bb_postorder[bb2->index] - bb_postorder[bb1->index];
 }
@@ -144,7 +142,7 @@ cmp_bb_postorder (const void *a, const void *b)
    i.e. by descending number in BB_POSTORDER array.  */
 
 static void
-sort_bbs_postorder (basic_block *bbs, int n)
+sort_bbs_postorder (basic_block *bbs, int n, int *bb_postorder)
 {
   if (__builtin_expect (n == 2, true))
     {
@@ -166,7 +164,7 @@ sort_bbs_postorder (basic_block *bbs, int n)
       bbs[0] = bb0, bbs[1] = bb1, bbs[2] = bb2;
     }
   else
-    qsort (bbs, n, sizeof *bbs, cmp_bb_postorder);
+    gcc_sort_r (bbs, n, sizeof *bbs, cmp_bb_postorder, bb_postorder);
 }
 
 /* Set EDGE_EXECUTABLE on every edge within FN's CFG.  */
@@ -294,7 +292,6 @@ dom_walker::walk (basic_block bb)
   basic_block *worklist = XNEWVEC (basic_block,
 				   n_basic_blocks_for_fn (cfun) * 2);
   int sp = 0;
-  bb_postorder = m_bb_to_rpo;
 
   while (true)
     {
@@ -339,7 +336,8 @@ dom_walker::walk (basic_block bb)
 	      if (sp - saved_sp > 1
 		  && m_dom_direction == CDI_DOMINATORS
 		  && m_bb_to_rpo)
-		sort_bbs_postorder (&worklist[saved_sp], sp - saved_sp);
+		sort_bbs_postorder (&worklist[saved_sp], sp - saved_sp,
+				    m_bb_to_rpo);
 	    }
 	}
       /* NULL is used to mark pop operations in the recursion stack.  */
@@ -360,6 +358,5 @@ dom_walker::walk (basic_block bb)
       else
 	break;
     }
-  bb_postorder = NULL;
   free (worklist);
 }
Richard Biener Aug. 1, 2019, 4:02 p.m. | #3
On Thu, Aug 1, 2019 at 6:01 PM Alexander Monakov <amonakov@ispras.ru> wrote:
>

> I used this patch by Richard to check gcc_sort_r in bootstrap.


OK to commit as well.

Thanks,
Richard.

>         * domwalk.c (bb_postorder): Remove static variable.

>         (cmp_bb_postorder): Adjust.

>         (sort_bbs_postorder): Adjust and use gcc_sort_r.

>         (dom_walker::walk): Adjust.

>

> diff --git a/gcc/domwalk.c b/gcc/domwalk.c

> index 8c0fdecb462..42c5127695b 100644

> --- a/gcc/domwalk.c

> +++ b/gcc/domwalk.c

> @@ -128,14 +128,12 @@ along with GCC; see the file COPYING3.  If not see

>      which is currently an abstraction over walking tree statements.  Thus

>      the dominator walker is currently only useful for trees.  */

>

> -/* Reverse postorder index of each basic block.  */

> -static int *bb_postorder;

> -

>  static int

> -cmp_bb_postorder (const void *a, const void *b)

> +cmp_bb_postorder (const void *a, const void *b, void *data)

>  {

>    basic_block bb1 = *(const basic_block *)(a);

>    basic_block bb2 = *(const basic_block *)(b);

> +  int *bb_postorder = (int *)data;

>    /* Place higher completion number first (pop off lower number first).  */

>    return bb_postorder[bb2->index] - bb_postorder[bb1->index];

>  }

> @@ -144,7 +142,7 @@ cmp_bb_postorder (const void *a, const void *b)

>     i.e. by descending number in BB_POSTORDER array.  */

>

>  static void

> -sort_bbs_postorder (basic_block *bbs, int n)

> +sort_bbs_postorder (basic_block *bbs, int n, int *bb_postorder)

>  {

>    if (__builtin_expect (n == 2, true))

>      {

> @@ -166,7 +164,7 @@ sort_bbs_postorder (basic_block *bbs, int n)

>        bbs[0] = bb0, bbs[1] = bb1, bbs[2] = bb2;

>      }

>    else

> -    qsort (bbs, n, sizeof *bbs, cmp_bb_postorder);

> +    gcc_sort_r (bbs, n, sizeof *bbs, cmp_bb_postorder, bb_postorder);

>  }

>

>  /* Set EDGE_EXECUTABLE on every edge within FN's CFG.  */

> @@ -294,7 +292,6 @@ dom_walker::walk (basic_block bb)

>    basic_block *worklist = XNEWVEC (basic_block,

>                                    n_basic_blocks_for_fn (cfun) * 2);

>    int sp = 0;

> -  bb_postorder = m_bb_to_rpo;

>

>    while (true)

>      {

> @@ -339,7 +336,8 @@ dom_walker::walk (basic_block bb)

>               if (sp - saved_sp > 1

>                   && m_dom_direction == CDI_DOMINATORS

>                   && m_bb_to_rpo)

> -               sort_bbs_postorder (&worklist[saved_sp], sp - saved_sp);

> +               sort_bbs_postorder (&worklist[saved_sp], sp - saved_sp,

> +                                   m_bb_to_rpo);

>             }

>         }

>        /* NULL is used to mark pop operations in the recursion stack.  */

> @@ -360,6 +358,5 @@ dom_walker::walk (basic_block bb)

>        else

>         break;

>      }

> -  bb_postorder = NULL;

>    free (worklist);

>  }

Patch

diff --git a/gcc/sort.cc b/gcc/sort.cc
index 3e9c032c462..73a9f7ed7c5 100644
--- a/gcc/sort.cc
+++ b/gcc/sort.cc
@@ -58,8 +58,25 @@  struct sort_ctx
   size_t nlim; // limit for network sort
 };
 
+/* Like sort_ctx, but for use with qsort_r-style comparators.  Several
+   functions in this file are templates that work with either context type.  */
+struct sort_r_ctx
+{
+  void          *data;
+  sort_r_cmp_fn *cmp_;
+  char   *out;
+  size_t n;
+  size_t size;
+  size_t nlim;
+  int cmp (const void *a, const void *b)
+  {
+    return cmp_ (a, b, data);
+  }
+};
+
 /* Helper for netsort. Permute, possibly in-place, 2 or 3 elements,
    placing E0 to C->OUT, E1 to C->OUT + C->SIZE, and so on.  */
+template<typename sort_ctx>
 static void
 reorder23 (sort_ctx *c, char *e0, char *e1, char *e2)
 {
@@ -90,6 +107,7 @@  do {                                                     \
 }
 
 /* Like reorder23, but permute 4 or 5 elements.  */
+template<typename sort_ctx>
 static void
 reorder45 (sort_ctx *c, char *e0, char *e1, char *e2, char *e3, char *e4)
 {
@@ -127,21 +145,23 @@  do {                                                     \
    Return E0^E1 if E0 compares less than E1, zero otherwise.
    This is noinline to avoid code growth and confine invocation
    to a single call site, assisting indirect branch prediction.  */
+template<typename sort_ctx>
 noinline static intptr_t
-cmp1 (char *e0, char *e1, cmp_fn *cmp)
+cmp1 (char *e0, char *e1, sort_ctx *c)
 {
   intptr_t x = (intptr_t)e0 ^ (intptr_t)e1;
-  return x & (cmp (e0, e1) >> 31);
+  return x & (c->cmp (e0, e1) >> 31);
 }
 
 /* Execute network sort on 2 to 5 elements from IN, placing them into C->OUT.
    IN may be equal to C->OUT, in which case elements are sorted in place.  */
+template<typename sort_ctx>
 static void
 netsort (char *in, sort_ctx *c)
 {
 #define CMP(e0, e1)                   \
 do {                                  \
-  intptr_t x = cmp1 (e1, e0, c->cmp); \
+  intptr_t x = cmp1 (e1, e0, c);      \
   e0 = (char *)((intptr_t)e0 ^ x);    \
   e1 = (char *)((intptr_t)e1 ^ x);    \
 } while (0)
@@ -176,6 +196,7 @@  do {                                  \
 /* Execute merge sort on N elements from IN, placing them into OUT,
    using TMP as temporary storage if IN is equal to OUT.
    This is a stable sort if netsort is used only for 2 or 3 elements.  */
+template<typename sort_ctx>
 static void
 mergesort (char *in, sort_ctx *c, size_t n, char *out, char *tmp)
 {
@@ -217,6 +238,17 @@  do {                                            \
   memcpy (out, l, r - out);
 }
 
+#if CHECKING_P
+/* Adapter for using two-argument comparators in functions expecting the
+   three-argument sort_r_cmp_fn type.  */
+static int
+cmp2to3 (const void *a, const void *b, void *c)
+{
+  return ((cmp_fn *)c) (a, b);
+}
+#endif
+
+/* Replacement for C qsort.  */
 void
 gcc_qsort (void *vbase, size_t n, size_t size, cmp_fn *cmp)
 {
@@ -235,10 +267,30 @@  gcc_qsort (void *vbase, size_t n, size_t size, cmp_fn *cmp)
   if (buf != scratch)
     free (buf);
 #if CHECKING_P
-  qsort_chk (vbase, n, size, cmp);
+  qsort_chk (vbase, n, size, cmp2to3, (void*)cmp);
 #endif
 }
 
+/* Substitute for Glibc qsort_r.  */
+void
+gcc_sort_r (void *vbase, size_t n, size_t size, sort_r_cmp_fn *cmp, void *data)
+{
+  if (n < 2)
+    return;
+  char *base = (char *)vbase;
+  sort_r_ctx c = {data, cmp, base, n, size, 5};
+  long long scratch[32];
+  size_t bufsz = (n / 2) * size;
+  void *buf = bufsz <= sizeof scratch ? scratch : xmalloc (bufsz);
+  mergesort (base, &c, n, base, (char *)buf);
+  if (buf != scratch)
+    free (buf);
+#if CHECKING_P
+  qsort_chk (vbase, n, size, cmp, data);
+#endif
+}
+
+/* Stable sort, signature-compatible to C qsort.  */
 void
 gcc_stablesort (void *vbase, size_t n, size_t size, cmp_fn *cmp)
 {
diff --git a/gcc/system.h b/gcc/system.h
index d04f8fd3360..56af544b70b 100644
--- a/gcc/system.h
+++ b/gcc/system.h
@@ -1197,13 +1197,14 @@  helper_const_non_const_cast (const char *p)
 /* Get definitions of HOST_WIDE_INT.  */
 #include "hwint.h"
 
-/* GCC qsort API-compatible functions: except in release-checking compilers,
-   redirect 4-argument qsort calls to gcc_qsort; keep 1-argument invocations
-   corresponding to vec::qsort (cmp): they use C qsort internally anyway.  */
-void qsort_chk (void *, size_t, size_t, int (*)(const void *, const void *));
+typedef int sort_r_cmp_fn (const void *, const void *, void *);
+void qsort_chk (void *, size_t, size_t, sort_r_cmp_fn *, void *);
+void gcc_sort_r (void *, size_t, size_t, sort_r_cmp_fn *, void *);
 void gcc_qsort (void *, size_t, size_t, int (*)(const void *, const void *));
 void gcc_stablesort (void *, size_t, size_t,
 		     int (*)(const void *, const void *));
+/* Redirect four-argument qsort calls to gcc_qsort; one-argument invocations
+   correspond to vec::qsort, and use C qsort internally.  */
 #define PP_5th(a1, a2, a3, a4, a5, ...) a5
 #undef qsort
 #define qsort(...) PP_5th (__VA_ARGS__, gcc_qsort, 3, 2, qsort, 0) (__VA_ARGS__)
diff --git a/gcc/vec.c b/gcc/vec.c
index cbd2db010f9..bac5eb753a3 100644
--- a/gcc/vec.c
+++ b/gcc/vec.c
@@ -192,21 +192,23 @@  dump_vec_loc_statistics (void)
 ATTRIBUTE_NORETURN ATTRIBUTE_COLD
 static void
 qsort_chk_error (const void *p1, const void *p2, const void *p3,
-		 int (*cmp) (const void *, const void *))
+		 sort_r_cmp_fn *cmp, void *data)
 {
   if (!p3)
     {
-      int r1 = cmp (p1, p2), r2 = cmp (p2, p1);
-      error ("qsort comparator not anti-commutative: %d, %d", r1, r2);
+      int r1 = cmp (p1, p2, data), r2 = cmp (p2, p1, data);
+      error ("qsort comparator not anti-symmetric: %d, %d", r1, r2);
     }
   else if (p1 == p2)
     {
-      int r = cmp (p1, p3);
+      int r = cmp (p1, p3, data);
       error ("qsort comparator non-negative on sorted output: %d", r);
     }
   else
     {
-      int r1 = cmp (p1, p2), r2 = cmp (p2, p3), r3 = cmp (p1, p3);
+      int r1 = cmp (p1, p2, data);
+      int r2 = cmp (p2, p3, data);
+      int r3 = cmp (p1, p3, data);
       error ("qsort comparator not transitive: %d, %d, %d", r1, r2, r3);
     }
   internal_error ("qsort checking failed");
@@ -215,8 +217,7 @@  qsort_chk_error (const void *p1, const void *p2, const void *p3,
 /* Verify anti-symmetry and transitivity for comparator CMP on sorted array
    of N SIZE-sized elements pointed to by BASE.  */
 void
-qsort_chk (void *base, size_t n, size_t size,
-	   int (*cmp)(const void *, const void *))
+qsort_chk (void *base, size_t n, size_t size, sort_r_cmp_fn *cmp, void *data)
 {
 #if 0
 #define LIM(n) (n)
@@ -225,9 +226,9 @@  qsort_chk (void *base, size_t n, size_t size,
 #define LIM(n) ((n) <= 16 ? (n) : 12 + floor_log2 (n))
 #endif
 #define ELT(i) ((const char *) base + (i) * size)
-#define CMP(i, j) cmp (ELT (i), ELT (j))
-#define ERR2(i, j) qsort_chk_error (ELT (i), ELT (j), NULL, cmp)
-#define ERR3(i, j, k) qsort_chk_error (ELT (i), ELT (j), ELT (k), cmp)
+#define CMP(i, j) cmp (ELT (i), ELT (j), data)
+#define ERR2(i, j) qsort_chk_error (ELT (i), ELT (j), NULL, cmp, data)
+#define ERR3(i, j, k) qsort_chk_error (ELT (i), ELT (j), ELT (k), cmp, data)
   size_t i1, i2, i, j;
   /* This outer loop iterates over maximum spans [i1, i2) such that
      elements within each span compare equal to each other.  */