diff options
Diffstat (limited to 'gas/config/tc-mips.c')
-rw-r--r-- | gas/config/tc-mips.c | 110 |
1 files changed, 84 insertions, 26 deletions
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index 1bd8f91..13f284f 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -304,6 +304,9 @@ static struct frag *prev_insn_frag; /* The offset into prev_insn_frag for the previous instruction. */ static long prev_insn_where; +/* The reloc type for the previous instruction, if any. */ +static bfd_reloc_code_real_type prev_insn_reloc_type; + /* The reloc for the previous instruction, if any. */ static fixS *prev_insn_fixp; @@ -452,30 +455,34 @@ static const int mips16_to_32_reg_map[] = the same time that we support the relaxation described above. We use the high bit of the subtype field to distinguish these cases. - The information we store for this type of relaxation is simply the - argument code found in the opcode file for this relocation, and - whether the user explicitly requested a small or extended form. - That tells us the size of the value, and how it should be stored. - We also store whether the fragment is considered to be extended or - not. We also store whether this is known to be a branch to a - different section, whether we have tried to relax this frag yet, - and whether we have ever extended a PC relative fragment because of - a shift count. */ -#define RELAX_MIPS16_ENCODE(type, small, ext) \ - (0x80000000 \ - | ((type) & 0xff) \ - | ((small) ? 0x100 : 0) \ - | ((ext) ? 0x200 : 0)) + The information we store for this type of relaxation is the + argument code found in the opcode file for this relocation, whether + the user explicitly requested a small or extended form, and whether + the relocation is in a jump or jal delay slot. That tells us the + size of the value, and how it should be stored. We also store + whether the fragment is considered to be extended or not. We also + store whether this is known to be a branch to a different section, + whether we have tried to relax this frag yet, and whether we have + ever extended a PC relative fragment because of a shift count. */ +#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \ + (0x80000000 \ + | ((type) & 0xff) \ + | ((small) ? 0x100 : 0) \ + | ((ext) ? 0x200 : 0) \ + | ((dslot) ? 0x400 : 0) \ + | ((jal_dslot) ? 0x800 : 0)) #define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0) #define RELAX_MIPS16_TYPE(i) ((i) & 0xff) #define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0) #define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0) -#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x400) != 0) -#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x400) -#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x400) -#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x800) != 0) -#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x800) -#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x800) +#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0) +#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0) +#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0) +#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000) +#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000) +#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0) +#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000) +#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000) /* Prototypes for static functions. */ @@ -1384,7 +1391,11 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi) assert (mips16 && address_expr != NULL); f = frag_var (rs_machine_dependent, 4, 0, RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED, - mips16_small, mips16_ext), + mips16_small, mips16_ext, + (prev_pinfo + & INSN_UNCOND_BRANCH_DELAY), + (prev_insn_reloc_type + == BFD_RELOC_MIPS16_JMP)), make_expr_symbol (address_expr), (long) 0, (char *) NULL); } @@ -1821,6 +1832,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi) prev_prev_insn.insn_mo = &dummy_opcode; prev_insn.insn_mo = &dummy_opcode; } + + prev_insn_fixp = NULL; + prev_insn_reloc_type = BFD_RELOC_UNUSED; + prev_insn_extended = 0; } else if (pinfo & INSN_COND_BRANCH_LIKELY) { @@ -1832,6 +1847,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi) /* Update the previous insn information. */ prev_prev_insn = *ip; prev_insn.insn_mo = &dummy_opcode; + prev_insn_fixp = NULL; + prev_insn_reloc_type = BFD_RELOC_UNUSED; + prev_insn_extended = 0; } else { @@ -1846,17 +1864,28 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi) immediately; since this insn is not a branch, we know it is not in a delay slot. */ prev_insn_is_delay_slot = 0; + + prev_insn_fixp = fixp; + prev_insn_reloc_type = reloc_type; + if (mips16) + prev_insn_extended = (ip->use_extend + || reloc_type > BFD_RELOC_UNUSED); } prev_prev_insn_unreordered = prev_insn_unreordered; prev_insn_unreordered = 0; prev_insn_frag = frag_now; prev_insn_where = f - frag_now->fr_literal; - prev_insn_fixp = fixp; - if (mips16) - prev_insn_extended = ip->use_extend || reloc_type > BFD_RELOC_UNUSED; prev_insn_valid = 1; } + else + { + /* We need to record a bit of uninformation even when we are not + reordering, in order to determine the base address for mips16 + PC relative relocs. */ + prev_insn = *ip; + prev_insn_reloc_type = reloc_type; + } /* We just output an insn, so the next one doesn't have a label. */ insn_label = NULL; @@ -1874,6 +1903,7 @@ mips_no_prev_insn () prev_insn_is_delay_slot = 0; prev_insn_unreordered = 0; prev_insn_extended = 0; + prev_insn_reloc_type = BFD_RELOC_UNUSED; prev_prev_insn_unreordered = 0; insn_label = NULL; } @@ -9163,7 +9193,20 @@ mips16_extended_frag (fragp, sec, stretch) val += stretch; } - addr = fragp->fr_address + fragp->fr_fix + 2; + addr = fragp->fr_address + fragp->fr_fix; + + /* The base address rules are complicated. The base address of + a branch is the following instruction. The base address of a + PC relative load or add is the instruction itself, but if it + is extended add 2, and if it is in a delay slot (in which + case it can not be extended) use the address of the + instruction whose delay slot it is in. */ + if (type == 'p' || type == 'q') + addr += 2; + else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)) + addr -= 4; + else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype)) + addr -= 2; /* If we are currently assuming that this frag should be extended, then the current address is two bytes higher. */ @@ -9558,11 +9601,26 @@ md_convert_frag (abfd, asec, fragp) { addressT addr; - addr = fragp->fr_address + fragp->fr_fix + 2; + addr = fragp->fr_address + fragp->fr_fix; + + /* The rules for the base address of a PC relative reloc are + complicated; see mips16_extended_frag. */ + if (type == 'p' || type == 'q') + addr += 2; + else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)) + addr -= 4; + else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype)) + addr -= 2; + if (ext) addr += 2; addr &= ~ (addressT) ((1 << op->shift) - 1); val -= addr; + + /* Make sure the section winds up with the alignment we have + assumed. */ + if (op->shift > 0) + record_alignment (asec, op->shift); } buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix); |