447 lines
14 KiB
Diff
447 lines
14 KiB
Diff
This is Andi's backport for unused types pruning. I (matz) have only added
|
|
the option to disable it again.
|
|
|
|
|
|
From: Andi Kleen <ak@suse.de>
|
|
Subject: More compact dwarf2 information for gcc (with patch)
|
|
Date: Sun, 2 Mar 2003 07:43:14 +0100
|
|
|
|
|
|
I ported Scott Snyder's dwarf2 compression patch that was recently
|
|
posted on the gcc mailing list to the SuSE gcc. What it does is to
|
|
not dump types to the debugging information that are not referenced
|
|
to dwarf2.
|
|
|
|
The main change compared to the patch (which was against 3.4 mainline)
|
|
was to not use varrays which do not seem to exist in that tree.
|
|
I just cloned the file_table dynamic array instead.
|
|
|
|
I enabled it by default. In theory it makes a difference when you try
|
|
to reference a type that is not used in the program from gdb,
|
|
but referencing just types seems to be somewhat pointless
|
|
to me (especially when such a obscure feature eats >100MB of disk space
|
|
in the build), so I just enabled it by default.
|
|
|
|
gcc also seems to have an -feliminate-dwarf2-dups option which may
|
|
further help (but is off by default for some reason). Perhaps
|
|
it can be considered for turning on too? Or is there some problem
|
|
with it I am missing?
|
|
|
|
Just applying the patch makes a nice difference (without
|
|
-feliminate-dwarf2-dups)
|
|
|
|
e.g. this is a simple x86-64 kernel compilation with -g. The tree
|
|
is stripped down with only minimal drivers enabled.
|
|
|
|
before:
|
|
|
|
build tree size 528M
|
|
-rwxr-xr-x 1 ak users 44M 2003-03-02 02:25 vmlinux
|
|
random object file:
|
|
1356440 276 -rw-r--r-- 2 ak users 276312 Mar 2 01:45 ./kernel/sys
|
|
|
|
after:
|
|
ld tree size 411M
|
|
-rwxr-xr-x 1 ak users 30M 2003-03-02 07:26 vmlinux
|
|
|
|
-rw-r--r-- 2 ak users 206096 2003-03-02 07:22 kernel/sys.o
|
|
|
|
|
|
Both the final executable and the object files shrink nicely.
|
|
Also the compilation is faster because gcc has to do much less IO
|
|
(compilation with -g seems to be mostly IO bound)
|
|
|
|
Can we please consider this patch for inclusion into the 3.3 SuSE gcc ?
|
|
|
|
-Andi
|
|
|
|
Index: gcc/flags.h
|
|
===================================================================
|
|
RCS file: /cvs/gcc/gcc/gcc/flags.h,v
|
|
retrieving revision 1.93.2.13
|
|
diff -u -p -r1.93.2.13 flags.h
|
|
--- gcc/flags.h 2 Feb 2004 16:25:40 -0000 1.93.2.13
|
|
+++ gcc/flags.h 21 May 2004 12:58:25 -0000
|
|
@@ -677,6 +677,10 @@ extern int flag_speculative_prefetching;
|
|
|
|
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;
|
|
Index: gcc/toplev.c
|
|
===================================================================
|
|
RCS file: /cvs/gcc/gcc/gcc/toplev.c,v
|
|
retrieving revision 1.688.2.50
|
|
diff -u -p -r1.688.2.50 toplev.c
|
|
--- gcc/toplev.c 21 Apr 2004 15:17:24 -0000 1.688.2.50
|
|
+++ gcc/toplev.c 21 May 2004 12:58:26 -0000
|
|
@@ -383,6 +383,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 = 1;
|
|
+
|
|
/* Nonzero if generating code to do profiling. */
|
|
|
|
int profile_flag = 0;
|
|
@@ -1054,6 +1058,8 @@ static const lang_independent_options f_
|
|
{
|
|
{"eliminate-dwarf2-dups", &flag_eliminate_dwarf2_dups, 1,
|
|
N_("Perform DWARF2 duplicate elimination") },
|
|
+ {"eliminate-unused-debug-types", &flag_eliminate_unused_debug_types, 1,
|
|
+ N_("Eliminate debug info of unused types") },
|
|
{"float-store", &flag_float_store, 1,
|
|
N_("Do not store floats in registers") },
|
|
{"volatile", &flag_volatile, 1,
|
|
Index: gcc/dwarf2out.c
|
|
===================================================================
|
|
RCS file: /cvs/gcc/gcc/gcc/dwarf2out.c,v
|
|
retrieving revision 1.389.2.20
|
|
diff -u -p -r1.389.2.20 dwarf2out.c
|
|
--- gcc/dwarf2out.c 29 Apr 2004 15:20:33 -0000 1.389.2.20
|
|
+++ gcc/dwarf2out.c 21 May 2004 12:58:26 -0000
|
|
@@ -3397,6 +3397,7 @@ struct file_table
|
|
|
|
/* Filenames referenced by this compilation unit. */
|
|
static struct file_table file_table;
|
|
+static struct file_table file_table_emitted;
|
|
|
|
/* Local pointer to the name of the main input file. Initialized in
|
|
dwarf2out_init. */
|
|
@@ -3796,6 +3797,14 @@ static void output_loc_list PARAMS ((dw
|
|
static char *gen_internal_sym PARAMS ((const char *));
|
|
static void mark_limbo_die_list PARAMS ((void *));
|
|
|
|
+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 unsigned long maybe_emit_file PARAMS ((int));
|
|
+
|
|
/* Section names used to hold DWARF debugging information. */
|
|
#ifndef DEBUG_INFO_SECTION
|
|
#define DEBUG_INFO_SECTION ".debug_info"
|
|
@@ -5172,6 +5181,7 @@ splice_child_die (parent, child)
|
|
break;
|
|
}
|
|
|
|
+ child->die_parent = parent;
|
|
child->die_sib = parent->die_child;
|
|
parent->die_child = child;
|
|
}
|
|
@@ -12746,13 +12756,21 @@ lookup_filename (file_name)
|
|
file_table.in_use = i + 1;
|
|
file_table.last_lookup_index = i;
|
|
|
|
- if (DWARF2_ASM_LINE_DEBUG_INFO)
|
|
+ /* Prepare to add a new table entry by making sure there is enough space in
|
|
+ the table to do so. If not, expand the current table. */
|
|
+ if (i >= file_table_emitted.allocated)
|
|
{
|
|
- fprintf (asm_out_file, "\t.file %u ", i);
|
|
- output_quoted_string (asm_out_file, file_name);
|
|
- fputc ('\n', asm_out_file);
|
|
+ file_table_emitted.allocated = i + FILE_TABLE_INCREMENT;
|
|
+ file_table_emitted.table = (char **)
|
|
+ xrealloc (file_table_emitted.table,
|
|
+ file_table_emitted.allocated * sizeof (char *));
|
|
}
|
|
|
|
+ /* Add the new entry to the end of the filename table. */
|
|
+ file_table_emitted.table[i] = NULL;
|
|
+ file_table_emitted.in_use = i + 1;
|
|
+ file_table_emitted.last_lookup_index = i;
|
|
+
|
|
return i;
|
|
}
|
|
|
|
@@ -12763,9 +12781,38 @@ init_file_table ()
|
|
file_table.table = (char **) xcalloc (FILE_TABLE_INCREMENT, sizeof (char *));
|
|
file_table.allocated = FILE_TABLE_INCREMENT;
|
|
|
|
+ file_table_emitted.table = (char **) xcalloc (FILE_TABLE_INCREMENT, sizeof (char *));
|
|
+ file_table_emitted.allocated = FILE_TABLE_INCREMENT;
|
|
+
|
|
/* Skip the first entry - file numbers begin at 1. */
|
|
file_table.in_use = 1;
|
|
file_table.last_lookup_index = 0;
|
|
+
|
|
+ file_table_emitted.in_use = 1;
|
|
+ file_table_emitted.last_lookup_index = 0;
|
|
+}
|
|
+
|
|
+static unsigned long
|
|
+maybe_emit_file (fileno)
|
|
+ int fileno;
|
|
+{
|
|
+ static int emitcount = 0;
|
|
+ if (DWARF2_ASM_LINE_DEBUG_INFO && fileno > 0)
|
|
+ {
|
|
+ if (!file_table_emitted.table[fileno])
|
|
+ {
|
|
+ file_table_emitted.table[fileno] = (char *)++emitcount;
|
|
+ fprintf (asm_out_file, "\t.file %lu ",
|
|
+ (unsigned long)
|
|
+ file_table_emitted.table[fileno]);
|
|
+ output_quoted_string (asm_out_file,
|
|
+ file_table.table[fileno]);
|
|
+ fputc ('\n', asm_out_file);
|
|
+ }
|
|
+ return (unsigned long)file_table_emitted.table[fileno];
|
|
+ }
|
|
+ else
|
|
+ return fileno;
|
|
}
|
|
|
|
/* Called by the final INSN scan whenever we see a var location. We
|
|
@@ -12847,6 +12894,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);
|
|
|
|
@@ -12932,6 +12981,7 @@ dwarf2out_start_source_file (lineno, fil
|
|
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");
|
|
}
|
|
@@ -13122,6 +13172,213 @@ output_indirect_string (pfile, 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. */
|
|
|
|
@@ -13196,6 +13453,9 @@ dwarf2out_finish (input_filename)
|
|
/* We need to reverse all the dies before break_out_includes, or
|
|
we'll see the end of an include file before the beginning. */
|
|
reverse_all_dies (comp_unit_die);
|
|
+
|
|
+ if (flag_eliminate_unused_debug_types)
|
|
+ prune_unused_types ();
|
|
|
|
/* Generate separate CUs for each of the include files we've seen.
|
|
They will go into limbo_die_list. */
|