[llvm] [MIPS][float] Fixed SingleFloat codegen on N32/N64 targets (PR #140575)

via llvm-commits llvm-commits at lists.llvm.org
Mon May 19 09:51:31 PDT 2025


https://github.com/Tazdevil971 created https://github.com/llvm/llvm-project/pull/140575

This patch aims at making the combination of single-float and N32/N64 ABI properly work.

Right now when both options are enabled the compiler chooses an incorrect ABI and in some cases even generates wrong instructions.

The floating point behavior on MIPS is controlled through 3 flags: soft-float, single-float, fp64. This makes things complicated because fp64 indicates the presence of 64bit floating point registers, but cannot be easily disabled (the mips3 feature require it, but mips3 CPUs with only 32bit floating point exist). Also if fp64 is missing it doesn't actually disable 64bit floating point operations, because certain MIPS1/2 CPUs support 64bit floating point with 32bit registers, hence the single-float option.

I'm guessing that originally single-float was only intended for the latter case, and that's the reason why it doesn't properly work on 64bit targets.

So this patch does the following:
- Make single-float a "master disable", even if fp64 is enabled this should completely disable generation of 64bit floating point operations, making it available on targets which hard require fp64.
- Add proper calling conventions for N32/N64 single-float combinations.
- Fixup codegen to not generate certain 64bit floating point operations, apparently not assigning a register class to f64 values is not enough to prevent them from showing up.
- Add tests for the new calling conventions and codegen.

>From 9db2755ab6ceb0284fca45be53063d28ac48a27b Mon Sep 17 00:00:00 2001
From: Davide Mor <tazdevil971 at gmail.com>
Date: Sat, 9 Nov 2024 12:22:01 +0100
Subject: [PATCH] [MIPS][float] Fixed SingleFloat codegen on N32/N64 targets

---
 llvm/lib/Target/Mips/MipsCallingConv.td       |  26 +-
 llvm/lib/Target/Mips/MipsISelLowering.cpp     |  14 +-
 llvm/lib/Target/Mips/MipsRegisterInfo.cpp     |  38 ++-
 llvm/lib/Target/Mips/MipsSEISelLowering.cpp   |  15 ++
 .../arguments-hard-float-single-varargs.ll    | 148 ++++++++++++
 .../Mips/cconv/arguments-hard-float-single.ll | 224 ++++++++++++++++++
 .../Mips/cconv/arguments-hard-fp128-single.ll |  42 ++++
 .../Mips/cconv/callee-saved-float-single.ll   | 111 +++++++++
 .../Mips/cconv/return-hard-float-single.ll    |  43 ++++
 .../Mips/cconv/return-hard-fp128-single.ll    |  24 ++
 .../CodeGen/Mips/int-to-float-conversion.ll   |  52 ++--
 11 files changed, 698 insertions(+), 39 deletions(-)
 create mode 100644 llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single-varargs.ll
 create mode 100644 llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single.ll
 create mode 100644 llvm/test/CodeGen/Mips/cconv/arguments-hard-fp128-single.ll
 create mode 100644 llvm/test/CodeGen/Mips/cconv/callee-saved-float-single.ll
 create mode 100644 llvm/test/CodeGen/Mips/cconv/return-hard-float-single.ll
 create mode 100644 llvm/test/CodeGen/Mips/cconv/return-hard-fp128-single.ll

diff --git a/llvm/lib/Target/Mips/MipsCallingConv.td b/llvm/lib/Target/Mips/MipsCallingConv.td
index 3c60114f507b9..929eb7e391667 100644
--- a/llvm/lib/Target/Mips/MipsCallingConv.td
+++ b/llvm/lib/Target/Mips/MipsCallingConv.td
@@ -196,7 +196,8 @@ def RetCC_MipsN : CallingConv<[
   //
   // f128 should only occur for the N64 ABI where long double is 128-bit. On
   // N32, long double is equivalent to double.
-  CCIfType<[i64], CCIfOrigArgWasF128<CCDelegateTo<RetCC_F128>>>,
+  CCIfSubtargetNot<"isSingleFloat()",
+      CCIfType<[i64], CCIfOrigArgWasF128<CCDelegateTo<RetCC_F128>>>>,
 
   // Aggregate returns are positioned at the lowest address in the slot for
   // both little and big-endian targets. When passing in registers, this
@@ -333,9 +334,10 @@ def CC_Mips_FixedArg : CallingConv<[
   //
   // f128 should only occur for the N64 ABI where long double is 128-bit. On
   // N32, long double is equivalent to double.
-  CCIfType<[i64],
-      CCIfSubtargetNot<"useSoftFloat()",
-          CCIfOrigArgWasF128<CCBitConvertToType<f64>>>>,
+  CCIfType<[i64], 
+      CCIfSubtargetNot<"isSingleFloat()",
+          CCIfSubtargetNot<"useSoftFloat()", 
+              CCIfOrigArgWasF128<CCBitConvertToType<f64>>>>>,
 
   CCIfCC<"CallingConv::Fast", CCDelegateTo<CC_Mips_FastCC>>,
 
@@ -359,8 +361,8 @@ def CC_Mips : CallingConv<[
 // Callee-saved register lists.
 //===----------------------------------------------------------------------===//
 
-def CSR_SingleFloatOnly : CalleeSavedRegs<(add (sequence "F%u", 31, 20), RA, FP,
-                                               (sequence "S%u", 7, 0))>;
+def CSR_O32_SingleFloat : CalleeSavedRegs<(add(sequence "F%u", 31, 20), RA, FP,
+                              (sequence "S%u", 7, 0))>;
 
 def CSR_O32_FPXX : CalleeSavedRegs<(add (sequence "D%u", 15, 10), RA, FP,
                                         (sequence "S%u", 7, 0))> {
@@ -374,13 +376,19 @@ def CSR_O32_FP64 :
   CalleeSavedRegs<(add (decimate (sequence "D%u_64", 30, 20), 2), RA, FP,
                        (sequence "S%u", 7, 0))>;
 
-def CSR_N32 : CalleeSavedRegs<(add D20_64, D22_64, D24_64, D26_64, D28_64,
-                                   D30_64, RA_64, FP_64, GP_64,
-                                   (sequence "S%u_64", 7, 0))>;
+def CSR_N32 : CalleeSavedRegs<(add(decimate(sequence "D%u_64", 30, 20), 2),
+                  RA_64, FP_64, GP_64, (sequence "S%u_64", 7, 0))>;
+
+def CSR_N32_SingleFloat
+    : CalleeSavedRegs<(add(decimate(sequence "F%u", 30, 20), 2), RA_64, FP_64,
+          GP_64, (sequence "S%u_64", 7, 0))>;
 
 def CSR_N64 : CalleeSavedRegs<(add (sequence "D%u_64", 31, 24), RA_64, FP_64,
                                    GP_64, (sequence "S%u_64", 7, 0))>;
 
+def CSR_N64_SingleFloat : CalleeSavedRegs<(add(sequence "F%u", 31, 24), RA_64,
+                              FP_64, GP_64, (sequence "S%u_64", 7, 0))>;
+
 def CSR_Mips16RetHelper :
   CalleeSavedRegs<(add V0, V1, FP,
                    (sequence "A%u", 3, 0), (sequence "S%u", 7, 0),
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index e933e97ea3706..6cf35397f52ec 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -4295,10 +4295,16 @@ parseRegForInlineAsmConstraint(StringRef C, MVT VT) const {
     return std::make_pair(0U, nullptr);
 
   if (Prefix == "$f") { // Parse $f0-$f31.
-    // If the size of FP registers is 64-bit or Reg is an even number, select
-    // the 64-bit register class. Otherwise, select the 32-bit register class.
-    if (VT == MVT::Other)
-      VT = (Subtarget.isFP64bit() || !(Reg % 2)) ? MVT::f64 : MVT::f32;
+    // If the targets is single float only, always select 32-bit registers,
+    // otherwise if the size of FP registers is 64-bit or Reg is an even number,
+    // select the 64-bit register class. Otherwise, select the 32-bit register
+    // class.
+    if (VT == MVT::Other) {
+      if (Subtarget.isSingleFloat())
+        VT = MVT::f32;
+      else
+        VT = (Subtarget.isFP64bit() || !(Reg % 2)) ? MVT::f64 : MVT::f32;
+    }
 
     RC = getRegClassFor(VT);
 
diff --git a/llvm/lib/Target/Mips/MipsRegisterInfo.cpp b/llvm/lib/Target/Mips/MipsRegisterInfo.cpp
index ae4b2377ad218..66288419e363d 100644
--- a/llvm/lib/Target/Mips/MipsRegisterInfo.cpp
+++ b/llvm/lib/Target/Mips/MipsRegisterInfo.cpp
@@ -102,14 +102,25 @@ MipsRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
                                      : CSR_Interrupt_32_SaveList;
   }
 
-  if (Subtarget.isSingleFloat())
-    return CSR_SingleFloatOnly_SaveList;
+  // N64 ABI
+  if (Subtarget.isABI_N64()) {
+    if (Subtarget.isSingleFloat())
+      return CSR_N64_SingleFloat_SaveList;
 
-  if (Subtarget.isABI_N64())
     return CSR_N64_SaveList;
+  }
+
+  // N32 ABI
+  if (Subtarget.isABI_N32()) {
+    if (Subtarget.isSingleFloat())
+      return CSR_N32_SingleFloat_SaveList;
 
-  if (Subtarget.isABI_N32())
     return CSR_N32_SaveList;
+  }
+
+  // O32 ABI
+  if (Subtarget.isSingleFloat())
+    return CSR_O32_SingleFloat_SaveList;
 
   if (Subtarget.isFP64bit())
     return CSR_O32_FP64_SaveList;
@@ -124,14 +135,25 @@ const uint32_t *
 MipsRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
                                        CallingConv::ID) const {
   const MipsSubtarget &Subtarget = MF.getSubtarget<MipsSubtarget>();
-  if (Subtarget.isSingleFloat())
-    return CSR_SingleFloatOnly_RegMask;
+  // N64 ABI
+  if (Subtarget.isABI_N64()) {
+    if (Subtarget.isSingleFloat())
+      return CSR_N64_SingleFloat_RegMask;
 
-  if (Subtarget.isABI_N64())
     return CSR_N64_RegMask;
+  }
+
+  // N32 ABI
+  if (Subtarget.isABI_N32()) {
+    if (Subtarget.isSingleFloat())
+      return CSR_N32_SingleFloat_RegMask;
 
-  if (Subtarget.isABI_N32())
     return CSR_N32_RegMask;
+  }
+
+  // O32 ABI
+  if (Subtarget.isSingleFloat())
+    return CSR_O32_SingleFloat_RegMask;
 
   if (Subtarget.isFP64bit())
     return CSR_O32_FP64_RegMask;
diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
index 71a70d9c2dd46..c5cb52dd91191 100644
--- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
@@ -28,6 +28,7 @@
 #include "llvm/CodeGen/SelectionDAG.h"
 #include "llvm/CodeGen/SelectionDAGNodes.h"
 #include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include "llvm/CodeGen/ValueTypes.h"
 #include "llvm/CodeGenTypes/MachineValueType.h"
@@ -211,6 +212,20 @@ MipsSETargetLowering::MipsSETargetLowering(const MipsTargetMachine &TM,
     }
   }
 
+  // Targets with 64bits integer registers, but no 64bit floating point register
+  // do not support conversion between them
+  if (Subtarget.isGP64bit() && Subtarget.isSingleFloat() &&
+      !Subtarget.useSoftFloat()) {
+    setOperationAction(ISD::FP_TO_SINT, MVT::i64, Expand);
+    setOperationAction(ISD::FP_TO_UINT, MVT::i64, Expand);
+    setOperationAction(ISD::STRICT_FP_TO_SINT, MVT::i64, Expand);
+    setOperationAction(ISD::STRICT_FP_TO_UINT, MVT::i64, Expand);
+    setOperationAction(ISD::SINT_TO_FP, MVT::i64, Expand);
+    setOperationAction(ISD::UINT_TO_FP, MVT::i64, Expand);
+    setOperationAction(ISD::STRICT_SINT_TO_FP, MVT::i64, Expand);
+    setOperationAction(ISD::STRICT_UINT_TO_FP, MVT::i64, Expand);
+  }
+
   setOperationAction(ISD::SMUL_LOHI,          MVT::i32, Custom);
   setOperationAction(ISD::UMUL_LOHI,          MVT::i32, Custom);
   setOperationAction(ISD::MULHS,              MVT::i32, Custom);
diff --git a/llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single-varargs.ll b/llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single-varargs.ll
new file mode 100644
index 0000000000000..8cbc879310f61
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single-varargs.ll
@@ -0,0 +1,148 @@
+; RUN: llc -mtriple=mips -relocation-model=static -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,O32 %s
+; RUN: llc -mtriple=mipsel -relocation-model=static -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,O32 %s
+
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,N32,NEW,NEWBE %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,N32,NEW,NEWLE %s
+
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64,N64,NEW,NEWBE %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64,N64,NEW,NEWLE %s
+
+ at floats = global [11 x float] zeroinitializer
+ at doubles = global [11 x double] zeroinitializer
+
+define void @double_args(double %a, ...)
+                         nounwind {
+entry:
+        %0 = getelementptr [11 x double], ptr @doubles, i32 0, i32 1
+        store volatile double %a, ptr %0
+
+        %ap = alloca ptr
+        call void @llvm.va_start(ptr %ap)
+        %b = va_arg ptr %ap, double
+        %1 = getelementptr [11 x double], ptr @doubles, i32 0, i32 2
+        store volatile double %b, ptr %1
+        call void @llvm.va_end(ptr %ap)
+        ret void
+}
+
+; ALL-LABEL: double_args:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles)
+
+; O32 forbids using floating point registers for the non-variable portion.
+; N32/N64 allow it.
+; O32-DAG:           sw $4, 8([[R2]])
+; O32-DAG:           sw $5, 12([[R2]])
+; NEW-DAG:           sd $4, 8([[R2]])
+
+; The varargs portion is dumped to stack
+; O32-DAG:           sw $6, 16($sp)
+; O32-DAG:           sw $7, 20($sp)
+; NEW-DAG:           sd $5, 8($sp)
+; NEW-DAG:           sd $6, 16($sp)
+; NEW-DAG:           sd $7, 24($sp)
+; NEW-DAG:           sd $8, 32($sp)
+; NEW-DAG:           sd $9, 40($sp)
+; NEW-DAG:           sd $10, 48($sp)
+; NEW-DAG:           sd $11, 56($sp)
+
+; Get the varargs pointer
+; O32 has 4 bytes padding, 4 bytes for the varargs pointer, and 8 bytes reserved
+; for arguments 1 and 2.
+; N32/N64 has 8 bytes for the varargs pointer, and no reserved area.
+; O32-DAG:           addiu [[VAPTR:\$[0-9]+]], $sp, 16
+; O32-DAG:           sw [[VAPTR]], 4($sp)
+; N32-DAG:           addiu [[VAPTR:\$[0-9]+]], $sp, 8
+; N32-DAG:           sw [[VAPTR]], 4($sp)
+; N64-DAG:           daddiu [[VAPTR:\$[0-9]+]], $sp, 8
+; N64-DAG:           sd [[VAPTR]], 0($sp)
+
+; Increment the pointer then get the varargs arg
+; LLVM will rebind the load to the stack pointer instead of the varargs pointer
+; during lowering. This is fine and doesn't change the behaviour.
+; O32-DAG:           addiu [[VAPTR]], [[VAPTR]], 8
+; N32-DAG:           addiu [[VAPTR]], [[VAPTR]], 8
+; N64-DAG:           daddiu [[VAPTR]], [[VAPTR]], 8
+; O32-DAG:           lw	[[R3:\$[0-9]+]], 16($sp)
+; O32-DAG:           lw	[[R4:\$[0-9]+]], 20($sp)
+; O32-DAG:           sw [[R3]], 16([[R2]])
+; O32-DAG:           sw [[R4]], 20([[R2]])
+; NEW-DAG:           ld [[R3:\$[0-9]+]], 8($sp)
+; NEW-DAG:           sd [[R3]], 16([[R2]])
+
+define void @float_args(float %a, ...) nounwind {
+entry:
+        %0 = getelementptr [11 x float], ptr @floats, i32 0, i32 1
+        store volatile float %a, ptr %0
+
+        %ap = alloca ptr
+        call void @llvm.va_start(ptr %ap)
+        %b = va_arg ptr %ap, float
+        %1 = getelementptr [11 x float], ptr @floats, i32 0, i32 2
+        store volatile float %b, ptr %1
+        call void @llvm.va_end(ptr %ap)
+        ret void
+}
+
+; ALL-LABEL: float_args:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats)
+
+; The first four arguments are the same in O32/N32/N64.
+; The non-variable portion should be unaffected.
+; O32-DAG:           mtc1 $4, $f0
+; O32-DAG:           swc1 $f0, 4([[R2]])
+; NEW-DAG:           swc1 $f12, 4([[R2]])
+
+; The varargs portion is dumped to stack
+; O32-DAG:           sw $5, 12($sp)
+; O32-DAG:           sw $6, 16($sp)
+; O32-DAG:           sw $7, 20($sp)
+; NEW-DAG:           sd $5, 8($sp)
+; NEW-DAG:           sd $6, 16($sp)
+; NEW-DAG:           sd $7, 24($sp)
+; NEW-DAG:           sd $8, 32($sp)
+; NEW-DAG:           sd $9, 40($sp)
+; NEW-DAG:           sd $10, 48($sp)
+; NEW-DAG:           sd $11, 56($sp)
+
+; Get the varargs pointer
+; O32 has 4 bytes padding, 4 bytes for the varargs pointer, and should have 8
+; bytes reserved for arguments 1 and 2 (the first float arg) but as discussed in
+; arguments-float.ll, GCC doesn't agree with MD00305 and treats floats as 4
+; bytes so we only have 12 bytes total.
+; N32/N64 has 8 bytes for the varargs pointer, and no reserved area.
+; O32-DAG:           addiu [[VAPTR:\$[0-9]+]], $sp, 12
+; O32-DAG:           sw [[VAPTR]], 4($sp)
+; N32-DAG:           addiu [[VAPTR:\$[0-9]+]], $sp, 8
+; N32-DAG:           sw [[VAPTR]], 4($sp)
+; N64-DAG:           daddiu [[VAPTR:\$[0-9]+]], $sp, 8
+; N64-DAG:           sd [[VAPTR]], 0($sp)
+
+; Increment the pointer then get the varargs arg
+; LLVM will rebind the load to the stack pointer instead of the varargs pointer
+; during lowering. This is fine and doesn't change the behaviour.
+; Also, in big-endian mode the offset must be increased by 4 to retrieve the
+; correct half of the argument slot.
+;
+; O32-DAG:           addiu [[VAPTR]], [[VAPTR]], 4
+; N32-DAG:           addiu [[VAPTR]], [[VAPTR]], 8
+; N64-DAG:           daddiu [[VAPTR]], [[VAPTR]], 8
+; O32-DAG:           lwc1 [[FTMP1:\$f[0-9]+]], 12($sp)
+; NEWLE-DAG:         lwc1 [[FTMP1:\$f[0-9]+]], 8($sp)
+; NEWBE-DAG:         lwc1 [[FTMP1:\$f[0-9]+]], 12($sp)
+; ALL-DAG:           swc1 [[FTMP1]], 8([[R2]])
+
+declare void @llvm.va_start(ptr)
+declare void @llvm.va_copy(ptr, ptr)
+declare void @llvm.va_end(ptr)
diff --git a/llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single.ll b/llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single.ll
new file mode 100644
index 0000000000000..6b7ad03c8e1c2
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/cconv/arguments-hard-float-single.ll
@@ -0,0 +1,224 @@
+; RUN: llc -mtriple=mips -relocation-model=static -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,O32 %s
+; RUN: llc -mtriple=mipsel -relocation-model=static -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,O32 %s
+
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,NEW %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32,NEW %s
+
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64,NEW %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64,NEW %s
+
+ at bytes = global [11 x i8] zeroinitializer
+ at dwords = global [11 x i64] zeroinitializer
+ at floats = global [11 x float] zeroinitializer
+ at doubles = global [11 x double] zeroinitializer
+
+define void @double_args(double %a, double %b, double %c, double %d, double %e,
+                         double %f, double %g, double %h, double %i) nounwind {
+entry:
+        %0 = getelementptr [11 x double], ptr @doubles, i32 0, i32 1
+        store volatile double %a, ptr %0
+        %1 = getelementptr [11 x double], ptr @doubles, i32 0, i32 2
+        store volatile double %b, ptr %1
+        %2 = getelementptr [11 x double], ptr @doubles, i32 0, i32 3
+        store volatile double %c, ptr %2
+        %3 = getelementptr [11 x double], ptr @doubles, i32 0, i32 4
+        store volatile double %d, ptr %3
+        %4 = getelementptr [11 x double], ptr @doubles, i32 0, i32 5
+        store volatile double %e, ptr %4
+        %5 = getelementptr [11 x double], ptr @doubles, i32 0, i32 6
+        store volatile double %f, ptr %5
+        %6 = getelementptr [11 x double], ptr @doubles, i32 0, i32 7
+        store volatile double %g, ptr %6
+        %7 = getelementptr [11 x double], ptr @doubles, i32 0, i32 8
+        store volatile double %h, ptr %7
+        %8 = getelementptr [11 x double], ptr @doubles, i32 0, i32 9
+        store volatile double %i, ptr %8
+        ret void
+}
+
+; ALL-LABEL: double_args:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles)
+
+; The first four arguments are the same in O32/N32/N64.
+; The first argument is floating point but single-float is enabled so floating
+; point registers are not used.
+; O32-DAG:           sw $4, 8([[R2]])
+; O32-DAG:           sw $5, 12([[R2]])
+; NEW-DAG:           sd $4, 8([[R2]])
+
+; O32-DAG:           sw $6, 16([[R2]])
+; O32-DAG:           sw $7, 20([[R2]])
+; NEW-DAG:           sd $5, 16([[R2]])
+
+; O32 has run out of argument registers and starts using the stack
+; O32-DAG:           lw [[R3:\$([0-9]+|gp)]], 16($sp)
+; O32-DAG:           lw [[R4:\$([0-9]+|gp)]], 20($sp)
+; O32-DAG:           sw [[R3]], 24([[R2]])
+; O32-DAG:           sw [[R4]], 28([[R2]])
+; NEW-DAG:           sd $6, 24([[R2]])
+
+; O32-DAG:           lw [[R3:\$([0-9]+|gp)]], 24($sp)
+; O32-DAG:           lw [[R4:\$([0-9]+|gp)]], 28($sp)
+; O32-DAG:           sw [[R3]], 32([[R2]])
+; O32-DAG:           sw [[R4]], 36([[R2]])
+; NEW-DAG:           sd $7, 32([[R2]])
+
+; O32-DAG:           lw [[R3:\$([0-9]+|gp)]], 32($sp)
+; O32-DAG:           lw [[R4:\$([0-9]+|gp)]], 36($sp)
+; O32-DAG:           sw [[R3]], 40([[R2]])
+; O32-DAG:           sw [[R4]], 44([[R2]])
+; NEW-DAG:           sd $8, 40([[R2]])
+
+; O32-DAG:           lw [[R3:\$([0-9]+|gp)]], 40($sp)
+; O32-DAG:           lw [[R4:\$([0-9]+|gp)]], 44($sp)
+; O32-DAG:           sw [[R3]], 48([[R2]])
+; O32-DAG:           sw [[R4]], 52([[R2]])
+; NEW-DAG:           sd $9, 48([[R2]])
+
+; O32-DAG:           lw [[R3:\$([0-9]+|gp)]], 48($sp)
+; O32-DAG:           lw [[R4:\$([0-9]+|gp)]], 52($sp)
+; O32-DAG:           sw [[R3]], 56([[R2]])
+; O32-DAG:           sw [[R4]], 60([[R2]])
+; NEW-DAG:           sd $10, 56([[R2]])
+
+; N32/N64 have run out of registers and starts using the stack too
+; O32-DAG:           lw [[R3:\$[0-9]+]], 56($sp)
+; O32-DAG:           lw [[R4:\$[0-9]+]], 60($sp)
+; O32-DAG:           sw [[R3]], 64([[R2]])
+; O32-DAG:           sw [[R4]], 68([[R2]])
+; NEW-DAG:           ld [[R3:\$[0-9]+]], 0($sp)
+; NEW-DAG:           sd $11, 64([[R2]])
+
+define void @float_args(float %a, float %b, float %c, float %d, float %e,
+                        float %f, float %g, float %h, float %i) nounwind {
+entry:
+        %0 = getelementptr [11 x float], ptr @floats, i32 0, i32 1
+        store volatile float %a, ptr %0
+        %1 = getelementptr [11 x float], ptr @floats, i32 0, i32 2
+        store volatile float %b, ptr %1
+        %2 = getelementptr [11 x float], ptr @floats, i32 0, i32 3
+        store volatile float %c, ptr %2
+        %3 = getelementptr [11 x float], ptr @floats, i32 0, i32 4
+        store volatile float %d, ptr %3
+        %4 = getelementptr [11 x float], ptr @floats, i32 0, i32 5
+        store volatile float %e, ptr %4
+        %5 = getelementptr [11 x float], ptr @floats, i32 0, i32 6
+        store volatile float %f, ptr %5
+        %6 = getelementptr [11 x float], ptr @floats, i32 0, i32 7
+        store volatile float %g, ptr %6
+        %7 = getelementptr [11 x float], ptr @floats, i32 0, i32 8
+        store volatile float %h, ptr %7
+        %8 = getelementptr [11 x float], ptr @floats, i32 0, i32 9
+        store volatile float %i, ptr %8
+        ret void
+}
+
+; ALL-LABEL: float_args:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(floats)
+; SYM64-DAG:         daddiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(floats)
+
+; The first argument is floating point so floating point registers are used.
+; The first argument is the same for O32/N32/N64 but the second argument differs
+; by register
+; ALL-DAG:           swc1 $f12, 4([[R1]])
+; O32-DAG:           swc1 $f14, 8([[R1]])
+; NEW-DAG:           swc1 $f13, 8([[R1]])
+
+; O32 has run out of argument registers and (in theory) starts using the stack
+; I've yet to find a reference in the documentation about this but GCC uses up
+; the remaining two argument slots in the GPR's first. We'll do the same for
+; compatibility.
+; O32-DAG:           mtc1 $6, $f0
+; O32-DAG:           swc1 $f0, 12([[R1]])
+; NEW-DAG:           swc1 $f14, 12([[R1]])
+; O32-DAG:           mtc1 $7, $f0
+; O32-DAG:           swc1 $f0, 16([[R1]])
+; NEW-DAG:           swc1 $f15, 16([[R1]])
+
+; O32 is definitely out of registers now and switches to the stack.
+; O32-DAG:           lwc1 [[F1:\$f[0-9]+]], 16($sp)
+; O32-DAG:           swc1 [[F1]], 20([[R1]])
+; NEW-DAG:           swc1 $f16, 20([[R1]])
+; O32-DAG:           lwc1 [[F1:\$f[0-9]+]], 20($sp)
+; O32-DAG:           swc1 [[F1]], 24([[R1]])
+; NEW-DAG:           swc1 $f17, 24([[R1]])
+; O32-DAG:           lwc1 [[F1:\$f[0-9]+]], 24($sp)
+; O32-DAG:           swc1 [[F1]], 28([[R1]])
+; NEW-DAG:           swc1 $f18, 28([[R1]])
+; O32-DAG:           lwc1 [[F1:\$f[0-9]+]], 28($sp)
+; O32-DAG:           swc1 [[F1]], 32([[R1]])
+; NEW-DAG:           swc1 $f19, 32([[R1]])
+
+; N32/N64 have run out of registers and start using the stack too
+; O32-DAG:           lwc1 [[F1:\$f[0-9]+]], 32($sp)
+; O32-DAG:           swc1 [[F1]], 36([[R1]])
+; NEW-DAG:           lwc1 [[F1:\$f[0-9]+]], 0($sp)
+; NEW-DAG:           swc1 [[F1]], 36([[R1]])
+
+
+define void @double_arg2(i8 %a, double %b) nounwind {
+entry:
+        %0 = getelementptr [11 x i8], ptr @bytes, i32 0, i32 1
+        store volatile i8 %a, ptr %0
+        %1 = getelementptr [11 x double], ptr @doubles, i32 0, i32 1
+        store volatile double %b, ptr %1
+        ret void
+}
+
+; ALL-LABEL: double_arg2:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes)
+; SYM64-DAG:         daddiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes)
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(doubles)
+
+; The first four arguments are the same in O32/N32/N64.
+; The first argument isn't floating point so floating point registers are not
+; used.
+; The second slot is insufficiently aligned for double on O32 so it is skipped.
+; Also, double occupies two slots on O32 and only one for N32/N64.
+; ALL-DAG:           sb $4, 1([[R1]])
+; O32-DAG:           sw $6, 8([[R2]])
+; O32-DAG:           sw $7, 12([[R2]])
+; NEW-DAG:           sd $5, 8([[R2]])
+
+define void @float_arg2(i8 %a, float %b) nounwind {
+entry:
+        %0 = getelementptr [11 x i8], ptr @bytes, i32 0, i32 1
+        store volatile i8 %a, ptr %0
+        %1 = getelementptr [11 x float], ptr @floats, i32 0, i32 1
+        store volatile float %b, ptr %1
+        ret void
+}
+
+; ALL-LABEL: float_arg2:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes)
+; SYM64-DAG:         daddiu [[R1:\$[0-9]+]], ${{[0-9]+}}, %lo(bytes)
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(floats)
+
+; The first argument is the same in O32/N32/N64.
+; ALL-DAG:           sb $4, 1([[R1]])
+
+; The first argument isn't floating point so floating point registers are not
+; used in O32, but N32/N64 will still use them.
+; MD00305 and GCC disagree on this one. MD00305 says that floats are treated
+; as 8-byte aligned and occupy two slots on O32. GCC is treating them as 4-byte
+; aligned and occupying one slot. We'll use GCC's definition.
+; O32-DAG:           mtc1 $5, $f0
+; O32-DAG:           swc1 $f0, 4([[R2]])
+; NEW-DAG:           swc1 $f13, 4([[R2]])
diff --git a/llvm/test/CodeGen/Mips/cconv/arguments-hard-fp128-single.ll b/llvm/test/CodeGen/Mips/cconv/arguments-hard-fp128-single.ll
new file mode 100644
index 0000000000000..9268e37b02fb5
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/cconv/arguments-hard-fp128-single.ll
@@ -0,0 +1,42 @@
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32 %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32 %s
+
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64 %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64 %s
+
+ at ldoubles = global [11 x fp128] zeroinitializer
+
+define void @ldouble_args(fp128 %a, fp128 %b, fp128 %c, fp128 %d, fp128 %e) nounwind {
+entry:
+        %0 = getelementptr [11 x fp128], ptr @ldoubles, i32 0, i32 1
+        store volatile fp128 %a, ptr %0
+        %1 = getelementptr [11 x fp128], ptr @ldoubles, i32 0, i32 2
+        store volatile fp128 %b, ptr %1
+        %2 = getelementptr [11 x fp128], ptr @ldoubles, i32 0, i32 3
+        store volatile fp128 %c, ptr %2
+        %3 = getelementptr [11 x fp128], ptr @ldoubles, i32 0, i32 4
+        store volatile fp128 %d, ptr %3
+        %4 = getelementptr [11 x fp128], ptr @ldoubles, i32 0, i32 5
+        store volatile fp128 %e, ptr %4
+        ret void
+}
+
+; ALL-LABEL: ldouble_args:
+; We won't test the way the global address is calculated in this test. This is
+; just to get the register number for the other checks.
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(ldoubles)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(ldoubles)
+
+; The first four arguments are the same in N32/N64.
+; ALL-DAG:           sd	$5, 24([[R2]])
+; ALL-DAG:           sd	$4, 16([[R2]])
+; ALL-DAG:           sd	$7, 40([[R2]])
+; ALL-DAG:           sd	$6, 32([[R2]])
+; ALL-DAG:           sd	$9, 56([[R2]])
+; ALL-DAG:           sd	$8, 48([[R2]])
+; ALL-DAG:           sd	$11, 72([[R2]])
+; ALL-DAG:           sd	$10, 64([[R2]])
diff --git a/llvm/test/CodeGen/Mips/cconv/callee-saved-float-single.ll b/llvm/test/CodeGen/Mips/cconv/callee-saved-float-single.ll
new file mode 100644
index 0000000000000..5bf1f2c2d60da
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/cconv/callee-saved-float-single.ll
@@ -0,0 +1,111 @@
+; RUN: llc -mtriple=mips -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,O32 %s
+; RUN: llc -mtriple=mipsel -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,O32 %s
+
+; RUN: llc -mtriple=mips64 -target-abi n32 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,N32 %s
+; RUN: llc -mtriple=mips64el -target-abi n32 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,N32 %s
+; RUN: llc -mtriple=mips64 -target-abi n32 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,ALL-INV,N32-INV %s
+; RUN: llc -mtriple=mips64el -target-abi n32 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,ALL-INV,N32-INV %s
+
+; RUN: llc -mtriple=mips64 -target-abi n64 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,N64 %s
+; RUN: llc -mtriple=mips64el -target-abi n64 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,N64 %s
+; RUN: llc -mtriple=mips64 -target-abi n64 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,ALL-INV,N64-INV %s
+; RUN: llc -mtriple=mips64el -target-abi n64 -mattr=+single-float < %s | FileCheck --check-prefixes=ALL,ALL-INV,N64-INV %s
+
+define void @fpu_clobber() nounwind {
+entry:
+        call void asm "# Clobber", "~{$f0},~{$f1},~{$f2},~{$f3},~{$f4},~{$f5},~{$f6},~{$f7},~{$f8},~{$f9},~{$f10},~{$f11},~{$f12},~{$f13},~{$f14},~{$f15},~{$f16},~{$f17},~{$f18},~{$f19},~{$f20},~{$f21},~{$f22},~{$f23},~{$f24},~{$f25},~{$f26},~{$f27},~{$f28},~{$f29},~{$f30},~{$f31}"()
+        ret void
+}
+
+; ALL-LABEL: fpu_clobber:
+; ALL-INV-NOT:   swc1 $f0,
+; ALL-INV-NOT:   swc1 $f1,
+; ALL-INV-NOT:   swc1 $f2,
+; ALL-INV-NOT:   swc1 $f3,
+; ALL-INV-NOT:   swc1 $f4,
+; ALL-INV-NOT:   swc1 $f5,
+; ALL-INV-NOT:   swc1 $f6,
+; ALL-INV-NOT:   swc1 $f7,
+; ALL-INV-NOT:   swc1 $f8,
+; ALL-INV-NOT:   swc1 $f9,
+; ALL-INV-NOT:   swc1 $f10,
+; ALL-INV-NOT:   swc1 $f11,
+; ALL-INV-NOT:   swc1 $f12,
+; ALL-INV-NOT:   swc1 $f13,
+; ALL-INV-NOT:   swc1 $f14,
+; ALL-INV-NOT:   swc1 $f15,
+; ALL-INV-NOT:   swc1 $f16,
+; ALL-INV-NOT:   swc1 $f17,
+; ALL-INV-NOT:   swc1 $f18,
+; ALL-INV-NOT:   swc1 $f19,
+
+; O32:           addiu $sp, $sp, -48
+; O32-DAG:       swc1 [[F20:\$f20]], [[OFF20:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F21:\$f21]], [[OFF21:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F22:\$f22]], [[OFF22:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F23:\$f23]], [[OFF23:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F24:\$f24]], [[OFF24:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F25:\$f25]], [[OFF25:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F26:\$f26]], [[OFF26:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F27:\$f27]], [[OFF27:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F28:\$f28]], [[OFF28:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F29:\$f29]], [[OFF29:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F30:\$f30]], [[OFF30:[0-9]+]]($sp)
+; O32-DAG:       swc1 [[F31:\$f31]], [[OFF31:[0-9]+]]($sp)
+; O32-DAG:       lwc1 [[F20]], [[OFF20]]($sp)
+; O32-DAG:       lwc1 [[F21]], [[OFF21]]($sp)
+; O32-DAG:       lwc1 [[F22]], [[OFF22]]($sp)
+; O32-DAG:       lwc1 [[F23]], [[OFF23]]($sp)
+; O32-DAG:       lwc1 [[F24]], [[OFF24]]($sp)
+; O32-DAG:       lwc1 [[F25]], [[OFF25]]($sp)
+; O32-DAG:       lwc1 [[F26]], [[OFF26]]($sp)
+; O32-DAG:       lwc1 [[F27]], [[OFF27]]($sp)
+; O32-DAG:       lwc1 [[F28]], [[OFF28]]($sp)
+; O32-DAG:       lwc1 [[F29]], [[OFF29]]($sp)
+; O32-DAG:       lwc1 [[F30]], [[OFF30]]($sp)
+; O32-DAG:       lwc1 [[F31]], [[OFF31]]($sp)
+; O32:           addiu $sp, $sp, 48
+
+; N32:           addiu $sp, $sp, -32
+; N32-DAG:       swc1 [[F20:\$f20]], [[OFF20:[0-9]+]]($sp)
+; N32-INV-NOT:   swc1 $f21,
+; N32-DAG:       swc1 [[F22:\$f22]], [[OFF22:[0-9]+]]($sp)
+; N32-INV-NOT:   swc1 $f23,
+; N32-DAG:       swc1 [[F24:\$f24]], [[OFF24:[0-9]+]]($sp)
+; N32-INV-NOT:   swc1 $f25,
+; N32-DAG:       swc1 [[F26:\$f26]], [[OFF26:[0-9]+]]($sp)
+; N32-INV-NOT:   swc1 $f27,
+; N32-DAG:       swc1 [[F28:\$f28]], [[OFF28:[0-9]+]]($sp)
+; N32-INV-NOT:   swc1 $f29,
+; N32-DAG:       swc1 [[F30:\$f30]], [[OFF30:[0-9]+]]($sp)
+; N32-INV-NOT:   swc1 $f31,
+; N32-DAG:       lwc1 [[F20]], [[OFF20]]($sp)
+; N32-DAG:       lwc1 [[F22]], [[OFF22]]($sp)
+; N32-DAG:       lwc1 [[F24]], [[OFF24]]($sp)
+; N32-DAG:       lwc1 [[F26]], [[OFF26]]($sp)
+; N32-DAG:       lwc1 [[F28]], [[OFF28]]($sp)
+; N32-DAG:       lwc1 [[F30]], [[OFF30]]($sp)
+; N32:           addiu $sp, $sp, 32
+
+; N64:           addiu $sp, $sp, -32
+; N64-INV-NOT:   swc1 $f20,
+; N64-INV-NOT:   swc1 $f21,
+; N64-INV-NOT:   swc1 $f22,
+; N64-INV-NOT:   swc1 $f23,
+; N64-DAG:       swc1 [[F24:\$f24]], [[OFF24:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F25:\$f25]], [[OFF25:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F26:\$f26]], [[OFF26:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F27:\$f27]], [[OFF27:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F28:\$f28]], [[OFF28:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F29:\$f29]], [[OFF29:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F30:\$f30]], [[OFF30:[0-9]+]]($sp)
+; N64-DAG:       swc1 [[F31:\$f31]], [[OFF31:[0-9]+]]($sp)
+; N64-DAG:       lwc1 [[F24]], [[OFF24]]($sp)
+; N64-DAG:       lwc1 [[F25]], [[OFF25]]($sp)
+; N64-DAG:       lwc1 [[F26]], [[OFF26]]($sp)
+; N64-DAG:       lwc1 [[F27]], [[OFF27]]($sp)
+; N64-DAG:       lwc1 [[F28]], [[OFF28]]($sp)
+; N64-DAG:       lwc1 [[F29]], [[OFF29]]($sp)
+; N64-DAG:       lwc1 [[F30]], [[OFF30]]($sp)
+; N64-DAG:       lwc1 [[F31]], [[OFF31]]($sp)
+; N64:           addiu $sp, $sp, 32
\ No newline at end of file
diff --git a/llvm/test/CodeGen/Mips/cconv/return-hard-float-single.ll b/llvm/test/CodeGen/Mips/cconv/return-hard-float-single.ll
new file mode 100644
index 0000000000000..1abf08d8200fb
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/cconv/return-hard-float-single.ll
@@ -0,0 +1,43 @@
+; RUN: llc -mtriple=mips-linux-gnu -relocation-model=static -mattr=+single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,O32 %s
+; RUN: llc -mtriple=mipsel-linux-gnu -relocation-model=static -mattr=+single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,O32 %s
+
+; RUN: llc -mtriple=mips64-linux-gnu -relocation-model=static -target-abi n32 -mattr=+single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,N32 %s
+; RUN: llc -mtriple=mips64el-linux-gnu -relocation-model=static -target-abi n32 -mattr=+single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,N32 %s
+
+; RUN: llc -mtriple=mips64-linux-gnu -relocation-model=static -target-abi n64 -mattr=+single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,N64 %s
+; RUN: llc -mtriple=mips64el-linux-gnu -relocation-model=static -target-abi n64 -mattr=+single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,N64 %s
+
+ at float = global float zeroinitializer
+ at double = global double zeroinitializer
+
+define float @retfloat() nounwind {
+entry:
+        %0 = load volatile float, ptr @float
+        ret float %0
+}
+
+; ALL-LABEL: retfloat:
+; O32-DAG:           lui [[R1:\$[0-9]+]], %hi(float)
+; O32-DAG:           lwc1 $f0, %lo(float)([[R1]])
+; N32-DAG:           lui [[R1:\$[0-9]+]], %hi(float)
+; N32-DAG:           lwc1 $f0, %lo(float)([[R1]])
+; N64-DAG:           lwc1 $f0, %lo(float)([[R1:\$[0-9+]]])
+
+define double @retdouble() nounwind {
+entry:
+        %0 = load volatile double, ptr @double
+        ret double %0
+}
+
+; ALL-LABEL: retdouble:
+; O32-DAG:           lw $2, %lo(double)([[R1:\$[0-9]+]])
+; O32-DAG:           addiu [[R2:\$[0-9]+]], [[R1]], %lo(double)
+; O32-DAG:           lw $3, 4([[R2]])
+; N32-DAG:           ld $2, %lo(double)([[R1:\$[0-9]+]])
+; N64-DAG:           ld $2, %lo(double)([[R1:\$[0-9]+]])
diff --git a/llvm/test/CodeGen/Mips/cconv/return-hard-fp128-single.ll b/llvm/test/CodeGen/Mips/cconv/return-hard-fp128-single.ll
new file mode 100644
index 0000000000000..e4d04146ecc2f
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/cconv/return-hard-fp128-single.ll
@@ -0,0 +1,24 @@
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32 %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n32 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM32 %s
+
+; RUN: llc -mtriple=mips64 -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64 %s
+; RUN: llc -mtriple=mips64el -relocation-model=static -target-abi n64 -mattr=single-float < %s \
+; RUN:   | FileCheck --check-prefixes=ALL,SYM64 %s
+
+ at fp128 = global fp128 zeroinitializer
+
+define fp128 @retldouble() nounwind {
+entry:
+        %0 = load volatile fp128, ptr @fp128
+        ret fp128 %0
+}
+
+; ALL-LABEL: retldouble:
+; SYM32-DAG:         addiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(fp128)
+; SYM64-DAG:         daddiu [[R2:\$[0-9]+]], ${{[0-9]+}}, %lo(fp128)
+
+; ALL-DAG:           ld $2, %lo(fp128)([[R2]])
+; ALL-DAG:           ld $3, 8([[R2]])
diff --git a/llvm/test/CodeGen/Mips/int-to-float-conversion.ll b/llvm/test/CodeGen/Mips/int-to-float-conversion.ll
index 84bc6a253595a..1c8ad9ad07e15 100644
--- a/llvm/test/CodeGen/Mips/int-to-float-conversion.ll
+++ b/llvm/test/CodeGen/Mips/int-to-float-conversion.ll
@@ -1,13 +1,24 @@
-; RUN: llc -mtriple=mipsel < %s | FileCheck %s -check-prefix=32
-; RUN: llc -mtriple=mips64el -mcpu=mips4 < %s | FileCheck %s -check-prefix=64
-; RUN: llc -mtriple=mips64el -mcpu=mips64 < %s | FileCheck %s -check-prefix=64
+; RUN: llc -mtriple=mipsel < %s | FileCheck %s -check-prefixes=ALL,32,32DF
+; RUN: llc -mtriple=mipsel -mattr=+single-float < %s | FileCheck %s -check-prefixes=ALL,32,32SF
+
+; RUN: llc -mtriple=mips64el -mcpu=mips4 < %s | FileCheck %s -check-prefixes=ALL,64,64DF
+; RUN: llc -mtriple=mips64el -mcpu=mips4 -mattr=+single-float < %s \
+; RUN:   | FileCheck %s -check-prefixes=ALL,64,64SF
+
+; RUN: llc -mtriple=mips64el -mcpu=mips64 < %s | FileCheck %s -check-prefixes=ALL,64,64DF
+; RUN: llc -mtriple=mips64el -mcpu=mips64 -mattr=+single-float < %s \
+; RUN:   | FileCheck %s -check-prefixes=ALL,64,64SF
+
+; Test various combinations of 32/64bit GP registers and single/double floating point support.
 
 @i1 = global [3 x i32] [i32 1, i32 2, i32 3], align 4
 @i3 = common global ptr null, align 4
 
-; 32-LABEL: test_float_int_:
-; 32: mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
-; 32: cvt.s.w $f{{[0-9]+}}, $f[[R0]]
+; ALL-LABEL: test_float_int_:
+; 32:   mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
+; 32:   cvt.s.w $f{{[0-9]+}}, $f[[R0]]
+; 64:   mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
+; 64:   cvt.s.w $f{{[0-9]+}}, $f[[R0]]
 
 define float @test_float_int_(i32 %a) {
 entry:
@@ -15,12 +26,13 @@ entry:
   ret float %conv
 }
 
-; 32-LABEL: test_double_int_:
-; 32: mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
-; 32: cvt.d.w $f{{[0-9]+}}, $f[[R0]]
-; 64-LABEL: test_double_int_:
-; 64: mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
-; 64: cvt.d.w $f{{[0-9]+}}, $f[[R0]]
+; ALL-LABEL: test_double_int_:
+; 32DF: mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
+; 32DF: cvt.d.w $f{{[0-9]+}}, $f[[R0]]
+; 32SF: jal	__floatsidf
+; 64DF: mtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
+; 64DF: cvt.d.w $f{{[0-9]+}}, $f[[R0]]
+; 64SF: jal	__floatsidf
 
 define double @test_double_int_(i32 %a) {
 entry:
@@ -28,9 +40,11 @@ entry:
   ret double %conv
 }
 
-; 64-LABEL: test_float_LL_:
-; 64: dmtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
-; 64: cvt.s.l $f{{[0-9]+}}, $f[[R0]]
+; ALL-LABEL: test_float_LL_:
+; 32:   jal __floatdisf
+; 64DF: dmtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
+; 64DF: cvt.s.l $f{{[0-9]+}}, $f[[R0]]
+; 64SF: jal __floatdisf
 
 define float @test_float_LL_(i64 %a) {
 entry:
@@ -38,9 +52,11 @@ entry:
   ret float %conv
 }
 
-; 64-LABEL: test_double_LL_:
-; 64: dmtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
-; 64: cvt.d.l $f{{[0-9]+}}, $f[[R0]]
+; ALL-LABEL: test_double_LL_:
+; 32:   jal __floatdidf
+; 64DF: dmtc1 ${{[0-9]+}}, $f[[R0:[0-9]+]]
+; 64DF: cvt.d.l $f{{[0-9]+}}, $f[[R0]]
+; 64SF: jal __floatdidf
 
 define double @test_double_LL_(i64 %a) {
 entry:



More information about the llvm-commits mailing list