Patchwork [committed] Add FMA support for MIPS

login
register
mail settings
Submitter Richard Sandiford
Date Nov. 7, 2010, 8:36 a.m.
Message ID <87fwvdediv.fsf@firetop.home>
Download mbox | patch
Permalink /patch/70342/
State New
Headers show

Comments

Richard Sandiford - Nov. 7, 2010, 8:36 a.m.
I didn't manage to finish this last weekend, so ended up missing the
stage 1 deadline, but I'm hoping it's covered by either the traditional
target leeway or "fleshing out fine details of new features".

Perhaps the only noteworthy thing about the patch is that fnma is defined
in the x86 style, -(a * b) + c, rather than the MIPS & Power style,
-(a * b + c).  This means that (a) we actually use NMSUB for fnma
and NMADD for fnms and (b) we can only do that when ignoring signed
zeros.  There are separate unnamed NMADD and NMSUB patterns for
(NEG (FMA ...)) that can be used even when honouring signed zeros.

Tested on mipsisa64-elf.  The patch introduces the following failures:

FAIL: gcc.dg/torture/builtin-attr-1.c  -O0  (test for excess errors)
FAIL: gcc.dg/torture/builtin-attr-1.c  -O1  (internal compiler error)
FAIL: gcc.dg/torture/builtin-attr-1.c  -O1  (test for excess errors)
FAIL: gcc.dg/torture/builtin-attr-1.c  -O2  (internal compiler error)
FAIL: gcc.dg/torture/builtin-attr-1.c  -O2  (test for excess errors)
[...]
FAIL: gcc.dg/torture/builtin-math-2.c  -O0  scan-tree-dump-times original "fma " 3
FAIL: gcc.dg/torture/builtin-math-2.c  -O0  scan-tree-dump-times original "fmaf" 3
FAIL: gcc.dg/torture/builtin-math-2.c  -O0  scan-tree-dump-times original "fmal" 3
FAIL: gcc.dg/torture/builtin-math-2.c  -O1  scan-tree-dump-times original "fma " 3
FAIL: gcc.dg/torture/builtin-math-2.c  -O1  scan-tree-dump-times original "fmaf" 3
FAIL: gcc.dg/torture/builtin-math-2.c  -O1  scan-tree-dump-times original "fmal" 3
[...]

but those are target-independent problems that also show up on x86
with -mfma.  Will try to look at those today if I can find time.
Applied.

Richard


gcc/
	* config/mips/mips.c (mips_rtx_costs): Handle FMA.
	* config/mips/mips.md (*madd4<mode>, *madd3<mode>, *msub4<mode>)
	(*msub3<mode>, *nmadd4<mode>_fastmath, *nmadd3<mode>_fastmath)
	(*nmsub4<mode>_fastmath, *nmsub3<mode>_fastmath): Delete.
	(*nmadd4<mode>, *nmadd3<mode>. *nmsub4<mode>, *nmsub3<mode>): Redefine
	to use FMA.
	(fma<mode>4, *fma<mode>4_madd3, *fma<mode>4_madd4): New patterns.
	(fms<mode>4, *fms<mode>4_msub3, *fms<mode>4_msub4): Likewise.
	(fnms<mode>4, *fnms<mode>4_nmadd3, *fnms<mode>4_nmadd4): Likewise.
	(fnma<mode>4, *fnma<mode>4_nmsub3, *fnma<mode>4_nmsub4): Likewise.

gcc/testsuite/
	* gcc.target/mips/mips.exp: Add support for -ffp-contract.
	* gcc.target/mips/fma-1.c: New test.
	* gcc.target/mips/fma-2.c: Likewise.
	* gcc.target/mips/fma-3.c: Likewise.
	* gcc.target/mips/fma-4.c: Likewise.
	* gcc.target/mips/fma-5.c: Likewise.
	* gcc.target/mips/fma-6.c: Likewise.
	* gcc.target/mips/fma-7.c: Likewise.
	* gcc.target/mips/fma-8.c: Likewise.
	* gcc.target/mips/fma-9.c: Likewise.
	* gcc.target/mips/fma-10.c: Likewise.
	* gcc.target/mips/fma-11.c: Likewise.
	* gcc.target/mips/fma-12.c: Likewise.
	* gcc.target/mips/fma-13.c: Likewise.
	* gcc.target/mips/fma-14.c: Likewise.
	* gcc.target/mips/fma-15.c: Likewise.
	* gcc.target/mips/fma-16.c: Likewise.
	* gcc.target/mips/fma-17.c: Likewise.
	* gcc.target/mips/fma-18.c: Likewise.
	* gcc.target/mips/fma-19.c: Likewise.
	* gcc.target/mips/fma-20.c: Likewise.
Joseph S. Myers - Nov. 7, 2010, 12:05 p.m.
Are you sure these are fused operations?  The MIPS32 manual I have to hand 
right now (MD00086 Revision 2.62) says explicitly for MADD.fmt, for 
example: "The intermediate product is rounded according to the current 
rounding mode in FCSR. ...  The results and flags are as if separate 
floating-point multiply and add instructions were executed.".
Richard Sandiford - Nov. 7, 2010, 12:32 p.m.
"Joseph S. Myers" <joseph@codesourcery.com> writes:
> Are you sure these are fused operations?  The MIPS32 manual I have to hand 
> right now (MD00086 Revision 2.62) says explicitly for MADD.fmt, for 
> example: "The intermediate product is rounded according to the current 
> rounding mode in FCSR. ...  The results and flags are as if separate 
> floating-point multiply and add instructions were executed.".

Ouch.  Serves me right for not checking.  They were fused in the
original R8000 implementation, but now I look at the MIPS IV spec,
it too explicitly says that only revisions of the R8000 used the
fused model, and that all subsequent MIPS IV implementations used
the unfused one.

R8000 isn't important enough now to support a whole different model,
so... patch reverted and brown paper bag duly adopted.  Thanks a lot
for catching my carelessness before it found its way into a release.

Richard
Richard Guenther - Nov. 7, 2010, 2:27 p.m.
On Sun, Nov 7, 2010 at 1:32 PM, Richard Sandiford
<rdsandiford@googlemail.com> wrote:
> "Joseph S. Myers" <joseph@codesourcery.com> writes:
>> Are you sure these are fused operations?  The MIPS32 manual I have to hand
>> right now (MD00086 Revision 2.62) says explicitly for MADD.fmt, for
>> example: "The intermediate product is rounded according to the current
>> rounding mode in FCSR. ...  The results and flags are as if separate
>> floating-point multiply and add instructions were executed.".
>
> Ouch.  Serves me right for not checking.  They were fused in the
> original R8000 implementation, but now I look at the MIPS IV spec,
> it too explicitly says that only revisions of the R8000 used the
> fused model, and that all subsequent MIPS IV implementations used
> the unfused one.
>
> R8000 isn't important enough now to support a whole different model,
> so... patch reverted and brown paper bag duly adopted.  Thanks a lot
> for catching my carelessness before it found its way into a release.

Well, the patch is still ok if you restrict the patterns to
flag_unsafe_math_optimizations.

For the -(a*b+c) variants we could introduce different named expanders
as well btw.

Richard.

> Richard
>

Patch

Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	2010-11-06 12:21:58.000000000 +0000
+++ gcc/config/mips/mips.c	2010-11-06 15:50:10.000000000 +0000
@@ -3827,6 +3827,10 @@  mips_rtx_costs (rtx x, int code, int out
 	*total = mips_cost->int_mult_si;
       return false;
 
+    case FMA:
+      *total = mips_fp_mult_cost (mode);
+      return false;
+
     case DIV:
       /* Check for a reciprocal.  */
       if (float_mode_p
Index: gcc/config/mips/mips.md
===================================================================
--- gcc/config/mips/mips.md	2010-11-06 12:21:58.000000000 +0000
+++ gcc/config/mips/mips.md	2010-11-06 18:15:32.000000000 +0000
@@ -2077,158 +2077,176 @@  (define_insn "<u>maddsidi4"
 
 ;; Floating point multiply accumulate instructions.
 
-(define_insn "*madd4<mode>"
-  [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(plus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
-			      (match_operand:ANYF 2 "register_operand" "f"))
-		   (match_operand:ANYF 3 "register_operand" "f")))]
-  "ISA_HAS_FP_MADD4_MSUB4 && TARGET_FUSED_MADD"
-  "madd.<fmt>\t%0,%3,%1,%2"
-  [(set_attr "type" "fmadd")
-   (set_attr "mode" "<UNITMODE>")])
-
-(define_insn "*madd3<mode>"
-  [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(plus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
-			      (match_operand:ANYF 2 "register_operand" "f"))
-		   (match_operand:ANYF 3 "register_operand" "0")))]
-  "ISA_HAS_FP_MADD3_MSUB3 && TARGET_FUSED_MADD"
+(define_expand "fma<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand")
+		  (match_operand:ANYF 2 "register_operand")
+		  (match_operand:ANYF 3 "register_operand")))]
+  "ISA_HAS_FP_MADD3_MSUB3 || ISA_HAS_FP_MADD4_MSUB4")
+
+(define_insn "*fma<mode>4_madd3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (match_operand:ANYF 3 "register_operand" "0")))]
+  "ISA_HAS_FP_MADD3_MSUB3"
   "madd.<fmt>\t%0,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*msub4<mode>"
+(define_insn "*fma<mode>4_madd4"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(minus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
-			       (match_operand:ANYF 2 "register_operand" "f"))
-		    (match_operand:ANYF 3 "register_operand" "f")))]
-  "ISA_HAS_FP_MADD4_MSUB4 && TARGET_FUSED_MADD"
-  "msub.<fmt>\t%0,%3,%1,%2"
+	(fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (match_operand:ANYF 3 "register_operand" "f")))]
+  "ISA_HAS_FP_MADD4_MSUB4"
+  "madd.<fmt>\t%0,%3,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*msub3<mode>"
-  [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(minus:ANYF (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
-			       (match_operand:ANYF 2 "register_operand" "f"))
-		    (match_operand:ANYF 3 "register_operand" "0")))]
-  "ISA_HAS_FP_MADD3_MSUB3 && TARGET_FUSED_MADD"
+(define_expand "fms<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand")
+		  (match_operand:ANYF 2 "register_operand")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand"))))]
+  "ISA_HAS_FP_MADD3_MSUB3 || ISA_HAS_FP_MADD4_MSUB4")
+
+(define_insn "*fms<mode>4_msub3"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+	(fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand" "0"))))]
+  "ISA_HAS_FP_MADD3_MSUB3"
   "msub.<fmt>\t%0,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmadd4<mode>"
+(define_insn "*fms<mode>4_msub4"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(neg:ANYF (plus:ANYF
-		   (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
-			      (match_operand:ANYF 2 "register_operand" "f"))
-		   (match_operand:ANYF 3 "register_operand" "f"))))]
-  "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
-   && TARGET_FUSED_MADD
-   && HONOR_SIGNED_ZEROS (<MODE>mode)
-   && !HONOR_NANS (<MODE>mode)"
-  "nmadd.<fmt>\t%0,%3,%1,%2"
+	(fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand" "f"))))]
+  "ISA_HAS_FP_MADD4_MSUB4"
+  "msub.<fmt>\t%0,%3,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmadd3<mode>"
+;; If we're ignoring signed zeros, we can use NMADD (-(a * b + c)) to
+;; implement fnms (-a * b - c, which is unconditionally equivalent to
+;; -(a * b) - c).
+(define_expand "fnms<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand"))
+		  (match_operand:ANYF 2 "register_operand")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand"))))]
+  "(ISA_HAS_NMADD3_NMSUB3 (<MODE>mode) || ISA_HAS_NMADD4_NMSUB4 (<MODE>mode))
+   && !HONOR_SIGNED_ZEROS (<MODE>mode)
+   && !HONOR_NANS (<MODE>mode)")
+
+(define_insn "*fnms<mode>4_nmadd3"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(neg:ANYF (plus:ANYF
-		   (mult:ANYF (match_operand:ANYF 1 "register_operand" "f")
-			      (match_operand:ANYF 2 "register_operand" "f"))
-		   (match_operand:ANYF 3 "register_operand" "0"))))]
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand" "0"))))]
   "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
-   && TARGET_FUSED_MADD
-   && HONOR_SIGNED_ZEROS (<MODE>mode)
+   && !HONOR_SIGNED_ZEROS (<MODE>mode)
    && !HONOR_NANS (<MODE>mode)"
   "nmadd.<fmt>\t%0,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmadd4<mode>_fastmath"
+(define_insn "*fnms<mode>4_nmadd4"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(minus:ANYF
-	 (mult:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
-		    (match_operand:ANYF 2 "register_operand" "f"))
-	 (match_operand:ANYF 3 "register_operand" "f")))]
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+		  (match_operand:ANYF 2 "register_operand" "f")
+		  (neg:ANYF (match_operand:ANYF 3 "register_operand" "f"))))]
   "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
-   && TARGET_FUSED_MADD
    && !HONOR_SIGNED_ZEROS (<MODE>mode)
    && !HONOR_NANS (<MODE>mode)"
   "nmadd.<fmt>\t%0,%3,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmadd3<mode>_fastmath"
+(define_insn "*nmadd3"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(minus:ANYF
-	 (mult:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
-		    (match_operand:ANYF 2 "register_operand" "f"))
-	 (match_operand:ANYF 3 "register_operand" "0")))]
-  "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
-   && TARGET_FUSED_MADD
-   && !HONOR_SIGNED_ZEROS (<MODE>mode)
-   && !HONOR_NANS (<MODE>mode)"
+  	(neg:ANYF
+	 (fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		   (match_operand:ANYF 2 "register_operand" "f")
+		   (match_operand:ANYF 3 "register_operand" "0"))))]
+  "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode) && !HONOR_NANS (<MODE>mode)"
   "nmadd.<fmt>\t%0,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmsub4<mode>"
+(define_insn "*nmadd4"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(neg:ANYF (minus:ANYF
-		   (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
-			      (match_operand:ANYF 3 "register_operand" "f"))
-		   (match_operand:ANYF 1 "register_operand" "f"))))]
-  "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
-   && TARGET_FUSED_MADD
-   && HONOR_SIGNED_ZEROS (<MODE>mode)
-   && !HONOR_NANS (<MODE>mode)"
-  "nmsub.<fmt>\t%0,%1,%2,%3"
+  	(neg:ANYF
+	 (fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+		   (match_operand:ANYF 2 "register_operand" "f")
+		   (match_operand:ANYF 3 "register_operand" "f"))))]
+  "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode) && !HONOR_NANS (<MODE>mode)"
+  "nmadd.<fmt>\t%0,%3,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmsub3<mode>"
+;; If we're ignoring signed zeros, we can use NMSUB (-(a * b - c)) to
+;; implement fnma (-a * b + c, which is unconditionally equivalent to
+;; -(a * b) + c).
+(define_expand "fnma<mode>4"
+  [(set (match_operand:ANYF 0 "register_operand")
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand"))
+	 	  (match_operand:ANYF 2 "register_operand")
+		  (match_operand:ANYF 3 "register_operand")))]
+  "(ISA_HAS_NMADD3_NMSUB3 (<MODE>mode) || ISA_HAS_NMADD4_NMSUB4 (<MODE>mode))
+   && !HONOR_SIGNED_ZEROS (<MODE>mode)
+   && !HONOR_NANS (<MODE>mode)")
+
+(define_insn "*fnma<mode>4_nmsub3"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(neg:ANYF (minus:ANYF
-		   (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
-			      (match_operand:ANYF 3 "register_operand" "f"))
-		   (match_operand:ANYF 1 "register_operand" "0"))))]
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+	 	  (match_operand:ANYF 2 "register_operand" "f")
+		  (match_operand:ANYF 3 "register_operand" "0")))]
   "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
-   && TARGET_FUSED_MADD
-   && HONOR_SIGNED_ZEROS (<MODE>mode)
+   && !HONOR_SIGNED_ZEROS (<MODE>mode)
    && !HONOR_NANS (<MODE>mode)"
   "nmsub.<fmt>\t%0,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmsub4<mode>_fastmath"
+(define_insn "*fnma<mode>4_nmsub4"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(minus:ANYF
-	 (match_operand:ANYF 1 "register_operand" "f")
-	 (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
-		    (match_operand:ANYF 3 "register_operand" "f"))))]
+	(fma:ANYF (neg:ANYF (match_operand:ANYF 1 "register_operand" "f"))
+	 	  (match_operand:ANYF 2 "register_operand" "f")
+		  (match_operand:ANYF 3 "register_operand" "f")))]
   "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode)
-   && TARGET_FUSED_MADD
    && !HONOR_SIGNED_ZEROS (<MODE>mode)
    && !HONOR_NANS (<MODE>mode)"
-  "nmsub.<fmt>\t%0,%1,%2,%3"
+  "nmsub.<fmt>\t%0,%3,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
-(define_insn "*nmsub3<mode>_fastmath"
+(define_insn "*nmsub3"
   [(set (match_operand:ANYF 0 "register_operand" "=f")
-	(minus:ANYF
-	 (match_operand:ANYF 1 "register_operand" "f")
-	 (mult:ANYF (match_operand:ANYF 2 "register_operand" "f")
-		    (match_operand:ANYF 3 "register_operand" "0"))))]
-  "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode)
-   && TARGET_FUSED_MADD
-   && !HONOR_SIGNED_ZEROS (<MODE>mode)
-   && !HONOR_NANS (<MODE>mode)"
+  	(neg:ANYF
+	 (fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+	 	   (match_operand:ANYF 2 "register_operand" "f")
+		   (neg:ANYF (match_operand:ANYF 3 "register_operand" "0")))))]
+  "ISA_HAS_NMADD3_NMSUB3 (<MODE>mode) && !HONOR_NANS (<MODE>mode)"
   "nmsub.<fmt>\t%0,%1,%2"
   [(set_attr "type" "fmadd")
    (set_attr "mode" "<UNITMODE>")])
 
+(define_insn "*nmsub4"
+  [(set (match_operand:ANYF 0 "register_operand" "=f")
+  	(neg:ANYF
+	 (fma:ANYF (match_operand:ANYF 1 "register_operand" "f")
+	 	   (match_operand:ANYF 2 "register_operand" "f")
+		   (neg:ANYF (match_operand:ANYF 3 "register_operand" "f")))))]
+  "ISA_HAS_NMADD4_NMSUB4 (<MODE>mode) && !HONOR_NANS (<MODE>mode)"
+  "nmsub.<fmt>\t%0,%3,%1,%2"
+  [(set_attr "type" "fmadd")
+   (set_attr "mode" "<UNITMODE>")])
+
 ;;
 ;;  ....................
 ;;
Index: gcc/testsuite/gcc.target/mips/mips.exp
===================================================================
--- gcc/testsuite/gcc.target/mips/mips.exp	2010-11-06 12:21:58.000000000 +0000
+++ gcc/testsuite/gcc.target/mips/mips.exp	2010-11-07 08:09:17.000000000 +0000
@@ -292,6 +292,13 @@  foreach option {
     lappend mips_option_groups $option "-f(no-|)$option"
 }
 
+# Add -ffoo= options to mips_option_groups.
+foreach option {
+    fp-contract
+} {
+    lappend mips_option_groups $option "-f$option=.*"
+}
+
 # A list of option groups that have an impact on the ABI.
 set mips_abi_groups {
     abi
Index: gcc/testsuite/gcc.target/mips/fma-1.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-1.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,82 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -fno-fast-math -ffp-contract=off" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 3 } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* We should not use NMADD or NMSUB without -ffinite-math-only because
+   those instructions may perform arithmetic negation.  */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+not_nmadd_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+not_nmsub_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+not_nmadd_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, -d);
+}
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, d);
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+not_nmadd_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+not_nmsub_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+not_nmadd_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, -d);
+}
+
+NOMIPS16 double
+not_nmsub_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-2.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-2.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,16 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -fno-fast-math -ffp-contract=off -ffinite-math-only" } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+NOMIPS16 float
+nmadd_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 double
+nmadd_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-3.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-3.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,16 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -fno-fast-math -ffp-contract=off -ffinite-math-only" } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+
+NOMIPS16 float
+nmsub_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 double
+nmsub_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, -d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-4.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-4.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,17 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -fno-fast-math -ffp-contract=off -ffinite-math-only" } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* These patterns can only use NMADD if -fno-signed-zeros is in effect.  */
+
+NOMIPS16 float
+not_nmadd_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, -d);
+}
+
+NOMIPS16 double
+not_nmadd_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, -d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-5.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-5.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,17 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -fno-fast-math -ffp-contract=off -ffinite-math-only" } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* These patterns can only use NMSUB if -fno-signed-zeros is in effect.  */
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, d);
+}
+
+NOMIPS16 double
+not_nmsub_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-6.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-6.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,16 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -ffast-math" } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+NOMIPS16 float
+nmadd_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, -d);
+}
+
+NOMIPS16 double
+nmadd_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, -d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-7.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-7.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,16 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -ffast-math" } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+
+NOMIPS16 float
+nmsub_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, d);
+}
+
+NOMIPS16 double
+nmsub_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-8.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-8.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,81 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O -ffast-math" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.d\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.d\t" 2 } } */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+nmadd_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+nmsub_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+nmadd_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, -d);
+}
+
+NOMIPS16 float
+nmsub_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, d);
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+nmadd_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+nmsub_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+nmadd_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, -d);
+}
+
+NOMIPS16 double
+nmsub_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-9.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-9.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,70 @@ 
+/* { dg-options "-mpaired-single -O3 -fno-fast-math -ftree-vectorize -ffp-contract=off" } */
+/* { dg-final { scan-assembler "\tmadd\\.ps\t" } } */
+/* { dg-final { scan-assembler "\tmsub\\.s\t" } } */
+/* { dg-final { scan-assembler-not "\tmsub\\.ps\t" } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* We should not use NMADD or NMSUB without -ffinite-math-only because
+   those instructions may perform arithmetic negation.  We don't really
+   expect the nmadd_ps and nmsub_ps functions to use MADD.PS and MSUB.PS,
+   but there's no reason in principle why they shouldn't.
+
+   ??? At the moment, we don't vectorize msub_ps, but we probably should.  */
+
+#define N 512
+float a[N], b[N], c[N], d[N];
+
+NOMIPS16 void
+madd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (b[i], c[i], d[i]);
+}
+
+NOMIPS16 float
+msub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (b[i], c[i], -d[i]);
+}
+
+NOMIPS16 float
+not_nmadd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -__builtin_fmaf (b[i], c[i], d[i]);
+}
+
+NOMIPS16 float
+not_nmsub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -__builtin_fmaf (b[i], c[i], -d[i]);
+}
+
+NOMIPS16 float
+not_nmadd_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (-b[i], c[i], -d[i]);
+}
+
+NOMIPS16 float
+not_nmsub_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (-b[i], c[i], d[i]);
+}
Index: gcc/testsuite/gcc.target/mips/fma-10.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-10.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,62 @@ 
+/* { dg-options "-mpaired-single -O -ffast-math -ftree-vectorize" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.ps\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.ps\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.ps\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.ps\t" 2 } } */
+
+#define N 512
+float a[N], b[N], c[N], d[N];
+
+NOMIPS16 void
+madd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (b[i], c[i], d[i]);
+}
+
+NOMIPS16 float
+msub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (b[i], c[i], -d[i]);
+}
+
+NOMIPS16 float
+nmadd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -__builtin_fmaf (b[i], c[i], d[i]);
+}
+
+NOMIPS16 float
+nmsub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -__builtin_fmaf (b[i], c[i], -d[i]);
+}
+
+NOMIPS16 float
+nmadd_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (-b[i], c[i], -d[i]);
+}
+
+NOMIPS16 float
+nmsub_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = __builtin_fmaf (-b[i], c[i], d[i]);
+}
Index: gcc/testsuite/gcc.target/mips/fma-11.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-11.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,79 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O3 -fno-fast-math -ffp-contract=off" } */
+/* { dg-final { scan-assembler-not "\tmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tmsub\\." } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* No function should use fused operations, however high the -O level.  */
+
+NOMIPS16 float
+not_madd_s (float b, float c, float d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 float
+not_msub_s (float b, float c, float d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 float
+not_nmadd_s (float b, float c, float d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 float
+not_nmsub_s (float b, float c, float d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 float
+not_nmadd_s_2 (float b, float c, float d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return -b * c + d;
+}
+
+NOMIPS16 double
+not_madd_d (double b, double c, double d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 double
+not_msub_d (double b, double c, double d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 double
+not_nmadd_d (double b, double c, double d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 double
+not_nmsub_d (double b, double c, double d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 double
+not_nmadd_d_2 (double b, double c, double d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 double
+not_nmsub_d_2 (double b, double c, double d)
+{
+  return -b * c + d;
+}
Index: gcc/testsuite/gcc.target/mips/fma-12.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-12.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,82 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O2 -fno-fast-math -ffp-contract=fast" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 3 } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* We should not use NMADD or NMSUB without -ffinite-math-only because
+   those instructions may perform arithmetic negation.  */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 float
+not_nmadd_s (float b, float c, float d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 float
+not_nmsub_s (float b, float c, float d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 float
+not_nmadd_s_2 (float b, float c, float d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return -b * c + d;
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 double
+not_nmadd_d (double b, double c, double d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 double
+not_nmsub_d (double b, double c, double d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 double
+not_nmadd_d_2 (double b, double c, double d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 double
+not_nmsub_d_2 (double b, double c, double d)
+{
+  return -b * c + d;
+}
Index: gcc/testsuite/gcc.target/mips/fma-13.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-13.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,81 @@ 
+/* { dg-options "-mgp64 -mhard-float isa>=4 -O2 -ffast-math" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.d\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.d\t" 2 } } */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 float
+nmadd_s (float b, float c, float d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 float
+nmsub_s (float b, float c, float d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 float
+nmadd_s_2 (float b, float c, float d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 float
+nmsub_s_2 (float b, float c, float d)
+{
+  return -b * c + d;
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 double
+nmadd_d (double b, double c, double d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 double
+nmsub_d (double b, double c, double d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 double
+nmadd_d_2 (double b, double c, double d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 double
+nmsub_d_2 (double b, double c, double d)
+{
+  return -b * c + d;
+}
Index: gcc/testsuite/gcc.target/mips/fma-14.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-14.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,64 @@ 
+/* { dg-options "-mpaired-single -O3 -fno-fast-math -ftree-vectorize -ffp-contract=off" } */
+/* { dg-final { scan-assembler-not "\tmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tmsub\\." } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* No function should use fused operations, however high the -O level.  */
+
+#define N 512
+float a[N], b[N], c[N], d[N];
+
+NOMIPS16 void
+not_madd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = b[i] * c[i] + d[i];
+}
+
+NOMIPS16 float
+not_msub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = b[i] * c[i] - d[i];
+}
+
+NOMIPS16 float
+not_nmadd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -(b[i] * c[i] + d[i]);
+}
+
+NOMIPS16 float
+not_nmsub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -(b[i] * c[i] - d[i]);
+}
+
+NOMIPS16 float
+not_nmadd_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -b[i] * c[i] - d[i];
+}
+
+NOMIPS16 float
+not_nmsub_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -b[i] * c[i] + d[i];
+}
Index: gcc/testsuite/gcc.target/mips/fma-15.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-15.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,65 @@ 
+/* { dg-options "-mpaired-single -O2 -fno-fast-math -ftree-vectorize -ffp-contract=fast" } */
+/* { dg-final { scan-assembler "\tmadd\\.ps" } } */
+/* { dg-final { scan-assembler "\tmsub\\.ps" } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* We should not use NMADD or NMSUB without -ffinite-math-only because
+   those instructions may perform arithmetic negation.  */
+
+#define N 512
+float a[N], b[N], c[N], d[N];
+
+NOMIPS16 void
+madd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = b[i] * c[i] + d[i];
+}
+
+NOMIPS16 float
+msub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = b[i] * c[i] - d[i];
+}
+
+NOMIPS16 float
+not_nmadd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -(b[i] * c[i] + d[i]);
+}
+
+NOMIPS16 float
+not_nmsub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -(b[i] * c[i] - d[i]);
+}
+
+NOMIPS16 float
+not_nmadd_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -b[i] * c[i] - d[i];
+}
+
+NOMIPS16 float
+not_nmsub_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -b[i] * c[i] + d[i];
+}
Index: gcc/testsuite/gcc.target/mips/fma-16.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-16.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,62 @@ 
+/* { dg-options "-mpaired-single -O2 -ffast-math -ftree-vectorize" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.ps" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.ps" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.ps" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.ps" 2 } } */
+
+#define N 512
+float a[N], b[N], c[N], d[N];
+
+NOMIPS16 void
+madd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = b[i] * c[i] + d[i];
+}
+
+NOMIPS16 float
+msub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = b[i] * c[i] - d[i];
+}
+
+NOMIPS16 float
+nmadd_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -(b[i] * c[i] + d[i]);
+}
+
+NOMIPS16 float
+nmsub_ps (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -(b[i] * c[i] - d[i]);
+}
+
+NOMIPS16 float
+nmadd_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -b[i] * c[i] - d[i];
+}
+
+NOMIPS16 float
+nmsub_ps_2 (void)
+{
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = -b[i] * c[i] + d[i];
+}
Index: gcc/testsuite/gcc.target/mips/fma-17.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-17.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,82 @@ 
+/* { dg-options "-mgp64 -mhard-float isa=loongson -O3 -fno-fast-math -ffp-contract=off" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 3 } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* We should not use NMADD or NMSUB without -ffinite-math-only because
+   those instructions may perform arithmetic negation.  */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+not_nmadd_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+not_nmsub_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+not_nmadd_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, -d);
+}
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, d);
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+not_nmadd_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+not_nmsub_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+not_nmadd_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, -d);
+}
+
+NOMIPS16 double
+not_nmsub_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-18.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-18.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,81 @@ 
+/* { dg-options "-mgp64 -mhard-float isa=loongson -O -ffast-math" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.d\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.d\t" 2 } } */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return __builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+nmadd_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, d);
+}
+
+NOMIPS16 float
+nmsub_s (float b, float c, float d)
+{
+  return -__builtin_fmaf (b, c, -d);
+}
+
+NOMIPS16 float
+nmadd_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, -d);
+}
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return __builtin_fmaf (-b, c, d);
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return __builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+nmadd_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, d);
+}
+
+NOMIPS16 double
+nmsub_d (double b, double c, double d)
+{
+  return -__builtin_fma (b, c, -d);
+}
+
+NOMIPS16 double
+nmadd_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, -d);
+}
+
+NOMIPS16 double
+nmsub_d_2 (double b, double c, double d)
+{
+  return __builtin_fma (-b, c, d);
+}
Index: gcc/testsuite/gcc.target/mips/fma-19.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-19.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,79 @@ 
+/* { dg-options "-mgp64 -mhard-float isa=loongson -O3 -fno-fast-math -ffp-contract=off" } */
+/* { dg-final { scan-assembler-not "\tmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tmsub\\." } } */
+/* { dg-final { scan-assembler-not "\tnmadd\\." } } */
+/* { dg-final { scan-assembler-not "\tnmsub\\." } } */
+
+/* No function should use fused operations, however high the -O level.  */
+
+NOMIPS16 float
+not_madd_s (float b, float c, float d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 float
+not_msub_s (float b, float c, float d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 float
+not_nmadd_s (float b, float c, float d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 float
+not_nmsub_s (float b, float c, float d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 float
+not_nmadd_s_2 (float b, float c, float d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 float
+not_nmsub_s_2 (float b, float c, float d)
+{
+  return -b * c + d;
+}
+
+NOMIPS16 double
+not_madd_d (double b, double c, double d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 double
+not_msub_d (double b, double c, double d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 double
+not_nmadd_d (double b, double c, double d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 double
+not_nmsub_d (double b, double c, double d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 double
+not_nmadd_d_2 (double b, double c, double d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 double
+not_nmsub_d_2 (double b, double c, double d)
+{
+  return -b * c + d;
+}
Index: gcc/testsuite/gcc.target/mips/fma-20.c
===================================================================
--- /dev/null	2010-11-07 07:45:48.025899246 +0000
+++ gcc/testsuite/gcc.target/mips/fma-20.c	2010-11-07 08:27:13.000000000 +0000
@@ -0,0 +1,81 @@ 
+/* { dg-options "-mgp64 -mhard-float isa=loongson -O2 -ffast-math" } */
+/* { dg-final { scan-assembler-times "\tmadd\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.s\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.s\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tmadd\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tmsub\\.d\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tnmadd\\.d\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tnmsub\\.d\t" 2 } } */
+
+NOMIPS16 float
+madd_s (float b, float c, float d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 float
+msub_s (float b, float c, float d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 float
+nmadd_s (float b, float c, float d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 float
+nmsub_s (float b, float c, float d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 float
+nmadd_s_2 (float b, float c, float d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 float
+nmsub_s_2 (float b, float c, float d)
+{
+  return -b * c + d;
+}
+
+NOMIPS16 double
+madd_d (double b, double c, double d)
+{
+  return b * c + d;
+}
+
+NOMIPS16 double
+msub_d (double b, double c, double d)
+{
+  return b * c + -d;
+}
+
+NOMIPS16 double
+nmadd_d (double b, double c, double d)
+{
+  return -(b * c + d);
+}
+
+NOMIPS16 double
+nmsub_d (double b, double c, double d)
+{
+  return -(b * c + -d);
+}
+
+NOMIPS16 double
+nmadd_d_2 (double b, double c, double d)
+{
+  return -b * c - d;
+}
+
+NOMIPS16 double
+nmsub_d_2 (double b, double c, double d)
+{
+  return -b * c + d;
+}