Patchwork Avoid .debug_loc loclist entries that look like loclist terminators (PR debug/56154)

login
register
mail settings
Submitter Jakub Jelinek
Date Jan. 31, 2013, 9:48 a.m.
Message ID <20130131094854.GG4385@tucnak.redhat.com>
Download mbox | patch
Permalink /patch/217144/
State New
Headers show

Comments

Jakub Jelinek - Jan. 31, 2013, 9:48 a.m.
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  <jakub@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.


	Jakub

Patch

--- 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;
+}