From 73c68f614dcd037a892768ace3481d45d8280436 Mon Sep 17 00:00:00 2001 From: Scott Snyder Date: Fri, 28 Feb 2003 15:57:50 -0800 Subject: [PATCH] flags.h: Add flag_eliminate_unused_debug_types. * flags.h: Add flag_eliminate_unused_debug_types. * toplev.c: Add flag_eliminate_unused_debug_types. (f_options): Add -feliminate-unused-debug-types. * dwarf2out.c (struct file_table): Add emitted member. (splice_child_die): Fix the parent pointer for the child being spliced. (lookup_filename): Maintain file_table.emitted array. Don't output .file directive here. (maybe_emit_file): (new) (init_file_table): Set up file_table.emitted. (dwarf2out_source_line): Use maybe_emit_file. (dwarf2out_start_source_file): Use maybe_emit_file. (dwarf2out_init): Use maybe_emit_file. (prune_unused_types_walk_attribs): (new) (prune_unused_types_mark): (new) (prune_unused_types_walk): (new) (prune_unused_types_prune): (new) (prune_unused_types): (new) (dwarf2out_finish): Call prune_unused_types if flag_eliminate_unused_debug_types is set. * doc/invoke.texi (Option Summary): Add -feliminate-unused-debug-types. (Debugging Options): Likewise. From-SVN: r63588 --- gcc/ChangeLog | 26 +++ gcc/doc/invoke.texi | 13 ++ gcc/dwarf2out.c | 549 ++++++++++++++++++++++++++++++++------------ gcc/flags.h | 4 + gcc/toplev.c | 6 + 5 files changed, 445 insertions(+), 153 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9a4e99cae61..298c1dd0e63 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,29 @@ +2003-02-28 scott snyder + + * flags.h: Add flag_eliminate_unused_debug_types. + * toplev.c: Add flag_eliminate_unused_debug_types. + (f_options): Add -feliminate-unused-debug-types. + * dwarf2out.c (struct file_table): Add emitted member. + (splice_child_die): Fix the parent pointer for the child being + spliced. + (lookup_filename): Maintain file_table.emitted array. Don't + output .file directive here. + (maybe_emit_file): (new) + (init_file_table): Set up file_table.emitted. + (dwarf2out_source_line): Use maybe_emit_file. + (dwarf2out_start_source_file): Use maybe_emit_file. + (dwarf2out_init): Use maybe_emit_file. + (prune_unused_types_walk_attribs): (new) + (prune_unused_types_mark): (new) + (prune_unused_types_walk): (new) + (prune_unused_types_prune): (new) + (prune_unused_types): (new) + (dwarf2out_finish): Call prune_unused_types if + flag_eliminate_unused_debug_types is set. + * doc/invoke.texi (Option Summary): Add + -feliminate-unused-debug-types. + (Debugging Options): Likewise. + 2003-02-28 Geoffrey Keating * doc/invoke.texi: Change .pch to .gch. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 90790df59af..755d0dd54aa 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -258,6 +258,7 @@ in the following sections. -p -pg -print-file-name=@var{library} -print-libgcc-file-name @gol -print-multi-directory -print-multi-lib @gol -print-prog-name=@var{program} -print-search-dirs -Q @gol +-feliminate-unused-debug-types @gol -save-temps -time} @item Optimization Options @@ -3412,6 +3413,18 @@ anything else. @opindex dumpspecs Print the compiler's built-in specs---and don't do anything else. (This is used when GCC itself is being built.) @xref{Spec Files}. + +@item -feliminate-unused-debug-types +@opindex feliminate-unused-debug-types +Normally, when producing DWARF2 output, GCC will emit debugging +information for all types declared in a compilation +unit, regardless of whether or not they are actually used +in that compilation unit. Sometimes this is useful, such as +if, in the debugger, you want to cast a value to a type that is +not actually used in your program (but is declared). More often, +however, this results in a significant amount of wasted space. +With this option, GCC will avoid producing debug symbol output +for types that are nowhere used in the source file being compiled. @end table @node Optimize Options diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index ff0b8b0bdbb..03d02cc3860 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -337,7 +337,7 @@ static void dwarf2out_frame_debug_expr PARAMS ((rtx, const char *)); /* Support for complex CFA locations. */ static void output_cfa_loc PARAMS ((dw_cfi_ref)); static void get_cfa_from_loc_descr PARAMS ((dw_cfa_location *, - struct dw_loc_descr_struct *)); + struct dw_loc_descr_struct *)); static struct dw_loc_descr_struct *build_cfa_loc PARAMS ((dw_cfa_location *)); static void def_cfa_1 PARAMS ((const char *, @@ -1236,7 +1236,7 @@ static dw_cfa_location cfa_temp; Rule 1: (set :cfa.reg) effects: cfa.reg = - cfa.offset unchanged + cfa.offset unchanged cfa_temp.reg = cfa_temp.offset = cfa.offset @@ -1357,7 +1357,7 @@ dwarf2out_frame_debug_expr (expr, label) case REG: /* Rule 1 */ /* Update the CFA rule wrt SP or FP. Make sure src is - relative to the current CFA register. */ + relative to the current CFA register. */ switch (GET_CODE (src)) { /* Setting FP from SP. */ @@ -1620,7 +1620,7 @@ dwarf2out_frame_debug_expr (expr, label) else { /* Otherwise, we'll need to look in the stack to - calculate the CFA. */ + calculate the CFA. */ rtx x = XEXP (dest, 0); if (GET_CODE (x) != REG) @@ -2123,13 +2123,13 @@ output_call_frame_info (for_eh) dw2_asm_output_data_uleb128 (size, "Augmentation size"); if (fde->uses_eh_lsda) - { - ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA", + { + ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA", fde->funcdef_number); - dw2_asm_output_encoded_addr_rtx ( + dw2_asm_output_encoded_addr_rtx ( lsda_encoding, gen_rtx_SYMBOL_REF (Pmode, l1), "Language Specific Data Area"); - } + } else { if (lsda_encoding == DW_EH_PE_aligned) @@ -2151,7 +2151,7 @@ output_call_frame_info (for_eh) /* Pad the FDE out to an address sized boundary. */ ASM_OUTPUT_ALIGN (asm_out_file, - floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE))); + floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE))); ASM_OUTPUT_LABEL (asm_out_file, l2); } @@ -2953,9 +2953,9 @@ output_loc_operands (loc) case DW_OP_skip: case DW_OP_bra: /* We currently don't make any attempt to make sure these are - aligned properly like we do for the main unwind info, so - don't support emitting things larger than a byte if we're - only doing unwinding. */ + aligned properly like we do for the main unwind info, so + don't support emitting things larger than a byte if we're + only doing unwinding. */ abort (); #endif case DW_OP_const1u: @@ -3458,6 +3458,7 @@ static GTY(()) limbo_die_node *limbo_die_list; /* Filenames referenced by this compilation unit. */ static GTY(()) varray_type file_table; +static GTY(()) varray_type file_table_emitted; static GTY(()) size_t file_table_last_lookup_index; /* A pointer to the base of a table of references to DIE's that describe @@ -3844,6 +3845,14 @@ static void add_loc_descr_to_loc_list PARAMS ((dw_loc_list_ref *, static void output_loc_list PARAMS ((dw_loc_list_ref)); static char *gen_internal_sym PARAMS ((const char *)); +static void prune_unmark_dies PARAMS ((dw_die_ref)); +static void prune_unused_types_mark PARAMS ((dw_die_ref, int)); +static void prune_unused_types_walk PARAMS ((dw_die_ref)); +static void prune_unused_types_walk_attribs PARAMS ((dw_die_ref)); +static void prune_unused_types_prune PARAMS ((dw_die_ref)); +static void prune_unused_types PARAMS ((void)); +static int maybe_emit_file PARAMS ((int)); + /* Section names used to hold DWARF debugging information. */ #ifndef DEBUG_INFO_SECTION #define DEBUG_INFO_SECTION ".debug_info" @@ -5217,6 +5226,7 @@ splice_child_die (parent, child) break; } + child->die_parent = parent; child->die_sib = parent->die_child; parent->die_child = child; } @@ -5699,12 +5709,12 @@ same_dw_val_p (v1, v2, mark) return v1->v.val_unsigned == v2->v.val_unsigned; case dw_val_class_long_long: return v1->v.val_long_long.hi == v2->v.val_long_long.hi - && v1->v.val_long_long.low == v2->v.val_long_long.low; + && v1->v.val_long_long.low == v2->v.val_long_long.low; case dw_val_class_float: if (v1->v.val_float.length != v2->v.val_float.length) return 0; for (i = 0; i < v1->v.val_float.length; i++) - if (v1->v.val_float.array[i] != v2->v.val_float.array[i]) + if (v1->v.val_float.array[i] != v2->v.val_float.array[i]) return 0; return 1; case dw_val_class_flag: @@ -6145,7 +6155,7 @@ break_out_includes (die) if (is_dupl) *pnode = node->next; else - { + { pnode = &node->next; record_comdat_symbol_number (node->die, cu_hash_table, comdat_symbol_number); @@ -7078,7 +7088,7 @@ output_aranges () if (DWARF_ARANGES_PAD_SIZE) { /* Pad using a 2 byte words so that padding is correct for any - pointer size. */ + pointer size. */ dw2_asm_output_data (2, 0, "Pad to %d byte boundary", 2 * DWARF2_ADDR_SIZE); for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2) @@ -7582,8 +7592,8 @@ output_line_info () prologue. */ /* Don't emit anything for redundant notes. Just updating the - address doesn't accomplish anything, because we already assume - that anything after the last address is this line. */ + address doesn't accomplish anything, because we already assume + that anything after the last address is this line. */ if (line_info->dw_line_num == current_line && line_info->dw_file_num == current_file) continue; @@ -7609,7 +7619,7 @@ output_line_info () else { /* This can handle any delta. This takes - 4+DWARF2_ADDR_SIZE bytes. */ + 4+DWARF2_ADDR_SIZE bytes. */ dw2_asm_output_data (1, 0, "DW_LNE_set_address"); dw2_asm_output_data_uleb128 (1 + DWARF2_ADDR_SIZE, NULL); dw2_asm_output_data (1, DW_LNE_set_address, NULL); @@ -7831,9 +7841,9 @@ base_type_die (type) { case INTEGER_TYPE: /* Carefully distinguish the C character types, without messing - up if the language is not C. Note that we check only for the names - that contain spaces; other names might occur by coincidence in other - languages. */ + up if the language is not C. Note that we check only for the names + that contain spaces; other names might occur by coincidence in other + languages. */ if (! (TYPE_PRECISION (type) == CHAR_TYPE_SIZE && (type == char_type_node || ! strcmp (type_name, "signed char") @@ -8284,28 +8294,28 @@ mem_loc_descriptor (rtl, mode) case SUBREG: /* The case of a subreg may arise when we have a local (register) - variable or a formal (register) parameter which doesn't quite fill - up an entire register. For now, just assume that it is - legitimate to make the Dwarf info refer to the whole register which - contains the given subreg. */ + variable or a formal (register) parameter which doesn't quite fill + up an entire register. For now, just assume that it is + legitimate to make the Dwarf info refer to the whole register which + contains the given subreg. */ rtl = SUBREG_REG (rtl); /* ... fall through ... */ case REG: /* Whenever a register number forms a part of the description of the - method for calculating the (dynamic) address of a memory resident - object, DWARF rules require the register number be referred to as - a "base register". This distinction is not based in any way upon - what category of register the hardware believes the given register - belongs to. This is strictly DWARF terminology we're dealing with - here. Note that in cases where the location of a memory-resident - data object could be expressed as: OP_ADD (OP_BASEREG (basereg), - OP_CONST (0)) the actual DWARF location descriptor that we generate - may just be OP_BASEREG (basereg). This may look deceptively like - the object in question was allocated to a register (rather than in - memory) so DWARF consumers need to be aware of the subtle - distinction between OP_REG and OP_BASEREG. */ + method for calculating the (dynamic) address of a memory resident + object, DWARF rules require the register number be referred to as + a "base register". This distinction is not based in any way upon + what category of register the hardware believes the given register + belongs to. This is strictly DWARF terminology we're dealing with + here. Note that in cases where the location of a memory-resident + data object could be expressed as: OP_ADD (OP_BASEREG (basereg), + OP_CONST (0)) the actual DWARF location descriptor that we generate + may just be OP_BASEREG (basereg). This may look deceptively like + the object in question was allocated to a register (rather than in + memory) so DWARF consumers need to be aware of the subtle + distinction between OP_REG and OP_BASEREG. */ if (REGNO (rtl) < FIRST_PSEUDO_REGISTER) mem_loc_result = based_loc_descr (reg_number (rtl), 0); break; @@ -8354,7 +8364,7 @@ mem_loc_descriptor (rtl, mode) case PRE_MODIFY: /* Extract the PLUS expression nested inside and fall into - PLUS code below. */ + PLUS code below. */ rtl = XEXP (rtl, 1); goto plus; @@ -8475,10 +8485,10 @@ loc_descriptor (rtl) { case SUBREG: /* The case of a subreg may arise when we have a local (register) - variable or a formal (register) parameter which doesn't quite fill - up an entire register. For now, just assume that it is - legitimate to make the Dwarf info refer to the whole register which - contains the given subreg. */ + variable or a formal (register) parameter which doesn't quite fill + up an entire register. For now, just assume that it is + legitimate to make the Dwarf info refer to the whole register which + contains the given subreg. */ rtl = SUBREG_REG (rtl); /* ... fall through ... */ @@ -9199,9 +9209,9 @@ add_const_value_attribute (die, rtl) case CONST_DOUBLE: /* Note that a CONST_DOUBLE rtx could represent either an integer or a - floating-point constant. A CONST_DOUBLE is used whenever the - constant requires more than one word in order to be adequately - represented. We output CONST_DOUBLEs as blocks. */ + floating-point constant. A CONST_DOUBLE is used whenever the + constant requires more than one word in order to be adequately + represented. We output CONST_DOUBLEs as blocks. */ { enum machine_mode mode = GET_MODE (rtl); @@ -9258,16 +9268,16 @@ add_const_value_attribute (die, rtl) case PLUS: /* In cases where an inlined instance of an inline function is passed - the address of an `auto' variable (which is local to the caller) we - can get a situation where the DECL_RTL of the artificial local - variable (for the inlining) which acts as a stand-in for the - corresponding formal parameter (of the inline function) will look - like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). This is not - exactly a compile-time constant expression, but it isn't the address - of the (artificial) local variable either. Rather, it represents the - *value* which the artificial local variable always has during its - lifetime. We currently have no way to represent such quasi-constant - values in Dwarf, so for now we just punt and generate nothing. */ + the address of an `auto' variable (which is local to the caller) we + can get a situation where the DECL_RTL of the artificial local + variable (for the inlining) which acts as a stand-in for the + corresponding formal parameter (of the inline function) will look + like (plus:SI (reg:SI FRAME_PTR) (const_int ...)). This is not + exactly a compile-time constant expression, but it isn't the address + of the (artificial) local variable either. Rather, it represents the + *value* which the artificial local variable always has during its + lifetime. We currently have no way to represent such quasi-constant + values in Dwarf, so for now we just punt and generate nothing. */ break; default: @@ -9366,7 +9376,7 @@ rtl_for_decl_location (decl) if (rtl && (CONSTANT_P (rtl) || (GET_CODE (rtl) == MEM - && CONSTANT_P (XEXP (rtl, 0))))) + && CONSTANT_P (XEXP (rtl, 0))))) { rtl = (*targetm.delegitimize_address) (rtl); return rtl; @@ -9653,23 +9663,23 @@ add_bound_info (subrange_die, bound_attr, bound) case SAVE_EXPR: /* If optimization is turned on, the SAVE_EXPRs that describe how to - access the upper bound values may be bogus. If they refer to a - register, they may only describe how to get at these values at the - points in the generated code right after they have just been - computed. Worse yet, in the typical case, the upper bound values - will not even *be* computed in the optimized code (though the - number of elements will), so these SAVE_EXPRs are entirely - bogus. In order to compensate for this fact, we check here to see - if optimization is enabled, and if so, we don't add an attribute - for the (unknown and unknowable) upper bound. This should not - cause too much trouble for existing (stupid?) debuggers because - they have to deal with empty upper bounds location descriptions - anyway in order to be able to deal with incomplete array types. - Of course an intelligent debugger (GDB?) should be able to - comprehend that a missing upper bound specification in an array - type used for a storage class `auto' local array variable - indicates that the upper bound is both unknown (at compile- time) - and unknowable (at run-time) due to optimization. + access the upper bound values may be bogus. If they refer to a + register, they may only describe how to get at these values at the + points in the generated code right after they have just been + computed. Worse yet, in the typical case, the upper bound values + will not even *be* computed in the optimized code (though the + number of elements will), so these SAVE_EXPRs are entirely + bogus. In order to compensate for this fact, we check here to see + if optimization is enabled, and if so, we don't add an attribute + for the (unknown and unknowable) upper bound. This should not + cause too much trouble for existing (stupid?) debuggers because + they have to deal with empty upper bounds location descriptions + anyway in order to be able to deal with incomplete array types. + Of course an intelligent debugger (GDB?) should be able to + comprehend that a missing upper bound specification in an array + type used for a storage class `auto' local array variable + indicates that the upper bound is both unknown (at compile- time) + and unknowable (at run-time) due to optimization. We assume that a MEM rtx is safe because gcc wouldn't put the value there unless it was going to be used repeatedly in the @@ -9790,7 +9800,7 @@ add_subscript_info (type_die, type) /* Arrays come in three flavors: Unspecified bounds, fixed bounds, and (in GNU C only) variable bounds. Handle all three forms - here. */ + here. */ subrange_die = new_die (DW_TAG_subrange_type, type_die, NULL); if (domain) { @@ -9852,9 +9862,9 @@ add_byte_size_attribute (die, tree_node) break; case FIELD_DECL: /* For a data member of a struct or union, the DW_AT_byte_size is - generally given as the number of bytes normally allocated for an - object of the *declared* type of the member itself. This is true - even for bit-fields. */ + generally given as the number of bytes normally allocated for an + object of the *declared* type of the member itself. This is true + even for bit-fields. */ size = simple_type_size_in_bits (field_type (tree_node)) / BITS_PER_UNIT; break; default: @@ -9979,7 +9989,7 @@ add_abstract_origin_attribute (die, origin) function, if we're in an exception handler or some such; make sure that the abstract function has been written out. - Doing this for nested functions is wrong, however; functions are + Doing this for nested functions is wrong, however; functions are distinct units, and our context might not even be inline. */ tree fn = origin; @@ -10239,8 +10249,8 @@ type_tag (type) t = TYPE_NAME (type); /* The g++ front end makes the TYPE_NAME of *each* tagged type point to - a TYPE_DECL node, regardless of whether or not a `typedef' was - involved. */ + a TYPE_DECL node, regardless of whether or not a `typedef' was + involved. */ else if (TREE_CODE (TYPE_NAME (type)) == TYPE_DECL && ! DECL_IGNORED_P (TYPE_NAME (type))) t = DECL_NAME (TYPE_NAME (type)); @@ -10888,8 +10898,8 @@ gen_subprogram_die (decl, context_die) #endif /* Define the "frame base" location for this routine. We use the - frame pointer or stack pointer registers, since the RTL for local - variables is relative to one of them. */ + frame pointer or stack pointer registers, since the RTL for local + variables is relative to one of them. */ fp_reg = frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx; add_AT_loc (subr_die, DW_AT_frame_base, reg_loc_descriptor (fp_reg)); @@ -10927,7 +10937,7 @@ gen_subprogram_die (decl, context_die) tree parm; /* When generating DIEs, generate the unspecified_parameters DIE - instead if we come across the arg "__builtin_va_alist" */ + instead if we come across the arg "__builtin_va_alist" */ for (parm = arg_decls; parm; parm = TREE_CHAIN (parm)) if (TREE_CODE (parm) == PARM_DECL) { @@ -10940,11 +10950,11 @@ gen_subprogram_die (decl, context_die) } /* Decide whether we need an unspecified_parameters DIE at the end. - There are 2 more cases to do this for: 1) the ansi ... declaration - - this is detectable when the end of the arg list is not a - void_type_node 2) an unprototyped function declaration (not a - definition). This just means that we have no info about the - parameters at all. */ + There are 2 more cases to do this for: 1) the ansi ... declaration - + this is detectable when the end of the arg list is not a + void_type_node 2) an unprototyped function declaration (not a + definition). This just means that we have no info about the + parameters at all. */ fn_arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl)); if (fn_arg_types != NULL) { @@ -11495,7 +11505,7 @@ gen_struct_or_union_type_die (type, context_die) if (complete) { /* Prevent infinite recursion in cases where the type of some member of - this type is expressed in terms of this type itself. */ + this type is expressed in terms of this type itself. */ TREE_ASM_WRITTEN (type) = 1; add_byte_size_attribute (type_die, type); if (TYPE_STUB_DECL (type) != NULL_TREE) @@ -11645,7 +11655,7 @@ gen_type_die (type, context_die) TREE_ASM_WRITTEN (type) = 1; /* For these types, all that is required is that we output a DIE (or a - set of DIEs) to represent the "basis" type. */ + set of DIEs) to represent the "basis" type. */ gen_type_die (TREE_TYPE (type), context_die); break; @@ -11658,7 +11668,7 @@ gen_type_die (type, context_die) gen_type_die (TREE_TYPE (type), context_die); /* Now output a DIE to represent this pointer-to-data-member type - itself. */ + itself. */ gen_ptr_to_mbr_type_die (type, context_die); break; @@ -11703,11 +11713,11 @@ gen_type_die (type, context_die) case UNION_TYPE: case QUAL_UNION_TYPE: /* If this is a nested type whose containing class hasn't been written - out yet, writing it out will cover this one, too. This does not apply - to instantiations of member class templates; they need to be added to - the containing class as they are generated. FIXME: This hurts the - idea of combining type decls from multiple TUs, since we can't predict - what set of template instantiations we'll get. */ + out yet, writing it out will cover this one, too. This does not apply + to instantiations of member class templates; they need to be added to + the containing class as they are generated. FIXME: This hurts the + idea of combining type decls from multiple TUs, since we can't predict + what set of template instantiations we'll get. */ if (TYPE_CONTEXT (type) && AGGREGATE_TYPE_P (TYPE_CONTEXT (type)) && ! TREE_ASM_WRITTEN (TYPE_CONTEXT (type))) @@ -11850,12 +11860,12 @@ gen_block_die (stmt, context_die, depth) else { /* In the case where the current block represents an inlining of the - "body block" of an inline function, we must *NOT* output any DIE for - this block because we have already output a DIE to represent the whole - inlined function scope and the "body block" of any function doesn't - really represent a different scope according to ANSI C rules. So we - check here to make sure that this block does not represent a "body - block inlining" before trying to set the MUST_OUTPUT_DIE flag. */ + "body block" of an inline function, we must *NOT* output any DIE for + this block because we have already output a DIE to represent the whole + inlined function scope and the "body block" of any function doesn't + really represent a different scope according to ANSI C rules. So we + check here to make sure that this block does not represent a "body + block inlining" before trying to set the MUST_OUTPUT_DIE flag. */ if (! is_body_block (origin ? origin : stmt)) { /* Determine if this block directly contains any "significant" @@ -11980,7 +11990,7 @@ gen_decl_die (decl, context_die) case CONST_DECL: /* The individual enumerators of an enum type get output when we output - the Dwarf representation of the relevant enum type itself. */ + the Dwarf representation of the relevant enum type itself. */ break; case FUNCTION_DECL: @@ -12030,16 +12040,16 @@ gen_decl_die (decl, context_die) case TYPE_DECL: /* If we are in terse mode, don't generate any DIEs to represent any - actual typedefs. */ + actual typedefs. */ if (debug_info_level <= DINFO_LEVEL_TERSE) break; /* In the special case of a TYPE_DECL node representing the declaration - of some type tag, if the given TYPE_DECL is marked as having been - instantiated from some other (original) TYPE_DECL node (e.g. one which - was generated within the original definition of an inline function) we - have to generate a special (abbreviated) DW_TAG_structure_type, - DW_TAG_union_type, or DW_TAG_enumeration_type DIE here. */ + of some type tag, if the given TYPE_DECL is marked as having been + instantiated from some other (original) TYPE_DECL node (e.g. one which + was generated within the original definition of an inline function) we + have to generate a special (abbreviated) DW_TAG_structure_type, + DW_TAG_union_type, or DW_TAG_enumeration_type DIE here. */ if (TYPE_DECL_IS_STUB (decl) && decl_ultimate_origin (decl) != NULL_TREE) { gen_tagged_type_instantiation_die (TREE_TYPE (decl), context_die); @@ -12060,12 +12070,12 @@ gen_decl_die (decl, context_die) case VAR_DECL: /* If we are in terse mode, don't generate any DIEs to represent any - variable declarations or definitions. */ + variable declarations or definitions. */ if (debug_info_level <= DINFO_LEVEL_TERSE) break; /* Output any DIEs that are needed to specify the type of this data - object. */ + object. */ gen_type_die (TREE_TYPE (decl), context_die); /* And its containing type. */ @@ -12074,9 +12084,9 @@ gen_decl_die (decl, context_die) gen_type_die_for_member (origin, decl, context_die); /* Now output the DIE to represent the data object itself. This gets - complicated because of the possibility that the VAR_DECL really - represents an inlined instance of a formal parameter for an inline - function. */ + complicated because of the possibility that the VAR_DECL really + represents an inlined instance of a formal parameter for an inline + function. */ origin = decl_ultimate_origin (decl); if (origin != NULL_TREE && TREE_CODE (origin) == PARM_DECL) gen_formal_parameter_die (decl, context_die); @@ -12163,32 +12173,32 @@ dwarf2out_decl (decl) case FUNCTION_DECL: /* Ignore this FUNCTION_DECL if it refers to a builtin declaration of a - builtin function. Explicit programmer-supplied declarations of - these same functions should NOT be ignored however. */ + builtin function. Explicit programmer-supplied declarations of + these same functions should NOT be ignored however. */ if (DECL_EXTERNAL (decl) && DECL_BUILT_IN (decl)) return; /* What we would really like to do here is to filter out all mere - file-scope declarations of file-scope functions which are never - referenced later within this translation unit (and keep all of ones - that *are* referenced later on) but we aren't clairvoyant, so we have - no idea which functions will be referenced in the future (i.e. later - on within the current translation unit). So here we just ignore all - file-scope function declarations which are not also definitions. If - and when the debugger needs to know something about these functions, - it will have to hunt around and find the DWARF information associated - with the definition of the function. + file-scope declarations of file-scope functions which are never + referenced later within this translation unit (and keep all of ones + that *are* referenced later on) but we aren't clairvoyant, so we have + no idea which functions will be referenced in the future (i.e. later + on within the current translation unit). So here we just ignore all + file-scope function declarations which are not also definitions. If + and when the debugger needs to know something about these functions, + it will have to hunt around and find the DWARF information associated + with the definition of the function. We can't just check DECL_EXTERNAL to find out which FUNCTION_DECL - nodes represent definitions and which ones represent mere - declarations. We have to check DECL_INITIAL instead. That's because - the C front-end supports some weird semantics for "extern inline" - function definitions. These can get inlined within the current - translation unit (an thus, we need to generate Dwarf info for their - abstract instances so that the Dwarf info for the concrete inlined - instances can have something to refer to) but the compiler never - generates any out-of-lines instances of such things (despite the fact - that they *are* definitions). + nodes represent definitions and which ones represent mere + declarations. We have to check DECL_INITIAL instead. That's because + the C front-end supports some weird semantics for "extern inline" + function definitions. These can get inlined within the current + translation unit (an thus, we need to generate Dwarf info for their + abstract instances so that the Dwarf info for the concrete inlined + instances can have something to refer to) but the compiler never + generates any out-of-lines instances of such things (despite the fact + that they *are* definitions). The important point is that the C front-end marks these "extern inline" functions as DECL_EXTERNAL, but we need to generate DWARF for @@ -12207,18 +12217,18 @@ dwarf2out_decl (decl) case VAR_DECL: /* Ignore this VAR_DECL if it refers to a file-scope extern data object - declaration and if the declaration was never even referenced from - within this entire compilation unit. We suppress these DIEs in - order to save space in the .debug section (by eliminating entries - which are probably useless). Note that we must not suppress - block-local extern declarations (whether used or not) because that - would screw-up the debugger's name lookup mechanism and cause it to - miss things which really ought to be in scope at a given point. */ + declaration and if the declaration was never even referenced from + within this entire compilation unit. We suppress these DIEs in + order to save space in the .debug section (by eliminating entries + which are probably useless). Note that we must not suppress + block-local extern declarations (whether used or not) because that + would screw-up the debugger's name lookup mechanism and cause it to + miss things which really ought to be in scope at a given point. */ if (DECL_EXTERNAL (decl) && !TREE_USED (decl)) return; /* If we are in terse mode, don't generate any DIEs to represent any - variable declarations or definitions. */ + variable declarations or definitions. */ if (debug_info_level <= DINFO_LEVEL_TERSE) return; break; @@ -12229,11 +12239,11 @@ dwarf2out_decl (decl) return; /* Don't bother trying to generate any DIEs to represent any of the - normal built-in types for the language we are compiling. */ + normal built-in types for the language we are compiling. */ if (DECL_SOURCE_LINE (decl) == 0) { /* OK, we need to generate one for `bool' so GDB knows what type - comparisons have. */ + comparisons have. */ if ((get_AT_unsigned (comp_unit_die, DW_AT_language) == DW_LANG_C_plus_plus) && TREE_CODE (TREE_TYPE (decl)) == BOOLEAN_TYPE @@ -12336,7 +12346,7 @@ lookup_filename (file_name) const char *last = VARRAY_CHAR_PTR (file_table, file_table_last_lookup_index); if (strcmp (file_name, last) == 0) - return file_table_last_lookup_index; + return file_table_last_lookup_index; } /* Didn't match the previous lookup, search the table */ @@ -12352,25 +12362,43 @@ lookup_filename (file_name) file_table_last_lookup_index = n; save_file_name = (char *) ggc_strdup (file_name); VARRAY_PUSH_CHAR_PTR (file_table, save_file_name); - - if (DWARF2_ASM_LINE_DEBUG_INFO) - { - fprintf (asm_out_file, "\t.file %lu ", (unsigned long) i); - output_quoted_string (asm_out_file, file_name); - fputc ('\n', asm_out_file); - } + VARRAY_PUSH_UINT (file_table_emitted, 0); return i; } +static int +maybe_emit_file (fileno) + int fileno; +{ + static int emitcount = 0; + if (DWARF2_ASM_LINE_DEBUG_INFO && fileno > 0) + { + if (!VARRAY_UINT (file_table_emitted, fileno)) + { + VARRAY_UINT (file_table_emitted, fileno) = ++emitcount; + fprintf (asm_out_file, "\t.file %u ", + VARRAY_UINT (file_table_emitted, fileno)); + output_quoted_string (asm_out_file, + VARRAY_CHAR_PTR (file_table, fileno)); + fputc ('\n', asm_out_file); + } + return VARRAY_UINT (file_table_emitted, fileno); + } + else + return fileno; +} + static void init_file_table () { /* Allocate the initial hunk of the file_table. */ VARRAY_CHAR_PTR_INIT (file_table, 64, "file_table"); + VARRAY_UINT_INIT (file_table_emitted, 64, "file_table_emitted"); /* Skip the first entry - file numbers begin at 1. */ VARRAY_PUSH_CHAR_PTR (file_table, NULL); + VARRAY_PUSH_UINT (file_table_emitted, 0); file_table_last_lookup_index = 0; } @@ -12396,6 +12424,8 @@ dwarf2out_source_line (line, filename) { unsigned file_num = lookup_filename (filename); + file_num = maybe_emit_file (file_num); + /* Emit the .loc directive understood by GNU as. */ fprintf (asm_out_file, "\t.loc %d %d 0\n", file_num, line); @@ -12487,6 +12517,7 @@ dwarf2out_start_source_file (lineno, filename) dw2_asm_output_data (1, DW_MACINFO_start_file, "Start new file"); dw2_asm_output_data_uleb128 (lineno, "Included from line number %d", lineno); + maybe_emit_file (lookup_filename (filename)); dw2_asm_output_data_uleb128 (lookup_filename (filename), "Filename we just started"); } @@ -12646,6 +12677,215 @@ output_indirect_string (h, v) return 1; } + + +/* Clear the marks for a die and its children. + Be cool if the mark isn't set. */ + +static void +prune_unmark_dies (die) + dw_die_ref die; +{ + dw_die_ref c; + die->die_mark = 0; + for (c = die->die_child; c; c = c->die_sib) + prune_unmark_dies (c); +} + + +/* Given DIE that we're marking as used, find any other dies + it references as attributes and mark them as used. */ + +static void +prune_unused_types_walk_attribs (die) + dw_die_ref die; +{ + dw_attr_ref a; + + for (a = die->die_attr; a != NULL; a = a->dw_attr_next) + { + if (a->dw_attr_val.val_class == dw_val_class_die_ref) + { + /* A reference to another DIE. + Make sure that it will get emitted. */ + prune_unused_types_mark (a->dw_attr_val.v.val_die_ref.die, 1); + } + else if (a->dw_attr == DW_AT_decl_file) + { + /* A reference to a file. Make sure the file name is emitted. */ + a->dw_attr_val.v.val_unsigned = + maybe_emit_file (a->dw_attr_val.v.val_unsigned); + } + } +} + + +/* Mark DIE as being used. If DOKIDS is true, then walk down + to DIE's children. */ + +static void +prune_unused_types_mark (die, dokids) + dw_die_ref die; + int dokids; +{ + dw_die_ref c; + + if (die->die_mark == 0) + { + /* We haven't done this node yet. Mark it as used. */ + die->die_mark = 1; + + /* We also have to mark its parents as used. + (But we don't want to mark our parents' kids due to this.) */ + if (die->die_parent) + prune_unused_types_mark (die->die_parent, 0); + + /* Mark any referenced nodes. */ + prune_unused_types_walk_attribs (die); + } + + if (dokids && die->die_mark != 2) + { + /* We need to walk the children, but haven't done so yet. + Remember that we've walked the kids. */ + die->die_mark = 2; + + /* Walk them. */ + for (c = die->die_child; c; c = c->die_sib) + { + /* If this is an array type, we need to make sure our + kids get marked, even if they're types. */ + if (die->die_tag == DW_TAG_array_type) + prune_unused_types_mark (c, 1); + else + prune_unused_types_walk (c); + } + } +} + + +/* Walk the tree DIE and mark types that we actually use. */ + +static void +prune_unused_types_walk (die) + dw_die_ref die; +{ + dw_die_ref c; + + /* Don't do anything if this node is already marked. */ + if (die->die_mark) + return; + + switch (die->die_tag) { + case DW_TAG_const_type: + case DW_TAG_packed_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_volatile_type: + case DW_TAG_typedef: + case DW_TAG_array_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_friend: + case DW_TAG_variant_part: + case DW_TAG_enumeration_type: + case DW_TAG_subroutine_type: + case DW_TAG_string_type: + case DW_TAG_set_type: + case DW_TAG_subrange_type: + case DW_TAG_ptr_to_member_type: + case DW_TAG_file_type: + /* It's a type node --- don't mark it. */ + return; + + default: + /* Mark everything else. */ + break; + } + + die->die_mark = 1; + + /* Now, mark any dies referenced from here. */ + prune_unused_types_walk_attribs (die); + + /* Mark children. */ + for (c = die->die_child; c; c = c->die_sib) + prune_unused_types_walk (c); +} + + +/* Remove from the tree DIE any dies that aren't marked. */ + +static void +prune_unused_types_prune (die) + dw_die_ref die; +{ + dw_die_ref c, p, n; + if (!die->die_mark) + abort(); + + p = NULL; + for (c = die->die_child; c; c = n) + { + n = c->die_sib; + if (c->die_mark) + { + prune_unused_types_prune (c); + p = c; + } + else + { + if (p) + p->die_sib = n; + else + die->die_child = n; + free_die (c); + } + } +} + + +/* Remove dies representing declarations that we never use. */ + +static void +prune_unused_types () +{ + unsigned int i; + limbo_die_node *node; + + /* Clear all the marks. */ + prune_unmark_dies (comp_unit_die); + for (node = limbo_die_list; node; node = node->next) + prune_unmark_dies (node->die); + + /* Set the mark on nodes that are actually used. */ + prune_unused_types_walk (comp_unit_die); + for (node = limbo_die_list; node; node = node->next) + prune_unused_types_walk (node->die); + + /* Also set the mark on nodes referenced from the + pubname_table or arange_table. */ + for (i=0; i < pubname_table_in_use; i++) + { + prune_unused_types_mark (pubname_table[i].die, 1); + } + for (i=0; i < arange_table_in_use; i++) + { + prune_unused_types_mark (arange_table[i], 1); + } + + /* Get rid of nodes that aren't marked. */ + prune_unused_types_prune (comp_unit_die); + for (node = limbo_die_list; node; node = node->next) + prune_unused_types_prune (node->die); + + /* Leave the marks clear. */ + prune_unmark_dies (comp_unit_die); + for (node = limbo_die_list; node; node = node->next) + prune_unmark_dies (node->die); +} + /* Output stuff that dwarf requires at the end of every file, and generate the DWARF-2 debugging info. */ @@ -12740,6 +12980,9 @@ dwarf2out_finish (input_filename) if (flag_eliminate_dwarf2_dups) break_out_includes (comp_unit_die); + if (flag_eliminate_unused_debug_types) + prune_unused_types (); + /* Traverse the DIE's and add add sibling attributes to those DIE's that have children. */ add_sibling_attributes (comp_unit_die); diff --git a/gcc/flags.h b/gcc/flags.h index 4a806896901..032edb6d941 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -641,6 +641,10 @@ extern int flag_gcse_sm; extern int flag_eliminate_dwarf2_dups; +/* Nonzero means we should do unused type elimination. */ + +extern int flag_eliminate_unused_debug_types; + /* Nonzero means to collect statistics which might be expensive and to print them when we are done. */ extern int flag_detailed_statistics; diff --git a/gcc/toplev.c b/gcc/toplev.c index ef0ac60ded7..7730a39aacf 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -378,6 +378,10 @@ tree current_function_func_begin_label; int flag_eliminate_dwarf2_dups = 0; +/* Nonzero if doing unused type elimination. */ + +int flag_eliminate_unused_debug_types = 0; + /* Nonzero if generating code to do profiling. */ int profile_flag = 0; @@ -999,6 +1003,8 @@ static const lang_independent_options f_options[] = { {"eliminate-dwarf2-dups", &flag_eliminate_dwarf2_dups, 1, N_("Perform DWARF2 duplicate elimination") }, + {"eliminate-unused-debug-types", &flag_eliminate_unused_debug_types, 1, + N_("Perform unused type elimination in debug info") }, {"float-store", &flag_float_store, 1, N_("Do not store floats in registers") }, {"defer-pop", &flag_defer_pop, 1,