Hi! As the pr56154-{1,2,3}.c testcases show, sometimes we start a location list with an empty range (either for to PR49382, or because the first insn in the function is inline asm (or many of them) which has zero size and the compiler doesn't know it). If !have_multiple_function_sections, we emit .debug_loc addresses relative to .Ltext0 symbol and DW_AT_low_pc of the CU is .Ltext0, otherwise they are emitted as absolute addresses, there is DW_AT_ranges on the CU and DW_AT_low_pc is 0. If an empty range, either known by the compiler or unknown (because of empty inline asm, or say inline asm which is .section someothersection; emits lots of stuff there; .previous) is emitted for the beginning of the first function emitted for the CU, then we can end up with e.g. .LVL0 - .Ltext0 .LVL0 - .Ltext0 range where .LVL0 == .Ltext0, or say .LVL0 - .Ltext0 .LVL23 - .Ltext0 range where .LVL0 == .LVL1 == ... == .LVL23 == .Ltext0. Unfortunately that is 0 0 and 0, 0 is location list terminator entry, rather than valid empty range. In that case, neither the empty range is usable by the debug info consumer, nor if there are any other ranges in the location list after it. E.g. readelf -wo then complains there is garbage hole in the .debug_loc section.
Fixed by checking for this on the first function, and if any empty range is detected at the beginning of the function in some location list, we force have_multiple_function_sections (and thus, DW_AT_ranges for the CU, DW_AT_low_pc 0 and absolute .debug_loc addresses). This patch doesn't seem to affect .debug_{info,loc,ranges} sizes on stage3 cc1plus binaries on either x86_64-linux and i686-linux, so I guess it doesn't hit that often, and even when it hits, it results in tiny decrease of size of .debug_info, tiny growth in .debug_ranges, no growth in .debug_loc, just possibly many relocations against .debug_loc (but that affects just *.o/*.a files). The alternative discussed was to force in such loclists a base selection entry, but that would be more work on the gcc side and .debug_loc size could grow more. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2013-01-31 Jakub Jelinek <ja...@redhat.com> PR debug/56154 * dwarf2out.c (dwarf2_debug_hooks): Set end_function hook to dwarf2out_end_function. (in_first_function_p, maybe_at_text_label_p, first_loclabel_num_not_at_text_label): New variables. (dwarf2out_var_location): In the first function find out lowest loclabel_num N where .LVLN is known not to be equal to .Ltext0. (find_empty_loc_ranges_at_text_label, dwarf2out_end_function): New functions. * gcc.dg/guality/pr56154-1.c: New test. * gcc.dg/guality/pr56154-2.c: New test. * gcc.dg/guality/pr56154-3.c: New test. * gcc.dg/guality/pr56154-4.c: New test. * gcc.dg/guality/pr56154-aux.c: New file. --- gcc/dwarf2out.c.jj 2013-01-11 09:02:48.000000000 +0100 +++ gcc/dwarf2out.c 2013-01-30 16:18:58.362552894 +0100 @@ -2351,6 +2351,7 @@ static void dwarf2out_imported_module_or static void dwarf2out_abstract_function (tree); static void dwarf2out_var_location (rtx); static void dwarf2out_begin_function (tree); +static void dwarf2out_end_function (unsigned int); static void dwarf2out_set_name (tree, tree); /* The debug hooks structure. */ @@ -2378,7 +2379,7 @@ const struct gcc_debug_hooks dwarf2_debu #endif dwarf2out_end_epilogue, dwarf2out_begin_function, - debug_nothing_int, /* end_function */ + dwarf2out_end_function, /* end_function */ dwarf2out_function_decl, /* function_decl */ dwarf2out_global_decl, dwarf2out_type_decl, /* type_decl */ @@ -20627,6 +20628,14 @@ dwarf2out_set_name (tree decl, tree name add_name_attribute (die, dname); } +/* True if before or during processing of the first function being emitted. */ +static bool in_first_function_p = true; +/* True if loc_note during dwarf2out_var_location call might still be + before first real instruction at address equal to .Ltext0. */ +static bool maybe_at_text_label_p = true; +/* One above highest N where .LVLN label might be equal to .Ltext0 label. */ +static unsigned int first_loclabel_num_not_at_text_label; + /* 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. */ @@ -20734,6 +20743,45 @@ dwarf2out_var_location (rtx loc_note) ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LVL", loclabel_num); loclabel_num++; last_label = ggc_strdup (loclabel); + /* See if loclabel might be equal to .Ltext0. If yes, + bump first_loclabel_num_not_at_text_label. */ + if (!have_multiple_function_sections + && in_first_function_p + && maybe_at_text_label_p) + { + static rtx last_start; + rtx insn; + for (insn = loc_note; insn; insn = previous_insn (insn)) + if (insn == last_start) + break; + else if (!NONDEBUG_INSN_P (insn)) + continue; + else + { + rtx body = PATTERN (insn); + if (GET_CODE (body) == USE || GET_CODE (body) == CLOBBER) + continue; + /* Inline asm could occupy zero bytes. */ + else if (GET_CODE (body) == ASM_INPUT + || asm_noperands (body) >= 0) + continue; +#ifdef HAVE_attr_length + else if (get_attr_min_length (insn) == 0) + continue; +#endif + else + { + /* Assume insn has non-zero length. */ + maybe_at_text_label_p = false; + break; + } + } + if (maybe_at_text_label_p) + { + last_start = loc_note; + first_loclabel_num_not_at_text_label = loclabel_num; + } + } } if (!var_loc_p) @@ -20903,6 +20951,59 @@ dwarf2out_begin_function (tree fun) set_cur_line_info_table (sec); } +/* Helper function of dwarf2out_end_function, called only after emitting + the very first function into assembly. Check if some .debug_loc range + might end with a .LVL* label that could be equal to .Ltext0. + In that case we must force using absolute addresses in .debug_loc ranges, + because this range could be .LVLN-.Ltext0 .. .LVLM-.Ltext0 for + .LVLN == .LVLM == .Ltext0, thus 0 .. 0, which is a .debug_loc + list terminator. + Set have_multiple_function_sections to true in that case and + terminate htab traversal. */ + +static int +find_empty_loc_ranges_at_text_label (void **slot, void *) +{ + var_loc_list *entry; + struct var_loc_node *node; + + entry = (var_loc_list *) *slot; + node = entry->first; + if (node && node->next && node->next->label) + { + unsigned int i; + const char *label = node->next->label; + char loclabel[MAX_ARTIFICIAL_LABEL_BYTES]; + + for (i = 0; i < first_loclabel_num_not_at_text_label; i++) + { + ASM_GENERATE_INTERNAL_LABEL (loclabel, "LVL", i); + if (strcmp (label, loclabel) == 0) + { + have_multiple_function_sections = true; + return 0; + } + } + } + return 1; +} + +/* Hook called after emitting a function into assembly. + This does something only for the very first function emitted. */ + +static void +dwarf2out_end_function (unsigned int) +{ + if (in_first_function_p + && !have_multiple_function_sections + && first_loclabel_num_not_at_text_label + && decl_loc_table) + htab_traverse (decl_loc_table, find_empty_loc_ranges_at_text_label, + NULL); + in_first_function_p = false; + maybe_at_text_label_p = false; +} + /* Add OPCODE+VAL as an entry at the end of the opcode array in TABLE. */ static void --- gcc/testsuite/gcc.dg/guality/pr56154-1.c.jj 2013-01-30 17:14:20.850820429 +0100 +++ gcc/testsuite/gcc.dg/guality/pr56154-1.c 2013-01-30 17:47:25.242537776 +0100 @@ -0,0 +1,29 @@ +/* PR debug/56154 */ +/* { dg-do run } */ +/* { dg-options "-g" } */ +/* { dg-additional-sources "pr56154-aux.c" } */ + +#include "../nop.h" + +union U { int a, b; }; +volatile int z; + +__attribute__((noinline, noclone)) int +foo (int fd, union U x) +{ + int result = x.a != 0; + if (fd != 0) + result = x.a == 0; + asm (NOP : : : "memory"); /* { dg-final { gdb-test pr56154-1.c:17 "x.a" "4" } } */ + z = x.a; + x.a = 6; + asm (NOP : : : "memory"); /* { dg-final { gdb-test pr56154-1.c:20 "x.a" "6" } } */ + return result; +} + +void +test_main (void) +{ + union U u = { .a = 4 }; + foo (0, u); +} --- gcc/testsuite/gcc.dg/guality/pr56154-2.c.jj 2013-01-30 17:58:28.229799607 +0100 +++ gcc/testsuite/gcc.dg/guality/pr56154-2.c 2013-01-30 18:06:38.306982101 +0100 @@ -0,0 +1,39 @@ +/* PR debug/56154 */ +/* { dg-do run } */ +/* { dg-options "-g" } */ +/* { dg-additional-sources "pr56154-aux.c" } */ + +#include "../nop.h" + +extern void abort (void); + +__attribute__((noinline, noclone)) int +foo (int x) +{ + asm (""); + x++; + asm (""); + x++; + asm (""); + x++; + asm (""); + x++; + asm (""); + x++; + asm (""); + x++; + asm (""); + x++; + asm (""); + x++; + asm (NOP : : : "memory"); + asm (NOP : : : "memory"); /* { dg-final { gdb-test pr56154-2.c:30 "x" "28" } } */ + return x; +} + +void +test_main (void) +{ + if (foo (20) != 28) + abort (); +} --- gcc/testsuite/gcc.dg/guality/pr56154-3.c.jj 2013-01-30 18:04:47.531604188 +0100 +++ gcc/testsuite/gcc.dg/guality/pr56154-3.c 2013-01-30 18:06:25.031055514 +0100 @@ -0,0 +1,31 @@ +/* PR debug/56154 */ +/* { dg-do run } */ +/* { dg-options "-g" } */ +/* { dg-additional-sources "pr56154-aux.c" } */ + +#include "../nop.h" + +extern void abort (void); + +__attribute__((noinline, noclone)) int +foo (int x) +{ + x++; + x++; + x++; + x++; + x++; + x++; + x++; + x++; + asm (NOP : : : "memory"); + asm (NOP : : : "memory"); /* { dg-final { gdb-test pr56154-3.c:22 "x" "28" } } */ + return x; +} + +void +test_main (void) +{ + if (foo (20) != 28) + abort (); +} --- gcc/testsuite/gcc.dg/guality/pr56154-4.c.jj 2013-01-30 18:05:45.959280837 +0100 +++ gcc/testsuite/gcc.dg/guality/pr56154-4.c 2013-01-30 18:07:50.457602221 +0100 @@ -0,0 +1,34 @@ +/* PR debug/56154 */ +/* { dg-do run } */ +/* { dg-options "-g" } */ +/* { dg-additional-sources "pr56154-aux.c" } */ + +#include "../nop.h" + +extern void abort (void); + +volatile int z; + +__attribute__((noinline, noclone)) int +foo (int x) +{ + z = 6; + x++; + x++; + x++; + x++; + x++; + x++; + x++; + x++; + asm (NOP : : : "memory"); + asm (NOP : : : "memory"); /* { dg-final { gdb-test pr56154-4.c:25 "x" "28" } } */ + return x; +} + +void +test_main (void) +{ + if (foo (20) != 28) + abort (); +} --- gcc/testsuite/gcc.dg/guality/pr56154-aux.c.jj 2013-01-30 17:47:08.467632262 +0100 +++ gcc/testsuite/gcc.dg/guality/pr56154-aux.c 2013-01-30 17:14:28.000000000 +0100 @@ -0,0 +1,11 @@ +/* PR debug/56154 */ +/* { dg-do compile } */ + +extern void test_main (void); + +int +main () +{ + test_main (); + return 0; +} Jakub