Fix for PR6697:

(nopic_need_relax): New static function, split out from
md_estimate_size_before_relax.
(md_estimate_size_before_relax): Call it.
(load_address, macro): In NO_PIC branches, if nopic_need_relax returns nonzero,
don't attempt GP optimization.
This commit is contained in:
Ken Raeburn
1995-04-11 01:29:49 +00:00
parent b79de3a178
commit d8a1c247ae

View File

@ -168,6 +168,19 @@ static int g_switch_seen = 0;
#define N_RMASK 0xc4 #define N_RMASK 0xc4
#define N_VFP 0xd4 #define N_VFP 0xd4
/* If we can determine in advance that GP optimization won't be
possible, we can skip the relaxation stuff that tries to produce
GP-relative references. This makes delay slot optimization work
better.
This function can only provide a guess, but it seems to work for
gcc output. If it guesses wrong, the only loss should be in
efficiency; it shouldn't introduce any bugs.
I don't know if a fix is needed for the SVR4_PIC mode. I've only
fixed it for the non-PIC mode. KR 95/04/07 */
static int nopic_need_relax PARAMS ((symbolS *));
/* handle of the OPCODE hash table */ /* handle of the OPCODE hash table */
static struct hash_control *op_hash = NULL; static struct hash_control *op_hash = NULL;
@ -361,7 +374,7 @@ static void macro_build_lui PARAMS ((char *place, int *counter,
static void set_at PARAMS ((int *counter, int reg, int unsignedp)); static void set_at PARAMS ((int *counter, int reg, int unsignedp));
static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip, static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip,
expressionS *)); expressionS *));
static void load_register PARAMS ((int *counter, int reg, expressionS * ep)); static void load_register PARAMS ((int *, int, expressionS *, int));
static void load_address PARAMS ((int *counter, int reg, expressionS *ep)); static void load_address PARAMS ((int *counter, int reg, expressionS *ep));
static void macro PARAMS ((struct mips_cl_insn * ip)); static void macro PARAMS ((struct mips_cl_insn * ip));
#ifdef LOSING_COMPILER #ifdef LOSING_COMPILER
@ -580,6 +593,19 @@ md_begin ()
if (mips_4650 == -1) if (mips_4650 == -1)
mips_4650 = 1; mips_4650 = 1;
} }
else if (strcmp (cpu, "r8000") == 0
|| strcmp (cpu, "mips4") == 0)
{
mips_isa = 4;
if (mips_cpu == -1)
mips_cpu = 8000;
}
else if (strcmp (cpu, "r10000") == 0)
{
mips_isa = 4;
if (mips_cpu == -1)
mips_cpu = 10000;
}
else else
{ {
mips_isa = 1; mips_isa = 1;
@ -608,6 +634,9 @@ md_begin ()
case 3: case 3:
ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 4000); ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 4000);
break; break;
case 4:
ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 8000);
break;
} }
if (! ok) if (! ok)
as_warn ("Could not set architecture and machine"); as_warn ("Could not set architecture and machine");
@ -831,9 +860,10 @@ append_insn (place, ip, address_expr, reloc_type)
/* The previous insn might require a delay slot, depending upon /* The previous insn might require a delay slot, depending upon
the contents of the current insn. */ the contents of the current insn. */
if ((prev_pinfo & INSN_LOAD_COPROC_DELAY) if (mips_isa < 4
|| (mips_isa < 2 && ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
&& (prev_pinfo & INSN_LOAD_MEMORY_DELAY))) || (mips_isa < 2
&& (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
{ {
/* A load from a coprocessor or from memory. All load /* A load from a coprocessor or from memory. All load
delays delay the use of general register rt for one delays delay the use of general register rt for one
@ -847,9 +877,10 @@ append_insn (place, ip, address_expr, reloc_type)
0)) 0))
++nops; ++nops;
} }
else if ((prev_pinfo & INSN_COPROC_MOVE_DELAY) else if (mips_isa < 4
|| (mips_isa < 2 && ((prev_pinfo & INSN_COPROC_MOVE_DELAY)
&& (prev_pinfo & INSN_COPROC_MEMORY_DELAY))) || (mips_isa < 2
&& (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
{ {
/* A generic coprocessor delay. The previous instruction /* A generic coprocessor delay. The previous instruction
modified a coprocessor general or control register. If modified a coprocessor general or control register. If
@ -899,7 +930,8 @@ append_insn (place, ip, address_expr, reloc_type)
++nops; ++nops;
} }
} }
else if (prev_pinfo & INSN_WRITE_COND_CODE) else if (mips_isa < 4
&& (prev_pinfo & INSN_WRITE_COND_CODE))
{ {
/* The previous instruction sets the coprocessor condition /* The previous instruction sets the coprocessor condition
codes, but does not require a general coprocessor delay codes, but does not require a general coprocessor delay
@ -940,7 +972,8 @@ append_insn (place, ip, address_expr, reloc_type)
instruction, we must check for these cases compared to the instruction, we must check for these cases compared to the
instruction previous to the previous instruction. */ instruction previous to the previous instruction. */
if (nops == 0 if (nops == 0
&& (((prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY) && ((mips_isa < 4
&& (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
&& (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE) && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
&& (pinfo & INSN_READ_COND_CODE)) && (pinfo & INSN_READ_COND_CODE))
|| ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO) || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
@ -1045,6 +1078,8 @@ append_insn (place, ip, address_expr, reloc_type)
mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS); mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0) if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT); mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
if ((pinfo & INSN_READ_FPR_R) != 0)
mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
if (pinfo & INSN_COP) if (pinfo & INSN_COP)
{ {
/* We don't keep enough information to sort these cases out. */ /* We don't keep enough information to sort these cases out. */
@ -1109,14 +1144,16 @@ append_insn (place, ip, address_expr, reloc_type)
bc1t LABEL bc1t LABEL
we can not swap, and I don't feel like handling that we can not swap, and I don't feel like handling that
case. */ case. */
|| (pinfo & INSN_READ_COND_CODE) || (mips_isa < 4
&& (pinfo & INSN_READ_COND_CODE))
/* We can not swap with an instruction that requires a /* We can not swap with an instruction that requires a
delay slot, becase the target of the branch might delay slot, becase the target of the branch might
interfere with that instruction. */ interfere with that instruction. */
|| (prev_pinfo || (mips_isa < 4
& (INSN_LOAD_COPROC_DELAY && (prev_pinfo
| INSN_COPROC_MOVE_DELAY & (INSN_LOAD_COPROC_DELAY
| INSN_WRITE_COND_CODE)) | INSN_COPROC_MOVE_DELAY
| INSN_WRITE_COND_CODE)))
|| (! mips_4650 || (! mips_4650
&& (prev_pinfo && (prev_pinfo
& (INSN_READ_LO & (INSN_READ_LO
@ -1183,10 +1220,11 @@ append_insn (place, ip, address_expr, reloc_type)
/* If the previous previous instruction has a load /* If the previous previous instruction has a load
delay, and sets a register that the branch reads, we delay, and sets a register that the branch reads, we
can not swap. */ can not swap. */
|| (((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY) || (mips_isa < 4
|| (mips_isa < 2 && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
&& (prev_prev_insn.insn_mo->pinfo || (mips_isa < 2
& INSN_LOAD_MEMORY_DELAY))) && (prev_prev_insn.insn_mo->pinfo
& INSN_LOAD_MEMORY_DELAY)))
&& insn_uses_reg (ip, && insn_uses_reg (ip,
((prev_prev_insn.insn_opcode >> OP_SH_RT) ((prev_prev_insn.insn_opcode >> OP_SH_RT)
& OP_MASK_RT), & OP_MASK_RT),
@ -1301,10 +1339,11 @@ mips_emit_delays ()
int nop; int nop;
nop = 0; nop = 0;
if ((prev_insn.insn_mo->pinfo if ((mips_isa < 4
& (INSN_LOAD_COPROC_DELAY && (prev_insn.insn_mo->pinfo
| INSN_COPROC_MOVE_DELAY & (INSN_LOAD_COPROC_DELAY
| INSN_WRITE_COND_CODE)) | INSN_COPROC_MOVE_DELAY
| INSN_WRITE_COND_CODE)))
|| (! mips_4650 || (! mips_4650
&& (prev_insn.insn_mo->pinfo && (prev_insn.insn_mo->pinfo
& (INSN_READ_LO & (INSN_READ_LO
@ -1315,13 +1354,15 @@ mips_emit_delays ()
| INSN_COPROC_MEMORY_DELAY)))) | INSN_COPROC_MEMORY_DELAY))))
{ {
nop = 1; nop = 1;
if ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE) if ((mips_isa < 4
&& (prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
|| (! mips_4650 || (! mips_4650
&& ((prev_insn.insn_mo->pinfo & INSN_READ_HI) && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_insn.insn_mo->pinfo & INSN_READ_LO)))) || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
emit_nop (); emit_nop ();
} }
else if ((prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE) else if ((mips_isa < 4
&& (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
|| (! mips_4650 || (! mips_4650
&& ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI) && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)))) || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
@ -1605,7 +1646,7 @@ set_at (counter, reg, unsignedp)
"t,r,j", AT, reg, (int) BFD_RELOC_LO16); "t,r,j", AT, reg, (int) BFD_RELOC_LO16);
else else
{ {
load_register (counter, AT, &imm_expr); load_register (counter, AT, &imm_expr, 0);
macro_build ((char *) NULL, counter, NULL, macro_build ((char *) NULL, counter, NULL,
unsignedp ? "sltu" : "slt", unsignedp ? "sltu" : "slt",
"d,v,t", AT, reg, AT); "d,v,t", AT, reg, AT);
@ -1628,18 +1669,24 @@ check_absolute_expr (ip, ex)
* an absolute expression value into a register. * an absolute expression value into a register.
*/ */
static void static void
load_register (counter, reg, ep) load_register (counter, reg, ep, dbl)
int *counter; int *counter;
int reg; int reg;
expressionS *ep; expressionS *ep;
int dbl;
{ {
int shift; int shift, freg;
expressionS hi32, lo32, tmp; expressionS hi32, lo32, tmp;
if (ep->X_op != O_big) if (ep->X_op != O_big)
{ {
assert (ep->X_op == O_constant); assert (ep->X_op == O_constant);
if (ep->X_add_number >= -0x8000 && ep->X_add_number < 0x8000) if (ep->X_add_number < 0x8000
&& (ep->X_add_number >= 0
|| (ep->X_add_number >= -0x8000
&& (! dbl
|| ! ep->X_unsigned
|| sizeof (ep->X_add_number) > 4))))
{ {
/* We can handle 16 bit signed values with an addiu to /* We can handle 16 bit signed values with an addiu to
$zero. No need to ever use daddiu here, since $zero and $zero. No need to ever use daddiu here, since $zero and
@ -1659,7 +1706,7 @@ load_register (counter, reg, ep)
else if (((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0 else if (((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
|| ((ep->X_add_number &~ (offsetT) 0x7fffffff) || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
== ~ (offsetT) 0x7fffffff)) == ~ (offsetT) 0x7fffffff))
&& (mips_isa < 3 && (! dbl
|| ! ep->X_unsigned || ! ep->X_unsigned
|| sizeof (ep->X_add_number) > 4 || sizeof (ep->X_add_number) > 4
|| (ep->X_add_number & 0x80000000) == 0)) || (ep->X_add_number & 0x80000000) == 0))
@ -1721,25 +1768,42 @@ load_register (counter, reg, ep)
hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16); hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
} }
load_register (counter, reg, &hi32); if (hi32.X_add_number == 0)
freg = 0;
else
{
load_register (counter, reg, &hi32, 0);
freg = reg;
}
if ((lo32.X_add_number & 0xffff0000) == 0) if ((lo32.X_add_number & 0xffff0000) == 0)
macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg, {
reg, 0); if (freg != 0)
{
macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
freg, 0);
freg = reg;
}
}
else else
{ {
expressionS mid16; expressionS mid16;
macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg, if (freg != 0)
reg, 16); {
macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
freg, 16);
freg = reg;
}
mid16 = lo32; mid16 = lo32;
mid16.X_add_number >>= 16; mid16.X_add_number >>= 16;
macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg, macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
reg, (int) BFD_RELOC_LO16); freg, (int) BFD_RELOC_LO16);
macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg, macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
reg, 16); reg, 16);
freg = reg;
} }
if ((lo32.X_add_number & 0xffff) != 0) if ((lo32.X_add_number & 0xffff) != 0)
macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg, macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, freg,
(int) BFD_RELOC_LO16); (int) BFD_RELOC_LO16);
} }
@ -1762,7 +1826,7 @@ load_address (counter, reg, ep)
if (ep->X_op == O_constant) if (ep->X_op == O_constant)
{ {
load_register (counter, reg, ep); load_register (counter, reg, ep, 0);
return; return;
} }
@ -1774,7 +1838,7 @@ load_address (counter, reg, ep)
lui $reg,<sym> (BFD_RELOC_HI16_S) lui $reg,<sym> (BFD_RELOC_HI16_S)
addiu $reg,$reg,<sym> (BFD_RELOC_LO16) addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
If we have an addend, we always use the latter form. */ If we have an addend, we always use the latter form. */
if (ep->X_add_number != 0) if (ep->X_add_number != 0 || nopic_need_relax (ep->X_add_symbol))
p = NULL; p = NULL;
else else
{ {
@ -1942,7 +2006,7 @@ macro (ip)
(int) BFD_RELOC_LO16); (int) BFD_RELOC_LO16);
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, dbl);
macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT); macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
break; break;
@ -1977,7 +2041,7 @@ macro (ip)
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT); macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
break; break;
@ -2001,7 +2065,7 @@ macro (ip)
0); 0);
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT); macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT);
break; break;
@ -2478,7 +2542,7 @@ macro (ip)
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, dbl);
macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT); macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT);
macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg); macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
break; break;
@ -2516,6 +2580,8 @@ macro (ip)
macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg); macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
return; return;
case M_DLA_AB:
dbl = 1;
case M_LA_AB: case M_LA_AB:
/* Load the address of a symbol into a register. If breg is not /* Load the address of a symbol into a register. If breg is not
zero, we then add a base register to it. */ zero, we then add a base register to it. */
@ -2567,7 +2633,7 @@ macro (ip)
} }
if (offset_expr.X_op == O_constant) if (offset_expr.X_op == O_constant)
load_register (&icnt, tempreg, &offset_expr); load_register (&icnt, tempreg, &offset_expr, dbl);
else if (mips_pic == NO_PIC) else if (mips_pic == NO_PIC)
{ {
/* If this is a reference to an GP relative symbol, we want /* If this is a reference to an GP relative symbol, we want
@ -2577,7 +2643,8 @@ macro (ip)
addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16) addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
If we have a constant, we need two instructions anyhow, If we have a constant, we need two instructions anyhow,
so we may as well always use the latter form. */ so we may as well always use the latter form. */
if (offset_expr.X_add_number != 0) if (offset_expr.X_add_number != 0
|| nopic_need_relax (offset_expr.X_add_symbol))
p = NULL; p = NULL;
else else
{ {
@ -2630,7 +2697,7 @@ macro (ip)
offset_expr.X_add_number = 0; offset_expr.X_add_number = 0;
frag_grow (32); frag_grow (32);
macro_build ((char *) NULL, &icnt, &offset_expr, macro_build ((char *) NULL, &icnt, &offset_expr,
mips_isa < 3 ? "lw" : "ld", dbl ? "ld" : "lw",
"t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP); "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
if (expr1.X_add_number == 0) if (expr1.X_add_number == 0)
{ {
@ -3031,7 +3098,8 @@ macro (ip)
With a constant we always use the latter case. */ With a constant we always use the latter case. */
if (breg == 0) if (breg == 0)
{ {
if (offset_expr.X_add_number != 0) if (offset_expr.X_add_number != 0
|| nopic_need_relax (offset_expr.X_add_symbol))
p = NULL; p = NULL;
else else
{ {
@ -3054,7 +3122,8 @@ macro (ip)
} }
else else
{ {
if (offset_expr.X_add_number != 0) if (offset_expr.X_add_number != 0
|| nopic_need_relax (offset_expr.X_add_symbol))
p = NULL; p = NULL;
else else
{ {
@ -3157,13 +3226,17 @@ macro (ip)
case M_LI: case M_LI:
case M_LI_S: case M_LI_S:
load_register (&icnt, treg, &imm_expr); load_register (&icnt, treg, &imm_expr, 0);
return;
case M_DLI:
load_register (&icnt, treg, &imm_expr, 1);
return; return;
case M_LI_SS: case M_LI_SS:
if (imm_expr.X_op == O_constant) if (imm_expr.X_op == O_constant)
{ {
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, (expressionS *) NULL, macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
"mtc1", "t,G", AT, treg); "mtc1", "t,G", AT, treg);
break; break;
@ -3390,7 +3463,8 @@ macro (ip)
If there is a base register, we add it to $at after the If there is a base register, we add it to $at after the
lui instruction. If there is a constant, we always use lui instruction. If there is a constant, we always use
the last case. */ the last case. */
if (offset_expr.X_add_number != 0) if (offset_expr.X_add_number != 0
|| nopic_need_relax (offset_expr.X_add_symbol))
{ {
p = NULL; p = NULL;
used_at = 1; used_at = 1;
@ -3638,7 +3712,7 @@ macro2 (ip)
/* The MIPS assembler some times generates shifts and adds. I'm /* The MIPS assembler some times generates shifts and adds. I'm
not trying to be that fancy. GCC should do this for us not trying to be that fancy. GCC should do this for us
anyway. */ anyway. */
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, dbl);
macro_build ((char *) NULL, &icnt, NULL, macro_build ((char *) NULL, &icnt, NULL,
dbl ? "dmult" : "mult", dbl ? "dmult" : "mult",
"s,t", sreg, AT); "s,t", sreg, AT);
@ -3787,7 +3861,7 @@ macro2 (ip)
} }
else else
{ {
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg, macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
sreg, AT); sreg, AT);
used_at = 1; used_at = 1;
@ -3820,7 +3894,7 @@ macro2 (ip)
} }
else else
{ {
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, macro_build ((char *) NULL, &icnt, NULL,
mask == M_SGE_I ? "slt" : "sltu", mask == M_SGE_I ? "slt" : "sltu",
"d,v,t", dreg, sreg, AT); "d,v,t", dreg, sreg, AT);
@ -3847,7 +3921,7 @@ macro2 (ip)
case M_SGTU_I: case M_SGTU_I:
s = "sltu"; s = "sltu";
sgti: sgti:
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg); macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
break; break;
@ -3868,7 +3942,7 @@ macro2 (ip)
case M_SLEU_I: case M_SLEU_I:
s = "sltu"; s = "sltu";
slei: slei:
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg); macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg, macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
(int) BFD_RELOC_LO16); (int) BFD_RELOC_LO16);
@ -3881,7 +3955,7 @@ macro2 (ip)
dreg, sreg, (int) BFD_RELOC_LO16); dreg, sreg, (int) BFD_RELOC_LO16);
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT); macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
break; break;
@ -3892,7 +3966,7 @@ macro2 (ip)
dreg, sreg, (int) BFD_RELOC_LO16); dreg, sreg, (int) BFD_RELOC_LO16);
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg, macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg,
AT); AT);
break; break;
@ -3945,7 +4019,7 @@ macro2 (ip)
} }
else else
{ {
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg, macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
sreg, AT); sreg, AT);
used_at = 1; used_at = 1;
@ -3966,7 +4040,7 @@ macro2 (ip)
"t,r,j", dreg, sreg, (int) BFD_RELOC_LO16); "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, dbl);
macro_build ((char *) NULL, &icnt, NULL, macro_build ((char *) NULL, &icnt, NULL,
dbl ? "dsub" : "sub", dbl ? "dsub" : "sub",
"d,v,t", dreg, sreg, AT); "d,v,t", dreg, sreg, AT);
@ -3983,7 +4057,7 @@ macro2 (ip)
"t,r,j", dreg, sreg, (int) BFD_RELOC_LO16); "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
return; return;
} }
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, dbl);
macro_build ((char *) NULL, &icnt, NULL, macro_build ((char *) NULL, &icnt, NULL,
dbl ? "dsubu" : "subu", dbl ? "dsubu" : "subu",
"d,v,t", dreg, sreg, AT); "d,v,t", dreg, sreg, AT);
@ -4007,7 +4081,7 @@ macro2 (ip)
case M_TNE_I: case M_TNE_I:
s = "tne"; s = "tne";
trap: trap:
load_register (&icnt, AT, &imm_expr); load_register (&icnt, AT, &imm_expr, 0);
macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT); macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT);
break; break;
@ -4288,6 +4362,8 @@ mips_ip (str, ip)
insn_isa = 2; insn_isa = 2;
else if ((insn->pinfo & INSN_ISA) == INSN_ISA3) else if ((insn->pinfo & INSN_ISA) == INSN_ISA3)
insn_isa = 3; insn_isa = 3;
else if ((insn->pinfo & INSN_ISA) == INSN_ISA4)
insn_isa = 4;
else else
insn_isa = 1; insn_isa = 1;
@ -4386,15 +4462,20 @@ mips_ip (str, ip)
continue; continue;
case 'k': /* cache code */ case 'k': /* cache code */
case 'h': /* prefx code */
my_getExpression (&imm_expr, s); my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr); check_absolute_expr (ip, &imm_expr);
if ((unsigned long) imm_expr.X_add_number > 31) if ((unsigned long) imm_expr.X_add_number > 31)
{ {
as_warn ("Invalid cahce opcode (%lu)", as_warn ("Invalid value for `%s' (%lu)",
ip->insn_mo->name,
(unsigned long) imm_expr.X_add_number); (unsigned long) imm_expr.X_add_number);
imm_expr.X_add_number &= 0x1f; imm_expr.X_add_number &= 0x1f;
} }
ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE; if (*args == 'k')
ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
else
ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX;
imm_expr.X_op = O_absent; imm_expr.X_op = O_absent;
s = expr_end; s = expr_end;
continue; continue;
@ -4569,6 +4650,7 @@ mips_ip (str, ip)
case 'D': /* floating point destination register */ case 'D': /* floating point destination register */
case 'S': /* floating point source register */ case 'S': /* floating point source register */
case 'T': /* floating point target register */ case 'T': /* floating point target register */
case 'R': /* floating point source register */
case 'V': case 'V':
case 'W': case 'W':
s_reset = s; s_reset = s;
@ -4620,6 +4702,10 @@ mips_ip (str, ip)
case 'W': case 'W':
case 'T': case 'T':
ip->insn_opcode |= regno << 16; ip->insn_opcode |= regno << 16;
break;
case 'R':
ip->insn_opcode |= regno << 21;
break;
} }
lastregno = regno; lastregno = regno;
continue; continue;
@ -4837,7 +4923,12 @@ mips_ip (str, ip)
max = 0x10000; max = 0x10000;
if (imm_expr.X_op == O_big if (imm_expr.X_op == O_big
|| imm_expr.X_add_number < -0x8000 || imm_expr.X_add_number < -0x8000
|| imm_expr.X_add_number >= max) || imm_expr.X_add_number >= max
|| (more
&& imm_expr.X_add_number < 0
&& mips_isa >= 3
&& imm_expr.X_unsigned
&& sizeof (imm_expr.X_add_number) <= 4))
{ {
if (more) if (more)
break; break;
@ -4917,6 +5008,24 @@ mips_ip (str, ip)
offset_reloc = BFD_RELOC_MIPS_JMP; offset_reloc = BFD_RELOC_MIPS_JMP;
continue; continue;
case 'N': /* 3 bit branch condition code */
case 'M': /* 3 bit compare condition code */
my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr);
if ((unsigned long) imm_expr.X_add_number > 7)
{
as_warn ("Condition code > 7 (%ld)",
(long) imm_expr.X_add_number);
imm_expr.X_add_number &= 7;
}
if (*args == 'N')
ip->insn_opcode |= imm_expr.X_add_number << OP_SH_BCC;
else
ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CCC;
imm_expr.X_op = O_absent;
s = expr_end;
continue;
default: default:
fprintf (stderr, "bad char = '%c'\n", *args); fprintf (stderr, "bad char = '%c'\n", *args);
internalError (); internalError ();
@ -5131,30 +5240,32 @@ struct option md_longopts[] = {
{"mips2", no_argument, NULL, OPTION_MIPS2}, {"mips2", no_argument, NULL, OPTION_MIPS2},
#define OPTION_MIPS3 (OPTION_MD_BASE + 3) #define OPTION_MIPS3 (OPTION_MD_BASE + 3)
{"mips3", no_argument, NULL, OPTION_MIPS3}, {"mips3", no_argument, NULL, OPTION_MIPS3},
#define OPTION_MCPU (OPTION_MD_BASE + 4) #define OPTION_MIPS4 (OPTION_MD_BASE + 4)
{"mips4", no_argument, NULL, OPTION_MIPS4},
#define OPTION_MCPU (OPTION_MD_BASE + 5)
{"mcpu", required_argument, NULL, OPTION_MCPU}, {"mcpu", required_argument, NULL, OPTION_MCPU},
#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 5) #define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6)
{"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC}, {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
#define OPTION_TRAP (OPTION_MD_BASE + 8) #define OPTION_TRAP (OPTION_MD_BASE + 9)
{"trap", no_argument, NULL, OPTION_TRAP}, {"trap", no_argument, NULL, OPTION_TRAP},
{"no-break", no_argument, NULL, OPTION_TRAP}, {"no-break", no_argument, NULL, OPTION_TRAP},
#define OPTION_BREAK (OPTION_MD_BASE + 9) #define OPTION_BREAK (OPTION_MD_BASE + 10)
{"break", no_argument, NULL, OPTION_BREAK}, {"break", no_argument, NULL, OPTION_BREAK},
{"no-trap", no_argument, NULL, OPTION_BREAK}, {"no-trap", no_argument, NULL, OPTION_BREAK},
#define OPTION_EB (OPTION_MD_BASE + 10) #define OPTION_EB (OPTION_MD_BASE + 11)
{"EB", no_argument, NULL, OPTION_EB}, {"EB", no_argument, NULL, OPTION_EB},
#define OPTION_EL (OPTION_MD_BASE + 11) #define OPTION_EL (OPTION_MD_BASE + 12)
{"EL", no_argument, NULL, OPTION_EL}, {"EL", no_argument, NULL, OPTION_EL},
#define OPTION_M4650 (OPTION_MD_BASE + 12) #define OPTION_M4650 (OPTION_MD_BASE + 13)
{"m4650", no_argument, NULL, OPTION_M4650}, {"m4650", no_argument, NULL, OPTION_M4650},
#define OPTION_NO_M4650 (OPTION_MD_BASE + 13) #define OPTION_NO_M4650 (OPTION_MD_BASE + 14)
{"no-m4650", no_argument, NULL, OPTION_NO_M4650}, {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
#ifdef OBJ_ELF #ifdef OBJ_ELF
#define OPTION_CALL_SHARED (OPTION_MD_BASE + 6) #define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
{"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},
#define OPTION_NON_SHARED (OPTION_MD_BASE + 7) #define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
{"non_shared", no_argument, NULL, OPTION_NON_SHARED}, {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
#endif #endif
@ -5233,6 +5344,12 @@ md_parse_option (c, arg)
mips_cpu = 4000; mips_cpu = 4000;
break; break;
case OPTION_MIPS4:
mips_isa = 4;
if (mips_cpu == -1)
mips_cpu = 8000;
break;
case OPTION_MCPU: case OPTION_MCPU:
{ {
char *p; char *p;
@ -5250,6 +5367,13 @@ md_parse_option (c, arg)
mips_cpu = -1; mips_cpu = -1;
switch (*p) switch (*p)
{ {
case '1':
if (strcmp (p, "10000") == 0
|| strcmp (p, "10k") == 0
|| strcmp (p, "10K") == 0)
mips_cpu = 10000;
break;
case '2': case '2':
if (strcmp (p, "2000") == 0 if (strcmp (p, "2000") == 0
|| strcmp (p, "2k") == 0 || strcmp (p, "2k") == 0
@ -5288,6 +5412,13 @@ md_parse_option (c, arg)
mips_cpu = 6000; mips_cpu = 6000;
break; break;
case '8':
if (strcmp (p, "8000") == 0
|| strcmp (p, "8k") == 0
|| strcmp (p, "8K") == 0)
mips_cpu = 8000;
break;
case 'o': case 'o':
if (strcmp (p, "orion") == 0) if (strcmp (p, "orion") == 0)
mips_cpu = 4600; mips_cpu = 4600;
@ -5378,6 +5509,9 @@ MIPS options:\n\
-mips1, -mcpu=r{2,3}000 generate code for r2000 and r3000\n\ -mips1, -mcpu=r{2,3}000 generate code for r2000 and r3000\n\
-mips2, -mcpu=r6000 generate code for r6000\n\ -mips2, -mcpu=r6000 generate code for r6000\n\
-mips3, -mcpu=r4000 generate code for r4000\n\ -mips3, -mcpu=r4000 generate code for r4000\n\
-mips4, -mcpu=r8000 generate code for r8000\n\
-m4650 permit -m4650 instructions\n\
-no-m4650 do not permit -m4650 instructions\n\
-O0 remove unneeded NOPs, do not swap branches\n\ -O0 remove unneeded NOPs, do not swap branches\n\
-O remove unneeded NOPs and swap branches\n\ -O remove unneeded NOPs and swap branches\n\
--trap, --no-break trap exception on div by 0 and mult overflow\n\ --trap, --no-break trap exception on div by 0 and mult overflow\n\
@ -6124,7 +6258,7 @@ s_mipsset (x)
isa = atoi (name + 4); isa = atoi (name + 4);
if (isa == 0) if (isa == 0)
mips_isa = file_mips_isa; mips_isa = file_mips_isa;
else if (isa < 1 || isa > 3) else if (isa < 1 || isa > 4)
as_bad ("unknown ISA level"); as_bad ("unknown ISA level");
else else
mips_isa = isa; mips_isa = isa;
@ -6368,6 +6502,61 @@ md_section_align (seg, addr)
relaxing here, and the final size is encoded in the subtype relaxing here, and the final size is encoded in the subtype
information. */ information. */
/* Utility routine, called from above as well. If called while the
input file is still being read, it's only an approximation. (For
example, a symbol may later become defined which appeared to be
undefined earlier.) */
static int nopic_need_relax (sym)
symbolS *sym;
{
if (sym == 0)
return 0;
#ifdef GPOPT
{
const char *symname;
int change;
/* Find out whether this symbol can be referenced off the GP
register. It can be if it is smaller than the -G size or if it
is in the .sdata or .sbss section. Certain symbols can not be
referenced off the GP, although it appears as though they can. */
symname = S_GET_NAME (sym);
if (symname != (const char *) NULL
&& (strcmp (symname, "eprol") == 0
|| strcmp (symname, "etext") == 0
|| strcmp (symname, "_gp") == 0
|| strcmp (symname, "edata") == 0
|| strcmp (symname, "_fbss") == 0
|| strcmp (symname, "_fdata") == 0
|| strcmp (symname, "_ftext") == 0
|| strcmp (symname, "end") == 0
|| strcmp (symname, "_gp_disp") == 0))
change = 1;
else if (! S_IS_DEFINED (sym)
&& ((sym->ecoff_extern_size != 0
&& sym->ecoff_extern_size <= g_switch_value)
|| (S_GET_VALUE (sym) != 0
&& S_GET_VALUE (sym) <= g_switch_value)))
change = 0;
else
{
const char *segname;
segname = segment_name (S_GET_SEGMENT (sym));
assert (strcmp (segname, ".lit8") != 0
&& strcmp (segname, ".lit4") != 0);
change = (strcmp (segname, ".sdata") != 0
&& strcmp (segname, ".sbss") != 0);
}
return change;
}
#else /* ! defined (GPOPT) */
/* We are not optimizing for the GP register. */
return 1;
#endif /* ! defined (GPOPT) */
}
/*ARGSUSED*/ /*ARGSUSED*/
int int
md_estimate_size_before_relax (fragp, segtype) md_estimate_size_before_relax (fragp, segtype)
@ -6378,46 +6567,7 @@ md_estimate_size_before_relax (fragp, segtype)
if (mips_pic == NO_PIC) if (mips_pic == NO_PIC)
{ {
#ifdef GPOPT change = nopic_need_relax (fragp->fr_symbol);
const char *symname;
/* Find out whether this symbol can be referenced off the GP
register. It can be if it is smaller than the -G size or if
it is in the .sdata or .sbss section. Certain symbols can
not be referenced off the GP, although it appears as though
they can. */
symname = S_GET_NAME (fragp->fr_symbol);
if (symname != (const char *) NULL
&& (strcmp (symname, "eprol") == 0
|| strcmp (symname, "etext") == 0
|| strcmp (symname, "_gp") == 0
|| strcmp (symname, "edata") == 0
|| strcmp (symname, "_fbss") == 0
|| strcmp (symname, "_fdata") == 0
|| strcmp (symname, "_ftext") == 0
|| strcmp (symname, "end") == 0
|| strcmp (symname, "_gp_disp") == 0))
change = 1;
else if (! S_IS_DEFINED (fragp->fr_symbol)
&& ((fragp->fr_symbol->ecoff_extern_size != 0
&& fragp->fr_symbol->ecoff_extern_size <= g_switch_value)
|| (S_GET_VALUE (fragp->fr_symbol) != 0
&& S_GET_VALUE (fragp->fr_symbol) <= g_switch_value)))
change = 0;
else
{
const char *segname;
segname = segment_name (S_GET_SEGMENT (fragp->fr_symbol));
assert (strcmp (segname, ".lit8") != 0
&& strcmp (segname, ".lit4") != 0);
change = (strcmp (segname, ".sdata") != 0
&& strcmp (segname, ".sbss") != 0);
}
#else /* ! defined (GPOPT) */
/* We are not optimizing for the GP register. */
change = 1;
#endif /* ! defined (GPOPT) */
} }
else if (mips_pic == SVR4_PIC) else if (mips_pic == SVR4_PIC)
{ {