Add support for debugging with ICF (Identical Code Folding).
authorCary Coutant <ccoutant@google.com>
Thu, 8 Oct 2009 21:00:04 +0000 (21:00 +0000)
committerCary Coutant <ccoutant@gcc.gnu.org>
Thu, 8 Oct 2009 21:00:04 +0000 (14:00 -0700)
gcc/ChangeLog:
Add support for debugging with ICF (Identical Code Folding).
* calls.c (debug.h): New #include.
(emit_call_1): Call virtual_call_token debug hook.
* common.opt (-fenable-icf-debug): New option.
* dwarf2out.c (dwarf2_debug_hooks): Add entries for new hooks (two
locations in the source).
(poc_label_num): New variable.
(dcall_entry, vcall_entry): New typedefs.
(dcall_table, vcall_table): New variables.
(struct vcall_insn): New type.
(vcall_insn_table): New variable.
(DEBUG_DCALL_SECTION, DEBUG_VCALL_SECTION): New macros.
(size_of_dcall_table): New function.
(output_dcall_table): New function.
(size_of_vcall_table): New function.
(output_vcall_table): New function.
(dwarf2out_direct_call): New function.
(vcall_insn_table_hash): New function.
(vcall_insn_table_eq): New function.
(dwarf2out_virtual_call_token): New function.
(dwarf2out_virtual_call): New function.
(dwarf2out_init): Allocate new tables and sections.
(prune_unused_types): Mark DIEs referenced from direct call table.
(dwarf2out_finish): Output direct and virtual call tables.
* final.c (final_scan_insn): Call direct_call and virtual_call
debug hooks.
* debug.h (struct gcc_debug_hooks): Add direct_call,
virtual_call_token, virtual_call hooks.
(debug_nothing_uid): New function.
* debug.c (do_nothing_debug_hooks): Add dummy entries for new hooks.
(debug_nothing_uid): New function.
* dbxout.c (dbx_debug_hooks): Add dummy entries for new hooks.
* sdbout.c (sdb_debug_hooks): Likewise.
* vmsdbgout.c (vmsdbg_debug_hooks): Likewise.
* doc/invoke.texi (-fenable-icf-debug): New option.

gcc/testsuite/ChangeLog:
Add support for debugging with ICF (Identical Code Folding).
* g++.dg/debug/dwarf2/icf.C: New test.

From-SVN: r152577

13 files changed:
gcc/ChangeLog
gcc/calls.c
gcc/common.opt
gcc/dbxout.c
gcc/debug.c
gcc/debug.h
gcc/doc/invoke.texi
gcc/dwarf2out.c
gcc/final.c
gcc/sdbout.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/debug/dwarf2/icf.C [new file with mode: 0644]
gcc/vmsdbgout.c

index a7e2290..370fcac 100644 (file)
@@ -1,3 +1,41 @@
+2009-10-08  Cary Coutant  <ccoutant@google.com>
+
+       Add support for debugging with ICF (Identical Code Folding).
+       * calls.c (debug.h): New #include.
+       (emit_call_1): Call virtual_call_token debug hook.
+       * common.opt (-fenable-icf-debug): New option.
+       * dwarf2out.c (dwarf2_debug_hooks): Add entries for new hooks (two
+       locations in the source).
+       (poc_label_num): New variable.
+       (dcall_entry, vcall_entry): New typedefs.
+       (dcall_table, vcall_table): New variables.
+       (struct vcall_insn): New type.
+       (vcall_insn_table): New variable.
+       (DEBUG_DCALL_SECTION, DEBUG_VCALL_SECTION): New macros.
+       (size_of_dcall_table): New function.
+       (output_dcall_table): New function.
+       (size_of_vcall_table): New function.
+       (output_vcall_table): New function.
+       (dwarf2out_direct_call): New function.
+       (vcall_insn_table_hash): New function.
+       (vcall_insn_table_eq): New function.
+       (dwarf2out_virtual_call_token): New function.
+       (dwarf2out_virtual_call): New function.
+       (dwarf2out_init): Allocate new tables and sections.
+       (prune_unused_types): Mark DIEs referenced from direct call table.
+       (dwarf2out_finish): Output direct and virtual call tables.
+       * final.c (final_scan_insn): Call direct_call and virtual_call
+       debug hooks.
+       * debug.h (struct gcc_debug_hooks): Add direct_call,
+       virtual_call_token, virtual_call hooks.
+       (debug_nothing_uid): New function.
+       * debug.c (do_nothing_debug_hooks): Add dummy entries for new hooks.
+       (debug_nothing_uid): New function.
+       * dbxout.c (dbx_debug_hooks): Add dummy entries for new hooks.
+       * sdbout.c (sdb_debug_hooks): Likewise.
+       * vmsdbgout.c (vmsdbg_debug_hooks): Likewise.
+       * doc/invoke.texi (-fenable-icf-debug): New option.
+
 2009-10-08  Alexandre Oliva  <aoliva@redhat.com>
 
        PR debug/41353
index f28fb51..49e576e 100644 (file)
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "sbitmap.h"
 #include "langhooks.h"
 #include "target.h"
+#include "debug.h"
 #include "cgraph.h"
 #include "except.h"
 #include "dbgcnt.h"
@@ -394,6 +395,11 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
 
   SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0);
 
+  /* Record debug information for virtual calls.  */
+  if (flag_enable_icf_debug && fndecl == NULL)
+    (*debug_hooks->virtual_call_token) (CALL_EXPR_FN (fntree),
+                                        INSN_UID (call_insn));
+
   /* Restore this now, so that we do defer pops for this call's args
      if the context of the call as a whole permits.  */
   inhibit_defer_pop = old_inhibit_defer_pop;
index de43201..dd2d815 100644 (file)
@@ -502,6 +502,10 @@ femit-class-debug-always
 Common Report Var(flag_emit_class_debug_always) Init(0)
 Do not suppress C++ class debug information.
 
+fenable-icf-debug
+Common Report Var(flag_enable_icf_debug)
+Generate debug information to support Identical Code Folding (ICF)
+
 fexceptions
 Common Report Var(flag_exceptions) Optimization
 Enable exception handling
index 097b20b..d09087a 100644 (file)
@@ -373,6 +373,9 @@ const struct gcc_debug_hooks dbx_debug_hooks =
   dbxout_handle_pch,                    /* handle_pch */
   debug_nothing_rtx,                    /* var_location */
   debug_nothing_void,                    /* switch_text_section */
+  debug_nothing_tree,                   /* direct_call */
+  debug_nothing_tree_int,               /* virtual_call_token */
+  debug_nothing_uid,                    /* virtual_call */
   debug_nothing_tree_tree,              /* set_name */
   0                                      /* start_end_main_source_file */
 };
@@ -406,6 +409,9 @@ const struct gcc_debug_hooks xcoff_debug_hooks =
   dbxout_handle_pch,                    /* handle_pch */
   debug_nothing_rtx,                    /* var_location */
   debug_nothing_void,                    /* switch_text_section */
+  debug_nothing_tree,                   /* direct_call */
+  debug_nothing_tree_int,               /* virtual_call_token */
+  debug_nothing_uid,                    /* virtual_call */
   debug_nothing_tree_tree,              /* set_name */
   0                                      /* start_end_main_source_file */
 };
index df69fd5..8035c43 100644 (file)
@@ -50,6 +50,9 @@ const struct gcc_debug_hooks do_nothing_debug_hooks =
   debug_nothing_int,                    /* handle_pch */
   debug_nothing_rtx,                    /* var_location */
   debug_nothing_void,                    /* switch_text_section */
+  debug_nothing_tree,                   /* direct_call */
+  debug_nothing_tree_int,               /* virtual_call_token */
+  debug_nothing_uid,                    /* virtual_call */
   debug_nothing_tree_tree,              /* set_name */
   0                                      /* start_end_main_source_file */
 };
@@ -127,3 +130,8 @@ debug_nothing_tree_int (tree decl ATTRIBUTE_UNUSED,
                        int local ATTRIBUTE_UNUSED)
 {
 }
+
+void
+debug_nothing_uid (int uid ATTRIBUTE_UNUSED)
+{
+}
index de525fe..4009cd6 100644 (file)
@@ -126,6 +126,25 @@ struct gcc_debug_hooks
      text sections.  */
   void (* switch_text_section) (void);
 
+  /* Records a direct call to the function DECL, noting the point of call
+     and the debug info for the function.  Called from final_scan_insn
+     when ICF debugging is enabled.  */
+  void (* direct_call) (tree decl);
+
+  /* Records the OBJ_TYPE_REF_TOKEN for a virtual call through ADDR, which
+     for C++ is the vtable slot index, noting the INSN_UID for the call
+     instruction.  Called from calls.c:emit_call_1 when ICF debugging is
+     enabled.  It's necessary to do this during lowering because the
+     call instruction and the OBJ_TYPE_REF become separated after that
+     point.  */
+  void (* virtual_call_token) (tree addr, int insn_uid);
+
+  /* Records a virtual call given INSN_UID, which is the UID of the call
+     instruction.  The UID is then mapped to the vtable slot index noted
+     during the lowering phase.  Called from final_scan_insn when ICF
+     debugging is enabled.  */
+  void (* virtual_call) (int insn_uid);
+
   /* Called from grokdeclarator.  Replaces the anonymous name with the
      type name.  */
   void (* set_name) (tree, tree);
@@ -151,6 +170,7 @@ extern void debug_nothing_tree_int (tree, int);
 extern void debug_nothing_tree_tree_tree_bool (tree, tree, tree, bool);
 extern bool debug_true_const_tree (const_tree);
 extern void debug_nothing_rtx (rtx);
+extern void debug_nothing_uid (int);
 
 /* Hooks for various debug formats.  */
 extern const struct gcc_debug_hooks do_nothing_debug_hooks;
index 6d0d4c4..da7afea 100644 (file)
@@ -307,6 +307,7 @@ Objective-C and Objective-C++ Dialects}.
 -fcompare-debug@r{[}=@var{opts}@r{]}  -fcompare-debug-second @gol
 -feliminate-dwarf2-dups -feliminate-unused-debug-types @gol
 -feliminate-unused-debug-symbols -femit-class-debug-always @gol
+-fenable-icf-debug @gol
 -fmem-report -fpre-ipa-mem-report -fpost-ipa-mem-report -fprofile-arcs @gol
 -frandom-seed=@var{string} -fsched-verbose=@var{n} @gol
 -fsel-sched-verbose -fsel-sched-dump-cfg -fsel-sched-pipelining-verbose @gol
@@ -4609,6 +4610,11 @@ The default is @samp{-femit-struct-debug-detailed=all}.
 
 This option works only with DWARF 2.
 
+@item -fenable-icf-debug
+@opindex fenable-icf-debug
+Generate additional debug information to support identical code folding (ICF).
+This option only works with DWARF version 2 or higher.
+
 @item -fno-merge-debug-strings
 @opindex fmerge-debug-strings
 @opindex fno-merge-debug-strings
index 7e0d466..27f2c45 100644 (file)
@@ -224,6 +224,8 @@ static GTY(()) section *debug_line_section;
 static GTY(()) section *debug_loc_section;
 static GTY(()) section *debug_pubnames_section;
 static GTY(()) section *debug_pubtypes_section;
+static GTY(()) section *debug_dcall_section;
+static GTY(()) section *debug_vcall_section;
 static GTY(()) section *debug_str_section;
 static GTY(()) section *debug_ranges_section;
 static GTY(()) section *debug_frame_section;
@@ -5413,6 +5415,9 @@ static void dwarf2out_imported_module_or_decl_1 (tree, tree, tree,
                                                 dw_die_ref);
 static void dwarf2out_abstract_function (tree);
 static void dwarf2out_var_location (rtx);
+static void dwarf2out_direct_call (tree);
+static void dwarf2out_virtual_call_token (tree, int);
+static void dwarf2out_virtual_call (int);
 static void dwarf2out_begin_function (tree);
 static void dwarf2out_set_name (tree, tree);
 
@@ -5448,6 +5453,9 @@ const struct gcc_debug_hooks dwarf2_debug_hooks =
   debug_nothing_int,           /* handle_pch */
   dwarf2out_var_location,
   dwarf2out_switch_text_section,
+  dwarf2out_direct_call,
+  dwarf2out_virtual_call_token,
+  dwarf2out_virtual_call,
   dwarf2out_set_name,
   1                             /* start_end_main_source_file */
 };
@@ -5828,6 +5836,45 @@ static GTY(()) bool have_location_lists;
 /* Unique label counter.  */
 static GTY(()) unsigned int loclabel_num;
 
+/* Unique label counter for point-of-call tables.  */
+static GTY(()) unsigned int poc_label_num;
+
+/* The direct call table structure.  */
+
+typedef struct GTY(()) dcall_struct {
+  unsigned int poc_label_num;
+  tree poc_decl;
+  dw_die_ref targ_die;
+}
+dcall_entry;
+
+DEF_VEC_O(dcall_entry);
+DEF_VEC_ALLOC_O(dcall_entry, gc);
+
+/* The virtual call table structure.  */
+
+typedef struct GTY(()) vcall_struct {
+  unsigned int poc_label_num;
+  unsigned int vtable_slot;
+}
+vcall_entry;
+
+DEF_VEC_O(vcall_entry);
+DEF_VEC_ALLOC_O(vcall_entry, gc);
+
+/* Pointers to the direct and virtual call tables.  */
+static GTY (()) VEC (dcall_entry, gc) * dcall_table = NULL;
+static GTY (()) VEC (vcall_entry, gc) * vcall_table = NULL;
+
+/* A hash table to map INSN_UIDs to vtable slot indexes.  */
+
+struct GTY (()) vcall_insn {
+  int insn_uid;
+  unsigned int vtable_slot;
+};
+
+static GTY ((param_is (struct vcall_insn))) htab_t vcall_insn_table;
+
 #ifdef DWARF2_DEBUGGING_INFO
 /* Record whether the function being analyzed contains inlined functions.  */
 static int current_function_has_inlines;
@@ -6165,6 +6212,12 @@ static void gen_remaining_tmpl_value_param_die_attribute (void);
 #ifndef DEBUG_PUBTYPES_SECTION
 #define DEBUG_PUBTYPES_SECTION ".debug_pubtypes"
 #endif
+#ifndef DEBUG_DCALL_SECTION
+#define DEBUG_DCALL_SECTION    ".debug_dcall"
+#endif
+#ifndef DEBUG_VCALL_SECTION
+#define DEBUG_VCALL_SECTION    ".debug_vcall"
+#endif
 #ifndef DEBUG_STR_SECTION
 #define DEBUG_STR_SECTION      ".debug_str"
 #endif
@@ -11684,6 +11737,129 @@ output_line_info (void)
   /* Output the marker for the end of the line number info.  */
   ASM_OUTPUT_LABEL (asm_out_file, l2);
 }
+
+/* Return the size of the .debug_dcall table for the compilation unit.  */
+
+static unsigned long
+size_of_dcall_table (void)
+{
+  unsigned long size;
+  unsigned int i;
+  dcall_entry *p;
+  tree last_poc_decl = NULL;
+
+  /* Header:  version + debug info section pointer + pointer size.  */
+  size = 2 + DWARF_OFFSET_SIZE + 1;
+
+  /* Each entry:  code label + DIE offset.  */
+  for (i = 0; VEC_iterate (dcall_entry, dcall_table, i, p); i++)
+    {
+      gcc_assert (p->targ_die != NULL);
+      /* Insert a "from" entry when the point-of-call DIE offset changes.  */
+      if (p->poc_decl != last_poc_decl)
+        {
+          dw_die_ref poc_die = lookup_decl_die (p->poc_decl);
+          gcc_assert (poc_die);
+          last_poc_decl = p->poc_decl;
+          if (poc_die)
+            size += (DWARF_OFFSET_SIZE
+                     + size_of_uleb128 (poc_die->die_offset));
+        }
+      size += DWARF_OFFSET_SIZE + size_of_uleb128 (p->targ_die->die_offset);
+    }
+
+  return size;
+}
+
+/* Output the direct call table used to disambiguate PC values when
+   identical function have been merged.  */
+
+static void
+output_dcall_table (void)
+{
+  unsigned i;
+  unsigned long dcall_length = size_of_dcall_table ();
+  dcall_entry *p;
+  char poc_label[MAX_ARTIFICIAL_LABEL_BYTES];
+  tree last_poc_decl = NULL;
+
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, dcall_length,
+                      "Length of Direct Call Table");
+  dw2_asm_output_data (2, 4, "Version number");
+  dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
+                        debug_info_section,
+                        "Offset of Compilation Unit Info");
+  dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
+
+  for (i = 0; VEC_iterate (dcall_entry, dcall_table, i, p); i++)
+    {
+      /* Insert a "from" entry when the point-of-call DIE offset changes.  */
+      if (p->poc_decl != last_poc_decl)
+        {
+          dw_die_ref poc_die = lookup_decl_die (p->poc_decl);
+          last_poc_decl = p->poc_decl;
+          if (poc_die)
+            {
+              dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, "New caller");
+              dw2_asm_output_data_uleb128 (poc_die->die_offset,
+                                           "Caller DIE offset");
+            }
+        }
+      ASM_GENERATE_INTERNAL_LABEL (poc_label, "LPOC", p->poc_label_num);
+      dw2_asm_output_addr (DWARF_OFFSET_SIZE, poc_label, "Point of call");
+      dw2_asm_output_data_uleb128 (p->targ_die->die_offset,
+                                   "Callee DIE offset");
+    }
+}
+\f
+/* Return the size of the .debug_vcall table for the compilation unit.  */
+
+static unsigned long
+size_of_vcall_table (void)
+{
+  unsigned long size;
+  unsigned int i;
+  vcall_entry *p;
+
+  /* Header:  version + pointer size.  */
+  size = 2 + 1;
+
+  /* Each entry:  code label + vtable slot index.  */
+  for (i = 0; VEC_iterate (vcall_entry, vcall_table, i, p); i++)
+    size += DWARF_OFFSET_SIZE + size_of_uleb128 (p->vtable_slot);
+
+  return size;
+}
+
+/* Output the virtual call table used to disambiguate PC values when
+   identical function have been merged.  */
+
+static void
+output_vcall_table (void)
+{
+  unsigned i;
+  unsigned long vcall_length = size_of_vcall_table ();
+  vcall_entry *p;
+  char poc_label[MAX_ARTIFICIAL_LABEL_BYTES];
+
+  if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
+    dw2_asm_output_data (4, 0xffffffff,
+      "Initial length escape value indicating 64-bit DWARF extension");
+  dw2_asm_output_data (DWARF_OFFSET_SIZE, vcall_length,
+                      "Length of Virtual Call Table");
+  dw2_asm_output_data (2, 4, "Version number");
+  dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Pointer Size (in bytes)");
+
+  for (i = 0; VEC_iterate (vcall_entry, vcall_table, i, p); i++)
+    {
+      ASM_GENERATE_INTERNAL_LABEL (poc_label, "LPOC", p->poc_label_num);
+      dw2_asm_output_addr (DWARF_OFFSET_SIZE, poc_label, "Point of call");
+      dw2_asm_output_data_uleb128 (p->vtable_slot, "Vtable slot");
+    }
+}
 \f
 /* Given a pointer to a tree node for some base type, return a pointer to
    a DIE that describes the given type.
@@ -19709,6 +19885,103 @@ dwarf2out_set_name (tree decl, tree name)
     add_name_attribute (die, dwarf2_name (name, 0));
 }
 
+/* Called by the final INSN scan whenever we see a direct function call.
+   Make an entry into the direct call table, recording the point of call
+   and a reference to the target function's debug entry.  */
+
+static void
+dwarf2out_direct_call (tree targ)
+{
+  dcall_entry e;
+  tree origin = decl_ultimate_origin (targ);
+
+  /* If this is a clone, use the abstract origin as the target.  */
+  if (origin)
+    targ = origin;
+
+  e.poc_label_num = poc_label_num++;
+  e.poc_decl = current_function_decl;
+  e.targ_die = force_decl_die (targ);
+  VEC_safe_push (dcall_entry, gc, dcall_table, &e);
+
+  /* Drop a label at the return point to mark the point of call.  */
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LPOC", e.poc_label_num);
+}
+
+/* Returns a hash value for X (which really is a struct vcall_insn).  */
+
+static hashval_t
+vcall_insn_table_hash (const void *x)
+{
+  return (hashval_t) ((const struct vcall_insn *) x)->insn_uid;
+}
+
+/* Return nonzero if insn_uid of struct vcall_insn *X is the same as
+   insnd_uid of *Y.  */
+
+static int
+vcall_insn_table_eq (const void *x, const void *y)
+{
+  return (((const struct vcall_insn *) x)->insn_uid
+          == ((const struct vcall_insn *) y)->insn_uid);
+}
+
+/* Called when lowering indirect calls to RTL.  We make a note of INSN_UID
+   and the OBJ_TYPE_REF_TOKEN from ADDR.  For C++ virtual calls, the token
+   is the vtable slot index that we will need to put in the virtual call
+   table later.  */
+
+static void
+dwarf2out_virtual_call_token (tree addr, int insn_uid)
+{
+  if (is_cxx() && TREE_CODE (addr) == OBJ_TYPE_REF)
+    {
+      tree token = OBJ_TYPE_REF_TOKEN (addr);
+      if (TREE_CODE (token) == INTEGER_CST)
+        {
+          struct vcall_insn *item = GGC_NEW (struct vcall_insn);
+          struct vcall_insn **slot;
+
+          gcc_assert (item);
+          item->insn_uid = insn_uid;
+          item->vtable_slot = TREE_INT_CST_LOW (token);
+          slot = (struct vcall_insn **)
+              htab_find_slot_with_hash (vcall_insn_table, &item,
+                                        (hashval_t) insn_uid, INSERT);
+          *slot = item;
+        }
+    }
+}
+
+/* Called by the final INSN scan whenever we see a virtual function call.
+   Make an entry into the virtual call table, recording the point of call
+   and the slot index of the vtable entry used to call the virtual member
+   function.  The slot index was associated with the INSN_UID during the
+   lowering to RTL.  */
+
+static void
+dwarf2out_virtual_call (int insn_uid)
+{
+  vcall_entry e;
+  struct vcall_insn item;
+  struct vcall_insn *p;
+
+  item.insn_uid = insn_uid;
+  item.vtable_slot = 0;
+  p = (struct vcall_insn *) htab_find_with_hash (vcall_insn_table,
+                                                 (void *) &item,
+                                                 (hashval_t) insn_uid);
+  if (p == NULL)
+    return;
+
+  e.poc_label_num = poc_label_num++;
+  e.vtable_slot = p->vtable_slot;
+  VEC_safe_push (vcall_entry, gc, vcall_table, &e);
+
+  /* Drop a label at the return point to mark the point of call.  */
+  ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LPOC", e.poc_label_num);
+}
+
 /* Called by the final INSN scan whenever we see a var location.  We
    use it to drop labels in the right places, and throw the location in
    our lookup table.  */
@@ -19997,6 +20270,10 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
   pubname_table = VEC_alloc (pubname_entry, gc, 32);
   pubtype_table = VEC_alloc (pubname_entry, gc, 32);
 
+  /* Allocate the table that maps insn UIDs to vtable slot indexes.  */
+  vcall_insn_table = htab_create_ggc (10, vcall_insn_table_hash,
+                                      vcall_insn_table_eq, NULL);
+
   /* Generate the initial DIE for the .debug section.  Note that the (string)
      value given in the DW_AT_name attribute of the DW_TAG_compile_unit DIE
      will (typically) be a relative pathname and that this pathname should be
@@ -20025,6 +20302,10 @@ dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
                                        SECTION_DEBUG, NULL);
   debug_pubtypes_section = get_section (DEBUG_PUBTYPES_SECTION,
                                        SECTION_DEBUG, NULL);
+  debug_dcall_section = get_section (DEBUG_DCALL_SECTION,
+                                    SECTION_DEBUG, NULL);
+  debug_vcall_section = get_section (DEBUG_VCALL_SECTION,
+                                    SECTION_DEBUG, NULL);
   debug_str_section = get_section (DEBUG_STR_SECTION,
                                   DEBUG_STR_SECTION_FLAGS, NULL);
   debug_ranges_section = get_section (DEBUG_RANGES_SECTION,
@@ -20399,6 +20680,7 @@ prune_unused_types (void)
   limbo_die_node *node;
   comdat_type_node *ctnode;
   pubname_ref pub;
+  dcall_entry *dcall;
 
 #if ENABLE_ASSERT_CHECKING
   /* All the marks should already be clear.  */
@@ -20429,6 +20711,10 @@ prune_unused_types (void)
   for (i = 0; i < arange_table_in_use; i++)
     prune_unused_types_mark (arange_table[i], 1);
 
+  /* Mark nodes referenced from the direct call table.  */
+  for (i = 0; VEC_iterate (dcall_entry, dcall_table, i, dcall); i++)
+    prune_unused_types_mark (dcall->targ_die, 1);
+
   /* Get rid of nodes that aren't marked; and update the string counts.  */
   if (debug_str_hash && debug_str_hash_forced)
     htab_traverse (debug_str_hash, prune_indirect_string, NULL);
@@ -20890,6 +21176,18 @@ dwarf2out_finish (const char *filename)
       output_pubnames (pubtype_table);
     }
 
+  /* Output direct and virtual call tables if necessary.  */
+  if (!VEC_empty (dcall_entry, dcall_table))
+    {
+      switch_to_section (debug_dcall_section);
+      output_dcall_table ();
+    }
+  if (!VEC_empty (vcall_entry, vcall_table))
+    {
+      switch_to_section (debug_vcall_section);
+      output_vcall_table ();
+    }
+
   /* Output the address range information.  We only put functions in the arange
      table, so don't write it out if we don't have any.  */
   if (fde_table_in_use)
@@ -20960,6 +21258,9 @@ const struct gcc_debug_hooks dwarf2_debug_hooks =
   0,           /* handle_pch */
   0,           /* var_location */
   0,           /* switch_text_section */
+  0,           /* direct_call */
+  0,           /* virtual_call_token */
+  0,           /* virtual_call */
   0,           /* set_name */
   0            /* start_end_main_source_file */
 };
index f121da1..b8f5e51 100644 (file)
@@ -2684,6 +2684,26 @@ final_scan_insn (rtx insn, FILE *file, int optimize ATTRIBUTE_UNUSED,
        /* Output assembler code from the template.  */
        output_asm_insn (templ, recog_data.operand);
 
+       /* Record point-of-call information for ICF debugging.  */
+       if (flag_enable_icf_debug && CALL_P (insn))
+         {
+           rtx x = call_from_call_insn (insn);
+           x = XEXP (x, 0);
+           if (x && MEM_P (x))
+             {
+               if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF)
+                 {
+                   tree t;
+                   x = XEXP (x, 0);
+                   t = SYMBOL_REF_DECL (x);
+                   if (t)
+                     (*debug_hooks->direct_call) (t);
+                 }
+               else
+                 (*debug_hooks->virtual_call) (INSN_UID (insn));
+             }
+         }
+
        /* Some target machines need to postscan each insn after
           it is output.  */
        if (targetm.asm_out.final_postscan_insn)
index 1c4ddfa..0553740 100644 (file)
@@ -337,6 +337,9 @@ const struct gcc_debug_hooks sdb_debug_hooks =
   debug_nothing_int,                    /* handle_pch */
   debug_nothing_rtx,                    /* var_location */
   debug_nothing_void,                    /* switch_text_section */
+  debug_nothing_tree,                   /* direct_call */
+  debug_nothing_tree_int,               /* virtual_call_token */
+  debug_nothing_uid,                    /* virtual_call */
   debug_nothing_tree_tree,              /* set_name */
   0                                      /* start_end_main_source_file */
 };
@@ -1725,6 +1728,9 @@ const struct gcc_debug_hooks sdb_debug_hooks =
   0,           /* handle_pch */
   0,           /* var_location */
   0,           /* switch_text_section */
+  0,           /* direct_call */
+  0,           /* virtual_call_token */
+  0,           /* virtual_call */
   0,           /* set_name */
   0            /* start_end_main_source_file */
 };
index 4212a36..65e739f 100644 (file)
@@ -1,3 +1,8 @@
+2009-10-08  Cary Coutant  <ccoutant@google.com>
+
+       Add support for debugging with ICF (Identical Code Folding).
+       * g++.dg/debug/dwarf2/icf.C: New test.
+
 2009-10-08  Adam Nemet  <anemet@caviumnetworks.com>
 
        * gcc.target/mips/truncate-6.c: New test.
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/icf.C b/gcc/testsuite/g++.dg/debug/dwarf2/icf.C
new file mode 100644 (file)
index 0000000..627b834
--- /dev/null
@@ -0,0 +1,50 @@
+// Test support for ICF debugging. 
+// { dg-do compile }
+// { dg-options "-O0 -gdwarf-2 -fenable-icf-debug -dA" }
+
+class A
+{
+ public:
+  A();
+  virtual void work();
+  virtual int p();
+ private:
+  int i;
+};
+
+class B
+{
+ public:
+  B();
+  ~B();
+  void work(const A* a);
+ private:
+  int j;
+};
+
+int
+test1(A* a)
+{
+  a->work();
+}
+
+int
+test2(A* a)
+{
+  if (a->p())
+    {
+      B b;
+      b.work(a);
+    }
+}
+
+// Verify that we get .debug_dcall and .debug_vcall tables generated
+// and that we see entries for both virtual calls. 
+// { dg-final { scan-assembler "\\.section.*\.debug_dcall" } }
+// { dg-final { scan-assembler "\\.section.*\.debug_vcall" } }
+// { dg-final { scan-assembler "New caller" } }
+// { dg-final { scan-assembler "Caller DIE offset" } }
+// { dg-final { scan-assembler "Point of call" } }
+// { dg-final { scan-assembler "Callee DIE offset" } }
+// { dg-final { scan-assembler "0x0.*Vtable slot" } }
+// { dg-final { scan-assembler "0x1.*Vtable slot" } }
index f7cca03..0fab5e7 100644 (file)
@@ -213,6 +213,9 @@ const struct gcc_debug_hooks vmsdbg_debug_hooks
    debug_nothing_int,            /* handle_pch */
    debug_nothing_rtx,            /* var_location */
    debug_nothing_void,            /* switch_text_section */
+   debug_nothing_tree,           /* direct_call */
+   debug_nothing_tree_int,       /* virtual_call_token */
+   debug_nothing_uid,            /* virtual_call */
    debug_nothing_tree_tree,      /* set_name */
    0                              /* start_end_main_source_file */
 };