Hi Guys,

  Sometimes gcc can generate instructions out of order with respect to
  lines of source code, and this can lead to problems for debuggers.
  For example, consider this source code snippet:

      v = 0;                 /* Line 31 */
      goto b;                /* Line 32 */
    a:  v++;                 /* Line 33 */
    b:  if (v < 3) goto a;   /* Line 34 */

  Compiled without optimization, but with -g, gcc will produce code like
  this:
  
    400692:     c7 05 94 09 20 00 00    movl   $0x0,0x200994(%rip)
    400699:     00 00 00 
    40069c:     eb 10                   jmp    4006ae <loop_test+0xc6>
    40069e:     90                      nop
    40069f:     8b 05 8b 09 20 00       mov    0x20098b(%rip),%eax
    4006a5:     83 c0 01                add    $0x1,%eax
    4006a8:     89 05 82 09 20 00       mov    %eax,0x200982(%rip)
    4006ae:     8b 05 7c 09 20 00       mov    0x20097c(%rip),%eax
    4006b4:     83 f8 02                cmp    $0x2,%eax
    4006b7:     7e e5                   jle    40069e <loop_test+0xb6>
    4006b9:     90                      nop

  This example uses x86_64, but the same problem occurs for most
  architectures.  Note the two NOP instructions.  The DWARF line table
  information produced for this code looks like this:

    advance Address by  5 to 0x400692 and Line by 7 to 31
    advance Address by 10 to 0x40069c and Line by 1 to 32
    advance Address by  2 to 0x40069e and Line by 2 to 34
    advance Address by  1 to 0x40069f and Line by -1 to 33
    advance Address by 15 to 0x4006ae and Line by 1 to 34
    advance Address by 11 to 0x4006b9 and Line by 1 to 35

  (Note the backwards movement of the line number to the start of line
  33).  The problem is line 34.  According to the line table it starts
  at address 0x40069e whereas in reality it starts at 0x4006ae.  What
  has happened is that gcc has generated a NOP instruction to be the
  destination of the goto on line 34 - ie the label a: - but rather than
  associate it with line 33, it has associated it with line 34.  This
  means that line 34 now appears twice in the line number table.

  I am offering the patch below as a fix for this problem, although I am
  open to suggestions for better solutions.  The patch creates a hash
  table for instructions which gcc knows should not be considered to be
  the start of a source code line, then it checks this table when
  noticing source line changes.  The patch only adds the NOP
  instructions generated for the test code above, although I expect that
  in the future there may be other places that need to record this
  information.

  Tested with no regressions on an x86_64-pc-linux toolchain.

  Comments / questions / approval ?

Cheers
  Nick

gcc/ChangeLog
2015-10-22  Nick Clifton  <ni...@redhat.com>

        * cfgrtl.c (not_a_stmt_hasher): New struct.
        (not_a_stmt_htab): New hash table.
        (is_a_stmt): New function.  Returns true iff the given insn is on
        the list of insns known not to start a line of source code.
        (fixup_reorder_chain): Add generated NOP instructions to the list
        of instructions that are known not to start a source line.
        * cfgrtl.h (is_a_stmt): Add prototype.
        * final.c (notice_source_line): Use the new function to set the
        is_stmt return value.

Index: gcc/cfgrtl.c
===================================================================
--- gcc/cfgrtl.c        (revision 229162)
+++ gcc/cfgrtl.c        (working copy)
@@ -3665,7 +3665,21 @@
   compact_blocks ();
 }
 
+struct not_a_stmt_hasher : ggc_cache_ptr_hash<rtx_def>
+{
+  static hashval_t hash (rtx x) { return htab_hash_pointer (x); }
+  static bool equal (rtx a, rtx b) { return a == b; }
+};
 
+static GTY((cache)) hash_table<not_a_stmt_hasher> *not_a_stmt_htab;
+
+/* Return TRUE if INSN is on the list of not-an-insn.  */
+bool
+is_a_stmt (rtx_insn *insn)
+{
+  return not_a_stmt_htab == NULL || ! not_a_stmt_htab->find (insn);
+}
+
 /* Given a reorder chain, rearrange the code to match.  */
 
 static void
@@ -3944,8 +3958,18 @@
                }
              nb = split_edge (e);
              if (!INSN_P (BB_END (nb)))
-               BB_END (nb) = emit_insn_after_noloc (gen_nop (), BB_END (nb),
-                                                        nb);
+               {
+                 BB_END (nb) = emit_insn_after_noloc (gen_nop (), BB_END (nb),
+                                                      nb);
+                 
+                 /* Note that this insn is a fake and should not be considered
+                    to be the starting insn of a line of source code.  */
+                 if (not_a_stmt_htab == NULL)
+                   not_a_stmt_htab = hash_table<not_a_stmt_hasher>::create_ggc 
(17);
+                 rtx_def ** slot;
+                 slot = not_a_stmt_htab->find_slot (BB_END (nb), INSERT);
+                 * slot = BB_END (nb);
+               }
              INSN_LOCATION (BB_END (nb)) = e->goto_locus;
 
              /* If there are other incoming edges to the destination block
Index: gcc/cfgrtl.h
===================================================================
--- gcc/cfgrtl.h        (revision 229162)
+++ gcc/cfgrtl.h        (working copy)
@@ -54,5 +54,6 @@
 extern void cfg_layout_finalize (void);
 extern void break_superblocks (void);
 extern void init_rtl_bb_info (basic_block);
+extern bool is_a_stmt (rtx_insn *);
 
 #endif /* GCC_CFGRTL_H */
Index: gcc/final.c
===================================================================
--- gcc/final.c (revision 229162)
+++ gcc/final.c (working copy)
@@ -3081,7 +3081,7 @@
       last_filename = filename;
       last_linenum = linenum;
       last_discriminator = discriminator;
-      *is_stmt = true;
+      *is_stmt = is_a_stmt (insn);
       high_block_linenum = MAX (last_linenum, high_block_linenum);
       high_function_linenum = MAX (last_linenum, high_function_linenum);
       return true;

Reply via email to