* config/tc-mips.c (mips16_extended_frag): Correct base address
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 13f284fe095c75a541e3aaf90ec0582050fa42bf..8be02a11fc23c3ad7f416e9c5f6affebd0a76d6f 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
@@ -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;
@@ -255,13 +260,8 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 static char *insn_error;
 
-static int byte_order;
-
 static int auto_align = 1;
 
-/* Symbol labelling the current insn.  */
-static symbolS *insn_label;
-
 /* When outputting SVR4 PIC code, the assembler needs to know the
    offset in the stack frame from which to restore the $gp register.
    This is set by the .cprestore pseudo-op, and saved in this
@@ -324,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
@@ -503,8 +519,8 @@ 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_emit_delays 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,
                                 const char *name, const char *fmt,
@@ -548,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));
@@ -587,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.  */
@@ -644,6 +662,30 @@ mips_pop_insert ()
     pop_insert (mips_nonecoff_pseudo_table);
 }
 \f
+/* Symbols labelling the current insn.  */
+
+struct insn_label_list
+{
+  struct insn_label_list *next;
+  symbolS *label;
+};
+
+static struct insn_label_list *insn_labels;
+static struct insn_label_list *free_insn_labels;
+
+static void mips_clear_insn_labels PARAMS ((void));
+
+static inline void
+mips_clear_insn_labels ()
+{
+  register struct insn_label_list **pl;
+
+  for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+    ;
+  *pl = insn_labels;
+  insn_labels = NULL;
+}
+\f
 static char *expr_end;
 
 /* Expressions which appear in instructions.  These are set by
@@ -761,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)
        {
@@ -808,7 +862,12 @@ md_begin ()
   if (mips_4100 < 0)
     mips_4100 = 0;
 
-  if (mips_4650 || mips_4010 || mips_4100 || mips_cpu == 4300)
+  /* start-sanitize-r5900 */
+  if (mips_5900 < 0)
+    mips_5900 = 0;
+  /* end-sanitize-r5900 */
+
+  if (mips_4010 || mips_4100 || mips_cpu == 4300)
     interlocks = 1;
   else
     interlocks = 0;
@@ -893,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;
@@ -902,8 +986,7 @@ md_begin ()
   mips_cprmask[3] = 0;
 
   /* set the default alignment for the text section (2**2) */
-  if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
-    record_alignment (text_section, 2);
+  record_alignment (text_section, 2);
 
   if (USE_GLOBAL_POINTER_OPT)
     bfd_set_gp_size (stdoutput, g_switch_value);
@@ -925,20 +1008,25 @@ md_begin ()
       {
        segT seg;
        subsegT subseg;
+       flagword flags;
        segT sec;
 
        seg = now_seg;
        subseg = now_subseg;
 
+       /* The ABI says this section should be loaded so that the
+          running program can access it.  However, we don't load it
+          if we are configured for an embedded target */
+       flags = SEC_READONLY | SEC_DATA;
+       if (strcmp (TARGET_OS, "elf") != 0)
+         flags |= SEC_ALLOC | SEC_LOAD;
+
        if (! mips_64)
          {
            sec = subseg_new (".reginfo", (subsegT) 0);
 
-           /* The ABI says this section should be loaded so that the
-              running program can access it.  */
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         (SEC_ALLOC | SEC_LOAD
-                                          | SEC_READONLY | SEC_DATA));
+
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
            (void) bfd_set_section_alignment (stdoutput, sec, 2);
        
 #ifdef OBJ_ELF
@@ -950,9 +1038,7 @@ md_begin ()
            /* The 64-bit ABI uses a .MIPS.options section rather than
                .reginfo section.  */
            sec = subseg_new (".MIPS.options", (subsegT) 0);
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         (SEC_ALLOC | SEC_LOAD
-                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
            (void) bfd_set_section_alignment (stdoutput, sec, 3);
 
 #ifdef OBJ_ELF
@@ -1161,22 +1247,34 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
   fixS *fixp;
   int nops = 0;
 
-  /* Make sure the section will be aligned appropriately.  Note that
-     we do not insert an alignment directive; it remains the user's
-     responsibility to align instructions if necessary.  Here we only
-     ensure that the section will have the right default alignment, so
-     that the right thing will happen if no alignment directive is
-     used.  */
+  /* Mark 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.  */
   if (mips16)
-    record_alignment (now_seg, 1);
-  else
-    record_alignment (now_seg, 2);
+    {
+      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;
+       }
+    }
 
   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.
@@ -1314,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
@@ -1322,34 +1425,44 @@ 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;
          int i;
+         struct insn_label_list *l;
 
          old_frag = frag_now;
          old_frag_offset = frag_now_fix ();
@@ -1371,11 +1484,14 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              frag_grow (40);
            }
 
-         if (insn_label != NULL)
+         for (l = insn_labels; l != NULL; l = l->next)
            {
-             assert (S_GET_SEGMENT (insn_label) == now_seg);
-             insn_label->sy_frag = frag_now;
-             S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
+             assert (S_GET_SEGMENT (l->label) == now_seg);
+             l->label->sy_frag = frag_now;
+             S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+             /* mips16 text labels are stored as odd.  */
+             if (mips16)
+               ++l->label->sy_value.X_add_number;
            }
 
 #ifndef NO_ECOFF_DEBUGGING
@@ -1383,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.  */
@@ -1409,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)
     {
@@ -1426,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)
@@ -1470,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)
@@ -1580,7 +1752,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                 whether there is a label on this instruction.  If
                 there are any branches to anything other than a
                 label, users must use .set noreorder.  */
-             || insn_label != NULL
+             || insn_labels != NULL
              /* If the previous instruction is in a variant frag, we
                 can not do the swap.  This does not apply to the
                 mips16, which uses variant frags for different
@@ -1777,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;
@@ -1806,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);
@@ -1878,49 +2034,63 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       prev_insn_where = f - frag_now->fr_literal;
       prev_insn_valid = 1;
     }
-  else
+  else if (place == NULL)
     {
-      /* We need to record a bit of uninformation even when we are not
+      /* 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.  */
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 }
 
 /* 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;
   prev_insn_extended = 0;
   prev_insn_reloc_type = BFD_RELOC_UNUSED;
   prev_prev_insn_unreordered = 0;
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 }
 
 /* This function must be called whenever we turn on noreorder or emit
    something other than instructions.  It inserts any NOPS which might
    be needed by the previous instruction, and clears the information
-   kept for the previous instructions.  */
+   kept for the previous instructions.  The INSNS parameter is true if
+   instructions are to follow. */
 
 static void
-mips_emit_delays ()
+mips_emit_delays (insns)
+     boolean insns;
 {
   if (! mips_noreorder)
     {
-      int nop;
+      int nops;
 
-      nop = 0;
+      nops = 0;
       if ((! mips16
           && mips_isa < 4
           && (! cop_interlocks
@@ -1938,7 +2108,7 @@ mips_emit_delays ()
                  & (INSN_LOAD_MEMORY_DELAY
                     | INSN_COPROC_MEMORY_DELAY))))
        {
-         nop = 1;
+         ++nops;
          if ((! mips16
               && mips_isa < 4
               && (! cop_interlocks
@@ -1946,7 +2116,10 @@ mips_emit_delays ()
              || (! 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
@@ -1955,20 +2128,72 @@ mips_emit_delays ()
               || (! interlocks
                   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
                       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-       nop = 1;
-      if (nop)
        {
-         emit_nop ();
-         if (insn_label != NULL)
+         if (! prev_prev_insn_unreordered)
+           ++nops;
+       }
+
+      if (nops > 0)
+       {
+         struct insn_label_list *l;
+
+         if (insns)
            {
-             assert (S_GET_SEGMENT (insn_label) == now_seg);
-             insn_label->sy_frag = frag_now;
-             S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
+             /* 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);
+             l->label->sy_frag = frag_now;
+             S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+             /* mips16 text labels are stored as odd.  */
+             if (mips16)
+               ++l->label->sy_value.X_add_number;
            }
        }
     }
 
-  mips_no_prev_insn ();
+  /* Mark 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.  */
+  if (mips16 && insns)
+    {
+      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
+         if ((l->label->sy_value.X_add_number & 1) == 0)
+           ++l->label->sy_value.X_add_number;
+       }
+    }
+
+  mips_no_prev_insn (insns);
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -2041,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);
@@ -2491,8 +2721,8 @@ load_register (counter, reg, ep, dbl)
      expressionS *ep;
      int dbl;
 {
-  int shift, freg;
-  expressionS hi32, lo32, tmp;
+  int freg;
+  expressionS hi32, lo32;
 
   if (ep->X_op != O_big)
     {
@@ -2552,11 +2782,9 @@ load_register (counter, reg, ep, dbl)
   if (ep->X_op != O_big)
     {
       hi32 = *ep;
-      shift = 32;
-      hi32.X_add_number >>= shift;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
       hi32.X_add_number &= 0xffffffff;
-      if ((hi32.X_add_number & 0x80000000) != 0)
-       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
       lo32 = *ep;
       lo32.X_add_number &= 0xffffffff;
     }
@@ -2577,76 +2805,130 @@ load_register (counter, reg, ep, dbl)
     freg = 0;
   else
     {
+      int shift, bit;
+      unsigned long hi, lo;
+
       if (hi32.X_add_number == 0xffffffff)
         {
           if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
             {
-              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j", reg, 0,
-                           (int) BFD_RELOC_LO16);
+              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
+                          reg, 0, (int) BFD_RELOC_LO16);
               return;
             }
           if (lo32.X_add_number & 0x80000000)
             {
               macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
                            (int) BFD_RELOC_HI16);
-              macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
-                           (int) BFD_RELOC_LO16);
+             if (lo32.X_add_number & 0xffff)
+               macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i",
+                            reg, reg, (int) BFD_RELOC_LO16);
               return;
             }
         }
 
-      /* Check for 16bit shifted constant: */
-      shift = 32;
-      tmp.X_add_number = hi32.X_add_number << shift | lo32.X_add_number;
-      /* We know that hi32 is non-zero, so start the mask on the first
-         bit of the hi32 value: */
+      /* Check for 16bit shifted constant.  We know that hi32 is
+         non-zero, so start the mask on the first bit of the hi32
+         value.  */
       shift = 17;
       do
        {
-         if ((tmp.X_add_number & ~((offsetT)0xffff << shift)) == 0)
-          {
-            tmp.X_op = O_constant;
-            tmp.X_add_number >>= shift;
-            macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
-                         (int) BFD_RELOC_LO16);
-            macro_build ((char *) NULL, counter, NULL,
-                         (shift >= 32) ? "dsll32" : "dsll",
-                         "d,w,<", reg, reg, (shift >= 32) ? shift - 32 : shift);
-            return;
-          }
+        unsigned long himask, lomask;
+
+        if (shift < 32)
+          {
+            himask = 0xffff >> (32 - shift);
+            lomask = (0xffff << shift) & 0xffffffff;
+          }
+        else
+          {
+            himask = 0xffff << (shift - 32);
+            lomask = 0;
+          }
+        if ((hi32.X_add_number & ~ (offsetT) himask) == 0
+            && (lo32.X_add_number & ~ (offsetT) lomask) == 0)
+          {
+            expressionS tmp;
+
+            tmp.X_op = O_constant;
+            if (shift < 32)
+              tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+                                  | (lo32.X_add_number >> shift));
+            else
+              tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+            macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
+                         (int) BFD_RELOC_LO16);
+            macro_build ((char *) NULL, counter, NULL,
+                         (shift >= 32) ? "dsll32" : "dsll",
+                         "d,w,<", reg, reg,
+                         (shift >= 32) ? shift - 32 : shift);
+            return;
+          }
          shift++;
        } while (shift <= (64 - 16));
 
-      freg = 0;
-      shift = 32;
-      tmp.X_add_number = hi32.X_add_number << shift | lo32.X_add_number;
-      while ((tmp.X_add_number & 1) == 0)
-        {
-          tmp.X_add_number >>= 1;
-          freg++;
-        }
-      if (((tmp.X_add_number + 1) & tmp.X_add_number) == 0) /* (power-of-2 - 1) */
+      /* Find the bit number of the lowest one bit, and store the
+         shifted value in hi/lo.  */
+      hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+      lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+      if (lo != 0)
+       {
+         bit = 0;
+         while ((lo & 1) == 0)
+           {
+             lo >>= 1;
+             ++bit;
+           }
+         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+         hi >>= bit;
+       }
+      else
+       {
+         bit = 32;
+         while ((hi & 1) == 0)
+           {
+             hi >>= 1;
+             ++bit;
+           }
+         lo = hi;
+         hi = 0;
+       }
+
+      /* Optimize if the shifted value is a (power of 2) - 1.  */
+      if ((hi == 0 && ((lo + 1) & lo) == 0)
+         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
         {
-          shift = COUNT_TOP_ZEROES((unsigned int)hi32.X_add_number);
+          shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
          if (shift != 0)
             {
+             expressionS tmp;
+
+             /* This instruction will set the register to be all
+                 ones.  */
               tmp.X_op = O_constant;
-              tmp.X_add_number = (offsetT)-1;
-              macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j", reg, 0,
-                           (int) BFD_RELOC_LO16); /* set all ones */
-              if (freg != 0)
+              tmp.X_add_number = (offsetT) -1;
+              macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
+                          reg, 0, (int) BFD_RELOC_LO16);
+              if (bit != 0)
                 {
-                  freg += shift;
+                  bit += shift;
                   macro_build ((char *) NULL, counter, NULL,
-                               (freg >= 32) ? "dsll32" : "dsll",
+                               (bit >= 32) ? "dsll32" : "dsll",
                                "d,w,<", reg, reg,
-                               (freg >= 32) ? freg - 32 : freg);
+                               (bit >= 32) ? bit - 32 : bit);
                 }
-              macro_build ((char *) NULL, counter, NULL, (shift >= 32) ? "dsrl32" : "dsrl",
-                           "d,w,<", reg, reg, (shift >= 32) ? shift - 32 : shift);
+              macro_build ((char *) NULL, counter, NULL,
+                          (shift >= 32) ? "dsrl32" : "dsrl",
+                           "d,w,<", reg, reg,
+                          (shift >= 32) ? shift - 32 : shift);
               return;
             }
         }
+
+      /* Sign extend hi32 before calling load_register, because we can
+         generally get better code when we load a sign extended value.  */
+      if ((hi32.X_add_number & 0x80000000) != 0)
+       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
       load_register (counter, reg, &hi32, 0);
       freg = reg;
     }
@@ -2914,7 +3196,7 @@ macro (ip)
         sub v0,$zero,$a0
         */
 
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
 
@@ -3372,7 +3654,7 @@ macro (ip)
          return;
        }
 
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -3512,7 +3794,7 @@ macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
@@ -4627,13 +4909,13 @@ macro (ip)
     dob:
       assert (mips_isa < 2);
       macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg : treg + 1,
+                  target_big_endian ? treg + 1 : treg,
                   (int) r, breg);
       /* FIXME: A possible overflow which I don't know how to deal
         with.  */
       offset_expr.X_add_number += 4;
       macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
+                  target_big_endian ? treg : treg + 1,
                   (int) r, breg);
 
       /* To avoid confusion in tc_gen_reloc, we must ensure that this
@@ -4712,7 +4994,7 @@ macro (ip)
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when loading from memory.  We set coproc if we must
         load $fn+1 first.  */
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        coproc = 0;
 
       if (mips_pic == NO_PIC
@@ -5091,7 +5373,7 @@ macro2 (ip)
     case M_DMULO:
       dbl = 1;
     case M_MULO:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -5118,7 +5400,7 @@ macro2 (ip)
     case M_DMULOU:
       dbl = 1;
     case M_MULOU:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -5175,11 +5457,11 @@ macro2 (ip)
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when storing to memory.  */
       macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg : treg + 1,
+                  target_big_endian ? treg + 1 : treg,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number += 4;
       macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
+                  target_big_endian ? treg : treg + 1,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
@@ -5464,7 +5746,7 @@ macro2 (ip)
        * Is the double cfc1 instruction a bug in the mips assembler;
        * or is there a reason for it?
        */
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
@@ -5494,11 +5776,11 @@ macro2 (ip)
       if (offset_expr.X_add_number >= 0x7fff)
        as_bad ("operand overflow");
       /* avoid load delay */
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number -= 1;
       else
        offset_expr.X_add_number += 1;
@@ -5520,11 +5802,11 @@ macro2 (ip)
     ulw:
       if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number += off;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number -= off;
       else
        offset_expr.X_add_number += off;
@@ -5547,13 +5829,13 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = off;
       else
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       else
        expr1.X_add_number = off;
@@ -5568,12 +5850,12 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1,
                   mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        expr1.X_add_number = 1;
       else
        expr1.X_add_number = 0;
@@ -5588,12 +5870,12 @@ macro2 (ip)
     case M_USH:
       if (offset_expr.X_add_number >= 0x7fff)
        as_bad ("operand overflow");
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8);
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        offset_expr.X_add_number -= 1;
       else
        offset_expr.X_add_number += 1;
@@ -5613,11 +5895,11 @@ macro2 (ip)
     usw:
       if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number += off;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number -= off;
       else
        offset_expr.X_add_number += off;
@@ -5640,13 +5922,13 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = off;
       else
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       else
        expr1.X_add_number = off;
@@ -5660,19 +5942,19 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", treg,
                   treg, 8);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 1;
       else
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       else
        expr1.X_add_number = 1;
@@ -5735,7 +6017,7 @@ mips16_macro (ip)
     case M_REM_3:
       s = "mfhi";
     do_div3:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -5768,7 +6050,7 @@ mips16_macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL, s, "0,x,y", xreg, yreg);
@@ -5910,6 +6192,17 @@ mips16_macro (ip)
       macro_build ((char *) NULL, &icnt, &imm_expr, s, s3, xreg);
       macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
       break;
+
+    case M_ABS:
+      expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, "slti", "x,8",  yreg);
+      if (xreg != yreg)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    "move", "y,X", xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build ((char *) NULL, &icnt, &expr1, "bteqz", "p");
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  "neg", "x,w", xreg, xreg);
     }
 }
 
@@ -5972,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)
@@ -6423,7 +6722,7 @@ mips_ip (str, ip)
                        ))
                  {
                    imm_expr.X_op = O_constant;
-                   if (byte_order == LITTLE_ENDIAN)
+                   if (! target_big_endian)
                      imm_expr.X_add_number =
                        (((((((int) temp[3] << 8)
                             | temp[2]) << 8)
@@ -7005,12 +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)
+               {
+                 /* 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 (imm_expr.X_op == O_register)
                {
-                 /* Looks like a register name.  */
+                 /* 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;
@@ -7024,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;
@@ -7068,7 +7406,7 @@ mips16_ip (str, ip)
                  mask = 7 << 3;
                while (*s != '\0')
                  {
-                   int reg1, reg2;
+                   int freg, reg1, reg2;
 
                    while (*s == ' ' || *s == ',')
                      ++s;
@@ -7078,6 +7416,13 @@ mips16_ip (str, ip)
                        break;
                      }
                    ++s;
+                   if (*s != 'f')
+                     freg = 0;
+                   else
+                     {
+                       freg = 1;
+                       ++s;
+                     }
                    reg1 = 0;
                    while (isdigit (*s))
                      {
@@ -7095,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))
                          {
@@ -7103,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 ();
            }
@@ -7246,11 +7633,7 @@ mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
 
   /* Branch offsets have an implicit 0 in the lowest bit.  */
   if (type == 'p' || type == 'q')
-    {
-      if ((val & 1) != 0)
-       as_bad_where (file, line, "branch to odd address");
-      val /= 2;
-    }
+    val /= 2;
 
   if ((val & ((1 << op->shift) - 1)) != 0
       || val < (mintiny << op->shift)
@@ -7416,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
@@ -7455,7 +7851,7 @@ md_atof (type, litP, sizeP)
 
   *sizeP = prec * 2;
 
-  if (byte_order == LITTLE_ENDIAN)
+  if (! target_big_endian)
     {
       for (i = prec - 1; i >= 0; i--)
        {
@@ -7481,19 +7877,10 @@ md_number_to_chars (buf, val, n)
      valueT val;
      int n;
 {
-  switch (byte_order)
-    {
-    case LITTLE_ENDIAN:
-      number_to_chars_littleendian (buf, val, n);
-      break;
-
-    case BIG_ENDIAN:
-      number_to_chars_bigendian (buf, val, n);
-      break;
-
-    default:
-      internalError ();
-    }
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
 }
 \f
 CONST char *md_shortopts = "O::g::G:";
@@ -7538,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)
@@ -7596,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:
@@ -7707,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':
@@ -7768,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:
@@ -7909,15 +8316,6 @@ MIPS options:\n\
 -64                    create 64 bit object file\n");
 #endif
 }
-
-void
-mips_init_after_args ()
-{
-  if (target_big_endian)
-    byte_order = BIG_ENDIAN;
-  else
-    byte_order = LITTLE_ENDIAN;
-}
 \f
 long
 md_pcrel_from (fixP)
@@ -7952,7 +8350,7 @@ cons_fix_new_mips (frag, where, nbytes, exp)
      4 byte reloc.  */
   if (nbytes == 8 && ! mips_64)
     {
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        where += 4;
       nbytes = 4;
     }
@@ -8090,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)
@@ -8109,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");
@@ -8137,7 +8555,7 @@ md_apply_fix (fixP, valueP)
        value += 0x10000;
       value >>= 16;
       buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        buf += 2;
       md_number_to_chars (buf, value, 2);
       break;
@@ -8148,7 +8566,7 @@ md_apply_fix (fixP, valueP)
       if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
        value += fixP->fx_frag->fr_address + fixP->fx_where;
       buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        buf += 2;
       md_number_to_chars (buf, value, 2);
       break;
@@ -8168,7 +8586,7 @@ md_apply_fix (fixP, valueP)
              long hiv;
 
              w1 = w2 = fixP->fx_where;
-             if (byte_order == BIG_ENDIAN)
+             if (target_big_endian)
                w1 += 4;
              else
                w2 += 4;
@@ -8212,7 +8630,7 @@ md_apply_fix (fixP, valueP)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          "relocation overflow");
          buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
-         if (byte_order == BIG_ENDIAN)
+         if (target_big_endian)
            buf += 2;
          md_number_to_chars (buf, value, 2);
        }
@@ -8224,28 +8642,17 @@ md_apply_fix (fixP, valueP)
        * might be deleting the relocation entry (i.e., a branch within
        * the current segment).
        */
-      /* TinyRISC can branch to odd addresses */
-      if ((value & (mips16 ? 0x1 : 0x3)) != 0)
-       as_warn_where (fixP->fx_file, fixP->fx_line,
-                      "Branch to odd address (%lx)", value);
+      if ((value & 0x3) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "Branch to odd address (%lx)", value);
       value >>= 2;
 
       /* update old instruction data */
       buf = (unsigned char *) (fixP->fx_where + fixP->fx_frag->fr_literal);
-      switch (byte_order)
-       {
-       case LITTLE_ENDIAN:
-         insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
-         break;
-
-       case BIG_ENDIAN:
-         insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-         break;
-
-       default:
-         internalError ();
-         return 0;
-       }
+      if (target_big_endian)
+       insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+      else
+       insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
 
       if (value >= -0x8000 && value < 0x8000)
        insn |= value & 0xffff;
@@ -8407,7 +8814,7 @@ mips_align (to, fill, label)
      int fill;
      symbolS *label;
 {
-  mips_emit_delays ();
+  mips_emit_delays (false);
   frag_align (to, fill);
   record_alignment (now_seg, to);
   if (label != NULL)
@@ -8459,7 +8866,8 @@ s_align (x)
   if (temp)
     {
       auto_align = 1;
-      mips_align (temp, (int) temp_fill, insn_label);
+      mips_align (temp, (int) temp_fill,
+                 insn_labels != NULL ? insn_labels->label : NULL);
     }
   else
     {
@@ -8472,8 +8880,8 @@ s_align (x)
 void
 mips_flush_pending_output ()
 {
-  mips_emit_delays ();
-  insn_label = NULL;
+  mips_emit_delays (false);
+  mips_clear_insn_labels ();
 }
 
 static void
@@ -8489,7 +8897,7 @@ s_change_sec (sec)
       && (sec == 'd' || sec == 'r'))
     sec = 's';
 
-  mips_emit_delays ();
+  mips_emit_delays (false);
   switch (sec)
     {
     case 't':
@@ -8567,11 +8975,11 @@ s_cons (log_size)
 {
   symbolS *label;
 
-  label = insn_label;
-  mips_emit_delays ();
+  label = insn_labels != NULL ? insn_labels->label : NULL;
+  mips_emit_delays (false);
   if (log_size > 0 && auto_align)
     mips_align (log_size, 0, label);
-  insn_label = NULL;
+  mips_clear_insn_labels ();
   cons (1 << log_size);
 }
 
@@ -8581,9 +8989,9 @@ s_float_cons (type)
 {
   symbolS *label;
 
-  label = insn_label;
+  label = insn_labels != NULL ? insn_labels->label : NULL;
 
-  mips_emit_delays ();
+  mips_emit_delays (false);
 
   if (auto_align)
     if (type == 'd')
@@ -8591,7 +8999,7 @@ s_float_cons (type)
     else
       mips_align (2, 0, label);
 
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 
   float_cons (type);
 }
@@ -8697,16 +9105,19 @@ 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;
     }
   else if (strcmp (name, "noreorder") == 0)
     {
-      mips_emit_delays ();
+      mips_emit_delays (true);
       mips_noreorder = 1;
       mips_any_noreorder = 1;
     }
@@ -8890,11 +9301,11 @@ s_gpword (ignore)
       return;
     }
 
-  label = insn_label;
-  mips_emit_delays ();
+  label = insn_labels != NULL ? insn_labels->label : NULL;
+  mips_emit_delays (true);
   if (auto_align)
     mips_align (2, 0, label);
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 
   expression (&ex);
 
@@ -8938,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.  */
@@ -9198,21 +9641,28 @@ 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;
+       {
+         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)
+           --val;
+       }
       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.  */
-      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.  */
@@ -9326,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.  */
 
@@ -9606,14 +10079,20 @@ md_convert_frag (abfd, asec, fragp)
          /* The rules for the base address of a PC relative reloc are
              complicated; see mips16_extended_frag.  */
          if (type == 'p' || type == 'q')
-           addr += 2;
+           {
+             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)
+               --val;
+           }
          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;
 
@@ -9623,22 +10102,22 @@ 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);
 
-      switch (byte_order)
-       {
-       default:
-         internalError ();
-       case LITTLE_ENDIAN:
-         insn = bfd_getl16 (buf);
-         break;
-       case BIG_ENDIAN:
-         insn = bfd_getb16 (buf);
-         break;
-       }
+      if (target_big_endian)
+       insn = bfd_getb16 (buf);
+      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)
        {
@@ -9667,6 +10146,39 @@ md_convert_frag (abfd, asec, fragp)
     }
 }
 
+#ifdef OBJ_ELF
+
+/* This function is called after the relocs have been generated.
+   We've been storing mips16 text labels as odd.  Here we convert them
+   back to even for the convenience of the debugger.  */
+
+void
+mips_frob_file_after_relocs ()
+{
+  asymbol **syms;
+  unsigned int count, i;
+
+  if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+    return;
+
+  syms = bfd_get_outsymbols (stdoutput);
+  count = bfd_get_symcount (stdoutput);
+  for (i = 0; i < count; i++, syms++)
+    {
+      if (elf_symbol (*syms)->internal_elf_sym.st_other == STO_MIPS16
+         && ((*syms)->value & 1) != 0)
+       {
+         (*syms)->value &= ~1;
+         /* If the symbol has an odd size, it was probably computed
+            incorrectly, so adjust that as well.  */
+         if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
+           ++elf_symbol (*syms)->internal_elf_sym.st_size;
+       }
+    }
+}
+
+#endif
+
 /* This function is called whenever a label is defined.  It is used
    when handling branch delays; if a branch has a label, we assume we
    can not move it.  */
@@ -9675,11 +10187,19 @@ void
 mips_define_label (sym)
      symbolS *sym;
 {
-  insn_label = sym;
-#ifdef OBJ_ELF
-  if (mips16)
-    S_SET_OTHER (insn_label, STO_MIPS16);
-#endif
+  struct insn_label_list *l;
+
+  if (free_insn_labels == NULL)
+    l = (struct insn_label_list *) xmalloc (sizeof *l);
+  else
+    {
+      l = free_insn_labels;
+      free_insn_labels = l->next;
+    }
+
+  l->label = sym;
+  l->next = insn_labels;
+  insn_labels = l;
 }
 
 /* Decide whether a label is local.  This is called by LOCAL_LABEL.
This page took 0.047548 seconds and 4 git commands to generate.