This patch is for the google/gcc-4_8 branch. I will also submit this to trunk separately.
This fixes a problem with -fdebug-types-section where some types that have contain nested type templates or member function templates are given different type signatures in different compilation units, depending on which instantiations of those members are contained in the compilation unit. The problem is two-fold: (1) When moving a type to its own type unit, nested template instantiations shouldn't appear at all as children of the type in the type unit. The skeleton declaration tree left behind in the CU, however, may still need declarations of those instantiations -- for example, if there's an instantiated member function template, the CU will need a DW_TAG_subprogram DIE at the top level, with DW_AT_specification pointing to its declaration inside the skeleton for the class. (2) When computing the type signature, we want to ignore instantiations. For nested instantiated type templates, I solve this by calling break_out_comdat_types recursively before computing the type signature instead of later. For instantiated member function templates, I simply ignore them during the signature computation, since they don't get moved back to the CU until after the signature computation. Bootstrapped and tested with GCC and GDB test suites. Google ref b/9884815. 2013-07-26 Cary Coutant <ccout...@google.com> gcc/ * dwarf2out.c (die_checksum_ordered): Don't include template instantiations in signature. (is_template_parameter): New function. (is_template_instantiation): New function. (generate_skeleton_bottom_up): Don't include template instantiations in type unit DIE. (generate_skeleton): Likewise. (break_out_comdat_types): Move recursive call to break out nested types earlier. (prune_unused_types_mark_generic_parms_dies): Call is_template_parameter. testsuite/ * g++.dg/debug/dwarf2/dwarf4-nested.C: Update test case. Index: testsuite/g++.dg/debug/dwarf2/dwarf4-nested.C =================================================================== --- testsuite/g++.dg/debug/dwarf2/dwarf4-nested.C (revision 201268) +++ testsuite/g++.dg/debug/dwarf2/dwarf4-nested.C (working copy) @@ -8,10 +8,9 @@ // // { dg-final { scan-assembler "DIE \\(\[^\n\]*\\) DW_TAG_type_unit" } } // -// Check that func is declared exactly twice in the debug info: -// once in the type unit for struct D, and once in the compile unit. +// Check that func is declared exactly once in the debug info. // -// { dg-final { scan-assembler-times "\\.ascii \"func\\\\0\"\[^\n\]*DW_AT_name" 2 } } +// { dg-final { scan-assembler-times "\\.ascii \"func\\\\0\"\[^\n\]*DW_AT_name" 1 } } // // Check to make sure that no type unit contains a DIE with DW_AT_low_pc // or DW_AT_ranges. These patterns assume that the compile unit is always Index: dwarf2out.c =================================================================== --- dwarf2out.c (revision 201268) +++ dwarf2out.c (working copy) @@ -3060,6 +3060,7 @@ static void compute_section_prefix (dw_d static int is_type_die (dw_die_ref); static int is_comdat_die (dw_die_ref); static int is_symbol_die (dw_die_ref); +static inline bool is_template_instantiation (dw_die_ref); static void assign_symbol_names (dw_die_ref); static void break_out_includes (dw_die_ref); static int is_declaration_die (dw_die_ref); @@ -6120,22 +6121,29 @@ die_checksum_ordered (dw_die_ref die, st CHECKSUM_ATTR (attrs.at_type); CHECKSUM_ATTR (attrs.at_friend); - /* Checksum the child DIEs, except for nested types and member functions. */ + /* Checksum the child DIEs. */ c = die->die_child; if (c) do { dw_attr_ref name_attr; c = c->die_sib; name_attr = get_AT (c, DW_AT_name); - if ((is_type_die (c) || c->die_tag == DW_TAG_subprogram) - && name_attr != NULL) + if (is_template_instantiation (c)) { + /* Ignore instantiations of member type and function templates. */ + } + else if (name_attr != NULL + && (is_type_die (c) || c->die_tag == DW_TAG_subprogram)) + { + /* Use a shallow checksum for named nested types and member + functions. */ CHECKSUM_ULEB128 ('S'); CHECKSUM_ULEB128 (c->die_tag); CHECKSUM_STRING (AT_string (name_attr)); } else { + /* Use a deep checksum for other children. */ /* Mark this DIE so it gets processed when unmarking. */ if (c->die_mark == 0) c->die_mark = -1; @@ -6540,6 +6548,36 @@ is_class_die (dw_die_ref c) || c->die_tag == DW_TAG_structure_type); } +/* Return non-zero if this DIE is a template parameter. */ + +static inline bool +is_template_parameter (dw_die_ref die) +{ + switch (die->die_tag) + { + case DW_TAG_template_type_param: + case DW_TAG_template_value_param: + case DW_TAG_GNU_template_template_param: + case DW_TAG_GNU_template_parameter_pack: + return true; + default: + return false; + } +} + +/* Return non-zero if this DIE represents a template instantiation. */ + +static inline bool +is_template_instantiation (dw_die_ref die) +{ + dw_die_ref c; + + if (!is_type_die (die) && die->die_tag != DW_TAG_subprogram) + return false; + FOR_EACH_CHILD (die, c, if (is_template_parameter (c)) return true); + return false; +} + static char * gen_internal_sym (const char *prefix) { @@ -7005,23 +7043,36 @@ generate_skeleton_bottom_up (skeleton_ch node.new_die = NULL; if (is_declaration_die (c)) { - /* Clone the existing DIE, move the original to the skeleton - tree (which is in the main CU), and put the clone, with - all the original's children, where the original came from. */ - dw_die_ref clone = clone_die (c); - move_all_children (c, clone); - - /* If the original has a DW_AT_object_pointer attribute, - it would now point to a child DIE just moved to the - cloned tree, so we need to remove that attribute from - the original. */ - remove_AT (c, DW_AT_object_pointer); - - replace_child (c, clone, prev); - generate_skeleton_ancestor_tree (parent); - add_child_die (parent->new_die, c); - node.new_die = c; - c = clone; + if (is_template_instantiation (c)) + { + /* Instantiated templates do not need to be cloned into the + type unit. Just move the DIE and its children back to + the skeleton tree (in the main CU). */ + remove_child_with_prev (c, prev); + add_child_die (parent->new_die, c); + c = prev; + } + else + { + /* Clone the existing DIE, move the original to the skeleton + tree (which is in the main CU), and put the clone, with + all the original's children, where the original came from + (which is about to be moved to the type unit). */ + dw_die_ref clone = clone_die (c); + move_all_children (c, clone); + + /* If the original has a DW_AT_object_pointer attribute, + it would now point to a child DIE just moved to the + cloned tree, so we need to remove that attribute from + the original. */ + remove_AT (c, DW_AT_object_pointer); + + replace_child (c, clone, prev); + generate_skeleton_ancestor_tree (parent); + add_child_die (parent->new_die, c); + node.new_die = c; + c = clone; + } } generate_skeleton_bottom_up (&node); } while (next != NULL); @@ -7039,8 +7090,11 @@ generate_skeleton (dw_die_ref die) node.parent = NULL; /* If this type definition is nested inside another type, - always leave at least a declaration in its place. */ - if (die->die_parent != NULL && is_type_die (die->die_parent)) + and is not an instantiation of a template, always leave + at least a declaration in its place. */ + if (die->die_parent != NULL + && is_type_die (die->die_parent) + && !is_template_instantiation (die)) node.new_die = clone_as_declaration (die); generate_skeleton_bottom_up (&node); @@ -7115,6 +7169,9 @@ break_out_comdat_types (dw_die_ref die) dw_die_ref replacement; comdat_type_node_ref type_node; + /* Break out nested types into their own type units. */ + break_out_comdat_types (c); + /* Create a new type unit DIE as the root for the new tree, and add it to the list of comdat types. */ unit = new_die (DW_TAG_type_unit, NULL, NULL); @@ -7134,9 +7191,6 @@ break_out_comdat_types (dw_die_ref die) replacement = remove_child_or_replace_with_skeleton (unit, c, prev); type_node->skeleton_die = replacement; - /* Break out nested types into their own type units. */ - break_out_comdat_types (c); - /* Add the DIE to the new compunit. */ add_child_die (unit, c); @@ -22278,17 +22332,8 @@ prune_unused_types_mark_generic_parms_di c = die->die_child; do { - switch (c->die_tag) - { - case DW_TAG_template_type_param: - case DW_TAG_template_value_param: - case DW_TAG_GNU_template_template_param: - case DW_TAG_GNU_template_parameter_pack: - prune_unused_types_mark (c, 1); - break; - default: - break; - } + if (is_template_parameter (c)) + prune_unused_types_mark (c, 1); c = c->die_sib; } while (c && c != die->die_child); }