Index: lib/Target/Mips/MipsMachineFunction.h =================================================================== --- lib/Target/Mips/MipsMachineFunction.h +++ lib/Target/Mips/MipsMachineFunction.h @@ -54,7 +54,8 @@ 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 @@ void setSaveS2() { SaveS2 = true; } bool hasSaveS2() const { return SaveS2; } + int getBuildPairF64_FI(const TargetRegisterClass *RC); + std::map StubsNeeded; @@ -136,6 +139,10 @@ // 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 ExternalCallEntries; ValueMap GlobalCallEntries; Index: lib/Target/Mips/MipsMachineFunction.cpp =================================================================== --- lib/Target/Mips/MipsMachineFunction.cpp +++ lib/Target/Mips/MipsMachineFunction.cpp @@ -137,4 +137,12 @@ 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: lib/Target/Mips/MipsSEFrameLowering.cpp =================================================================== --- lib/Target/Mips/MipsSEFrameLowering.cpp +++ lib/Target/Mips/MipsSEFrameLowering.cpp @@ -64,6 +64,8 @@ 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 @@ 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 @@ 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().isABI_FPXX() + && !TM.getSubtarget().hasMTHC1()) { + const MipsSEInstrInfo &TII = + *static_cast(TM.getInstrInfo()); + const MipsRegisterInfo &TRI = + *static_cast(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().isFP64bit()); + + const TargetRegisterClass *RC = &Mips::GPR32RegClass; + const TargetRegisterClass *RC2 = &Mips::AFGR64RegClass; + + int FI = MF.getInfo()->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: lib/Target/Mips/MipsSEInstrInfo.cpp =================================================================== --- lib/Target/Mips/MipsSEInstrInfo.cpp +++ lib/Target/Mips/MipsSEInstrInfo.cpp @@ -547,29 +547,26 @@ const MCInstrDesc& Mtc1Tdd = get(Mips::MTC1); DebugLoc dl = I->getDebugLoc(); const TargetRegisterInfo &TRI = getRegisterInfo(); - bool HasMTHC1 = TM.getSubtarget().hasMips32r2() || - TM.getSubtarget().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().hasMips32r2() && - "MTHC1 requires MIPS32r2"); - + if (TM.getSubtarget().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 @@ BuildMI(MBB, I, dl, get(FP64 ? Mips::MTHC1_D64 : Mips::MTHC1_D32), DstReg) .addReg(DstReg) .addReg(HiReg); - } else + } else if (TM.getSubtarget().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: lib/Target/Mips/MipsSubtarget.h =================================================================== --- lib/Target/Mips/MipsSubtarget.h +++ lib/Target/Mips/MipsSubtarget.h @@ -169,7 +169,7 @@ 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 @@ /// 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: lib/Target/Mips/MipsSubtarget.cpp =================================================================== --- lib/Target/Mips/MipsSubtarget.cpp +++ lib/Target/Mips/MipsSubtarget.cpp @@ -157,6 +157,9 @@ "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: test/CodeGen/Mips/abiflags-xx.ll =================================================================== --- test/CodeGen/Mips/abiflags-xx.ll +++ test/CodeGen/Mips/abiflags-xx.ll @@ -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 Index: test/CodeGen/Mips/fpxx.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/fpxx.ll @@ -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 +}