This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] add option to eliminate unused types in dwarf2 output (v2)
- From: Scott Snyder <snyder at fnal dot gov>
- To: gcc-patches at gcc dot gnu dot org
- Date: 04 Jul 2002 17:15:45 -0500
- Subject: [PATCH] add option to eliminate unused types in dwarf2 output (v2)
hi -
Here's a revised version of the -feliminate-unused-dwarf2-types
patch i first sent a couple weeks ago.
(See http://gcc.gnu.org/ml/gcc-patches/2002-06/msg01473.html
for the original explaination.)
In brief, this patch adds a new option -feliminate-unused-dwarf2-types
which eliminates the debugging information for types, functions,
and source files not used in the particular compilation unit being
compiled. This can result in a substantial savings in the size
of the binaries produced by gcc.
I've fixed two bugs since the last version: one that caused the
java front-end to crash, and one that was dropping information
about array dimensions.
Debugging should still work ok when this option is used, except
as follows:
- Any types which are declared but not actually used in the program will not
be known to the debugger.
- This is the biggie: I drop information about function declarations
if a definition is not also present; if it is callable, a definition
should be present in another compilation unit. This applies
to functions that are class members too; this saves a considerable
amount of space. However, this ends up causing a problem for
calling class member functions. For example, in one compilation
unit, the debug information for class A might have a declaration
for a() but not b(), and in a different compilation might
have a declaration for b() but not a(). Which member functiosn
you can call depends on which class definition gdb picks.
Note that you can still set breakpoints, etc., on all the
member functions --- you just have problems calling them.
In princple, i suppose gdb could try to merge together member
function information from all the defintions of a particular
class it sees. But even in the absence of that, the space
savings of this option are great enough that losing this
functionality in gdb is a tradeoff i'd be willing to make.
(In fact, i didn't notice that this was a problem until
i tried running the gdb test suite.)
- Related to the last problem: declarations of pure virtual
functions are also lost. This means that to call such a function,
you need to cast the pointer through which you're calling
to the concrete class.
I've verified that if i change the default for ths
-feliminate-unused-dwarf2-types to 1, the bootstrap still
works and no new regressions show up in the gcc, g++, g77,
and objc test suites.
I've also run the gdb test suite both ways. Enabling the option
causes some additional failures. Some of them are related
to the three points mentioned above. There were also a couple
failures (and also some tests migrating from XFAIL to PASS
and vice-verse) due to gdb's heuristic for choosing
whether to describe a class as `class' or `struct'.
Caveat: i've only tested this on gnu/i86 systems.
sss
2002-07-04 scott snyder <snyder@fnal.gov>
* flags.h: Add flag_eliminate_unused_dwarf2_types.
* toplev.c: Add flag_eliminate_unused_dwarf2_types.
(f_options): Add -feliminate-unused-dwarf2-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_dwarf2_types is set.
* doc/invoke.texi (Option Summary): Add
-feliminate-unused-dwarf2-types.
(Debugging Options): Likewise.
Index: flags.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/flags.h,v
retrieving revision 1.86
diff -u -p -c -r1.86 flags.h
*** flags.h 25 May 2002 01:56:55 -0000 1.86
--- flags.h 4 Jul 2002 19:01:30 -0000
*************** extern int flag_gcse_sm;
*** 653,658 ****
--- 653,662 ----
extern int flag_eliminate_dwarf2_dups;
+ /* Nonzero means we should do dwarf2 unused type elimination. */
+
+ extern int flag_eliminate_unused_dwarf2_types;
+
/* Non-zero means to collect statistics which might be expensive
and to print them when we are done. */
extern int flag_detailed_statistics;
Index: toplev.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/toplev.c,v
retrieving revision 1.654
diff -u -p -c -r1.654 toplev.c
*** toplev.c 16 Jun 2002 19:31:01 -0000 1.654
--- toplev.c 4 Jul 2002 19:01:39 -0000
*************** tree current_function_func_begin_label;
*** 356,361 ****
--- 356,365 ----
int flag_eliminate_dwarf2_dups = 0;
+ /* Nonzero if doing dwarf2 unused type elimination. */
+
+ int flag_eliminate_unused_dwarf2_types = 0;
+
/* Nonzero if generating code to do profiling. */
int profile_flag = 0;
*************** static const lang_independent_options f_
*** 966,971 ****
--- 970,977 ----
{
{"eliminate-dwarf2-dups", &flag_eliminate_dwarf2_dups, 1,
N_("Perform DWARF2 duplicate elimination") },
+ {"eliminate-unused-dwarf2-types", &flag_eliminate_unused_dwarf2_types, 1,
+ N_("Perform DWARF2 unused type elimination") },
{"float-store", &flag_float_store, 1,
N_("Do not store floats in registers") },
{"volatile", &flag_volatile, 1,
Index: dwarf2out.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/dwarf2out.c,v
retrieving revision 1.379
diff -u -p -c -r1.379 dwarf2out.c
*** dwarf2out.c 17 Jun 2002 17:47:16 -0000 1.379
--- dwarf2out.c 4 Jul 2002 19:01:48 -0000
*************** struct file_table
*** 3333,3338 ****
--- 3333,3339 ----
unsigned allocated;
unsigned in_use;
unsigned last_lookup_index;
+ unsigned *emitted;
};
/* Size (in elements) of increments by which we may expand the filename
*************** static void output_loc_list PARAMS ((dw
*** 3693,3698 ****
--- 3694,3706 ----
static char *gen_internal_sym PARAMS ((const char *));
static void mark_limbo_die_list PARAMS ((void *));
+ 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"
*************** splice_child_die (parent, child)
*** 5078,5083 ****
--- 5086,5092 ----
break;
}
+ child->die_parent = parent;
child->die_sib = parent->die_child;
parent->die_child = child;
}
*************** lookup_filename (file_name)
*** 11813,11818 ****
--- 11822,11830 ----
file_table.allocated = i + FILE_TABLE_INCREMENT;
file_table.table = (char **)
xrealloc (file_table.table, file_table.allocated * sizeof (char *));
+ file_table.emitted = (unsigned *)
+ xrealloc (file_table.emitted,
+ file_table.allocated * sizeof (unsigned));
}
/* Add the new entry to the end of the filename table. */
*************** lookup_filename (file_name)
*** 11820,11841 ****
file_table.in_use = i + 1;
file_table.last_lookup_index = i;
! if (DWARF2_ASM_LINE_DEBUG_INFO)
! {
! fprintf (asm_out_file, "\t.file %u ", i);
! output_quoted_string (asm_out_file, file_name);
! fputc ('\n', asm_out_file);
! }
return i;
}
static void
init_file_table ()
{
/* Allocate the initial hunk of the file_table. */
file_table.table = (char **) xcalloc (FILE_TABLE_INCREMENT, sizeof (char *));
file_table.allocated = FILE_TABLE_INCREMENT;
/* Skip the first entry - file numbers begin at 1. */
file_table.in_use = 1;
--- 11832,11870 ----
file_table.in_use = i + 1;
file_table.last_lookup_index = i;
! file_table.emitted[i] = 0;
return i;
}
+ static int
+ maybe_emit_file (fileno)
+ int fileno;
+ {
+ static int emitcount = 0;
+ if (DWARF2_ASM_LINE_DEBUG_INFO && fileno > 0)
+ {
+ if (!file_table.emitted[fileno])
+ {
+ file_table.emitted[fileno] = ++emitcount;
+ fprintf (asm_out_file, "\t.file %u ", file_table.emitted[fileno]);
+ output_quoted_string (asm_out_file, file_table.table[fileno]);
+ fputc ('\n', asm_out_file);
+ }
+ return file_table.emitted[fileno];
+ }
+ else
+ return fileno;
+ }
+
static void
init_file_table ()
{
/* Allocate the initial hunk of the file_table. */
file_table.table = (char **) xcalloc (FILE_TABLE_INCREMENT, sizeof (char *));
file_table.allocated = FILE_TABLE_INCREMENT;
+ file_table.emitted = (unsigned *) xcalloc (FILE_TABLE_INCREMENT,
+ sizeof (unsigned));
/* Skip the first entry - file numbers begin at 1. */
file_table.in_use = 1;
*************** dwarf2out_source_line (line, filename)
*** 11864,11869 ****
--- 11893,11900 ----
{
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);
*************** dwarf2out_start_source_file (lineno, fil
*** 11945,11950 ****
--- 11976,11982 ----
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");
}
*************** dwarf2out_init (main_input_filename)
*** 12018,12023 ****
--- 12050,12056 ----
be emitting line number data for it first, which avoids having
to add an initial DW_LNS_set_file. */
lookup_filename (main_input_filename);
+ maybe_emit_file (lookup_filename (main_input_filename));
/* Allocate the initial hunk of the decl_die_table. */
decl_die_table
*************** output_indirect_string (pfile, h, v)
*** 12132,12137 ****
--- 12165,12370 ----
return 1;
}
+
+
+ /* 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;
+
+ case DW_TAG_subprogram:
+ /* Mark functions, unless it's only a declaration. */
+ if (get_AT_flag (die, DW_AT_declaration))
+ return;
+ break;
+
+ 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. */
+ unmark_dies (comp_unit_die);
+ for (node = limbo_die_list; node; node = node->next)
+ 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. */
+ unmark_dies (comp_unit_die);
+ for (node = limbo_die_list; node; node = node->next)
+ unmark_dies (node->die);
+ }
+
/* Output stuff that dwarf requires at the end of every file,
and generate the DWARF-2 debugging info. */
*************** dwarf2out_finish (input_filename)
*** 12211,12216 ****
--- 12444,12452 ----
They will go into limbo_die_list. */
if (flag_eliminate_dwarf2_dups)
break_out_includes (comp_unit_die);
+
+ if (flag_eliminate_unused_dwarf2_types)
+ prune_unused_types ();
/* Traverse the DIE's and add add sibling attributes to those DIE's
that have children. */
Index: doc/invoke.texi
===================================================================
RCS file: /cvs/gcc/egcs/gcc/doc/invoke.texi,v
retrieving revision 1.153
diff -u -p -c -r1.153 invoke.texi
*** doc/invoke.texi 30 Jun 2002 18:53:52 -0000 1.153
--- doc/invoke.texi 4 Jul 2002 19:01:58 -0000
*************** in the following sections.
*** 254,259 ****
--- 254,260 ----
-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-dwarf2-types @gol
-save-temps -time}
@item Optimization Options
*************** anything else.
*** 3173,3178 ****
--- 3174,3196 ----
@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-dwarf2-types
+ @opindex feliminate-unused-dwarf2-types
+ Normally, when producing DWARF2 output, GCC will emit debugging
+ information for all types and functions 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 and functions that are nowhere used in the source
+ file being compiled.
+
+ Note that, in some cases, turning on this option may prevent
+ you from being able to call some C++ member functions directly
+ from within the debugger.
@end table
@node Optimize Options