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.
This commit is contained in:
parent
06335781f6
commit
88fc725d2f
4 changed files with 250 additions and 16 deletions
|
@ -1,3 +1,27 @@
|
|||
2003-10-18 Hans-Peter Nilsson <hp@bitrange.com>
|
||||
|
||||
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.
|
||||
|
||||
2003-10-17 Paul Dale <pauli@snapgear.com>
|
||||
Bernardo Innocenti <bernie@develer.com>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -82,6 +82,21 @@ that at link stage can be contracted. (Though linker relaxation isn't yet
|
|||
implemented in @code{@value{LD}}.) The option @samp{-x} also imples
|
||||
@samp{--linker-allocated-gregs}.
|
||||
|
||||
@cindex @samp{--no-pushj-stubs} command line option, MMIX
|
||||
@cindex @samp{--no-stubs} command line option, MMIX
|
||||
If instruction expansion is enabled, @code{@value{AS}} can expand a
|
||||
@samp{PUSHJ} instruction into a series of instructions. The shortest
|
||||
expansion is to not expand it, but just mark the call as redirectable to a
|
||||
stub, which @code{@value{LD}} creates at link-time, but only if the
|
||||
original @samp{PUSHJ} instruction is found not to reach the target. The
|
||||
stub consists of the necessary instructions to form a jump to the target.
|
||||
This happens if @code{@value{AS}} can assert that the @samp{PUSHJ}
|
||||
instruction can reach such a stub. The option @samp{--no-pushj-stubs}
|
||||
disables this shorter expansion, and the longer series of instructions is
|
||||
then created at assembly-time. The option @samp{--no-stubs} is a synonym,
|
||||
intended for compatibility with future releases, where generation of stubs
|
||||
for other instructions may be implemented.
|
||||
|
||||
@cindex @samp{--linker-allocated-gregs} command line option, MMIX
|
||||
Usually a two-operand-expression (@pxref{GREG-base}) without a matching
|
||||
@samp{GREG} directive is treated as an error by @code{@value{AS}}. When
|
||||
|
|
Loading…
Reference in a new issue