gcc/unittests/ChangeLog: * test-ggc.c: New file. --- gcc/unittests/test-ggc.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 gcc/unittests/test-ggc.c
diff --git a/gcc/unittests/test-ggc.c b/gcc/unittests/test-ggc.c new file mode 100644 index 0000000..9168b71 --- /dev/null +++ b/gcc/unittests/test-ggc.c @@ -0,0 +1,292 @@ +/* Unit tests for GCC's garbage collector (and gengtype etc). + Copyright (C) 2015 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 <gtest/gtest.h> + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "opts.h" +#include "signop.h" +#include "hash-set.h" +#include "fixed-value.h" +#include "alias.h" +#include "flags.h" +#include "symtab.h" +#include "tree-core.h" +#include "stor-layout.h" +#include "tree.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "predict.h" +#include "vec.h" +#include "hashtab.h" +#include "hash-set.h" +#include "machmode.h" +#include "ggc-internal.h" /* (for ggc_force_collect). */ + +/* The various GTY markers must be outside of a namespace to be seen by + gengtype, so we don't put this file within an anonymous namespace. */ + +/* A test fixture for writing ggc tests. */ +class ggc_test : public ::testing::Test +{ + protected: + void + forcibly_ggc_collect () + { + ggc_force_collect = true; + ggc_collect (); + ggc_force_collect = false; + } +}; + +/* Smoketest to ensure that a GC root is marked ("tree" type). */ + +static GTY(()) tree dummy_unittesting_tree; + +TEST_F (ggc_test, tree_marking) +{ + dummy_unittesting_tree = build_int_cst (integer_type_node, 1066); + + forcibly_ggc_collect (); + + EXPECT_TRUE (ggc_marked_p (dummy_unittesting_tree)); +} + +/* Verify that a simple custom struct works, and that it can + own references to non-roots, and have them be marked. */ + +struct GTY(()) test_struct +{ + test_struct *other; +}; + +static GTY(()) test_struct *root_test_struct; + +TEST_F (ggc_test, custom_struct) +{ + root_test_struct = ggc_cleared_alloc <test_struct> (); + root_test_struct->other = ggc_cleared_alloc <test_struct> (); + + forcibly_ggc_collect (); + + EXPECT_TRUE (ggc_marked_p (root_test_struct)); + EXPECT_TRUE (ggc_marked_p (root_test_struct->other)); +} + +/* Verify that destructors get run when instances are collected. */ + +struct GTY(()) test_struct_with_dtor +{ + /* 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; + +TEST_F (ggc_test, finalization) +{ + EXPECT_FALSE (need_finalization_p <test_struct> ()); + EXPECT_TRUE (need_finalization_p <test_struct_with_dtor> ()); + + /* 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; + + forcibly_ggc_collect (); + + /* Verify that the destructor was run for each instance. */ + EXPECT_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; + +TEST_F (ggc_test, deletable_global) +{ + test_of_deletable = ggc_cleared_alloc <test_struct> (); + EXPECT_TRUE (test_of_deletable != NULL); + + forcibly_ggc_collect (); + + EXPECT_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; + +TEST_F (ggc_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 (); + + forcibly_ggc_collect (); + + /* Verify that the roots and everything referenced by them got marked + (both for fields in the base class and those in subclasses). */ + EXPECT_TRUE (ggc_marked_p (test_example_base)); + EXPECT_TRUE (ggc_marked_p (test_example_base->m_a)); + + EXPECT_TRUE (ggc_marked_p (test_some_subclass)); + EXPECT_TRUE (ggc_marked_p (test_some_subclass->m_a)); + EXPECT_TRUE (ggc_marked_p (test_some_subclass->m_b)); + + EXPECT_TRUE (ggc_marked_p (test_some_other_subclass)); + EXPECT_TRUE (ggc_marked_p (test_some_other_subclass->m_a)); + EXPECT_TRUE (ggc_marked_p (test_some_other_subclass->m_c)); + + EXPECT_TRUE (ggc_marked_p (test_some_subclass_as_base_ptr)); + EXPECT_TRUE (ggc_marked_p (test_some_subclass_as_base_ptr->m_a)); + EXPECT_TRUE (ggc_marked_p (((some_subclass *) + test_some_subclass_as_base_ptr)->m_b)); + + EXPECT_TRUE (ggc_marked_p (test_some_other_subclass_as_base_ptr)); + EXPECT_TRUE (ggc_marked_p (test_some_other_subclass_as_base_ptr->m_a)); + EXPECT_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; + +TEST_F (ggc_test, chain_next) +{ + /* 2 million nodes (and thus the same number of stack frames) ought + to be deep enough to crash if gengtype has created something + that recurses. + + This length reliably causes the test to segfault without the + chain_next/prev optimization on this box (Fedora 20 x86_64 with 128GB + of RAM), but causes this test to take about 0.5s, dominating the time + taken by the overall testsuite. + + We could perhaps lower this by not increasing the stack size so much + in toplev.c, or perhaps reducing the stack size when running this + testcase. */ + const int count = 2000000; + + /* 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; + } + + forcibly_ggc_collect (); + + /* If we got here, we survived. */ + + /* Verify that all nodes in the list were marked. */ + EXPECT_TRUE (ggc_marked_p (root_test_node)); + test_node *iter_node = root_test_node->m_next; + for (int i = 0; i < count; i++) + { + EXPECT_TRUE (ggc_marked_p (iter_node)); + EXPECT_EQ (i, iter_node->m_idx); + iter_node = iter_node->m_next; + } +} + +/* Ideas for other tests: + - pch-handling */ + +#include "gt-unittests-test-ggc.h" -- 1.8.5.3