We've been running builds/regression tests for GCC 8.2 configured with
--enable-checking=all, and have observed some failures related to
garbage collection.
First problem:
The g++.dg/pr85039-2.C tests (I've looked in detail at -std=c++98, but
-std=c++11 and -std=c++14 appear to follow the same pattern) see gcc
garbage-collecting a live vector. A subsequent access to the vector
with vec_quick_push causes a segmentation fault, as m_vecpfx.m_num is
0xa5a5a5a5 . The vec data is also being freed / poisoned. The vector in
question is an auto-variable of cp_parser_parenthesized_expression_list,
which is declared as: vec<tree, va_gc> *expression_list;
According to doc/gty/texi: "you should reference all your data from
static or external @code{GTY}-ed variables, and it is advised to call
@code{ggc_collect} with a shallow call stack."
In this case, cgraph_node::finalize_function calls the garage collector,
as we are finishing a member function of a struct. gdb shows a backtrace
of 34 frames, which is not really much as far as C++ parsing goes. The
caller of finalize_function is expand_or_defer_fn, which uses the
expression "function_depth > 1" to compute the no_collect paramter to
finalize_function.
cp_parser_parenthesized_expression_list is in frame 21 of the backtrace
at this point.
So, if we consider this shallow, cp_parser_parenthesized_expression_list
either has to refrain from using a vector with garbage-collected
allocation, or it has to make the pointer reachable from a GC root - at
least if function_depth <= 1.
Is the attached patch the right approach?
When looking at regression test results for gcc version 9.0.0 20181028
(experimental), the excess errors test for g++.dg/pr85039-2.C seems to
pass, yet I can see no definite reason in the source code why that is
so. I tried running the test by hand in order to check if maybe the
patch for PR c++/84455 plays a role,
but running the test by hand, it crashes again, and gdb shows the
telltale a5 pattern in a pointer register.
#0 vec<tree_node*, va_gc, vl_embed>::quick_push (obj=<optimized out>,
this=0x7ffff05ece60)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/vec.h:974
#1 vec_safe_push<tree_node*, va_gc> (obj=<optimized out>,
v=@0x7fffffffd038: 0x7ffff05ece60)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/vec.h:766
#2 cp_parser_parenthesized_expression_list (
parser=parser@entry=0x7ffff7ff83f0,
is_attribute_list=is_attribute_list@entry=0, cast_p=cast_p@entry=false,
allow_expansion_p=allow_expansion_p@entry=true,
non_constant_p=non_constant_p@entry=0x7fffffffd103,
close_paren_loc=close_paren_loc@entry=0x0, wrap_locations_p=false)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:7803
#3 0x00000000006e910d in cp_parser_initializer (
parser=parser@entry=0x7ffff7ff83f0,
is_direct_init=is_direct_init@entry=0x7fffffffd102,
non_constant_p=non_constant_p@entry=0x7fffffffd103,
subexpression_p=subexpression_p@entry=false)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:22009
#4 0x000000000070954e in cp_parser_init_declarator (
parser=parser@entry=0x7ffff7ff83f0,
decl_specifiers=decl_specifiers@entry=0x7fffffffd1c0,
checks=checks@entry=0x0,
function_definition_allowed_p=function_definition_allowed_p@entry=true,
member_p=member_p@entry=false, declares_class_or_enum=<optimized out>,
function_definition_p=0x7fffffffd250, maybe_range_for_decl=0x0,
init_loc=0x7fffffffd1ac, auto_result=0x7fffffffd2e0)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:19827
#5 0x0000000000711c5d in cp_parser_simple_declaration
(parser=0x7ffff7ff83f0,
function_definition_allowed_p=<optimized out>,
maybe_range_for_decl=0x0)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:13179
#6 0x0000000000717bb5 in cp_parser_declaration (parser=0x7ffff7ff83f0)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:12876
#7 0x000000000071837d in cp_parser_translation_unit (parser=0x7ffff7ff83f0)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:4631
#8 c_parse_file ()
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/cp/parser.c:39108
#9 0x0000000000868db1 in c_common_parse_file ()
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/c-family/c-opts.c:1150
#10 0x0000000000e0aaaf in compile_file ()
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/toplev.c:455
#11 0x000000000059248a in do_compile ()
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/toplev.c:2172
#12 toplev::main (this=this@entry=0x7fffffffd54e, argc=<optimized out>,
argc@entry=100, argv=<optimized out>, argv@entry=0x7fffffffd648)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/toplev.c:2307
#13 0x0000000000594b5b in main (argc=100, argv=0x7fffffffd648)
at
/data/hudson/jobs/gcc-9.0.0-linux/workspace/gcc/build/../gcc/gcc/main.c:39
(gdb) info reg rdx
rdx 0xa5a5a5a5 2779096485
0x00000000006e84eb
<cp_parser_parenthesized_expression_list(cp_parser*, int, bool, bool,
bool*, location_t*, bool)+283>: je 0x6e8608
<cp_parser_parenthesized_expression_list(cp_parser*, int, bool, bool,
bool*, location_t*, bool)+568>
0x00000000006e84f1
<cp_parser_parenthesized_expression_list(cp_parser*, int, bool, bool,
bool*, location_t*, bool)+289>: lea 0x1(%rdx),%esi
0x00000000006e84f4
<cp_parser_parenthesized_expression_list(cp_parser*, int, bool, bool,
bool*, location_t*, bool)+292>: mov %esi,0x4(%rax)
=> 0x00000000006e84f7
<cp_parser_parenthesized_expression_list(cp_parser*, int, bool, bool,
bool*, location_t*, bool)+295>: mov %rcx,0x8(%rax,%rdx,8)
0x00000000006e84fc
<cp_parser_parenthesized_expression_list(cp_parser*, int, bool, bool,
bool*, location_t*, bool)+300>: cmp %rcx,0x16cd67d(%rip) #
0x1db5b80 <global_trees>
Second problem:
We see: FAIL: gcc.dg/plugin/start_unit-test-1.c
-fplugin=./start_unit_plugin.so (test for excess errors)
Excess errors:
fake_var not a VAR_DECL
The message comes from plugin; the source code is in
testsuite/gcc.dg/plugin/start_unit_plugin.c . The plugin holds a tree
value in a static variable, yet fails to make it referenced by any
garbage collection root tables.
When configuring with --enable-checking=all, a garbage colection comes
along and
poisons the fake_var allocated by the plugin.
gty.texi advises:
Plugins can add additional root tables. Run the @code{gengtype}
utility in plugin mode as @code{gengtype -P pluginout.h @var{source-dir}
@var{file-list} @var{plugin*.c}} with your plugin files
@var{plugin*.c} using @code{GTY} to generate the @var{pluginout.h} file.
The GCC build tree is needed to be present in that mode.
However, can we use the build directory in the test suite, considering that
it's supposed to be usable also for installed compilers?
I suppose, for gcc core types like tree we should be able to prepare a
header file beforehand.
Third problem:
Both in 8.2 and 9.0.0 configureed with --enable-checking-all, we see:
FAIL: gcc.dg/ipa/ipa-pta-1.c scan-ipa-dump pta2 "bar.arg0 = { a }"
FAIL: gcc.dg/ipa/ipa-pta-1.c scan-ipa-dump pta2 "bar.arg1 = { a }"
Looking closer at the problem in 8.2, see that large parts of the dump
file show signs of garbage-collector poisoned memory.
Looking at "Points-to sets", the information is reached from a static
variable in
tree-ssa-structalias.c that lacks a GTY marker:
static vec<varinfo_t> varmap;
The "name" field in struct variable_info sometimes contains a string
literal, and sometimes a ggc-allocated string.
I'm not sure if ggc can handle this, or if we need to change the code so
that we make these either all ggc-allocated strings, or add a
discriminator so we can tell which kind of string there is so that we
can tell the garbage collector to ignore string literals.
At any rate, We can't make the "vec<varinfo_t>" as-is into a GC root.
Should that be changed into a va_gc vector pointer, and all the code that
uses it adjusted accordingly, or would it be better to use a new variable
as GC root and fake the references with a GTY((user)) for the new variable
that employs a marking function that marks the elements inside varmap?
2018-12-27 Joern Rennecke <joern.renne...@riscy-ip.com>
* cp/parser.c: (expression_list_ref): New GTY variable.
(cp_parser_parenthesized_expression_list): Make it reference
expression_list.
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c (revision 5644)
+++ gcc/cp/parser.c (revision 5645)
@@ -7649,6 +7649,14 @@ cp_parser_postfix_dot_deref_expression (
return postfix_expression;
}
+/* When processing a member function, cgraph_node::finalize_function can
+ call ggc_collect (like if called by expand_or_defer_fn from a struct
+ member function with (function_depth <= 1)).
+ Prevent expression_list in cp_parser_parenthesized_expression_list to
+ be garbage collected then by referencing it from a GC root. */
+typedef vec<tree, va_gc> *tree_gc_vec;
+static GTY(()) vec<tree_gc_vec, va_gc> *expression_list_ref;
+
/* Parse a parenthesized expression-list.
expression-list:
@@ -7705,6 +7713,7 @@ cp_parser_parenthesized_expression_list
return NULL;
expression_list = make_tree_vector ();
+ vec_safe_push (expression_list_ref, expression_list);
/* Within a parenthesized expression, a `>' token is always
the greater-than operator. */
@@ -7797,6 +7806,8 @@ cp_parser_parenthesized_expression_list
cp_lexer_consume_token (parser->lexer);
}
+ expression_list_ref->pop ();
+
if (close_paren_loc)
*close_paren_loc = cp_lexer_peek_token (parser->lexer)->location;