From: Ian Lance Taylor Date: Fri, 6 Feb 1998 03:42:05 +0000 (+0000) Subject: * ehopt.c: New file. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ffd652c31307010b25c9a137a8ecff9773e4253a;p=binutils-gdb.git * 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. --- diff --git a/gas/.Sanitize b/gas/.Sanitize index 5003bcb3f43..6226e7dfe65 100644 --- a/gas/.Sanitize +++ b/gas/.Sanitize @@ -63,6 +63,7 @@ depend.c doc ecoff.c ecoff.h +ehopt.c emul-target.h emul.h expr.c diff --git a/gas/ChangeLog b/gas/ChangeLog index ccac0d68f4e..9411dfba20a 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,5 +1,24 @@ Thu Feb 5 12:39:08 1998 Ian Lance Taylor + * 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. diff --git a/gas/Makefile.am b/gas/Makefile.am index 310e6c3b521..06c360ad51c 100644 --- a/gas/Makefile.am +++ b/gas/Makefile.am @@ -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 \ diff --git a/gas/Makefile.in b/gas/Makefile.in index a7225956924..cc1058b3da5 100644 --- a/gas/Makefile.in +++ b/gas/Makefile.in @@ -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 diff --git a/gas/NEWS b/gas/NEWS index 2542e2e0184..6bd74d5b3d8 100644 --- 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 diff --git a/gas/as.c b/gas/as.c index 984bf246c84..3bbf2d52fa6 100644 --- 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); diff --git a/gas/as.h b/gas/as.h index d86e8bf51b8..8dfbe3c5f75 100644 --- 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 */ diff --git a/gas/doc/as.1 b/gas/doc/as.1 index b005117b883..adf28868eac 100644 --- a/gas/doc/as.1 +++ b/gas/doc/as.1 @@ -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 index 00000000000..d339a305412 --- /dev/null +++ b/gas/ehopt.c @@ -0,0 +1,378 @@ +/* ehopt.c--optimize gcc exception frame information. + Copyright (C) 1998 Free Software Foundation, Inc. + Written by Ian Lance Taylor . + +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; +}