public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* Reducing eh_frame table size
@ 1997-11-17 14:21 Bruno Haible
  0 siblings, 0 replies; 3+ messages in thread
From: Bruno Haible @ 1997-11-17 14:21 UTC (permalink / raw)
  To: egcs

Here is a patch which starts reducing the eh_table size. On a sample
C executable compiled with -fomit-frame-pointer, on i486-linux, I got
the following figures:

before patch:         text: 568920    eh_frame: 308732 (+ 54%)
after patch:          text: 568920    eh_frame: 173796 (+ 31%)

that is, a reduction of the eh_frame section by 44%.

The patch does the following:
  1. When there are two DW_CFA_advance_loc4 entries with no call between
     them, the latter one is removed. Recall that exceptions can only
     be thrown through function calls.
  2. When the tail of the CFIs (the ones after the last DW_CFA_advance_loc4)
     refers to a code section with no calls, it is removed.
  3. Introduce a short 1-byte opcode for a pretty common combination of
     DW_CFA_def_cfa_offset and DW_CFA_GNU_args_size.

*** testgcc-971111/frame.c.bak	Thu Nov 13 00:46:54 1997
--- testgcc-971111/frame.c	Sun Nov 16 18:12:44 1997
***************
*** 503,508 ****
--- 503,514 ----
        state->s.args_size = offset;
        break;
  
+     case DW_CFA_GNU_push_argword:
+       /* A combination of DW_CFA_def_cfa_offset and DW_CFA_GNU_args_size. */
+       state->s.cfa_offset += sizeof (long);
+       state->s.args_size += sizeof (long);
+       break;
+ 
      default:
        abort ();
      }
*** testgcc-971111/dwarf2.h.bak	Thu Nov  6 23:48:55 1997
--- testgcc-971111/dwarf2.h	Sun Nov 16 18:13:18 1997
***************
*** 500,506 ****
  
      /* GNU extensions */
      DW_CFA_GNU_window_save = 0x2d,
!     DW_CFA_GNU_args_size = 0x2e
    };
  
  #define DW_CIE_ID	  0xffffffff
--- 500,507 ----
  
      /* GNU extensions */
      DW_CFA_GNU_window_save = 0x2d,
!     DW_CFA_GNU_args_size = 0x2e,
!     DW_CFA_GNU_push_argword = 0x2f
    };
  
  #define DW_CIE_ID	  0xffffffff
*** testgcc-971111/dwarf2out.c.bak	Thu Nov  6 23:48:57 1997
--- testgcc-971111/dwarf2out.c	Sun Nov 16 18:17:12 1997
***************
*** 44,49 ****
--- 44,53 ----
  /* #define NDEBUG 1 */
  #include "assert.h"
  
+ #ifndef LONG_TYPE_SIZE
+ #define LONG_TYPE_SIZE BITS_PER_WORD
+ #endif
+ 
  /* Decide whether we want to emit frame unwind information for the current
     translation unit.  */
  
***************
*** 91,96 ****
--- 95,102 ----
    enum dwarf_call_frame_info dw_cfi_opc;
    dw_cfi_oprnd dw_cfi_oprnd1;
    dw_cfi_oprnd dw_cfi_oprnd2;
+   int dw_cfi_pushp : 1;
+   int dw_cfi_seen_call : 1;
  }
  dw_cfi_node;
  
***************
*** 104,109 ****
--- 110,116 ----
  {
    char *dw_fde_begin;
    char *dw_fde_current_label;
+   int dw_fde_seen_call;
    char *dw_fde_end;
    dw_cfi_ref dw_fde_cfi;
  }
***************
*** 637,642 ****
--- 653,660 ----
        return "DW_CFA_GNU_window_save";
      case DW_CFA_GNU_args_size:
        return "DW_CFA_GNU_args_size";
+     case DW_CFA_GNU_push_argword:
+       return "DW_CFA_GNU_push_argword";
  
      default:
        return "DW_CFA_<unknown>";
***************
*** 703,709 ****
  	label = dwarf2out_cfi_label ();
  
        if (fde->dw_fde_current_label == NULL
! 	  || strcmp (label, fde->dw_fde_current_label) != 0)
  	{
  	  register dw_cfi_ref xcfi;
  
--- 721,728 ----
  	label = dwarf2out_cfi_label ();
  
        if (fde->dw_fde_current_label == NULL
! 	  || strcmp (label, fde->dw_fde_current_label) != 0
! 	     && fde->dw_fde_seen_call)
  	{
  	  register dw_cfi_ref xcfi;
  
***************
*** 713,719 ****
--- 732,741 ----
  	  xcfi = new_cfi ();
  	  xcfi->dw_cfi_opc = DW_CFA_advance_loc4;
  	  xcfi->dw_cfi_oprnd1.dw_cfi_addr = label;
+ 	  xcfi->dw_cfi_seen_call = fde->dw_fde_seen_call;
  	  add_cfi (&fde->dw_fde_cfi, xcfi);
+ 
+ 	  fde->dw_fde_seen_call = 0;
  	}
  
        add_cfi (&fde->dw_fde_cfi, cfi);
***************
*** 814,819 ****
--- 836,843 ----
      {
        cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
        cfi->dw_cfi_oprnd1.dw_cfi_offset = offset;
+       cfi->dw_cfi_pushp =
+ 	(offset - old_offset == LONG_TYPE_SIZE / BITS_PER_UNIT);
      }
  
  #ifndef MIPS_DEBUGGING_INFO  /* SGI dbx thinks this means no offset.  */
***************
*** 1097,1102 ****
--- 1121,1132 ----
        return;
      }
  
+   /* Since exceptions can only occur through call instructions, when doing
+      for_eh we can remove advance_loc CFIs corresponding to ranges without
+      call instructions. */
+   if (GET_CODE (insn) == CALL_INSN)
+     fde_table[fde_table_in_use - 1].dw_fde_seen_call = 1;
+ 
    if (! RTX_FRAME_RELATED_P (insn))
      {
        dwarf2out_stack_adjust (insn);
***************
*** 1241,1246 ****
--- 1271,1317 ----
      }
  }
  
+ /* Combine adjacent CFIs, to reduce the size of the eh table. */
+ static void
+ peephole_fde (fde)
+     dw_fde_node *fde;
+ {
+   register dw_cfi_node *curr, **pcurr;
+   register dw_cfi_node *next;
+ 
+   for (pcurr = &fde->dw_fde_cfi; (curr = *pcurr); )
+     {
+       next = curr->dw_cfi_next;
+       if (curr->dw_cfi_opc == DW_CFA_advance_loc4
+           && ! curr->dw_cfi_seen_call)
+         {
+           *pcurr = next;
+           continue;
+         }
+       if (curr->dw_cfi_opc == DW_CFA_def_cfa_offset
+           && curr->dw_cfi_pushp
+           && next->dw_cfi_opc == DW_CFA_GNU_args_size)
+         {
+           curr->dw_cfi_opc = DW_CFA_GNU_push_argword;
+           curr->dw_cfi_next = next->dw_cfi_next;
+           continue;
+         }
+       pcurr = &curr->dw_cfi_next;
+     }
+ 
+   /* If there are no calls after the last advance, the trailing CFIs can
+      be removed. */
+   if (! fde->dw_fde_seen_call)
+     {
+       pcurr = &fde->dw_fde_cfi;
+       curr = *pcurr;
+       for (next = curr; next; next = next->dw_cfi_next)
+         if (next->dw_cfi_opc == DW_CFA_advance_loc4)
+           pcurr = &next->dw_cfi_next;
+       *pcurr = NULL;
+     }
+ }
+ 
  /* Return the size of an unsigned LEB128 quantity.  */
  
  static inline unsigned long
***************
*** 1453,1458 ****
--- 1527,1534 ----
  	  output_uleb128 (cfi->dw_cfi_oprnd1.dw_cfi_offset);
            fputc ('\n', asm_out_file);
  	  break;
+ 	case DW_CFA_GNU_push_argword:
+ 	  break;
  	default:
  	  break;
  	}
***************
*** 1601,1606 ****
--- 1677,1686 ----
      {
        fde = &fde_table[i];
  
+       /* Reduce the size of the CFI table. */
+       if (for_eh)
+ 	peephole_fde (fde);
+ 
        ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + i*2);
        ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + i*2);
        if (for_eh)
***************
*** 1693,1698 ****
--- 1773,1779 ----
    fde = &fde_table[fde_table_in_use++];
    fde->dw_fde_begin = xstrdup (label);
    fde->dw_fde_current_label = NULL;
+   fde->dw_fde_seen_call = 0;
    fde->dw_fde_end = NULL;
    fde->dw_fde_cfi = NULL;
  

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Reducing eh_frame table size
  1997-11-17 13:49 Bruno Haible
@ 1997-11-17 14:21 ` Ian Lance Taylor
  0 siblings, 0 replies; 3+ messages in thread
From: Ian Lance Taylor @ 1997-11-17 14:21 UTC (permalink / raw)
  To: haible; +Cc: egcs

   From: Bruno Haible <haible@ilog.fr>
   Date: Mon, 17 Nov 1997 21:39:17 +0100 (MET)

   Can we hope that this will be implemented in gas in the near future?

I certainly think this would be a good idea.  I personally am quite
busy on other projects.  However, as always, I am happy to accept
contributed patches, and to answer questions about how such patches
should be implemented.

I've appended a note from Jason Merrill about various dwarf2
optimizations that could be implemented in the assembler and linker.

Ian

Date: Mon, 15 Sep 1997 19:18:43 -0700
From: Jason Merrill <jason@cygnus.com>

Now that the dwarf2 EH unwinder code is in gcc, it would be nice to have
the assembler and/or linker do some optimization thereof.  The unwind
information lives in .eh_frame, a loaded section, and looks like the
following.  The most obvious optimizations are sharing of CIEs and
shrinking offset fields.  

Each FDE (function descriptor) contains a pointer to the CIE for that
translation unit, but there can be more or fewer than one CIE per TU.
Currently all CIEs are the same, so they could all be merged into a single
CIE.

Because the compiler doesn't know how large insns are, and because it
doesn't know about relaxations, it uses five bytes (opcode + 4byte delta)
for the PC advance insns; this can be reduced to one, two or three bytes
depending on the actual delta.

It would also be nice to factor out common insn patterns into the CIE;
currently the only insns in the CIE set up the initial CFA and return addr
location, but since most prologues start the same way all those insns could
be moved into the CIE.

Just a future direction...

Jason

__FRAME_BEGIN__:
	.4byte	.LECIE1-.LSCIE1	/ Length of Common Information Entry
.LSCIE1:
	.4byte	0xffffffff	/ CIE Identifier Tag
	.byte	0x1	/ CIE Version
	.ascii "e\0"	/ CIE Augmentation
	.byte	0x1	/ ULEB128 0x1 (CIE Code Alignment Factor)
	.byte	0x7c	/ SLEB128 -4 (CIE Data Alignment Factor)
	.byte	0x8	/ CIE RA Column
	.byte	0xc	/ DW_CFA_def_cfa
	.byte	0x4	/ ULEB128 0x4
	.byte	0x4	/ ULEB128 0x4
	.byte	0x88	/ DW_CFA_offset, column 0x8
	.byte	0x1	/ ULEB128 0x1
	.align 4
.LECIE1:
	.4byte	.LEFDE1-.LSFDE1	/ FDE Length
.LSFDE1:
	.4byte	__FRAME_BEGIN__	/ FDE CIE offset
	.4byte	.LFB1	/ FDE initial location
	.4byte	.LFE1-.LFB1	/ FDE address range
	.4byte	__EXCEPTION_TABLE__	/ pointer to exception region info
	.byte	0x4	/ DW_CFA_advance_loc4
	.4byte	.LCFI0-.LFB1
	.byte	0xe	/ DW_CFA_def_cfa_offset
	.byte	0x8	/ ULEB128 0x8
	.byte	0x85	/ DW_CFA_offset, column 0x5
	.byte	0x2	/ ULEB128 0x2
	.byte	0x4	/ DW_CFA_advance_loc4
	.4byte	.LCFI1-.LCFI0
	.byte	0xd	/ DW_CFA_def_cfa_register
	.byte	0x5	/ ULEB128 0x5
	.byte	0x4	/ DW_CFA_advance_loc4
	.4byte	.LCFI2-.LCFI1
	.byte	0x2e	/ DW_CFA_GNU_args_size
	.byte	0x4	/ ULEB128 0x4
	.byte	0x4	/ DW_CFA_advance_loc4
	.4byte	.LCFI3-.LCFI2
	.byte	0x2e	/ DW_CFA_GNU_args_size
	.byte	0x0	/ ULEB128 0x0
	.align 4
.LEFDE1:


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Reducing eh_frame table size
@ 1997-11-17 13:49 Bruno Haible
  1997-11-17 14:21 ` Ian Lance Taylor
  0 siblings, 1 reply; 3+ messages in thread
From: Bruno Haible @ 1997-11-17 13:49 UTC (permalink / raw)
  To: egcs, Ian Lance Taylor

Hi,

The size of exception handling tables emitted by g++ in DWARF2 format
could be reduced by about 20% if the following modification was made in
the assembler, for the targets supporting the ELF object format.

A new pseudo-opcode ".dwarf_advance" is introduced. Its argument its
a difference between two labels in the same segmemt (like the 2nd argument
of the ".size" pseudo-op). It expands as follows:

        .dwarf_advance Label2-Label1
==>
        .if Label2-Label1 < 0x10000
        .if Label2-Label1 < 0x100
        .if Label2-Label1 < 0x40
        .byte   Label2-Label1+0x40
        .else
        .byte   0x2
        .byte   Label2-Label1
        .endif
        .else
        .byte   0x3
        .2byte  Label2-Label1
        .endif
        .else
        .byte   0x4
        .4byte  Label2-Label1
        .endif

Unfortunately, the .if expansion doesn't work as is, because Label2-Label1
is not a constant expression; its value is only at the end of the assembly.
One can assume that Label1 and Label2 are in the same segment, and that the
segment containing the .dwarf_advance op is different from this.

As I see it, gas would need just a few modifications to write.c:relax_segment()
in order to support this.

Can we hope that this will be implemented in gas in the near future?

Bruno

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~1997-11-17 14:21 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1997-11-17 14:21 Reducing eh_frame table size Bruno Haible
  -- strict thread matches above, loose matches on Subject: below --
1997-11-17 13:49 Bruno Haible
1997-11-17 14:21 ` Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).