aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorHans-Peter Nilsson <hp@axis.com>2003-10-18 15:53:40 +0000
committerHans-Peter Nilsson <hp@axis.com>2003-10-18 15:53:40 +0000
commit88fc725d2fa9234fef229e80472740610172421b (patch)
tree0270b842577e8d62dba87d7a61af5b912ae9c41b /gas/config
parent06335781f67d53f9512551e31195e40d0b584f8f (diff)
downloadfsf-binutils-gdb-88fc725d2fa9234fef229e80472740610172421b.zip
fsf-binutils-gdb-88fc725d2fa9234fef229e80472740610172421b.tar.gz
fsf-binutils-gdb-88fc725d2fa9234fef229e80472740610172421b.tar.bz2
Generate BFD_RELOC_MMIX_PUSHJ_STUBBABLE for PUSHJ when possible.
* doc/c-mmix.texi (MMIX-Opts): Document --no-pushj-stubs and --no-stubs. * config/tc-mmix.c: Include limits.h. Expand on mmix_relax_table comment. (expand_op, mmix_next_semicolon_is_eoln): Fix head comment. (pushj_stubs): New variable. (OPTION_NOPUSHJSTUBS, STATE_PUSHJSTUB, PUSHJSTUB_MIN) (PUSHJSTUB_MAX): New macros. (md_longopts): New options "--no-pushj-stubs" and synonym "--no-stubs". (mmix_relax_table): Handle new entry for STATE_PUSHJSTUB. (md_parse_option): Handle OPTION_NOPUSHJSTUBS. (md_estimate_size_before_relax): Modify STATE_PUSHJ state for PUSHJ stub relaxation. (md_convert_frag): Handle STATE_PUSHJSTUB. (md_apply_fix3): Handle BFD_RELOC_MMIX_PUSHJ_STUBBABLE. (tc_gen_reloc): Ditto. (mmix_md_relax_frag): Handle PUSHJ stub relaxation. * config/tc-mmix.h (TC_SEGMENT_INFO_TYPE): Define. (struct mmix_segment_info_type): New.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-mmix.c212
-rw-r--r--gas/config/tc-mmix.h15
2 files changed, 211 insertions, 16 deletions
diff --git a/gas/config/tc-mmix.c b/gas/config/tc-mmix.c
index 7483159..1e01b7a 100644
--- a/gas/config/tc-mmix.c
+++ b/gas/config/tc-mmix.c
@@ -27,6 +27,7 @@
#include <stdio.h>
+#include <limits.h>
#include "as.h"
#include "subsegs.h"
#include "bfd.h"
@@ -143,7 +144,8 @@ struct mmix_symbol_gregs
this line? */
static int label_without_colon_this_line = 1;
-/* Should we expand operands for external symbols? */
+/* Should we automatically expand instructions into multiple insns in
+ order to generate working code? */
static int expand_op = 1;
/* Should we warn when expanding operands? FIXME: test-cases for when -x
@@ -170,8 +172,12 @@ int mmix_gnu_syntax = 0;
/* Do we globalize all symbols? */
int mmix_globalize_symbols = 0;
+/* When expanding insns, do we want to expand PUSHJ as a call to a stub
+ (or else as a series of insns)? */
+int pushj_stubs = 1;
+
/* Do we know that the next semicolon is at the end of the operands field
- (in mmixal mode; constant 1 in GNU mode)? */
+ (in mmixal mode; constant 1 in GNU mode)? */
int mmix_next_semicolon_is_eoln = 1;
/* Do we have a BSPEC in progress? */
@@ -189,6 +195,7 @@ struct option md_longopts[] =
#define OPTION_GLOBALIZE_SYMBOLS (OPTION_GNU_SYNTAX + 1)
#define OPTION_FIXED_SPEC_REGS (OPTION_GLOBALIZE_SYMBOLS + 1)
#define OPTION_LINKER_ALLOCATED_GREGS (OPTION_FIXED_SPEC_REGS + 1)
+#define OPTION_NOPUSHJSTUBS (OPTION_LINKER_ALLOCATED_GREGS + 1)
{"linkrelax", no_argument, NULL, OPTION_RELAX},
{"no-expand", no_argument, NULL, OPTION_NOEXPAND},
{"no-merge-gregs", no_argument, NULL, OPTION_NOMERGEGREG},
@@ -199,6 +206,8 @@ struct option md_longopts[] =
OPTION_FIXED_SPEC_REGS},
{"linker-allocated-gregs", no_argument, NULL,
OPTION_LINKER_ALLOCATED_GREGS},
+ {"no-pushj-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
+ {"no-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
{NULL, no_argument, NULL, 0}
};
@@ -227,15 +236,27 @@ struct obstack mmix_sym_obstack;
3. PUSHJ
extra length: zero or four insns.
+ Special handling to deal with transition to PUSHJSTUB.
4. JMP
- extra length: zero or four insns. */
+ extra length: zero or four insns.
+
+ 5. GREG
+ special handling, allocates a named global register unless another
+ is within reach for all uses.
+
+ 6. PUSHJSTUB
+ special handling (mostly) for external references; assumes the
+ linker will generate a stub if target is no longer than 256k from
+ the end of the section plus max size of previous stubs. Zero or
+ four insns. */
#define STATE_GETA (1)
#define STATE_BCC (2)
#define STATE_PUSHJ (3)
#define STATE_JMP (4)
#define STATE_GREG (5)
+#define STATE_PUSHJSTUB (6)
/* No fine-grainedness here. */
#define STATE_LENGTH_MASK (1)
@@ -279,6 +300,24 @@ struct obstack mmix_sym_obstack;
#define PUSHJ_4F GETA_3F
#define PUSHJ_4B GETA_3B
+/* We'll very rarely have sections longer than LONG_MAX, but we'll make a
+ feeble attempt at getting 64-bit C99 or gcc-specific values (assuming
+ long long is 64 bits on the host). */
+#ifdef LLONG_MIN
+#define PUSHJSTUB_MIN LLONG_MIN
+#elsif defined (LONG_LONG_MIN)
+#define PUSHJSTUB_MIN LONG_LONG_MIN
+#else
+#define PUSHJSTUB_MIN LONG_MIN
+#endif
+#ifdef LLONG_MAX
+#define PUSHJSTUB_MAX LLONG_MAX
+#elsif defined (LONG_LONG_MAX)
+#define PUSHJSTUB_MAX LONG_LONG_MAX
+#else
+#define PUSHJSTUB_MAX LONG_MAX
+#endif
+
#define JMP_0F (65536 * 256 * 4 - 8)
#define JMP_0B (-65536 * 256 * 4 - 4)
@@ -311,8 +350,8 @@ const relax_typeS mmix_relax_table[] =
{BCC_5F, BCC_5B,
BCC_MAX_LEN - 4, 0},
- /* PUSHJ (3, 0). */
- {PUSHJ_0F, PUSHJ_0B, 0, ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)},
+ /* PUSHJ (3, 0). Next state is actually PUSHJSTUB (6, 0). */
+ {PUSHJ_0F, PUSHJ_0B, 0, ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO)},
/* PUSHJ (3, 1). */
{PUSHJ_4F, PUSHJ_4B,
@@ -326,7 +365,13 @@ const relax_typeS mmix_relax_table[] =
JMP_MAX_LEN - 4, 0},
/* GREG (5, 0), (5, 1), though the table entry isn't used. */
- {0, 0, 0, 0}, {0, 0, 0, 0}
+ {0, 0, 0, 0}, {0, 0, 0, 0},
+
+ /* PUSHJSTUB (6, 0). PUSHJ (3, 0) uses the range, so we set it to infinite. */
+ {PUSHJSTUB_MAX, PUSHJSTUB_MIN,
+ 0, ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)},
+ /* PUSHJSTUB (6, 1) isn't used. */
+ {0, 0, PUSHJ_MAX_LEN, 0}
};
const pseudo_typeS md_pseudo_table[] =
@@ -661,6 +706,10 @@ md_parse_option (c, arg)
allocate_undefined_gregs_in_linker = 1;
break;
+ case OPTION_NOPUSHJSTUBS:
+ pushj_stubs = 0;
+ break;
+
default:
return 0;
}
@@ -2181,12 +2230,27 @@ md_estimate_size_before_relax (fragP, segment)
{
HANDLE_RELAXABLE (STATE_GETA);
HANDLE_RELAXABLE (STATE_BCC);
- HANDLE_RELAXABLE (STATE_PUSHJ);
HANDLE_RELAXABLE (STATE_JMP);
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF):
+ if (fragP->fr_symbol != NULL
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && !S_IS_WEAK (fragP->fr_symbol))
+ /* The symbol lies in the same segment - a relaxable case. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO);
+ else if (pushj_stubs)
+ /* If we're to generate stubs, assume we can reach a stub after
+ the section. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
+ /* FALLTHROUGH. */
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+ case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+ /* We need to distinguish different relaxation rounds. */
+ seg_info (segment)->tc_segment_info_data.last_stubfrag = fragP;
+ break;
+
case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
- case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
/* When relaxing a section for the second time, we don't need to do
anything except making sure that fr_var is set right. */
@@ -2307,6 +2371,16 @@ md_convert_frag (abfd, sec, fragP)
switch (fragP->fr_subtype)
{
+ case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+ /* Setting the unknown bits to 0 seems the most appropriate. */
+ mmix_set_geta_branch_offset (opcodep, 0);
+ tmpfixP = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 8,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_MMIX_PUSHJ_STUBBABLE);
+ COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+ var_part_size = 0;
+ break;
+
case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
@@ -2453,6 +2527,7 @@ md_apply_fix3 (fixP, valP, segment)
case BFD_RELOC_MMIX_GETA:
case BFD_RELOC_MMIX_CBRANCH:
case BFD_RELOC_MMIX_PUSHJ:
+ case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
/* If this fixup is out of range, punt to the linker to emit an
error. This should only happen with -no-expand. */
if (val < -(((offsetT) 1 << 19)/2)
@@ -2665,6 +2740,7 @@ tc_gen_reloc (section, fixP)
case BFD_RELOC_MMIX_PUSHJ_1:
case BFD_RELOC_MMIX_PUSHJ_2:
case BFD_RELOC_MMIX_PUSHJ_3:
+ case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
case BFD_RELOC_MMIX_JMP:
case BFD_RELOC_MMIX_JMP_1:
case BFD_RELOC_MMIX_JMP_2:
@@ -3333,13 +3409,121 @@ mmix_md_relax_frag (seg, fragP, stretch)
fragS *fragP;
long stretch;
{
- if (fragP->fr_subtype != STATE_GREG_DEF
- && fragP->fr_subtype != STATE_GREG_UNDF)
- return relax_frag (seg, fragP, stretch);
+ switch (fragP->fr_subtype)
+ {
+ /* Growth for this type has been handled by mmix_md_end and
+ correctly estimated, so there's nothing more to do here. */
+ case STATE_GREG_DEF:
+ return 0;
- /* If we're defined, we don't grow. */
- if (fragP->fr_subtype == STATE_GREG_DEF)
- return 0;
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+ {
+ /* We need to handle relaxation type ourselves, since relax_frag
+ doesn't update fr_subtype if there's no size increase in the
+ current section; when going from plain PUSHJ to a stub. This
+ is otherwise functionally the same as relax_frag in write.c,
+ simplified for this case. */
+ offsetT aim;
+ addressT target;
+ addressT address;
+ symbolS *symbolP;
+ target = fragP->fr_offset;
+ address = fragP->fr_address;
+ symbolP = fragP->fr_symbol;
+
+ if (symbolP)
+ {
+ fragS *sym_frag;
+
+ sym_frag = symbol_get_frag (symbolP);
+ know (S_GET_SEGMENT (symbolP) != absolute_section
+ || sym_frag == &zero_address_frag);
+ target += S_GET_VALUE (symbolP);
+
+ /* If frag has yet to be reached on this pass, assume it will
+ move by STRETCH just as we did. If this is not so, it will
+ be because some frag between grows, and that will force
+ another pass. */
+
+ if (stretch != 0
+ && sym_frag->relax_marker != fragP->relax_marker
+ && S_GET_SEGMENT (symbolP) == seg)
+ target += stretch;
+ }
+
+ aim = target - address - fragP->fr_fix;
+ if (aim >= PUSHJ_0B && aim <= PUSHJ_0F)
+ {
+ /* Target is reachable with a PUSHJ. */
+ segment_info_type *seginfo = seg_info (seg);
+
+ /* If we're at the end of a relaxation round, clear the stub
+ counter as initialization for the next round. */
+ if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+ seginfo->tc_segment_info_data.nstubs = 0;
+ return 0;
+ }
+
+ /* Not reachable. Try a stub. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
+ }
+ /* FALLTHROUGH. */
+
+ /* See if this PUSHJ is redirectable to a stub. */
+ case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+ {
+ segment_info_type *seginfo = seg_info (seg);
+ fragS *lastfrag = seginfo->frchainP->frch_last;
+ relax_substateT prev_type = fragP->fr_subtype;
+
+ /* The last frag is always an empty frag, so it suffices to look
+ at its address to know the ending address of this section. */
+ know (lastfrag->fr_type == rs_fill
+ && lastfrag->fr_fix == 0
+ && lastfrag->fr_var == 0);
+
+ /* For this PUSHJ to be relaxable into a call to a stub, the
+ distance must be no longer than 256k bytes from the PUSHJ to
+ the end of the section plus the maximum size of stubs so far. */
+ if ((lastfrag->fr_address
+ + stretch
+ + PUSHJ_MAX_LEN * seginfo->tc_segment_info_data.nstubs)
+ - (fragP->fr_address + fragP->fr_fix)
+ > GETA_0F
+ || !pushj_stubs)
+ fragP->fr_subtype = mmix_relax_table[prev_type].rlx_more;
+ else
+ seginfo->tc_segment_info_data.nstubs++;
+
+ /* If we're at the end of a relaxation round, clear the stub
+ counter as initialization for the next round. */
+ if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+ seginfo->tc_segment_info_data.nstubs = 0;
+
+ return
+ (mmix_relax_table[fragP->fr_subtype].rlx_length
+ - mmix_relax_table[prev_type].rlx_length);
+ }
+
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_MAX):
+ {
+ segment_info_type *seginfo = seg_info (seg);
+
+ /* Need to cover all STATE_PUSHJ states to act on the last stub
+ frag (the end of this relax round; initialization for the
+ next). */
+ if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+ seginfo->tc_segment_info_data.nstubs = 0;
+
+ return 0;
+ }
+
+ default:
+ return relax_frag (seg, fragP, stretch);
+
+ case STATE_GREG_UNDF:
+ BAD_CASE (fragP->fr_subtype);
+ }
as_fatal (_("internal: unexpected relax type %d:%d"),
fragP->fr_type, fragP->fr_subtype);
diff --git a/gas/config/tc-mmix.h b/gas/config/tc-mmix.h
index 30d2c95..c6d222f 100644
--- a/gas/config/tc-mmix.h
+++ b/gas/config/tc-mmix.h
@@ -1,5 +1,5 @@
/* tc-mmix.h -- Header file for tc-mmix.c.
- Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
Written by Hans-Peter Nilsson (hp@bitrange.com).
This file is part of GAS, the GNU Assembler.
@@ -123,7 +123,7 @@ extern int mmix_assemble_return_nonzero PARAMS ((char *));
extern const struct relax_type mmix_relax_table[];
#define TC_GENERIC_RELAX_TABLE mmix_relax_table
-/* We use the relax table for everything except the GREG frags. */
+/* We use the relax table for everything except the GREG frags and PUSHJ. */
extern long mmix_md_relax_frag PARAMS ((segT, fragS *, long));
#define md_relax_frag mmix_md_relax_frag
@@ -199,6 +199,17 @@ extern void mmix_frob_file PARAMS ((void));
struct mmix_symbol_gregs;
#define TC_SYMFIELD_TYPE struct mmix_symbol_gregs *
+/* Used by relaxation, counting maximum needed PUSHJ stubs for a section. */
+struct mmix_segment_info_type
+ {
+ /* We only need to keep track of the last stubbable frag because
+ there's no less hackish way to keep track of different relaxation
+ rounds. */
+ fragS *last_stubfrag;
+ bfd_size_type nstubs;
+ };
+#define TC_SEGMENT_INFO_TYPE struct mmix_segment_info_type
+
extern void mmix_md_elf_section_change_hook PARAMS ((void));
#define md_elf_section_change_hook mmix_md_elf_section_change_hook