Mercurial > hg > CbC > GCC_original
comparison gcc/vec.c @ 16:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
comparison
equal
deleted
inserted
replaced
15:561a7518be6b | 16:04ced10e8804 |
---|---|
1 /* Vector API for GNU compiler. | 1 /* Vector API for GNU compiler. |
2 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 | 2 Copyright (C) 2004-2017 Free Software Foundation, Inc. |
3 Free Software Foundation, Inc. | |
4 Contributed by Nathan Sidwell <nathan@codesourcery.com> | 3 Contributed by Nathan Sidwell <nathan@codesourcery.com> |
4 Re-implemented in C++ by Diego Novillo <dnovillo@google.com> | |
5 | 5 |
6 This file is part of GCC. | 6 This file is part of GCC. |
7 | 7 |
8 GCC is free software; you can redistribute it and/or modify it under | 8 GCC is free software; you can redistribute it and/or modify it under |
9 the terms of the GNU General Public License as published by the Free | 9 the terms of the GNU General Public License as published by the Free |
26 #else | 26 #else |
27 #include "config.h" | 27 #include "config.h" |
28 #endif | 28 #endif |
29 | 29 |
30 #include "system.h" | 30 #include "system.h" |
31 #include "ggc.h" | |
32 #include "vec.h" | |
33 #include "coretypes.h" | 31 #include "coretypes.h" |
32 #include "hash-table.h" | |
33 #include "selftest.h" | |
34 #ifdef GENERATOR_FILE | |
35 #include "errors.h" | |
36 #else | |
37 #include "input.h" | |
34 #include "diagnostic-core.h" | 38 #include "diagnostic-core.h" |
35 #include "hashtab.h" | 39 #endif |
36 | 40 |
37 struct vec_prefix | 41 /* vNULL is an empty type with a template cast operation that returns |
38 { | 42 a zero-initialized vec<T, A, L> instance. Use this when you want |
39 unsigned num; | 43 to assign nil values to new vec instances or pass a nil vector as |
40 unsigned alloc; | 44 a function call argument. |
41 void *vec[1]; | 45 |
46 We use this technique because vec<T, A, L> must be PODs (they are | |
47 stored in unions and passed in vararg functions), this means that | |
48 they cannot have ctors/dtors. */ | |
49 vnull vNULL; | |
50 | |
51 /* Vector memory usage. */ | |
52 struct vec_usage: public mem_usage | |
53 { | |
54 /* Default constructor. */ | |
55 vec_usage (): m_items (0), m_items_peak (0) {} | |
56 | |
57 /* Constructor. */ | |
58 vec_usage (size_t allocated, size_t times, size_t peak, | |
59 size_t items, size_t items_peak) | |
60 : mem_usage (allocated, times, peak), | |
61 m_items (items), m_items_peak (items_peak) {} | |
62 | |
63 /* Comparison operator. */ | |
64 inline bool | |
65 operator< (const vec_usage &second) const | |
66 { | |
67 return (m_allocated == second.m_allocated ? | |
68 (m_peak == second.m_peak ? m_times < second.m_times | |
69 : m_peak < second.m_peak) : m_allocated < second.m_allocated); | |
70 } | |
71 | |
72 /* Sum the usage with SECOND usage. */ | |
73 vec_usage | |
74 operator+ (const vec_usage &second) | |
75 { | |
76 return vec_usage (m_allocated + second.m_allocated, | |
77 m_times + second.m_times, | |
78 m_peak + second.m_peak, | |
79 m_items + second.m_items, | |
80 m_items_peak + second.m_items_peak); | |
81 } | |
82 | |
83 /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */ | |
84 inline void | |
85 dump (mem_location *loc, mem_usage &total) const | |
86 { | |
87 char s[4096]; | |
88 sprintf (s, "%s:%i (%s)", loc->get_trimmed_filename (), | |
89 loc->m_line, loc->m_function); | |
90 | |
91 s[48] = '\0'; | |
92 | |
93 fprintf (stderr, "%-48s %10li:%4.1f%%%10li%10li:%4.1f%%%11li%11li\n", s, | |
94 (long)m_allocated, m_allocated * 100.0 / total.m_allocated, | |
95 (long)m_peak, (long)m_times, m_times * 100.0 / total.m_times, | |
96 (long)m_items, (long)m_items_peak); | |
97 } | |
98 | |
99 /* Dump footer. */ | |
100 inline void | |
101 dump_footer () | |
102 { | |
103 print_dash_line (); | |
104 fprintf (stderr, "%s%55li%25li%17li\n", "Total", (long)m_allocated, | |
105 (long)m_times, (long)m_items); | |
106 print_dash_line (); | |
107 } | |
108 | |
109 /* Dump header with NAME. */ | |
110 static inline void | |
111 dump_header (const char *name) | |
112 { | |
113 fprintf (stderr, "%-48s %11s%15s%10s%17s%11s\n", name, "Leak", "Peak", | |
114 "Times", "Leak items", "Peak items"); | |
115 print_dash_line (); | |
116 } | |
117 | |
118 /* Compare wrapper used by qsort method. */ | |
119 static int | |
120 compare (const void *first, const void *second) | |
121 { | |
122 typedef std::pair<mem_location *, vec_usage *> mem_pair_t; | |
123 | |
124 const mem_pair_t f = *(const mem_pair_t *)first; | |
125 const mem_pair_t s = *(const mem_pair_t *)second; | |
126 | |
127 return (*f.second) < (*s.second); | |
128 } | |
129 | |
130 /* Current number of items allocated. */ | |
131 size_t m_items; | |
132 /* Peak value of number of allocated items. */ | |
133 size_t m_items_peak; | |
42 }; | 134 }; |
43 | 135 |
44 | 136 /* Vector memory description. */ |
45 #ifdef GATHER_STATISTICS | 137 static mem_alloc_description <vec_usage> vec_mem_desc; |
46 | |
47 /* Store information about each particular vector. */ | |
48 struct vec_descriptor | |
49 { | |
50 const char *function; | |
51 const char *file; | |
52 int line; | |
53 size_t allocated; | |
54 size_t times; | |
55 size_t peak; | |
56 }; | |
57 | |
58 | |
59 /* Hashtable mapping vec addresses to descriptors. */ | |
60 static htab_t vec_desc_hash; | |
61 | |
62 /* Hashtable helpers. */ | |
63 static hashval_t | |
64 hash_descriptor (const void *p) | |
65 { | |
66 const struct vec_descriptor *const d = | |
67 (const struct vec_descriptor *) p; | |
68 return htab_hash_pointer (d->file) + d->line; | |
69 } | |
70 static int | |
71 eq_descriptor (const void *p1, const void *p2) | |
72 { | |
73 const struct vec_descriptor *const d = (const struct vec_descriptor *) p1; | |
74 const struct vec_descriptor *const l = (const struct vec_descriptor *) p2; | |
75 return d->file == l->file && d->function == l->function && d->line == l->line; | |
76 } | |
77 | |
78 /* Hashtable converting address of allocated field to loc descriptor. */ | |
79 static htab_t ptr_hash; | |
80 struct ptr_hash_entry | |
81 { | |
82 void *ptr; | |
83 struct vec_descriptor *loc; | |
84 size_t allocated; | |
85 }; | |
86 | |
87 /* Hash table helpers functions. */ | |
88 static hashval_t | |
89 hash_ptr (const void *p) | |
90 { | |
91 const struct ptr_hash_entry *const d = (const struct ptr_hash_entry *) p; | |
92 | |
93 return htab_hash_pointer (d->ptr); | |
94 } | |
95 | |
96 static int | |
97 eq_ptr (const void *p1, const void *p2) | |
98 { | |
99 const struct ptr_hash_entry *const p = (const struct ptr_hash_entry *) p1; | |
100 | |
101 return (p->ptr == p2); | |
102 } | |
103 | |
104 /* Return descriptor for given call site, create new one if needed. */ | |
105 static struct vec_descriptor * | |
106 vec_descriptor (const char *name, int line, const char *function) | |
107 { | |
108 struct vec_descriptor loc; | |
109 struct vec_descriptor **slot; | |
110 | |
111 loc.file = name; | |
112 loc.line = line; | |
113 loc.function = function; | |
114 if (!vec_desc_hash) | |
115 vec_desc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL); | |
116 | |
117 slot = (struct vec_descriptor **) htab_find_slot (vec_desc_hash, &loc, | |
118 INSERT); | |
119 if (*slot) | |
120 return *slot; | |
121 *slot = XCNEW (struct vec_descriptor); | |
122 (*slot)->file = name; | |
123 (*slot)->line = line; | |
124 (*slot)->function = function; | |
125 (*slot)->allocated = 0; | |
126 (*slot)->peak = 0; | |
127 return *slot; | |
128 } | |
129 | 138 |
130 /* Account the overhead. */ | 139 /* Account the overhead. */ |
131 static void | |
132 register_overhead (struct vec_prefix *ptr, size_t size, | |
133 const char *name, int line, const char *function) | |
134 { | |
135 struct vec_descriptor *loc = vec_descriptor (name, line, function); | |
136 struct ptr_hash_entry *p = XNEW (struct ptr_hash_entry); | |
137 PTR *slot; | |
138 | |
139 p->ptr = ptr; | |
140 p->loc = loc; | |
141 p->allocated = size; | |
142 if (!ptr_hash) | |
143 ptr_hash = htab_create (10, hash_ptr, eq_ptr, NULL); | |
144 slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr), INSERT); | |
145 gcc_assert (!*slot); | |
146 *slot = p; | |
147 | |
148 loc->allocated += size; | |
149 if (loc->peak < loc->allocated) | |
150 loc->peak += loc->allocated; | |
151 loc->times++; | |
152 } | |
153 | |
154 /* Notice that the pointer has been freed. */ | |
155 static void | |
156 free_overhead (struct vec_prefix *ptr) | |
157 { | |
158 PTR *slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr), | |
159 NO_INSERT); | |
160 struct ptr_hash_entry *p = (struct ptr_hash_entry *) *slot; | |
161 p->loc->allocated -= p->allocated; | |
162 htab_clear_slot (ptr_hash, slot); | |
163 free (p); | |
164 } | |
165 | 140 |
166 void | 141 void |
167 vec_heap_free (void *ptr) | 142 vec_prefix::register_overhead (void *ptr, size_t size, size_t elements |
168 { | 143 MEM_STAT_DECL) |
169 free_overhead ((struct vec_prefix *)ptr); | 144 { |
170 free (ptr); | 145 vec_mem_desc.register_descriptor (ptr, VEC_ORIGIN, false |
171 } | 146 FINAL_PASS_MEM_STAT); |
172 #endif | 147 vec_usage *usage = vec_mem_desc.register_instance_overhead (size, ptr); |
173 | 148 usage->m_items += elements; |
174 /* Calculate the new ALLOC value, making sure that RESERVE slots are | 149 if (usage->m_items_peak < usage->m_items) |
175 free. If EXACT grow exactly, otherwise grow exponentially. */ | 150 usage->m_items_peak = usage->m_items; |
176 | 151 } |
177 static inline unsigned | 152 |
178 calculate_allocation (const struct vec_prefix *pfx, int reserve, bool exact) | 153 /* Notice that the memory allocated for the vector has been freed. */ |
179 { | 154 |
180 unsigned alloc = 0; | 155 void |
181 unsigned num = 0; | 156 vec_prefix::release_overhead (void *ptr, size_t size, bool in_dtor |
182 | 157 MEM_STAT_DECL) |
183 gcc_assert (reserve >= 0); | 158 { |
184 | 159 if (!vec_mem_desc.contains_descriptor_for_instance (ptr)) |
185 if (pfx) | 160 vec_mem_desc.register_descriptor (ptr, VEC_ORIGIN, |
161 false FINAL_PASS_MEM_STAT); | |
162 vec_mem_desc.release_instance_overhead (ptr, size, in_dtor); | |
163 } | |
164 | |
165 | |
166 /* Calculate the number of slots to reserve a vector, making sure that | |
167 it is of at least DESIRED size by growing ALLOC exponentially. */ | |
168 | |
169 unsigned | |
170 vec_prefix::calculate_allocation_1 (unsigned alloc, unsigned desired) | |
171 { | |
172 /* We must have run out of room. */ | |
173 gcc_assert (alloc < desired); | |
174 | |
175 /* Exponential growth. */ | |
176 if (!alloc) | |
177 alloc = 4; | |
178 else if (alloc < 16) | |
179 /* Double when small. */ | |
180 alloc = alloc * 2; | |
181 else | |
182 /* Grow slower when large. */ | |
183 alloc = (alloc * 3 / 2); | |
184 | |
185 /* If this is still too small, set it to the right size. */ | |
186 if (alloc < desired) | |
187 alloc = desired; | |
188 return alloc; | |
189 } | |
190 | |
191 /* Dump per-site memory statistics. */ | |
192 | |
193 void | |
194 dump_vec_loc_statistics (void) | |
195 { | |
196 vec_mem_desc.dump (VEC_ORIGIN); | |
197 } | |
198 | |
199 #if CHECKING_P | |
200 /* Report qsort comparator CMP consistency check failure with P1, P2, P3 as | |
201 witness elements. */ | |
202 ATTRIBUTE_NORETURN ATTRIBUTE_COLD | |
203 static void | |
204 qsort_chk_error (const void *p1, const void *p2, const void *p3, | |
205 int (*cmp) (const void *, const void *)) | |
206 { | |
207 if (!p3) | |
186 { | 208 { |
187 alloc = pfx->alloc; | 209 int r1 = cmp (p1, p2), r2 = cmp (p2, p1); |
188 num = pfx->num; | 210 error ("qsort comparator not anti-commutative: %d, %d", r1, r2); |
189 } | 211 } |
190 else if (!reserve) | 212 else if (p1 == p2) |
191 /* If there's no prefix, and we've not requested anything, then we | 213 { |
192 will create a NULL vector. */ | 214 int r = cmp (p1, p3); |
193 return 0; | 215 error ("qsort comparator non-negative on sorted output: %d", r); |
194 | 216 } |
195 /* We must have run out of room. */ | |
196 gcc_assert (alloc - num < (unsigned) reserve); | |
197 | |
198 if (exact) | |
199 /* Exact size. */ | |
200 alloc = num + reserve; | |
201 else | 217 else |
202 { | 218 { |
203 /* Exponential growth. */ | 219 int r1 = cmp (p1, p2), r2 = cmp (p2, p3), r3 = cmp (p1, p3); |
204 if (!alloc) | 220 error ("qsort comparator not transitive: %d, %d, %d", r1, r2, r3); |
205 alloc = 4; | |
206 else if (alloc < 16) | |
207 /* Double when small. */ | |
208 alloc = alloc * 2; | |
209 else | |
210 /* Grow slower when large. */ | |
211 alloc = (alloc * 3 / 2); | |
212 | |
213 /* If this is still too small, set it to the right size. */ | |
214 if (alloc < num + reserve) | |
215 alloc = num + reserve; | |
216 } | 221 } |
217 return alloc; | 222 internal_error ("qsort checking failed"); |
218 } | 223 } |
219 | 224 |
220 /* Ensure there are at least RESERVE free slots in VEC. If EXACT grow | 225 /* Wrapper around qsort with checking that CMP is consistent on given input. |
221 exactly, else grow exponentially. As a special case, if VEC is | 226 |
222 NULL and RESERVE is 0, no vector will be created. The vector's | 227 Strictly speaking, passing invalid (non-transitive, non-anti-commutative) |
223 trailing array is at VEC_OFFSET offset and consists of ELT_SIZE | 228 comparators to libc qsort can result in undefined behavior. Therefore we |
224 sized elements. */ | 229 should ideally perform consistency checks prior to invoking qsort, but in |
225 | 230 order to do that optimally we'd need to sort the array ourselves beforehand |
226 static void * | 231 with a sorting routine known to be "safe". Instead, we expect that most |
227 vec_gc_o_reserve_1 (void *vec, int reserve, size_t vec_offset, size_t elt_size, | 232 implementations in practice will still produce some permutation of input |
228 bool exact MEM_STAT_DECL) | 233 array even for invalid comparators, which enables us to perform checks on |
229 { | 234 the output array. */ |
230 struct vec_prefix *pfx = (struct vec_prefix *) vec; | 235 void |
231 unsigned alloc = calculate_allocation (pfx, reserve, exact); | 236 qsort_chk (void *base, size_t n, size_t size, |
232 | 237 int (*cmp)(const void *, const void *)) |
233 if (!alloc) | 238 { |
239 (qsort) (base, n, size, cmp); | |
240 #if 0 | |
241 #define LIM(n) (n) | |
242 #else | |
243 /* Limit overall time complexity to O(n log n). */ | |
244 #define LIM(n) ((n) <= 16 ? (n) : 12 + floor_log2 (n)) | |
245 #endif | |
246 #define ELT(i) ((const char *) base + (i) * size) | |
247 #define CMP(i, j) cmp (ELT (i), ELT (j)) | |
248 #define ERR2(i, j) qsort_chk_error (ELT (i), ELT (j), NULL, cmp) | |
249 #define ERR3(i, j, k) qsort_chk_error (ELT (i), ELT (j), ELT (k), cmp) | |
250 size_t i1, i2, i, j; | |
251 /* This outer loop iterates over maximum spans [i1, i2) such that | |
252 elements within each span compare equal to each other. */ | |
253 for (i1 = 0; i1 < n; i1 = i2) | |
234 { | 254 { |
235 if (pfx) | 255 /* Position i2 one past last element that compares equal to i1'th. */ |
236 ggc_free (pfx); | 256 for (i2 = i1 + 1; i2 < n; i2++) |
237 return NULL; | 257 if (CMP (i1, i2)) |
258 break; | |
259 else if (CMP (i2, i1)) | |
260 return ERR2 (i1, i2); | |
261 size_t lim1 = LIM (i2 - i1), lim2 = LIM (n - i2); | |
262 /* Verify that other pairs within current span compare equal. */ | |
263 for (i = i1 + 1; i + 1 < i2; i++) | |
264 for (j = i + 1; j < i1 + lim1; j++) | |
265 if (CMP (i, j)) | |
266 return ERR3 (i, i1, j); | |
267 else if (CMP (j, i)) | |
268 return ERR2 (i, j); | |
269 /* Verify that elements within this span compare less than | |
270 elements beyond the span. */ | |
271 for (i = i1; i < i2; i++) | |
272 for (j = i2; j < i2 + lim2; j++) | |
273 if (CMP (i, j) >= 0) | |
274 return ERR3 (i, i1, j); | |
275 else if (CMP (j, i) <= 0) | |
276 return ERR2 (i, j); | |
238 } | 277 } |
239 | 278 #undef ERR3 |
240 vec = ggc_realloc_stat (vec, vec_offset + alloc * elt_size PASS_MEM_STAT); | 279 #undef ERR2 |
241 ((struct vec_prefix *)vec)->alloc = alloc; | 280 #undef CMP |
242 if (!pfx) | 281 #undef ELT |
243 ((struct vec_prefix *)vec)->num = 0; | 282 #undef LIM |
244 | 283 } |
245 return vec; | 284 #endif /* #if CHECKING_P */ |
246 } | 285 |
247 | 286 #ifndef GENERATOR_FILE |
248 /* Ensure there are at least RESERVE free slots in VEC, growing | 287 #if CHECKING_P |
249 exponentially. If RESERVE < 0 grow exactly, else grow | 288 |
250 exponentially. As a special case, if VEC is NULL, and RESERVE is | 289 namespace selftest { |
251 0, no vector will be created. */ | 290 |
252 | 291 /* Selftests. */ |
253 void * | 292 |
254 vec_gc_p_reserve (void *vec, int reserve MEM_STAT_DECL) | 293 /* Call V.safe_push for all ints from START up to, but not including LIMIT. |
255 { | 294 Helper function for selftests. */ |
256 return vec_gc_o_reserve_1 (vec, reserve, | 295 |
257 offsetof (struct vec_prefix, vec), | 296 static void |
258 sizeof (void *), false | 297 safe_push_range (vec <int>&v, int start, int limit) |
259 PASS_MEM_STAT); | 298 { |
260 } | 299 for (int i = start; i < limit; i++) |
261 | 300 v.safe_push (i); |
262 /* Ensure there are at least RESERVE free slots in VEC, growing | 301 } |
263 exactly. If RESERVE < 0 grow exactly, else grow exponentially. As | 302 |
264 a special case, if VEC is NULL, and RESERVE is 0, no vector will be | 303 /* Verify that vec::quick_push works correctly. */ |
265 created. */ | 304 |
266 | 305 static void |
267 void * | 306 test_quick_push () |
268 vec_gc_p_reserve_exact (void *vec, int reserve MEM_STAT_DECL) | 307 { |
269 { | 308 auto_vec <int> v; |
270 return vec_gc_o_reserve_1 (vec, reserve, | 309 ASSERT_EQ (0, v.length ()); |
271 offsetof (struct vec_prefix, vec), | 310 v.reserve (3); |
272 sizeof (void *), true | 311 ASSERT_EQ (0, v.length ()); |
273 PASS_MEM_STAT); | 312 ASSERT_TRUE (v.space (3)); |
274 } | 313 v.quick_push (5); |
275 | 314 v.quick_push (6); |
276 /* As for vec_gc_p_reserve, but for object vectors. The vector's | 315 v.quick_push (7); |
277 trailing array is at VEC_OFFSET offset and consists of ELT_SIZE | 316 ASSERT_EQ (3, v.length ()); |
278 sized elements. */ | 317 ASSERT_EQ (5, v[0]); |
279 | 318 ASSERT_EQ (6, v[1]); |
280 void * | 319 ASSERT_EQ (7, v[2]); |
281 vec_gc_o_reserve (void *vec, int reserve, size_t vec_offset, size_t elt_size | 320 } |
282 MEM_STAT_DECL) | 321 |
283 { | 322 /* Verify that vec::safe_push works correctly. */ |
284 return vec_gc_o_reserve_1 (vec, reserve, vec_offset, elt_size, false | 323 |
285 PASS_MEM_STAT); | 324 static void |
286 } | 325 test_safe_push () |
287 | 326 { |
288 /* As for vec_gc_p_reserve_exact, but for object vectors. The | 327 auto_vec <int> v; |
289 vector's trailing array is at VEC_OFFSET offset and consists of | 328 ASSERT_EQ (0, v.length ()); |
290 ELT_SIZE sized elements. */ | 329 v.safe_push (5); |
291 | 330 v.safe_push (6); |
292 void * | 331 v.safe_push (7); |
293 vec_gc_o_reserve_exact (void *vec, int reserve, size_t vec_offset, | 332 ASSERT_EQ (3, v.length ()); |
294 size_t elt_size MEM_STAT_DECL) | 333 ASSERT_EQ (5, v[0]); |
295 { | 334 ASSERT_EQ (6, v[1]); |
296 return vec_gc_o_reserve_1 (vec, reserve, vec_offset, elt_size, true | 335 ASSERT_EQ (7, v[2]); |
297 PASS_MEM_STAT); | 336 } |
298 } | 337 |
299 | 338 /* Verify that vec::truncate works correctly. */ |
300 /* As for vec_gc_o_reserve_1, but for heap allocated vectors. */ | 339 |
301 | 340 static void |
302 static void * | 341 test_truncate () |
303 vec_heap_o_reserve_1 (void *vec, int reserve, size_t vec_offset, | 342 { |
304 size_t elt_size, bool exact MEM_STAT_DECL) | 343 auto_vec <int> v; |
305 { | 344 ASSERT_EQ (0, v.length ()); |
306 struct vec_prefix *pfx = (struct vec_prefix *) vec; | 345 safe_push_range (v, 0, 10); |
307 unsigned alloc = calculate_allocation (pfx, reserve, exact); | 346 ASSERT_EQ (10, v.length ()); |
308 | 347 |
309 if (!alloc) | 348 v.truncate (5); |
310 { | 349 ASSERT_EQ (5, v.length ()); |
311 if (pfx) | 350 } |
312 vec_heap_free (pfx); | 351 |
313 return NULL; | 352 /* Verify that vec::safe_grow_cleared works correctly. */ |
314 } | 353 |
315 | 354 static void |
316 #ifdef GATHER_STATISTICS | 355 test_safe_grow_cleared () |
317 if (vec) | 356 { |
318 free_overhead (pfx); | 357 auto_vec <int> v; |
319 #endif | 358 ASSERT_EQ (0, v.length ()); |
320 | 359 v.safe_grow_cleared (50); |
321 vec = xrealloc (vec, vec_offset + alloc * elt_size); | 360 ASSERT_EQ (50, v.length ()); |
322 ((struct vec_prefix *)vec)->alloc = alloc; | 361 ASSERT_EQ (0, v[0]); |
323 if (!pfx) | 362 ASSERT_EQ (0, v[49]); |
324 ((struct vec_prefix *)vec)->num = 0; | 363 } |
325 #ifdef GATHER_STATISTICS | 364 |
326 if (vec) | 365 /* Verify that vec::pop works correctly. */ |
327 register_overhead ((struct vec_prefix *)vec, | 366 |
328 vec_offset + alloc * elt_size PASS_MEM_STAT); | 367 static void |
329 #endif | 368 test_pop () |
330 | 369 { |
331 return vec; | 370 auto_vec <int> v; |
332 } | 371 safe_push_range (v, 5, 20); |
333 | 372 ASSERT_EQ (15, v.length ()); |
334 /* As for vec_gc_p_reserve, but for heap allocated vectors. */ | 373 |
335 | 374 int last = v.pop (); |
336 void * | 375 ASSERT_EQ (19, last); |
337 vec_heap_p_reserve (void *vec, int reserve MEM_STAT_DECL) | 376 ASSERT_EQ (14, v.length ()); |
338 { | 377 } |
339 return vec_heap_o_reserve_1 (vec, reserve, | 378 |
340 offsetof (struct vec_prefix, vec), | 379 /* Verify that vec::safe_insert works correctly. */ |
341 sizeof (void *), false | 380 |
342 PASS_MEM_STAT); | 381 static void |
343 } | 382 test_safe_insert () |
344 | 383 { |
345 /* As for vec_gc_p_reserve_exact, but for heap allocated vectors. */ | 384 auto_vec <int> v; |
346 | 385 safe_push_range (v, 0, 10); |
347 void * | 386 v.safe_insert (5, 42); |
348 vec_heap_p_reserve_exact (void *vec, int reserve MEM_STAT_DECL) | 387 ASSERT_EQ (4, v[4]); |
349 { | 388 ASSERT_EQ (42, v[5]); |
350 return vec_heap_o_reserve_1 (vec, reserve, | 389 ASSERT_EQ (5, v[6]); |
351 offsetof (struct vec_prefix, vec), | 390 ASSERT_EQ (11, v.length ()); |
352 sizeof (void *), true | 391 } |
353 PASS_MEM_STAT); | 392 |
354 } | 393 /* Verify that vec::ordered_remove works correctly. */ |
355 | 394 |
356 /* As for vec_gc_o_reserve, but for heap allocated vectors. */ | 395 static void |
357 | 396 test_ordered_remove () |
358 void * | 397 { |
359 vec_heap_o_reserve (void *vec, int reserve, size_t vec_offset, size_t elt_size | 398 auto_vec <int> v; |
360 MEM_STAT_DECL) | 399 safe_push_range (v, 0, 10); |
361 { | 400 v.ordered_remove (5); |
362 return vec_heap_o_reserve_1 (vec, reserve, vec_offset, elt_size, false | 401 ASSERT_EQ (4, v[4]); |
363 PASS_MEM_STAT); | 402 ASSERT_EQ (6, v[5]); |
364 } | 403 ASSERT_EQ (9, v.length ()); |
365 | 404 } |
366 /* As for vec_gc_o_reserve_exact, but for heap allocated vectors. */ | 405 |
367 | 406 /* Verify that vec::unordered_remove works correctly. */ |
368 void * | 407 |
369 vec_heap_o_reserve_exact (void *vec, int reserve, size_t vec_offset, | 408 static void |
370 size_t elt_size MEM_STAT_DECL) | 409 test_unordered_remove () |
371 { | 410 { |
372 return vec_heap_o_reserve_1 (vec, reserve, vec_offset, elt_size, true | 411 auto_vec <int> v; |
373 PASS_MEM_STAT); | 412 safe_push_range (v, 0, 10); |
374 } | 413 v.unordered_remove (5); |
375 | 414 ASSERT_EQ (9, v.length ()); |
376 /* Stack vectors are a little different. VEC_alloc turns into a call | 415 } |
377 to vec_stack_p_reserve_exact1 and passes in space allocated via a | 416 |
378 call to alloca. We record that pointer so that we know that we | 417 /* Verify that vec::block_remove works correctly. */ |
379 shouldn't free it. If the vector is resized, we resize it on the | 418 |
380 heap. We record the pointers in a vector and search it in LIFO | 419 static void |
381 order--i.e., we look for the newest stack vectors first. We don't | 420 test_block_remove () |
382 expect too many stack vectors at any one level, and searching from | 421 { |
383 the end should normally be efficient even if they are used in a | 422 auto_vec <int> v; |
384 recursive function. */ | 423 safe_push_range (v, 0, 10); |
385 | 424 v.block_remove (5, 3); |
386 typedef void *void_p; | 425 ASSERT_EQ (3, v[3]); |
387 DEF_VEC_P(void_p); | 426 ASSERT_EQ (4, v[4]); |
388 DEF_VEC_ALLOC_P(void_p,heap); | 427 ASSERT_EQ (8, v[5]); |
389 | 428 ASSERT_EQ (9, v[6]); |
390 static VEC(void_p,heap) *stack_vecs; | 429 ASSERT_EQ (7, v.length ()); |
391 | 430 } |
392 /* Allocate a vector which uses alloca for the initial allocation. | 431 |
393 SPACE is space allocated using alloca, ALLOC is the number of | 432 /* Comparator for use by test_qsort. */ |
394 entries allocated. */ | 433 |
395 | 434 static int |
396 void * | 435 reverse_cmp (const void *p_i, const void *p_j) |
397 vec_stack_p_reserve_exact_1 (int alloc, void *space) | 436 { |
398 { | 437 return *(const int *)p_j - *(const int *)p_i; |
399 struct vec_prefix *pfx = (struct vec_prefix *) space; | 438 } |
400 | 439 |
401 VEC_safe_push (void_p, heap, stack_vecs, space); | 440 /* Verify that vec::qsort works correctly. */ |
402 | 441 |
403 pfx->num = 0; | 442 static void |
404 pfx->alloc = alloc; | 443 test_qsort () |
405 | 444 { |
406 return space; | 445 auto_vec <int> v; |
407 } | 446 safe_push_range (v, 0, 10); |
408 | 447 v.qsort (reverse_cmp); |
409 /* Grow a vector allocated using alloca. When this happens, we switch | 448 ASSERT_EQ (9, v[0]); |
410 back to heap allocation. We remove the vector from stack_vecs, if | 449 ASSERT_EQ (8, v[1]); |
411 it is there, since we no longer need to avoid freeing it. */ | 450 ASSERT_EQ (1, v[8]); |
412 | 451 ASSERT_EQ (0, v[9]); |
413 static void * | 452 ASSERT_EQ (10, v.length ()); |
414 vec_stack_o_reserve_1 (void *vec, int reserve, size_t vec_offset, | 453 } |
415 size_t elt_size, bool exact MEM_STAT_DECL) | 454 |
416 { | 455 /* Run all of the selftests within this file. */ |
417 bool found; | |
418 unsigned int ix; | |
419 void *newvec; | |
420 | |
421 found = false; | |
422 for (ix = VEC_length (void_p, stack_vecs); ix > 0; --ix) | |
423 { | |
424 if (VEC_index (void_p, stack_vecs, ix - 1) == vec) | |
425 { | |
426 VEC_unordered_remove (void_p, stack_vecs, ix - 1); | |
427 found = true; | |
428 break; | |
429 } | |
430 } | |
431 | |
432 if (!found) | |
433 { | |
434 /* VEC is already on the heap. */ | |
435 return vec_heap_o_reserve_1 (vec, reserve, vec_offset, elt_size, | |
436 exact PASS_MEM_STAT); | |
437 } | |
438 | |
439 /* Move VEC to the heap. */ | |
440 reserve += ((struct vec_prefix *) vec)->num; | |
441 newvec = vec_heap_o_reserve_1 (NULL, reserve, vec_offset, elt_size, | |
442 exact PASS_MEM_STAT); | |
443 if (newvec && vec) | |
444 { | |
445 ((struct vec_prefix *) newvec)->num = ((struct vec_prefix *) vec)->num; | |
446 memcpy (((struct vec_prefix *) newvec)->vec, | |
447 ((struct vec_prefix *) vec)->vec, | |
448 ((struct vec_prefix *) vec)->num * elt_size); | |
449 } | |
450 return newvec; | |
451 } | |
452 | |
453 /* Grow a vector allocated on the stack. */ | |
454 | |
455 void * | |
456 vec_stack_p_reserve (void *vec, int reserve MEM_STAT_DECL) | |
457 { | |
458 return vec_stack_o_reserve_1 (vec, reserve, | |
459 offsetof (struct vec_prefix, vec), | |
460 sizeof (void *), false | |
461 PASS_MEM_STAT); | |
462 } | |
463 | |
464 /* Exact version of vec_stack_p_reserve. */ | |
465 | |
466 void * | |
467 vec_stack_p_reserve_exact (void *vec, int reserve MEM_STAT_DECL) | |
468 { | |
469 return vec_stack_o_reserve_1 (vec, reserve, | |
470 offsetof (struct vec_prefix, vec), | |
471 sizeof (void *), true | |
472 PASS_MEM_STAT); | |
473 } | |
474 | |
475 /* Like vec_stack_p_reserve, but for objects. */ | |
476 | |
477 void * | |
478 vec_stack_o_reserve (void *vec, int reserve, size_t vec_offset, | |
479 size_t elt_size MEM_STAT_DECL) | |
480 { | |
481 return vec_stack_o_reserve_1 (vec, reserve, vec_offset, elt_size, false | |
482 PASS_MEM_STAT); | |
483 } | |
484 | |
485 /* Like vec_stack_p_reserve_exact, but for objects. */ | |
486 | |
487 void * | |
488 vec_stack_o_reserve_exact (void *vec, int reserve, size_t vec_offset, | |
489 size_t elt_size MEM_STAT_DECL) | |
490 { | |
491 return vec_stack_o_reserve_1 (vec, reserve, vec_offset, elt_size, true | |
492 PASS_MEM_STAT); | |
493 } | |
494 | |
495 /* Free a vector allocated on the stack. Don't actually free it if we | |
496 find it in the hash table. */ | |
497 | 456 |
498 void | 457 void |
499 vec_stack_free (void *vec) | 458 vec_c_tests () |
500 { | 459 { |
501 unsigned int ix; | 460 test_quick_push (); |
502 | 461 test_safe_push (); |
503 for (ix = VEC_length (void_p, stack_vecs); ix > 0; --ix) | 462 test_truncate (); |
504 { | 463 test_safe_grow_cleared (); |
505 if (VEC_index (void_p, stack_vecs, ix - 1) == vec) | 464 test_pop (); |
506 { | 465 test_safe_insert (); |
507 VEC_unordered_remove (void_p, stack_vecs, ix - 1); | 466 test_ordered_remove (); |
508 return; | 467 test_unordered_remove (); |
509 } | 468 test_block_remove (); |
510 } | 469 test_qsort (); |
511 | 470 } |
512 /* VEC was not on the list of vecs allocated on the stack, so it | 471 |
513 must be allocated on the heap. */ | 472 } // namespace selftest |
514 vec_heap_free (vec); | 473 |
515 } | 474 #endif /* #if CHECKING_P */ |
516 | 475 #endif /* #ifndef GENERATOR_FILE */ |
517 #if ENABLE_CHECKING | |
518 /* Issue a vector domain error, and then fall over. */ | |
519 | |
520 void | |
521 vec_assert_fail (const char *op, const char *struct_name, | |
522 const char *file, unsigned int line, const char *function) | |
523 { | |
524 internal_error ("vector %s %s domain error, in %s at %s:%u", | |
525 struct_name, op, function, trim_filename (file), line); | |
526 } | |
527 #endif | |
528 | |
529 #ifdef GATHER_STATISTICS | |
530 /* Helper for qsort; sort descriptors by amount of memory consumed. */ | |
531 static int | |
532 cmp_statistic (const void *loc1, const void *loc2) | |
533 { | |
534 const struct vec_descriptor *const l1 = | |
535 *(const struct vec_descriptor *const *) loc1; | |
536 const struct vec_descriptor *const l2 = | |
537 *(const struct vec_descriptor *const *) loc2; | |
538 long diff; | |
539 diff = l1->allocated - l2->allocated; | |
540 if (!diff) | |
541 diff = l1->peak - l2->peak; | |
542 if (!diff) | |
543 diff = l1->times - l2->times; | |
544 return diff > 0 ? 1 : diff < 0 ? -1 : 0; | |
545 } | |
546 /* Collect array of the descriptors from hashtable. */ | |
547 static struct vec_descriptor **loc_array; | |
548 static int | |
549 add_statistics (void **slot, void *b) | |
550 { | |
551 int *n = (int *)b; | |
552 loc_array[*n] = (struct vec_descriptor *) *slot; | |
553 (*n)++; | |
554 return 1; | |
555 } | |
556 | |
557 /* Dump per-site memory statistics. */ | |
558 #endif | |
559 void | |
560 dump_vec_loc_statistics (void) | |
561 { | |
562 #ifdef GATHER_STATISTICS | |
563 int nentries = 0; | |
564 char s[4096]; | |
565 size_t allocated = 0; | |
566 size_t times = 0; | |
567 int i; | |
568 | |
569 loc_array = XCNEWVEC (struct vec_descriptor *, vec_desc_hash->n_elements); | |
570 fprintf (stderr, "Heap vectors:\n"); | |
571 fprintf (stderr, "\n%-48s %10s %10s %10s\n", | |
572 "source location", "Leak", "Peak", "Times"); | |
573 fprintf (stderr, "-------------------------------------------------------\n"); | |
574 htab_traverse (vec_desc_hash, add_statistics, &nentries); | |
575 qsort (loc_array, nentries, sizeof (*loc_array), cmp_statistic); | |
576 for (i = 0; i < nentries; i++) | |
577 { | |
578 struct vec_descriptor *d = loc_array[i]; | |
579 allocated += d->allocated; | |
580 times += d->times; | |
581 } | |
582 for (i = 0; i < nentries; i++) | |
583 { | |
584 struct vec_descriptor *d = loc_array[i]; | |
585 const char *s1 = d->file; | |
586 const char *s2; | |
587 while ((s2 = strstr (s1, "gcc/"))) | |
588 s1 = s2 + 4; | |
589 sprintf (s, "%s:%i (%s)", s1, d->line, d->function); | |
590 s[48] = 0; | |
591 fprintf (stderr, "%-48s %10li:%4.1f%% %10li %10li:%4.1f%% \n", s, | |
592 (long)d->allocated, | |
593 (d->allocated) * 100.0 / allocated, | |
594 (long)d->peak, | |
595 (long)d->times, | |
596 (d->times) * 100.0 / times); | |
597 } | |
598 fprintf (stderr, "%-48s %10ld %10ld\n", | |
599 "Total", (long)allocated, (long)times); | |
600 fprintf (stderr, "\n%-48s %10s %10s %10s\n", | |
601 "source location", "Leak", "Peak", "Times"); | |
602 fprintf (stderr, "-------------------------------------------------------\n"); | |
603 #endif | |
604 } |