Fix dwarf2 debug info on local static vars 1/2

Jan Hubicka hubicka@ucw.cz
Sat Feb 28 18:34:00 GMT 2009


Hi,
following testcase:

static inline int t(int op)
{
  static int counter;
  int lcounter;
  for (lcounter = 0; lcounter<op; lcounter++)
    printf ("%i\n",counter++);
}
main()
{
 t(5);
 t(5);
 t(5);
 t(5);
}

shows multiple problems with debug info

 1) The function gets clonned first (ipcp propagate op==5) and inlined later.
    We forget to output abstract function for functions being clonned so everything
    gets undefined abstract origins (we should probably ICE in such case)
 
 2) Local static variabes are always omitted in abstract functions and only sometimes
    included in compiled functions

 3) Local static variables are not preserved by inlining

 4) Inliner kills debug info when fully replacing decl (op==5)
    (this is done for const arguments even at -O0)

 5) Function versioning puts declaration "op" twice into outer scope of t's clone
    and it is kept this way all the way down to debug info
 
 6) Inliner puts "counter" several times into local_decls list.

I got quite snowballing effect trying to fix those problems, so I will post separate
patches for the subproblems.  With all combined and gdb patched to support inline functions
the result is quite pleasant: single stepping works, 'n' command executes whole inlined
function body and stops on next line in main.  When single settping into the t's body
it is correctly displayed at 't' not as T.0 (clone name) and all three variables
can be inspected.

We don't get proper unwind info, because we can't represent that parameter was omitted in
versioning (pretty-ipa has some support for this) and 'fin' command for some reason does not
finish 't', but I am convinced that is GDB bug.


The first patch fixes handling of unused static vars.   After adding cgraph code to eliminate
unneded static variables we hit problem with dwarf debug info referencing them.  I tried to fix
that by postnoting producing all static var dies after decision is finished, but this does not
work.  For abstract functions we need to output them in scope block.  Fortunately it is easy
to build simple die first and feed in address info at later time.

Bootstrapped/regtested x86_64-linux and tested using gdb testsuite, OK?

	* cgraph.h (varpool_output_debug_info): Remove.
	* cgraphunit.c (varpool_output_debug_info): Remove.
	* dwarf2out.c (deferred_locations_struct): New struct
	(deferred_locations): New type.
	(deferred_locations_list): New static var.
	(deffer_location): New function.
	(gen_variable_die): Use it.
	(decls_for_scope): Output info on local static vars.
	(dwarf2out_finish): Process deferred locations.
	* varpool.c (varpool_output_debug_info): Remove.

	* gcc.dg/debug/dwarf2/static1.c: New testcase.
Index: cgraph.h
===================================================================
*** cgraph.h	(revision 144489)
--- cgraph.h	(working copy)
*************** enum availability cgraph_variable_initia
*** 413,419 ****
  bool varpool_assemble_pending_decls (void);
  bool varpool_assemble_decl (struct varpool_node *node);
  bool varpool_analyze_pending_decls (void);
- void varpool_output_debug_info (void);
  void varpool_remove_unreferenced_decls (void);
  void varpool_empty_needed_queue (void);
  
--- 413,418 ----
Index: cgraphunit.c
===================================================================
*** cgraphunit.c	(revision 144489)
--- cgraphunit.c	(working copy)
*************** cgraph_optimize (void)
*** 1313,1319 ****
  
        varpool_assemble_pending_decls ();
      }
-   varpool_output_debug_info ();
    cgraph_process_new_functions ();
    cgraph_state = CGRAPH_STATE_FINISHED;
  
--- 1313,1318 ----
Index: testsuite/gcc.dg/debug/dwarf2/static1.c
===================================================================
*** testsuite/gcc.dg/debug/dwarf2/static1.c	(revision 0)
--- testsuite/gcc.dg/debug/dwarf2/static1.c	(revision 0)
***************
*** 0 ****
--- 1,8 ----
+ /* { dg-do compile } */
+ /* { dg-options "-O2 -gdwarf-2" } */
+ void
+ main(void)
+ {
+  static int unused_local_var;
+ }
+ /* { dg-final { scan-assembler "unused_local_var" } } */
Index: dwarf2out.c
===================================================================
*** dwarf2out.c	(revision 144489)
--- dwarf2out.c	(working copy)
*************** typedef const struct die_struct *const_d
*** 3405,3410 ****
--- 3405,3422 ----
  typedef struct dw_loc_descr_struct *dw_loc_descr_ref;
  typedef struct dw_loc_list_struct *dw_loc_list_ref;
  
+ typedef struct deferred_locations_struct GTY(()) 
+ {
+   tree variable;
+   dw_die_ref die;
+ } deferred_locations;
+ 
+ DEF_VEC_O(deferred_locations);
+ DEF_VEC_ALLOC_O(deferred_locations,gc);
+ 
+ /* List of DIEs where we should set location after compiling current unit.  */
+ static GTY(()) VEC(deferred_locations, gc) *deferred_locations_list;
+ 
  /* Each DIE may have a series of attribute/value pairs.  Values
     can take on several forms.  The forms that are used in this
     implementation are listed below.  */
*************** add_location_or_const_value_attribute (d
*** 11858,11863 ****
--- 11869,11883 ----
    tree_add_const_value_attribute (die, decl);
  }
  
+ /* Add VARIABLE and DIE into deferred locations list.  */
+ 
+ static void
+ deffer_location (tree variable, dw_die_ref die)
+ {
+   deferred_locations entry = {variable, die};
+   VEC_safe_push (deferred_locations, gc, deferred_locations_list, &entry);
+ }
+ 
  /* Helper function for tree_add_const_value_attribute.  Natively encode
     initializer INIT into an array.  Return true if successful.  */
  
*************** gen_variable_die (tree decl, dw_die_ref 
*** 14054,14060 ****
  
    if (! declaration && ! DECL_ABSTRACT (decl))
      {
!       add_location_or_const_value_attribute (var_die, decl, DW_AT_location);
        add_pubname (decl, var_die);
      }
    else
--- 14074,14084 ----
  
    if (! declaration && ! DECL_ABSTRACT (decl))
      {
!       if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl)
!           && !TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
! 	deffer_location (decl, var_die);
!       else
!         add_location_or_const_value_attribute (var_die, decl, DW_AT_location);
        add_pubname (decl, var_die);
      }
    else
*************** decls_for_scope (tree stmt, dw_die_ref c
*** 14934,14949 ****
  
  	  if (die != NULL && die->die_parent == NULL)
  	    add_child_die (context_die, die);
- 	  /* Do not produce debug information for static variables since
- 	     these might be optimized out.  We are called for these later
- 	     in varpool_analyze_pending_decls.
- 
- 	     But *do* produce it for Fortran COMMON variables because,
- 	     even though they are static, their names can differ depending
- 	     on the scope, which we need to preserve.  */
- 	  if (TREE_CODE (decl) == VAR_DECL && TREE_STATIC (decl)
- 	      && !(is_fortran () && TREE_PUBLIC (decl)))
- 	    ;
  	  else if (TREE_CODE (decl) == IMPORTED_DECL)
  	    dwarf2out_imported_module_or_decl_1 (decl, DECL_NAME (decl),
  						 stmt, context_die);
--- 14975,14980 ----
*************** dwarf2out_finish (const char *filename)
*** 16443,16448 ****
--- 16474,16480 ----
  {
    limbo_die_node *node, *next_node;
    dw_die_ref die = 0;
+   unsigned int i;
  
    /* Add the name for the main input file now.  We delayed this from
       dwarf2out_init to avoid complications with PCH.  */
*************** dwarf2out_finish (const char *filename)
*** 16457,16462 ****
--- 16489,16502 ----
  	add_comp_dir_attribute (comp_unit_die);
      }
  
+   for (i = 0; i < VEC_length (deferred_locations, deferred_locations_list); i++)
+     {
+       add_location_or_const_value_attribute (
+         VEC_index (deferred_locations, deferred_locations_list, i)->die,
+         VEC_index (deferred_locations, deferred_locations_list, i)->variable,
+ 	DW_AT_location);
+     }
+ 
    /* Traverse the limbo die list, and add parent/child links.  The only
       dies without parents that should be here are concrete instances of
       inline functions, and the comp_unit_die.  We can ignore the comp_unit_die.
Index: varpool.c
===================================================================
*** varpool.c	(revision 144489)
--- varpool.c	(working copy)
*************** varpool_empty_needed_queue (void)
*** 456,484 ****
    varpool_last_needed_node = NULL;
  }
  
- /* Output all variables enqueued to be assembled.  */
- void
- varpool_output_debug_info (void)
- {
-   timevar_push (TV_SYMOUT);
-   if (errorcount == 0 && sorrycount == 0)
-     while (varpool_assembled_nodes_queue)
-       {
- 	struct varpool_node *node = varpool_assembled_nodes_queue;
- 
- 	/* Local static variables are never seen by check_global_declarations
- 	   so we need to output debug info by hand.  */
- 	if (DECL_CONTEXT (node->decl)
- 	    && (TREE_CODE (DECL_CONTEXT (node->decl)) == BLOCK
- 		|| TREE_CODE (DECL_CONTEXT (node->decl)) == FUNCTION_DECL)
- 	    && errorcount == 0 && sorrycount == 0)
- 	     (*debug_hooks->global_decl) (node->decl);
- 	varpool_assembled_nodes_queue = node->next_needed;
- 	node->next_needed = 0;
-       }
-   timevar_pop (TV_SYMOUT);
- }
- 
  /* Create a new global variable of type TYPE.  */
  tree
  add_new_static_var (tree type)
--- 456,461 ----



More information about the Gcc-patches mailing list