Hi!

This patch optimizes away unneeded DW_OP_GNU_converts.  mem_loc_descriptor
attempts to keep the operands signed when it returns, if next op
needs it unsigned again with the same size, there might be useless
converts.  The patch won't change DW_OP_GNU_convert to integral from
non-integral (so that say float to {un,}signed conversion is done with the
right sign), for other converts will change if possible preceeding typed
op's base type if size is the same, both the typed op and following
DW_OP_GNU_convert are integral or have the same encoding.
Example testcase which is improved is e.g.:
/* { dg-do run } */
/* { dg-options "-g" } */

volatile int vv;

__attribute__((noclone, noinline)) void
foo (double d)
{
  unsigned long f = ((unsigned long) d) / 33UL;
  vv++;         /* { dg-final { gdb-test 10 "f" "7" } } */
}

int
main ()
{
  foo (231.0);
  return 0;
}
where previously we emitted
DW_OP_GNU_regval_type <xmm0, double> DW_OP_GNU_convert <ulong>
DW_OP_GNU_convert <long> DW_OP_GNU_convert <ulong> DW_OP_const1u <33>
DW_OP_GNU_convert <ulong> DW_OP_div DW_OP_GNU_convert <long>
while with this patch DW_OP_GNU_convert <long> DW_OP_GNU_convert <ulong>
can go away.

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

2011-05-17  Jakub Jelinek  <ja...@redhat.com>

        * dwarf2out.c (resolve_addr_in_expr): Optimize away redundant
        DW_OP_GNU_convert ops.

--- gcc/dwarf2out.c.jj  2011-05-17 13:35:26.000000000 +0200
+++ gcc/dwarf2out.c     2011-05-17 14:41:21.000000000 +0200
@@ -24092,23 +24092,84 @@ resolve_one_addr (rtx *addr, void *data 
 static bool
 resolve_addr_in_expr (dw_loc_descr_ref loc)
 {
+  dw_loc_descr_ref keep = NULL;
   for (; loc; loc = loc->dw_loc_next)
-    if (((loc->dw_loc_opc == DW_OP_addr || loc->dtprel)
-        && resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL))
-       || (loc->dw_loc_opc == DW_OP_implicit_value
-           && 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)
+    switch (loc->dw_loc_opc)
       {
-       dw_die_ref ref
-         = lookup_decl_die (loc->dw_loc_oprnd1.v.val_decl_ref);
-       if (ref == NULL)
+      case DW_OP_addr:
+       if (resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, 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;
+       break;
+      case DW_OP_const4u:
+      case DW_OP_const8u:
+       if (loc->dtprel
+           && resolve_one_addr (&loc->dw_loc_oprnd1.v.val_addr, NULL))
+         return false;
+       break;
+      case DW_OP_implicit_value:
+       if (loc->dw_loc_oprnd2.val_class == dw_val_class_addr
+           && resolve_one_addr (&loc->dw_loc_oprnd2.v.val_addr, NULL))
+         return false;
+       break;
+      case DW_OP_GNU_implicit_pointer:
+       if (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;
+         }
+       break;
+      case DW_OP_GNU_const_type:
+      case DW_OP_GNU_regval_type:
+      case DW_OP_GNU_deref_type:
+      case DW_OP_GNU_convert:
+      case DW_OP_GNU_reinterpret:
+       while (loc->dw_loc_next
+              && loc->dw_loc_next->dw_loc_opc == DW_OP_GNU_convert)
+         {
+           dw_die_ref base1, base2;
+           unsigned enc1, enc2, size1, size2;
+           if (loc->dw_loc_opc == DW_OP_GNU_regval_type
+               || loc->dw_loc_opc == DW_OP_GNU_deref_type)
+             base1 = loc->dw_loc_oprnd2.v.val_die_ref.die;
+           else
+             base1 = loc->dw_loc_oprnd1.v.val_die_ref.die;
+           base2 = loc->dw_loc_next->dw_loc_oprnd1.v.val_die_ref.die;
+           gcc_assert (base1->die_tag == DW_TAG_base_type
+                       && base2->die_tag == DW_TAG_base_type);
+           enc1 = get_AT_unsigned (base1, DW_AT_encoding);
+           enc2 = get_AT_unsigned (base2, DW_AT_encoding);
+           size1 = get_AT_unsigned (base1, DW_AT_byte_size);
+           size2 = get_AT_unsigned (base2, DW_AT_byte_size);
+           if (size1 == size2
+               && (((enc1 == DW_ATE_unsigned || enc1 == DW_ATE_signed)
+                    && (enc2 == DW_ATE_unsigned || enc2 == DW_ATE_signed)
+                    && loc != keep)
+                   || enc1 == enc2))
+             {
+               /* Optimize away next DW_OP_GNU_convert after
+                  adjusting LOC's base type die reference.  */
+               if (loc->dw_loc_opc == DW_OP_GNU_regval_type
+                   || loc->dw_loc_opc == DW_OP_GNU_deref_type)
+                 loc->dw_loc_oprnd2.v.val_die_ref.die = base2;
+               else
+                 loc->dw_loc_oprnd1.v.val_die_ref.die = base2;
+               loc->dw_loc_next = loc->dw_loc_next->dw_loc_next;
+               continue;
+             }
+           /* Don't change integer DW_OP_GNU_convert after e.g. floating
+              point typed stack entry.  */
+           else if (enc1 != DW_ATE_unsigned && enc1 != DW_ATE_signed)
+             keep = loc;
+           break;
+         }
+       break;
+      default:
+       break;
       }
   return true;
 }

        Jakub

Reply via email to