qemu/gcc-compact-dwarf2.patch

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. */