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:
Hans-Peter Nilsson 2003-10-18 15:53:40 +00:00
parent 06335781f6
commit 88fc725d2f
4 changed files with 250 additions and 16 deletions

View file

@ -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>

View file

@ -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);

View file

@ -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

View file

@ -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