* config/tc-mips.c (prev_insn_reloc_type): New static variable.
authorIan Lance Taylor <ian@airs.com>
Sun, 15 Dec 1996 03:42:36 +0000 (03:42 +0000)
committerIan Lance Taylor <ian@airs.com>
Sun, 15 Dec 1996 03:42:36 +0000 (03:42 +0000)
(RELAX_MIPS16_ENCODE): Add dslot and jal_dslot arguments, and
store them.  Adjust other RELAX_MIPS16 macros.
(RELAX_MIPS16_DSLOT): Define.
(RELAX_MIPS16_JAL_DSLOT): Define.
(append_insn): Pass new arguments to RELAX_MIPS16_ENCODE.  Correct
handling of whether previous instruction has a fixup.  Set
prev_insn_reloc_type.
(mips_no_prev_insn): Clear prev_insn_reloc_type.
(mips16_extended_frag): Use the right base address for a PC
relative add or load.
(md_convert_frag): Likewise.  If a PC relative add or load is
used, record the alignment for the section.

gas/ChangeLog
gas/config/tc-mips.c

index 22567b9e1a1a033ca518a042a2289ea6f9bfffab..917bbab6d7fd6d222aa0e4595aae7f1d5f0938f0 100644 (file)
@@ -1,3 +1,19 @@
+Sat Dec 14 22:37:27 1996  Ian Lance Taylor  <ian@cygnus.com>
+
+       * config/tc-mips.c (prev_insn_reloc_type): New static variable.
+       (RELAX_MIPS16_ENCODE): Add dslot and jal_dslot arguments, and
+       store them.  Adjust other RELAX_MIPS16 macros.
+       (RELAX_MIPS16_DSLOT): Define.
+       (RELAX_MIPS16_JAL_DSLOT): Define.
+       (append_insn): Pass new arguments to RELAX_MIPS16_ENCODE.  Correct
+       handling of whether previous instruction has a fixup.  Set
+       prev_insn_reloc_type.
+       (mips_no_prev_insn): Clear prev_insn_reloc_type.
+       (mips16_extended_frag): Use the right base address for a PC
+       relative add or load.
+       (md_convert_frag): Likewise.  If a PC relative add or load is
+       used, record the alignment for the section.
+
 Fri Dec 13 13:00:33 1996  Ian Lance Taylor  <ian@cygnus.com>
 
        * write.c (adjust_reloc_syms): Don't reduce a reloc against a
index 1bd8f9163fbd80a7000e500d9500e12df581120c..13f284fe095c75a541e3aaf90ec0582050fa42bf 100644 (file)
@@ -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)
 \f
 /* 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);