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

Richard Sandiford rsandifo@redhat.com
Tue Mar 8 14:39:00 GMT 2005


This is the first of a series of patches to work around the VR4133
errata described in the attachment.  Briefly, we need to look for cases
where an mflo or mfhi is followed by a multiplication or division and
where there is a gap of less than 4 instructions between them.

It's difficult to handle this errata within the current gas infrastructure
because everything is set up for inserting either one or two nops.
Specifically, gas keeps a lot of information about the last instruction
in global prev_insn* variables, and a limited amount of information
about the last-but-one instruction in prev_prev_insn*.  Extending this
approach to -mfix-vr4130 would have meant adding prev_prev_prev_insn*
and prev_prev_prev_prev_insn*, and I didn't really want to do that. ;)

Instead, I've tried to provide a more flexible (and hopefully cleaner)
nop-insertion infrastructure.  The main changes are to:

  (1) reduce code duplication
  (2) remove the hard-coded limit of 2 nops and
  (3) use a history array instead of a bunch of separate global variables.

As an added bonus, these patches allow gas to fill coprocessor delay
slots for !cop_interlocks, something that it can't currently do due
to potential ctc hazards:

	      /* If the branch reads the condition codes, we don't
		 even try to swap, because in the sequence
		   ctc1 $X,$31
		   INSN
		   INSN
		   bc1t LABEL
		 we can not swap, and I don't feel like handling that
		 case.  */
	      || (! mips_opts.mips16
		  && (pinfo & INSN_READ_COND_CODE)
		  && ! cop_interlocks)

The complete set of changes is quite big and difficult to follow, so
I've tried my best to split it up.  Most of the patches should make
sense as stand-alone improvements, but unfortunately, one or two will
leave a bit of ugliness lying around for later patches to clean up.
The first patch is an example of this....

----------------------------------------------------------------

This first patch just introduces the history array.  It mechanically
replaces all references to prev_insn* with references to history[0] and
all references to prev_prev_insn* with references to history[1].

The size of the history buffer is hard-coded at 2 for now; this will
be cleaned up by a later patch.  There is also some redundancy in the
information we keep; this too will be cleaned up later.  The point of
this patch is to be as simple and mechanical as possible.

One of the main decisions was whether to introduce a new type
for storing the history or whether to add fields to the existing
mips_cl_insn structure (the one used by append_insn to communicate
with its callers).  I've gone for the latter here.  Hopefully this
choice becomes more obvious after patch 5, which introduces a new
set of functions for manipulating instructions.  Until then,
each valid field of the old structure must be copied explicitly,
but this really is just temporary ugliness.

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

Richard


	* config/tc-mips.h (mips_cl_insn): Move definition to...
	* config/tc-mips.c (mips_cl_insn): ...here.  Add new fields:
	frag, where, fixp, reloc_type, valid_p, noreorder_p, delay_slot_p
	and extended_p.
	(history): New variable.
	(prev_insn, prev_prev_insn, prev_insn_valid, prev_insn_frag)
	(prev_insn_where, prev_insn_reloc_type, prev_insn_fixp)
	(prev_insn_is_delay_slot, prev_insn_unreordered, prev_insn_extended)
	(prev_prev_insn_unreordered): Delete.
	(reg_needs_delay, append_insn, mips_no_prev_insn, mips_emit_delays)
	(macro_start): Replace uses of prev_insn* with the equivalent history[]
	field.

diff -urpN gas.1/config/tc-mips.h gas/config/tc-mips.h
--- gas.1/config/tc-mips.h	2005-03-08 08:59:34.929057103 +0000
+++ gas/config/tc-mips.h	2005-03-08 09:07:40.524922786 +0000
@@ -79,15 +79,6 @@ enum mips_pic_level
 
 extern enum mips_pic_level mips_pic;
 
-struct mips_cl_insn
-{
-  unsigned long insn_opcode;
-  const struct mips_opcode *insn_mo;
-  /* The next two fields are used when generating mips16 code.  */
-  bfd_boolean use_extend;
-  unsigned short extend;
-};
-
 extern int tc_get_register (int frame);
 
 #define md_after_parse_args() mips_after_parse_args()
diff -urpN gas.1/config/tc-mips.c gas/config/tc-mips.c
--- gas.1/config/tc-mips.c	2005-03-08 09:06:54.215895139 +0000
+++ gas/config/tc-mips.c	2005-03-08 09:07:40.562917062 +0000
@@ -119,6 +119,50 @@ extern int target_big_endian;
 			    ? ".rodata" \
 			    : (abort (), ""))
 
+/* Information about an instruction, including its format, operands
+   and fixups.  */
+struct mips_cl_insn
+{
+  /* The opcode's entry in mips_opcodes or mips16_opcodes.  */
+  const struct mips_opcode *insn_mo;
+
+  /* True if this is a mips16 instruction and if we want the extended
+     form of INSN_MO.  */
+  bfd_boolean use_extend;
+
+  /* The 16-bit extension instruction to use when USE_EXTEND is true.  */
+  unsigned short extend;
+
+  /* The 16-bit or 32-bit bitstring of the instruction itself.  This is
+     a copy of INSN_MO->match with the operands filled in.  */
+  unsigned long insn_opcode;
+
+  /* The frag that contains the instruction.  */
+  struct frag *frag;
+
+  /* The offset into FRAG of the first instruction byte.  */
+  long where;
+
+  /* The relocs associated with the instruction, if any.  */
+  fixS *fixp[3];
+
+  /* The reloc types associated with the instruction.  */
+  bfd_reloc_code_real_type reloc_type[3];
+
+  /* True if this entry describes a real instruction.  */
+  unsigned int valid_p : 1;
+
+  /* True if this instruction occured in a .set noreorder block.  */
+  unsigned int noreorder_p : 1;
+
+  /* True if this instruction corresponds to an assembler-filled
+     delay slot.  Always false if noreorder_p.  */
+  unsigned int delay_slot_p : 1;
+
+  /* True for extended mips16 instructions.  */
+  unsigned int extended_p : 1;
+};
+
 /* The ABI to use.  */
 enum mips_abi_level
 {
@@ -520,45 +564,13 @@ static int mips_optimize = 2;
    equivalent to seeing no -g option at all.  */
 static int mips_debug = 0;
 
-/* The previous instruction.  */
-static struct mips_cl_insn prev_insn;
+/* A list of previous instructions, with index 0 being the most recent.  */
+static struct mips_cl_insn history[2];
 
-/* The instruction before prev_insn.  */
-static struct mips_cl_insn prev_prev_insn;
-
-/* If we don't want information for prev_insn or prev_prev_insn, we
+/* If we don't want information for a history[] entry, we
    point the insn_mo field at this dummy integer.  */
 static const struct mips_opcode dummy_opcode = { NULL, NULL, 0, 0, 0, 0, 0 };
 
-/* Non-zero if prev_insn is valid.  */
-static int prev_insn_valid;
-
-/* The frag for the previous instruction.  */
-static struct frag *prev_insn_frag;
-
-/* The offset into prev_insn_frag for the previous instruction.  */
-static long prev_insn_where;
-
-/* The reloc type for the previous instruction, if any.  */
-static bfd_reloc_code_real_type prev_insn_reloc_type[3];
-
-/* The reloc for the previous instruction, if any.  */
-static fixS *prev_insn_fixp[3];
-
-/* Non-zero if the previous instruction was in a delay slot.  */
-static int prev_insn_is_delay_slot;
-
-/* Non-zero if the previous instruction was in a .set noreorder.  */
-static int prev_insn_unreordered;
-
-/* Non-zero if the previous instruction uses an extend opcode (if
-   mips16).  */
-static int prev_insn_extended;
-
-/* Non-zero if the previous previous instruction was in a .set
-   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
@@ -1515,7 +1527,7 @@ reg_needs_delay (unsigned int reg)
 {
   unsigned long prev_pinfo;
 
-  prev_pinfo = prev_insn.insn_mo->pinfo;
+  prev_pinfo = history[0].insn_mo->pinfo;
   if (! mips_opts.noreorder
       && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY)
 	   && ! gpr_interlocks)
@@ -1526,7 +1538,7 @@ reg_needs_delay (unsigned int reg)
 	 delay the use of general register rt for one instruction.  */
       /* Itbl support may require additional care here.  */
       know (prev_pinfo & INSN_WRITE_GPR_T);
-      if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
+      if (reg == ((history[0].insn_opcode >> OP_SH_RT) & OP_MASK_RT))
 	return 1;
     }
 
@@ -1627,7 +1639,7 @@ append_insn (struct mips_cl_insn *ip, ex
   /* Mark instruction labels in mips16 mode.  */
   mips16_mark_labels ();
 
-  prev_pinfo = prev_insn.insn_mo->pinfo;
+  prev_pinfo = history[0].insn_mo->pinfo;
   pinfo = ip->insn_mo->pinfo;
 
   if (mips_relax.sequence != 2
@@ -1676,7 +1688,7 @@ append_insn (struct mips_cl_insn *ip, ex
 	  know (prev_pinfo & INSN_WRITE_GPR_T);
 	  if (mips_optimize == 0
 	      || insn_uses_reg (ip,
-				((prev_insn.insn_opcode >> OP_SH_RT)
+				((history[0].insn_opcode >> OP_SH_RT)
 				 & OP_MASK_RT),
 				MIPS_GR_REG))
 	    ++nops;
@@ -1705,7 +1717,7 @@ append_insn (struct mips_cl_insn *ip, ex
 	    {
 	      if (mips_optimize == 0
 		  || insn_uses_reg (ip,
-				    ((prev_insn.insn_opcode >> OP_SH_FT)
+				    ((history[0].insn_opcode >> OP_SH_FT)
 				     & OP_MASK_FT),
 				    MIPS_FP_REG))
 		++nops;
@@ -1714,7 +1726,7 @@ append_insn (struct mips_cl_insn *ip, ex
 	    {
 	      if (mips_optimize == 0
 		  || insn_uses_reg (ip,
-				    ((prev_insn.insn_opcode >> OP_SH_FS)
+				    ((history[0].insn_opcode >> OP_SH_FS)
 				     & OP_MASK_FS),
 				    MIPS_FP_REG))
 		++nops;
@@ -1758,7 +1770,7 @@ append_insn (struct mips_cl_insn *ip, ex
 
       else if (mips_7000_hilo_fix
 	       && MF_HILO_INSN (prev_pinfo)
-	       && insn_uses_reg (ip, ((prev_insn.insn_opcode >> OP_SH_RD)
+	       && insn_uses_reg (ip, ((history[0].insn_opcode >> OP_SH_RD)
 				      & OP_MASK_RD),
 				 MIPS_GR_REG))
 	{
@@ -1771,8 +1783,8 @@ append_insn (struct mips_cl_insn *ip, ex
 	 insert one nop.  */
 
       else if (mips_7000_hilo_fix
-	       && MF_HILO_INSN (prev_prev_insn.insn_opcode)
-	       && insn_uses_reg (ip, ((prev_prev_insn.insn_opcode >> OP_SH_RD)
+	       && MF_HILO_INSN (history[1].insn_opcode)
+	       && insn_uses_reg (ip, ((history[1].insn_opcode >> OP_SH_RD)
                                        & OP_MASK_RD),
                                     MIPS_GR_REG))
 
@@ -1805,7 +1817,7 @@ append_insn (struct mips_cl_insn *ip, ex
 		       || (pinfo & MIPS16_INSN_BRANCH)))
 	    ++nops;
 	}
-      else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
+      else if (history[0].insn_mo->pinfo & INSN_READ_HI)
 	{
 	  /* The previous instruction reads the HI register; if the
 	     current instruction writes to the HI register, we must
@@ -1831,7 +1843,7 @@ append_insn (struct mips_cl_insn *ip, ex
       /* If the previous instruction was in a noreorder section, then
          we don't want to insert the nop after all.  */
       /* Itbl support may require additional care here.  */
-      if (prev_insn_unreordered)
+      if (history[0].noreorder_p)
 	nops = 0;
 
       /* There are two cases which require two intervening
@@ -1843,15 +1855,15 @@ append_insn (struct mips_cl_insn *ip, ex
 	 instruction, we must check for these cases compared to the
 	 instruction previous to the previous instruction.  */
       if ((! mips_opts.mips16
-	   && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
-	   && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+	   && (history[1].insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+	   && (history[1].insn_mo->pinfo & INSN_WRITE_COND_CODE)
 	   && (pinfo & INSN_READ_COND_CODE)
 	   && ! cop_interlocks)
-	  || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+	  || ((history[1].insn_mo->pinfo & INSN_READ_LO)
 	      && (pinfo & INSN_WRITE_LO)
 	      && ! (hilo_interlocks
 		    || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT))))
-	  || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+	  || ((history[1].insn_mo->pinfo & INSN_READ_HI)
 	      && (pinfo & INSN_WRITE_HI)
 	      && ! (hilo_interlocks
 		    || (mips_opts.arch == CPU_R3900 && (pinfo & INSN_MULT)))))
@@ -1859,19 +1871,19 @@ append_insn (struct mips_cl_insn *ip, ex
       else
 	prev_prev_nop = 0;
 
-      if (prev_prev_insn_unreordered)
+      if (history[1].noreorder_p)
 	prev_prev_nop = 0;
 
       if (prev_prev_nop && nops == 0)
 	++nops;
 
-      if (mips_fix_vr4120 && prev_insn.insn_mo->name)
+      if (mips_fix_vr4120 && history[0].insn_mo->name)
 	{
 	  /* We're out of bits in pinfo, so we must resort to string
 	     ops here.  Shortcuts are selected based on opcodes being
 	     limited to the VR4120 instruction set.  */
 	  int min_nops = 0;
-	  const char *pn = prev_insn.insn_mo->name;
+	  const char *pn = history[0].insn_mo->name;
 	  const char *tn = ip->insn_mo->name;
 	  if (strncmp (pn, "macc", 4) == 0
 	      || strncmp (pn, "dmacc", 5) == 0)
@@ -2024,8 +2036,8 @@ append_insn (struct mips_cl_insn *ip, ex
 #endif
 
   /* Record the frag type before frag_var.  */
-  if (prev_insn_frag)
-    prev_insn_frag_type = prev_insn_frag->fr_type;
+  if (history[0].frag)
+    prev_insn_frag_type = history[0].frag->fr_type;
 
   if (address_expr
       && *reloc_type == BFD_RELOC_16_PCREL_S2
@@ -2064,7 +2076,7 @@ append_insn (struct mips_cl_insn *ip, ex
 					 mips16_small, mips16_ext,
 					 (prev_pinfo
 					  & INSN_UNCOND_BRANCH_DELAY),
-					 (*prev_insn_reloc_type
+					 (*history[0].reloc_type
 					  == BFD_RELOC_MIPS16_JMP)),
 		    make_expr_symbol (address_expr), 0, NULL);
     }
@@ -2348,10 +2360,10 @@ append_insn (struct mips_cl_insn *ip, ex
 	      || nops != 0
 	      /* If we don't even know the previous insn, we can not
 		 swap.  */
-	      || ! prev_insn_valid
+	      || ! history[0].valid_p
 	      /* If the previous insn is already in a branch delay
 		 slot, then we can not swap.  */
-	      || prev_insn_is_delay_slot
+	      || history[0].delay_slot_p
 	      /* If the previous previous insn was in a .set
 		 noreorder, we can't swap.  Actually, the MIPS
 		 assembler will swap in this situation.  However, gcc
@@ -2364,11 +2376,11 @@ append_insn (struct mips_cl_insn *ip, ex
 		 in which we can not swap the bne and INSN.  If gcc is
 		 not configured -with-gnu-as, it does not output the
 		 .set pseudo-ops.  We don't have to check
-		 prev_insn_unreordered, because prev_insn_valid will
+		 history[0].noreorder_p, because history[0].valid_p will
 		 be 0 in that case.  We don't want to use
-		 prev_prev_insn_valid, because we do want to be able
+		 history[1].valid_p, because we do want to be able
 		 to swap at the start of a function.  */
-	      || prev_prev_insn_unreordered
+	      || history[1].noreorder_p
 	      /* If the branch is itself the target of a branch, we
 		 can not swap.  We cheat on this; all we check for is
 		 whether there is a label on this instruction.  If
@@ -2428,31 +2440,31 @@ append_insn (struct mips_cl_insn *ip, ex
 	      || (! mips_opts.mips16
 		  && (prev_pinfo & INSN_WRITE_GPR_T)
 		  && insn_uses_reg (ip,
-				    ((prev_insn.insn_opcode >> OP_SH_RT)
+				    ((history[0].insn_opcode >> OP_SH_RT)
 				     & OP_MASK_RT),
 				    MIPS_GR_REG))
 	      || (! mips_opts.mips16
 		  && (prev_pinfo & INSN_WRITE_GPR_D)
 		  && insn_uses_reg (ip,
-				    ((prev_insn.insn_opcode >> OP_SH_RD)
+				    ((history[0].insn_opcode >> OP_SH_RD)
 				     & OP_MASK_RD),
 				    MIPS_GR_REG))
 	      || (mips_opts.mips16
 		  && (((prev_pinfo & MIPS16_INSN_WRITE_X)
 		       && insn_uses_reg (ip,
-					 ((prev_insn.insn_opcode
+					 ((history[0].insn_opcode
 					   >> MIPS16OP_SH_RX)
 					  & MIPS16OP_MASK_RX),
 					 MIPS16_REG))
 		      || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
 			  && insn_uses_reg (ip,
-					    ((prev_insn.insn_opcode
+					    ((history[0].insn_opcode
 					      >> MIPS16OP_SH_RY)
 					     & MIPS16OP_MASK_RY),
 					    MIPS16_REG))
 		      || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
 			  && insn_uses_reg (ip,
-					    ((prev_insn.insn_opcode
+					    ((history[0].insn_opcode
 					      >> MIPS16OP_SH_RZ)
 					     & MIPS16OP_MASK_RZ),
 					    MIPS16_REG))
@@ -2462,8 +2474,8 @@ append_insn (struct mips_cl_insn *ip, ex
 			  && insn_uses_reg (ip, RA, MIPS_GR_REG))
 		      || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
 			  && insn_uses_reg (ip,
-					    MIPS16OP_EXTRACT_REG32R (prev_insn.
-								     insn_opcode),
+					    MIPS16OP_EXTRACT_REG32R
+					      (history[0].insn_opcode),
 					    MIPS_GR_REG))))
 	      /* If the branch writes a register that the previous
 		 instruction sets, we can not swap (we know that
@@ -2471,54 +2483,54 @@ append_insn (struct mips_cl_insn *ip, ex
 	      || (! mips_opts.mips16
 		  && (prev_pinfo & INSN_WRITE_GPR_T)
 		  && (((pinfo & INSN_WRITE_GPR_D)
-		       && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)
+		       && (((history[0].insn_opcode >> OP_SH_RT) & OP_MASK_RT)
 			   == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
 		      || ((pinfo & INSN_WRITE_GPR_31)
-			  && (((prev_insn.insn_opcode >> OP_SH_RT)
+			  && (((history[0].insn_opcode >> OP_SH_RT)
 			       & OP_MASK_RT)
 			      == RA))))
 	      || (! mips_opts.mips16
 		  && (prev_pinfo & INSN_WRITE_GPR_D)
 		  && (((pinfo & INSN_WRITE_GPR_D)
-		       && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD)
+		       && (((history[0].insn_opcode >> OP_SH_RD) & OP_MASK_RD)
 			   == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
 		      || ((pinfo & INSN_WRITE_GPR_31)
-			  && (((prev_insn.insn_opcode >> OP_SH_RD)
+			  && (((history[0].insn_opcode >> OP_SH_RD)
 			       & OP_MASK_RD)
 			      == RA))))
 	      || (mips_opts.mips16
 		  && (pinfo & MIPS16_INSN_WRITE_31)
 		  && ((prev_pinfo & MIPS16_INSN_WRITE_31)
 		      || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
-			  && (MIPS16OP_EXTRACT_REG32R (prev_insn.insn_opcode)
+			  && (MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode)
 			      == RA))))
 	      /* If the branch writes a register that the previous
 		 instruction reads, we can not swap (we know that
 		 branches only write to RD or to $31).  */
 	      || (! mips_opts.mips16
 		  && (pinfo & INSN_WRITE_GPR_D)
-		  && insn_uses_reg (&prev_insn,
+		  && insn_uses_reg (&history[0],
 				    ((ip->insn_opcode >> OP_SH_RD)
 				     & OP_MASK_RD),
 				    MIPS_GR_REG))
 	      || (! mips_opts.mips16
 		  && (pinfo & INSN_WRITE_GPR_31)
-		  && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
+		  && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
 	      || (mips_opts.mips16
 		  && (pinfo & MIPS16_INSN_WRITE_31)
-		  && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
+		  && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
 	      /* If the previous previous instruction has a load
 		 delay, and sets a register that the branch reads, we
 		 can not swap.  */
 	      || (! mips_opts.mips16
               /* Itbl support may require additional care here.  */
-		  && (((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
+		  && (((history[1].insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
 		       && ! cop_interlocks)
-		      || ((prev_prev_insn.insn_mo->pinfo
+		      || ((history[1].insn_mo->pinfo
 			   & INSN_LOAD_MEMORY_DELAY)
 			  && ! gpr_interlocks))
 		  && insn_uses_reg (ip,
-				    ((prev_prev_insn.insn_opcode >> OP_SH_RT)
+				    ((history[1].insn_opcode >> OP_SH_RT)
 				     & OP_MASK_RT),
 				    MIPS_GR_REG))
 	      /* If one instruction sets a condition code and the
@@ -2533,11 +2545,11 @@ append_insn (struct mips_cl_insn *ip, ex
 		  && (prev_pinfo & MIPS16_INSN_READ_PC))
 	      /* If the previous instruction was extended, we can not
                  swap.  */
-	      || (mips_opts.mips16 && prev_insn_extended)
+	      || (mips_opts.mips16 && history[0].extended_p)
 	      /* If the previous instruction had a fixup in mips16
                  mode, we can not swap.  This normally means that the
                  previous instruction was a 4 byte branch anyhow.  */
-	      || (mips_opts.mips16 && prev_insn_fixp[0])
+	      || (mips_opts.mips16 && history[0].fixp[0])
 	      /* If the previous instruction is a sync, sync.l, or
 		 sync.p, we can not swap.  */
 	      || (prev_pinfo & INSN_SYNC))
@@ -2550,8 +2562,11 @@ append_insn (struct mips_cl_insn *ip, ex
 	      if (mips_relax.sequence)
 		mips_relax.sizes[mips_relax.sequence - 1] += 4;
 	      /* Update the previous insn information.  */
-	      prev_prev_insn = *ip;
-	      prev_insn.insn_mo = &dummy_opcode;
+	      history[1].insn_mo = ip->insn_mo;
+	      history[1].use_extend = ip->use_extend;
+	      history[1].extend = ip->extend;
+	      history[1].insn_opcode = ip->insn_opcode;
+	      history[0].insn_mo = &dummy_opcode;
 	    }
 	  else
 	    {
@@ -2561,7 +2576,7 @@ append_insn (struct mips_cl_insn *ip, ex
 		  char *prev_f;
 		  char temp[4];
 
-		  prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+		  prev_f = history[0].frag->fr_literal + history[0].where;
 		  if (!relaxed_branch)
 		    {
 		      /* If this is not a relaxed branch, then just
@@ -2580,31 +2595,31 @@ append_insn (struct mips_cl_insn *ip, ex
 			 into the space freed by the moved instruction.  */
 		      f = frag_more (4);
 		      memcpy (f, prev_f, 4);
-		      prev_insn_frag->fr_fix -= 4;
-		      if (prev_insn_frag->fr_type == rs_machine_dependent)
-			memmove (prev_f, prev_f + 4, prev_insn_frag->fr_var);
+		      history[0].frag->fr_fix -= 4;
+		      if (history[0].frag->fr_type == rs_machine_dependent)
+			memmove (prev_f, prev_f + 4, history[0].frag->fr_var);
 		    }
 
-		  if (prev_insn_fixp[0])
+		  if (history[0].fixp[0])
 		    {
-		      prev_insn_fixp[0]->fx_frag = frag_now;
-		      prev_insn_fixp[0]->fx_where = f - frag_now->fr_literal;
+		      history[0].fixp[0]->fx_frag = frag_now;
+		      history[0].fixp[0]->fx_where = f - frag_now->fr_literal;
 		    }
-		  if (prev_insn_fixp[1])
+		  if (history[0].fixp[1])
 		    {
-		      prev_insn_fixp[1]->fx_frag = frag_now;
-		      prev_insn_fixp[1]->fx_where = f - frag_now->fr_literal;
+		      history[0].fixp[1]->fx_frag = frag_now;
+		      history[0].fixp[1]->fx_where = f - frag_now->fr_literal;
 		    }
-		  if (prev_insn_fixp[2])
+		  if (history[0].fixp[2])
 		    {
-		      prev_insn_fixp[2]->fx_frag = frag_now;
-		      prev_insn_fixp[2]->fx_where = f - frag_now->fr_literal;
+		      history[0].fixp[2]->fx_frag = frag_now;
+		      history[0].fixp[2]->fx_where = f - frag_now->fr_literal;
 		    }
-		  if (prev_insn_fixp[0] && HAVE_NEWABI
-		      && prev_insn_frag != frag_now
-		      && (prev_insn_fixp[0]->fx_r_type
+		  if (history[0].fixp[0] && HAVE_NEWABI
+		      && history[0].frag != frag_now
+		      && (history[0].fixp[0]->fx_r_type
 			  == BFD_RELOC_MIPS_GOT_DISP
-			  || (prev_insn_fixp[0]->fx_r_type
+			  || (history[0].fixp[0]->fx_r_type
 			      == BFD_RELOC_MIPS_CALL16)))
 		    {
 		      /* To avoid confusion in tc_gen_reloc, we must
@@ -2617,21 +2632,21 @@ append_insn (struct mips_cl_insn *ip, ex
 		    {
 		      if (fixp[0])
 			{
-			  fixp[0]->fx_frag = prev_insn_frag;
-			  fixp[0]->fx_where = prev_insn_where;
+			  fixp[0]->fx_frag = history[0].frag;
+			  fixp[0]->fx_where = history[0].where;
 			}
 		      if (fixp[1])
 			{
-			  fixp[1]->fx_frag = prev_insn_frag;
-			  fixp[1]->fx_where = prev_insn_where;
+			  fixp[1]->fx_frag = history[0].frag;
+			  fixp[1]->fx_where = history[0].where;
 			}
 		      if (fixp[2])
 			{
-			  fixp[2]->fx_frag = prev_insn_frag;
-			  fixp[2]->fx_where = prev_insn_where;
+			  fixp[2]->fx_frag = history[0].frag;
+			  fixp[2]->fx_where = history[0].where;
 			}
 		    }
-		  else if (prev_insn_frag->fr_type == rs_machine_dependent)
+		  else if (history[0].frag->fr_type == rs_machine_dependent)
 		    {
 		      if (fixp[0])
 			fixp[0]->fx_where -= 4;
@@ -2646,10 +2661,10 @@ append_insn (struct mips_cl_insn *ip, ex
 		  char *prev_f;
 		  char temp[2];
 
-		  assert (prev_insn_fixp[0] == NULL);
-		  assert (prev_insn_fixp[1] == NULL);
-		  assert (prev_insn_fixp[2] == NULL);
-		  prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+		  assert (history[0].fixp[0] == NULL);
+		  assert (history[0].fixp[1] == NULL);
+		  assert (history[0].fixp[2] == NULL);
+		  prev_f = history[0].frag->fr_literal + history[0].where;
 		  memcpy (temp, prev_f, 2);
 		  memcpy (prev_f, f, 2);
 		  if (*reloc_type != BFD_RELOC_MIPS16_JMP)
@@ -2664,42 +2679,45 @@ append_insn (struct mips_cl_insn *ip, ex
 		    }
 		  if (fixp[0])
 		    {
-		      fixp[0]->fx_frag = prev_insn_frag;
-		      fixp[0]->fx_where = prev_insn_where;
+		      fixp[0]->fx_frag = history[0].frag;
+		      fixp[0]->fx_where = history[0].where;
 		    }
 		  if (fixp[1])
 		    {
-		      fixp[1]->fx_frag = prev_insn_frag;
-		      fixp[1]->fx_where = prev_insn_where;
+		      fixp[1]->fx_frag = history[0].frag;
+		      fixp[1]->fx_where = history[0].where;
 		    }
 		  if (fixp[2])
 		    {
-		      fixp[2]->fx_frag = prev_insn_frag;
-		      fixp[2]->fx_where = prev_insn_where;
+		      fixp[2]->fx_frag = history[0].frag;
+		      fixp[2]->fx_where = history[0].where;
 		    }
 		}
 
-	      /* Update the previous insn information; leave prev_insn
+	      /* Update the previous insn information; leave history[0]
 		 unchanged.  */
-	      prev_prev_insn = *ip;
+	      history[1].insn_mo = ip->insn_mo;
+	      history[1].use_extend = ip->use_extend;
+	      history[1].extend = ip->extend;
+	      history[1].insn_opcode = ip->insn_opcode;
 	    }
-	  prev_insn_is_delay_slot = 1;
+	  history[0].delay_slot_p = 1;
 
 	  /* If that was an unconditional branch, forget the previous
 	     insn information.  */
 	  if (pinfo & INSN_UNCOND_BRANCH_DELAY)
 	    {
-	      prev_prev_insn.insn_mo = &dummy_opcode;
-	      prev_insn.insn_mo = &dummy_opcode;
+	      history[1].insn_mo = &dummy_opcode;
+	      history[0].insn_mo = &dummy_opcode;
 	    }
 
-	  prev_insn_fixp[0] = NULL;
-	  prev_insn_fixp[1] = NULL;
-	  prev_insn_fixp[2] = NULL;
-	  prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
-	  prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
-	  prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
-	  prev_insn_extended = 0;
+	  history[0].fixp[0] = NULL;
+	  history[0].fixp[1] = NULL;
+	  history[0].fixp[2] = NULL;
+	  history[0].reloc_type[0] = BFD_RELOC_UNUSED;
+	  history[0].reloc_type[1] = BFD_RELOC_UNUSED;
+	  history[0].reloc_type[2] = BFD_RELOC_UNUSED;
+	  history[0].extended_p = 0;
 	}
       else if (pinfo & INSN_COND_BRANCH_LIKELY)
 	{
@@ -2709,60 +2727,77 @@ append_insn (struct mips_cl_insn *ip, ex
 	     the next instruction.  */
 	  emit_nop ();
 	  /* Update the previous insn information.  */
-	  prev_prev_insn = *ip;
-	  prev_insn.insn_mo = &dummy_opcode;
-	  prev_insn_fixp[0] = NULL;
-	  prev_insn_fixp[1] = NULL;
-	  prev_insn_fixp[2] = NULL;
-	  prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
-	  prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
-	  prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
-	  prev_insn_extended = 0;
-	  prev_insn_is_delay_slot = 1;
+	  history[1].insn_mo = ip->insn_mo;
+	  history[1].use_extend = ip->use_extend;
+	  history[1].extend = ip->extend;
+	  history[1].insn_opcode = ip->insn_opcode;
+	  history[0].insn_mo = &dummy_opcode;
+	  history[0].fixp[0] = NULL;
+	  history[0].fixp[1] = NULL;
+	  history[0].fixp[2] = NULL;
+	  history[0].reloc_type[0] = BFD_RELOC_UNUSED;
+	  history[0].reloc_type[1] = BFD_RELOC_UNUSED;
+	  history[0].reloc_type[2] = BFD_RELOC_UNUSED;
+	  history[0].extended_p = 0;
+	  history[0].delay_slot_p = 1;
 	}
       else
 	{
 	  /* Update the previous insn information.  */
 	  if (nops > 0)
-	    prev_prev_insn.insn_mo = &dummy_opcode;
+	    history[1].insn_mo = &dummy_opcode;
 	  else
-	    prev_prev_insn = prev_insn;
-	  prev_insn = *ip;
+	    {
+	      history[1].insn_mo = history[0].insn_mo;
+	      history[1].use_extend = history[0].use_extend;
+	      history[1].extend = history[0].extend;
+	      history[1].insn_opcode = history[0].insn_opcode;
+	    }
+	  history[0].insn_mo = ip->insn_mo;
+	  history[0].use_extend = ip->use_extend;
+	  history[0].extend = ip->extend;
+	  history[0].insn_opcode = ip->insn_opcode;
 
 	  /* Any time we see a branch, we always fill the delay slot
 	     immediately; since this insn is not a branch, we know it
 	     is not in a delay slot.  */
-	  prev_insn_is_delay_slot = 0;
+	  history[0].delay_slot_p = 0;
 
-	  prev_insn_fixp[0] = fixp[0];
-	  prev_insn_fixp[1] = fixp[1];
-	  prev_insn_fixp[2] = fixp[2];
-	  prev_insn_reloc_type[0] = reloc_type[0];
-	  prev_insn_reloc_type[1] = reloc_type[1];
-	  prev_insn_reloc_type[2] = reloc_type[2];
+	  history[0].fixp[0] = fixp[0];
+	  history[0].fixp[1] = fixp[1];
+	  history[0].fixp[2] = fixp[2];
+	  history[0].reloc_type[0] = reloc_type[0];
+	  history[0].reloc_type[1] = reloc_type[1];
+	  history[0].reloc_type[2] = reloc_type[2];
 	  if (mips_opts.mips16)
-	    prev_insn_extended = (ip->use_extend
-				  || *reloc_type > BFD_RELOC_UNUSED);
+	    history[0].extended_p = (ip->use_extend
+				     || *reloc_type > BFD_RELOC_UNUSED);
 	}
 
-      prev_prev_insn_unreordered = prev_insn_unreordered;
-      prev_insn_unreordered = 0;
-      prev_insn_frag = frag_now;
-      prev_insn_where = f - frag_now->fr_literal;
-      prev_insn_valid = 1;
+      history[1].noreorder_p = history[0].noreorder_p;
+      history[0].noreorder_p = 0;
+      history[0].frag = frag_now;
+      history[0].where = f - frag_now->fr_literal;
+      history[0].valid_p = 1;
     }
   else if (mips_relax.sequence != 2)
     {
       /* 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[0] = reloc_type[0];
-      prev_insn_reloc_type[1] = reloc_type[1];
-      prev_insn_reloc_type[2] = reloc_type[2];
-      prev_prev_insn_unreordered = prev_insn_unreordered;
-      prev_insn_unreordered = 1;
+      history[1].insn_mo = history[0].insn_mo;
+      history[1].use_extend = history[0].use_extend;
+      history[1].extend = history[0].extend;
+      history[1].insn_opcode = history[0].insn_opcode;
+      history[0].insn_mo = ip->insn_mo;
+      history[0].use_extend = ip->use_extend;
+      history[0].extend = ip->extend;
+      history[0].insn_opcode = ip->insn_opcode;
+      history[0].reloc_type[0] = reloc_type[0];
+      history[0].reloc_type[1] = reloc_type[1];
+      history[0].reloc_type[2] = reloc_type[2];
+      history[1].noreorder_p = history[0].noreorder_p;
+      history[0].noreorder_p = 1;
     }
 
   /* We just output an insn, so the next one doesn't have a label.  */
@@ -2778,21 +2813,21 @@ mips_no_prev_insn (int preserve)
 {
   if (! preserve)
     {
-      prev_insn.insn_mo = &dummy_opcode;
-      prev_prev_insn.insn_mo = &dummy_opcode;
+      history[0].insn_mo = &dummy_opcode;
+      history[1].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[0] = BFD_RELOC_UNUSED;
-  prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
-  prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
-  prev_prev_insn_unreordered = 0;
+  history[0].valid_p = 0;
+  history[0].delay_slot_p = 0;
+  history[0].noreorder_p = 0;
+  history[0].extended_p = 0;
+  history[0].reloc_type[0] = BFD_RELOC_UNUSED;
+  history[0].reloc_type[1] = BFD_RELOC_UNUSED;
+  history[0].reloc_type[2] = BFD_RELOC_UNUSED;
+  history[1].noreorder_p = 0;
   mips_clear_insn_labels ();
 }
 
@@ -2811,51 +2846,51 @@ mips_emit_delays (bfd_boolean insns)
 
       nops = 0;
       if ((! mips_opts.mips16
-	   && ((prev_insn.insn_mo->pinfo
+	   && ((history[0].insn_mo->pinfo
 		& (INSN_LOAD_COPROC_DELAY
 		   | INSN_COPROC_MOVE_DELAY
 		   | INSN_WRITE_COND_CODE))
 	       && ! cop_interlocks))
 	  || (! hilo_interlocks
-	      && (prev_insn.insn_mo->pinfo
+	      && (history[0].insn_mo->pinfo
 		  & (INSN_READ_LO
 		     | INSN_READ_HI)))
 	  || (! mips_opts.mips16
-	      && (prev_insn.insn_mo->pinfo & INSN_LOAD_MEMORY_DELAY)
+	      && (history[0].insn_mo->pinfo & INSN_LOAD_MEMORY_DELAY)
 	      && ! gpr_interlocks)
 	  || (! mips_opts.mips16
-	      && (prev_insn.insn_mo->pinfo & INSN_COPROC_MEMORY_DELAY)
+	      && (history[0].insn_mo->pinfo & INSN_COPROC_MEMORY_DELAY)
 	      && ! cop_mem_interlocks))
 	{
 	  /* Itbl support may require additional care here.  */
 	  ++nops;
 	  if ((! mips_opts.mips16
-	       && ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+	       && ((history[0].insn_mo->pinfo & INSN_WRITE_COND_CODE)
 		   && ! cop_interlocks))
 	      || (! hilo_interlocks
-		  && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
-		      || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+		  && ((history[0].insn_mo->pinfo & INSN_READ_HI)
+		      || (history[0].insn_mo->pinfo & INSN_READ_LO))))
 	    ++nops;
 
-	  if (prev_insn_unreordered)
+	  if (history[0].noreorder_p)
 	    nops = 0;
 	}
       else if ((! mips_opts.mips16
-		&& ((prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+		&& ((history[1].insn_mo->pinfo & INSN_WRITE_COND_CODE)
 		    && ! cop_interlocks))
 	       || (! hilo_interlocks
-		   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-		       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+		   && ((history[1].insn_mo->pinfo & INSN_READ_HI)
+		       || (history[1].insn_mo->pinfo & INSN_READ_LO))))
 	{
 	  /* Itbl support may require additional care here.  */
-	  if (! prev_prev_insn_unreordered)
+	  if (! history[1].noreorder_p)
 	    ++nops;
 	}
 
-      if (mips_fix_vr4120 && prev_insn.insn_mo->name)
+      if (mips_fix_vr4120 && history[0].insn_mo->name)
 	{
 	  int min_nops = 0;
-	  const char *pn = prev_insn.insn_mo->name;
+	  const char *pn = history[0].insn_mo->name;
 	  if (strncmp (pn, "macc", 4) == 0
 	      || strncmp (pn, "dmacc", 5) == 0
 	      || strncmp (pn, "dmult", 5) == 0
@@ -2920,7 +2955,7 @@ macro_start (void)
 {
   memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
   mips_macro_warning.delay_slot_p = (mips_opts.noreorder
-				     && (prev_insn.insn_mo->pinfo
+				     && (history[0].insn_mo->pinfo
 					 & (INSN_UNCOND_BRANCH_DELAY
 					    | INSN_COND_BRANCH_DELAY
 					    | INSN_COND_BRANCH_LIKELY)) != 0);

-------------- next part --------------
<VR4133 Problem> ===========================================================

[Restriction 15] CPU (4)

When "mult/div/macc" instruction (NOTE1) follows "mflo/mfhi" instruction, 
the "mult/div/macc" result is mistaken for "mflo/mfhi" one in the following
conditions.

NOTE1 MULT/MULTU/DMULT/DMULTU, DIV/DIVU/DDIV/DDIVU, 
      MACC/DMACC, MACCU/DMACCU, MACCHI/DMACCHI, 
      MACCHIU/DMACCHIU, MACCS/DMACCS, MACCUS/DMACCUS, 
      MACCHIS/DMACCHIS, MACCHIUS/DMACCHIUS

  Conditions:
       - The instruction steps are less than 4 between "mflo/mfhi" and 
         "mult/div/macc".
       - The "mflo/mfhi" execution is stalled or held during executing
         the load/store instructions located in right before the mflo/mfhi
         (within 2 instructions).


<Relevant instruction sequence>
The following instruction sequence causes the problem.
---------------------------
all load/store instruction
 (0 or 1 instruction)
mflo/mfhi
 (3 or less instructions)
mult/div/macc NOTE1, NOTE2

NOTE2 The following case is the exception and doesn't cause
      the problem.
   - The case which the result of mflo/mfhi is used for 
     the source of mult/div/macc
---------------------------


<Possible work-arounds>

Either one of the following four can be the work-around.

(1) Place 4 or more instructions between mflo/mfhi and mult/div/macc.

ex.
?@?@mflo  s2
    nop
    nop
    nop
    nop
    mult  s1,v1

(2) Place the dummy instruction which generates the source interlock right after
    the mflo/mfhi.

ex1.
    mflo  s2
    or    zero,zero,s2
    mult  s1,v1

ex2.
?@?@mflo  s2
    addu  zero,zero,s2
    mult  s1,v1

(3) Don't put load/store instruction before mflo/mfhi within 2 instructions.
    Or don't put mflo/mfhi instruction after load/store instruction within 
    2 instructions. 

ex.
    lw  s0,0(a0)
    nop
    nop
    mflo  s2
    mult  s1,v1

(4) Use the MACC type of instructions which have destination instead of
    "mflo/mfhi" instructions.

ex1.
    mtlo zero
    macc  s2,a1,v0   /* alternative to mflo */
    mult  s1,v1

ex2.
    mtlo zero
    mthi zero
    macchi  s2,a1,v0 /* alternative to mfhi */
    mult  s1,v1

<End> ===========================================================


More information about the Binutils mailing list