mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-26 05:47:26 +08:00
gas/
* config/tc-mips.c (append_method): New enum. (can_swap_branch_p, get_append_method): New functions. (append_insn): Use get_append_method to decide how the instruction should be added.
This commit is contained in:
@ -1,3 +1,10 @@
|
|||||||
|
2011-06-29 Richard Sandiford <rdsandiford@googlemail.com>
|
||||||
|
|
||||||
|
* config/tc-mips.c (append_method): New enum.
|
||||||
|
(can_swap_branch_p, get_append_method): New functions.
|
||||||
|
(append_insn): Use get_append_method to decide how the instruction
|
||||||
|
should be added.
|
||||||
|
|
||||||
2011-06-29 Richard Sandiford <rdsandiford@googlemail.com>
|
2011-06-29 Richard Sandiford <rdsandiford@googlemail.com>
|
||||||
|
|
||||||
* config/tc-mips.c (append_insn): Remove bogus goto.
|
* config/tc-mips.c (append_insn): Remove bogus goto.
|
||||||
|
@ -121,6 +121,21 @@ extern int target_big_endian;
|
|||||||
? ".rodata" \
|
? ".rodata" \
|
||||||
: (abort (), ""))
|
: (abort (), ""))
|
||||||
|
|
||||||
|
/* Ways in which an instruction can be "appended" to the output. */
|
||||||
|
enum append_method {
|
||||||
|
/* Just add it normally. */
|
||||||
|
APPEND_ADD,
|
||||||
|
|
||||||
|
/* Add it normally and then add a nop. */
|
||||||
|
APPEND_ADD_WITH_NOP,
|
||||||
|
|
||||||
|
/* Turn an instruction with a delay slot into a "compact" version. */
|
||||||
|
APPEND_ADD_COMPACT,
|
||||||
|
|
||||||
|
/* Insert the instruction before the last one. */
|
||||||
|
APPEND_SWAP
|
||||||
|
};
|
||||||
|
|
||||||
/* Information about an instruction, including its format, operands
|
/* Information about an instruction, including its format, operands
|
||||||
and fixups. */
|
and fixups. */
|
||||||
struct mips_cl_insn
|
struct mips_cl_insn
|
||||||
@ -3073,6 +3088,168 @@ fix_loongson2f (struct mips_cl_insn * ip)
|
|||||||
fix_loongson2f_jump (ip);
|
fix_loongson2f_jump (ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* IP is a branch that has a delay slot, and we need to fill it
|
||||||
|
automatically. Return true if we can do that by swapping IP
|
||||||
|
with the previous instruction. */
|
||||||
|
|
||||||
|
static bfd_boolean
|
||||||
|
can_swap_branch_p (struct mips_cl_insn *ip)
|
||||||
|
{
|
||||||
|
unsigned long pinfo, prev_pinfo;
|
||||||
|
unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
|
||||||
|
|
||||||
|
/* -O2 and above is required for this optimization. */
|
||||||
|
if (mips_optimize < 2)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If we have seen .set volatile or .set nomove, don't optimize. */
|
||||||
|
if (mips_opts.nomove)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* We can't swap if the previous instruction's position is fixed. */
|
||||||
|
if (history[0].fixed_p)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the previous previous insn was in a .set noreorder, we can't
|
||||||
|
swap. Actually, the MIPS assembler will swap in this situation.
|
||||||
|
However, gcc configured -with-gnu-as will generate code like
|
||||||
|
|
||||||
|
.set noreorder
|
||||||
|
lw $4,XXX
|
||||||
|
.set reorder
|
||||||
|
INSN
|
||||||
|
bne $4,$0,foo
|
||||||
|
|
||||||
|
in which we can not swap the bne and INSN. If gcc is not configured
|
||||||
|
-with-gnu-as, it does not output the .set pseudo-ops. */
|
||||||
|
if (history[1].noreorder_p)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the previous instruction had a fixup in mips16 mode, we can not
|
||||||
|
swap. This normally means that the previous instruction was a 4
|
||||||
|
byte branch anyhow. */
|
||||||
|
if (mips_opts.mips16 && history[0].fixp[0])
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the branch is itself the target of a branch, we can not swap.
|
||||||
|
We cheat on this; all we check for is whether there is a label on
|
||||||
|
this instruction. If there are any branches to anything other than
|
||||||
|
a label, users must use .set noreorder. */
|
||||||
|
if (seg_info (now_seg)->label_list)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the previous instruction is in a variant frag other than this
|
||||||
|
branch's one, we cannot do the swap. This does not apply to the
|
||||||
|
mips16, which uses variant frags for different purposes. */
|
||||||
|
if (!mips_opts.mips16
|
||||||
|
&& history[0].frag
|
||||||
|
&& history[0].frag->fr_type == rs_machine_dependent)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* We do not swap with a trap instruction, since it complicates trap
|
||||||
|
handlers to have the trap instruction be in a delay slot. */
|
||||||
|
prev_pinfo = history[0].insn_mo->pinfo;
|
||||||
|
if (prev_pinfo & INSN_TRAP)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the previous instruction is a sync, sync.l, or sync.p, we can
|
||||||
|
not swap. */
|
||||||
|
if (prev_pinfo & INSN_SYNC)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the previous instruction is an ERET or DERET, avoid the swap. */
|
||||||
|
if (history[0].insn_opcode == INSN_ERET)
|
||||||
|
return FALSE;
|
||||||
|
if (history[0].insn_opcode == INSN_DERET)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Check for conflicts between the branch and the instructions
|
||||||
|
before the candidate delay slot. */
|
||||||
|
if (nops_for_insn (0, history + 1, ip) > 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Check for conflicts between the swapped sequence and the
|
||||||
|
target of the branch. */
|
||||||
|
if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the branch reads a register that the previous
|
||||||
|
instruction sets, we can not swap. */
|
||||||
|
gpr_read = gpr_read_mask (ip);
|
||||||
|
prev_gpr_write = gpr_write_mask (&history[0]);
|
||||||
|
if (gpr_read & prev_gpr_write)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the branch writes a register that the previous
|
||||||
|
instruction sets, we can not swap. */
|
||||||
|
gpr_write = gpr_write_mask (ip);
|
||||||
|
if (gpr_write & prev_gpr_write)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the branch writes a register that the previous
|
||||||
|
instruction reads, we can not swap. */
|
||||||
|
prev_gpr_read = gpr_read_mask (&history[0]);
|
||||||
|
if (gpr_write & prev_gpr_read)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If one instruction sets a condition code and the
|
||||||
|
other one uses a condition code, we can not swap. */
|
||||||
|
pinfo = ip->insn_mo->pinfo;
|
||||||
|
if ((pinfo & INSN_READ_COND_CODE)
|
||||||
|
&& (prev_pinfo & INSN_WRITE_COND_CODE))
|
||||||
|
return FALSE;
|
||||||
|
if ((pinfo & INSN_WRITE_COND_CODE)
|
||||||
|
&& (prev_pinfo & INSN_READ_COND_CODE))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* If the previous instruction uses the PC, we can not swap. */
|
||||||
|
if (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decide how we should add IP to the instruction stream. */
|
||||||
|
|
||||||
|
static enum append_method
|
||||||
|
get_append_method (struct mips_cl_insn *ip)
|
||||||
|
{
|
||||||
|
unsigned long pinfo;
|
||||||
|
|
||||||
|
/* The relaxed version of a macro sequence must be inherently
|
||||||
|
hazard-free. */
|
||||||
|
if (mips_relax.sequence == 2)
|
||||||
|
return APPEND_ADD;
|
||||||
|
|
||||||
|
/* We must not dabble with instructions in a ".set norerorder" block. */
|
||||||
|
if (mips_opts.noreorder)
|
||||||
|
return APPEND_ADD;
|
||||||
|
|
||||||
|
/* Otherwise, it's our responsibility to fill branch delay slots. */
|
||||||
|
pinfo = ip->insn_mo->pinfo;
|
||||||
|
if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
|
||||||
|
|| (pinfo & INSN_COND_BRANCH_DELAY))
|
||||||
|
{
|
||||||
|
if (can_swap_branch_p (ip))
|
||||||
|
return APPEND_SWAP;
|
||||||
|
|
||||||
|
if (mips_opts.mips16
|
||||||
|
&& ISA_SUPPORTS_MIPS16E
|
||||||
|
&& (pinfo & INSN_UNCOND_BRANCH_DELAY)
|
||||||
|
&& (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
|
||||||
|
return APPEND_ADD_COMPACT;
|
||||||
|
|
||||||
|
return APPEND_ADD_WITH_NOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We don't bother trying to track the target of branches, so there's
|
||||||
|
nothing we can use to fill a branch-likely slot. */
|
||||||
|
if (pinfo & INSN_COND_BRANCH_LIKELY)
|
||||||
|
return APPEND_ADD_WITH_NOP;
|
||||||
|
|
||||||
|
return APPEND_ADD;
|
||||||
|
}
|
||||||
|
|
||||||
/* IP is a MIPS16 instruction whose opcode we have just changed.
|
/* IP is a MIPS16 instruction whose opcode we have just changed.
|
||||||
Point IP->insn_mo to the new opcode's definition. */
|
Point IP->insn_mo to the new opcode's definition. */
|
||||||
|
|
||||||
@ -3101,9 +3278,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
|||||||
{
|
{
|
||||||
unsigned long prev_pinfo, pinfo;
|
unsigned long prev_pinfo, pinfo;
|
||||||
unsigned long prev_pinfo2, pinfo2;
|
unsigned long prev_pinfo2, pinfo2;
|
||||||
relax_stateT prev_insn_frag_type = 0;
|
|
||||||
bfd_boolean relaxed_branch = FALSE;
|
bfd_boolean relaxed_branch = FALSE;
|
||||||
segment_info_type *si = seg_info (now_seg);
|
enum append_method method;
|
||||||
|
|
||||||
if (mips_fix_loongson2f)
|
if (mips_fix_loongson2f)
|
||||||
fix_loongson2f (ip);
|
fix_loongson2f (ip);
|
||||||
@ -3273,6 +3449,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
method = get_append_method (ip);
|
||||||
|
|
||||||
#ifdef OBJ_ELF
|
#ifdef OBJ_ELF
|
||||||
/* The value passed to dwarf2_emit_insn is the distance between
|
/* The value passed to dwarf2_emit_insn is the distance between
|
||||||
the beginning of the current instruction and the address that
|
the beginning of the current instruction and the address that
|
||||||
@ -3282,10 +3460,6 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
|||||||
dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0);
|
dwarf2_emit_insn (mips_opts.mips16 ? -1 : 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Record the frag type before frag_var. */
|
|
||||||
if (history[0].frag)
|
|
||||||
prev_insn_frag_type = history[0].frag->fr_type;
|
|
||||||
|
|
||||||
if (address_expr
|
if (address_expr
|
||||||
&& *reloc_type == BFD_RELOC_16_PCREL_S2
|
&& *reloc_type == BFD_RELOC_16_PCREL_S2
|
||||||
&& (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
|
&& (pinfo & INSN_UNCOND_BRANCH_DELAY || pinfo & INSN_COND_BRANCH_DELAY
|
||||||
@ -3469,116 +3643,30 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
|||||||
mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
|
mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
|
||||||
mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
|
mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
|
||||||
|
|
||||||
if (mips_relax.sequence != 2 && !mips_opts.noreorder)
|
switch (method)
|
||||||
{
|
|
||||||
/* Filling the branch delay slot is more complex. We try to
|
|
||||||
switch the branch with the previous instruction, which we can
|
|
||||||
do if the previous instruction does not set up a condition
|
|
||||||
that the branch tests and if the branch is not itself the
|
|
||||||
target of any branch. */
|
|
||||||
if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
|
|
||||||
|| (pinfo & INSN_COND_BRANCH_DELAY))
|
|
||||||
{
|
|
||||||
if (mips_optimize < 2
|
|
||||||
/* If we have seen .set volatile or .set nomove, don't
|
|
||||||
optimize. */
|
|
||||||
|| mips_opts.nomove != 0
|
|
||||||
/* We can't swap if the previous instruction's position
|
|
||||||
is fixed. */
|
|
||||||
|| history[0].fixed_p
|
|
||||||
/* If the previous previous insn was in a .set
|
|
||||||
noreorder, we can't swap. Actually, the MIPS
|
|
||||||
assembler will swap in this situation. However, gcc
|
|
||||||
configured -with-gnu-as will generate code like
|
|
||||||
.set noreorder
|
|
||||||
lw $4,XXX
|
|
||||||
.set reorder
|
|
||||||
INSN
|
|
||||||
bne $4,$0,foo
|
|
||||||
in which we can not swap the bne and INSN. If gcc is
|
|
||||||
not configured -with-gnu-as, it does not output the
|
|
||||||
.set pseudo-ops. */
|
|
||||||
|| history[1].noreorder_p
|
|
||||||
/* If the branch is itself the target of a branch, we
|
|
||||||
can not swap. We cheat on this; all we check for is
|
|
||||||
whether there is a label on this instruction. If
|
|
||||||
there are any branches to anything other than a
|
|
||||||
label, users must use .set noreorder. */
|
|
||||||
|| si->label_list != NULL
|
|
||||||
/* If the previous instruction is in a variant frag
|
|
||||||
other than this branch's one, we cannot do the swap.
|
|
||||||
This does not apply to the mips16, which uses variant
|
|
||||||
frags for different purposes. */
|
|
||||||
|| (! mips_opts.mips16
|
|
||||||
&& prev_insn_frag_type == rs_machine_dependent)
|
|
||||||
/* Check for conflicts between the branch and the instructions
|
|
||||||
before the candidate delay slot. */
|
|
||||||
|| nops_for_insn (0, history + 1, ip) > 0
|
|
||||||
/* Check for conflicts between the swapped sequence and the
|
|
||||||
target of the branch. */
|
|
||||||
|| nops_for_sequence (2, 0, history + 1, ip, history) > 0
|
|
||||||
/* We do not swap with a trap instruction, since it
|
|
||||||
complicates trap handlers to have the trap
|
|
||||||
instruction be in a delay slot. */
|
|
||||||
|| (prev_pinfo & INSN_TRAP)
|
|
||||||
/* If the branch reads a register that the previous
|
|
||||||
instruction sets, we can not swap. */
|
|
||||||
|| (gpr_read_mask (ip) & gpr_write_mask (&history[0])) != 0
|
|
||||||
/* If the branch writes a register that the previous
|
|
||||||
instruction sets, we can not swap. */
|
|
||||||
|| (gpr_write_mask (ip) & gpr_write_mask (&history[0])) != 0
|
|
||||||
/* If the branch writes a register that the previous
|
|
||||||
instruction reads, we can not swap. */
|
|
||||||
|| (gpr_write_mask (ip) & gpr_read_mask (&history[0])) != 0
|
|
||||||
/* If one instruction sets a condition code and the
|
|
||||||
other one uses a condition code, we can not swap. */
|
|
||||||
|| ((pinfo & INSN_READ_COND_CODE)
|
|
||||||
&& (prev_pinfo & INSN_WRITE_COND_CODE))
|
|
||||||
|| ((pinfo & INSN_WRITE_COND_CODE)
|
|
||||||
&& (prev_pinfo & INSN_READ_COND_CODE))
|
|
||||||
/* If the previous instruction uses the PC, we can not
|
|
||||||
swap. */
|
|
||||||
|| (mips_opts.mips16
|
|
||||||
&& (prev_pinfo & MIPS16_INSN_READ_PC))
|
|
||||||
/* If the previous instruction had a fixup in mips16
|
|
||||||
mode, we can not swap. This normally means that the
|
|
||||||
previous instruction was a 4 byte branch anyhow. */
|
|
||||||
|| (mips_opts.mips16 && history[0].fixp[0])
|
|
||||||
/* If the previous instruction is a sync, sync.l, or
|
|
||||||
sync.p, we can not swap. */
|
|
||||||
|| (prev_pinfo & INSN_SYNC)
|
|
||||||
/* If the previous instruction is an ERET or
|
|
||||||
DERET, avoid the swap. */
|
|
||||||
|| (history[0].insn_opcode == INSN_ERET)
|
|
||||||
|| (history[0].insn_opcode == INSN_DERET))
|
|
||||||
{
|
|
||||||
if (mips_opts.mips16
|
|
||||||
&& (pinfo & INSN_UNCOND_BRANCH_DELAY)
|
|
||||||
&& (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31))
|
|
||||||
&& ISA_SUPPORTS_MIPS16E)
|
|
||||||
{
|
{
|
||||||
|
case APPEND_ADD:
|
||||||
|
insert_into_history (0, 1, ip);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case APPEND_ADD_WITH_NOP:
|
||||||
|
insert_into_history (0, 1, ip);
|
||||||
|
emit_nop ();
|
||||||
|
if (mips_relax.sequence)
|
||||||
|
mips_relax.sizes[mips_relax.sequence - 1] += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case APPEND_ADD_COMPACT:
|
||||||
/* Convert MIPS16 jr/jalr into a "compact" jump. */
|
/* Convert MIPS16 jr/jalr into a "compact" jump. */
|
||||||
|
gas_assert (mips_opts.mips16);
|
||||||
ip->insn_opcode |= 0x0080;
|
ip->insn_opcode |= 0x0080;
|
||||||
find_altered_mips16_opcode (ip);
|
find_altered_mips16_opcode (ip);
|
||||||
install_insn (ip);
|
install_insn (ip);
|
||||||
insert_into_history (0, 1, ip);
|
insert_into_history (0, 1, ip);
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
/* We could do even better for unconditional branches to
|
|
||||||
portions of this object file; we could pick up the
|
|
||||||
instruction at the destination, put it in the delay
|
|
||||||
slot, and bump the destination address. */
|
|
||||||
insert_into_history (0, 1, ip);
|
|
||||||
emit_nop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mips_relax.sequence)
|
case APPEND_SWAP:
|
||||||
mips_relax.sizes[mips_relax.sequence - 1] += 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* It looks like we can actually do the swap. */
|
|
||||||
struct mips_cl_insn delay = history[0];
|
struct mips_cl_insn delay = history[0];
|
||||||
if (mips_opts.mips16)
|
if (mips_opts.mips16)
|
||||||
{
|
{
|
||||||
@ -3606,21 +3694,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
|||||||
delay.fixed_p = 1;
|
delay.fixed_p = 1;
|
||||||
insert_into_history (0, 1, &delay);
|
insert_into_history (0, 1, &delay);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (pinfo & INSN_COND_BRANCH_LIKELY)
|
|
||||||
{
|
|
||||||
/* We don't yet optimize a branch likely. What we should do
|
|
||||||
is look at the target, copy the instruction found there
|
|
||||||
into the delay slot, and increment the branch to jump to
|
|
||||||
the next instruction. */
|
|
||||||
insert_into_history (0, 1, ip);
|
|
||||||
emit_nop ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
insert_into_history (0, 1, ip);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
insert_into_history (0, 1, ip);
|
|
||||||
|
|
||||||
/* If we have just completed an unconditional branch, clear the history. */
|
/* If we have just completed an unconditional branch, clear the history. */
|
||||||
if ((history[1].insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY)
|
if ((history[1].insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY)
|
||||||
|
Reference in New Issue
Block a user