[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 (®_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