Mercurial > hg > CbC > CbC_gcc
diff gcc/vec.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/vec.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/vec.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,7 +1,7 @@ /* Vector API for GNU compiler. - Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 - Free Software Foundation, Inc. + Copyright (C) 2004-2017 Free Software Foundation, Inc. Contributed by Nathan Sidwell <nathan@codesourcery.com> + Re-implemented in C++ by Diego Novillo <dnovillo@google.com> This file is part of GCC. @@ -28,577 +28,448 @@ #endif #include "system.h" -#include "ggc.h" -#include "vec.h" #include "coretypes.h" +#include "hash-table.h" +#include "selftest.h" +#ifdef GENERATOR_FILE +#include "errors.h" +#else +#include "input.h" #include "diagnostic-core.h" -#include "hashtab.h" +#endif + +/* vNULL is an empty type with a template cast operation that returns + a zero-initialized vec<T, A, L> instance. Use this when you want + to assign nil values to new vec instances or pass a nil vector as + a function call argument. + + We use this technique because vec<T, A, L> must be PODs (they are + stored in unions and passed in vararg functions), this means that + they cannot have ctors/dtors. */ +vnull vNULL; + +/* Vector memory usage. */ +struct vec_usage: public mem_usage +{ + /* Default constructor. */ + vec_usage (): m_items (0), m_items_peak (0) {} -struct vec_prefix -{ - unsigned num; - unsigned alloc; - void *vec[1]; -}; + /* Constructor. */ + vec_usage (size_t allocated, size_t times, size_t peak, + size_t items, size_t items_peak) + : mem_usage (allocated, times, peak), + m_items (items), m_items_peak (items_peak) {} + + /* Comparison operator. */ + inline bool + operator< (const vec_usage &second) const + { + return (m_allocated == second.m_allocated ? + (m_peak == second.m_peak ? m_times < second.m_times + : m_peak < second.m_peak) : m_allocated < second.m_allocated); + } + + /* Sum the usage with SECOND usage. */ + vec_usage + operator+ (const vec_usage &second) + { + return vec_usage (m_allocated + second.m_allocated, + m_times + second.m_times, + m_peak + second.m_peak, + m_items + second.m_items, + m_items_peak + second.m_items_peak); + } + /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */ + inline void + dump (mem_location *loc, mem_usage &total) const + { + char s[4096]; + sprintf (s, "%s:%i (%s)", loc->get_trimmed_filename (), + loc->m_line, loc->m_function); -#ifdef GATHER_STATISTICS + s[48] = '\0'; + + fprintf (stderr, "%-48s %10li:%4.1f%%%10li%10li:%4.1f%%%11li%11li\n", s, + (long)m_allocated, m_allocated * 100.0 / total.m_allocated, + (long)m_peak, (long)m_times, m_times * 100.0 / total.m_times, + (long)m_items, (long)m_items_peak); + } + + /* Dump footer. */ + inline void + dump_footer () + { + print_dash_line (); + fprintf (stderr, "%s%55li%25li%17li\n", "Total", (long)m_allocated, + (long)m_times, (long)m_items); + print_dash_line (); + } -/* Store information about each particular vector. */ -struct vec_descriptor -{ - const char *function; - const char *file; - int line; - size_t allocated; - size_t times; - size_t peak; + /* Dump header with NAME. */ + static inline void + dump_header (const char *name) + { + fprintf (stderr, "%-48s %11s%15s%10s%17s%11s\n", name, "Leak", "Peak", + "Times", "Leak items", "Peak items"); + print_dash_line (); + } + + /* Compare wrapper used by qsort method. */ + static int + compare (const void *first, const void *second) + { + typedef std::pair<mem_location *, vec_usage *> mem_pair_t; + + const mem_pair_t f = *(const mem_pair_t *)first; + const mem_pair_t s = *(const mem_pair_t *)second; + + return (*f.second) < (*s.second); + } + + /* Current number of items allocated. */ + size_t m_items; + /* Peak value of number of allocated items. */ + size_t m_items_peak; }; - -/* Hashtable mapping vec addresses to descriptors. */ -static htab_t vec_desc_hash; +/* Vector memory description. */ +static mem_alloc_description <vec_usage> vec_mem_desc; -/* Hashtable helpers. */ -static hashval_t -hash_descriptor (const void *p) -{ - const struct vec_descriptor *const d = - (const struct vec_descriptor *) p; - return htab_hash_pointer (d->file) + d->line; -} -static int -eq_descriptor (const void *p1, const void *p2) -{ - const struct vec_descriptor *const d = (const struct vec_descriptor *) p1; - const struct vec_descriptor *const l = (const struct vec_descriptor *) p2; - return d->file == l->file && d->function == l->function && d->line == l->line; -} +/* Account the overhead. */ -/* Hashtable converting address of allocated field to loc descriptor. */ -static htab_t ptr_hash; -struct ptr_hash_entry +void +vec_prefix::register_overhead (void *ptr, size_t size, size_t elements + MEM_STAT_DECL) { - void *ptr; - struct vec_descriptor *loc; - size_t allocated; -}; - -/* Hash table helpers functions. */ -static hashval_t -hash_ptr (const void *p) -{ - const struct ptr_hash_entry *const d = (const struct ptr_hash_entry *) p; - - return htab_hash_pointer (d->ptr); -} - -static int -eq_ptr (const void *p1, const void *p2) -{ - const struct ptr_hash_entry *const p = (const struct ptr_hash_entry *) p1; - - return (p->ptr == p2); + vec_mem_desc.register_descriptor (ptr, VEC_ORIGIN, false + FINAL_PASS_MEM_STAT); + vec_usage *usage = vec_mem_desc.register_instance_overhead (size, ptr); + usage->m_items += elements; + if (usage->m_items_peak < usage->m_items) + usage->m_items_peak = usage->m_items; } -/* Return descriptor for given call site, create new one if needed. */ -static struct vec_descriptor * -vec_descriptor (const char *name, int line, const char *function) -{ - struct vec_descriptor loc; - struct vec_descriptor **slot; - - loc.file = name; - loc.line = line; - loc.function = function; - if (!vec_desc_hash) - vec_desc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL); +/* Notice that the memory allocated for the vector has been freed. */ - slot = (struct vec_descriptor **) htab_find_slot (vec_desc_hash, &loc, - INSERT); - if (*slot) - return *slot; - *slot = XCNEW (struct vec_descriptor); - (*slot)->file = name; - (*slot)->line = line; - (*slot)->function = function; - (*slot)->allocated = 0; - (*slot)->peak = 0; - return *slot; -} - -/* Account the overhead. */ -static void -register_overhead (struct vec_prefix *ptr, size_t size, - const char *name, int line, const char *function) +void +vec_prefix::release_overhead (void *ptr, size_t size, bool in_dtor + MEM_STAT_DECL) { - struct vec_descriptor *loc = vec_descriptor (name, line, function); - struct ptr_hash_entry *p = XNEW (struct ptr_hash_entry); - PTR *slot; - - p->ptr = ptr; - p->loc = loc; - p->allocated = size; - if (!ptr_hash) - ptr_hash = htab_create (10, hash_ptr, eq_ptr, NULL); - slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr), INSERT); - gcc_assert (!*slot); - *slot = p; - - loc->allocated += size; - if (loc->peak < loc->allocated) - loc->peak += loc->allocated; - loc->times++; + if (!vec_mem_desc.contains_descriptor_for_instance (ptr)) + vec_mem_desc.register_descriptor (ptr, VEC_ORIGIN, + false FINAL_PASS_MEM_STAT); + vec_mem_desc.release_instance_overhead (ptr, size, in_dtor); } -/* Notice that the pointer has been freed. */ -static void -free_overhead (struct vec_prefix *ptr) -{ - PTR *slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr), - NO_INSERT); - struct ptr_hash_entry *p = (struct ptr_hash_entry *) *slot; - p->loc->allocated -= p->allocated; - htab_clear_slot (ptr_hash, slot); - free (p); -} -void -vec_heap_free (void *ptr) +/* Calculate the number of slots to reserve a vector, making sure that + it is of at least DESIRED size by growing ALLOC exponentially. */ + +unsigned +vec_prefix::calculate_allocation_1 (unsigned alloc, unsigned desired) { - free_overhead ((struct vec_prefix *)ptr); - free (ptr); -} -#endif - -/* Calculate the new ALLOC value, making sure that RESERVE slots are - free. If EXACT grow exactly, otherwise grow exponentially. */ - -static inline unsigned -calculate_allocation (const struct vec_prefix *pfx, int reserve, bool exact) -{ - unsigned alloc = 0; - unsigned num = 0; - - gcc_assert (reserve >= 0); + /* We must have run out of room. */ + gcc_assert (alloc < desired); - if (pfx) - { - alloc = pfx->alloc; - num = pfx->num; - } - else if (!reserve) - /* If there's no prefix, and we've not requested anything, then we - will create a NULL vector. */ - return 0; - - /* We must have run out of room. */ - gcc_assert (alloc - num < (unsigned) reserve); + /* Exponential growth. */ + if (!alloc) + alloc = 4; + else if (alloc < 16) + /* Double when small. */ + alloc = alloc * 2; + else + /* Grow slower when large. */ + alloc = (alloc * 3 / 2); - if (exact) - /* Exact size. */ - alloc = num + reserve; - else - { - /* Exponential growth. */ - if (!alloc) - alloc = 4; - else if (alloc < 16) - /* Double when small. */ - alloc = alloc * 2; - else - /* Grow slower when large. */ - alloc = (alloc * 3 / 2); - - /* If this is still too small, set it to the right size. */ - if (alloc < num + reserve) - alloc = num + reserve; - } + /* If this is still too small, set it to the right size. */ + if (alloc < desired) + alloc = desired; return alloc; } -/* Ensure there are at least RESERVE free slots in VEC. If EXACT grow - exactly, else grow exponentially. As a special case, if VEC is - NULL and RESERVE is 0, no vector will be created. The vector's - trailing array is at VEC_OFFSET offset and consists of ELT_SIZE - sized elements. */ - -static void * -vec_gc_o_reserve_1 (void *vec, int reserve, size_t vec_offset, size_t elt_size, - bool exact MEM_STAT_DECL) -{ - struct vec_prefix *pfx = (struct vec_prefix *) vec; - unsigned alloc = calculate_allocation (pfx, reserve, exact); - - if (!alloc) - { - if (pfx) - ggc_free (pfx); - return NULL; - } - - vec = ggc_realloc_stat (vec, vec_offset + alloc * elt_size PASS_MEM_STAT); - ((struct vec_prefix *)vec)->alloc = alloc; - if (!pfx) - ((struct vec_prefix *)vec)->num = 0; - - return vec; -} - -/* Ensure there are at least RESERVE free slots in VEC, growing - exponentially. If RESERVE < 0 grow exactly, else grow - exponentially. As a special case, if VEC is NULL, and RESERVE is - 0, no vector will be created. */ - -void * -vec_gc_p_reserve (void *vec, int reserve MEM_STAT_DECL) -{ - return vec_gc_o_reserve_1 (vec, reserve, - offsetof (struct vec_prefix, vec), - sizeof (void *), false - PASS_MEM_STAT); -} - -/* Ensure there are at least RESERVE free slots in VEC, growing - exactly. If RESERVE < 0 grow exactly, else grow exponentially. As - a special case, if VEC is NULL, and RESERVE is 0, no vector will be - created. */ - -void * -vec_gc_p_reserve_exact (void *vec, int reserve MEM_STAT_DECL) -{ - return vec_gc_o_reserve_1 (vec, reserve, - offsetof (struct vec_prefix, vec), - sizeof (void *), true - PASS_MEM_STAT); -} - -/* As for vec_gc_p_reserve, but for object vectors. The vector's - trailing array is at VEC_OFFSET offset and consists of ELT_SIZE - sized elements. */ - -void * -vec_gc_o_reserve (void *vec, int reserve, size_t vec_offset, size_t elt_size - MEM_STAT_DECL) -{ - return vec_gc_o_reserve_1 (vec, reserve, vec_offset, elt_size, false - PASS_MEM_STAT); -} - -/* As for vec_gc_p_reserve_exact, but for object vectors. The - vector's trailing array is at VEC_OFFSET offset and consists of - ELT_SIZE sized elements. */ - -void * -vec_gc_o_reserve_exact (void *vec, int reserve, size_t vec_offset, - size_t elt_size MEM_STAT_DECL) -{ - return vec_gc_o_reserve_1 (vec, reserve, vec_offset, elt_size, true - PASS_MEM_STAT); -} - -/* As for vec_gc_o_reserve_1, but for heap allocated vectors. */ - -static void * -vec_heap_o_reserve_1 (void *vec, int reserve, size_t vec_offset, - size_t elt_size, bool exact MEM_STAT_DECL) -{ - struct vec_prefix *pfx = (struct vec_prefix *) vec; - unsigned alloc = calculate_allocation (pfx, reserve, exact); - - if (!alloc) - { - if (pfx) - vec_heap_free (pfx); - return NULL; - } - -#ifdef GATHER_STATISTICS - if (vec) - free_overhead (pfx); -#endif - - vec = xrealloc (vec, vec_offset + alloc * elt_size); - ((struct vec_prefix *)vec)->alloc = alloc; - if (!pfx) - ((struct vec_prefix *)vec)->num = 0; -#ifdef GATHER_STATISTICS - if (vec) - register_overhead ((struct vec_prefix *)vec, - vec_offset + alloc * elt_size PASS_MEM_STAT); -#endif - - return vec; -} - -/* As for vec_gc_p_reserve, but for heap allocated vectors. */ - -void * -vec_heap_p_reserve (void *vec, int reserve MEM_STAT_DECL) -{ - return vec_heap_o_reserve_1 (vec, reserve, - offsetof (struct vec_prefix, vec), - sizeof (void *), false - PASS_MEM_STAT); -} - -/* As for vec_gc_p_reserve_exact, but for heap allocated vectors. */ - -void * -vec_heap_p_reserve_exact (void *vec, int reserve MEM_STAT_DECL) -{ - return vec_heap_o_reserve_1 (vec, reserve, - offsetof (struct vec_prefix, vec), - sizeof (void *), true - PASS_MEM_STAT); -} - -/* As for vec_gc_o_reserve, but for heap allocated vectors. */ - -void * -vec_heap_o_reserve (void *vec, int reserve, size_t vec_offset, size_t elt_size - MEM_STAT_DECL) -{ - return vec_heap_o_reserve_1 (vec, reserve, vec_offset, elt_size, false - PASS_MEM_STAT); -} - -/* As for vec_gc_o_reserve_exact, but for heap allocated vectors. */ - -void * -vec_heap_o_reserve_exact (void *vec, int reserve, size_t vec_offset, - size_t elt_size MEM_STAT_DECL) -{ - return vec_heap_o_reserve_1 (vec, reserve, vec_offset, elt_size, true - PASS_MEM_STAT); -} - -/* Stack vectors are a little different. VEC_alloc turns into a call - to vec_stack_p_reserve_exact1 and passes in space allocated via a - call to alloca. We record that pointer so that we know that we - shouldn't free it. If the vector is resized, we resize it on the - heap. We record the pointers in a vector and search it in LIFO - order--i.e., we look for the newest stack vectors first. We don't - expect too many stack vectors at any one level, and searching from - the end should normally be efficient even if they are used in a - recursive function. */ - -typedef void *void_p; -DEF_VEC_P(void_p); -DEF_VEC_ALLOC_P(void_p,heap); +/* Dump per-site memory statistics. */ -static VEC(void_p,heap) *stack_vecs; - -/* Allocate a vector which uses alloca for the initial allocation. - SPACE is space allocated using alloca, ALLOC is the number of - entries allocated. */ - -void * -vec_stack_p_reserve_exact_1 (int alloc, void *space) -{ - struct vec_prefix *pfx = (struct vec_prefix *) space; - - VEC_safe_push (void_p, heap, stack_vecs, space); - - pfx->num = 0; - pfx->alloc = alloc; - - return space; -} - -/* Grow a vector allocated using alloca. When this happens, we switch - back to heap allocation. We remove the vector from stack_vecs, if - it is there, since we no longer need to avoid freeing it. */ - -static void * -vec_stack_o_reserve_1 (void *vec, int reserve, size_t vec_offset, - size_t elt_size, bool exact MEM_STAT_DECL) -{ - bool found; - unsigned int ix; - void *newvec; - - found = false; - for (ix = VEC_length (void_p, stack_vecs); ix > 0; --ix) - { - if (VEC_index (void_p, stack_vecs, ix - 1) == vec) - { - VEC_unordered_remove (void_p, stack_vecs, ix - 1); - found = true; - break; - } - } - - if (!found) - { - /* VEC is already on the heap. */ - return vec_heap_o_reserve_1 (vec, reserve, vec_offset, elt_size, - exact PASS_MEM_STAT); - } - - /* Move VEC to the heap. */ - reserve += ((struct vec_prefix *) vec)->num; - newvec = vec_heap_o_reserve_1 (NULL, reserve, vec_offset, elt_size, - exact PASS_MEM_STAT); - if (newvec && vec) - { - ((struct vec_prefix *) newvec)->num = ((struct vec_prefix *) vec)->num; - memcpy (((struct vec_prefix *) newvec)->vec, - ((struct vec_prefix *) vec)->vec, - ((struct vec_prefix *) vec)->num * elt_size); - } - return newvec; -} - -/* Grow a vector allocated on the stack. */ - -void * -vec_stack_p_reserve (void *vec, int reserve MEM_STAT_DECL) -{ - return vec_stack_o_reserve_1 (vec, reserve, - offsetof (struct vec_prefix, vec), - sizeof (void *), false - PASS_MEM_STAT); -} - -/* Exact version of vec_stack_p_reserve. */ - -void * -vec_stack_p_reserve_exact (void *vec, int reserve MEM_STAT_DECL) -{ - return vec_stack_o_reserve_1 (vec, reserve, - offsetof (struct vec_prefix, vec), - sizeof (void *), true - PASS_MEM_STAT); -} - -/* Like vec_stack_p_reserve, but for objects. */ - -void * -vec_stack_o_reserve (void *vec, int reserve, size_t vec_offset, - size_t elt_size MEM_STAT_DECL) -{ - return vec_stack_o_reserve_1 (vec, reserve, vec_offset, elt_size, false - PASS_MEM_STAT); -} - -/* Like vec_stack_p_reserve_exact, but for objects. */ - -void * -vec_stack_o_reserve_exact (void *vec, int reserve, size_t vec_offset, - size_t elt_size MEM_STAT_DECL) -{ - return vec_stack_o_reserve_1 (vec, reserve, vec_offset, elt_size, true - PASS_MEM_STAT); -} - -/* Free a vector allocated on the stack. Don't actually free it if we - find it in the hash table. */ - -void -vec_stack_free (void *vec) -{ - unsigned int ix; - - for (ix = VEC_length (void_p, stack_vecs); ix > 0; --ix) - { - if (VEC_index (void_p, stack_vecs, ix - 1) == vec) - { - VEC_unordered_remove (void_p, stack_vecs, ix - 1); - return; - } - } - - /* VEC was not on the list of vecs allocated on the stack, so it - must be allocated on the heap. */ - vec_heap_free (vec); -} - -#if ENABLE_CHECKING -/* Issue a vector domain error, and then fall over. */ - -void -vec_assert_fail (const char *op, const char *struct_name, - const char *file, unsigned int line, const char *function) -{ - internal_error ("vector %s %s domain error, in %s at %s:%u", - struct_name, op, function, trim_filename (file), line); -} -#endif - -#ifdef GATHER_STATISTICS -/* Helper for qsort; sort descriptors by amount of memory consumed. */ -static int -cmp_statistic (const void *loc1, const void *loc2) -{ - const struct vec_descriptor *const l1 = - *(const struct vec_descriptor *const *) loc1; - const struct vec_descriptor *const l2 = - *(const struct vec_descriptor *const *) loc2; - long diff; - diff = l1->allocated - l2->allocated; - if (!diff) - diff = l1->peak - l2->peak; - if (!diff) - diff = l1->times - l2->times; - return diff > 0 ? 1 : diff < 0 ? -1 : 0; -} -/* Collect array of the descriptors from hashtable. */ -static struct vec_descriptor **loc_array; -static int -add_statistics (void **slot, void *b) -{ - int *n = (int *)b; - loc_array[*n] = (struct vec_descriptor *) *slot; - (*n)++; - return 1; -} - -/* Dump per-site memory statistics. */ -#endif void dump_vec_loc_statistics (void) { -#ifdef GATHER_STATISTICS - int nentries = 0; - char s[4096]; - size_t allocated = 0; - size_t times = 0; - int i; + vec_mem_desc.dump (VEC_ORIGIN); +} + +#if CHECKING_P +/* Report qsort comparator CMP consistency check failure with P1, P2, P3 as + witness elements. */ +ATTRIBUTE_NORETURN ATTRIBUTE_COLD +static void +qsort_chk_error (const void *p1, const void *p2, const void *p3, + int (*cmp) (const void *, const void *)) +{ + if (!p3) + { + int r1 = cmp (p1, p2), r2 = cmp (p2, p1); + error ("qsort comparator not anti-commutative: %d, %d", r1, r2); + } + else if (p1 == p2) + { + int r = cmp (p1, p3); + error ("qsort comparator non-negative on sorted output: %d", r); + } + else + { + int r1 = cmp (p1, p2), r2 = cmp (p2, p3), r3 = cmp (p1, p3); + error ("qsort comparator not transitive: %d, %d, %d", r1, r2, r3); + } + internal_error ("qsort checking failed"); +} - loc_array = XCNEWVEC (struct vec_descriptor *, vec_desc_hash->n_elements); - fprintf (stderr, "Heap vectors:\n"); - fprintf (stderr, "\n%-48s %10s %10s %10s\n", - "source location", "Leak", "Peak", "Times"); - fprintf (stderr, "-------------------------------------------------------\n"); - htab_traverse (vec_desc_hash, add_statistics, &nentries); - qsort (loc_array, nentries, sizeof (*loc_array), cmp_statistic); - for (i = 0; i < nentries; i++) - { - struct vec_descriptor *d = loc_array[i]; - allocated += d->allocated; - times += d->times; - } - for (i = 0; i < nentries; i++) +/* Wrapper around qsort with checking that CMP is consistent on given input. + + Strictly speaking, passing invalid (non-transitive, non-anti-commutative) + comparators to libc qsort can result in undefined behavior. Therefore we + should ideally perform consistency checks prior to invoking qsort, but in + order to do that optimally we'd need to sort the array ourselves beforehand + with a sorting routine known to be "safe". Instead, we expect that most + implementations in practice will still produce some permutation of input + array even for invalid comparators, which enables us to perform checks on + the output array. */ +void +qsort_chk (void *base, size_t n, size_t size, + int (*cmp)(const void *, const void *)) +{ + (qsort) (base, n, size, cmp); +#if 0 +#define LIM(n) (n) +#else + /* Limit overall time complexity to O(n log n). */ +#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) + 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. */ + for (i1 = 0; i1 < n; i1 = i2) { - struct vec_descriptor *d = loc_array[i]; - const char *s1 = d->file; - const char *s2; - while ((s2 = strstr (s1, "gcc/"))) - s1 = s2 + 4; - sprintf (s, "%s:%i (%s)", s1, d->line, d->function); - s[48] = 0; - fprintf (stderr, "%-48s %10li:%4.1f%% %10li %10li:%4.1f%% \n", s, - (long)d->allocated, - (d->allocated) * 100.0 / allocated, - (long)d->peak, - (long)d->times, - (d->times) * 100.0 / times); + /* Position i2 one past last element that compares equal to i1'th. */ + for (i2 = i1 + 1; i2 < n; i2++) + if (CMP (i1, i2)) + break; + else if (CMP (i2, i1)) + return ERR2 (i1, i2); + size_t lim1 = LIM (i2 - i1), lim2 = LIM (n - i2); + /* Verify that other pairs within current span compare equal. */ + for (i = i1 + 1; i + 1 < i2; i++) + for (j = i + 1; j < i1 + lim1; j++) + if (CMP (i, j)) + return ERR3 (i, i1, j); + else if (CMP (j, i)) + return ERR2 (i, j); + /* Verify that elements within this span compare less than + elements beyond the span. */ + for (i = i1; i < i2; i++) + for (j = i2; j < i2 + lim2; j++) + if (CMP (i, j) >= 0) + return ERR3 (i, i1, j); + else if (CMP (j, i) <= 0) + return ERR2 (i, j); } - fprintf (stderr, "%-48s %10ld %10ld\n", - "Total", (long)allocated, (long)times); - fprintf (stderr, "\n%-48s %10s %10s %10s\n", - "source location", "Leak", "Peak", "Times"); - fprintf (stderr, "-------------------------------------------------------\n"); -#endif +#undef ERR3 +#undef ERR2 +#undef CMP +#undef ELT +#undef LIM +} +#endif /* #if CHECKING_P */ + +#ifndef GENERATOR_FILE +#if CHECKING_P + +namespace selftest { + +/* Selftests. */ + +/* Call V.safe_push for all ints from START up to, but not including LIMIT. + Helper function for selftests. */ + +static void +safe_push_range (vec <int>&v, int start, int limit) +{ + for (int i = start; i < limit; i++) + v.safe_push (i); +} + +/* Verify that vec::quick_push works correctly. */ + +static void +test_quick_push () +{ + auto_vec <int> v; + ASSERT_EQ (0, v.length ()); + v.reserve (3); + ASSERT_EQ (0, v.length ()); + ASSERT_TRUE (v.space (3)); + v.quick_push (5); + v.quick_push (6); + v.quick_push (7); + ASSERT_EQ (3, v.length ()); + ASSERT_EQ (5, v[0]); + ASSERT_EQ (6, v[1]); + ASSERT_EQ (7, v[2]); +} + +/* Verify that vec::safe_push works correctly. */ + +static void +test_safe_push () +{ + auto_vec <int> v; + ASSERT_EQ (0, v.length ()); + v.safe_push (5); + v.safe_push (6); + v.safe_push (7); + ASSERT_EQ (3, v.length ()); + ASSERT_EQ (5, v[0]); + ASSERT_EQ (6, v[1]); + ASSERT_EQ (7, v[2]); +} + +/* Verify that vec::truncate works correctly. */ + +static void +test_truncate () +{ + auto_vec <int> v; + ASSERT_EQ (0, v.length ()); + safe_push_range (v, 0, 10); + ASSERT_EQ (10, v.length ()); + + v.truncate (5); + ASSERT_EQ (5, v.length ()); +} + +/* Verify that vec::safe_grow_cleared works correctly. */ + +static void +test_safe_grow_cleared () +{ + auto_vec <int> v; + ASSERT_EQ (0, v.length ()); + v.safe_grow_cleared (50); + ASSERT_EQ (50, v.length ()); + ASSERT_EQ (0, v[0]); + ASSERT_EQ (0, v[49]); } + +/* Verify that vec::pop works correctly. */ + +static void +test_pop () +{ + auto_vec <int> v; + safe_push_range (v, 5, 20); + ASSERT_EQ (15, v.length ()); + + int last = v.pop (); + ASSERT_EQ (19, last); + ASSERT_EQ (14, v.length ()); +} + +/* Verify that vec::safe_insert works correctly. */ + +static void +test_safe_insert () +{ + auto_vec <int> v; + safe_push_range (v, 0, 10); + v.safe_insert (5, 42); + ASSERT_EQ (4, v[4]); + ASSERT_EQ (42, v[5]); + ASSERT_EQ (5, v[6]); + ASSERT_EQ (11, v.length ()); +} + +/* Verify that vec::ordered_remove works correctly. */ + +static void +test_ordered_remove () +{ + auto_vec <int> v; + safe_push_range (v, 0, 10); + v.ordered_remove (5); + ASSERT_EQ (4, v[4]); + ASSERT_EQ (6, v[5]); + ASSERT_EQ (9, v.length ()); +} + +/* Verify that vec::unordered_remove works correctly. */ + +static void +test_unordered_remove () +{ + auto_vec <int> v; + safe_push_range (v, 0, 10); + v.unordered_remove (5); + ASSERT_EQ (9, v.length ()); +} + +/* Verify that vec::block_remove works correctly. */ + +static void +test_block_remove () +{ + auto_vec <int> v; + safe_push_range (v, 0, 10); + v.block_remove (5, 3); + ASSERT_EQ (3, v[3]); + ASSERT_EQ (4, v[4]); + ASSERT_EQ (8, v[5]); + ASSERT_EQ (9, v[6]); + ASSERT_EQ (7, v.length ()); +} + +/* Comparator for use by test_qsort. */ + +static int +reverse_cmp (const void *p_i, const void *p_j) +{ + return *(const int *)p_j - *(const int *)p_i; +} + +/* Verify that vec::qsort works correctly. */ + +static void +test_qsort () +{ + auto_vec <int> v; + safe_push_range (v, 0, 10); + v.qsort (reverse_cmp); + ASSERT_EQ (9, v[0]); + ASSERT_EQ (8, v[1]); + ASSERT_EQ (1, v[8]); + ASSERT_EQ (0, v[9]); + ASSERT_EQ (10, v.length ()); +} + +/* Run all of the selftests within this file. */ + +void +vec_c_tests () +{ + test_quick_push (); + test_safe_push (); + test_truncate (); + test_safe_grow_cleared (); + test_pop (); + test_safe_insert (); + test_ordered_remove (); + test_unordered_remove (); + test_block_remove (); + test_qsort (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ +#endif /* #ifndef GENERATOR_FILE */