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