[PATCH] Avoid emitting invalid DWARF location expressions for DECL_BY_REFERENCE decls and in unwind info

Jakub Jelinek jakub@redhat.com
Wed May 6 11:12:00 GMT 2009


Hi!

As discussed in
https://bugzilla.redhat.com/show_bug.cgi?id=481675
and on the dwarf-discuss mailining list afterwards, GCC emits invalid DWARF
location expressions.
The standard says that DW_OP_reg{0,...,31,x} can only appear alone in
location expression, or followed by DW_OP_{,bit_}piece, but GCC uses it
in several places as a shorthand to DW_OP_bregX 0 (one byte shorter).
As one short example, try e.g. -g -O2 -dA
function foo (f)
integer :: f, foo
foo = f
end
where this patch changes:
 	.uleb128 0x3	# (DIE (0x50) DW_TAG_formal_parameter)
 	.ascii "f\0"	# DW_AT_name
 	.byte	0x1	# DW_AT_decl_file (t.f90)
 	.byte	0x1	# DW_AT_decl_line
	.long	0x67	# DW_AT_type
-	.byte	0x2	# DW_AT_location
-	.byte	0x55	# DW_OP_reg5
- 	.byte	0x6	# DW_OP_deref
+	.byte	0x3	# DW_AT_location
+	.byte	0x75	# DW_OP_breg5
+	.sleb128 0
Without it gdb complains:
foo (f=DWARF-2 expression error: DW_OP_reg operations must be used either alone or in conjuction with DW_OP_piece.
) at t.f90:1
1	function foo (f)
The following patch removes all the places where DW_OP_regX was used
as a shorthand to DW_OP_bregX 0 (mostly unwind info, DRAP, ...), leaving
one_reg_loc_descriptor the only function which creates DW_OP_regX. 
loc_descriptor is only called from loc_descriptor_from_tree_1
with want_address == 2, so generally that won't be followed by anything
but DW_OP_{,bit_}piece or DW_OP_GNU_uninit, except for DECL_BY_REFERENCE.
loc_by_reference needs fixing too, because it was appending DW_OP_deref
after loc_descriptor_from_tree result.  As DW_OP_regX alone means
value is in a register, while anything else means compute the location
expression value to find out memory address where the object is stored,
we want to change DW_OP_regX in that case into DW_OP_bregX 0 and
not append DW_OP_deref.

In addition to the above Fortran testcase the patch also fixes the
C++ -g -O2 -dA -std=c++0x testcase from the above mentioned bugzilla.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2009-05-06  Jakub Jelinek  <jakub@redhat.com>

	* dwarf2out.c (new_reg_loc_descr): Don't ever create DW_OP_regX.
	(one_reg_loc_descriptor): Create DW_OP_regX here instead of calling
	new_reg_loc_descr.
	(loc_by_reference): If loc is DW_OP_regX, change it into DW_OP_bregX 0
	instead of appending DW_OP_deref*.

--- gcc/dwarf2out.c.jj	2009-05-04 16:46:33.000000000 +0200
+++ gcc/dwarf2out.c	2009-05-06 10:47:03.000000000 +0200
@@ -3851,18 +3851,11 @@ new_loc_descr (enum dwarf_location_atom 
 static inline dw_loc_descr_ref
 new_reg_loc_descr (unsigned int reg,  unsigned HOST_WIDE_INT offset)
 {
-  if (offset)
-    {
-      if (reg <= 31)
-	return new_loc_descr ((enum dwarf_location_atom) (DW_OP_breg0 + reg),
-			      offset, 0);
-      else
-	return new_loc_descr (DW_OP_bregx, reg, offset);
-    }
-  else if (reg <= 31)
-    return new_loc_descr ((enum dwarf_location_atom) (DW_OP_reg0 + reg), 0, 0);
+  if (reg <= 31)
+    return new_loc_descr ((enum dwarf_location_atom) (DW_OP_breg0 + reg),
+			  offset, 0);
   else
-   return new_loc_descr (DW_OP_regx, reg, 0);
+    return new_loc_descr (DW_OP_bregx, reg, offset);
 }
 
 /* Add a location description term to a location description expression.  */
@@ -9702,7 +9695,13 @@ reg_loc_descriptor (rtx rtl, enum var_in
 static dw_loc_descr_ref
 one_reg_loc_descriptor (unsigned int regno, enum var_init_status initialized)
 {
-  dw_loc_descr_ref reg_loc_descr = new_reg_loc_descr (regno, 0);
+  dw_loc_descr_ref reg_loc_descr;
+
+  if (regno <= 31)
+    reg_loc_descr
+      = new_loc_descr ((enum dwarf_location_atom) (DW_OP_reg0 + regno), 0, 0);
+  else
+    reg_loc_descr = new_loc_descr (DW_OP_regx, regno, 0);
 
   if (initialized == VAR_INIT_STATUS_UNINITIALIZED)
     add_loc_descr (&reg_loc_descr, new_loc_descr (DW_OP_GNU_uninit, 0, 0));
@@ -11719,6 +11718,31 @@ loc_by_reference (dw_loc_descr_ref loc, 
       || !DECL_BY_REFERENCE (decl))
     return loc;
 
+  /* If loc is DW_OP_reg{0...31,x}, don't add DW_OP_deref, instead
+     change it into corresponding DW_OP_breg{0...31,x} 0.  Then the
+     location expression is considered to be address of a memory location,
+     rather than the register itself.  */
+  if (((loc->dw_loc_opc >= DW_OP_reg0 && loc->dw_loc_opc <= DW_OP_reg31)
+       || loc->dw_loc_opc == DW_OP_regx)
+      && (loc->dw_loc_next == NULL
+	  || (loc->dw_loc_next->dw_loc_opc == DW_OP_GNU_uninit
+	      && loc->dw_loc_next->dw_loc_next == NULL)))
+    {
+      if (loc->dw_loc_opc == DW_OP_regx)
+	{
+	  loc->dw_loc_opc = DW_OP_bregx;
+	  loc->dw_loc_oprnd2.v.val_int = 0;
+	}
+      else
+	{
+	  loc->dw_loc_opc
+	    = (enum dwarf_location_atom)
+	      (loc->dw_loc_opc + (DW_OP_breg0 - DW_OP_reg0));
+	  loc->dw_loc_oprnd1.v.val_int = 0;
+	}
+      return loc;
+    }
+
   size = int_size_in_bytes (TREE_TYPE (decl));
   if (size > DWARF2_ADDR_SIZE || size == -1)
     return 0;

	Jakub



More information about the Gcc-patches mailing list