[mips] Expand BuildPairF64 to a spill and reload when the O32 FPXX ABI is
authorSasa Stankovic <Sasa.Stankovic@imgtec.com>
Mon, 14 Jul 2014 09:40:29 +0000 (09:40 +0000)
committerSasa Stankovic <Sasa.Stankovic@imgtec.com>
Mon, 14 Jul 2014 09:40:29 +0000 (09:40 +0000)
enabled and mthc1 and dmtc1 are not available (e.g. on MIPS32r1)

This prevents the upper 32-bits of a double precision value from being moved to
the FPU with mtc1 to an odd-numbered FPU register. This is necessary to ensure
that the code generated executes correctly regardless of the current FPU mode.

MIPS32r2 and above continues to use mtc1/mthc1, while MIPS-IV and above continue
to use dmtc1.

Differential Revision: http://reviews.llvm.org/D4465

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@212930 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/Mips/MipsMachineFunction.cpp
lib/Target/Mips/MipsMachineFunction.h
lib/Target/Mips/MipsSEFrameLowering.cpp
lib/Target/Mips/MipsSEInstrInfo.cpp
lib/Target/Mips/MipsSubtarget.cpp
lib/Target/Mips/MipsSubtarget.h
test/CodeGen/Mips/abiflags-xx.ll
test/CodeGen/Mips/fpxx.ll [new file with mode: 0644]

index e30302e0afddc8095c869aa0e025eb6a034615d3..a3306686fc4389fc6018d0f9fdb58a3451653454 100644 (file)
@@ -137,4 +137,12 @@ MachinePointerInfo MipsFunctionInfo::callPtrInfo(const GlobalValue *Val) {
   return MachinePointerInfo(E);
 }
 
+int MipsFunctionInfo::getBuildPairF64_FI(const TargetRegisterClass *RC) {
+  if (BuildPairF64_FI == -1) {
+    BuildPairF64_FI = MF.getFrameInfo()->CreateStackObject(RC->getSize(),
+        RC->getAlignment(), false);
+  }
+  return BuildPairF64_FI;
+}
+
 void MipsFunctionInfo::anchor() { }
index 8c16f82bfb687b384eb0816e17752d1e3ff44ad1..a667d43724c17110c763bd369f29e339d621d8b8 100644 (file)
@@ -54,7 +54,8 @@ class MipsFunctionInfo : public MachineFunctionInfo {
 public:
   MipsFunctionInfo(MachineFunction &MF)
       : MF(MF), SRetReturnReg(0), GlobalBaseReg(0), Mips16SPAliasReg(0),
-        VarArgsFrameIndex(0), CallsEhReturn(false), SaveS2(false) {}
+        VarArgsFrameIndex(0), CallsEhReturn(false), SaveS2(false),
+        BuildPairF64_FI(-1) {}
 
   ~MipsFunctionInfo();
 
@@ -96,6 +97,8 @@ public:
   void setSaveS2() { SaveS2 = true; }
   bool hasSaveS2() const { return SaveS2; }
 
+  int getBuildPairF64_FI(const TargetRegisterClass *RC);
+
   std::map<const char *, const llvm::Mips16HardFloatInfo::FuncSignature *>
   StubsNeeded;
 
@@ -136,6 +139,10 @@ private:
   // saveS2
   bool SaveS2;
 
+  /// FrameIndex for expanding BuildPairF64 nodes to spill and reload when the
+  /// O32 FPXX ABI is enabled. -1 is used to denote invalid index.
+  int BuildPairF64_FI;
+
   /// MipsCallEntry maps.
   StringMap<const MipsCallEntry *> ExternalCallEntries;
   ValueMap<const GlobalValue *, const MipsCallEntry *> GlobalCallEntries;
index 42fc8d13ce0164b6b6dd7e7c215d8fc59eff3a00..f2276f19afa160df8a742bd1fcf24d8a2bfeefeb 100644 (file)
@@ -64,6 +64,8 @@ private:
   bool expandCopy(MachineBasicBlock &MBB, Iter I);
   bool expandCopyACC(MachineBasicBlock &MBB, Iter I, unsigned MFHiOpc,
                      unsigned MFLoOpc);
+  bool expandBuildPairF64(MachineBasicBlock &MBB,
+                          MachineBasicBlock::iterator I, bool FP64) const;
 
   MachineFunction &MF;
   MachineRegisterInfo &MRI;
@@ -108,6 +110,14 @@ bool ExpandPseudo::expandInstr(MachineBasicBlock &MBB, Iter I) {
   case Mips::STORE_ACC128:
     expandStoreACC(MBB, I, Mips::PseudoMFHI64, Mips::PseudoMFLO64, 8);
     break;
+  case Mips::BuildPairF64:
+    if (expandBuildPairF64(MBB, I, false))
+      MBB.erase(I);
+    return false;
+  case Mips::BuildPairF64_64:
+    if (expandBuildPairF64(MBB, I, true))
+      MBB.erase(I);
+    return false;
   case TargetOpcode::COPY:
     if (!expandCopy(MBB, I))
       return false;
@@ -258,6 +268,50 @@ bool ExpandPseudo::expandCopyACC(MachineBasicBlock &MBB, Iter I,
   return true;
 }
 
+/// This method expands the same instruction that MipsSEInstrInfo::
+/// expandBuildPairF64 does, for the case when ABI is fpxx and mthc1 is
+/// not available. It is implemented here because frame indexes are
+/// eliminated before MipsSEInstrInfo::expandBuildPairF64 is called.
+bool ExpandPseudo::expandBuildPairF64(MachineBasicBlock &MBB,
+                                      MachineBasicBlock::iterator I,
+                                      bool FP64) const {
+  // For fpxx and when mthc1 is not available, use:
+  //   spill + reload via ldc1
+  //
+  // The case where dmtc1 is available doesn't need to be handled here
+  // because it never creates a BuildPairF64 node.
+
+  const TargetMachine &TM = MF.getTarget();
+  if (TM.getSubtarget<MipsSubtarget>().isABI_FPXX()
+      && !TM.getSubtarget<MipsSubtarget>().hasMTHC1()) {
+    const MipsSEInstrInfo &TII =
+      *static_cast<const MipsSEInstrInfo*>(TM.getInstrInfo());
+    const MipsRegisterInfo &TRI =
+      *static_cast<const MipsRegisterInfo*>(TM.getRegisterInfo());
+
+    unsigned DstReg = I->getOperand(0).getReg();
+    unsigned LoReg = I->getOperand(1).getReg();
+    unsigned HiReg = I->getOperand(2).getReg();
+
+    // It should be impossible to have FGR64 on MIPS-II or MIPS32r1 (which are
+    // the cases where mthc1 is not available).
+    assert(!TM.getSubtarget<MipsSubtarget>().isFP64bit());
+
+    const TargetRegisterClass *RC = &Mips::GPR32RegClass;
+    const TargetRegisterClass *RC2 = &Mips::AFGR64RegClass;
+
+    int FI = MF.getInfo<MipsFunctionInfo>()->getBuildPairF64_FI(RC2);
+    TII.storeRegToStack(MBB, I, LoReg, I->getOperand(1).isKill(), FI, RC, &TRI,
+                        0);
+    TII.storeRegToStack(MBB, I, HiReg, I->getOperand(2).isKill(), FI, RC, &TRI,
+                        4);
+    TII.loadRegFromStack(MBB, I, DstReg, FI, RC2, &TRI, 0);
+    return true;
+  }
+
+  return false;
+}
+
 MipsSEFrameLowering::MipsSEFrameLowering(const MipsSubtarget &STI)
     : MipsFrameLowering(STI, STI.stackAlignment()) {}
 
index 32da7492dad41cbbcfef2432be4c010b1102338e..aad401857c3bac3316b5a9a00855ba3fd0aa2d9b 100644 (file)
@@ -547,29 +547,26 @@ void MipsSEInstrInfo::expandBuildPairF64(MachineBasicBlock &MBB,
   const MCInstrDesc& Mtc1Tdd = get(Mips::MTC1);
   DebugLoc dl = I->getDebugLoc();
   const TargetRegisterInfo &TRI = getRegisterInfo();
-  bool HasMTHC1 = TM.getSubtarget<MipsSubtarget>().hasMips32r2() ||
-                  TM.getSubtarget<MipsSubtarget>().hasMips32r6();
 
   // When mthc1 is available, use:
   //   mtc1 Lo, $fp
   //   mthc1 Hi, $fp
   //
-  // Otherwise, for FP64:
+  // Otherwise, for O32 FPXX ABI:
   //   spill + reload via ldc1
-  // This has not been implemented since FP64 on MIPS32 and earlier is not
-  // supported.
+  // This case is handled by the frame lowering code.
   //
   // Otherwise, for FP32:
   //   mtc1 Lo, $fp
   //   mtc1 Hi, $fp + 1
+  //
+  // The case where dmtc1 is available doesn't need to be handled here
+  // because it never creates a BuildPairF64 node.
 
   BuildMI(MBB, I, dl, Mtc1Tdd, TRI.getSubReg(DstReg, Mips::sub_lo))
     .addReg(LoReg);
 
-  if (HasMTHC1 || FP64) {
-    assert(TM.getSubtarget<MipsSubtarget>().hasMips32r2() &&
-           "MTHC1 requires MIPS32r2");
-
+  if (TM.getSubtarget<MipsSubtarget>().hasMTHC1()) {
     // FIXME: The .addReg(DstReg) is a white lie used to temporarily work
     //        around a widespread bug in the -mfp64 support.
     //        The problem is that none of the 32-bit fpu ops mention the fact
@@ -584,7 +581,9 @@ void MipsSEInstrInfo::expandBuildPairF64(MachineBasicBlock &MBB,
     BuildMI(MBB, I, dl, get(FP64 ? Mips::MTHC1_D64 : Mips::MTHC1_D32), DstReg)
         .addReg(DstReg)
         .addReg(HiReg);
-  } else
+  } else if (TM.getSubtarget<MipsSubtarget>().isABI_FPXX())
+    llvm_unreachable("BuildPairF64 not expanded in frame lowering code!");
+  else
     BuildMI(MBB, I, dl, Mtc1Tdd, TRI.getSubReg(DstReg, Mips::sub_hi))
       .addReg(HiReg);
 }
index 693daa3fde2efc162d61b5bcf414ef41f2bd3d29..0254d4da4d24243091c1af86bbfd9515f85b9231 100644 (file)
@@ -157,6 +157,9 @@ MipsSubtarget::MipsSubtarget(const std::string &TT, const std::string &CPU,
                        "the O32 ABI.",
                        false);
 
+  if (IsFPXX && (isABI_N32() || isABI_N64()))
+    report_fatal_error("FPXX is not permitted for the N32/N64 ABI's.", false);
+
   if (hasMips32r6()) {
     StringRef ISA = hasMips64r6() ? "MIPS64r6" : "MIPS32r6";
 
index a3dcf03c63a910e89f3dc59827b82969c927aa93..6f6e2a6714070ca8c8a856287f576b2df2fdcb2b 100644 (file)
@@ -169,7 +169,7 @@ public:
   bool isABI_N64() const { return MipsABI == N64; }
   bool isABI_N32() const { return MipsABI == N32; }
   bool isABI_O32() const { return MipsABI == O32; }
-  bool isABI_FPXX() const { return false; } // TODO: add check for FPXX
+  bool isABI_FPXX() const { return isABI_O32() && IsFPXX; }
   unsigned getTargetABI() const { return MipsABI; }
 
   /// This constructor initializes the data members to match that
@@ -253,6 +253,7 @@ public:
 
   /// Features related to the presence of specific instructions.
   bool hasExtractInsert() const { return !inMips16Mode() && hasMips32r2(); }
+  bool hasMTHC1() const { return hasMips32r2(); }
 
   const InstrItineraryData &getInstrItineraryData() const { return InstrItins; }
   bool allowMixed16_32() const { return inMips16ModeDefault() |
index b8aa0717abf1bf2b42a0917183ccdebb877a405e..c4610120fdd56d4cb698e3ae2ab259420dbaf698 100644 (file)
@@ -1,5 +1,4 @@
 ; RUN: llc -filetype=asm -mtriple mipsel-unknown-linux -mcpu=mips32 -mattr=fpxx %s -o - | FileCheck %s
-; XFAIL: *
 
 ; CHECK: .nan    legacy
 ; CHECK: .module fp=xx
diff --git a/test/CodeGen/Mips/fpxx.ll b/test/CodeGen/Mips/fpxx.ll
new file mode 100644 (file)
index 0000000..fb75e36
--- /dev/null
@@ -0,0 +1,142 @@
+; RUN: llc -march=mipsel -mcpu=mips32 < %s | FileCheck %s -check-prefix=ALL -check-prefix=32-NOFPXX
+; RUN: llc -march=mipsel -mcpu=mips32 -mattr=fpxx < %s | FileCheck %s -check-prefix=ALL -check-prefix=32-FPXX
+
+; RUN: llc -march=mipsel -mcpu=mips32r2 < %s | FileCheck %s -check-prefix=ALL -check-prefix=32R2-NOFPXX
+; RUN: llc -march=mipsel -mcpu=mips32r2 -mattr=fpxx < %s | FileCheck %s -check-prefix=ALL -check-prefix=32R2-FPXX
+
+; RUN: llc -march=mips64 -mcpu=mips4 < %s | FileCheck %s -check-prefix=ALL -check-prefix=4-NOFPXX
+; RUN: not llc -march=mips64 -mcpu=mips4 -mattr=fpxx < %s 2>&1 | FileCheck %s -check-prefix=4-FPXX
+
+; RUN: llc -march=mips64 -mcpu=mips64 < %s | FileCheck %s -check-prefix=ALL -check-prefix=64-NOFPXX
+; RUN: not llc -march=mips64 -mcpu=mips64 -mattr=fpxx < %s 2>&1 | FileCheck %s -check-prefix=64-FPXX
+
+; RUN-TODO: llc -march=mips64 -mcpu=mips4 -mattr=-n64,+o32 < %s | FileCheck %s -check-prefix=ALL -check-prefix=4-O32-NOFPXX
+; RUN-TOOD: llc -march=mips64 -mcpu=mips4 -mattr=-n64,+o32 -mattr=fpxx < %s | FileCheck %s -check-prefix=ALL -check-prefix=4-O32-FPXX
+
+; RUN-TODO: llc -march=mips64 -mcpu=mips64 -mattr=-n64,+o32 < %s | FileCheck %s -check-prefix=ALL -check-prefix=64-O32-NOFPXX
+; RUN-TOOD: llc -march=mips64 -mcpu=mips64 -mattr=-n64,+o32 -mattr=fpxx < %s | FileCheck %s -check-prefix=ALL -check-prefix=64-O32-FPXX
+
+
+; 4-FPXX:    LLVM ERROR: FPXX is not permitted for the N32/N64 ABI's.
+; 64-FPXX:    LLVM ERROR: FPXX is not permitted for the N32/N64 ABI's.
+
+define double @test1(double %d, ...) {
+  ret double %d
+
+; ALL-LABEL: test1:
+
+; 32-NOFPXX:    mtc1    $4, $f0
+; 32-NOFPXX:    mtc1    $5, $f1
+
+; 32-FPXX:       addiu   $sp, $sp, -8
+; 32-FPXX:       sw      $4, 0($sp)
+; 32-FPXX:       sw      $5, 4($sp)
+; 32-FPXX:       ldc1    $f0, 0($sp)
+
+; 32R2-NOFPXX:    mtc1    $4, $f0
+; 32R2-NOFPXX:    mthc1   $5, $f0
+
+; 32R2-FPXX:    mtc1    $4, $f0
+; 32R2-FPXX:    mthc1   $5, $f0
+
+; floats/doubles are not passed in integer registers for n64, so dmtc1 is not used.
+; 4-NOFPXX:    mov.d   $f0, $f12
+
+; 64-NOFPXX:    mov.d   $f0, $f12
+}
+
+define double @test2(i32 %i, double %d) {
+  ret double %d
+
+; ALL-LABEL: test2:
+
+; 32-NOFPXX:    mtc1    $6, $f0
+; 32-NOFPXX:    mtc1    $7, $f1
+
+; 32-FPXX:       addiu   $sp, $sp, -8
+; 32-FPXX:       sw      $6, 0($sp)
+; 32-FPXX:       sw      $7, 4($sp)
+; 32-FPXX:       ldc1    $f0, 0($sp)
+
+; 32R2-NOFPXX:    mtc1    $6, $f0
+; 32R2-NOFPXX:    mthc1   $7, $f0
+
+; 32R2-FPXX:    mtc1    $6, $f0
+; 32R2-FPXX:    mthc1   $7, $f0
+
+; 4-NOFPXX:    mov.d   $f0, $f13
+
+; 64-NOFPXX:    mov.d   $f0, $f13
+}
+
+define double @test3(float %f1, float %f2, double %d) {
+  ret double %d
+
+; ALL-LABEL: test3:
+
+; 32-NOFPXX:    mtc1    $6, $f0
+; 32-NOFPXX:    mtc1    $7, $f1
+
+; 32-FPXX:       addiu   $sp, $sp, -8
+; 32-FPXX:       sw      $6, 0($sp)
+; 32-FPXX:       sw      $7, 4($sp)
+; 32-FPXX:       ldc1    $f0, 0($sp)
+
+; 32R2-NOFPXX:    mtc1    $6, $f0
+; 32R2-NOFPXX:    mthc1   $7, $f0
+
+; 32R2-FPXX:    mtc1    $6, $f0
+; 32R2-FPXX:    mthc1   $7, $f0
+
+; 4-NOFPXX:    mov.d   $f0, $f14
+
+; 64-NOFPXX:    mov.d   $f0, $f14
+}
+
+define double @test4(float %f, double %d, ...) {
+  ret double %d
+
+; ALL-LABEL: test4:
+
+; 32-NOFPXX:    mtc1    $6, $f0
+; 32-NOFPXX:    mtc1    $7, $f1
+
+; 32-FPXX:       addiu   $sp, $sp, -8
+; 32-FPXX:       sw      $6, 0($sp)
+; 32-FPXX:       sw      $7, 4($sp)
+; 32-FPXX:       ldc1    $f0, 0($sp)
+
+; 32R2-NOFPXX:    mtc1    $6, $f0
+; 32R2-NOFPXX:    mthc1   $7, $f0
+
+; 32R2-FPXX:    mtc1    $6, $f0
+; 32R2-FPXX:    mthc1   $7, $f0
+
+; 4-NOFPXX:    mov.d   $f0, $f13
+
+; 64-NOFPXX:    mov.d   $f0, $f13
+}
+
+define double @test5() {
+  ret double 0.000000e+00
+
+; ALL-LABEL: test5:
+
+; 32-NOFPXX:    mtc1    $zero, $f0
+; 32-NOFPXX:    mtc1    $zero, $f1
+
+; 32-FPXX:    addiu   $sp, $sp, -8
+; 32-FPXX:    sw      $zero, 0($sp)
+; 32-FPXX:    sw      $zero, 4($sp)
+; 32-FPXX:    ldc1    $f0, 0($sp)
+
+; 32R2-NOFPXX:    mtc1    $zero, $f0
+; 32R2-NOFPXX:    mthc1   $zero, $f0
+
+; 32R2-FPXX:    mtc1    $zero, $f0
+; 32R2-FPXX:    mthc1   $zero, $f0
+
+; 4-NOFPXX:    dmtc1 $zero, $f0
+
+; 64-NOFPXX:    dmtc1 $zero, $f0
+}