head: MIPS: A workaround for the R4000 divide/shift errata

Richard Sandiford rsandifo@redhat.com
Sat Feb 28 22:28:00 GMT 2004


For the record: there was a bit of doubt about the status of Maciej's
copyright assignment.  He was kind enough to dig out the paper copy
(past & future gcc assignment from 2001) and sent me a scanned image.
So that's _that_ sorted out...

"Maciej W. Rozycki" <macro@ds2.pg.gda.pl> writes:
> 	* gcc/config/mips/mips.h (MASK_FIX_4000): New flag for workarounds
> 	for R4000 errata.
> 	(TARGET_FIX_4000): New macro to control the workarounds.
> 	(TARGET_SWITCHES): Add "fix-4000" and "no-fix-4000" to control the
> 	workarounds.
> 	gcc/config/mips/mips.c (mips_output_division): Add a workaround
> 	for the R4000 divide/shift errata.
> 	(mips_idiv_insns): New function.
> 	gcc/config/mips/mips.md (length): Use mips_idiv_insns() for
> 	calculation for "idiv".
> 	config/mips/mips-protos.h (mips_idiv_insns): New prototype.
> 	doc/invoke.texi: Document the new switches.

I hope you don't mind, but I decided to change the name of the option to
-mfix-r4000 (and changed the names of the internal macros accordingly).
-march=r4000 is the canonical option for selecting R4000 code and it
would be confusing to have a different convention for -mfix-*.

[ I know this isn't really consistent with the existing -mfix4300
  option, but then again, neither was -mfix-4000.  -mfix4300 should
  probably have been named -mfix-vr4300 (to make the family explicit)
  but it's too late do anything about that now.

  Also, gas does have a -mfix-7000 option, but the negative of that
  is -mno-fix7000 (no extra dash), so I don't see that as a good
  example to follow.  There's also -mfix-vr4122-bugs, which is a bit
  more consistent with the new name, except for the extra "-bugs".

  -mfix-r4000 seems more in the spirit of -mfix-sb1 though.

  All anal stuff, but I had a feeling someone would point this out
  to me unless I proved I'd done my homework. ;) ]

I was going to add a comment about why the nop was needed even for
-mcheck-zero-division.  In the end, I figured it was easier just to
force the division into the delay slot as previously discussed.
Working on that now.

Patch applied to HEAD after bootstrapping & regression testing on
mips-sgi-irix6.5.  Thanks for your patience.

Richard


	* config/mips/mips-protos.h (mips_idiv_insns): Declare.
	* config/mips/mips.h (MASK_FIX_SB1): Bump.
	(MASK_FIX_R4000, TARGET_FIX_R4000): New macros.
	(TARGET_SWITCHES): Add -mfix-r4000 and -mno-fix-r4000.
	* config/mips/mips.c (mips_idiv_insns): New function.
	(override_options): Make -march=r4000 imply -mfix-r4000 by default.
	(mips_output_division): Add a workaround for the R4000 divide/shift
	errata.
	* config/mips/mips.md (length): Use mips_idiv_insns() to calculate
	the length of an "idiv" instruction.
	* doc/invoke.texi: Document the new switches.

Index: config/mips/mips-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips-protos.h,v
retrieving revision 1.64
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.64 mips-protos.h
--- config/mips/mips-protos.h	14 Feb 2004 13:00:49 -0000	1.64
+++ config/mips/mips-protos.h	28 Feb 2004 19:52:34 -0000
@@ -30,6 +30,7 @@ extern int mips_regno_mode_ok_for_base_p
 extern int mips_address_insns (rtx, enum machine_mode);
 extern int mips_const_insns (rtx);
 extern int mips_fetch_insns (rtx);
+extern int mips_idiv_insns (void);
 extern bool mips_legitimate_address_p (enum machine_mode, rtx, int);
 extern bool mips_legitimize_address (rtx *, enum machine_mode);
 extern rtx mips_gotoff_page (rtx);
Index: config/mips/mips.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.h,v
retrieving revision 1.321
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.321 mips.h
--- config/mips/mips.h	14 Feb 2004 12:45:52 -0000	1.321
+++ config/mips/mips.h	28 Feb 2004 19:52:35 -0000
@@ -169,7 +169,8 @@ #define MASK_BRANCHLIKELY  0x00400000   
 #define MASK_UNINIT_CONST_IN_RODATA \
 			   0x00800000	/* Store uninitialized
 					   consts in rodata */
-#define MASK_FIX_SB1       0x01000000   /* Work around SB-1 errata.  */
+#define MASK_FIX_R4000	   0x01000000	/* Work around R4000 errata.  */
+#define MASK_FIX_SB1       0x02000000   /* Work around SB-1 errata.  */
 
 					/* Debug switches, not documented */
 #define MASK_DEBUG	0		/* unused */
@@ -248,6 +249,9 @@ #define TARGET_BRANCHLIKELY	(target_flag
 
 #define TARGET_FIX_SB1		(target_flags & MASK_FIX_SB1)
 
+					/* Work around R4000 errata.  */
+#define TARGET_FIX_R4000	(target_flags & MASK_FIX_R4000)
+
 /* True if we should use NewABI-style relocation operators for
    symbolic addresses.  This is never true for mips16 code,
    which has its own conventions.  */
@@ -588,6 +592,10 @@ #define TARGET_SWITCHES							\
      N_("Work around errata for early SB-1 revision 2 cores")},		\
   {"no-fix-sb1",         -MASK_FIX_SB1,					\
      N_("Don't work around errata for early SB-1 revision 2 cores")},	\
+  {"fix-r4000",		  MASK_FIX_R4000,				\
+     N_("Work around R4000 errata")},					\
+  {"no-fix-r4000",	 -MASK_FIX_R4000,				\
+     N_("Don't work around R4000 errata")},				\
   {"check-zero-division",-MASK_NO_CHECK_ZERO_DIV,			\
      N_("Trap on integer divide by zero")},				\
   {"no-check-zero-division", MASK_NO_CHECK_ZERO_DIV,			\
Index: config/mips/mips.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.c,v
retrieving revision 1.386
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.386 mips.c
--- config/mips/mips.c	24 Feb 2004 23:40:02 -0000	1.386
+++ config/mips/mips.c	28 Feb 2004 19:52:37 -0000
@@ -1350,6 +1350,27 @@ mips_fetch_insns (rtx x)
 }
 
 
+/* Return the number of instructions needed for an integer division.  */
+
+int
+mips_idiv_insns (void)
+{
+  int count;
+
+  count = 1;
+  if (TARGET_CHECK_ZERO_DIV)
+    {
+      if (TARGET_MIPS16)
+	count += 2;
+      else
+	count += 3;
+    }
+  if (TARGET_FIX_R4000)
+    count++;
+  return count;
+}
+
+
 /* Return truth value of whether OP can be used as an operands
    where a register or 16 bit unsigned integer is needed.  */
 
@@ -5111,6 +5132,12 @@ override_options (void)
       mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
       mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
     }
+
+  /* Default to working around R4000 errata only if the processor
+     was selected explicitly.  */
+  if ((target_flags_explicit & MASK_FIX_R4000) == 0
+      && mips_matching_cpu_name_p (mips_arch_info->name, "r4000"))
+    target_flags |= MASK_FIX_R4000;
 }
 
 /* Implement CONDITIONAL_REGISTER_USAGE.  */
@@ -9185,21 +9212,37 @@ mips_output_conditional_branch (rtx insn
 /* Used to output div or ddiv instruction DIVISION, which has the
    operands given by OPERANDS.  If we need a divide-by-zero check,
    output the instruction and return an asm string that traps if
-   operand 2 is zero.  Otherwise just return DIVISION itself.  */
+   operand 2 is zero.
+
+   The original R4000 has a cpu bug.  If a double-word or a variable
+   shift executes immediately after starting an integer division, the
+   shift may give an incorrect result.  Avoid this by adding a nop on
+   the R4000.  See quotations of errata #16 and #28 from "MIPS
+   R4000PC/SC Errata, Processor Revision 2.2 and 3.0" in mips.md for
+   details.
+
+   Otherwise just return DIVISION itself.  */
 
 const char *
 mips_output_division (const char *division, rtx *operands)
 {
+  const char *s = division;
+
   if (TARGET_CHECK_ZERO_DIV)
     {
-      output_asm_insn (division, operands);
+      output_asm_insn (s, operands);
 
       if (TARGET_MIPS16)
-	return "bnez\t%2,1f\n\tbreak\t7\n1:";
+	s = "bnez\t%2,1f\n\tbreak\t7\n1:";
       else
-	return "bne\t%2,%.,1f%#\n\tbreak\t7\n1:";
+	s = "bne\t%2,%.,1f%#\n\tbreak\t7\n1:";
+    }
+  if (TARGET_FIX_R4000)
+    {
+      output_asm_insn (s, operands);
+      s = "nop";
     }
-  return division;
+  return s;
 }
 
 /* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
Index: config/mips/mips.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.md,v
retrieving revision 1.218
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.218 mips.md
--- config/mips/mips.md	19 Feb 2004 22:49:47 -0000	1.218
+++ config/mips/mips.md	28 Feb 2004 19:52:39 -0000
@@ -204,11 +204,8 @@ (define_attr "length" ""
 	       (ne (symbol_ref "TARGET_MIPS16") (const_int 0)))
 	  (const_int 8)
 
-	  (and (eq_attr "type" "idiv")
-	       (ne (symbol_ref "TARGET_CHECK_ZERO_DIV") (const_int 0)))
-	  (cond [(ne (symbol_ref "TARGET_MIPS16") (const_int 0))
-		 (const_int 12)]
-		(const_int 16))
+	  (eq_attr "type" "idiv")
+	  (symbol_ref "mips_idiv_insns () * 4")
 	  ] (const_int 4)))
 
 ;; Attribute describing the processor.  This attribute must match exactly
Index: doc/invoke.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/invoke.texi,v
retrieving revision 1.415
diff -u -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.415 invoke.texi
--- doc/invoke.texi	24 Feb 2004 20:09:16 -0000	1.415
+++ doc/invoke.texi	28 Feb 2004 19:52:43 -0000
@@ -478,8 +478,9 @@ in the following sections.
 -mcheck-zero-division  -mno-check-zero-division @gol
 -mmemcpy  -mno-memcpy  -mlong-calls  -mno-long-calls @gol
 -mmad  -mno-mad  -mfused-madd  -mno-fused-madd  -nocpp @gol
--mfix-sb1  -mno-fix-sb1  -mflush-func=@var{func} @gol
--mno-flush-func  -mbranch-likely  -mno-branch-likely}
+-mfix-r4000  -mno-fix-r4000  -mfix-sb1  -mno-fix-sb1 @gol
+-mflush-func=@var{func}  -mno-flush-func @gol
+-mbranch-likely  -mno-branch-likely}
 
 @emph{i386 and x86-64 Options}
 @gccoptlist{-mtune=@var{cpu-type}  -march=@var{cpu-type} @gol
@@ -8044,6 +8045,17 @@ circumstances.
 @opindex nocpp
 Tell the MIPS assembler to not run its preprocessor over user
 assembler files (with a @samp{.s} suffix) when assembling them.
+
+@item -mfix-r4000
+@itemx -mno-fix-r4000
+@opindex mfix-r4000
+@opindex mno-fix-r4000
+Work around certain R4000 CPU errata:
+@itemize @minus
+@item
+A double-word or a variable shift may give an incorrect result if executed
+immediately after starting an integer division.
+@end itemize
 
 @item -mfix-sb1
 @itemx -mno-fix-sb1



More information about the Gcc-patches mailing list