This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC 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]

scheduling vs lexical scopes


[ Jason, you are cc'd because there is a dwarf2 issue that is
  unresolved, and that I'm not sure how to address.  ]

Bear with me a moment, this is complicated.

The thing I'm trying to fix is ia64 compilation of libstdc++.  We were
crashing building misc-inst.cc in add_abstract_origin_attribute due to
the origin not having a die.  The inlined function is:

      bool
      _M_check_facet(const locale::facet* __f)
      {
        bool __ret = false;
        if (__f)
          __ret = true;
        else
          __throw_bad_cast();
        return __ret;
      }

The decl in question is __ret.

Another prerequisite to see this bug is that an out-of-line version of
the inlined function must be emitted before the inlining function is used.
The reason for this will be apparent in a moment.

The reason the origin doesn't have a die is that the origin was never
emitted.  It wasn't emitted because the lexical block containing the
origin was removed.  The lexical block was removed because (1) if-conversion
managed to transform this function into a single basic block and
(2) scheduling doesn't know what to do with BLOCK_BEG/END notes,
and so re-emits them _all_ at the beginning of the block and (3)
remove_unnecessary_notes removes lexical blocks that contain no
real instructions.

So when we generated the out of line version of _M_check_facet, we
wound up removing all lexical blocks, which means that none of the
local variables were emitted into the debug info.

Next, somehow (I didn't investigate why) the inlined version of
_M_check_facet _didn't_ get its blocks removed, so __ret still
existed when we went to emit debug information.

So the outstanding dwarf2 question is: how do we prevent crashes in
this situation?  I'm not sure that we can ever completely prevent
this situation from occurring.

Now, I also consider it a Bad Thing that any time scheduling is
enabled for a function that has one basic block, we'll never be 
able to see any local variables.  This is almost certainly one
of the major causes of the irritating "No local variables" thing
I've seen in gdb from time to time.

Solving this problem for real is hard.  We'd have to remember
which instruction belonged to which lexical block, then regenerate
a modified block tree based on the final schedule.  For extra
points, figure out which instructions make no visible changes
to user variables and consider them part of no lexical block
so that scheduling is less likely to fragment a lexical block
into thousands of little pieces.

However, there are a few simple things we can do to prevent losage
in some common cases.  I've added two trivial test cases for things
we got wrong.  As it happens, the ia64 libstdc++ problem falls into
one of these two categories, so we can get bootstrapped again.

Bootstrapped and checked on alphaev6, i686 and ia64 linux.


r~


        * haifa-sched.c (reemit_other_notes): New.
        (schedule_block): Use it.
        * sched-ebb.c (schedule_ebbs): Call remove_unnecessary_notes.
        * sched-rgn.c (schedule_insns): Likewise.
        * cfglayout.c (remove_scope_notes): Handle removing note at
        the end of the insn chain.
        * function.c (debug_find_var_in_block_tree): New.

        * gcc.dg/debug-1.c, gcc.dg/debug-2.c: New.

Index: cfglayout.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/cfglayout.c,v
retrieving revision 1.5
diff -c -p -d -r1.5 cfglayout.c
*** cfglayout.c	2001/12/24 15:44:45	1.5
--- cfglayout.c	2001/12/27 22:16:56
*************** remove_scope_notes ()
*** 540,546 ****
  	  if (PREV_INSN (x))
  	    {
  	      NEXT_INSN (PREV_INSN (x)) = next;
! 	      PREV_INSN (next) = PREV_INSN (x);
  
                NEXT_INSN (x) = NULL;
                PREV_INSN (x) = NULL;
--- 540,547 ----
  	  if (PREV_INSN (x))
  	    {
  	      NEXT_INSN (PREV_INSN (x)) = next;
! 	      if (next)
! 	        PREV_INSN (next) = PREV_INSN (x);
  
                NEXT_INSN (x) = NULL;
                PREV_INSN (x) = NULL;
Index: function.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/function.c,v
retrieving revision 1.338
diff -c -p -d -r1.338 function.c
*** function.c	2001/12/26 19:54:44	1.338
--- function.c	2001/12/27 22:16:57
*************** static void reorder_fix_fragments PARAMS
*** 277,282 ****
--- 277,283 ----
  static tree blocks_nreverse	PARAMS ((tree));
  static int all_blocks		PARAMS ((tree, tree *));
  static tree *get_block_vector   PARAMS ((tree, int *));
+ extern tree debug_find_var_in_block_tree PARAMS ((tree, tree));
  /* We always define `record_insns' even if its not used so that we
     can always export `prologue_epilogue_contains'.  */
  static void record_insns	PARAMS ((rtx, varray_type *)) ATTRIBUTE_UNUSED;
*************** number_blocks (fn)
*** 6050,6055 ****
--- 6051,6079 ----
    free (block_vector);
  
    return;
+ }
+ 
+ /* If VAR is present in a subblock of BLOCK, return the subblock.  */
+ 
+ tree
+ debug_find_var_in_block_tree (var, block)
+      tree var;
+      tree block;
+ {
+   tree t;
+ 
+   for (t = BLOCK_VARS (block); t; t = TREE_CHAIN (t))
+     if (t == var)
+       return block;
+ 
+   for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t))
+     {
+       tree ret = debug_find_var_in_block_tree (var, t);
+       if (ret)
+ 	return ret;
+     }
+ 
+   return NULL_TREE;
  }
  
  /* Allocate a function structure and reset its contents to the defaults.  */
Index: haifa-sched.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/haifa-sched.c,v
retrieving revision 1.190
diff -c -p -d -r1.190 haifa-sched.c
*** haifa-sched.c	2001/10/28 12:42:03	1.190
--- haifa-sched.c	2001/12/27 22:16:57
*************** static void adjust_priority PARAMS ((rtx
*** 319,324 ****
--- 319,325 ----
  static rtx unlink_other_notes PARAMS ((rtx, rtx));
  static rtx unlink_line_notes PARAMS ((rtx, rtx));
  static rtx reemit_notes PARAMS ((rtx, rtx));
+ static rtx reemit_other_notes PARAMS ((rtx, rtx));
  
  static rtx *ready_lastpos PARAMS ((struct ready_list *));
  static void ready_sort PARAMS ((struct ready_list *));
*************** reemit_notes (insn, last)
*** 1575,1580 ****
--- 1576,1635 ----
    return retval;
  }
  
+ 
+ /* NOTE_LIST is the end of a chain of notes previously found among the
+    insns.  Insert them at the beginning of the insns.  Actually, insert
+    NOTE_INSN_BLOCK_END notes at the end of the insns.  Doing otherwise
+    tends to collapse lexical blocks into empty regions, which is somewhat
+    less than useful.  */
+ /* ??? Ideally we'd mark each insn with the block it originated from,
+    and preserve that information.  This requires some moderately
+    sophisticated block reconstruction code, since block nestings must
+    be preserved.  */
+ 
+ static rtx
+ reemit_other_notes (head, tail)
+      rtx head, tail;
+ {
+   bool saw_block_beg = false;
+ 
+   while (note_list)
+     {
+       rtx note_tail = note_list;
+       note_list = PREV_INSN (note_tail);
+ 
+       if (NOTE_LINE_NUMBER (note_tail) == NOTE_INSN_BLOCK_END
+ 	  /* We can only extend the lexical block while we havn't
+ 	     seen a BLOCK_BEG note.  Otherwise we risk mis-nesting
+ 	     the notes.  */
+ 	  && ! saw_block_beg)
+ 	{
+ 	  rtx insert_after = tail;
+ 	  if (GET_CODE (NEXT_INSN (tail)) == BARRIER)
+ 	    insert_after = NEXT_INSN (tail);
+ 
+ 	  PREV_INSN (note_tail) = insert_after;
+ 	  NEXT_INSN (note_tail) = NEXT_INSN (insert_after);
+ 	  if (NEXT_INSN (insert_after))
+ 	    PREV_INSN (NEXT_INSN (insert_after)) = note_tail;
+ 	  NEXT_INSN (insert_after) = note_tail;
+ 	}
+       else
+ 	{
+ 	  if (NOTE_LINE_NUMBER (note_tail) == NOTE_INSN_BLOCK_BEG)
+ 	    saw_block_beg = true;
+ 
+ 	  PREV_INSN (note_tail) = PREV_INSN (head);
+ 	  NEXT_INSN (PREV_INSN (head)) = note_tail;
+ 	  NEXT_INSN (note_tail) = head;
+ 	  PREV_INSN (head) = note_tail;
+ 	  head = note_tail;
+ 	}
+     }
+ 
+   return head;
+ }
+ 
  /* Move INSN, and all insns which should be issued before it,
     due to SCHED_GROUP_P flag.  Reemit notes if needed.
  
*************** schedule_block (b, rgn_n_insns)
*** 1800,1823 ****
    head = NEXT_INSN (prev_head);
    tail = last;
  
!   /* Restore-other-notes: NOTE_LIST is the end of a chain of notes
!      previously found among the insns.  Insert them at the beginning
!      of the insns.  */
!   if (note_list != 0)
!     {
!       rtx note_head = note_list;
! 
!       while (PREV_INSN (note_head))
! 	{
! 	  note_head = PREV_INSN (note_head);
! 	}
! 
!       PREV_INSN (note_head) = PREV_INSN (head);
!       NEXT_INSN (PREV_INSN (head)) = note_head;
!       PREV_INSN (head) = note_list;
!       NEXT_INSN (note_list) = head;
!       head = note_head;
!     }
  
    /* Debugging.  */
    if (sched_verbose)
--- 1855,1861 ----
    head = NEXT_INSN (prev_head);
    tail = last;
  
!   head = reemit_other_notes (head, tail);
  
    /* Debugging.  */
    if (sched_verbose)
Index: sched-ebb.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/sched-ebb.c,v
retrieving revision 1.6
diff -c -p -d -r1.6 sched-ebb.c
*** sched-ebb.c	2001/09/14 11:05:39	1.6
--- sched-ebb.c	2001/12/27 22:16:57
*************** schedule_ebbs (dump_file)
*** 285,290 ****
--- 285,294 ----
    if (n_basic_blocks == 0)
      return;
  
+   /* Remove lexical block notes for empty regions.  These get shuffled
+      about during scheduling and confuse the debugging issue.  */
+   remove_unnecessary_notes ();
+ 
    sched_init (dump_file);
  
    current_sched_info = &ebb_sched_info;
Index: sched-rgn.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/sched-rgn.c,v
retrieving revision 1.27
diff -c -p -d -r1.27 sched-rgn.c
*** sched-rgn.c	2001/12/11 22:50:35	1.27
--- sched-rgn.c	2001/12/27 22:16:58
*************** schedule_insns (dump_file)
*** 2896,2901 ****
--- 2896,2905 ----
    if (n_basic_blocks == 0)
      return;
  
+   /* Remove lexical block notes for empty regions.  These get shuffled
+      about during scheduling and confuse the debugging issue.  */
+   remove_unnecessary_notes ();
+ 
    nr_inter = 0;
    nr_spec = 0;
  
Index: testsuite/gcc.dg/debug-1.c
===================================================================
RCS file: debug-1.c
diff -N debug-1.c
*** /dev/null	Tue May  5 13:32:27 1998
--- debug-1.c	Thu Dec 27 14:16:58 2001
***************
*** 0 ****
--- 1,14 ----
+ /* Verify that the scheduler does not discard the lexical block.  */
+ /* { dg-do compile } */
+ /* { dg-options "-O2 -g -dA" } */
+ /* { dg-final { scan-assembler "xyzzy" } } */
+ 
+ long foo(long p)
+ {
+   {
+     long xyzzy = 0;
+     if (p)
+       xyzzy = 2;
+     return xyzzy;
+   }
+ }
Index: testsuite/gcc.dg/debug-2.c
===================================================================
RCS file: debug-2.c
diff -N debug-2.c
*** /dev/null	Tue May  5 13:32:27 1998
--- debug-2.c	Thu Dec 27 14:16:58 2001
***************
*** 0 ****
--- 1,20 ----
+ /* Verify that the scheduler does not discard the lexical block.  */
+ /* { dg-do compile } */
+ /* { dg-options "-O2 -g -dA" } */
+ /* { dg-final { scan-assembler "xyzzy" } } */
+ 
+ long foo(long p)
+ {
+   if (1)
+     {
+       long xyzzy = 0;
+       if (p)
+         xyzzy = 2;
+       return xyzzy;
+     }
+   else
+     {
+       int x = 0;
+       return x;
+     }
+ }


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