* ehopt.c: New file.
authorIan Lance Taylor <ian@airs.com>
Fri, 6 Feb 1998 03:42:05 +0000 (03:42 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 6 Feb 1998 03:42:05 +0000 (03:42 +0000)
* as.h (enum _relax_state): Add rs_cfa.
(check_eh_frame, eh_frame_estimate_size_before_relax): Declare.
(eh_frame_relax_frag, eh_frame_convert_frag): Declare.
* read.c (emit_expr): Call check_eh_frame.
* write.c (cvt_frag_to_fill): Handle rs_cfa.
(relax_segment): Likewise.
* Makefile.am: Rebuild dependencies.
  (GAS_CFILES): Add ehopt.c.
(GENERIC_OBJS): Add ehopt.o.
* doc/internals.texi (Frags): Document rs_cfa.

* as.c (show_usage): Mention --traditional-format.
(parse_args): Accept --traditional-format.
* as.h (flag_traditional_format): Declare.
* output-file.c (output_file_create): If flag_traditional_format,
set BFD_TRADITIONAL_FORMAT on stdoutput.
* doc/as.texinfo, doc/as.1: Document --traditional-format.

gas/.Sanitize
gas/ChangeLog
gas/Makefile.am
gas/Makefile.in
gas/NEWS
gas/as.c
gas/as.h
gas/doc/as.1
gas/ehopt.c [new file with mode: 0644]

index 5003bcb3f43c528236fae7e1436b0e4e7a47678f..6226e7dfe65f07dbb519f2eef180189b70739808 100644 (file)
@@ -63,6 +63,7 @@ depend.c
 doc
 ecoff.c
 ecoff.h
+ehopt.c
 emul-target.h
 emul.h
 expr.c
index ccac0d68f4e9b1e125aa1a449ce208d915de0832..9411dfba20ab6122aaf090a53e57d4ef1b5ac1f6 100644 (file)
@@ -1,5 +1,24 @@
 Thu Feb  5 12:39:08 1998  Ian Lance Taylor  <ian@cygnus.com>
 
+       * ehopt.c: New file.
+       * as.h (enum _relax_state): Add rs_cfa.
+       (check_eh_frame, eh_frame_estimate_size_before_relax): Declare.
+       (eh_frame_relax_frag, eh_frame_convert_frag): Declare.
+       * read.c (emit_expr): Call check_eh_frame.
+       * write.c (cvt_frag_to_fill): Handle rs_cfa.
+       (relax_segment): Likewise.
+       * Makefile.am: Rebuild dependencies.
+       (GAS_CFILES): Add ehopt.c.
+       (GENERIC_OBJS): Add ehopt.o.
+       * doc/internals.texi (Frags): Document rs_cfa.
+
+       * as.c (show_usage): Mention --traditional-format.
+       (parse_args): Accept --traditional-format.
+       * as.h (flag_traditional_format): Declare.
+       * output-file.c (output_file_create): If flag_traditional_format,
+       set BFD_TRADITIONAL_FORMAT on stdoutput.
+       * doc/as.texinfo, doc/as.1: Document --traditional-format.
+
        * config/tc-mips.c (append_insn): Make sure that if we have a
        fixup for an unmatched %hi reloc, it does not associated with a
        variant frag.
index 310e6c3b521637c4b732cae171ce975cceb370ca..06c360ad51cb12d21fa08d8f85f5c628e81ff6b7 100644 (file)
@@ -128,6 +128,7 @@ GAS_CFILES = \
        cond.c \
        depend.c \
        ecoff.c \
+       ehopt.c \
        expr.c \
        flonum-copy.c \
        flonum-konst.c \
@@ -315,6 +316,7 @@ GENERIC_OBJS = \
        bignum-copy.o \
        cond.o \
        depend.o \
+       ehopt.o \
        expr.o \
        flonum-konst.o \
        flonum-copy.o \
index a722595692484599be9c6ee43f7516835549f212..cc1058b3da55f13212d2f99532a5295fef8d6f6a 100644 (file)
@@ -203,6 +203,7 @@ GAS_CFILES = \
        cond.c \
        depend.c \
        ecoff.c \
+       ehopt.c \
        expr.c \
        flonum-copy.c \
        flonum-konst.c \
@@ -390,6 +391,7 @@ GENERIC_OBJS = \
        bignum-copy.o \
        cond.o \
        depend.o \
+       ehopt.o \
        expr.o \
        flonum-konst.o \
        flonum-copy.o \
@@ -1191,10 +1193,10 @@ itbl_test_DEPENDENCIES =  itbl-test-ops.o itbl-test.o \
 ../libiberty/libiberty.a
 itbl_test_LDFLAGS = 
 as_new_OBJECTS =  app.o as.o atof-generic.o bignum-copy.o cond.o \
-depend.o ecoff.o expr.o flonum-copy.o flonum-konst.o flonum-mult.o \
-frags.o hash.o input-file.o input-scrub.o listing.o literal.o macro.o \
-messages.o output-file.o read.o sb.o stabs.o subsegs.o symbols.o \
-write.o
+depend.o ecoff.o ehopt.o expr.o flonum-copy.o flonum-konst.o \
+flonum-mult.o frags.o hash.o input-file.o input-scrub.o listing.o \
+literal.o macro.o messages.o output-file.o read.o sb.o stabs.o \
+subsegs.o symbols.o write.o
 as_new_LDFLAGS = 
 gasp_new_OBJECTS =  gasp.o macro.o sb.o hash.o
 gasp_new_DEPENDENCIES =  ../libiberty/libiberty.a
index 2542e2e018472858bc5576896e9ce3e98b44b0bb..6bd74d5b3d8698d3dec568cd516ac834a880f904 100644 (file)
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -4,6 +4,9 @@ Changes in 2.9:
 
 Texas Instruction c30 (tms320c30) support added.
 
+The assembler now optimizes the exception frame information generated by egcs
+and gcc 2.8.  The new --traditional-format disables this optimization.
+
 Added --gstabs option to generates stabs debugging information.
 
 The -a option takes a new suboption, m (e.g., -alm) to expand macros in a
index 984bf246c84057fc9f40067ec0003fcbaa4be23b..3bbf2d52fa6e94a4185c08d44e3cac36b0b27f23 100644 (file)
--- a/gas/as.c
+++ b/gas/as.c
@@ -1,5 +1,5 @@
 /* as.c - GAS main program.
-   Copyright (C) 1987, 90, 91, 92, 93, 94, 95, 96, 1997
+   Copyright (C) 1987, 90, 91, 92, 93, 94, 95, 96, 97, 1998
    Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
@@ -159,6 +159,7 @@ Options:\n\
 -R                     fold data section into text section\n\
 --statistics           print various measured statistics from execution\n\
 --strip-local-absolute strip local absolute symbols\n\
+--traditional-format   Use same format as native assembler when possible\n\
 --version              print assembler version number and exit\n\
 -W                     suppress warnings\n\
 --itbl INSTTBL         extend instruction set to include instructions\n\
@@ -356,7 +357,9 @@ parse_args (pargc, pargv)
 #define OPTION_GSTABS (OPTION_STD_BASE + 14)
     {"gstabs", no_argument, NULL, OPTION_GSTABS},
 #define OPTION_STRIP_LOCAL_ABSOLUTE (OPTION_STD_BASE + 15)
-    {"strip-local-absolute", no_argument, NULL, OPTION_STRIP_LOCAL_ABSOLUTE}
+    {"strip-local-absolute", no_argument, NULL, OPTION_STRIP_LOCAL_ABSOLUTE},
+#define OPTION_TRADITIONAL_FORMAT (OPTION_STD_BASE + 16)
+    {"traditional-format", no_argument, NULL, OPTION_TRADITIONAL_FORMAT}
   };
 
   /* Construct the option lists from the standard list and the
@@ -441,6 +444,10 @@ parse_args (pargc, pargv)
          flag_strip_local_absolute = 1;
          break;
 
+       case OPTION_TRADITIONAL_FORMAT:
+         flag_traditional_format = 1;
+         break;
+
        case OPTION_VERSION:
          /* This output is intended to follow the GNU standards document.  */
          printf ("GNU assembler %s\n", VERSION);
index d86e8bf51b8131b3ab43d309414f5fe3a2f988cc..8dfbe3c5f754d4b924f74017263a2de2470b2c5e 100644 (file)
--- a/gas/as.h
+++ b/gas/as.h
@@ -381,7 +381,10 @@ enum _relax_state
 
     /* A DWARF leb128 value; only ELF uses this.  The subtype is 0 for
        unsigned, 1 for signed.  */
-    rs_leb128
+    rs_leb128,
+
+    /* Exception frame information which we may be able to optimize.  */
+    rs_cfa
   };
 
 typedef enum _relax_state relax_stateT;
@@ -432,6 +435,9 @@ COMMON unsigned char flag_print_statistics;
 /* True if local absolute symbols are to be stripped.  */
 COMMON int flag_strip_local_absolute;
 
+/* True if we should generate a traditional format object file.  */
+COMMON int flag_traditional_format;
+
 /* name of emitted object file */
 COMMON char *out_file_name;
 
@@ -585,6 +591,11 @@ typedef struct frag fragS;
 valueT add_to_literal_pool PARAMS ((struct symbol *, valueT, segT, int));
 #endif
 
+int check_eh_frame PARAMS ((struct expressionS *, unsigned int *));
+int eh_frame_estimate_size_before_relax PARAMS ((fragS *));
+int eh_frame_relax_frag PARAMS ((fragS *));
+void eh_frame_convert_frag PARAMS ((fragS *));
+
 #include "expr.h"              /* Before targ-*.h */
 
 /* this one starts the chain of target dependant headers */
index b005117b883691b85f856d8bd16c5972891ac6e6..adf28868eac174285ac83a55d582dee4052f9816 100644 (file)
@@ -1,4 +1,4 @@
-.\" Copyright (c) 1991, 1992, 1996, 1997 Free Software Foundation
+.\" Copyright (c) 1991, 1992, 1996, 1997, 1998 Free Software Foundation
 .\" See section COPYING for conditions for redistribution
 .TH as 1 "29 March 1996" "cygnus support" "GNU Development Tools"
 
@@ -26,6 +26,7 @@ GNU as \- the portable GNU assembler.
 .I objfile\c
 \&\|]
 .RB "[\|" \-R "\|]"
+.RB "[\|" \-\-traditional\-format "\|]"
 .RB "[\|" \-v "\|]"
 .RB "[\|" \-w "\|]"
 .RB "[\|" \-\^\- "\ |\ " \c
@@ -186,6 +187,9 @@ Name the object-file output from \c
 .B \-R
 Fold data section into text section
 .TP
+.B \-\-traditional\-format
+Use same format as native assembler, when possible.
+.TP
 .B \-v
 Announce \c
 .B as\c
diff --git a/gas/ehopt.c b/gas/ehopt.c
new file mode 100644 (file)
index 0000000..d339a30
--- /dev/null
@@ -0,0 +1,378 @@
+/* ehopt.c--optimize gcc exception frame information.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor <ian@cygnus.com>.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+
+/* We include this ELF file, even though we may not be assembling for
+   ELF, since the exception frame information is always in a format
+   derived from DWARF.  */
+
+#include "elf/dwarf2.h"
+
+/* Try to optimize gcc 2.8 exception frame information.
+
+   Exception frame information is emitted for every function in the
+   .eh_frame section.  Simple information for a function with no
+   exceptions looks like this:
+
+__FRAME_BEGIN__:
+       .4byte  .LLCIE1 / Length of Common Information Entry
+.LSCIE1:
+       .4byte  0x0     / CIE Identifier Tag
+       .byte   0x1     / CIE Version
+       .byte   0x0     / CIE Augmentation (none)
+       .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:
+       .set    .LLCIE1,.LECIE1-.LSCIE1 / CIE Length Symbol
+       .4byte  .LLFDE1 / FDE Length
+.LSFDE1:
+       .4byte  .LSFDE1-__FRAME_BEGIN__ / FDE CIE offset
+       .4byte  .LFB1   / FDE initial location
+       .4byte  .LFE1-.LFB1     / FDE address range
+       .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:
+       .set    .LLFDE1,.LEFDE1-.LSFDE1 / FDE Length Symbol
+
+   The immediate issue we can address in the assembler is the
+   DW_CFA_advance_loc4 followed by a four byte value.  The value is
+   the difference of two addresses in the function.  Since gcc does
+   not know this value, it always uses four bytes.  We will know the
+   value at the end of assembly, so we can do better.  */
+
+static int eh_frame_code_alignment PARAMS ((void));
+
+/* Get the code alignment factor from the CIE.  */
+
+static int
+eh_frame_code_alignment ()
+{
+  static int code_alignment;
+  segT current_seg;
+  subsegT current_subseg;
+  fragS *f;
+  int offset;
+
+  if (code_alignment != 0)
+    return code_alignment;
+
+  /* We should find the CIE at the start of the .eh_frame section.  */
+
+  current_seg = now_seg;
+  current_subseg = now_subseg;
+  subseg_new (".eh_frame", 0);
+  f = seg_info (now_seg)->frchainP->frch_root;
+  subseg_set (current_seg, current_subseg);
+
+  /* Look through the frags of the section to find the code alignment.  */
+
+  /* First make sure that the CIE Identifier Tag is 0.  */
+
+  offset = 4;
+  while (f != NULL && offset >= f->fr_fix)
+    {
+      offset -= f->fr_fix;
+      f = f->fr_next;
+    }
+  if (f == NULL
+      || f->fr_fix - offset < 4
+      || f->fr_literal[offset] != 0
+      || f->fr_literal[offset + 1] != 0
+      || f->fr_literal[offset + 2] != 0
+      || f->fr_literal[offset + 3] != 0)
+    {
+      code_alignment = -1;
+      return -1;
+    }
+
+  /* Next make sure the CIE version number is 1.  */
+
+  offset += 4;
+  while (f != NULL && offset >= f->fr_fix)
+    {
+      offset -= f->fr_fix;
+      f = f->fr_next;
+    }
+  if (f == NULL
+      || f->fr_fix - offset < 1
+      || f->fr_literal[offset] != 1)
+    {
+      code_alignment = -1;
+      return -1;
+    }
+
+  /* Skip the augmentation (a null terminated string).  */
+
+  ++offset;
+  while (1)
+    {
+      while (f != NULL && offset >= f->fr_fix)
+       {
+         offset -= f->fr_fix;
+         f = f->fr_next;
+       }
+      if (f == NULL)
+       {
+         code_alignment = -1;
+         return -1;
+       }
+      while (offset < f->fr_fix && f->fr_literal[offset] != '\0')
+       ++offset;
+      if (offset < f->fr_fix)
+       break;
+    }
+  ++offset;
+  while (f != NULL && offset >= f->fr_fix)
+    {
+      offset -= f->fr_fix;
+      f = f->fr_next;
+    }
+  if (f == NULL)
+    {
+      code_alignment = -1;
+      return -1;
+    }
+
+  /* We're now at the code alignment factor, which is a ULEB128.  If
+     it isn't a single byte, forget it.  */
+
+  code_alignment = f->fr_literal[offset] & 0xff;
+  if ((code_alignment & 0x80) != 0 || code_alignment == 0)
+    {
+      code_alignment = -1;
+      return -1;
+    }
+
+  return code_alignment;
+}
+
+/* This function is called from emit_expr.  It looks for cases which
+   we can optimize.
+
+   Rather than try to parse all this information as we read it, we
+   look for a single byte DW_CFA_advance_loc4 followed by a 4 byte
+   difference.  We turn that into a rs_cfa_advance frag, and handle
+   those frags at the end of the assembly.  If the gcc output changes
+   somewhat, this optimization may stop working.
+
+   This function returns non-zero if it handled the expression and
+   emit_expr should not do anything, or zero otherwise.  It can also
+   change *EXP and *PNBYTES.  */
+
+int
+check_eh_frame (exp, pnbytes)
+     expressionS *exp;
+     unsigned int *pnbytes;
+{
+  static int saw_advance_loc4;
+  static fragS *loc4_frag;
+  static int loc4_fix;
+
+  if (flag_traditional_format)
+    {
+      /* Don't optimize.  */
+    }
+  else if (strcmp (segment_name (now_seg), ".eh_frame") != 0)
+    saw_advance_loc4 = 0;
+  else if (*pnbytes == 1
+          && exp->X_op == O_constant
+          && exp->X_add_number == DW_CFA_advance_loc4)
+    {
+      /* This might be a DW_CFA_advance_loc4.  Record the frag and the
+         position within the frag, so that we can change it later.  */
+      saw_advance_loc4 = 1;
+      frag_grow (1);
+      loc4_frag = frag_now;
+      loc4_fix = frag_now_fix ();
+    }
+  else if (saw_advance_loc4
+          && *pnbytes == 4
+          && exp->X_op == O_constant)
+    {
+      int ca;
+
+      /* This is a case which we can optimize.  The two symbols being
+         subtracted were in the same frag and the expression was
+         reduced to a constant.  We can do the optimization entirely
+         in this function.  */
+
+      saw_advance_loc4 = 0;
+
+      ca = eh_frame_code_alignment ();
+      if (ca < 0)
+       {
+         /* Don't optimize.  */
+       }
+      else if (exp->X_add_number % ca == 0
+         && exp->X_add_number / ca < 0x40)
+       {
+         loc4_frag->fr_literal[loc4_fix]
+           = DW_CFA_advance_loc | (exp->X_add_number / ca);
+         /* No more bytes needed.  */
+         return 1;
+       }
+      else if (exp->X_add_number < 0x100)
+       {
+         loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
+         *pnbytes = 1;
+       }
+      else if (exp->X_add_number < 0x10000)
+       {
+         loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
+         *pnbytes = 2;
+       }
+    }
+  else if (saw_advance_loc4
+          && *pnbytes == 4
+          && exp->X_op == O_subtract)
+    {
+
+      /* This is a case we can optimize.  The expression was not
+         reduced, so we can not finish the optimization until the end
+         of the assembly.  We set up a variant frag which we handle
+         later.  */
+
+      saw_advance_loc4 = 0;
+
+      frag_var (rs_cfa, 4, 0, 0, make_expr_symbol (exp),
+               loc4_fix, (char *) loc4_frag);
+    }
+  else
+    saw_advance_loc4 = 0;
+
+  return 0;
+}
+
+/* The function estimates the size of a rs_cfa variant frag based on
+   the current values of the symbols.  It is called before the
+   relaxation loop.  We set fr_subtype to the expected length.  */
+
+int
+eh_frame_estimate_size_before_relax (frag)
+     fragS *frag;
+{
+  int ca;
+  offsetT diff;
+  int ret;
+
+  ca = eh_frame_code_alignment ();
+  diff = resolve_symbol_value (frag->fr_symbol, 0);
+
+  if (ca < 0)
+    ret = 4;
+  else if (diff % ca == 0 && diff / ca < 0x40)
+    ret = 0;
+  else if (diff < 0x100)
+    ret = 1;
+  else if (diff < 0x10000)
+    ret = 2;
+  else
+    ret = 4;
+
+  frag->fr_subtype = ret;
+
+  return ret;
+}
+
+/* This function relaxes a rs_cfa variant frag based on the current
+   values of the symbols.  fr_subtype is the current length of the
+   frag.  This returns the change in frag length.  */
+
+int
+eh_frame_relax_frag (frag)
+     fragS *frag;
+{
+  int oldsize, newsize;
+
+  oldsize = frag->fr_subtype;
+  newsize = eh_frame_estimate_size_before_relax (frag);
+  return newsize - oldsize;
+}
+
+/* This function converts a rs_cfa variant frag into a normal fill
+   frag.  This is called after all relaxation has been done.
+   fr_subtype will be the desired length of the frag.  */
+
+void
+eh_frame_convert_frag (frag)
+     fragS *frag;
+{
+  offsetT diff;
+  fragS *loc4_frag;
+  int loc4_fix;
+
+  loc4_frag = (fragS *) frag->fr_opcode;
+  loc4_fix = (int) frag->fr_offset;
+
+  diff = resolve_symbol_value (frag->fr_symbol, 1);
+
+  if (frag->fr_subtype == 0)
+    {
+      int ca;
+
+      ca = eh_frame_code_alignment ();
+      assert (ca > 0 && diff % ca == 0 && diff / ca < 0x40);
+      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | (diff / ca);
+    }
+  else if (frag->fr_subtype == 1)
+    {
+      assert (diff < 0x100);
+      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1;
+      frag->fr_literal[frag->fr_fix] = diff;
+    }
+  else if (frag->fr_subtype == 2)
+    {
+      assert (diff < 0x10000);
+      loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2;
+      md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2);
+    }
+  else
+    md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4);
+
+  frag->fr_fix += frag->fr_subtype;
+  frag->fr_type = rs_fill;
+}