[patch] Add discriminators to DWARF line table

Cary Coutant ccoutant@google.com
Wed Jun 3 01:03:00 GMT 2009


>>> +  from = expand_location (locus1);
>>> +  to = expand_location (locus2);
>>> +  return (from.file == to.file && from.line == to.line);
>>
>> Are file name strings canonicalized by the location_t code?  I
>> assume they are, but I'm not sure.  If not, you may need a better
>> matching code here.
>
> I checked this experimentally, but I'll delve deeper into the code to make sure.

It looks like the string pointers may not be the same if there's an
intervening #line directive. I've changed this to do a quick pointer
comparison, then fall back to a strcmp:

  if (from.line != to.line)
    return false;
  if (from.file == to.file)
    return true;
  return strcmp (from.file, to.file);

New patch is attached.  I've done an overdue svn update on my working
copy, so I'll bootstrap and test again. Assuming that works, is this
OK?

-cary


	* basic-block.h (struct basic_block_def): Add discriminator field.
	* dbxout.c (dbxout_source_line): Add new parameter.  Change all
	callers.
	* debug.c (do_nothing_debug_hooks): Add additional entry.
	(debug_nothing_int_charstar_int): New function.
	* debug.h (struct gcc_debug_hooks): Add parameter to source_line
	hook.
	(debug_nothing_int_charstar_int): New declaration.
	* dwarf2out.c (dwarf2out_source_line): Add new parameter.  Write
	discriminator value in .loc directive.
	* final.c (last_discriminator): New variable.
	(discriminator): New variable.
	(final_start_function): Initialize above variables, pass current
	discriminator to debug hook.
	(notice_source_line): Check for discriminator change.
	* gimple-pretty-print.c (dump_bb_header): Print discriminator value.
	* sdbout.c (sdbout_source_line): New parameter.
	* tree-cfg.c (struct locus_discrim_map): New structure type.
	(discriminator_per_locus): New hash table.
	(build_gimple_cfg): Allocate and free discriminator hash table.
	(make_edges): Call assign_discriminator.
	(locus_map_hash): New function.
	(locus_map_eq): New function.
	(next_discriminator_for_locus): New function.
	(same_line_p): New function.
	(assign_discriminator): New function.
	(make_cond_expr_edges): Call assign_discriminator.
	(make_gimple_switch_edges): Likewise.
	(first_non_label_stmt): New function.
	* vmsdbgout.c (vmsdbgout_source_line): Add new parameter.  Change
	all callers.
	* xcoffout.c (xcoffout_source_line): Add new parameter.

	* configure.ac (gcc_cv_as_discriminator): New configury check for
	gas support for discriminator.
	* configure: Regenerate.
	* config.in: Regenerate.
-------------- next part --------------
Index: basic-block.h
===================================================================
--- basic-block.h	(revision 148110)
+++ basic-block.h	(working copy)
@@ -249,6 +249,9 @@ struct GTY((chain_next ("%h.next_bb"), c
   /* Expected frequency.  Normalized to be in range 0 to BB_FREQ_MAX.  */
   int frequency;
 
+  /* The discriminator for this block.  */
+  int discriminator;
+
   /* Various flags.  See BB_* below.  */
   int flags;
 };
Index: configure.ac
===================================================================
--- configure.ac	(revision 148110)
+++ configure.ac	(working copy)
@@ -2408,6 +2408,15 @@ AC_DEFINE_UNQUOTED(HAVE_COMDAT_GROUP,
   [`if test $gcc_cv_as_comdat_group = yes || test $gcc_cv_as_comdat_group_percent = yes; then echo 1; else echo 0; fi`],
 [Define 0/1 if your assembler and linker support COMDAT groups.])
 
+gcc_GAS_CHECK_FEATURE([line table discriminator support],
+ gcc_cv_as_discriminator,
+ [2,19,51],,
+[	.text
+	.file 1 "conf.c"
+	.loc 1 1 0 discriminator 1],,
+[AC_DEFINE(HAVE_GAS_DISCRIMINATOR, 1,
+  [Define if your assembler supports the .loc discriminator sub-directive.])])
+
 # Thread-local storage - the check is heavily parameterized.
 conftest_s=
 tls_first_major=
Index: dbxout.c
===================================================================
--- dbxout.c	(revision 148110)
+++ dbxout.c	(working copy)
@@ -333,7 +333,7 @@ static void dbxout_handle_pch (unsigned)
 /* The debug hooks structure.  */
 #if defined (DBX_DEBUGGING_INFO)
 
-static void dbxout_source_line (unsigned int, const char *);
+static void dbxout_source_line (unsigned int, const char *, int);
 static void dbxout_begin_prologue (unsigned int, const char *);
 static void dbxout_source_file (const char *);
 static void dbxout_function_end (tree);
@@ -1265,7 +1265,7 @@ dbxout_begin_prologue (unsigned int line
   /* pre-increment the scope counter */
   scope_labelno++;
 
-  dbxout_source_line (lineno, filename);
+  dbxout_source_line (lineno, filename, 0);
   /* Output function begin block at function scope, referenced 
      by dbxout_block, dbxout_source_line and dbxout_function_end.  */
   emit_pending_bincls_if_required ();
@@ -1276,7 +1276,8 @@ dbxout_begin_prologue (unsigned int line
    number LINENO.  */
 
 static void
-dbxout_source_line (unsigned int lineno, const char *filename)
+dbxout_source_line (unsigned int lineno, const char *filename,
+                    int discriminator ATTRIBUTE_UNUSED)
 {
   dbxout_source_file (filename);
 
Index: debug.c
===================================================================
--- debug.c	(revision 148110)
+++ debug.c	(working copy)
@@ -34,7 +34,7 @@ const struct gcc_debug_hooks do_nothing_
   debug_nothing_int_int,	         /* begin_block */
   debug_nothing_int_int,	         /* end_block */
   debug_true_const_tree,	         /* ignore_block */
-  debug_nothing_int_charstar,	         /* source_line */
+  debug_nothing_int_charstar_int,	 /* source_line */
   debug_nothing_int_charstar,	         /* begin_prologue */
   debug_nothing_int_charstar,	         /* end_prologue */
   debug_nothing_int_charstar,	         /* end_epilogue */
@@ -104,6 +104,13 @@ debug_nothing_int_charstar (unsigned int
 }
 
 void
+debug_nothing_int_charstar_int (unsigned int line ATTRIBUTE_UNUSED,
+			        const char *text ATTRIBUTE_UNUSED,
+			        int discriminator ATTRIBUTE_UNUSED)
+{
+}
+
+void
 debug_nothing_int (unsigned int line ATTRIBUTE_UNUSED)
 {
 }
Index: debug.h
===================================================================
--- debug.h	(revision 148110)
+++ debug.h	(working copy)
@@ -59,8 +59,9 @@ struct gcc_debug_hooks
      though the BLOCK information is messed up.  Defaults to true.  */
   bool (* ignore_block) (const_tree);
 
-  /* Record a source file location at (FILE, LINE).  */
-  void (* source_line) (unsigned int line, const char *file);
+  /* Record a source file location at (FILE, LINE, DISCRIMINATOR).  */
+  void (* source_line) (unsigned int line, const char *file,
+                        int discriminator);
 
   /* Called at start of prologue code.  LINE is the first line in the
      function.  This has been given the same prototype as source_line,
@@ -141,6 +142,7 @@ extern const struct gcc_debug_hooks *deb
 extern void debug_nothing_void (void);
 extern void debug_nothing_charstar (const char *);
 extern void debug_nothing_int_charstar (unsigned int, const char *);
+extern void debug_nothing_int_charstar_int (unsigned int, const char *, int);
 extern void debug_nothing_int (unsigned int);
 extern void debug_nothing_int_int (unsigned int, unsigned int);
 extern void debug_nothing_tree (tree);
Index: dwarf2out.c
===================================================================
--- dwarf2out.c	(revision 148110)
+++ dwarf2out.c	(working copy)
@@ -91,7 +91,7 @@ along with GCC; see the file COPYING3.  
 #include "input.h"
 
 #ifdef DWARF2_DEBUGGING_INFO
-static void dwarf2out_source_line (unsigned int, const char *);
+static void dwarf2out_source_line (unsigned int, const char *, int);
 #endif
 
 #ifndef DWARF2_FRAME_INFO
@@ -3608,7 +3608,7 @@ dwarf2out_begin_prologue (unsigned int l
      prologue case, not the eh frame case.  */
 #ifdef DWARF2_DEBUGGING_INFO
   if (file)
-    dwarf2out_source_line (line, file);
+    dwarf2out_source_line (line, file, 0);
 #endif
 
   if (dwarf2out_do_cfi_asm ())
@@ -16207,7 +16207,8 @@ dwarf2out_begin_function (tree fun)
    'line_info_table' for later output of the .debug_line section.  */
 
 static void
-dwarf2out_source_line (unsigned int line, const char *filename)
+dwarf2out_source_line (unsigned int line, const char *filename,
+                       int discriminator)
 {
   if (debug_info_level >= DINFO_LEVEL_NORMAL
       && line != 0)
@@ -16224,7 +16225,12 @@ dwarf2out_source_line (unsigned int line
       if (DWARF2_ASM_LINE_DEBUG_INFO)
 	{
 	  /* Emit the .loc directive understood by GNU as.  */
-	  fprintf (asm_out_file, "\t.loc %d %d 0\n", file_num, line);
+	  fprintf (asm_out_file, "\t.loc %d %d 0", file_num, line);
+#ifdef HAVE_GAS_DISCRIMINATOR
+	  if (discriminator != 0)
+	    fprintf (asm_out_file, " discriminator %d", discriminator);
+#endif /* HAVE_GAS_DISCRIMINATOR */
+	  fputc ('\n', asm_out_file);
 
 	  /* Indicate that line number info exists.  */
 	  line_info_table_in_use++;
Index: final.c
===================================================================
--- final.c	(revision 148110)
+++ final.c	(working copy)
@@ -130,6 +130,12 @@ rtx current_output_insn;
 /* Line number of last NOTE.  */
 static int last_linenum;
 
+/* Last discriminator written to assembly.  */
+static int last_discriminator;
+
+/* Discriminator of current block.  */
+static int discriminator;
+
 /* Highest line number in current block.  */
 static int high_block_linenum;
 
@@ -1496,6 +1502,7 @@ final_start_function (rtx first ATTRIBUT
 
   last_filename = locator_file (prologue_locator);
   last_linenum = locator_line (prologue_locator);
+  last_discriminator = discriminator = 0;
 
   high_block_linenum = high_function_linenum = last_linenum;
 
@@ -1852,6 +1859,8 @@ final_scan_insn (rtx insn, FILE *file, i
 	  else
 	    *seen |= SEEN_BB;
 
+          discriminator = NOTE_BASIC_BLOCK (insn)->discriminator;
+
 	  break;
 
 	case NOTE_INSN_EH_REGION_BEG:
@@ -2183,7 +2192,9 @@ final_scan_insn (rtx insn, FILE *file, i
 	   note in a row.  */
 	if (notice_source_line (insn))
 	  {
-	    (*debug_hooks->source_line) (last_linenum, last_filename);
+	    (*debug_hooks->source_line) (last_linenum,
+	                                 last_filename,
+	                                 last_discriminator);
 	  }
 
 	if (GET_CODE (body) == ASM_INPUT)
@@ -2709,11 +2720,13 @@ notice_source_line (rtx insn)
   if (filename
       && (force_source_line
 	  || filename != last_filename
-	  || last_linenum != linenum))
+	  || last_linenum != linenum
+	  || last_discriminator != discriminator))
     {
       force_source_line = false;
       last_filename = filename;
       last_linenum = linenum;
+      last_discriminator = discriminator;
       high_block_linenum = MAX (last_linenum, high_block_linenum);
       high_function_linenum = MAX (last_linenum, high_function_linenum);
       return true;
Index: gimple-pretty-print.c
===================================================================
--- gimple-pretty-print.c	(revision 148110)
+++ gimple-pretty-print.c	(working copy)
@@ -1558,6 +1558,12 @@ dump_bb_header (pretty_printer *buffer, 
 		pp_decimal_int (buffer, get_lineno (gsi_stmt (gsi)));
 		break;
 	      }
+
+          if (bb->discriminator)
+            {
+              pp_string (buffer, ", discriminator ");
+	      pp_decimal_int (buffer, bb->discriminator);
+            }
 	}
       newline_and_indent (buffer, indent);
 
Index: sdbout.c
===================================================================
--- sdbout.c	(revision 148110)
+++ sdbout.c	(working copy)
@@ -117,7 +117,7 @@ static void sdbout_start_source_file	(un
 static void sdbout_end_source_file	(unsigned int);
 static void sdbout_begin_block		(unsigned int, unsigned int);
 static void sdbout_end_block		(unsigned int, unsigned int);
-static void sdbout_source_line		(unsigned int, const char *);
+static void sdbout_source_line		(unsigned int, const char *, int);
 static void sdbout_end_epilogue		(unsigned int, const char *);
 static void sdbout_global_decl		(tree);
 #ifndef MIPS_DEBUGGING_INFO
@@ -1541,7 +1541,8 @@ sdbout_end_block (unsigned int line, uns
    number LINE.  */
 
 static void
-sdbout_source_line (unsigned int line, const char *filename ATTRIBUTE_UNUSED)
+sdbout_source_line (unsigned int line, const char *filename ATTRIBUTE_UNUSED,
+                    int discriminator ATTRIBUTE_UNUSED)
 {
   /* COFF relative line numbers must be positive.  */
   if ((int) line > sdb_begin_function_line)
Index: tree-cfg.c
===================================================================
--- tree-cfg.c	(revision 148110)
+++ tree-cfg.c	(working copy)
@@ -82,6 +82,14 @@ static struct cfg_stats_d cfg_stats;
 /* Nonzero if we found a computed goto while building basic blocks.  */
 static bool found_computed_goto;
 
+/* Hash table to store last discriminator assigned for each locus.  */
+struct locus_discrim_map
+{
+  location_t locus;
+  int discriminator;
+};
+static htab_t discriminator_per_locus;
+
 /* Basic blocks and flowgraphs.  */
 static void make_blocks (gimple_seq);
 static void factor_computed_gotos (void);
@@ -91,6 +99,9 @@ static void make_edges (void);
 static void make_cond_expr_edges (basic_block);
 static void make_gimple_switch_edges (basic_block);
 static void make_goto_expr_edges (basic_block);
+static unsigned int locus_map_hash (const void *);
+static int locus_map_eq (const void *, const void *);
+static void assign_discriminator (location_t, basic_block);
 static edge gimple_redirect_edge_and_branch (edge, basic_block);
 static edge gimple_try_redirect_by_replacing_jump (edge, basic_block);
 static unsigned int split_critical_edges (void);
@@ -100,6 +111,7 @@ static inline bool stmt_starts_bb_p (gim
 static int gimple_verify_flow_info (void);
 static void gimple_make_forwarder_block (edge);
 static void gimple_cfg2vcg (FILE *);
+static gimple first_non_label_stmt (basic_block);
 
 /* Flowgraph optimization and cleanup.  */
 static void gimple_merge_blocks (basic_block, basic_block);
@@ -193,8 +205,11 @@ build_gimple_cfg (gimple_seq seq)
   group_case_labels ();
 
   /* Create the edges of the flowgraph.  */
+  discriminator_per_locus = htab_create (13, locus_map_hash, locus_map_eq,
+                                         free);
   make_edges ();
   cleanup_dead_labels ();
+  htab_delete (discriminator_per_locus);
 
   /* Debugging dumps.  */
 
@@ -650,7 +665,11 @@ make_edges (void)
 	fallthru = true;
 
       if (fallthru)
-	make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+        {
+	  make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+	  if (last)
+            assign_discriminator (gimple_location (last), bb->next_bb);
+	}
     }
 
   if (root_omp_region)
@@ -660,6 +679,89 @@ make_edges (void)
   fold_cond_expr_cond ();
 }
 
+/* Trivial hash function for a location_t.  ITEM is a pointer to
+   a hash table entry that maps a location_t to a discriminator.  */
+
+static unsigned int
+locus_map_hash (const void *item)
+{
+  return ((const struct locus_discrim_map *) item)->locus;
+}
+
+/* Equality function for the locus-to-discriminator map.  VA and VB
+   point to the two hash table entries to compare.  */
+
+static int
+locus_map_eq (const void *va, const void *vb)
+{
+  const struct locus_discrim_map *a = (const struct locus_discrim_map *) va;
+  const struct locus_discrim_map *b = (const struct locus_discrim_map *) vb;
+  return a->locus == b->locus;
+}
+
+/* Find the next available discriminator value for LOCUS.  The
+   discriminator distinguishes among several basic blocks that
+   share a common locus, allowing for more accurate sample-based
+   profiling.  */
+
+static int
+next_discriminator_for_locus (location_t locus)
+{
+  struct locus_discrim_map item;
+  struct locus_discrim_map **slot;
+
+  item.locus = locus;
+  item.discriminator = 0;
+  slot = (struct locus_discrim_map **)
+      htab_find_slot_with_hash (discriminator_per_locus, (void *) &item,
+                                (hashval_t) locus, INSERT);
+  gcc_assert (slot);
+  if (*slot == HTAB_EMPTY_ENTRY)
+    {
+      *slot = XNEW (struct locus_discrim_map);
+      gcc_assert (*slot);
+      (*slot)->locus = locus;
+      (*slot)->discriminator = 0;
+    }
+  (*slot)->discriminator++;
+  return (*slot)->discriminator;
+}
+
+/* Return TRUE if LOCUS1 and LOCUS2 refer to the same source line.  */
+
+static bool
+same_line_p (location_t locus1, location_t locus2)
+{
+  expanded_location from, to;
+
+  if (locus1 == locus2)
+    return true;
+
+  from = expand_location (locus1);
+  to = expand_location (locus2);
+
+  if (from.line != to.line)
+    return false;
+  if (from.file == to.file)
+    return true;
+  return strcmp (from.file, to.file);
+}
+
+/* Assign a unique discriminator value to block BB if it begins at the same
+   LOCUS as its predecessor block.  */
+
+static void
+assign_discriminator (location_t locus, basic_block bb)
+{
+  gimple to_stmt;
+
+  if (locus == 0 || bb->discriminator != 0)
+    return;
+
+  to_stmt = first_non_label_stmt (bb);
+  if (to_stmt && same_line_p (locus, gimple_location (to_stmt)))
+    bb->discriminator = next_discriminator_for_locus (locus);
+}
 
 /* Create the edges for a GIMPLE_COND starting at block BB.  */
 
@@ -671,10 +773,13 @@ make_cond_expr_edges (basic_block bb)
   basic_block then_bb, else_bb;
   tree then_label, else_label;
   edge e;
+  location_t entry_locus;
 
   gcc_assert (entry);
   gcc_assert (gimple_code (entry) == GIMPLE_COND);
 
+  entry_locus = gimple_location (entry);
+
   /* Entry basic blocks for each component.  */
   then_label = gimple_cond_true_label (entry);
   else_label = gimple_cond_false_label (entry);
@@ -684,12 +789,14 @@ make_cond_expr_edges (basic_block bb)
   else_stmt = first_stmt (else_bb);
 
   e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
+  assign_discriminator (entry_locus, then_bb);
   e->goto_locus = gimple_location (then_stmt);
   if (e->goto_locus)
     e->goto_block = gimple_block (then_stmt);
   e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
   if (e)
     {
+      assign_discriminator (entry_locus, else_bb);
       e->goto_locus = gimple_location (else_stmt);
       if (e->goto_locus)
 	e->goto_block = gimple_block (else_stmt);
@@ -799,8 +906,11 @@ static void
 make_gimple_switch_edges (basic_block bb)
 {
   gimple entry = last_stmt (bb);
+  location_t entry_locus;
   size_t i, n;
 
+  entry_locus = gimple_location (entry);
+
   n = gimple_switch_num_labels (entry);
 
   for (i = 0; i < n; ++i)
@@ -808,6 +918,7 @@ make_gimple_switch_edges (basic_block bb
       tree lab = CASE_LABEL (gimple_switch_label (entry, i));
       basic_block label_bb = label_to_block (lab);
       make_edge (bb, label_bb, 0);
+      assign_discriminator (entry_locus, label_bb);
     }
 }
 
@@ -880,8 +991,10 @@ make_goto_expr_edges (basic_block bb)
   if (simple_goto_p (goto_t))
     {
       tree dest = gimple_goto_dest (goto_t);
-      edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
+      basic_block label_bb = label_to_block (dest);
+      edge e = make_edge (bb, label_bb, EDGE_FALLTHRU);
       e->goto_locus = gimple_location (goto_t);
+      assign_discriminator (e->goto_locus, label_bb);
       if (e->goto_locus)
 	e->goto_block = gimple_block (goto_t);
       gsi_remove (&last, true);
@@ -2690,6 +2803,17 @@ first_stmt (basic_block bb)
   return !gsi_end_p (i) ? gsi_stmt (i) : NULL;
 }
 
+/* Return the first non-label statement in basic block BB.  */
+
+static gimple
+first_non_label_stmt (basic_block bb)
+{
+  gimple_stmt_iterator i = gsi_start_bb (bb);
+  while (!gsi_end_p (i) && gimple_code (gsi_stmt (i)) == GIMPLE_LABEL)
+    gsi_next (&i);
+  return !gsi_end_p (i) ? gsi_stmt (i) : NULL;
+}
+
 /* Return the last statement in basic block BB.  */
 
 gimple
Index: vmsdbgout.c
===================================================================
--- vmsdbgout.c	(revision 148110)
+++ vmsdbgout.c	(working copy)
@@ -173,7 +173,7 @@ static void vmsdbgout_end_source_file (u
 static void vmsdbgout_begin_block (unsigned int, unsigned int);
 static void vmsdbgout_end_block (unsigned int, unsigned int);
 static bool vmsdbgout_ignore_block (const_tree);
-static void vmsdbgout_source_line (unsigned int, const char *);
+static void vmsdbgout_source_line (unsigned int, const char *, int);
 static void vmsdbgout_begin_prologue (unsigned int, const char *);
 static void vmsdbgout_end_prologue (unsigned int, const char *);
 static void vmsdbgout_end_function (unsigned int);
@@ -1297,7 +1297,7 @@ vmsdbgout_end_prologue (unsigned int lin
       ASM_OUTPUT_LABEL (asm_out_file, label);
 
       /* VMS PCA expects every PC range to correlate to some line and file.  */
-      vmsdbgout_source_line (line, file);
+      vmsdbgout_source_line (line, file, 0);
     }
 }
 
@@ -1331,7 +1331,7 @@ vmsdbgout_end_epilogue (unsigned int lin
       ASM_OUTPUT_LABEL (asm_out_file, label);
 
       /* VMS PCA expects every PC range to correlate to some line and file.  */
-      vmsdbgout_source_line (line, file);
+      vmsdbgout_source_line (line, file, 0);
     }
 }
 
@@ -1533,10 +1533,11 @@ lookup_filename (const char *file_name)
    'line_info_table' for later output of the .debug_line section.  */
 
 static void
-vmsdbgout_source_line (register unsigned line, register const char *filename)
+vmsdbgout_source_line (register unsigned line, register const char *filename,
+                       int discriminator)
 {
   if (write_symbols == VMS_AND_DWARF2_DEBUG)
-    (*dwarf2_debug_hooks.source_line) (line, filename);
+    (*dwarf2_debug_hooks.source_line) (line, filename, discriminator);
 
   if (debug_info_level >= DINFO_LEVEL_TERSE)
     {
Index: xcoffout.c
===================================================================
--- xcoffout.c	(revision 148110)
+++ xcoffout.c	(working copy)
@@ -321,7 +321,8 @@ xcoffout_source_file (FILE *file, const 
 /* Output a line number symbol entry for location (FILENAME, LINE).  */
 
 void
-xcoffout_source_line (unsigned int line, const char *filename)
+xcoffout_source_line (unsigned int line, const char *filename,
+                      int discriminator ATTRIBUTE_UNUSED)
 {
   bool inline_p = (strcmp (xcoff_current_function_file, filename) != 0
 		   || (int) line < xcoff_begin_function_line);


More information about the Gcc-patches mailing list