diff mbox

DW_OP_GNU_implicit_pointer support

Message ID 20100812080522.GL702@tyan-ft48-01.lab.bos.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Aug. 12, 2010, 8:05 a.m. UTC
Hi!

This patch implements the GCC side of implicit pointer DWARF extension,
which allows optimized out pointers to be partially usable in debug info
consumers.
For details see the proposal Roland sent last night to Dwarf-Discuss
mailing list.  In short, the user can dereference such pointers in the
debugger, even when what they are pointing to doesn't live in memory (can
live in registers, or have constant values, or piecewise mix thereof),
can query their type (that was possible before already), but can't
print their value.  In cc1plus .debug_info/.debug_loc this
DW_OP_GNU_implicit_pointer op is used over 22000 times.

Ok for trunk (assuming nobody suggests some encoding change on Dwarf-Discuss
within a few days - as DWARF 5 is probably years away, we want to implement
this as a GNU vendor extension and if it is accepted for DWARF 5, switch
over to the standard op when DWARF 5 is drafted or released, but if possible
we'd like to just switch to a different op code instead of switching
semantics as well)?

	Jakub
2010-08-12  Jakub Jelinek  <jakub@redhat.com>

	* rtl.def (DEBUG_IMPLICIT_PTR): New rtl code.
	* rtl.h (DEBUG_IMPLICIT_PTR_DECL): Define.
	* rtl.c (rtx_equal_p_cb, rtx_equal_p): Handle DEBUG_IMPLICIT_PTR.
	* print-rtl.c (print_rtx): Likewise.
	* cselib.c (rtx_equal_for_cselib_p, cselib_hash_rtx): Likewise.
	* cfgexpand.c (expand_debug_expr): Generate DEBUG_IMPLICIT_PTR
	for ADDR_EXPR with non-addressable object.
	* dwarf2out.c (enum dw_val_class): Add dw_val_class_decl_ref.
	(struct dw_val_struct): Add v.val_decl_ref.
	(dwarf_stack_op_name, output_loc_operands, output_loc_operands_raw):
	Handle DW_OP_GNU_implicit_pointer.
	(size_of_loc_descr): Likewise.  Fix up DW_OP_call_ref size.
	(get_ref_die_offset_label): New function.
	(implicit_ptr_descriptor): New function.
	(mem_loc_descriptor): Handle DEBUG_IMPLICIT_PTR.
	(loc_descriptor): Likewise.
	(gen_variable_die): Put even definitions into decl_die_table.
	(resolve_addr_in_expr): Resolve still unresolved
	DW_OP_GNU_implicit_pointer operands, if it can't be resolved
	return false.
	(dwarf2out_finish): Call output_location_lists after outputting
	.debug_info and .debug_abbrev instead of before.

	* dwarf2.h (DW_OP_GNU_implicit_pointer): New.

2010-08-12  Roland McGrath  <roland@redhat.com>

	* dwarf2out.c (DWARF_REF_SIZE): Define.
	(size_of_loc_descr): Use it for DW_OP_call_ref.
struct S
{
  int *x, y;
};

int u[6];

static inline void
add (struct S *a, struct S *b, int c)
{
  *a->x += *b->x;
  a->y += b->y;
  u[c + 0]++;
  a = (struct S *) 0;
  u[c + 1]++;
  a = b;
  u[c + 2]++;
}

int
foo (int i)
{
  int j = i;
  struct S p[2] = { {&i, i * 2}, {&j, j * 2} };
  add (&p[0], &p[1], 0);
  p[0].x = &j;
  p[1].x = &i;
  add (&p[0], &p[1], 3);
  return i + j;
}

int
bar (int i)
{
  int *j = &i;
  int **k = &j;
  int ***l = &k;
  i++;
  return i;
}

Comments

Jason Merrill Sept. 8, 2010, 9:30 p.m. UTC | #1
OK.

Jason
Jack Howarth Sept. 10, 2010, 1:31 a.m. UTC | #2
On Thu, Aug 12, 2010 at 10:05:22AM +0200, Jakub Jelinek wrote:
> Hi!
> 
> This patch implements the GCC side of implicit pointer DWARF extension,
> which allows optimized out pointers to be partially usable in debug info
> consumers.
> For details see the proposal Roland sent last night to Dwarf-Discuss
> mailing list.  In short, the user can dereference such pointers in the
> debugger, even when what they are pointing to doesn't live in memory (can
> live in registers, or have constant values, or piecewise mix thereof),
> can query their type (that was possible before already), but can't
> print their value.  In cc1plus .debug_info/.debug_loc this
> DW_OP_GNU_implicit_pointer op is used over 22000 times.
> 
> Ok for trunk (assuming nobody suggests some encoding change on Dwarf-Discuss
> within a few days - as DWARF 5 is probably years away, we want to implement
> this as a GNU vendor extension and if it is accepted for DWARF 5, switch
> over to the standard op when DWARF 5 is drafted or released, but if possible
> we'd like to just switch to a different op code instead of switching
> semantics as well)?
> 
> 	Jakub

Jakub,
   This patch, committed as r164050, appears to be leaking the new DW_OP_GNU_implicit_pointer
opcode past gstrict-dwarf on darwin. Dominique Dhumieres discovered that the fortran testcase...

logical function foo(a, x)
  integer,intent(in) :: a
  integer,intent(out) :: x

  foo = .false.
  if (a > 0) then
    foo = .true.
    x = a
  end if
end function foo

program test
  logical :: foo
  integer :: a, x

  a = 1
  if (foo (a,x)) print *, x
end program test

compiled with " -O3 -g -save-temps" and the resulting executable processed with
dsymutil crashes that utility. The Apple dsymutil maintainer looked at the object files
for this testcase and commented...

> Looks like you the FSF gcc is using custom DW_OP operands as we can see if the parameter definition for "a":
>
> 0x000000fa:             TAG_formal_parameter [11]
>                         AT_abstract_origin( {0x0000006d} ( "a" ) )
>                         AT_location( Unknown DW_OP constant: 0xf2UNKNOWN OP ò )
>
> the DW_OP value for 0xf2 is between DW_OP_lo_user and DW_OP_hi_user. This means dsymutil doesn't know if it needs to do any fixups on the location because it doesn't
> understand the opcode 0xf2 and know if it has any operands.  dsymutil should really just not touch any locations that it doesn't understand, but it doesn't
> currently do that. The assertion is in the code to make sure we didn't start parsing off in the weeds and is causing the crash.
>
> Do you know what custom DW_OP values the FSF gcc is using?

Any suggestions on how to suppress this new opcode for strict dwarf with dwarf2?
               Jack
diff mbox

Patch

--- gcc/cfgexpand.c
+++ gcc/cfgexpand.c
@@ -2904,7 +2904,32 @@  expand_debug_expr (tree exp)
     case ADDR_EXPR:
       op0 = expand_debug_expr (TREE_OPERAND (exp, 0));
       if (!op0 || !MEM_P (op0))
-	return NULL;
+	{
+	  if ((TREE_CODE (TREE_OPERAND (exp, 0)) == VAR_DECL
+	       || TREE_CODE (TREE_OPERAND (exp, 0)) == PARM_DECL
+	       || TREE_CODE (TREE_OPERAND (exp, 0)) == RESULT_DECL)
+	      && !TREE_ADDRESSABLE (TREE_OPERAND (exp, 0)))
+	    return gen_rtx_DEBUG_IMPLICIT_PTR (mode, TREE_OPERAND (exp, 0));
+
+	  if (handled_component_p (TREE_OPERAND (exp, 0)))
+	    {
+	      HOST_WIDE_INT bitoffset, bitsize, maxsize;
+	      tree decl
+		= get_ref_base_and_extent (TREE_OPERAND (exp, 0),
+					   &bitoffset, &bitsize, &maxsize);
+	      if ((TREE_CODE (decl) == VAR_DECL
+		   || TREE_CODE (decl) == PARM_DECL
+		   || TREE_CODE (decl) == RESULT_DECL)
+		  && !TREE_ADDRESSABLE (decl)
+		  && (bitoffset % BITS_PER_UNIT) == 0
+		  && bitsize > 0
+		  && bitsize == maxsize)
+		return plus_constant (gen_rtx_DEBUG_IMPLICIT_PTR (mode, decl),
+				      bitoffset / BITS_PER_UNIT);
+	    }
+
+	  return NULL;
+	}
 
       op0 = convert_debug_memory_address (mode, XEXP (op0, 0));
 
--- gcc/cselib.c
+++ gcc/cselib.c
@@ -700,6 +700,10 @@  rtx_equal_for_cselib_p (rtx x, rtx y)
     case DEBUG_EXPR:
       return 0;
 
+    case DEBUG_IMPLICIT_PTR:
+      return DEBUG_IMPLICIT_PTR_DECL (x)
+	     == DEBUG_IMPLICIT_PTR_DECL (y);
+
     case LABEL_REF:
       return XEXP (x, 0) == XEXP (y, 0);
 
@@ -834,6 +838,11 @@  cselib_hash_rtx (rtx x, int create)
 	      + DEBUG_TEMP_UID (DEBUG_EXPR_TREE_DECL (x));
       return hash ? hash : (unsigned int) DEBUG_EXPR;
 
+    case DEBUG_IMPLICIT_PTR:
+      hash += ((unsigned) DEBUG_IMPLICIT_PTR << 7)
+	      + DECL_UID (DEBUG_IMPLICIT_PTR_DECL (x));
+      return hash ? hash : (unsigned int) DEBUG_IMPLICIT_PTR;
+
     case CONST_INT:
       hash += ((unsigned) CONST_INT << 7) + INTVAL (x);
       return hash ? hash : (unsigned int) CONST_INT;
--- gcc/dwarf2out.c.jj	2010-08-11 10:47:28.083229922 +0200
+++ gcc/dwarf2out.c	2010-08-12 00:54:17.102190031 +0200
@@ -4295,6 +4295,7 @@  enum dw_val_class
   dw_val_class_macptr,
   dw_val_class_file,
   dw_val_class_data8,
+  dw_val_class_decl_ref,
   dw_val_class_vms_delta
 };
 
@@ -4333,6 +4334,7 @@  typedef struct GTY(()) dw_val_struct {
       unsigned char GTY ((tag ("dw_val_class_flag"))) val_flag;
       struct dwarf_file_data * GTY ((tag ("dw_val_class_file"))) val_file;
       unsigned char GTY ((tag ("dw_val_class_data8"))) val_data8[8];
+      tree GTY ((tag ("dw_val_class_decl_ref"))) val_decl_ref;
       struct dw_val_vms_delta_union
 	{
 	  char * lbl1;
@@ -4696,6 +4698,8 @@  dwarf_stack_op_name (unsigned int op)
       return "DW_OP_GNU_uninit";
     case DW_OP_GNU_encoded_addr:
       return "DW_OP_GNU_encoded_addr";
+    case DW_OP_GNU_implicit_pointer:
+      return "DW_OP_GNU_implicit_pointer";
 
     default:
       return "OP_<unknown>";
@@ -4799,6 +4803,9 @@  loc_list_plus_const (dw_loc_list_ref lis
     loc_descr_plus_const (&d->expr, offset);
 }
 
+#define DWARF_REF_SIZE	\
+  (dwarf_version == 2 ? DWARF2_ADDR_SIZE : DWARF_OFFSET_SIZE)
+
 /* Return the size of a location descriptor.  */
 
 static unsigned long
@@ -4905,12 +4912,15 @@  size_of_loc_descr (dw_loc_descr_ref loc)
       size += 4;
       break;
     case DW_OP_call_ref:
-      size += DWARF2_ADDR_SIZE;
+      size += DWARF_REF_SIZE;
       break;
     case DW_OP_implicit_value:
       size += size_of_uleb128 (loc->dw_loc_oprnd1.v.val_unsigned)
 	      + loc->dw_loc_oprnd1.v.val_unsigned;
       break;
+    case DW_OP_GNU_implicit_pointer:
+      size += DWARF_REF_SIZE + size_of_sleb128 (loc->dw_loc_oprnd2.v.val_int);
+      break;
     default:
       break;
     }
@@ -4947,6 +4957,7 @@  size_of_locs (dw_loc_descr_ref loc)
 }
 
 static HOST_WIDE_INT extract_int (const unsigned char *, unsigned);
+static void get_ref_die_offset_label (char *, dw_die_ref);
 
 /* Output location description stack opcode's operands (if any).  */
 
@@ -5166,6 +5177,17 @@  output_loc_operands (dw_loc_descr_ref lo
 	}
       break;
 
+    case DW_OP_GNU_implicit_pointer:
+      {
+	char label[MAX_ARTIFICIAL_LABEL_BYTES
+		   + HOST_BITS_PER_WIDE_INT / 2 + 2];
+	gcc_assert (val1->val_class == dw_val_class_die_ref);
+	get_ref_die_offset_label (label, val1->v.val_die_ref.die);
+	dw2_asm_output_offset (DWARF_REF_SIZE, label, debug_info_section, NULL);
+	dw2_asm_output_data_sleb128 (val2->v.val_int, NULL);
+      }
+      break;
+
     default:
       /* Other codes have no operands.  */
       break;
@@ -5304,6 +5326,10 @@  output_loc_operands_raw (dw_loc_descr_re
       dw2_asm_output_data_sleb128_raw (val2->v.val_int);
       break;
 
+    case DW_OP_GNU_implicit_pointer:
+      gcc_unreachable ();
+      break;
+
     default:
       /* Other codes have no operands.  */
       break;
@@ -6518,6 +6544,15 @@  is_tagged_type (const_tree type)
 	  || code == QUAL_UNION_TYPE || code == ENUMERAL_TYPE);
 }
 
+/* Set label to debug_info_section_label + die_offset of a DIE reference.  */
+
+static void
+get_ref_die_offset_label (char *label, dw_die_ref ref)
+{
+  sprintf (label, "%s+" HOST_WIDE_INT_PRINT_DEC,
+	   debug_info_section_label, ref->die_offset);
+}
+
 /* Convert a DIE tag into its string name.  */
 
 static const char *
@@ -13655,6 +13690,7 @@  mem_loc_descriptor (rtx rtl, enum machin
     case CONCAT:
     case CONCATN:
     case VAR_LOCATION:
+    case DEBUG_IMPLICIT_PTR:
       expansion_failed (NULL_TREE, rtl,
 			"CONCAT/CONCATN/VAR_LOCATION is handled only by loc_descriptor");
       return 0;
@@ -14244,6 +14280,35 @@  concatn_loc_descriptor (rtx concatn, enu
   return cc_loc_result;
 }
 
+/* Helper function for loc_descriptor.  Return DW_OP_GNU_implicit_pointer
+   for DEBUG_IMPLICIT_PTR RTL.  */
+
+static dw_loc_descr_ref
+implicit_ptr_descriptor (rtx rtl, HOST_WIDE_INT offset)
+{
+  dw_loc_descr_ref ret;
+  dw_die_ref ref;
+
+  gcc_assert (TREE_CODE (DEBUG_IMPLICIT_PTR_DECL (rtl)) == VAR_DECL
+	      || TREE_CODE (DEBUG_IMPLICIT_PTR_DECL (rtl)) == PARM_DECL
+	      || TREE_CODE (DEBUG_IMPLICIT_PTR_DECL (rtl)) == RESULT_DECL);
+  ref = lookup_decl_die (DEBUG_IMPLICIT_PTR_DECL (rtl));
+  ret = new_loc_descr (DW_OP_GNU_implicit_pointer, 0, offset);
+  ret->dw_loc_oprnd2.val_class = dw_val_class_const;
+  if (ref)
+    {
+      ret->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+      ret->dw_loc_oprnd1.v.val_die_ref.die = ref;
+      ret->dw_loc_oprnd1.v.val_die_ref.external = 0;
+    }
+  else
+    {
+      ret->dw_loc_oprnd1.val_class = dw_val_class_decl_ref;
+      ret->dw_loc_oprnd1.v.val_decl_ref = DEBUG_IMPLICIT_PTR_DECL (rtl);
+    }
+  return ret;
+}
+
 /* Output a proper Dwarf location descriptor for a variable or parameter
    which is either allocated in a register or in a memory location.  For a
    register, we just generate an OP_REG and the register number.  For a
@@ -14461,6 +14526,19 @@  loc_descriptor (rtx rtl, enum machine_mo
 	}
       break;
 
+    case DEBUG_IMPLICIT_PTR:
+      loc_result = implicit_ptr_descriptor (rtl, 0);
+      break;
+
+    case PLUS:
+      if (GET_CODE (XEXP (rtl, 0)) == DEBUG_IMPLICIT_PTR
+	  && CONST_INT_P (XEXP (rtl, 1)))
+	{
+	  loc_result
+	    = implicit_ptr_descriptor (XEXP (rtl, 0), INTVAL (XEXP (rtl, 1)));
+	  break;
+	}
+      /* FALLTHRU */
     default:
       if (GET_MODE_CLASS (mode) == MODE_INT && GET_MODE (rtl) == mode
 	  && GET_MODE_SIZE (GET_MODE (rtl)) <= DWARF2_ADDR_SIZE
@@ -19210,7 +19288,7 @@  gen_variable_die (tree decl, tree origin
   if (declaration)
     add_AT_flag (var_die, DW_AT_declaration, 1);
 
-  if (decl && (DECL_ABSTRACT (decl) || declaration))
+  if (decl && (DECL_ABSTRACT (decl) || declaration || old_die == NULL))
     equate_decl_number_to_die (decl, var_die);
 
   if (! declaration
@@ -22172,6 +22250,17 @@  resolve_addr_in_expr (dw_loc_descr_ref l
 	    && loc->dw_loc_oprnd2.val_class == dw_val_class_addr
 	    && resolve_one_addr (&loc->dw_loc_oprnd2.v.val_addr, NULL)))
       return false;
+    else if (loc->dw_loc_opc == DW_OP_GNU_implicit_pointer
+	     && loc->dw_loc_oprnd1.val_class == dw_val_class_decl_ref)
+      {
+	dw_die_ref ref
+	  = lookup_decl_die (loc->dw_loc_oprnd1.v.val_decl_ref);
+	if (ref == NULL)
+	  return false;
+	loc->dw_loc_oprnd1.val_class = dw_val_class_die_ref;
+	loc->dw_loc_oprnd1.v.val_die_ref.die = ref;
+	loc->dw_loc_oprnd1.v.val_die_ref.external = 0;
+      }
   return true;
 }
 
@@ -22451,17 +22540,6 @@  dwarf2out_finish (const char *filename)
 	add_ranges (NULL);
     }
 
-  /* Output location list section if necessary.  */
-  if (have_location_lists)
-    {
-      /* Output the location lists info.  */
-      switch_to_section (debug_loc_section);
-      ASM_GENERATE_INTERNAL_LABEL (loc_section_label,
-				   DEBUG_LOC_SECTION_LABEL, 0);
-      ASM_OUTPUT_LABEL (asm_out_file, loc_section_label);
-      output_location_lists (die);
-    }
-
   if (debug_info_level >= DINFO_LEVEL_NORMAL)
     add_AT_lineptr (comp_unit_die, DW_AT_stmt_list,
 		    debug_line_section_label);
@@ -22503,6 +22581,17 @@  dwarf2out_finish (const char *filename)
   switch_to_section (debug_abbrev_section);
   output_abbrev_section ();
 
+  /* Output location list section if necessary.  */
+  if (have_location_lists)
+    {
+      /* Output the location lists info.  */
+      switch_to_section (debug_loc_section);
+      ASM_GENERATE_INTERNAL_LABEL (loc_section_label,
+				   DEBUG_LOC_SECTION_LABEL, 0);
+      ASM_OUTPUT_LABEL (asm_out_file, loc_section_label);
+      output_location_lists (die);
+    }
+
   /* Output public names table if necessary.  */
   if (!VEC_empty (pubname_entry, pubname_table))
     {
--- gcc/print-rtl.c
+++ gcc/print-rtl.c
@@ -529,7 +529,10 @@  print_rtx (const_rtx in_rtx)
 
       case 't':
 #ifndef GENERATOR_FILE
-	dump_addr (outfile, " ", XTREE (in_rtx, i));
+	if (i == 0 && GET_CODE (in_rtx) == DEBUG_IMPLICIT_PTR)
+	  print_mem_expr (outfile, DEBUG_IMPLICIT_PTR_DECL (in_rtx));
+	else
+	  dump_addr (outfile, " ", XTREE (in_rtx, i));
 #endif
 	break;
 
--- gcc/rtl.c
+++ gcc/rtl.c
@@ -407,6 +407,10 @@  rtx_equal_p_cb (const_rtx x, const_rtx y, rtx_equal_p_callback_function cb)
     case CONST_FIXED:
       return 0;
 
+    case DEBUG_IMPLICIT_PTR:
+      return DEBUG_IMPLICIT_PTR_DECL (x)
+	     == DEBUG_IMPLICIT_PTR_DECL (y);
+
     default:
       break;
     }
@@ -527,6 +531,10 @@  rtx_equal_p (const_rtx x, const_rtx y)
     case CONST_FIXED:
       return 0;
 
+    case DEBUG_IMPLICIT_PTR:
+      return DEBUG_IMPLICIT_PTR_DECL (x)
+	     == DEBUG_IMPLICIT_PTR_DECL (y);
+
     default:
       break;
     }
--- gcc/rtl.def
+++ gcc/rtl.def
@@ -711,6 +711,10 @@  DEF_RTL_EXPR(US_TRUNCATE, "us_truncate", "e", RTX_UNARY)
    initialization status of variables.  */
 DEF_RTL_EXPR(VAR_LOCATION, "var_location", "tei", RTX_EXTRA)
 
+/* Used in VAR_LOCATION for a pointer to a decl that is no longer
+   addressable.  */
+DEF_RTL_EXPR(DEBUG_IMPLICIT_PTR, "debug_implicit_ptr", "t", RTX_OBJ)
+
 /* All expressions from this point forward appear only in machine
    descriptions.  */
 #ifdef GENERATOR_FILE
--- gcc/rtl.h
+++ gcc/rtl.h
@@ -931,6 +931,9 @@  extern const char * const reg_note_name[];
 /* DEBUG_EXPR_DECL corresponding to a DEBUG_EXPR RTX.  */
 #define DEBUG_EXPR_TREE_DECL(RTX) XCTREE (RTX, 0, DEBUG_EXPR)
 
+/* VAR_DECL/PARM_DECL DEBUG_IMPLICIT_PTR takes address of.  */
+#define DEBUG_IMPLICIT_PTR_DECL(RTX) XCTREE (RTX, 0, DEBUG_IMPLICIT_PTR)
+
 /* Possible initialization status of a variable.   When requested
    by the user, this information is tracked and recorded in the DWARF
    debug information, along with the variable's location.  */
--- include/dwarf2.h
+++ include/dwarf2.h
@@ -622,6 +622,7 @@  enum dwarf_location_atom
     /* The following is for marking variables that are uninitialized.  */
     DW_OP_GNU_uninit     = 0xf0,
     DW_OP_GNU_encoded_addr = 0xf1,
+    DW_OP_GNU_implicit_pointer = 0xf2,
     /* HP extensions.  */
     DW_OP_HP_unknown     = 0xe0, /* Ouch, the same as GNU_push_tls_address.  */
     DW_OP_HP_is_value    = 0xe1,