* config/tc-mips.c (prev_nop_frag): New static variable.
authorIan Lance Taylor <ian@airs.com>
Thu, 13 Feb 1997 17:25:10 +0000 (17:25 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 13 Feb 1997 17:25:10 +0000 (17:25 +0000)
(prev_nop_frag_holds): New static variable.
(prev_nop_frag_required): New static variable.
(prev_nop_frag_since): New static variable.
(append_insn): If we aren't reordering, and prev_nop_frag is not
NULL, and we don't need any nops, then decrease the size of
prev_nop_frag.  Don't insert nops because of instructions in
noreorder sections.  Remember whether the previous instructions
where in noreorder sections even when not reordering.
(mips_no_prev_insn): Add preserver parameter.  Change all
callers.  Refer prev_nop_frag variables when appropriate.
(mips_emit_delays): Set up prev_nop_frag.
(s_mipsset): Clear prev_nop_frag if reordering.

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

index 5e0c67392fe86223ce9f1a4bab876f65ee6053e3..9065228890e1086d96063c707a25cd8b4a53752a 100644 (file)
@@ -1,3 +1,19 @@
+Thu Feb 13 11:40:58 1997  Ian Lance Taylor  <ian@cygnus.com>
+
+       * config/tc-mips.c (prev_nop_frag): New static variable.
+       (prev_nop_frag_holds): New static variable.
+       (prev_nop_frag_required): New static variable.
+       (prev_nop_frag_since): New static variable.
+       (append_insn): If we aren't reordering, and prev_nop_frag is not
+       NULL, and we don't need any nops, then decrease the size of
+       prev_nop_frag.  Don't insert nops because of instructions in
+       noreorder sections.  Remember whether the previous instructions
+       where in noreorder sections even when not reordering.
+       (mips_no_prev_insn): Add preserver parameter.  Change all
+       callers.  Refer prev_nop_frag variables when appropriate.
+       (mips_emit_delays): Set up prev_nop_frag.
+       (s_mipsset): Clear prev_nop_frag if reordering.
+
 Wed Feb 12 14:36:29 1997  Ian Lance Taylor  <ian@cygnus.com>
 
        * config/tc-mips.c (append_insn): Remove useless code which
index 098144e508e499f73bad1f0b7f776fc64fc9a670..07441b427d62d282cbcf0b4b392385e4be2ed4c6 100644 (file)
@@ -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,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,
@@ -961,7 +977,7 @@ md_begin ()
   symbol_table_insert (symbol_new ("$pc", reg_section, -1,
                                   &zero_address_frag));
 
-  mips_no_prev_insn ();
+  mips_no_prev_insn (false);
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -1255,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.
@@ -1394,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
@@ -1402,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;
@@ -1467,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.  */
@@ -1970,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.  */
@@ -1979,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;
@@ -2007,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
@@ -2027,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
@@ -2035,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
@@ -2044,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);
@@ -2084,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
@@ -8068,12 +8177,12 @@ md_parse_option (c, arg)
 
     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:
@@ -8996,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;
     }