aboutsummaryrefslogtreecommitdiff
path: root/gcc/ggc-tests.cc
blob: 34a39933fdfa54e34b4d4a2f16fc7d867afc7e92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
/* Unit tests for GCC's garbage collector (and gengtype etc).
   Copyright (C) 2015-2024 Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree-core.h"
#include "tree.h"
#include "selftest.h"

#if CHECKING_P

/* The various GTY markers must be outside of a namespace to be seen by
   gengtype, so we don't put this file within the selftest namespace.  */



/* Verify that a simple struct works, and that it can
   own references to non-roots, and have them be marked.  */

struct GTY(()) test_struct
{
  struct test_struct *other;
};

static GTY(()) test_struct *root_test_struct;

static void
test_basic_struct ()
{
  root_test_struct = ggc_cleared_alloc <test_struct> ();
  root_test_struct->other = ggc_cleared_alloc <test_struct> ();

  ggc_collect (GGC_COLLECT_FORCE);

  ASSERT_TRUE (ggc_marked_p (root_test_struct));
  ASSERT_TRUE (ggc_marked_p (root_test_struct->other));
}



/* Selftest for GTY((length)).  */

/* A test struct using GTY((length)).  */

struct GTY(()) test_of_length
{
  int num_elem;
  struct test_of_length * GTY ((length ("%h.num_elem"))) elem[1];
};

static GTY(()) test_of_length *root_test_of_length;

static void
test_length ()
{
  const int count = 5;
  size_t sz = sizeof (test_of_length) + (count- 1) * sizeof (test_of_length *);
  root_test_of_length = (test_of_length *)ggc_internal_cleared_alloc (sz);
  root_test_of_length->num_elem = count;
  for (int i = 0; i < count; i++)
    root_test_of_length->elem[i] = ggc_cleared_alloc <test_of_length> ();

  ggc_collect (GGC_COLLECT_FORCE);

  ASSERT_TRUE (ggc_marked_p (root_test_of_length));
  for (int i = 0; i < count; i++)
    ASSERT_TRUE (ggc_marked_p (root_test_of_length->elem[i]));
}



/* Selftest for unions, GTY((tag)), and GTY((desc)).  */

/* A struct with a reference that's an a different offset to test_struct,
   to ensure that we're using the correct types.  */

struct GTY(()) test_other
{
  char dummy[256];
  test_struct *m_ptr;
};

enum which_field
{
  WHICH_FIELD_USE_TEST_STRUCT,
  WHICH_FIELD_USE_TEST_OTHER
};

/* An example function for use by a GTY((desc)) marker.  */

static enum which_field
calc_desc (int kind)
{
  switch (kind)
    {
    case 0: return WHICH_FIELD_USE_TEST_STRUCT;
    case 1: return WHICH_FIELD_USE_TEST_OTHER;
    default:
      gcc_unreachable ();
    }
}

/* A struct containing an example of a union, showing the "tag" and
   "desc" markers.  */

struct GTY(()) test_of_union
{
  int m_kind;
  union u {
    test_struct * GTY ((tag ("WHICH_FIELD_USE_TEST_STRUCT") )) u_test_struct;
    test_other * GTY ((tag ("WHICH_FIELD_USE_TEST_OTHER") )) u_test_other;
  } GTY ((desc ("calc_desc (%0.m_kind)"))) m_u;
};

/* Example roots.  */

static GTY(()) test_of_union *root_test_of_union_1;
static GTY(()) test_of_union *root_test_of_union_2;

/* Verify that the above work correctly.  */

static void
test_union ()
{
  root_test_of_union_1 = ggc_cleared_alloc <test_of_union> ();
  root_test_of_union_1->m_kind = 0;
  test_struct *ts = ggc_cleared_alloc <test_struct> ();
  root_test_of_union_1->m_u.u_test_struct = ts;

  root_test_of_union_2 = ggc_cleared_alloc <test_of_union> ();
  root_test_of_union_2->m_kind = 1;
  test_other *other = ggc_cleared_alloc <test_other> ();
  root_test_of_union_2->m_u.u_test_other = other;
  test_struct *referenced_by_other = ggc_cleared_alloc <test_struct> ();
  other->m_ptr = referenced_by_other;

  ggc_collect (GGC_COLLECT_FORCE);

  ASSERT_TRUE (ggc_marked_p (root_test_of_union_1));
  ASSERT_TRUE (ggc_marked_p (ts));

  ASSERT_TRUE (ggc_marked_p (root_test_of_union_2));
  ASSERT_TRUE (ggc_marked_p (other));
  ASSERT_TRUE (ggc_marked_p (referenced_by_other));
}



/* Verify that destructors get run when instances are collected.  */

class GTY(()) test_struct_with_dtor
{
public:
  /* This struct has a destructor; it *ought* to be called
     by the ggc machinery when instances are collected.  */
  ~test_struct_with_dtor () { dtor_call_count++; }

  static int dtor_call_count;
};

int test_struct_with_dtor::dtor_call_count;

static void
test_finalization ()
{
#if GCC_VERSION >= 4003
  ASSERT_FALSE (need_finalization_p <test_struct> ());
  ASSERT_TRUE (need_finalization_p <test_struct_with_dtor> ());
#endif

  /* Create some garbage.  */
  const int count = 10;
  for (int i = 0; i < count; i++)
    ggc_cleared_alloc <test_struct_with_dtor> ();

  test_struct_with_dtor::dtor_call_count = 0;

  ggc_collect (GGC_COLLECT_FORCE);

  /* Verify that the destructor was run for each instance.  */
  ASSERT_EQ (count, test_struct_with_dtor::dtor_call_count);
}



/* Verify that a global can be marked as "deletable".  */

static GTY((deletable)) test_struct *test_of_deletable;

static void
test_deletable_global ()
{
  test_of_deletable = ggc_cleared_alloc <test_struct> ();
  ASSERT_TRUE (test_of_deletable != NULL);

  ggc_collect (GGC_COLLECT_FORCE);

  ASSERT_EQ (NULL, test_of_deletable);
}



/* Verify that gengtype etc can cope with inheritance.  */

class GTY((desc("%h.m_kind"), tag("0"))) example_base
{
 public:
  example_base ()
    : m_kind (0),
      m_a (ggc_cleared_alloc <test_struct> ())
  {}

  void *
  operator new (size_t sz)
  {
    return ggc_internal_cleared_alloc (sz);
  }

 protected:
  example_base (int kind)
    : m_kind (kind),
      m_a (ggc_cleared_alloc <test_struct> ())
  {}

 public:
  int m_kind;
  test_struct *m_a;
};

class GTY((tag("1"))) some_subclass : public example_base
{
 public:
  some_subclass ()
    : example_base (1),
      m_b (ggc_cleared_alloc <test_struct> ())
  {}

  test_struct *m_b;
};

class GTY((tag("2"))) some_other_subclass : public example_base
{
 public:
  some_other_subclass ()
    : example_base (2),
      m_c (ggc_cleared_alloc <test_struct> ())
  {}

  test_struct *m_c;
};

/* Various test roots, both expressed as a ptr to the actual class, and
   as a ptr to the base class.  */
static GTY(()) example_base *test_example_base;
static GTY(()) some_subclass *test_some_subclass;
static GTY(()) some_other_subclass *test_some_other_subclass;
static GTY(()) example_base *test_some_subclass_as_base_ptr;
static GTY(()) example_base *test_some_other_subclass_as_base_ptr;

static void
test_inheritance ()
{
  test_example_base = new example_base ();
  test_some_subclass = new some_subclass ();
  test_some_other_subclass = new some_other_subclass ();
  test_some_subclass_as_base_ptr = new some_subclass ();
  test_some_other_subclass_as_base_ptr = new some_other_subclass ();

  ggc_collect (GGC_COLLECT_FORCE);

  /* Verify that the roots and everything referenced by them got marked
     (both for fields in the base class and those in subclasses).  */
  ASSERT_TRUE (ggc_marked_p (test_example_base));
  ASSERT_TRUE (ggc_marked_p (test_example_base->m_a));

  ASSERT_TRUE (ggc_marked_p (test_some_subclass));
  ASSERT_TRUE (ggc_marked_p (test_some_subclass->m_a));
  ASSERT_TRUE (ggc_marked_p (test_some_subclass->m_b));

  ASSERT_TRUE (ggc_marked_p (test_some_other_subclass));
  ASSERT_TRUE (ggc_marked_p (test_some_other_subclass->m_a));
  ASSERT_TRUE (ggc_marked_p (test_some_other_subclass->m_c));

  ASSERT_TRUE (ggc_marked_p (test_some_subclass_as_base_ptr));
  ASSERT_TRUE (ggc_marked_p (test_some_subclass_as_base_ptr->m_a));
  ASSERT_TRUE (ggc_marked_p (((some_subclass *)
			      test_some_subclass_as_base_ptr)->m_b));

  ASSERT_TRUE (ggc_marked_p (test_some_other_subclass_as_base_ptr));
  ASSERT_TRUE (ggc_marked_p (test_some_other_subclass_as_base_ptr->m_a));
  ASSERT_TRUE (ggc_marked_p (((some_other_subclass *)
			      test_some_other_subclass_as_base_ptr)->m_c));
}



/* Test of chain_next/chain_prev

   Construct a very long linked list, so that without
   the chain_next/chain_prev optimization we'd have
   a stack overflow when gt_ggc_mx_test_node recurses.  */

struct GTY(( chain_next ("%h.m_next"),
	     chain_prev ("%h.m_prev") )) test_node
{
  test_node *m_prev;
  test_node *m_next;
  int m_idx;
};

static GTY(()) test_node *root_test_node;

static void
test_chain_next ()
{
  /* Ideally we would construct a long list so that the number of
     stack frames would be deep enough to crash if gengtype has created
     something that recurses.

     However, as the list is lengthened to increase the chance of
     overflowing the stack, the test will require more time and memory
     to run.  On a Fedora 20 x86_64 box with 128GB of RAM, count=2000000
     without the chain_next optimization reliably overflowed the stack,
     but the test took 0.5s to run.

     For now this test runs with a low value for "count", which defeats
     the main purpose of the test - though it at least gives us coverage
     for walking a GTY((chain_next)) list.

     We could potentially increase this value once we have a better sense
     of the time and space requirements of the test on different hosts,
     or perhaps find a way to reduce the stack size when running this
     testcase.  */
  const int count = 10;

  /* Build the linked list.  */
  root_test_node = ggc_cleared_alloc <test_node> ();
  test_node *tail_node = root_test_node;
  for (int i = 0; i < count; i++)
    {
      test_node *new_node = ggc_cleared_alloc <test_node> ();
      tail_node->m_next = new_node;
      new_node->m_prev = tail_node;
      new_node->m_idx = i;
      tail_node = new_node;
    }

  ggc_collect (GGC_COLLECT_FORCE);

  /* If we got here, we survived.  */

  /* Verify that all nodes in the list were marked.  */
  ASSERT_TRUE (ggc_marked_p (root_test_node));
  test_node *iter_node = root_test_node->m_next;
  for (int i = 0; i < count; i++)
    {
      ASSERT_TRUE (ggc_marked_p (iter_node));
      ASSERT_EQ (i, iter_node->m_idx);
      iter_node = iter_node->m_next;
    }
}



/* Test for GTY((user)).  */

struct GTY((user)) user_struct
{
  char dummy[16];
  test_struct *m_ptr;
};

static GTY(()) user_struct *root_user_struct_ptr;

/* A global for verifying that the user-provided gt_ggc_mx gets
   called.  */
static int num_calls_to_user_gt_ggc_mx;

/* User-provided implementation of gt_ggc_mx.  */

static void
gt_ggc_mx (user_struct *p)
{
  num_calls_to_user_gt_ggc_mx++;
  gt_ggc_mx_test_struct (p->m_ptr);
}

/* User-provided implementation of gt_pch_nx.  */

static void
gt_pch_nx (user_struct *p)
{
  gt_pch_nx_test_struct (p->m_ptr);
}

/* User-provided implementation of gt_pch_nx.  */

static void
gt_pch_nx (user_struct *p, gt_pointer_operator op, void *cookie)
{
  op (&(p->m_ptr), NULL, cookie);
}

/* Verify that GTY((user)) works.  */

static void
test_user_struct ()
{
  root_user_struct_ptr = ggc_cleared_alloc <user_struct> ();
  test_struct *referenced = ggc_cleared_alloc <test_struct> ();
  root_user_struct_ptr->m_ptr = referenced;

  num_calls_to_user_gt_ggc_mx = 0;

  ggc_collect (GGC_COLLECT_FORCE);

  ASSERT_TRUE (ggc_marked_p (root_user_struct_ptr));
  ASSERT_TRUE (ggc_marked_p (referenced));
  ASSERT_TRUE (num_calls_to_user_gt_ggc_mx > 0);
}



/* Smoketest to ensure that the tree type is marked.  */

static GTY(()) tree dummy_unittesting_tree;

static void
test_tree_marking ()
{
  dummy_unittesting_tree = build_int_cst (integer_type_node, 1066);

  ggc_collect (GGC_COLLECT_FORCE);

  ASSERT_TRUE (ggc_marked_p (dummy_unittesting_tree));
}



/* Ideas for other tests:
   - pch-handling  */

namespace selftest {

/* Run all of the selftests within this file.  */

void
ggc_tests_cc_tests ()
{
  test_basic_struct ();
  test_length ();
  test_union ();
  test_finalization ();
  test_deletable_global ();
  test_inheritance ();
  test_chain_next ();
  test_user_struct ();
  test_tree_marking ();
}

} // namespace selftest

#include "gt-ggc-tests.h"

#else /* #if CHECKING_P */

/* The #if CHECKING_P code above has various GTY-marked roots.
   gengtype has no knowledge of the preprocessor, and so detects
   these roots and writes them out to gt-ggc-tests.h.
   In a !CHECKING_P build we can ignore gt-ggc-tests.h, but the
   root tables are referenced in the various generated gtype-*.c
   files like this:

      ...snip...
      extern const struct ggc_root_tab gt_ggc_r_gt_ggc_tests_h[];
      ...snip...

      EXPORTED_CONST struct ggc_root_tab * const gt_ggc_rtab[] = {
        ...snip...
        gt_ggc_r_gt_ggc_tests_h,
        ...snip...
      };

    Hence to avoid a link failure, we provide dummy implementations
    of these root tables in an unchecked build.

    Note that these conditional roots imply that PCH files are
    incompatible between checked and unchecked builds.  */

EXPORTED_CONST struct ggc_root_tab gt_ggc_r_gt_ggc_tests_h[] = {
  LAST_GGC_ROOT_TAB
};

EXPORTED_CONST struct ggc_root_tab gt_ggc_rd_gt_ggc_tests_h[] = {
  LAST_GGC_ROOT_TAB
};

#endif /* #else clause of #if CHECKING_P */