Hi!

This patch shrinks .debug_ranges section in cc1plus (and probably most of
other binaries) by ~ 22.5% by removing redundancies in the ranges.
If say a lexical block or inlined subroutine covers the same instruction
as its supercontext scope, they can share .debug_range offsets instead of
adding yet another set of ranges.  This patch handles also the case where
the child scope only covers the last two or more ranges of the supercontext
scope, in that case DW_AT_ranges can point somewhere into the middle of
supercontext ranges.  dwarf2out.c alone doesn't have information where
ranges start and end and if they start/end in adjacent locations, it has
has BLOCK_NUMBERs of the fragments for which labels are emitted.

So this patch introduces a new bit in BLOCK, BLOCK_SAME_RANGE, which,
after it is computed by reorder_blocks and its helpers, indicates that
the particular BLOCK covers the same ranges as its BLOCK_SUPERCONTEXT
(if the length of their BLOCK_FRAGMENT_CHAIN chain is the same) or
that all N fragments of BLOCK cover the last N fragments of
BLOCK_SUPERCONTEXT.  dwarf2out.c then just looks up the parent supercontext,
verifies that the ranges table contains the right data and adds DW_AT_ranges
with corresponding offset, without adding any new content into
.debug_ranges.

BLOCK_SAME_RANGE is first set on each individual fragment (where it
says that there were no non-note insns or other NOTE_INSN_BLOCK_{BEG,END}
in between the supercontext NOTE_INSN_BLOCK_BEG and child
NOTE_INSN_BLOCK_BEG and later in between child NOTE_INSN_BLOCK_END
and supercontext NOTE_INSN_BLOCK_END.  Later on during
blocks_nreverse_all we clear BLOCK_SAME_RANGE bit if the next fragment
exists and has that bit cleared (that way we can just check BLOCK_SAME_RANGE
on the fragment origin BLOCK).  Unfortunately testing of that revealed
that this wasn't sufficient, e.g. if supercontext range had 3 ranges
and child range was equal to the first and last fragment thereof, but not
the middle one, we'd misoptimize it.  So, in order to detect even that
the patch during reorder_blocks_1 doesn't set BLOCK_SUPERCONTEXT to
the supercontext's fragment origin for all fragments, but instead to the
corresponding supercontext fragment it is contained within.  During
blocks_nreverse_all we then additionally clear BLOCK_SAME_RANGE if the
fragment BLOCK_SUPERCONTEXT's BLOCK_FRAGMENT_ORIGIN isn't equal
to its BLOCK_FRAGMENT_ORIGIN's BLOCK_SUPERCONTEXT.  Finally
BLOCK_SUPERCONTEXT is adjusted to point to supercontext fragment origin,
so after reorder_blocks it has the all meaning for all fragments.

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

2012-01-23  Jakub Jelinek  <ja...@redhat.com>

        PR debug/51902
        * tree.h (BLOCK_SAME_RANGE): Define.
        * function.c (block_fragments_nreverse): Clear BLOCK_SAME_RANGE
        if BLOCK_FRAGMENT_CHAIN is non-NULL, but has it cleared.
        Also clear BLOCK_SAME_RANGE if fragment chain's supercontext fragment
        isn't equal to supercontext fragment's fragment chain.
        Adjust BLOCK_SUPERCONTEXT to point to supercontext fragment's
        fragment origin.
        (blocks_nreverse_all): Likewise.
        (reorder_blocks_1): Compute BLOCK_SAME_RANGE bits.  Set
        BLOCK_SUPERCONTEXT to supercontext fragment instead of
        supercontext fragment's fragment origin.
        * dwarf2out.c (add_high_low_attributes): If stmt has the same
        range as its parent (or parents thereof etc.), use the parent's
        DW_AT_ranges value instead of creating a new .debug_ranges range.

--- gcc/tree.h.jj       2012-01-22 16:01:52.549668218 +0100
+++ gcc/tree.h  2012-01-23 15:07:26.738058032 +0100
@@ -2088,6 +2088,9 @@ struct GTY(()) tree_omp_clause {
 #define BLOCK_ABSTRACT_ORIGIN(NODE) (BLOCK_CHECK (NODE)->block.abstract_origin)
 #define BLOCK_ABSTRACT(NODE) (BLOCK_CHECK (NODE)->block.abstract_flag)
 
+/* True if BLOCK has the same ranges as its BLOCK_SUPERCONTEXT.  */
+#define BLOCK_SAME_RANGE(NODE) (BLOCK_CHECK (NODE)->base.nameless_flag)
+
 /* An index number for this block.  These values are not guaranteed to
    be unique across functions -- whether or not they are depends on
    the debugging output format in use.  */
--- gcc/function.c.jj   2012-01-22 16:01:52.678667459 +0100
+++ gcc/function.c      2012-01-23 15:54:06.427002523 +0100
@@ -4005,18 +4005,35 @@ generate_setjmp_warnings (void)
 
 
 /* Reverse the order of elements in the fragment chain T of blocks,
-   and return the new head of the chain (old last element).  */
+   and return the new head of the chain (old last element).
+   In addition to that clear BLOCK_SAME_RANGE flags when needed
+   and adjust BLOCK_SUPERCONTEXT from the super fragment to
+   its super fragment origin.  */
 
 static tree
 block_fragments_nreverse (tree t)
 {
-  tree prev = 0, block, next;
+  tree prev = 0, block, next, prev_super = 0;
+  tree super = BLOCK_SUPERCONTEXT (t);
+  if (BLOCK_FRAGMENT_ORIGIN (super))
+    super = BLOCK_FRAGMENT_ORIGIN (super);
   for (block = t; block; block = next)
     {
       next = BLOCK_FRAGMENT_CHAIN (block);
       BLOCK_FRAGMENT_CHAIN (block) = prev;
+      if ((prev && !BLOCK_SAME_RANGE (prev))
+         || (BLOCK_FRAGMENT_CHAIN (BLOCK_SUPERCONTEXT (block))
+             != prev_super))
+       BLOCK_SAME_RANGE (block) = 0;
+      prev_super = BLOCK_SUPERCONTEXT (block);
+      BLOCK_SUPERCONTEXT (block) = super;
       prev = block;
     }
+  t = BLOCK_FRAGMENT_ORIGIN (t);
+  if (BLOCK_FRAGMENT_CHAIN (BLOCK_SUPERCONTEXT (t))
+      != prev_super)
+    BLOCK_SAME_RANGE (t) = 0;
+  BLOCK_SUPERCONTEXT (t) = super;
   return prev;
 }
 
@@ -4033,11 +4050,15 @@ blocks_nreverse_all (tree t)
     {
       next = BLOCK_CHAIN (block);
       BLOCK_CHAIN (block) = prev;
-      BLOCK_SUBBLOCKS (block) = blocks_nreverse_all (BLOCK_SUBBLOCKS (block));
       if (BLOCK_FRAGMENT_CHAIN (block)
          && BLOCK_FRAGMENT_ORIGIN (block) == NULL_TREE)
-       BLOCK_FRAGMENT_CHAIN (block)
-         = block_fragments_nreverse (BLOCK_FRAGMENT_CHAIN (block));
+       {
+         BLOCK_FRAGMENT_CHAIN (block)
+           = block_fragments_nreverse (BLOCK_FRAGMENT_CHAIN (block));
+         if (!BLOCK_SAME_RANGE (BLOCK_FRAGMENT_CHAIN (block)))
+           BLOCK_SAME_RANGE (block) = 0;
+       }
+      BLOCK_SUBBLOCKS (block) = blocks_nreverse_all (BLOCK_SUBBLOCKS (block));
       prev = block;
     }
   return prev;
@@ -4092,6 +4113,7 @@ static void
 reorder_blocks_1 (rtx insns, tree current_block, VEC(tree,heap) 
**p_block_stack)
 {
   rtx insn;
+  tree prev_beg = NULL_TREE, prev_end = NULL_TREE;
 
   for (insn = insns; insn; insn = NEXT_INSN (insn))
     {
@@ -4105,12 +4127,17 @@ reorder_blocks_1 (rtx insns, tree curren
              gcc_assert (BLOCK_FRAGMENT_ORIGIN (block) == NULL_TREE);
              origin = block;
 
+             if (prev_end)
+               BLOCK_SAME_RANGE (prev_end) = 0;
+             prev_end = NULL_TREE;
+
              /* If we have seen this block before, that means it now
                 spans multiple address regions.  Create a new fragment.  */
              if (TREE_ASM_WRITTEN (block))
                {
                  tree new_block = copy_node (block);
 
+                 BLOCK_SAME_RANGE (new_block) = 0;
                  BLOCK_FRAGMENT_ORIGIN (new_block) = origin;
                  BLOCK_FRAGMENT_CHAIN (new_block)
                    = BLOCK_FRAGMENT_CHAIN (origin);
@@ -4120,6 +4147,11 @@ reorder_blocks_1 (rtx insns, tree curren
                  block = new_block;
                }
 
+             if (prev_beg == current_block && prev_beg)
+               BLOCK_SAME_RANGE (block) = 1;
+
+             prev_beg = origin;
+
              BLOCK_SUBBLOCKS (block) = 0;
              TREE_ASM_WRITTEN (block) = 1;
              /* When there's only one block for the entire function,
@@ -4127,10 +4159,22 @@ reorder_blocks_1 (rtx insns, tree curren
                 will cause infinite recursion.  */
              if (block != current_block)
                {
+                 tree super;
                  if (block != origin)
-                   gcc_assert (BLOCK_SUPERCONTEXT (origin) == current_block);
-
-                 BLOCK_SUPERCONTEXT (block) = current_block;
+                   gcc_assert (BLOCK_SUPERCONTEXT (origin) == current_block
+                               || BLOCK_FRAGMENT_ORIGIN (BLOCK_SUPERCONTEXT
+                                                                     (origin))
+                                  == current_block);
+                 if (VEC_empty (tree, *p_block_stack))
+                   super = current_block;
+                 else
+                   {
+                     super = VEC_last (tree, *p_block_stack);
+                     gcc_assert (super == current_block
+                                 || BLOCK_FRAGMENT_ORIGIN (super)
+                                    == current_block);
+                   }
+                 BLOCK_SUPERCONTEXT (block) = super;
                  BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block);
                  BLOCK_SUBBLOCKS (current_block) = block;
                  current_block = origin;
@@ -4141,8 +4185,20 @@ reorder_blocks_1 (rtx insns, tree curren
            {
              NOTE_BLOCK (insn) = VEC_pop (tree, *p_block_stack);
              current_block = BLOCK_SUPERCONTEXT (current_block);
+             if (BLOCK_FRAGMENT_ORIGIN (current_block))
+               current_block = BLOCK_FRAGMENT_ORIGIN (current_block);
+             prev_beg = NULL_TREE;
+             prev_end = BLOCK_SAME_RANGE (NOTE_BLOCK (insn))
+                        ? NOTE_BLOCK (insn) : NULL_TREE;
            }
        }
+      else
+       {
+         prev_beg = NULL_TREE;
+         if (prev_end)
+           BLOCK_SAME_RANGE (prev_end) = 0;
+         prev_end = NULL_TREE;
+       }
     }
 }
 
--- gcc/dwarf2out.c.jj  2012-01-23 13:58:09.887380014 +0100
+++ gcc/dwarf2out.c     2012-01-23 15:07:26.744058144 +0100
@@ -18104,7 +18104,9 @@ add_high_low_attributes (tree stmt, dw_d
   if (BLOCK_FRAGMENT_CHAIN (stmt)
       && (dwarf_version >= 3 || !dwarf_strict))
     {
-      tree chain;
+      tree chain, superblock = NULL_TREE;
+      dw_die_ref pdie;
+      dw_attr_ref attr = NULL;
 
       if (inlined_function_outer_scope_p (stmt))
        {
@@ -18113,6 +18115,56 @@ add_high_low_attributes (tree stmt, dw_d
          add_AT_lbl_id (die, DW_AT_entry_pc, label);
        }
 
+      /* Optimize duplicate .debug_ranges lists or even tails of
+        lists.  If this BLOCK has same ranges as its supercontext,
+        lookup DW_AT_ranges attribute in the supercontext (and
+        recursively so), verify that the ranges_table contains the
+        right values and use it instead of adding a new .debug_range.  */
+      for (chain = stmt, pdie = die;
+          BLOCK_SAME_RANGE (chain);
+          chain = BLOCK_SUPERCONTEXT (chain))
+       {
+         dw_attr_ref new_attr;
+
+         pdie = pdie->die_parent;
+         if (pdie == NULL)
+           break;
+         if (BLOCK_SUPERCONTEXT (chain) == NULL_TREE)
+           break;
+         new_attr = get_AT (pdie, DW_AT_ranges);
+         if (new_attr == NULL
+             || new_attr->dw_attr_val.val_class != dw_val_class_range_list)
+           break;
+         attr = new_attr;
+         superblock = BLOCK_SUPERCONTEXT (chain);
+       }
+      if (attr != NULL
+         && (ranges_table[attr->dw_attr_val.v.val_offset
+                          / 2 / DWARF2_ADDR_SIZE].num
+             == BLOCK_NUMBER (superblock))
+         && BLOCK_FRAGMENT_CHAIN (superblock))
+       {
+         unsigned long off = attr->dw_attr_val.v.val_offset
+                             / 2 / DWARF2_ADDR_SIZE;
+         unsigned long supercnt = 0, thiscnt = 0;
+         for (chain = BLOCK_FRAGMENT_CHAIN (superblock);
+              chain; chain = BLOCK_FRAGMENT_CHAIN (chain))
+           {
+             ++supercnt;
+             gcc_checking_assert (ranges_table[off + supercnt].num
+                                  == BLOCK_NUMBER (chain));
+           }
+         gcc_checking_assert (ranges_table[off + supercnt + 1].num == 0);
+         for (chain = BLOCK_FRAGMENT_CHAIN (stmt);
+              chain; chain = BLOCK_FRAGMENT_CHAIN (chain))
+           ++thiscnt;
+         gcc_assert (supercnt >= thiscnt);
+         add_AT_range_list (die, DW_AT_ranges,
+                            (off + supercnt - thiscnt)
+                            * 2 * DWARF2_ADDR_SIZE);
+         return;
+       }
+
       add_AT_range_list (die, DW_AT_ranges, add_ranges (stmt));
 
       chain = BLOCK_FRAGMENT_CHAIN (stmt);

        Jakub

Reply via email to