Add support for debugging with ICF (Identical Code Folding).
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
This commit is contained in:
parent
968e57283e
commit
77831620cf
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
20
gcc/debug.h
20
gcc/debug.h
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
301
gcc/dwarf2out.c
301
gcc/dwarf2out.c
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
/* 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");
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
};
|
||||
|
|
20
gcc/final.c
20
gcc/final.c
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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" } }
|
|
@ -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 */
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue