On Fri, Jul 15, 2016 at 6:00 PM, Alexander Monakov <amona...@ispras.ru> wrote: > Hi, > > this patch adds internal checking for comparator functions that GCC passes to > qsort. PR71702 describes an ICE that happens because comparator > 'dr_group_sort_cmp' used to be non-transitive in some cases until GCC 6. This > patch would uncover that issue on a number of small testcases in GCC's > testsuite, so it should be useful to catch similar issues in the future as > well. > > Although the meat of the implementation is not tied to vec<>, this patch adds > verification only to vec::qsort. I see there's a number of other ::qsort uses > in GCC; it should be useful to have such checking there as well. I'd > appreciate > input on that (I'd go with '#define qsort(b, n, s, c) qsort_chk (b, n, s, c)' > in system.h and implement qsort_chk as a checking wrapper around libc qsort). > > Bootstrapped and regtested on x86_64, OK to apply?
Ugh. What impact does this have on stage2 compile-time? Richard. > Alexander > > * vec.c (vec_check_sort_cmp): New. Use it... > * vec.h (vec<T, A, vl_embed>::qsort): ...here to verify comparator > consistency when checking is enabled. > > diff --git a/gcc/vec.c b/gcc/vec.c > index fd200ea..b4ac0b4 100644 > --- a/gcc/vec.c > +++ b/gcc/vec.c > @@ -190,6 +190,44 @@ dump_vec_loc_statistics (void) > vec_mem_desc.dump (VEC_ORIGIN); > } > > +/* Verify consistency of comparator CMP on array BASE of N SIZE-sized > elements. > + Assuming the array should be sorted according to CMP, any assertion > failure > + here implies that CMP is not transitive, or is not anti-commutative. */ > + > +void > +vec_check_sort_cmp (const void *base, size_t n, size_t size, > + int (*cmp) (const void *, const void *)) > +{ > + const char *cbase = (const char *) base; > + size_t i1, i2, i, j; > + /* The following loop nest has O(n^2) time complexity. Limit n to avoid > + slowness on artificial testcases. */ > + if (n > 100) > + n = 100; > +#define CMP(i, j) cmp (cbase + (i) * size, cbase + (j) * size) > + /* The outer loop iterates over maximum spans [i1, i2) such that elements > + within each span compare equal. */ > + for (i1 = 0; i1 < n; i1 = i2) > + { > + /* Position i2 past the last element that compares equal to i1'th. */ > + for (i2 = i1 + 1; i2 < n; i2++) > + if (CMP (i1, i2)) > + break; > + else > + gcc_assert (!CMP (i2, i1)); > + /* Verify that all remaining pairs within current span compare equal. > */ > + for (i = i1 + 1; i + 1 < i2; i++) > + for (j = i + 1; j < i2; j++) > + gcc_assert (!CMP (i, j) && !CMP (j, i)); > + /* Verify that all elements within current span compare less than any > + element beyond the span. */ > + for (i = i1; i < i2; i++) > + for (j = i2; j < n; j++) > + gcc_assert (CMP (i, j) < 0 && CMP (j, i) > 0); > + } > +#undef CMP > +} > + > #ifndef GENERATOR_FILE > #if CHECKING_P > > diff --git a/gcc/vec.h b/gcc/vec.h > index eb8c270..ff1e37e 100644 > --- a/gcc/vec.h > +++ b/gcc/vec.h > @@ -182,6 +182,9 @@ extern void *ggc_realloc (void *, size_t MEM_STAT_DECL); > /* Support function for statistics. */ > extern void dump_vec_loc_statistics (void); > > +extern void vec_check_sort_cmp (const void *, size_t, size_t, > + int (*) (const void *, const void *)); > + > /* Hashtable mapping vec addresses to descriptors. */ > extern htab_t vec_mem_usage_hash; > > @@ -947,8 +950,11 @@ template<typename T, typename A> > inline void > vec<T, A, vl_embed>::qsort (int (*cmp) (const void *, const void *)) > { > - if (length () > 1) > - ::qsort (address (), length (), sizeof (T), cmp); > + if (length () <= 1) > + return; > + ::qsort (address (), length (), sizeof (T), cmp); > + if (CHECKING_P) > + vec_check_sort_cmp (address (), length (), sizeof (T), cmp); > } > >