* config/tc-mips.c (mips16_extended_frag): Correct base address
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 3eadbbe561a97d8a2b0eef0a8516a86b4f36b27e..8be02a11fc23c3ad7f416e9c5f6affebd0a76d6f 100644 (file)
@@ -148,6 +148,11 @@ static int mips_4010 = -1;
 /* Whether the 4100 MADD16 and DMADD16 are permitted. */
 static int mips_4100 = -1;
 
+/* start-sanitize-r5900 */
+/* Whether Toshiba r5900 instructions are permitted. */
+static int mips_5900 = -1;
+/* end-sanitize-r5900 */
+
 /* Whether the processor uses hardware interlocks, and thus does not
    require nops to be inserted.  */
 static int interlocks = -1;
@@ -319,6 +324,22 @@ static int prev_insn_extended;
    noreorder.  */
 static int prev_prev_insn_unreordered;
 
+/* If this is set, it points to a frag holding nop instructions which
+   were inserted before the start of a noreorder section.  If those
+   nops turn out to be unnecessary, the size of the frag can be
+   decreased.  */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag.  */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+   prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag.  */
+static int prev_nop_frag_since;
+
 /* For ECOFF and ELF, relocations against symbols are done in two
    parts, with a HI relocation and a LO relocation.  Each relocation
    has only 16 bits of space to store an addend.  This means that in
@@ -498,7 +519,7 @@ static void append_insn PARAMS ((char *place,
                                 expressionS * p,
                                 bfd_reloc_code_real_type r,
                                 boolean));
-static void mips_no_prev_insn PARAMS ((void));
+static void mips_no_prev_insn PARAMS ((int));
 static void mips_emit_delays PARAMS ((boolean));
 #ifdef USE_STDARG
 static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
@@ -543,6 +564,7 @@ static void s_cpload PARAMS ((int));
 static void s_cprestore PARAMS ((int));
 static void s_gpword PARAMS ((int));
 static void s_cpadd PARAMS ((int));
+static void s_insn PARAMS ((int));
 static void md_obj_begin PARAMS ((void));
 static void md_obj_end PARAMS ((void));
 static long get_number PARAMS ((void));
@@ -582,6 +604,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cprestore", s_cprestore, 0},
   {"gpword", s_gpword, 0},
   {"cpadd", s_cpadd, 0},
+  {"insn", s_insn, 0},
 
  /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
@@ -780,6 +803,18 @@ md_begin ()
          if (mips_cpu == -1)
            mips_cpu = 5000;
        }
+      /* start-sanitize-r5900 */
+      else if (strcmp (cpu, "r5900") == 0
+              || strcmp (cpu, "mips64vr5900") == 0
+               || strcmp (cpu, "mips64vr5900el") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 5900;
+          if (mips_5900 == -1)
+            mips_5900 = 1;
+       }
+      /* end-sanitize-r5900 */
       else if (strcmp (cpu, "r8000") == 0
               || strcmp (cpu, "mips4") == 0)
        {
@@ -827,6 +862,11 @@ md_begin ()
   if (mips_4100 < 0)
     mips_4100 = 0;
 
+  /* start-sanitize-r5900 */
+  if (mips_5900 < 0)
+    mips_5900 = 0;
+  /* end-sanitize-r5900 */
+
   if (mips_4010 || mips_4100 || mips_cpu == 4300)
     interlocks = 1;
   else
@@ -912,7 +952,32 @@ md_begin ()
             && strcmp (mips16_opcodes[i].name, name) == 0);
     }
 
-  mips_no_prev_insn ();
+  /* We add all the general register names to the symbol table.  This
+     helps us detect invalid uses of them.  */
+  for (i = 0; i < 32; i++)
+    {
+      char buf[5];
+
+      sprintf (buf, "$%d", i);
+      symbol_table_insert (symbol_new (buf, reg_section, i,
+                                      &zero_address_frag));
+    }
+  symbol_table_insert (symbol_new ("$fp", reg_section, FP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$sp", reg_section, SP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$gp", reg_section, GP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$at", reg_section, AT,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$kt0", reg_section, KT0,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$pc", reg_section, -1,
+                                  &zero_address_frag));
+
+  mips_no_prev_insn (false);
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -1206,8 +1271,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
   prev_pinfo = prev_insn.insn_mo->pinfo;
   pinfo = ip->insn_mo->pinfo;
 
-  if (place == NULL && ! mips_noreorder)
+  if (place == NULL && (! mips_noreorder || prev_nop_frag != NULL))
     {
+      int prev_prev_nop;
+
       /* If the previous insn required any delay slots, see if we need
         to insert a NOP or two.  There are eight kinds of possible
         hazards, of which an instruction can have at most one type.
@@ -1345,6 +1412,11 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            nops += 2;
        }
 
+      /* If the previous instruction was in a noreorder section, then
+         we don't want to insert the nop after all.  */
+      if (prev_insn_unreordered)
+       nops = 0;
+
       /* There are two cases which require two intervening
         instructions: 1) setting the condition codes using a move to
         coprocessor instruction which requires a general coprocessor
@@ -1353,30 +1425,39 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
         which have interlocks).  If we are not already emitting a NOP
         instruction, we must check for these cases compared to the
         instruction previous to the previous instruction.  */
-      if (nops == 0
-         && ((! mips16
-              && mips_isa < 4
-              && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
-              && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              && (pinfo & INSN_READ_COND_CODE)
-               && ! cop_interlocks)
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
-                 && (pinfo & INSN_WRITE_LO)
-                 && ! interlocks)
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                 && (pinfo & INSN_WRITE_HI)
-                 && ! interlocks)))
+      if ((! mips16
+          && mips_isa < 4
+          && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+          && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+          && (pinfo & INSN_READ_COND_CODE)
+          && ! cop_interlocks)
+         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+             && (pinfo & INSN_WRITE_LO)
+             && ! interlocks)
+         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+             && (pinfo & INSN_WRITE_HI)
+             && ! interlocks))
+       prev_prev_nop = 1;
+      else
+       prev_prev_nop = 0;
+
+      if (prev_prev_insn_unreordered)
+       prev_prev_nop = 0;
+
+      if (prev_prev_nop && nops == 0)
        ++nops;
 
       /* If we are being given a nop instruction, don't bother with
         one of the nops we would otherwise output.  This will only
         happen when a nop instruction is used with mips_optimize set
         to 0.  */
-      if (nops > 0 && ip->insn_opcode == (mips16 ? 0x6500 : 0))
+      if (nops > 0
+         && ! mips_noreorder
+         && ip->insn_opcode == (mips16 ? 0x6500 : 0))
        --nops;
 
       /* Now emit the right number of NOP instructions.  */
-      if (nops > 0)
+      if (nops > 0 && ! mips_noreorder)
        {
          fragS *old_frag;
          unsigned long old_frag_offset;
@@ -1418,8 +1499,45 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            ecoff_fix_loc (old_frag, old_frag_offset);
 #endif
        }
+      else if (prev_nop_frag != NULL)
+       {
+         /* We have a frag holding nops we may be able to remove.  If
+             we don't need any nops, we can decrease the size of
+             prev_nop_frag by the size of one instruction.  If we do
+             need some nops, we count them in prev_nops_required. */
+         if (prev_nop_frag_since == 0)
+           {
+             if (nops == 0)
+               {
+                 prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+                 --prev_nop_frag_holds;
+               }
+             else
+               prev_nop_frag_required += nops;
+           }
+         else
+           {
+             if (prev_prev_nop == 0)
+               {
+                 prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+                 --prev_nop_frag_holds;
+               }
+             else
+               ++prev_nop_frag_required;
+           }
+
+         if (prev_nop_frag_holds <= prev_nop_frag_required)
+           prev_nop_frag = NULL;
+
+         ++prev_nop_frag_since;
+
+         /* Sanity check: by the time we reach the second instruction
+             after prev_nop_frag, we should have used up all the nops
+             one way or another.  */
+         assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+       }
     }
-  
+
   if (reloc_type > BFD_RELOC_UNUSED)
     {
       /* We need to set up a variant frag.  */
@@ -1444,7 +1562,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       f = frag_more (2);
     }
   else
-    f = frag_more (4);
+    {
+      if (mips16
+         && mips_noreorder
+         && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+       as_warn ("extended instruction in delay slot");
+
+      f = frag_more (4);
+    }
+
   fixp = NULL;
   if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
     {
@@ -1461,10 +1587,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              break;
 
            case BFD_RELOC_MIPS_JMP:
+             if ((address_expr->X_add_number & 3) != 0)
+               as_bad ("jump to misaligned address (0x%lx)",
+                       (unsigned long) address_expr->X_add_number);
              ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
              break;
 
            case BFD_RELOC_MIPS16_JMP:
+             if ((address_expr->X_add_number & 3) != 0)
+               as_bad ("jump to misaligned address (0x%lx)",
+                       (unsigned long) address_expr->X_add_number);
              ip->insn_opcode |=
                (((address_expr->X_add_number & 0x7c0000) << 3)
                 | ((address_expr->X_add_number & 0xf800000) >> 7)
@@ -1505,8 +1637,13 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
     }
 
-  if (! mips16 || reloc_type == BFD_RELOC_MIPS16_JMP)
+  if (! mips16)
     md_number_to_chars (f, ip->insn_opcode, 4);
+  else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+    {
+      md_number_to_chars (f, ip->insn_opcode >> 16, 2);
+      md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
+    }
   else
     {
       if (ip->use_extend)
@@ -1812,25 +1949,6 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                      fixp->fx_where = prev_insn_where;
                    }
                }
-             else if (reloc_type > BFD_RELOC_UNUSED)
-               {
-                 char *prev_f;
-                 char temp[2];
-
-                 /* We are in mips16 mode, and we have just created a
-                     variant frag.  We need to extract the old
-                     instruction from the end of the previous frag,
-                     and add it to a new frag.  */
-                 prev_f = prev_insn_frag->fr_literal + prev_insn_where;
-                 memcpy (temp, prev_f, 2);
-                 prev_insn_frag->fr_fix -= 2;
-                 if (prev_insn_frag->fr_type == rs_machine_dependent)
-                   {
-                     assert (prev_insn_where == prev_insn_frag->fr_fix);
-                     memcpy (prev_f, prev_f + 2, 2);
-                   }
-                 memcpy (frag_more (2), temp, 2);
-               }
              else
                {
                  char *prev_f;
@@ -1841,7 +1959,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  memcpy (temp, prev_f, 2);
                  memcpy (prev_f, f, 2);
                  if (reloc_type != BFD_RELOC_MIPS16_JMP)
-                   memcpy (f, temp, 2);
+                   {
+                     assert (reloc_type == BFD_RELOC_UNUSED);
+                     memcpy (f, temp, 2);
+                   }
                  else
                    {
                      memcpy (f, f + 2, 2);
@@ -1918,8 +2039,11 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       /* We need to record a bit of information even when we are not
          reordering, in order to determine the base address for mips16
          PC relative relocs.  */
+      prev_prev_insn = prev_insn;
       prev_insn = *ip;
       prev_insn_reloc_type = reloc_type;
+      prev_prev_insn_unreordered = prev_insn_unreordered;
+      prev_insn_unreordered = 1;
     }
 
   /* We just output an insn, so the next one doesn't have a label.  */
@@ -1927,13 +2051,22 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
 }
 
 /* This function forgets that there was any previous instruction or
-   label.  */
+   label.  If PRESERVE is non-zero, it remembers enough information to
+   know whether nops are needed before a noreorder section. */
 
 static void
-mips_no_prev_insn ()
+mips_no_prev_insn (preserve)
+     int preserve;
 {
-  prev_insn.insn_mo = &dummy_opcode;
-  prev_prev_insn.insn_mo = &dummy_opcode;
+  if (! preserve)
+    {
+      prev_insn.insn_mo = &dummy_opcode;
+      prev_prev_insn.insn_mo = &dummy_opcode;
+      prev_nop_frag = NULL;
+      prev_nop_frag_holds = 0;
+      prev_nop_frag_required = 0;
+      prev_nop_frag_since = 0;
+    }
   prev_insn_valid = 0;
   prev_insn_is_delay_slot = 0;
   prev_insn_unreordered = 0;
@@ -1955,9 +2088,9 @@ mips_emit_delays (insns)
 {
   if (! mips_noreorder)
     {
-      int nop;
+      int nops;
 
-      nop = 0;
+      nops = 0;
       if ((! mips16
           && mips_isa < 4
           && (! cop_interlocks
@@ -1975,7 +2108,7 @@ mips_emit_delays (insns)
                  & (INSN_LOAD_MEMORY_DELAY
                     | INSN_COPROC_MEMORY_DELAY))))
        {
-         nop = 1;
+         ++nops;
          if ((! mips16
               && mips_isa < 4
               && (! cop_interlocks
@@ -1983,7 +2116,10 @@ mips_emit_delays (insns)
              || (! interlocks
                  && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
                      || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-           emit_nop ();
+           ++nops;
+
+         if (prev_insn_unreordered)
+           nops = 0;
        }
       else if ((! mips16
                && mips_isa < 4
@@ -1992,12 +2128,37 @@ mips_emit_delays (insns)
               || (! interlocks
                   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
                       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-       nop = 1;
-      if (nop)
+       {
+         if (! prev_prev_insn_unreordered)
+           ++nops;
+       }
+
+      if (nops > 0)
        {
          struct insn_label_list *l;
 
-         emit_nop ();
+         if (insns)
+           {
+             /* Record the frag which holds the nop instructions, so
+                 that we can remove them if we don't need them.  */
+             frag_grow (mips16 ? nops * 2 : nops * 4);
+             prev_nop_frag = frag_now;
+             prev_nop_frag_holds = nops;
+             prev_nop_frag_required = 0;
+             prev_nop_frag_since = 0;
+           }
+
+         for (; nops > 0; --nops)
+           emit_nop ();
+
+         if (insns)
+           {
+             /* Move on to a new frag, so that it is safe to simply
+                 decrease the size of prev_nop_frag. */
+             frag_wane (frag_now);
+             frag_new (0);
+           }
+
          for (l = insn_labels; l != NULL; l = l->next)
            {
              assert (S_GET_SEGMENT (l->label) == now_seg);
@@ -2032,7 +2193,7 @@ mips_emit_delays (insns)
        }
     }
 
-  mips_no_prev_insn ();
+  mips_no_prev_insn (insns);
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -2105,7 +2266,12 @@ macro_build (place, counter, ep, name, fmt, va_alist)
         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4010
             && ! mips_4010)
         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4100
-            && ! mips_4100))
+            && ! mips_4100)
+        /* start-sanitize-r5900 */
+         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_5900
+            && ! mips_5900)
+         /* end-sanitize-r5900 */
+         )
     {
       ++insn.insn_mo;
       assert (insn.insn_mo->name);
@@ -6099,12 +6265,18 @@ mips_ip (str, ip)
        insn_isa = 1;
 
       if (insn_isa > mips_isa
-         || ((insn->pinfo & INSN_ISA) == INSN_4650
-             && ! mips_4650)
-         || ((insn->pinfo & INSN_ISA) == INSN_4010
-             && ! mips_4010)
-         || ((insn->pinfo & INSN_ISA) == INSN_4100
-             && ! mips_4100))
+         || (insn->pinfo != INSN_MACRO
+             && (((insn->pinfo & INSN_ISA) == INSN_4650
+                  && ! mips_4650)
+                 || ((insn->pinfo & INSN_ISA) == INSN_4010
+                     && ! mips_4010)
+                 || ((insn->pinfo & INSN_ISA) == INSN_4100
+                     && ! mips_4100)
+                 /* start-sanitize-r5900 */
+                 || ((insn->pinfo & INSN_ISA) == INSN_5900
+                     && ! mips_5900)
+                 /* end-sanitize-r5900 */
+                 )))
        {
          if (insn + 1 < &mips_opcodes[NUMOPCODES]
              && strcmp (insn->name, insn[1].name) == 0)
@@ -7132,28 +7304,52 @@ mips16_ip (str, ip)
            case 'U':
            case 'k':
            case 'K':
-             if (s[0] == '$' && isdigit (s[1]))
+             if (s[0] == '%'
+                 && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
                {
-                 /* Looks like a register name.  */
-                 break;
+                 /* This is %gprel(SYMBOL).  We need to read SYMBOL,
+                     and generate the appropriate reloc.  If the text
+                     inside %gprel is not a symbol name with an
+                     optional offset, then we generate a normal reloc
+                     and will probably fail later.  */
+                 my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
+                 if (imm_expr.X_op == O_symbol)
+                   {
+                     mips16_ext = true;
+                     imm_reloc = BFD_RELOC_MIPS16_GPREL;
+                     s = expr_end;
+                     ip->use_extend = true;
+                     ip->extend = 0;
+                     continue;
+                   }
+               }
+             else
+               {
+                 /* Just pick up a normal expression.  */
+                 my_getExpression (&imm_expr, s);
                }
 
-             if (s[0] == '('
-                 && args[1] == '('
-                 && s[1] == '$')
+             if (imm_expr.X_op == O_register)
                {
-                 /* It looks like the expression was omitted before a
-                     register indirection, which means that the
-                     expression is implicitly zero.  We still set up
-                     imm_expr, so that we handle explicit extensions
-                     correctly.  */
-                 imm_expr.X_op = O_constant;
-                 imm_expr.X_add_number = 0;
-                 imm_reloc = (int) BFD_RELOC_UNUSED + c;
-                 continue;
+                 /* What we thought was an expression turned out to
+                     be a register.  */
+
+                 if (s[0] == '(' && args[1] == '(')
+                   {
+                     /* It looks like the expression was omitted
+                        before a register indirection, which means
+                        that the expression is implicitly zero.  We
+                        still set up imm_expr, so that we handle
+                        explicit extensions correctly.  */
+                     imm_expr.X_op = O_constant;
+                     imm_expr.X_add_number = 0;
+                     imm_reloc = (int) BFD_RELOC_UNUSED + c;
+                     continue;
+                   }
+
+                 break;
                }
 
-             my_getExpression (&imm_expr, s);
              /* We need to relax this instruction.  */
              imm_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
@@ -7167,12 +7363,11 @@ mips16_ip (str, ip)
              /* We use offset_reloc rather than imm_reloc for the PC
                  relative operands.  This lets macros with both
                  immediate and address operands work correctly.  */
-             if (s[0] == '$' && isdigit (s[1]))
-               {
-                 /* Looks like a register name.  */
-                 break;
-               }
              my_getExpression (&offset_expr, s);
+
+             if (offset_expr.X_op == O_register)
+               break;
+
              /* We need to relax this instruction.  */
              offset_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
@@ -7211,7 +7406,7 @@ mips16_ip (str, ip)
                  mask = 7 << 3;
                while (*s != '\0')
                  {
-                   int reg1, reg2;
+                   int freg, reg1, reg2;
 
                    while (*s == ' ' || *s == ',')
                      ++s;
@@ -7221,6 +7416,13 @@ mips16_ip (str, ip)
                        break;
                      }
                    ++s;
+                   if (*s != 'f')
+                     freg = 0;
+                   else
+                     {
+                       freg = 1;
+                       ++s;
+                     }
                    reg1 = 0;
                    while (isdigit (*s))
                      {
@@ -7238,6 +7440,16 @@ mips16_ip (str, ip)
                        if (*s != '$')
                          break;
                        ++s;
+                       if (freg)
+                         {
+                           if (*s == 'f')
+                             ++s;
+                           else
+                             {
+                               as_bad ("invalid register list");
+                               break;
+                             }
+                         }
                        reg2 = 0;
                        while (isdigit (*s))
                          {
@@ -7246,19 +7458,51 @@ mips16_ip (str, ip)
                            ++s;
                          }
                      }
-                   if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
+                   if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
+                     {
+                       mask &= ~ (7 << 3);
+                       mask |= 5 << 3;
+                     }
+                   else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
+                     {
+                       mask &= ~ (7 << 3);
+                       mask |= 6 << 3;
+                     }
+                   else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
                      mask |= (reg2 - 3) << 3;
                    else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
                      mask |= (reg2 - 15) << 1;
                    else if (reg1 == 31 && reg2 == 31)
                      mask |= 1;
                    else
-                     as_bad ("invalid register list");
+                     {
+                       as_bad ("invalid register list");
+                       break;
+                     }
                  }
+               /* The mask is filled in in the opcode table for the
+                   benefit of the disassembler.  We remove it before
+                   applying the actual mask.  */
+               ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
                ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
              }
            continue;
 
+           case 'e':           /* extend code */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+               {
+                 as_warn ("Invalid value for `%s' (%lu)",
+                          ip->insn_mo->name,
+                          (unsigned long) imm_expr.X_add_number);
+                 imm_expr.X_add_number &= 0x7ff;
+               }
+             ip->insn_opcode |= imm_expr.X_add_number;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
            default:
              internalError ();
            }
@@ -7555,6 +7799,19 @@ my_getExpression (ep, str)
   expression (ep);
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
+
+  /* If we are in mips16 mode, and this is an expression based on `.',
+     then we bump the value of the symbol by 1 since that is how other
+     text symbols are handled.  We don't bother to handle complex
+     expressions, just `.' plus or minus a constant.  */
+  if (mips16
+      && ep->X_op == O_symbol
+      && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
+      && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
+      && ep->X_add_symbol->sy_frag == frag_now
+      && ep->X_add_symbol->sy_value.X_op == O_constant
+      && ep->X_add_symbol->sy_value.X_add_number == frag_now_fix ())
+    ++ep->X_add_symbol->sy_value.X_add_number;
 }
 
 /* Turn a string in input_line_pointer into a floating point constant
@@ -7668,6 +7925,12 @@ struct option md_longopts[] = {
   {"mips16", no_argument, NULL, OPTION_MIPS16},
 #define OPTION_NO_MIPS16 (OPTION_MD_BASE + 23)
   {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+  /* start-sanitize-5900 */
+#define OPTION_M5900 (OPTION_MD_BASE + 24)
+  {"m5900", no_argument, NULL, OPTION_M5900},
+#define OPTION_NO_M5900 (OPTION_MD_BASE + 25)
+  {"no-m5900", no_argument, NULL, OPTION_NO_M5900},
+  /* end-sanitize-5900 */
 
 #define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
 #define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
@@ -7726,7 +7989,7 @@ md_parse_option (c, arg)
          optimizations which limit full symbolic debugging.  We take
          that to be equivalent to -O0.  */
       if (mips_debug == 2)
-       mips_optimize = 0;
+       mips_optimize = 1;
       break;
 
     case OPTION_MIPS1:
@@ -7837,6 +8100,10 @@ md_parse_option (c, arg)
                    || strcmp (p, "5k") == 0
                    || strcmp (p, "5K") == 0)
                  mips_cpu = 5000;
+                /* start-sanitize-r5900 */
+                else if (strcmp (p, "5900") == 0)
+                  mips_cpu = 5900;
+                /* end-sanitize-r5900 */
                break;
 
              case '6':
@@ -7898,14 +8165,24 @@ md_parse_option (c, arg)
       mips_4100 = 0;
       break;
 
+      /* start-sanitize-r5900 */
+    case OPTION_M5900:
+      mips_5900 = 1;
+      break;
+
+    case OPTION_NO_M5900:
+      mips_5900 = 0;
+      break;
+      /* end-sanitize-r5900 */
+
     case OPTION_MIPS16:
       mips16 = 1;
-      mips_no_prev_insn ();
+      mips_no_prev_insn (false);
       break;
 
     case OPTION_NO_MIPS16:
       mips16 = 0;
-      mips_no_prev_insn ();
+      mips_no_prev_insn (false);
       break;
 
     case OPTION_MEMBEDDED_PIC:
@@ -8211,6 +8488,25 @@ md_apply_fix (fixP, valueP)
          || fixP->fx_r_type == BFD_RELOC_64);
 
   value = *valueP;
+
+  /* If we aren't adjusting this fixup to be against the section
+     symbol, we need to adjust the value.  */
+#ifdef S_GET_OTHER
+  if (fixP->fx_addsy != NULL
+      && OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16)
+    {
+      value -= S_GET_VALUE (fixP->fx_addsy);
+      if (value != 0 && ! fixP->fx_pcrel)
+       {
+         /* In this case, the bfd_install_relocation routine will
+             incorrectly add the symbol value back in.  We just want
+             the addend to appear in the object file.  */
+         value -= S_GET_VALUE (fixP->fx_addsy);
+       }
+    }
+#endif
+
   fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc */
 
   if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
@@ -8230,6 +8526,7 @@ md_apply_fix (fixP, valueP)
     case BFD_RELOC_MIPS_GOT_LO16:
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
+    case BFD_RELOC_MIPS16_GPREL:
       if (fixP->fx_pcrel)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      "Invalid PC relative reloc");
@@ -8346,8 +8643,8 @@ md_apply_fix (fixP, valueP)
        * the current segment).
        */
       if ((value & 0x3) != 0)
-       as_warn_where (fixP->fx_file, fixP->fx_line,
-                      "Branch to odd address (%lx)", value);
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "Branch to odd address (%lx)", value);
       value >>= 2;
 
       /* update old instruction data */
@@ -8808,10 +9105,13 @@ s_mipsset (x)
 
   if (strcmp (name, "reorder") == 0)
     {
-      if (mips_noreorder)
+      if (mips_noreorder && prev_nop_frag != NULL)
        {
-         prev_insn_unreordered = 1;
-         prev_prev_insn_unreordered = 1;
+         /* If we still have pending nops, we can discard them.  The
+            usual nop handling will insert any that are still
+            needed. */
+         prev_nop_frag->fr_fix -= prev_nop_frag_holds * (mips16 ? 2 : 4);
+         prev_nop_frag = NULL;
        }
       mips_noreorder = 0;
     }
@@ -9049,6 +9349,38 @@ s_cpadd (ignore)
   demand_empty_rest_of_line ();  
 }
 
+/* Handle the .insn pseudo-op.  This marks instruction labels in
+   mips16 mode.  This permits the linker to handle them specially,
+   such as generating jalx instructions when needed.  We also make
+   them odd for the duration of the assembly, in order to generate the
+   right sort of code.  We will make them even in the adjust_symtab
+   routine, while leaving them marked.  This is convenient for the
+   debugger and the disassembler.  The linker knows to make them odd
+   again.  */
+
+static void
+s_insn (ignore)
+     int ignore;
+{
+  if (mips16)
+    {
+      struct insn_label_list *l;
+
+      for (l = insn_labels; l != NULL; l = l->next)
+       {
+#ifdef S_SET_OTHER
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+         ++l->label->sy_value.X_add_number;
+       }
+
+      mips_clear_insn_labels ();
+    }
+
+  demand_empty_rest_of_line ();
+}
+
 /* Parse a register string into a number.  Called from the ECOFF code
    to parse .frame.  The argument is non-zero if this is the frame
    register, so that we can record it in mips_frame_reg.  */
@@ -9309,12 +9641,18 @@ mips16_extended_frag (fragp, sec, stretch)
       /* 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.  */
+         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;
+
+         /* If we are currently assuming that this frag should be
+            extended, then, the current address is two bytes
+            higher. */
+         if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+           addr += 2;
+
          /* Ignore the low bit in the target, since it will be set
              for a text label.  */
          if ((val & 1) != 0)
@@ -9325,11 +9663,6 @@ mips16_extended_frag (fragp, sec, stretch)
       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.  */
-      if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
-       addr += 2;
-
       val -= addr & ~ ((1 << op->shift) - 1);
 
       /* Branch offsets have an implicit 0 in the lowest bit.  */
@@ -9443,6 +9776,29 @@ md_estimate_size_before_relax (fragp, segtype)
     return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
 }
 
+/* This is called to see whether a reloc against a defined symbol
+   should be converted into a reloc against a section.  Don't adjust
+   MIPS16 jump relocations, so we don't have to worry about the format
+   of the offset in the .o file.  Don't adjust relocations against
+   mips16 symbols, so that the linker can find them if it needs to set
+   up a stub.  */
+
+int
+mips_fix_adjustable (fixp)
+     fixS *fixp;
+{
+  if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
+    return 0;
+  if (fixp->fx_addsy == NULL)
+    return 1;
+#ifdef S_GET_OTHER
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16)
+    return 0;
+#endif
+  return 1;
+}
+
 /* Translate internal representation of relocation info to BFD target
    format.  */
 
@@ -9725,6 +10081,8 @@ md_convert_frag (abfd, asec, fragp)
          if (type == 'p' || type == 'q')
            {
              addr += 2;
+             if (ext)
+               addr += 2;
              /* Ignore the low bit in the target, since it will be
                  set for a text label.  */
              if ((val & 1) != 0)
@@ -9735,8 +10093,6 @@ md_convert_frag (abfd, asec, fragp)
          else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
            addr -= 2;
 
-         if (ext)
-           addr += 2;
          addr &= ~ (addressT) ((1 << op->shift) - 1);
          val -= addr;
 
@@ -9746,6 +10102,12 @@ md_convert_frag (abfd, asec, fragp)
            record_alignment (asec, op->shift);
        }
 
+      if (ext
+         && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+             || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+       as_warn_where (fragp->fr_file, fragp->fr_line,
+                      "extended instruction in delay slot");
+
       buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
 
       if (target_big_endian)
@@ -9753,8 +10115,9 @@ md_convert_frag (abfd, asec, fragp)
       else
        insn = bfd_getl16 (buf);
 
-      mips16_immed (fragp->fr_file, fragp->fr_line, type, val, false, small,
-                   ext, &insn, &use_extend, &extend);
+      mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
+                   RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
+                   small, ext, &insn, &use_extend, &extend);
 
       if (use_extend)
        {
This page took 0.042155 seconds and 4 git commands to generate.