* config/tc-mips.h (mips_relax_frag): Take segment as argument.

(md_relax_frag): Adjust macro.
* config/tc-mips.c (mips_relax_branch): New variable.
(RELAX_BRANCH_ENCODE, RELAX_BRANCH_P, RELAX_BRANCH_LIKELY,
RELAX_BRANCH_LINK, RELAX_BRANCH_TOOBAR): New.
(RELAX_MIPS16_P): Adjust.
(append_insn): Emit branch to non-constant in a frag_var if
branch-relaxation is desirable and possible.
(OPTION_RELAX_BRANCH, OPTION_NO_RELAX_BRANCH): New options.
(OPTION_ELF_BASE): Adjust.
(md_parse_option): Handle new options.
(md_apply_fix3): Update comment on EMBEDDED_PIC conditional
branch relaxation.
(relaxed_branch_length): New function.
(md_estimate_size_before_relax): Handle branch frags.
(mips_relax_frag): Likewise.
(md_convert_frag): Handle branch frags.  Warn if branch is
relaxed.
This commit is contained in:
Alexandre Oliva 2002-10-12 05:23:33 +00:00
parent e2b23ee922
commit 4a6a3df43d
3 changed files with 460 additions and 13 deletions

View File

@ -1,3 +1,24 @@
2002-10-12 Alexandre Oliva <aoliva@redhat.com>
* config/tc-mips.h (mips_relax_frag): Take segment as argument.
(md_relax_frag): Adjust macro.
* config/tc-mips.c (mips_relax_branch): New variable.
(RELAX_BRANCH_ENCODE, RELAX_BRANCH_P, RELAX_BRANCH_LIKELY,
RELAX_BRANCH_LINK, RELAX_BRANCH_TOOBAR): New.
(RELAX_MIPS16_P): Adjust.
(append_insn): Emit branch to non-constant in a frag_var if
branch-relaxation is desirable and possible.
(OPTION_RELAX_BRANCH, OPTION_NO_RELAX_BRANCH): New options.
(OPTION_ELF_BASE): Adjust.
(md_parse_option): Handle new options.
(md_apply_fix3): Update comment on EMBEDDED_PIC conditional
branch relaxation.
(relaxed_branch_length): New function.
(md_estimate_size_before_relax): Handle branch frags.
(mips_relax_frag): Likewise.
(md_convert_frag): Handle branch frags. Warn if branch is
relaxed.
2002-10-11 Kaz Kojima <kkojima@rr.iij4u.or.jp> 2002-10-11 Kaz Kojima <kkojima@rr.iij4u.or.jp>
* config/tc-sh.c (sh_force_relocation): Make sure TLS relocs get * config/tc-sh.c (sh_force_relocation): Make sure TLS relocs get

View File

@ -563,6 +563,13 @@ static const unsigned int mips16_to_32_reg_map[] =
}; };
static int mips_fix_4122_bugs; static int mips_fix_4122_bugs;
/* We don't relax branches by default, since this causes us to expand
`la .l2 - .l1' if there's a branch between .l1 and .l2, because we
fail to compute the offset before expanding the macro to the most
efficient expansion. */
static int mips_relax_branch;
/* Since the MIPS does not have multiple forms of PC relative /* Since the MIPS does not have multiple forms of PC relative
instructions, we do not have to do relaxing as is done on other instructions, we do not have to do relaxing as is done on other
@ -640,6 +647,88 @@ static int mips_fix_4122_bugs;
#define RELAX_RELOC3(i) (((i) >> 1) & 1) #define RELAX_RELOC3(i) (((i) >> 1) & 1)
#define RELAX_WARN(i) ((i) & 1) #define RELAX_WARN(i) ((i) & 1)
/* Branch without likely bit. If label is out of range, we turn:
beq reg1, reg2, label
delay slot
into
bne reg1, reg2, 0f
nop
j label
0: delay slot
with the following opcode replacements:
beq <-> bne
blez <-> bgtz
bltz <-> bgez
bc1f <-> bc1t
bltzal <-> bgezal (with jal label instead of j label)
Even though keeping the delay slot instruction in the delay slot of
the branch would be more efficient, it would be very tricky to do
correctly, because we'd have to introduce a variable frag *after*
the delay slot instruction, and expand that instead. Let's do it
the easy way for now, even if the branch-not-taken case now costs
one additional instruction. Out-of-range branches are not supposed
to be common, anyway.
Branch likely. If label is out of range, we turn:
beql reg1, reg2, label
delay slot (annulled if branch not taken)
into
beql reg1, reg2, 1f
nop
beql $0, $0, 2f
nop
1: j[al] label
delay slot (executed only if branch taken)
2:
It would be possible to generate a shorter sequence by losing the
likely bit, generating something like:
bne reg1, reg2, 0f
nop
j[al] label
delay slot (executed only if branch taken)
0:
beql -> bne
bnel -> beq
blezl -> bgtz
bgtzl -> blez
bltzl -> bgez
bgezl -> bltz
bc1fl -> bc1t
bc1tl -> bc1f
bltzall -> bgezal (with jal label instead of j label)
bgezall -> bltzal (ditto)
but it's not clear that it would actually improve performance. */
#define RELAX_BRANCH_ENCODE(reloc_s2, uncond, likely, link, toofar) \
((relax_substateT) \
(0xc0000000 \
| ((toofar) ? 1 : 0) \
| ((link) ? 2 : 0) \
| ((likely) ? 4 : 0) \
| ((uncond) ? 8 : 0) \
| ((reloc_s2) ? 16 : 0)))
#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
#define RELAX_BRANCH_RELOC_S2(i) (((i) & 16) != 0)
#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
#define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0)
#define RELAX_BRANCH_LINK(i) (((i) & 2) != 0)
#define RELAX_BRANCH_TOOFAR(i) (((i) & 1))
/* For mips16 code, we use an entirely different form of relaxation. /* For mips16 code, we use an entirely different form of relaxation.
mips16 supports two versions of most instructions which take mips16 supports two versions of most instructions which take
immediate values: a small one which takes some small value, and a immediate values: a small one which takes some small value, and a
@ -667,7 +756,7 @@ static int mips_fix_4122_bugs;
| ((ext) ? 0x200 : 0) \ | ((ext) ? 0x200 : 0) \
| ((dslot) ? 0x400 : 0) \ | ((dslot) ? 0x400 : 0) \
| ((jal_dslot) ? 0x800 : 0)) | ((jal_dslot) ? 0x800 : 0))
#define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0) #define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000)
#define RELAX_MIPS16_TYPE(i) ((i) & 0xff) #define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0) #define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)
#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0) #define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)
@ -785,6 +874,7 @@ static void s_mips_weakext PARAMS ((int));
static void s_mips_file PARAMS ((int)); static void s_mips_file PARAMS ((int));
static void s_mips_loc PARAMS ((int)); static void s_mips_loc PARAMS ((int));
static int mips16_extended_frag PARAMS ((fragS *, asection *, long)); static int mips16_extended_frag PARAMS ((fragS *, asection *, long));
static int relaxed_branch_length (fragS *, asection *, int);
static int validate_mips_insn PARAMS ((const struct mips_opcode *)); static int validate_mips_insn PARAMS ((const struct mips_opcode *));
static void show PARAMS ((FILE *, const char *, int *, int *)); static void show PARAMS ((FILE *, const char *, int *, int *));
#ifdef OBJ_ELF #ifdef OBJ_ELF
@ -1840,7 +1930,38 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
} }
} }
if (*reloc_type > BFD_RELOC_UNUSED) if (place == NULL
&& address_expr
&& ((*reloc_type == BFD_RELOC_16_PCREL
&& address_expr->X_op != O_constant)
|| *reloc_type == BFD_RELOC_16_PCREL_S2)
&& (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
|| pinfo & INSN_COND_BRANCH_LIKELY)
&& mips_relax_branch
/* Don't try branch relaxation within .set nomacro, or within
.set noat if we use $at for PIC computations. If it turns
out that the branch was out-of-range, we'll get an error. */
&& !mips_opts.warn_about_macros
&& !(mips_opts.noat && mips_pic != NO_PIC)
&& !mips_opts.mips16)
{
f = frag_var (rs_machine_dependent,
relaxed_branch_length
(NULL, NULL,
(pinfo & INSN_UNCOND_BRANCH_DELAY) ? -1
: (pinfo & INSN_COND_BRANCH_LIKELY) ? 1 : 0), 4,
RELAX_BRANCH_ENCODE
(*reloc_type == BFD_RELOC_16_PCREL_S2,
pinfo & INSN_UNCOND_BRANCH_DELAY,
pinfo & INSN_COND_BRANCH_LIKELY,
pinfo & INSN_WRITE_GPR_31,
0),
address_expr->X_add_symbol,
address_expr->X_add_number,
0);
*reloc_type = BFD_RELOC_UNUSED;
}
else if (*reloc_type > BFD_RELOC_UNUSED)
{ {
/* We need to set up a variant frag. */ /* We need to set up a variant frag. */
assert (mips_opts.mips16 && address_expr != NULL); assert (mips_opts.mips16 && address_expr != NULL);
@ -10125,8 +10246,12 @@ struct option md_longopts[] =
#define OPTION_NO_FIX_VR4122 (OPTION_MD_BASE + 38) #define OPTION_NO_FIX_VR4122 (OPTION_MD_BASE + 38)
{"mfix-vr4122-bugs", no_argument, NULL, OPTION_FIX_VR4122}, {"mfix-vr4122-bugs", no_argument, NULL, OPTION_FIX_VR4122},
{"no-mfix-vr4122-bugs", no_argument, NULL, OPTION_NO_FIX_VR4122}, {"no-mfix-vr4122-bugs", no_argument, NULL, OPTION_NO_FIX_VR4122},
#define OPTION_RELAX_BRANCH (OPTION_MD_BASE + 39)
#define OPTION_NO_RELAX_BRANCH (OPTION_MD_BASE + 40)
{"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
{"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
#ifdef OBJ_ELF #ifdef OBJ_ELF
#define OPTION_ELF_BASE (OPTION_MD_BASE + 39) #define OPTION_ELF_BASE (OPTION_MD_BASE + 41)
#define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0) #define OPTION_CALL_SHARED (OPTION_ELF_BASE + 0)
{"KPIC", no_argument, NULL, OPTION_CALL_SHARED}, {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
{"call_shared", no_argument, NULL, OPTION_CALL_SHARED}, {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
@ -10335,6 +10460,14 @@ md_parse_option (c, arg)
mips_fix_4122_bugs = 0; mips_fix_4122_bugs = 0;
break; break;
case OPTION_RELAX_BRANCH:
mips_relax_branch = 1;
break;
case OPTION_NO_RELAX_BRANCH:
mips_relax_branch = 0;
break;
#ifdef OBJ_ELF #ifdef OBJ_ELF
/* When generating ELF code, we permit -KPIC and -call_shared to /* When generating ELF code, we permit -KPIC and -call_shared to
select SVR4_PIC, and -non_shared to select no PIC. This is select SVR4_PIC, and -non_shared to select no PIC. This is
@ -11186,13 +11319,9 @@ md_apply_fix3 (fixP, valP, seg)
} }
else else
{ {
/* FIXME. It would be possible in principle to handle /* If we got here, we have branch-relaxation disabled,
conditional branches which overflow. They could be and there's nothing we can do to fix this instruction
transformed into a branch around a jump. This would without turning it into a longer sequence. */
require setting up variant frags for each different
branch type. The native MIPS assembler attempts to
handle these cases, but it appears to do it
incorrectly. */
as_bad_where (fixP->fx_file, fixP->fx_line, as_bad_where (fixP->fx_file, fixP->fx_line,
_("Branch out of range")); _("Branch out of range"));
} }
@ -12662,6 +12791,74 @@ mips16_extended_frag (fragp, sec, stretch)
return 0; return 0;
} }
/* Compute the length of a branch sequence, and adjust the
RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the
worst-case length is computed, with UPDATE being used to indicate
whether an unconditional (-1), branch-likely (+1) or regular (0)
branch is to be computed. */
static int
relaxed_branch_length (fragp, sec, update)
fragS *fragp;
asection *sec;
int update;
{
boolean toofar;
int length;
if (fragp
&& S_IS_DEFINED (fragp->fr_symbol)
&& sec == S_GET_SEGMENT (fragp->fr_symbol))
{
addressT addr;
offsetT val;
val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
addr = fragp->fr_address + fragp->fr_fix + 4;
val -= addr;
toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
}
else if (fragp)
/* If the symbol is not defined or it's in a different segment,
assume the user knows what's going on and emit a short
branch. */
toofar = false;
else
toofar = true;
if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
fragp->fr_subtype
= RELAX_BRANCH_ENCODE (RELAX_BRANCH_RELOC_S2 (fragp->fr_subtype),
RELAX_BRANCH_UNCOND (fragp->fr_subtype),
RELAX_BRANCH_LIKELY (fragp->fr_subtype),
RELAX_BRANCH_LINK (fragp->fr_subtype),
toofar);
length = 4;
if (toofar)
{
if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0))
length += 8;
if (mips_pic != NO_PIC)
{
/* Additional space for PIC loading of target address. */
length += 8;
if (mips_opts.isa == ISA_MIPS1)
/* Additional space for $at-stabilizing nop. */
length += 4;
}
/* If branch is conditional. */
if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0))
length += 8;
}
return length;
}
/* Estimate the size of a frag before relaxing. Unless this is the /* Estimate the size of a frag before relaxing. Unless this is the
mips16, we are not really relaxing here, and the final size is mips16, we are not really relaxing here, and the final size is
encoded in the subtype information. For the mips16, we have to encoded in the subtype information. For the mips16, we have to
@ -12675,6 +12872,14 @@ md_estimate_size_before_relax (fragp, segtype)
int change = 0; int change = 0;
boolean linkonce = false; boolean linkonce = false;
if (RELAX_BRANCH_P (fragp->fr_subtype))
{
fragp->fr_var = relaxed_branch_length (fragp, segtype, false);
return fragp->fr_var;
}
if (RELAX_MIPS16_P (fragp->fr_subtype)) if (RELAX_MIPS16_P (fragp->fr_subtype))
/* We don't want to modify the EXTENDED bit here; it might get us /* We don't want to modify the EXTENDED bit here; it might get us
into infinite loops. We change it only in mips_relax_frag(). */ into infinite loops. We change it only in mips_relax_frag(). */
@ -13049,10 +13254,20 @@ tc_gen_reloc (section, fixp)
the current size of the frag should change. */ the current size of the frag should change. */
int int
mips_relax_frag (fragp, stretch) mips_relax_frag (sec, fragp, stretch)
asection *sec;
fragS *fragp; fragS *fragp;
long stretch; long stretch;
{ {
if (RELAX_BRANCH_P (fragp->fr_subtype))
{
offsetT old_var = fragp->fr_var;
fragp->fr_var = relaxed_branch_length (fragp, sec, true);
return fragp->fr_var - old_var;
}
if (! RELAX_MIPS16_P (fragp->fr_subtype)) if (! RELAX_MIPS16_P (fragp->fr_subtype))
return 0; return 0;
@ -13085,6 +13300,216 @@ md_convert_frag (abfd, asec, fragp)
int old, new; int old, new;
char *fixptr; char *fixptr;
if (RELAX_BRANCH_P (fragp->fr_subtype))
{
bfd_byte *buf;
unsigned long insn;
expressionS exp;
fixS *fixp;
buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
if (target_big_endian)
insn = bfd_getb32 (buf);
else
insn = bfd_getl32 (buf);
if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
{
/* We generate a fixup instead of applying it right now
because, if there are linker relaxations, we're going to
need the relocations. */
exp.X_op = O_symbol;
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
4, &exp, 1,
RELAX_BRANCH_RELOC_S2 (fragp->fr_subtype)
? BFD_RELOC_16_PCREL_S2
: BFD_RELOC_16_PCREL);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
md_number_to_chars ((char *)buf, insn, 4);
buf += 4;
}
else
{
int i;
as_warn_where (fragp->fr_file, fragp->fr_line,
_("relaxed out-of-range branch into a jump"));
if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
goto uncond;
if (!RELAX_BRANCH_LIKELY (fragp->fr_subtype))
{
/* Reverse the branch. */
switch ((insn >> 28) & 0xf)
{
case 4:
/* bc[0-3][tf]l? and bc1any[24][ft] instructions can
have the condition reversed by tweaking a single
bit, and their opcodes all have 0x4???????. */
assert ((insn & 0xf1000000) == 0x41000000);
insn ^= 0x00010000;
break;
case 0:
/* bltz 0x04000000 bgez 0x04010000
bltzal 0x04100000 bgezal 0x04110000 */
assert ((insn & 0xfc0e0000) == 0x04000000);
insn ^= 0x00010000;
break;
case 1:
/* beq 0x10000000 bne 0x14000000
blez 0x18000000 bgtz 0x1c000000 */
insn ^= 0x04000000;
break;
default:
abort ();
}
}
if (RELAX_BRANCH_LINK (fragp->fr_subtype))
{
/* Clear the and-link bit. */
assert ((insn & 0xfc1c0000) == 0x04100000);
/* bltzal 0x04100000 bgezal 0x04110000
bltzall 0x04120000 bgezall 0x04130000 */
insn &= ~0x00100000;
}
/* Branch over the branch (if the branch was likely) or the
full jump (not likely case). Compute the offset from the
current instruction to branch to. */
if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
i = 16;
else
{
/* How many bytes in instructions we've already emitted? */
i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
/* How many bytes in instructions from here to the end? */
i = fragp->fr_var - i;
}
/* Convert to instruction count. */
i >>= 2;
/* Branch counts from the next instruction. */
i--;
insn |= i;
/* Branch over the jump. */
md_number_to_chars ((char *)buf, insn, 4);
buf += 4;
/* Nop */
md_number_to_chars ((char*)buf, 0, 4);
buf += 4;
if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
{
/* beql $0, $0, 2f */
insn = 0x50000000;
/* Compute the PC offset from the current instruction to
the end of the variable frag. */
/* How many bytes in instructions we've already emitted? */
i = buf - (bfd_byte *)fragp->fr_literal - fragp->fr_fix;
/* How many bytes in instructions from here to the end? */
i = fragp->fr_var - i;
/* Convert to instruction count. */
i >>= 2;
/* Don't decrement i, because we want to branch over the
delay slot. */
insn |= i;
md_number_to_chars ((char *)buf, insn, 4);
buf += 4;
md_number_to_chars ((char *)buf, 0, 4);
buf += 4;
}
uncond:
if (mips_pic == NO_PIC)
{
/* j or jal. */
insn = (RELAX_BRANCH_LINK (fragp->fr_subtype)
? 0x0c000000 : 0x08000000);
exp.X_op = O_symbol;
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
4, &exp, 0, BFD_RELOC_MIPS_JMP);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
md_number_to_chars ((char*)buf, insn, 4);
buf += 4;
}
else
{
/* lw/ld $at, <sym>($gp) R_MIPS_GOT16 */
insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000;
exp.X_op = O_symbol;
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
if (fragp->fr_offset)
{
exp.X_add_symbol = make_expr_symbol (&exp);
exp.X_add_number = 0;
}
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
4, &exp, 0, BFD_RELOC_MIPS_GOT16);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
md_number_to_chars ((char*)buf, insn, 4);
buf += 4;
if (mips_opts.isa == ISA_MIPS1)
{
/* nop */
md_number_to_chars ((char*)buf, 0, 4);
buf += 4;
}
/* d/addiu $at, $at, <sym> R_MIPS_LO16 */
insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
4, &exp, 0, BFD_RELOC_LO16);
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
md_number_to_chars ((char*)buf, insn, 4);
buf += 4;
/* j(al)r $at. */
if (RELAX_BRANCH_LINK (fragp->fr_subtype))
insn = 0x0020f809;
else
insn = 0x00200008;
md_number_to_chars ((char*)buf, insn, 4);
buf += 4;
}
}
assert (buf == (bfd_byte *)fragp->fr_literal
+ fragp->fr_fix + fragp->fr_var);
fragp->fr_fix += fragp->fr_var;
return;
}
if (RELAX_MIPS16_P (fragp->fr_subtype)) if (RELAX_MIPS16_P (fragp->fr_subtype))
{ {
int type; int type;

View File

@ -49,8 +49,9 @@ struct expressionS;
relocation: */ relocation: */
#define MAX_GPREL_OFFSET (0x7FF0) #define MAX_GPREL_OFFSET (0x7FF0)
#define md_relax_frag(segment, fragp, stretch) mips_relax_frag(fragp, stretch) #define md_relax_frag(segment, fragp, stretch) \
extern int mips_relax_frag PARAMS ((struct frag *, long)); mips_relax_frag(segment, fragp, stretch)
extern int mips_relax_frag PARAMS ((asection *, struct frag *, long));
#define md_undefined_symbol(name) (0) #define md_undefined_symbol(name) (0)
#define md_operand(x) #define md_operand(x)