]>
Commit | Line | Data |
---|---|---|
8c4294b2 | 1 | /* Unit tests for GCC's garbage collector (and gengtype etc). |
aeee4812 | 2 | Copyright (C) 2015-2023 Free Software Foundation, Inc. |
8c4294b2 DM |
3 | |
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify it under | |
7 | the terms of the GNU General Public License as published by the Free | |
8 | Software Foundation; either version 3, or (at your option) any later | |
9 | version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GCC; see the file COPYING3. If not see | |
18 | <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "config.h" | |
21 | #include "system.h" | |
22 | #include "coretypes.h" | |
23 | #include "tree-core.h" | |
24 | #include "tree.h" | |
8c4294b2 DM |
25 | #include "selftest.h" |
26 | ||
27 | #if CHECKING_P | |
28 | ||
6c3b5bf0 DM |
29 | /* The various GTY markers must be outside of a namespace to be seen by |
30 | gengtype, so we don't put this file within the selftest namespace. */ | |
31 | ||
8c4294b2 DM |
32 | \f |
33 | ||
34 | /* Verify that a simple struct works, and that it can | |
35 | own references to non-roots, and have them be marked. */ | |
36 | ||
37 | struct GTY(()) test_struct | |
38 | { | |
39 | struct test_struct *other; | |
40 | }; | |
41 | ||
42 | static GTY(()) test_struct *root_test_struct; | |
43 | ||
44 | static void | |
45 | test_basic_struct () | |
46 | { | |
47 | root_test_struct = ggc_cleared_alloc <test_struct> (); | |
48 | root_test_struct->other = ggc_cleared_alloc <test_struct> (); | |
49 | ||
602fca42 | 50 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
51 | |
52 | ASSERT_TRUE (ggc_marked_p (root_test_struct)); | |
53 | ASSERT_TRUE (ggc_marked_p (root_test_struct->other)); | |
54 | } | |
55 | ||
56 | \f | |
57 | ||
58 | /* Selftest for GTY((length)). */ | |
59 | ||
60 | /* A test struct using GTY((length)). */ | |
61 | ||
62 | struct GTY(()) test_of_length | |
63 | { | |
64 | int num_elem; | |
65 | struct test_of_length * GTY ((length ("%h.num_elem"))) elem[1]; | |
66 | }; | |
67 | ||
68 | static GTY(()) test_of_length *root_test_of_length; | |
69 | ||
70 | static void | |
71 | test_length () | |
72 | { | |
73 | const int count = 5; | |
74 | size_t sz = sizeof (test_of_length) + (count- 1) * sizeof (test_of_length *); | |
75 | root_test_of_length = (test_of_length *)ggc_internal_cleared_alloc (sz); | |
76 | root_test_of_length->num_elem = count; | |
77 | for (int i = 0; i < count; i++) | |
78 | root_test_of_length->elem[i] = ggc_cleared_alloc <test_of_length> (); | |
79 | ||
602fca42 | 80 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
81 | |
82 | ASSERT_TRUE (ggc_marked_p (root_test_of_length)); | |
83 | for (int i = 0; i < count; i++) | |
84 | ASSERT_TRUE (ggc_marked_p (root_test_of_length->elem[i])); | |
85 | } | |
86 | ||
87 | \f | |
88 | ||
89 | /* Selftest for unions, GTY((tag)), and GTY((desc)). */ | |
90 | ||
91 | /* A struct with a reference that's an a different offset to test_struct, | |
92 | to ensure that we're using the correct types. */ | |
93 | ||
94 | struct GTY(()) test_other | |
95 | { | |
96 | char dummy[256]; | |
97 | test_struct *m_ptr; | |
98 | }; | |
99 | ||
100 | enum which_field | |
101 | { | |
102 | WHICH_FIELD_USE_TEST_STRUCT, | |
103 | WHICH_FIELD_USE_TEST_OTHER | |
104 | }; | |
105 | ||
106 | /* An example function for use by a GTY((desc)) marker. */ | |
107 | ||
108 | static enum which_field | |
109 | calc_desc (int kind) | |
110 | { | |
111 | switch (kind) | |
112 | { | |
113 | case 0: return WHICH_FIELD_USE_TEST_STRUCT; | |
114 | case 1: return WHICH_FIELD_USE_TEST_OTHER; | |
115 | default: | |
116 | gcc_unreachable (); | |
117 | } | |
118 | } | |
119 | ||
120 | /* A struct containing an example of a union, showing the "tag" and | |
121 | "desc" markers. */ | |
122 | ||
123 | struct GTY(()) test_of_union | |
124 | { | |
125 | int m_kind; | |
126 | union u { | |
127 | test_struct * GTY ((tag ("WHICH_FIELD_USE_TEST_STRUCT") )) u_test_struct; | |
128 | test_other * GTY ((tag ("WHICH_FIELD_USE_TEST_OTHER") )) u_test_other; | |
129 | } GTY ((desc ("calc_desc (%0.m_kind)"))) m_u; | |
130 | }; | |
131 | ||
132 | /* Example roots. */ | |
133 | ||
134 | static GTY(()) test_of_union *root_test_of_union_1; | |
135 | static GTY(()) test_of_union *root_test_of_union_2; | |
136 | ||
137 | /* Verify that the above work correctly. */ | |
138 | ||
139 | static void | |
140 | test_union () | |
141 | { | |
142 | root_test_of_union_1 = ggc_cleared_alloc <test_of_union> (); | |
143 | root_test_of_union_1->m_kind = 0; | |
144 | test_struct *ts = ggc_cleared_alloc <test_struct> (); | |
145 | root_test_of_union_1->m_u.u_test_struct = ts; | |
146 | ||
147 | root_test_of_union_2 = ggc_cleared_alloc <test_of_union> (); | |
148 | root_test_of_union_2->m_kind = 1; | |
149 | test_other *other = ggc_cleared_alloc <test_other> (); | |
150 | root_test_of_union_2->m_u.u_test_other = other; | |
151 | test_struct *referenced_by_other = ggc_cleared_alloc <test_struct> (); | |
152 | other->m_ptr = referenced_by_other; | |
153 | ||
602fca42 | 154 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
155 | |
156 | ASSERT_TRUE (ggc_marked_p (root_test_of_union_1)); | |
157 | ASSERT_TRUE (ggc_marked_p (ts)); | |
158 | ||
159 | ASSERT_TRUE (ggc_marked_p (root_test_of_union_2)); | |
160 | ASSERT_TRUE (ggc_marked_p (other)); | |
161 | ASSERT_TRUE (ggc_marked_p (referenced_by_other)); | |
162 | } | |
163 | ||
164 | \f | |
165 | ||
166 | /* Verify that destructors get run when instances are collected. */ | |
167 | ||
6c1dae73 | 168 | class GTY(()) test_struct_with_dtor |
8c4294b2 | 169 | { |
6c1dae73 | 170 | public: |
8c4294b2 DM |
171 | /* This struct has a destructor; it *ought* to be called |
172 | by the ggc machinery when instances are collected. */ | |
173 | ~test_struct_with_dtor () { dtor_call_count++; } | |
174 | ||
175 | static int dtor_call_count; | |
176 | }; | |
177 | ||
178 | int test_struct_with_dtor::dtor_call_count; | |
179 | ||
180 | static void | |
181 | test_finalization () | |
182 | { | |
30717592 | 183 | #if GCC_VERSION >= 4003 |
8c4294b2 DM |
184 | ASSERT_FALSE (need_finalization_p <test_struct> ()); |
185 | ASSERT_TRUE (need_finalization_p <test_struct_with_dtor> ()); | |
30717592 | 186 | #endif |
8c4294b2 DM |
187 | |
188 | /* Create some garbage. */ | |
189 | const int count = 10; | |
190 | for (int i = 0; i < count; i++) | |
191 | ggc_cleared_alloc <test_struct_with_dtor> (); | |
192 | ||
193 | test_struct_with_dtor::dtor_call_count = 0; | |
194 | ||
602fca42 | 195 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
196 | |
197 | /* Verify that the destructor was run for each instance. */ | |
198 | ASSERT_EQ (count, test_struct_with_dtor::dtor_call_count); | |
199 | } | |
200 | ||
201 | \f | |
202 | ||
203 | /* Verify that a global can be marked as "deletable". */ | |
204 | ||
205 | static GTY((deletable)) test_struct *test_of_deletable; | |
206 | ||
207 | static void | |
208 | test_deletable_global () | |
209 | { | |
210 | test_of_deletable = ggc_cleared_alloc <test_struct> (); | |
211 | ASSERT_TRUE (test_of_deletable != NULL); | |
212 | ||
602fca42 | 213 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
214 | |
215 | ASSERT_EQ (NULL, test_of_deletable); | |
216 | } | |
217 | ||
218 | \f | |
219 | ||
220 | /* Verify that gengtype etc can cope with inheritance. */ | |
221 | ||
222 | class GTY((desc("%h.m_kind"), tag("0"))) example_base | |
223 | { | |
224 | public: | |
225 | example_base () | |
226 | : m_kind (0), | |
227 | m_a (ggc_cleared_alloc <test_struct> ()) | |
228 | {} | |
229 | ||
230 | void * | |
231 | operator new (size_t sz) | |
232 | { | |
233 | return ggc_internal_cleared_alloc (sz); | |
234 | } | |
235 | ||
236 | protected: | |
237 | example_base (int kind) | |
238 | : m_kind (kind), | |
239 | m_a (ggc_cleared_alloc <test_struct> ()) | |
240 | {} | |
241 | ||
242 | public: | |
243 | int m_kind; | |
244 | test_struct *m_a; | |
245 | }; | |
246 | ||
247 | class GTY((tag("1"))) some_subclass : public example_base | |
248 | { | |
249 | public: | |
250 | some_subclass () | |
251 | : example_base (1), | |
252 | m_b (ggc_cleared_alloc <test_struct> ()) | |
253 | {} | |
254 | ||
255 | test_struct *m_b; | |
256 | }; | |
257 | ||
258 | class GTY((tag("2"))) some_other_subclass : public example_base | |
259 | { | |
260 | public: | |
261 | some_other_subclass () | |
262 | : example_base (2), | |
263 | m_c (ggc_cleared_alloc <test_struct> ()) | |
264 | {} | |
265 | ||
266 | test_struct *m_c; | |
267 | }; | |
268 | ||
269 | /* Various test roots, both expressed as a ptr to the actual class, and | |
270 | as a ptr to the base class. */ | |
271 | static GTY(()) example_base *test_example_base; | |
272 | static GTY(()) some_subclass *test_some_subclass; | |
273 | static GTY(()) some_other_subclass *test_some_other_subclass; | |
274 | static GTY(()) example_base *test_some_subclass_as_base_ptr; | |
275 | static GTY(()) example_base *test_some_other_subclass_as_base_ptr; | |
276 | ||
277 | static void | |
278 | test_inheritance () | |
279 | { | |
280 | test_example_base = new example_base (); | |
281 | test_some_subclass = new some_subclass (); | |
282 | test_some_other_subclass = new some_other_subclass (); | |
283 | test_some_subclass_as_base_ptr = new some_subclass (); | |
284 | test_some_other_subclass_as_base_ptr = new some_other_subclass (); | |
285 | ||
602fca42 | 286 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
287 | |
288 | /* Verify that the roots and everything referenced by them got marked | |
289 | (both for fields in the base class and those in subclasses). */ | |
290 | ASSERT_TRUE (ggc_marked_p (test_example_base)); | |
291 | ASSERT_TRUE (ggc_marked_p (test_example_base->m_a)); | |
292 | ||
293 | ASSERT_TRUE (ggc_marked_p (test_some_subclass)); | |
294 | ASSERT_TRUE (ggc_marked_p (test_some_subclass->m_a)); | |
295 | ASSERT_TRUE (ggc_marked_p (test_some_subclass->m_b)); | |
296 | ||
297 | ASSERT_TRUE (ggc_marked_p (test_some_other_subclass)); | |
298 | ASSERT_TRUE (ggc_marked_p (test_some_other_subclass->m_a)); | |
299 | ASSERT_TRUE (ggc_marked_p (test_some_other_subclass->m_c)); | |
300 | ||
301 | ASSERT_TRUE (ggc_marked_p (test_some_subclass_as_base_ptr)); | |
302 | ASSERT_TRUE (ggc_marked_p (test_some_subclass_as_base_ptr->m_a)); | |
303 | ASSERT_TRUE (ggc_marked_p (((some_subclass *) | |
304 | test_some_subclass_as_base_ptr)->m_b)); | |
305 | ||
306 | ASSERT_TRUE (ggc_marked_p (test_some_other_subclass_as_base_ptr)); | |
307 | ASSERT_TRUE (ggc_marked_p (test_some_other_subclass_as_base_ptr->m_a)); | |
308 | ASSERT_TRUE (ggc_marked_p (((some_other_subclass *) | |
309 | test_some_other_subclass_as_base_ptr)->m_c)); | |
310 | } | |
311 | ||
312 | \f | |
313 | ||
314 | /* Test of chain_next/chain_prev | |
315 | ||
316 | Construct a very long linked list, so that without | |
317 | the chain_next/chain_prev optimization we'd have | |
318 | a stack overflow when gt_ggc_mx_test_node recurses. */ | |
319 | ||
320 | struct GTY(( chain_next ("%h.m_next"), | |
321 | chain_prev ("%h.m_prev") )) test_node | |
322 | { | |
323 | test_node *m_prev; | |
324 | test_node *m_next; | |
325 | int m_idx; | |
326 | }; | |
327 | ||
328 | static GTY(()) test_node *root_test_node; | |
329 | ||
330 | static void | |
331 | test_chain_next () | |
332 | { | |
333 | /* Ideally we would construct a long list so that the number of | |
334 | stack frames would be deep enough to crash if gengtype has created | |
335 | something that recurses. | |
336 | ||
337 | However, as the list is lengthened to increase the chance of | |
338 | overflowing the stack, the test will require more time and memory | |
339 | to run. On a Fedora 20 x86_64 box with 128GB of RAM, count=2000000 | |
340 | without the chain_next optimization reliably overflowed the stack, | |
341 | but the test took 0.5s to run. | |
342 | ||
343 | For now this test runs with a low value for "count", which defeats | |
344 | the main purpose of the test - though it at least gives us coverage | |
345 | for walking a GTY((chain_next)) list. | |
346 | ||
347 | We could potentially increase this value once we have a better sense | |
348 | of the time and space requirements of the test on different hosts, | |
349 | or perhaps find a way to reduce the stack size when running this | |
350 | testcase. */ | |
351 | const int count = 10; | |
352 | ||
353 | /* Build the linked list. */ | |
354 | root_test_node = ggc_cleared_alloc <test_node> (); | |
355 | test_node *tail_node = root_test_node; | |
356 | for (int i = 0; i < count; i++) | |
357 | { | |
358 | test_node *new_node = ggc_cleared_alloc <test_node> (); | |
359 | tail_node->m_next = new_node; | |
360 | new_node->m_prev = tail_node; | |
361 | new_node->m_idx = i; | |
362 | tail_node = new_node; | |
363 | } | |
364 | ||
602fca42 | 365 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
366 | |
367 | /* If we got here, we survived. */ | |
368 | ||
369 | /* Verify that all nodes in the list were marked. */ | |
370 | ASSERT_TRUE (ggc_marked_p (root_test_node)); | |
371 | test_node *iter_node = root_test_node->m_next; | |
372 | for (int i = 0; i < count; i++) | |
373 | { | |
374 | ASSERT_TRUE (ggc_marked_p (iter_node)); | |
375 | ASSERT_EQ (i, iter_node->m_idx); | |
376 | iter_node = iter_node->m_next; | |
377 | } | |
378 | } | |
379 | ||
380 | \f | |
381 | ||
382 | /* Test for GTY((user)). */ | |
383 | ||
384 | struct GTY((user)) user_struct | |
385 | { | |
386 | char dummy[16]; | |
387 | test_struct *m_ptr; | |
388 | }; | |
389 | ||
390 | static GTY(()) user_struct *root_user_struct_ptr; | |
391 | ||
392 | /* A global for verifying that the user-provided gt_ggc_mx gets | |
393 | called. */ | |
394 | static int num_calls_to_user_gt_ggc_mx; | |
395 | ||
396 | /* User-provided implementation of gt_ggc_mx. */ | |
397 | ||
398 | static void | |
399 | gt_ggc_mx (user_struct *p) | |
400 | { | |
401 | num_calls_to_user_gt_ggc_mx++; | |
402 | gt_ggc_mx_test_struct (p->m_ptr); | |
403 | } | |
404 | ||
405 | /* User-provided implementation of gt_pch_nx. */ | |
406 | ||
407 | static void | |
408 | gt_pch_nx (user_struct *p) | |
409 | { | |
410 | gt_pch_nx_test_struct (p->m_ptr); | |
411 | } | |
412 | ||
413 | /* User-provided implementation of gt_pch_nx. */ | |
414 | ||
415 | static void | |
416 | gt_pch_nx (user_struct *p, gt_pointer_operator op, void *cookie) | |
417 | { | |
747380f4 | 418 | op (&(p->m_ptr), NULL, cookie); |
8c4294b2 DM |
419 | } |
420 | ||
421 | /* Verify that GTY((user)) works. */ | |
422 | ||
423 | static void | |
424 | test_user_struct () | |
425 | { | |
426 | root_user_struct_ptr = ggc_cleared_alloc <user_struct> (); | |
427 | test_struct *referenced = ggc_cleared_alloc <test_struct> (); | |
428 | root_user_struct_ptr->m_ptr = referenced; | |
429 | ||
430 | num_calls_to_user_gt_ggc_mx = 0; | |
431 | ||
602fca42 | 432 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
433 | |
434 | ASSERT_TRUE (ggc_marked_p (root_user_struct_ptr)); | |
435 | ASSERT_TRUE (ggc_marked_p (referenced)); | |
436 | ASSERT_TRUE (num_calls_to_user_gt_ggc_mx > 0); | |
437 | } | |
438 | ||
439 | \f | |
440 | ||
441 | /* Smoketest to ensure that the tree type is marked. */ | |
442 | ||
443 | static GTY(()) tree dummy_unittesting_tree; | |
444 | ||
445 | static void | |
446 | test_tree_marking () | |
447 | { | |
448 | dummy_unittesting_tree = build_int_cst (integer_type_node, 1066); | |
449 | ||
602fca42 | 450 | ggc_collect (GGC_COLLECT_FORCE); |
8c4294b2 DM |
451 | |
452 | ASSERT_TRUE (ggc_marked_p (dummy_unittesting_tree)); | |
453 | } | |
454 | ||
455 | \f | |
456 | ||
457 | /* Ideas for other tests: | |
458 | - pch-handling */ | |
459 | ||
460 | namespace selftest { | |
461 | ||
462 | /* Run all of the selftests within this file. */ | |
463 | ||
464 | void | |
d5148d4f | 465 | ggc_tests_cc_tests () |
8c4294b2 DM |
466 | { |
467 | test_basic_struct (); | |
468 | test_length (); | |
469 | test_union (); | |
470 | test_finalization (); | |
471 | test_deletable_global (); | |
472 | test_inheritance (); | |
473 | test_chain_next (); | |
474 | test_user_struct (); | |
475 | test_tree_marking (); | |
476 | } | |
477 | ||
478 | } // namespace selftest | |
479 | ||
480 | #include "gt-ggc-tests.h" | |
481 | ||
482 | #else /* #if CHECKING_P */ | |
483 | ||
484 | /* The #if CHECKING_P code above has various GTY-marked roots. | |
485 | gengtype has no knowledge of the preprocessor, and so detects | |
486 | these roots and writes them out to gt-ggc-tests.h. | |
487 | In a !CHECKING_P build we can ignore gt-ggc-tests.h, but the | |
488 | root tables are referenced in the various generated gtype-*.c | |
489 | files like this: | |
490 | ||
491 | ...snip... | |
492 | extern const struct ggc_root_tab gt_ggc_r_gt_ggc_tests_h[]; | |
493 | ...snip... | |
494 | ||
495 | EXPORTED_CONST struct ggc_root_tab * const gt_ggc_rtab[] = { | |
496 | ...snip... | |
497 | gt_ggc_r_gt_ggc_tests_h, | |
498 | ...snip... | |
499 | }; | |
500 | ||
501 | Hence to avoid a link failure, we provide dummy implementations | |
502 | of these root tables in an unchecked build. | |
503 | ||
504 | Note that these conditional roots imply that PCH files are | |
505 | incompatible between checked and unchecked builds. */ | |
506 | ||
507 | EXPORTED_CONST struct ggc_root_tab gt_ggc_r_gt_ggc_tests_h[] = { | |
508 | LAST_GGC_ROOT_TAB | |
509 | }; | |
510 | ||
511 | EXPORTED_CONST struct ggc_root_tab gt_ggc_rd_gt_ggc_tests_h[] = { | |
512 | LAST_GGC_ROOT_TAB | |
513 | }; | |
514 | ||
515 | #endif /* #else clause of #if CHECKING_P */ |