Rework MIPS nop-insertion code, add -mfix-vr4130 [9/11]

Richard Sandiford rsandifo@redhat.com
Tue Mar 8 15:10:00 GMT 2005


This patch generalises the end-of-noreorder code in s_mipsset (of which
we have two copies, one for ".set reorder" itself and one for ".set pop").

The current code removes all prev_nop_frag_holds nops from prev_nop_frag
and then sets prev_nop_frag to NULL.  This is safe with our current set
of hazards because prev_nop_frag_required will always be 0 (I'll spare
you the full run-down ;).  However, with this new, more general, interface,
we should cope with cases where:

    prev_nop_frag_holds > prev_nop_frag_required > 0

(which can occur with the VR4133 workarounds).

This patch introduces two new functions, start_noreorder() and
end_noreorder(), for wrapping (potentially nested) noreorder blocks.
end_noreorder() will commit to inserting prev_nop_frag_required nops
before dropping back to .set reorder mode, and will update the history
accordingly.

start_noreorder() subsumes "mips_emit_delays (TRUE)", so this
patch also removes the INSNS parameter from mips_emit_delays and
mips_no_prev_insn.  Note that s_gpword and s_gpdword were wrongly
passing TRUE instead of FALSE here (wrongly because these directives
insert data, not instructions).

md_flush_pending_output is now identical to mips_emit_delays, so the
patch removes the former and uses the latter exclusively.

Tested on mips64{,el}-linux-gnu and mipsisa64{,el}-elf.  OK to install?

Richard


	* config/tc-mips.h (mips_flush_pending_output): Delete.
	(mips_emit_delays): Declare.
	(md_flush_pending_output): Use mips_emit_delays.
	* config/tc-mips.c (mips_no_prev_insn): Remove parameter; always forget
	the previous instructions.
	(md_begin, append_insn, md_parse_option): Update callers.
	(mips_emit_delay): Remove parameter.  Move INSNS != 0 code to
	start_noreorder.
	(mips_align, s_change_sec, s_cons, s_float_cons, s_gpword)
	(s_gpdword): Update callers.
	(start_noreorder, end_noreorder): New functions.
	(macro, macro2, mips16_macro, s_mipsset): Use them instead of
	manipulating mips_opts or prev_nop_frag directly.
	(mips_flush_pending_output): Delete.

diff -urpN gas.9/config/tc-mips.h gas/config/tc-mips.h
--- gas.9/config/tc-mips.h	2005-03-08 09:07:40.000000000 +0000
+++ gas/config/tc-mips.h	2005-03-08 12:19:23.653556697 +0000
@@ -149,8 +149,8 @@ extern void md_mips_end (void);
 extern void mips_pop_insert (void);
 #define md_pop_insert()		mips_pop_insert()
 
-extern void mips_flush_pending_output (void);
-#define md_flush_pending_output mips_flush_pending_output
+extern void mips_emit_delays (void);
+#define md_flush_pending_output mips_emit_delays
 
 extern void mips_enable_auto_align (void);
 #define md_elf_section_change_hook()	mips_enable_auto_align()
diff -urpN gas.9/config/tc-mips.c gas/config/tc-mips.c
--- gas.9/config/tc-mips.c	2005-03-08 11:47:57.177536421 +0000
+++ gas/config/tc-mips.c	2005-03-08 12:19:23.686551681 +0000
@@ -928,7 +928,7 @@ enum mips_regclass { MIPS_GR_REG, MIPS_F
 
 static void append_insn
   (struct mips_cl_insn *ip, expressionS *p, bfd_reloc_code_real_type *r);
-static void mips_no_prev_insn (int);
+static void mips_no_prev_insn (void);
 static void mips16_macro_build
   (expressionS *, const char *, const char *, va_list);
 static void load_register (int, expressionS *, int);
@@ -1477,7 +1477,7 @@ md_begin (void)
 				       &zero_address_frag));
     }
 
-  mips_no_prev_insn (FALSE);
+  mips_no_prev_insn ();
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -2664,7 +2664,7 @@ append_insn (struct mips_cl_insn *ip, ex
 	  /* If that was an unconditional branch, forget the previous
 	     insn information.  */
 	  if (pinfo & INSN_UNCOND_BRANCH_DELAY)
-	    mips_no_prev_insn (FALSE);
+	    mips_no_prev_insn ();
 	}
       else if (pinfo & INSN_COND_BRANCH_LIKELY)
 	{
@@ -2685,49 +2685,57 @@ append_insn (struct mips_cl_insn *ip, ex
   mips_clear_insn_labels ();
 }
 
-/* This function forgets that there was any previous instruction or
-   label.  If PRESERVE is non-zero, it remembers enough information to
-   know whether nops are needed before a noreorder section.  */
+/* Forget that there was any previous instruction or label.  */
 
 static void
-mips_no_prev_insn (int preserve)
+mips_no_prev_insn (void)
 {
-  size_t i;
- 
-  if (! preserve)
-    {
-      prev_nop_frag = NULL;
-      prev_nop_frag_holds = 0;
-      prev_nop_frag_required = 0;
-      prev_nop_frag_since = 0;
-      for (i = 0; i < ARRAY_SIZE (history); i++)
-	history[i] = (mips_opts.mips16 ? mips16_nop_insn : nop_insn);
-    }
-  else
-    for (i = 0; i < ARRAY_SIZE (history); i++)
-      {
-	history[i].fixed_p = 1;
-	history[i].noreorder_p = 0;
-	history[i].mips16_absolute_jump_p = 0;
-      }
+  prev_nop_frag = NULL;
+  insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
   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.  The INSNS parameter is true if
-   instructions are to follow.  */
+/* This function must be called before we emit something other than
+   instructions.  It is like mips_no_prev_insn except that it inserts
+   any NOPS that might be needed by previous instructions.  */
 
-static void
-mips_emit_delays (bfd_boolean insns)
+void
+mips_emit_delays (void)
 {
   if (! mips_opts.noreorder)
     {
       int nops = nops_for_insn (history, NULL);
       if (nops > 0)
 	{
-	  if (insns && mips_optimize != 0)
+	  while (nops-- > 0)
+	    add_fixed_insn (NOP_INSN);
+	  mips_move_labels ();
+	}
+    }
+  mips_no_prev_insn ();
+}
+
+/* Start a (possibly nested) noreorder block.  */
+
+static void
+start_noreorder (void)
+{
+  if (mips_opts.noreorder == 0)
+    {
+      unsigned int i;
+      int nops;
+
+      /* None of the instructions before the .set noreorder can be moved.  */
+      for (i = 0; i < ARRAY_SIZE (history); i++)
+	history[i].fixed_p = 1;
+
+      /* Insert any nops that might be needed between the .set noreorder
+	 block and the previous instructions.  We will later remove any
+	 nops that turn out not to be needed.  */
+      nops = nops_for_insn (history, NULL);
+      if (nops > 0)
+	{
+	  if (mips_optimize != 0)
 	    {
 	      /* Record the frag which holds the nop instructions, so
                  that we can remove them if we don't need them.  */
@@ -2741,23 +2749,35 @@ mips_emit_delays (bfd_boolean insns)
 	  for (; nops > 0; --nops)
 	    add_fixed_insn (NOP_INSN);
 
-	  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);
-	    }
-
+	  /* 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);
 	  mips_move_labels ();
 	}
+      mips16_mark_labels ();
+      mips_clear_insn_labels ();
     }
+  mips_opts.noreorder++;
+  mips_any_noreorder = 1;
+}
 
-  /* Mark instruction labels in mips16 mode.  */
-  if (insns)
-    mips16_mark_labels ();
+/* End a nested noreorder block.  */
 
-  mips_no_prev_insn (insns);
+static void
+end_noreorder (void)
+{
+  mips_opts.noreorder--;
+  if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+    {
+      /* Commit to inserting prev_nop_frag_required nops and go back to
+	 handling nop insertion the .set reorder way.  */
+      prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+				* (mips_opts.mips16 ? 2 : 4));
+      insert_into_history (prev_nop_frag_since,
+			   prev_nop_frag_required, NOP_INSN);
+      prev_nop_frag = NULL;
+    }
 }
 
 /* Set up global variables for the start of a new macro.  */
@@ -4061,9 +4081,7 @@ macro (struct mips_cl_insn *ip)
 	 sub v0,$zero,$a0
 	 */
 
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
 
       expr1.X_add_number = 8;
       macro_build (&expr1, "bgez", "s,p", sreg);
@@ -4073,7 +4091,7 @@ macro (struct mips_cl_insn *ip)
 	move_register (dreg, sreg);
       macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
 
-      --mips_opts.noreorder;
+      end_noreorder ();
       break;
 
     case M_ADD_I:
@@ -4581,9 +4599,7 @@ macro (struct mips_cl_insn *ip)
 	  break;
 	}
 
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       if (mips_trap)
 	{
 	  macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
@@ -4617,7 +4633,7 @@ macro (struct mips_cl_insn *ip)
 	  macro_build (NULL, "teq", "s,t,q", sreg, AT, 6);
 	  /* We want to close the noreorder block as soon as possible, so
 	     that later insns are available for delay slot filling.  */
-	  --mips_opts.noreorder;
+	  end_noreorder ();
 	}
       else
 	{
@@ -4627,7 +4643,7 @@ macro (struct mips_cl_insn *ip)
 
 	  /* We want to close the noreorder block as soon as possible, so
 	     that later insns are available for delay slot filling.  */
-	  --mips_opts.noreorder;
+	  end_noreorder ();
 
 	  macro_build (NULL, "break", "c", 6);
 	}
@@ -4722,16 +4738,14 @@ macro (struct mips_cl_insn *ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       if (mips_trap)
 	{
 	  macro_build (NULL, "teq", "s,t,q", treg, 0, 7);
 	  macro_build (NULL, s, "z,s,t", sreg, treg);
 	  /* We want to close the noreorder block as soon as possible, so
 	     that later insns are available for delay slot filling.  */
-	  --mips_opts.noreorder;
+	  end_noreorder ();
 	}
       else
 	{
@@ -4741,7 +4755,7 @@ macro (struct mips_cl_insn *ip)
 
 	  /* We want to close the noreorder block as soon as possible, so
 	     that later insns are available for delay slot filling.  */
-	  --mips_opts.noreorder;
+	  end_noreorder ();
 	  macro_build (NULL, "break", "c", 7);
 	}
       macro_build (NULL, s2, "d", dreg);
@@ -6669,9 +6683,7 @@ macro2 (struct mips_cl_insn *ip)
       dbl = 1;
     case M_MULO:
     do_mulo:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       used_at = 1;
       if (imm)
 	load_register (AT, &imm_expr, dbl);
@@ -6688,7 +6700,7 @@ macro2 (struct mips_cl_insn *ip)
 	  macro_build (NULL, "nop", "", 0);
 	  macro_build (NULL, "break", "c", 6);
 	}
-      --mips_opts.noreorder;
+      end_noreorder ();
       macro_build (NULL, "mflo", "d", dreg);
       break;
 
@@ -6702,9 +6714,7 @@ macro2 (struct mips_cl_insn *ip)
       dbl = 1;
     case M_MULOU:
     do_mulou:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       used_at = 1;
       if (imm)
 	load_register (AT, &imm_expr, dbl);
@@ -6721,7 +6731,7 @@ macro2 (struct mips_cl_insn *ip)
 	  macro_build (NULL, "nop", "", 0);
 	  macro_build (NULL, "break", "c", 6);
 	}
-      --mips_opts.noreorder;
+      end_noreorder ();
       break;
 
     case M_DROL:
@@ -7186,9 +7196,7 @@ macro2 (struct mips_cl_insn *ip)
        * Is the double cfc1 instruction a bug in the mips assembler;
        * or is there a reason for it?
        */
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       macro_build (NULL, "cfc1", "t,G", treg, RA);
       macro_build (NULL, "cfc1", "t,G", treg, RA);
       macro_build (NULL, "nop", "");
@@ -7202,7 +7210,7 @@ macro2 (struct mips_cl_insn *ip)
 		   dreg, sreg);
       macro_build (NULL, "ctc1", "t,G", treg, RA);
       macro_build (NULL, "nop", "");
-      --mips_opts.noreorder;
+      end_noreorder ();
       break;
 
     case M_ULH:
@@ -7442,9 +7450,7 @@ mips16_macro (struct mips_cl_insn *ip)
     case M_REM_3:
       s = "mfhi";
     do_div3:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
       expr1.X_add_number = 2;
       macro_build (&expr1, "bnez", "x,p", yreg);
@@ -7454,7 +7460,7 @@ mips16_macro (struct mips_cl_insn *ip)
          since that causes an overflow.  We should do that as well,
          but I don't see how to do the comparisons without a temporary
          register.  */
-      --mips_opts.noreorder;
+      end_noreorder ();
       macro_build (NULL, s, "x", zreg);
       break;
 
@@ -7474,14 +7480,12 @@ mips16_macro (struct mips_cl_insn *ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays (TRUE);
-      ++mips_opts.noreorder;
-      mips_any_noreorder = 1;
+      start_noreorder ();
       macro_build (NULL, s, "0,x,y", xreg, yreg);
       expr1.X_add_number = 2;
       macro_build (&expr1, "bnez", "x,p", yreg);
       macro_build (NULL, "break", "6", 7);
-      --mips_opts.noreorder;
+      end_noreorder ();
       macro_build (NULL, s2, "x", zreg);
       break;
 
@@ -10194,12 +10198,12 @@ md_parse_option (int c, char *arg)
 
     case OPTION_MIPS16:
       mips_opts.mips16 = 1;
-      mips_no_prev_insn (FALSE);
+      mips_no_prev_insn ();
       break;
 
     case OPTION_NO_MIPS16:
       mips_opts.mips16 = 0;
-      mips_no_prev_insn (FALSE);
+      mips_no_prev_insn ();
       break;
 
     case OPTION_MIPS3D:
@@ -11058,7 +11062,7 @@ get_symbol (void)
 static void
 mips_align (int to, int fill, symbolS *label)
 {
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
   frag_align (to, fill, 0);
   record_alignment (now_seg, to);
   if (label != NULL)
@@ -11120,13 +11124,6 @@ s_align (int x ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
-void
-mips_flush_pending_output (void)
-{
-  mips_emit_delays (FALSE);
-  mips_clear_insn_labels ();
-}
-
 static void
 s_change_sec (int sec)
 {
@@ -11142,7 +11139,7 @@ s_change_sec (int sec)
   obj_elf_section_change_hook ();
 #endif
 
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
   switch (sec)
     {
     case 't':
@@ -11273,7 +11270,7 @@ s_cons (int log_size)
   symbolS *label;
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
   if (log_size > 0 && auto_align)
     mips_align (log_size, 0, label);
   mips_clear_insn_labels ();
@@ -11287,7 +11284,7 @@ s_float_cons (int type)
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
 
-  mips_emit_delays (FALSE);
+  mips_emit_delays ();
 
   if (auto_align)
     {
@@ -11415,22 +11412,13 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
 
   if (strcmp (name, "reorder") == 0)
     {
-      if (mips_opts.noreorder && prev_nop_frag != NULL)
-	{
-	  /* 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
-				    * (mips_opts.mips16 ? 2 : 4));
-	  prev_nop_frag = NULL;
-	}
-      mips_opts.noreorder = 0;
+      if (mips_opts.noreorder)
+	end_noreorder ();
     }
   else if (strcmp (name, "noreorder") == 0)
     {
-      mips_emit_delays (TRUE);
-      mips_opts.noreorder = 1;
-      mips_any_noreorder = 1;
+      if (!mips_opts.noreorder)
+	start_noreorder ();
     }
   else if (strcmp (name, "at") == 0)
     {
@@ -11575,16 +11563,9 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
 	  /* If we're changing the reorder mode we need to handle
              delay slots correctly.  */
 	  if (s->options.noreorder && ! mips_opts.noreorder)
-	    mips_emit_delays (TRUE);
+	    start_noreorder ();
 	  else if (! s->options.noreorder && mips_opts.noreorder)
-	    {
-	      if (prev_nop_frag != NULL)
-		{
-		  prev_nop_frag->fr_fix -= (prev_nop_frag_holds
-					    * (mips_opts.mips16 ? 2 : 4));
-		  prev_nop_frag = NULL;
-		}
-	    }
+	    end_noreorder ();
 
 	  mips_opts = s->options;
 	  mips_opts_stack = s->next;
@@ -11924,7 +11905,7 @@ s_gpword (int ignore ATTRIBUTE_UNUSED)
     }
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (TRUE);
+  mips_emit_delays ();
   if (auto_align)
     mips_align (2, 0, label);
   mips_clear_insn_labels ();
@@ -11960,7 +11941,7 @@ s_gpdword (int ignore ATTRIBUTE_UNUSED)
     }
 
   label = insn_labels != NULL ? insn_labels->label : NULL;
-  mips_emit_delays (TRUE);
+  mips_emit_delays ();
   if (auto_align)
     mips_align (3, 0, label);
   mips_clear_insn_labels ();



More information about the Binutils mailing list