This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

enhance .loc directive for dwarf3 special features


This patch adds some sub-directives to ".loc" to enable access to
the entire dwarf3 state machine.  Well, actually, two of these
were present in dwarf2 and we hadn't exposed them either.

I think the syntax is fairly clean, but if someone thinks we should
use separate directives like ".loc_basic_block", speak now or forever
hold your peace.

Tested on i686-linux and committed to HEAD.


r~


        * dwarf2dbg.c: Include safe-ctype.h.
        (DWARF2_LINE_OPCODE_BASE): Bump to 13.
        (current): Initialize.
        (dwarf2_emit_insn): Clear DWARF2_FLAG_BASIC_BLOCK,
        DWARF2_FLAG_PROLOGUE_END, DWARF2_FLAG_EPILOGUE_BEGIN.
        (dwarf2_directive_file): Cope with invalid filename.
        (dwarf2_directive_loc): Add handling for basic_block, prologue_end,
        epilogue_begin, is_stmt, isa.
        (emit_inc_line_addr): Move line_delta == 0, addr_delta == 0 special
        case down lower.
        (process_entries): Handle isa, DWARF2_FLAG_PROLOGUE_END,
        and DWARF2_FLAG_EPILOGUE_BEGIN.
        (out_debug_line): Emit sizes for DW_LNS_set_prologue_end,
        DW_LNS_set_epilogue_begin, DW_LNS_set_isa.
        * dwarf2dbg.h (DWARF2_FLAG_IS_STMT): Rename from DWARF2_FLAG_BEGIN_STMT.        (DWARF2_FLAG_BASIC_BLOCK): Rename from DWARF2_FLAG_BEGIN_BLOCK.
        (DWARF2_FLAG_PROLOGUE_END, DWARF2_FLAG_EPILOGUE_BEGIN): New.
        (struct dwarf2_line_info): Add isa member.
        * doc/as.texinfo (LNS directives): New node.

Index: gas/dwarf2dbg.c
===================================================================
RCS file: /cvs/src/src/gas/dwarf2dbg.c,v
retrieving revision 1.76
diff -u -p -r1.76 dwarf2dbg.c
--- gas/dwarf2dbg.c	11 Aug 2005 01:25:20 -0000	1.76
+++ gas/dwarf2dbg.c	7 Sep 2005 11:31:40 -0000
@@ -21,15 +21,20 @@
    02110-1301, USA.  */
 
 /* Logical line numbers can be controlled by the compiler via the
-   following two directives:
+   following directives:
 
 	.file FILENO "file.c"
 	.loc  FILENO LINENO [COLUMN]
-
-   FILENO is the filenumber.  */
+	.loc  basic_block
+	.loc  prologue_end
+	.loc  epilogue_begin
+	.loc  is_stmt [VALUE]
+	.loc  isa [VALUE]
+*/
 
 #include "ansidecl.h"
 #include "as.h"
+#include "safe-ctype.h"
 
 #ifdef HAVE_LIMITS_H
 #include <limits.h>
@@ -68,8 +73,8 @@
 /* First special line opcde - leave room for the standard opcodes.
    Note: If you want to change this, you'll have to update the
    "standard_opcode_lengths" table that is emitted below in
-   dwarf2_finish().  */
-#define DWARF2_LINE_OPCODE_BASE		10
+   out_debug_line().  */
+#define DWARF2_LINE_OPCODE_BASE		13
 
 #ifndef DWARF2_LINE_BASE
   /* Minimum line offset in a special line info. opcode.  This value
@@ -88,10 +93,7 @@
 # define DWARF2_LINE_MIN_INSN_LENGTH	1
 #endif
 
-/* Flag that indicates the initial value of the is_stmt_start flag.
-   In the present implementation, we do not mark any lines as
-   the beginning of a source statement, because that information
-   is not made available by the GCC front-end.  */
+/* Flag that indicates the initial value of the is_stmt_start flag.  */
 #define	DWARF2_LINE_DEFAULT_IS_STMT	1
 
 /* Given a special op, return the line skip amount.  */
@@ -150,7 +152,10 @@ static unsigned int dirs_allocated;
 static bfd_boolean loc_directive_seen;
 
 /* Current location as indicated by the most recent .loc directive.  */
-static struct dwarf2_line_info current;
+static struct dwarf2_line_info current = {
+  1, 1, 0, 0,
+  DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0
+};
 
 /* The size of an address on the target.  */
 static unsigned int sizeof_address;
@@ -291,7 +296,7 @@ dwarf2_where (struct dwarf2_line_info *l
       as_where (&filename, &line->line);
       line->filenum = get_filenum (filename, 0);
       line->column = 0;
-      line->flags = DWARF2_FLAG_BEGIN_STMT;
+      line->flags = DWARF2_FLAG_IS_STMT;
     }
   else
     *line = current;
@@ -324,9 +329,13 @@ dwarf2_emit_insn (int size)
   else if (debug_type != DEBUG_DWARF2)
     return;
   else
-    dwarf2_where (& loc);
+    dwarf2_where (&loc);
 
   dwarf2_gen_line_info (frag_now_fix () - size, &loc);
+
+  current.flags &= ~(DWARF2_FLAG_BASIC_BLOCK
+		     | DWARF2_FLAG_PROLOGUE_END
+		     | DWARF2_FLAG_EPILOGUE_BEGIN);
 }
 
 /* Get a .debug_line file number for FILENAME.  If NUM is nonzero,
@@ -449,6 +458,8 @@ dwarf2_directive_file (int dummy ATTRIBU
 
   num = get_absolute_expression ();
   filename = demand_copy_C_string (&filename_len);
+  if (filename == NULL)
+    return NULL;
   demand_empty_rest_of_line ();
 
   if (num < 1)
@@ -471,53 +482,105 @@ dwarf2_directive_file (int dummy ATTRIBU
 void
 dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
 {
-  offsetT filenum, line, column;
-
-  filenum = get_absolute_expression ();
   SKIP_WHITESPACE ();
-  line = get_absolute_expression ();
-  SKIP_WHITESPACE ();
-  column = get_absolute_expression ();
-  demand_empty_rest_of_line ();
-
-  if (filenum < 1)
+  if (ISALPHA (*input_line_pointer))
     {
-      as_bad (_("file number less than one"));
-      return;
+      char *p, c;
+      offsetT value;
+
+      p = input_line_pointer;
+      c = get_symbol_end ();
+
+      if (strcmp (p, "basic_block") == 0)
+	{
+	  current.flags |= DWARF2_FLAG_BASIC_BLOCK;
+	  *input_line_pointer = c;
+	}
+      else if (strcmp (p, "prologue_end") == 0)
+	{
+	  current.flags |= DWARF2_FLAG_PROLOGUE_END;
+	  *input_line_pointer = c;
+	}
+      else if (strcmp (p, "epilogue_begin") == 0)
+	{
+	  current.flags |= DWARF2_FLAG_EPILOGUE_BEGIN;
+	  *input_line_pointer = c;
+	}
+      else if (strcmp (p, "is_stmt") == 0)
+	{
+	  *input_line_pointer = c;
+	  value = get_absolute_expression ();
+	  if (value == 0)
+	    current.flags &= ~DWARF2_FLAG_IS_STMT;
+	  else if (value == 1)
+	    current.flags |= DWARF2_FLAG_IS_STMT;
+	  else
+	    as_bad (_("is_stmt value not 0 or 1"));
+	}
+      else if (strcmp (p, "isa") == 0)
+	{
+          *input_line_pointer = c;
+	  value = get_absolute_expression ();
+	  if (value < 0)
+	    as_bad (_("isa number less than zero"));
+	  else
+	    current.isa = value;
+	}
+      else
+	{
+	  as_bad (_("unknown .loc sub-directive %s"), p);
+          *input_line_pointer = c;
+	}
     }
-  if (filenum >= (int) files_in_use || files[filenum].filename == 0)
+  else
     {
-      as_bad (_("unassigned file number %ld"), (long) filenum);
-      return;
-    }
+      offsetT filenum, line, column;
 
-  current.filenum = filenum;
-  current.line = line;
-  current.column = column;
-  current.flags = DWARF2_FLAG_BEGIN_STMT;
+      filenum = get_absolute_expression ();
+      SKIP_WHITESPACE ();
+      line = get_absolute_expression ();
+      SKIP_WHITESPACE ();
+      column = get_absolute_expression ();
 
-  loc_directive_seen = TRUE;
+      if (filenum < 1)
+	{
+	  as_bad (_("file number less than one"));
+	  return;
+	}
+      if (filenum >= (int) files_in_use || files[filenum].filename == 0)
+	{
+	  as_bad (_("unassigned file number %ld"), (long) filenum);
+	  return;
+	}
+
+      current.filenum = filenum;
+      current.line = line;
+      current.column = column;
 
 #ifndef NO_LISTING
-  if (listing)
-    {
-      if (files[filenum].dir)
+      if (listing)
 	{
-	  size_t dir_len = strlen (dirs[files[filenum].dir]);
-	  size_t file_len = strlen (files[filenum].filename);
-	  char *cp = (char *) alloca (dir_len + 1 + file_len + 1);
-
-	  memcpy (cp, dirs[files[filenum].dir], dir_len);
-	  cp[dir_len] = '/';
-	  memcpy (cp + dir_len + 1, files[filenum].filename, file_len);
-	  cp[dir_len + file_len + 1] = '\0';
-	  listing_source_file (cp);
+	  if (files[filenum].dir)
+	    {
+	      size_t dir_len = strlen (dirs[files[filenum].dir]);
+	      size_t file_len = strlen (files[filenum].filename);
+	      char *cp = (char *) alloca (dir_len + 1 + file_len + 1);
+
+	      memcpy (cp, dirs[files[filenum].dir], dir_len);
+	      cp[dir_len] = '/';
+	      memcpy (cp + dir_len + 1, files[filenum].filename, file_len);
+	      cp[dir_len + file_len + 1] = '\0';
+	      listing_source_file (cp);
+	    }
+	  else
+	    listing_source_file (files[filenum].filename);
+	  listing_source_line (line);
 	}
-      else
-	listing_source_file (files[filenum].filename);
-      listing_source_line (line);
-    }
 #endif
+    }
+
+  demand_empty_rest_of_line ();
+  loc_directive_seen = TRUE;
 }
 
 static struct frag *
@@ -759,19 +822,19 @@ emit_inc_line_addr (int line_delta, addr
       *p++ = DW_LNS_advance_line;
       p += output_leb128 (p, line_delta, 1);
 
-      /* Prettier, I think, to use DW_LNS_copy instead of a
-	 "line +0, addr +0" special opcode.  */
-      if (addr_delta == 0)
-	{
-	  *p++ = DW_LNS_copy;
-	  goto done;
-	}
-
       line_delta = 0;
       tmp = 0 - DWARF2_LINE_BASE;
       need_copy = 1;
     }
 
+  /* Prettier, I think, to use DW_LNS_copy instead of a "line +0, addr +0"
+     special opcode.  */
+  if (line_delta == 0 && addr_delta == 0)
+    {
+      *p++ = DW_LNS_copy;
+      goto done;
+    }
+
   /* Bias the opcode by the special opcode base.  */
   tmp += DWARF2_LINE_OPCODE_BASE;
 
@@ -913,7 +976,8 @@ process_entries (segT seg, struct line_e
   unsigned filenum = 1;
   unsigned line = 1;
   unsigned column = 0;
-  unsigned flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_BEGIN_STMT : 0;
+  unsigned isa = 0;
+  unsigned flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0;
   fragS *frag = NULL;
   fragS *last_frag;
   addressT frag_ofs = 0;
@@ -940,19 +1004,39 @@ process_entries (segT seg, struct line_e
 	  changed = 1;
 	}
 
-      if ((e->loc.flags ^ flags) & DWARF2_FLAG_BEGIN_STMT)
+      if (isa != e->loc.isa)
+	{
+	  isa = e->loc.isa;
+	  out_opcode (DW_LNS_set_isa);
+	  out_uleb128 (isa);
+	  changed = 1;
+	}
+
+      if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT)
 	{
 	  flags = e->loc.flags;
 	  out_opcode (DW_LNS_negate_stmt);
 	  changed = 1;
 	}
 
-      if (e->loc.flags & DWARF2_FLAG_BEGIN_BLOCK)
+      if (e->loc.flags & DWARF2_FLAG_BASIC_BLOCK)
 	{
 	  out_opcode (DW_LNS_set_basic_block);
 	  changed = 1;
 	}
 
+      if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END)
+	{
+	  out_opcode (DW_LNS_set_prologue_end);
+	  changed = 1;
+	}
+
+      if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN)
+	{
+	  out_opcode (DW_LNS_set_epilogue_begin);
+	  changed = 1;
+	}
+
       /* Don't try to optimize away redundant entries; gdb wants two
 	 entries for a function where the code starts on the same line as
 	 the {, and there's no way to identify that case here.  Trust gcc
@@ -1115,6 +1199,9 @@ out_debug_line (segT line_seg)
   out_byte (0);			/* DW_LNS_set_basic_block */
   out_byte (0);			/* DW_LNS_const_add_pc */
   out_byte (1);			/* DW_LNS_fixed_advance_pc */
+  out_byte (0);			/* DW_LNS_set_prologue_end */
+  out_byte (0);			/* DW_LNS_set_epilogue_begin */
+  out_byte (1);			/* DW_LNS_set_isa */
 
   out_file_list ();
 
Index: gas/dwarf2dbg.h
===================================================================
RCS file: /cvs/src/src/gas/dwarf2dbg.h,v
retrieving revision 1.15
diff -u -p -r1.15 dwarf2dbg.h
--- gas/dwarf2dbg.h	5 May 2005 09:12:44 -0000	1.15
+++ gas/dwarf2dbg.h	7 Sep 2005 11:31:40 -0000
@@ -23,13 +23,16 @@
 
 #include "as.h"
 
-#define DWARF2_FLAG_BEGIN_STMT	(1 << 0)	/* beginning of statement */
-#define DWARF2_FLAG_BEGIN_BLOCK	(1 << 1)	/* beginning of basic block */
+#define DWARF2_FLAG_IS_STMT		(1 << 0)
+#define DWARF2_FLAG_BASIC_BLOCK		(1 << 1)
+#define DWARF2_FLAG_PROLOGUE_END	(1 << 2)
+#define DWARF2_FLAG_EPILOGUE_BEGIN	(1 << 3)
 
 struct dwarf2_line_info {
   unsigned int filenum;
   unsigned int line;
   unsigned int column;
+  unsigned int isa;
   unsigned int flags;
 };
 
Index: gas/doc/as.texinfo
===================================================================
RCS file: /cvs/src/src/gas/doc/as.texinfo,v
retrieving revision 1.125
diff -u -p -r1.125 as.texinfo
--- gas/doc/as.texinfo	6 Sep 2005 18:53:03 -0000	1.125
+++ gas/doc/as.texinfo	7 Sep 2005 11:31:40 -0000
@@ -3745,9 +3745,12 @@ Some machine configurations provide addi
 * Line::                        @code{.line @var{line-number}}
 @end ifclear
 
-* Ln::                          @code{.ln @var{line-number}}
 * Linkonce::			@code{.linkonce [@var{type}]}
 * List::                        @code{.list}
+* Ln::                          @code{.ln @var{line-number}}
+
+* LNS directives::              @code{.file}, @code{.loc}, etc.
+
 * Long::                        @code{.long @var{expressions}}
 @ignore
 * Lsym::                        @code{.lsym @var{symbol}, @var{expression}}
@@ -4049,6 +4052,48 @@ Allows the user to add arbitrary bytes t
 might use this to add OS-specific CFI opcodes, or generic CFI
 opcodes that GAS does not yet support.
 
+@node LNS directives
+@section @code{.file @var{fileno} @var{filename}}
+@cindex @code{file} directive
+When emitting dwarf2 line number information @code{.file} assigns filenames
+to the @code{.debug_line} file name table.  The @var{fileno} operand should
+be a unique positive integer to use as the index of the entry in the table.
+The @var{filename} operand is a C string literal.
+
+The detail of filename indicies is exposed to the user because the filename
+table is shared with the @code{.debug_info} section of the dwarf2 debugging
+information, and thus the user must know the exact indicies that table
+entries will have.
+
+@section @code{.loc @var{fileno} @var{lineno} [@var{column}]}
+@cindex @code{loc} directive
+The @code{.loc} directive will add row to the @code{.debug_line} line
+number matrix corresponding to the immediately following assembly
+instruction.  The @var{fileno}, @var{lineno}, and optional @var{column}
+arguments will be applied to the @code{.debug_line} state machine before
+the row is added.
+
+@section @code{.loc basic_block}
+This directive will set the @code{basic_block} register in the
+@code{.debug_line} state machine to @code{true}.
+
+@section @code{.loc prologue_end}
+This directive will set the @code{prologue_end} register in the
+@code{.debug_line} state machine to @code{true}.
+
+@section @code{.loc epilogue_begin}
+This directive will set the @code{epilogue_begin} register in the
+@code{.debug_line} state machine to @code{true}.
+
+@section @code{.loc is_stmt @var{value}}
+This directive will set the @code{epilogue_begin} register in the
+@code{.debug_line} state machine to @code{value}, which must be 
+either 0 or 1.
+
+@section @code{.loc isa @var{value}}
+This directive will set the @code{isa} register in the @code{.debug_line}
+state machine to @var{value}, which must be an unsigned integer.
+
 @node Data
 @section @code{.data @var{subsection}}
 
Index: gas/testsuite/gas/lns/lns-common-1.d
===================================================================
RCS file: gas/testsuite/gas/lns/lns-common-1.d
diff -N gas/testsuite/gas/lns/lns-common-1.d
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/lns/lns-common-1.d	7 Sep 2005 11:31:41 -0000
@@ -0,0 +1,29 @@
+#readelf: -wl
+#name: lns-common-1
+Dump of debug contents of section \.debug_line:
+#...
+  DWARF Version:               2
+  Prologue Length:             28
+  Minimum Instruction Length:  1
+  Initial value of 'is_stmt':  1
+  Line Base:                   -5
+  Line Range:                  14
+  Opcode Base:                 13
+#...
+ Line Number Statements:
+  Extended opcode 2: set Address to 0x0
+  Copy
+  Set prologue_end to true
+  Special opcode 19: advance Address by .* to .* and Line by 0 to 1
+  Set epilogue_begin to true
+  Special opcode 20: advance Address by .* to .* and Line by 1 to 2
+  Set ISA to 1
+  Set basic block
+  Special opcode 20: advance Address by .* to .* and Line by 1 to 3
+  Set is_stmt to 0
+  Special opcode 19: advance Address by .* to .* and Line by 0 to 3
+  Set is_stmt to 1
+  Special opcode 19: advance Address by .* to .* and Line by 0 to 3
+  Advance PC by .* to .*
+  Extended opcode 1: End of Sequence
+#...
Index: gas/testsuite/gas/lns/lns-common-1.s
===================================================================
RCS file: gas/testsuite/gas/lns/lns-common-1.s
diff -N gas/testsuite/gas/lns/lns-common-1.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/lns/lns-common-1.s	7 Sep 2005 11:31:41 -0000
@@ -0,0 +1,16 @@
+	.file 1 "foo.c"
+	.loc 1 1
+	nop
+	.loc prologue_end
+	nop
+	.loc epilogue_begin
+	.loc 1 2
+	nop
+	.loc isa 1
+	.loc basic_block
+	.loc 1 3
+	nop
+	.loc is_stmt 0
+	nop
+	.loc is_stmt 1
+	nop
Index: gas/testsuite/gas/lns/lns-diag-1.l
===================================================================
RCS file: gas/testsuite/gas/lns/lns-diag-1.l
diff -N gas/testsuite/gas/lns/lns-diag-1.l
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/lns/lns-diag-1.l	7 Sep 2005 11:31:41 -0000
@@ -0,0 +1,11 @@
+.*: Assembler messages:
+.*:2: Error: file number less than one
+.*:3: Error: missing string
+.*:4: Error: file number 1 already allocated
+.*:8: Error: unassigned file number 3
+.*:9: Error: junk at end of line, first unrecognized character is `1'
+.*:12: Error: junk at end of line, first unrecognized character is `0'
+.*:18: Error: is_stmt value not 0 or 1
+.*:19: Error: bad or irreducible absolute expression
+.*:23: Error: isa number less than zero
+.*:26: Error: unknown .loc sub-directive frobnitz
Index: gas/testsuite/gas/lns/lns-diag-1.s
===================================================================
RCS file: gas/testsuite/gas/lns/lns-diag-1.s
diff -N gas/testsuite/gas/lns/lns-diag-1.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/lns/lns-diag-1.s	7 Sep 2005 11:31:41 -0000
@@ -0,0 +1,26 @@
+	.file 1 "foo.c"
+	.file 0 "bar.c"
+	.file 2 baz.c
+	.file 1 "bar.c"
+
+	.loc 1 1
+	.loc 1 2 3
+	.loc 3 1
+	.loc 1 1 1 1
+
+	.loc basic_block
+	.loc basic_block 0
+	.loc prologue_end
+	.loc epilogue_begin
+
+	.loc is_stmt 0
+	.loc is_stmt 1
+	.loc is_stmt 2
+	.loc is_stmt foo
+
+	.loc isa 1
+	.loc isa 2
+	.loc isa -1
+	.loc isa 0
+
+	.loc frobnitz
Index: gas/testsuite/gas/lns/lns.exp
===================================================================
RCS file: gas/testsuite/gas/lns/lns.exp
diff -N gas/testsuite/gas/lns/lns.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/lns/lns.exp	7 Sep 2005 11:31:41 -0000
@@ -0,0 +1,26 @@
+# ??? This probably shouldn't be replicated here...
+proc run_list_test { name opts } {
+    global srcdir subdir
+    set testname "lns $name"
+    set file $srcdir/$subdir/$name
+    gas_run ${name}.s $opts ">&dump.out"
+    if { [regexp_diff "dump.out" "${file}.l"] } then {
+        fail $testname
+        verbose "output is [file_contents "dump.out"]" 2
+        return
+    }
+    pass $testname
+}
+
+if ![is_elf_format] then {
+    return
+}
+
+run_list_test "lns-diag-1" ""
+
+# ??? Won't work on targets that don't have a bare "nop" insn,
+# which is only ia64, afaik.  Perhaps we could arrange for an
+# include file or something that defined a macro...
+if ![istarget ia64*-*-*] {
+  run_dump_test "lns-common-1"
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]