[llvm] [Mips] Add r5900 (PlayStation 2 Emotion Engine) FPU Support (PR #178942)
Rick Gaiser via llvm-commits
llvm-commits at lists.llvm.org
Sun Feb 1 03:59:43 PST 2026
https://github.com/rickgaiser updated https://github.com/llvm/llvm-project/pull/178942
>From ce13ddea7bc75fe2058c6fb8e49c600a5e5b1742 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Wed, 28 Jan 2026 20:48:27 +0100
Subject: [PATCH 1/4] [MIPS] Add infrastructure for single-precision-only FPU
support
Add support for single-precision-only FPU configurations by:
- Adding isSingleFloat() method to MipsAsmParser
- Adding SINGLE FpABIKind to MipsABIFlagsSection
- Properly setting CPR1Size and FpABI for single-float mode
- Excluding double-precision PseudoCVT instructions when using single-float
This infrastructure allows MIPS targets with single-precision-only
FPUs to properly declare their capabilities and avoid generating
double-precision instructions.
---
.../Target/Mips/AsmParser/MipsAsmParser.cpp | 5 ++++
.../Mips/MCTargetDesc/MipsABIFlagsSection.cpp | 2 ++
.../Mips/MCTargetDesc/MipsABIFlagsSection.h | 6 ++++-
llvm/lib/Target/Mips/MipsInstrFPU.td | 26 ++++++++++++-------
4 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
index f91c378ad0afa..67b6622d0ab87 100644
--- a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
+++ b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
@@ -710,6 +710,11 @@ class MipsAsmParser : public MCTargetAsmParser {
bool useSoftFloat() const {
return getSTI().hasFeature(Mips::FeatureSoftFloat);
}
+
+ bool isSingleFloat() const {
+ return getSTI().hasFeature(Mips::FeatureSingleFloat);
+ }
+
bool hasMT() const {
return getSTI().hasFeature(Mips::FeatureMT);
}
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.cpp b/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.cpp
index c5a1a3e6286ed..9da7c8fbba7ac 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.cpp
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.cpp
@@ -29,6 +29,8 @@ uint8_t MipsABIFlagsSection::getFpABIValue() {
return OddSPReg ? Mips::Val_GNU_MIPS_ABI_FP_64
: Mips::Val_GNU_MIPS_ABI_FP_64A;
return Mips::Val_GNU_MIPS_ABI_FP_DOUBLE;
+ case FpABIKind::SINGLE:
+ return Mips::Val_GNU_MIPS_ABI_FP_SINGLE;
}
llvm_unreachable("unexpected fp abi value");
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h b/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h
index f7b2fa5537d31..53fda3e9efbcb 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h
@@ -20,7 +20,7 @@ class StringRef;
struct MipsABIFlagsSection {
// Internal representation of the fp_abi related values used in .module.
- enum class FpABIKind { ANY, XX, S32, S64, SOFT };
+ enum class FpABIKind { ANY, XX, S32, S64, SOFT, SINGLE };
// Version of flags structure.
uint16_t Version = 0;
@@ -133,6 +133,8 @@ struct MipsABIFlagsSection {
CPR1Size = Mips::AFL_REG_NONE;
else if (P.hasMSA())
CPR1Size = Mips::AFL_REG_128;
+ else if (P.isSingleFloat())
+ CPR1Size = Mips::AFL_REG_32;
else
CPR1Size = P.isFP64bit() ? Mips::AFL_REG_64 : Mips::AFL_REG_32;
}
@@ -179,6 +181,8 @@ struct MipsABIFlagsSection {
FpABI = FpABIKind::ANY;
if (P.useSoftFloat())
FpABI = FpABIKind::SOFT;
+ else if (P.isSingleFloat())
+ FpABI = FpABIKind::SINGLE;
else if (P.isABI_N32() || P.isABI_N64())
FpABI = FpABIKind::S64;
else if (P.isABI_O32()) {
diff --git a/llvm/lib/Target/Mips/MipsInstrFPU.td b/llvm/lib/Target/Mips/MipsInstrFPU.td
index 50d7a74ada60e..703c1edda6c78 100644
--- a/llvm/lib/Target/Mips/MipsInstrFPU.td
+++ b/llvm/lib/Target/Mips/MipsInstrFPU.td
@@ -89,8 +89,8 @@ def HasMips3D : Predicate<"Subtarget->has3D()">,
// They are mutually exclusive.
//===----------------------------------------------------------------------===//
-class FGR_32 { list<Predicate> FGRPredicates = [NotFP64bit]; }
-class FGR_64 { list<Predicate> FGRPredicates = [IsFP64bit]; }
+class FGR_32 { list<Predicate> FGRPredicates = [NotFP64bit, IsNotSingleFloat]; }
+class FGR_64 { list<Predicate> FGRPredicates = [IsFP64bit, IsNotSingleFloat]; }
class HARDFLOAT { list<Predicate> HardFloatPredicate = [IsNotSoftFloat]; }
//===----------------------------------------------------------------------===//
@@ -541,10 +541,12 @@ let DecoderNamespace = "MipsFP64", mayRaiseFPException = 1, Uses = [FCR31] in {
let isPseudo = 1, isCodeGenOnly = 1 in {
def PseudoCVT_S_W : ABSS_FT<"", FGR32Opnd, GPR32Opnd, II_CVT>;
- def PseudoCVT_D32_W : ABSS_FT<"", AFGR64Opnd, GPR32Opnd, II_CVT>;
- def PseudoCVT_S_L : ABSS_FT<"", FGR64Opnd, GPR64Opnd, II_CVT>;
- def PseudoCVT_D64_W : ABSS_FT<"", FGR64Opnd, GPR32Opnd, II_CVT>;
- def PseudoCVT_D64_L : ABSS_FT<"", FGR64Opnd, GPR64Opnd, II_CVT>;
+ let Predicates = [IsNotSingleFloat] in {
+ def PseudoCVT_D32_W : ABSS_FT<"", AFGR64Opnd, GPR32Opnd, II_CVT>;
+ def PseudoCVT_S_L : ABSS_FT<"", FGR64Opnd, GPR64Opnd, II_CVT>;
+ def PseudoCVT_D64_W : ABSS_FT<"", FGR64Opnd, GPR32Opnd, II_CVT>;
+ def PseudoCVT_D64_L : ABSS_FT<"", FGR64Opnd, GPR64Opnd, II_CVT>;
+ }
}
let AdditionalPredicates = [NotInMicroMips, UseAbs] in {
@@ -605,10 +607,14 @@ let AdditionalPredicates = [NotInMicroMips] in {
let DecoderNamespace = "MipsFP64";
}
- def DMTC1 : MTC1_FT<"dmtc1", FGR64Opnd, GPR64Opnd, II_DMTC1,
- bitconvert>, MFC1_FM<5>, ISA_MIPS3;
- def DMFC1 : MFC1_FT<"dmfc1", GPR64Opnd, FGR64Opnd, II_DMFC1,
- bitconvert>, MFC1_FM<1>, ISA_MIPS3;
+ let AdditionalPredicates = [IsNotSingleFloat] in {
+ def DMTC1 : MTC1_FT<"dmtc1", FGR64Opnd, GPR64Opnd, II_DMTC1, bitconvert>,
+ MFC1_FM<5>,
+ ISA_MIPS3;
+ def DMFC1 : MFC1_FT<"dmfc1", GPR64Opnd, FGR64Opnd, II_DMFC1, bitconvert>,
+ MFC1_FM<1>,
+ ISA_MIPS3;
+ }
let isMoveReg = 1 in {
def FMOV_S : MMRel, ABSS_FT<"mov.s", FGR32Opnd, FGR32Opnd, II_MOV_S>,
ABSS_FM<0x6, 16>, ISA_MIPS1;
>From 13032c4d55b26effe49d55ca1f41f698a07b3ec3 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Fri, 30 Jan 2026 17:20:47 +0100
Subject: [PATCH 2/4] [MIPS] Add R5900 FPU support
Enable FPU support for the R5900 (PS2 Emotion Engine) by:
- Switching R5900 from soft-float to single-float mode
- Implementing R5900-specific FPU lowering with limited compare conditions
(R5900 only supports C.F, C.EQ, C.LT, C.LE)
- Using CVT.W.S instead of TRUNC.W.S for FP-to-int conversion
- Renaming HasR5900 predicate to IsR5900 for consistency
- Adding test coverage for R5900 FPU instructions and comparisons
The R5900 has a single-precision-only FPU with limited functionality
compared to standard MIPS FPUs.
---
llvm/lib/Target/Mips/Mips.td | 2 +-
llvm/lib/Target/Mips/MipsISelLowering.cpp | 70 ++++++++++
llvm/lib/Target/Mips/MipsInstrFPU.td | 4 +
llvm/lib/Target/Mips/MipsInstrInfo.td | 4 +-
llvm/test/CodeGen/Mips/fcmp.ll | 46 +++++++
llvm/test/MC/Mips/r5900-fpu.s | 150 ++++++++++++++++++++++
6 files changed, 273 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/MC/Mips/r5900-fpu.s
diff --git a/llvm/lib/Target/Mips/Mips.td b/llvm/lib/Target/Mips/Mips.td
index 2f3ec5d051a79..8176c63d8660c 100644
--- a/llvm/lib/Target/Mips/Mips.td
+++ b/llvm/lib/Target/Mips/Mips.td
@@ -191,7 +191,7 @@ def FeatureFixR5900 : SubtargetFeature<"fix-r5900", "FixR5900", "true",
def FeatureR5900 : SubtargetFeature<"r5900", "IsR5900", "true",
"R5900 (PS2 Emotion Engine) Support",
- [FeatureMips3, FeatureSoftFloat,
+ [FeatureMips3, FeatureSingleFloat,
FeatureFixR5900]>;
def FeatureUseTCCInDIV : SubtargetFeature<
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index f7e2825edcc5d..b8fde7e03cae5 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -539,6 +539,65 @@ static Mips::CondCode condCodeToFCC(ISD::CondCode CC) {
}
}
+// R5900 FPU only supports 4 compare conditions: C.F, C.EQ, C.LT, C.LE
+// (opcodes 0x30, 0x32, 0x34, 0x36). Map unsupported conditions to equivalents.
+// The R5900 FPU doesn't produce NaN, so unordered comparisons can be mapped
+// to their ordered equivalents.
+// Returns the R5900-compatible condition code and whether operands should be
+// swapped.
+static std::pair<Mips::CondCode, bool> condCodeToFCCR5900(ISD::CondCode CC) {
+ // R5900 supports: FCOND_F (0), FCOND_OEQ (2), FCOND_OLT (4), FCOND_OLE (6)
+ // All other conditions must be transformed.
+ switch (CC) {
+ default:
+ llvm_unreachable("Unknown fp condition code!");
+ // Directly supported conditions
+ case ISD::SETEQ:
+ case ISD::SETOEQ:
+ return {Mips::FCOND_OEQ, false};
+ case ISD::SETLT:
+ case ISD::SETOLT:
+ return {Mips::FCOND_OLT, false};
+ case ISD::SETLE:
+ case ISD::SETOLE:
+ return {Mips::FCOND_OLE, false};
+
+ // Unordered variants - map to ordered (R5900 has no NaN)
+ case ISD::SETUEQ:
+ return {Mips::FCOND_OEQ, false};
+ case ISD::SETULT:
+ return {Mips::FCOND_OLT, false};
+ case ISD::SETULE:
+ return {Mips::FCOND_OLE, false};
+
+ // Greater-than: use less-than with swapped operands
+ case ISD::SETGT:
+ case ISD::SETOGT:
+ return {Mips::FCOND_OLT, true}; // a > b -> b < a
+ case ISD::SETGE:
+ case ISD::SETOGE:
+ return {Mips::FCOND_OLE, true}; // a >= b -> b <= a
+ case ISD::SETUGT:
+ return {Mips::FCOND_OLT, true}; // a > b unordered -> b < a (no NaN)
+ case ISD::SETUGE:
+ return {Mips::FCOND_OLE, true}; // a >= b unordered -> b <= a (no NaN)
+
+ // Not-equal: use EQ and invert result
+ case ISD::SETNE:
+ case ISD::SETONE:
+ return {Mips::FCOND_UNE, false};
+ case ISD::SETUNE:
+ return {Mips::FCOND_UNE, false};
+
+ // Unordered: always false on R5900 (no NaN)
+ case ISD::SETUO:
+ return {Mips::FCOND_F, false};
+ // Ordered: always true (use FCOND_T which is inverted F)
+ case ISD::SETO:
+ return {Mips::FCOND_OR, false};
+ }
+}
+
/// This function returns true if the floating point conditional branches and
/// conditional moves which use condition code CC should be inverted.
static bool invertFPCondCodeUser(Mips::CondCode CC) {
@@ -571,6 +630,17 @@ static SDValue createFPCmp(SelectionDAG &DAG, const SDValue &Op) {
// node if necessary.
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(2))->get();
+ // R5900 FPU only supports C.F, C.EQ, C.LT, C.LE (conditions 0, 2, 4, 6).
+ // Use R5900-specific condition code mapping which may swap operands.
+ const MipsSubtarget &Subtarget = DAG.getSubtarget<MipsSubtarget>();
+ if (Subtarget.isR5900()) {
+ auto [FCC, Swap] = condCodeToFCCR5900(CC);
+ if (Swap)
+ std::swap(LHS, RHS);
+ return DAG.getNode(MipsISD::FPCmp, DL, MVT::Glue, LHS, RHS,
+ DAG.getConstant(FCC, DL, MVT::i32));
+ }
+
return DAG.getNode(MipsISD::FPCmp, DL, MVT::Glue, LHS, RHS,
DAG.getConstant(condCodeToFCC(CC), DL, MVT::i32));
}
diff --git a/llvm/lib/Target/Mips/MipsInstrFPU.td b/llvm/lib/Target/Mips/MipsInstrFPU.td
index 703c1edda6c78..da0887cdcc232 100644
--- a/llvm/lib/Target/Mips/MipsInstrFPU.td
+++ b/llvm/lib/Target/Mips/MipsInstrFPU.td
@@ -965,6 +965,10 @@ def : MipsPat<(f32 fpimm0neg), (FNEG_S (MTC1 ZERO))>, ISA_MIPS1;
def : MipsPat<(f32 (any_sint_to_fp GPR32Opnd:$src)),
(PseudoCVT_S_W GPR32Opnd:$src)>;
+let Predicates = [IsR5900] in {
+ def : MipsPat<(MipsTruncIntFP FGR32Opnd:$src),
+ (CVT_W_S FGR32Opnd:$src)>, ISA_MIPS1;
+}
def : MipsPat<(MipsTruncIntFP FGR32Opnd:$src),
(TRUNC_W_S FGR32Opnd:$src)>, ISA_MIPS1;
diff --git a/llvm/lib/Target/Mips/MipsInstrInfo.td b/llvm/lib/Target/Mips/MipsInstrInfo.td
index 2a476e789032c..8127ec5c043f2 100644
--- a/llvm/lib/Target/Mips/MipsInstrInfo.td
+++ b/llvm/lib/Target/Mips/MipsInstrInfo.td
@@ -259,8 +259,8 @@ def UseIndirectJumpsHazard : Predicate<"Subtarget->useIndirectJumpsHazard()">,
AssemblerPredicate<(all_of FeatureUseIndirectJumpsHazard)>;
def NoIndirectJumpGuards : Predicate<"!Subtarget->useIndirectJumpsHazard()">,
AssemblerPredicate<(all_of (not FeatureUseIndirectJumpsHazard))>;
-def HasR5900 : Predicate<"Subtarget->isR5900()">,
- AssemblerPredicate<(all_of FeatureR5900)>;
+def IsR5900 : Predicate<"Subtarget->isR5900()">,
+ AssemblerPredicate<(all_of FeatureR5900)>;
def NotR5900 : Predicate<"!Subtarget->isR5900()">,
AssemblerPredicate<(all_of (not FeatureR5900))>;
def HasCRC : Predicate<"Subtarget->hasCRC()">,
diff --git a/llvm/test/CodeGen/Mips/fcmp.ll b/llvm/test/CodeGen/Mips/fcmp.ll
index 00c2a10fc226d..c3f062e5a30cf 100644
--- a/llvm/test/CodeGen/Mips/fcmp.ll
+++ b/llvm/test/CodeGen/Mips/fcmp.ll
@@ -16,6 +16,8 @@
; RUN: -check-prefixes=ALL,MM,MM32R3
; RUN: llc < %s -mtriple=mips -mcpu=mips32r6 -mattr=+micromips | FileCheck %s \
; RUN: -check-prefixes=ALL,MM,MMR6,MM32R6
+; RUN: llc < %s -mtriple=mips64el -mcpu=r5900 | \
+; RUN: FileCheck %s -check-prefixes=ALL,R5900
define i32 @false_f32(float %a, float %b) nounwind {
; ALL-LABEL: false_f32:
@@ -27,6 +29,8 @@ define i32 @false_f32(float %a, float %b) nounwind {
; 64-CMP: addiu $2, $zero, 0
+; R5900: addiu $2, $zero, 0
+
; MM-DAG: li16 $2, 0
%1 = fcmp false float %a, %b
@@ -45,6 +49,9 @@ define i32 @oeq_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.eq.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: c.eq.s $f12, $f13
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.eq.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -78,6 +85,9 @@ define i32 @ogt_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ule.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: c.olt.s $f13, $f12
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.lt.s $[[T0:f[0-9]+]], $f14, $f12
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -111,6 +121,9 @@ define i32 @oge_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ult.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: c.ole.s $f13, $f12
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.le.s $[[T0:f[0-9]+]], $f14, $f12
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -144,6 +157,9 @@ define i32 @olt_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.olt.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: c.olt.s $f12, $f13
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.lt.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -177,6 +193,9 @@ define i32 @ole_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ole.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: c.ole.s $f12, $f13
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.le.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -210,6 +229,9 @@ define i32 @one_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ueq.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: c.eq.s $f12, $f13
+; R5900: bc1t
+
; 32-CMP-DAG: cmp.ueq.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: not $[[T2:[0-9]+]], $[[T1]]
@@ -246,6 +268,8 @@ define i32 @ord_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.un.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: addiu $2, $zero, 1
+
; 32-CMP-DAG: cmp.un.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: not $[[T2:[0-9]+]], $[[T1]]
@@ -282,6 +306,9 @@ define i32 @ueq_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ueq.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: c.eq.s $f12, $f13
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.ueq.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -315,6 +342,9 @@ define i32 @ugt_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ole.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: c.olt.s $f13, $f12
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.ult.s $[[T0:f[0-9]+]], $f14, $f12
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -348,6 +378,9 @@ define i32 @uge_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.olt.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: c.ole.s $f13, $f12
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.ule.s $[[T0:f[0-9]+]], $f14, $f12
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -381,6 +414,9 @@ define i32 @ult_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ult.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: c.olt.s $f12, $f13
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.ult.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -414,6 +450,9 @@ define i32 @ule_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.ule.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: c.ole.s $f12, $f13
+; R5900: bc1f
+
; 32-CMP-DAG: cmp.ule.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -447,6 +486,9 @@ define i32 @une_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.eq.s $f12, $f13
; 64-C: movt $2, $zero, $fcc0
+; R5900: c.eq.s $f12, $f13
+; R5900: bc1t
+
; 32-CMP-DAG: cmp.eq.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: not $[[T2:[0-9]+]], $[[T1]]
@@ -483,6 +525,8 @@ define i32 @uno_f32(float %a, float %b) nounwind {
; 64-C-DAG: c.un.s $f12, $f13
; 64-C: movf $2, $zero, $fcc0
+; R5900: addiu $2, $zero, 0
+
; 32-CMP-DAG: cmp.un.s $[[T0:f[0-9]+]], $f12, $f14
; 32-CMP-DAG: mfc1 $[[T1:[0-9]+]], $[[T0]]
; 32-CMP-DAG: andi $2, $[[T1]], 1
@@ -515,6 +559,8 @@ define i32 @true_f32(float %a, float %b) nounwind {
; 64-CMP: addiu $2, $zero, 1
+; R5900: addiu $2, $zero, 1
+
; MM-DAG: li16 $2, 1
%1 = fcmp true float %a, %b
diff --git a/llvm/test/MC/Mips/r5900-fpu.s b/llvm/test/MC/Mips/r5900-fpu.s
new file mode 100644
index 0000000000000..eafdbad3a3845
--- /dev/null
+++ b/llvm/test/MC/Mips/r5900-fpu.s
@@ -0,0 +1,150 @@
+# RUN: llvm-mc %s -triple=mips64el-unknown-linux -mcpu=r5900 -show-encoding \
+# RUN: | FileCheck %s
+
+# Test R5900 FPU instructions (single-precision only, no double-precision support)
+# The R5900 has 32x32-bit FPU registers and a 32-bit accumulator (ACC)
+
+#------------------------------------------------------------------------------
+# FP Arithmetic Instructions
+#------------------------------------------------------------------------------
+
+# CHECK: abs.s $f6, $f7 # encoding: [0x85,0x39,0x00,0x46]
+abs.s $f6, $f7
+
+# CHECK: add.s $f9, $f6, $f7 # encoding: [0x40,0x32,0x07,0x46]
+add.s $f9, $f6, $f7
+
+# CHECK: sub.s $f9, $f6, $f7 # encoding: [0x41,0x32,0x07,0x46]
+sub.s $f9, $f6, $f7
+
+# CHECK: mul.s $f9, $f6, $f7 # encoding: [0x42,0x32,0x07,0x46]
+mul.s $f9, $f6, $f7
+
+# CHECK: div.s $f9, $f6, $f7 # encoding: [0x43,0x32,0x07,0x46]
+div.s $f9, $f6, $f7
+
+# CHECK: sqrt.s $f6, $f7 # encoding: [0x84,0x39,0x00,0x46]
+sqrt.s $f6, $f7
+
+# CHECK: neg.s $f6, $f7 # encoding: [0x87,0x39,0x00,0x46]
+neg.s $f6, $f7
+
+# CHECK: mov.s $f6, $f7 # encoding: [0x86,0x39,0x00,0x46]
+mov.s $f6, $f7
+
+#------------------------------------------------------------------------------
+# FP Conversion Instructions
+#------------------------------------------------------------------------------
+
+# CHECK: cvt.s.w $f6, $f7 # encoding: [0xa0,0x39,0x80,0x46]
+cvt.s.w $f6, $f7
+
+# CHECK: cvt.w.s $f6, $f7 # encoding: [0xa4,0x39,0x00,0x46]
+cvt.w.s $f6, $f7
+
+#------------------------------------------------------------------------------
+# FP Compare Instructions (R5900 supports only 4 comparisons: F, EQ, OLT, OLE)
+#------------------------------------------------------------------------------
+
+# CHECK: c.f.s $f6, $f7 # encoding: [0x30,0x30,0x07,0x46]
+c.f.s $f6, $f7
+
+# CHECK: c.eq.s $f6, $f7 # encoding: [0x32,0x30,0x07,0x46]
+c.eq.s $f6, $f7
+
+# CHECK: c.olt.s $f6, $f7 # encoding: [0x34,0x30,0x07,0x46]
+c.olt.s $f6, $f7
+
+# CHECK: c.ole.s $f6, $f7 # encoding: [0x36,0x30,0x07,0x46]
+c.ole.s $f6, $f7
+
+#------------------------------------------------------------------------------
+# FP Branch Instructions
+#------------------------------------------------------------------------------
+
+# CHECK: bc1f 8 # encoding: [0x02,0x00,0x00,0x45]
+bc1f 8
+
+# CHECK: bc1fl 8 # encoding: [0x02,0x00,0x02,0x45]
+bc1fl 8
+
+# CHECK: bc1t 8 # encoding: [0x02,0x00,0x01,0x45]
+bc1t 8
+
+# CHECK: bc1tl 8 # encoding: [0x02,0x00,0x03,0x45]
+bc1tl 8
+
+#------------------------------------------------------------------------------
+# FP Load/Store Instructions
+#------------------------------------------------------------------------------
+
+# CHECK: lwc1 $f6, 4($5) # encoding: [0x04,0x00,0xa6,0xc4]
+lwc1 $f6, 4($5)
+
+# CHECK: swc1 $f6, 4($5) # encoding: [0x04,0x00,0xa6,0xe4]
+swc1 $f6, 4($5)
+
+#------------------------------------------------------------------------------
+# FP Move Instructions (between GPR and FPR)
+#------------------------------------------------------------------------------
+
+# CHECK: mfc1 $6, $f7 # encoding: [0x00,0x38,0x06,0x44]
+mfc1 $6, $f7
+
+# CHECK: mtc1 $6, $f7 # encoding: [0x00,0x38,0x86,0x44]
+mtc1 $6, $f7
+
+#------------------------------------------------------------------------------
+# FP Control Register Move Instructions
+#------------------------------------------------------------------------------
+
+# CHECK: cfc1 $6, $0 # encoding: [0x00,0x00,0x46,0x44]
+cfc1 $6, $0
+
+# CHECK: ctc1 $6, $31 # encoding: [0x00,0xf8,0xc6,0x44]
+ctc1 $6, $31
+
+# TODO: R5900 FP Accumulator Instructions
+# #------------------------------------------------------------------------------
+# # R5900-Specific: FP Accumulator Instructions
+# #------------------------------------------------------------------------------
+#
+# # DISABLED: adda.s $f1, $f2 # encoding: [0x18,0x08,0x02,0x46]
+# adda.s $f1, $f2
+#
+# # DISABLED: suba.s $f3, $f4 # encoding: [0x19,0x18,0x04,0x46]
+# suba.s $f3, $f4
+#
+# # DISABLED: mula.s $f5, $f6 # encoding: [0x1a,0x28,0x06,0x46]
+# mula.s $f5, $f6
+#
+# # DISABLED: madda.s $f7, $f8 # encoding: [0x1e,0x38,0x08,0x46]
+# madda.s $f7, $f8
+#
+# # DISABLED: msuba.s $f9, $f10 # encoding: [0x1f,0x48,0x0a,0x46]
+# msuba.s $f9, $f10
+#
+# # DISABLED: madd.s $f0, $f1, $f2 # encoding: [0x1c,0x08,0x02,0x46]
+# madd.s $f0, $f1, $f2
+#
+# # DISABLED: msub.s $f3, $f4, $f5 # encoding: [0xdd,0x20,0x05,0x46]
+# msub.s $f3, $f4, $f5
+
+# TODO: R5900-Specific FPU Instructions (max.s, min.s, rsqrt.s)
+# #------------------------------------------------------------------------------
+# # R5900-Specific: FP Min/Max Instructions
+# #------------------------------------------------------------------------------
+#
+# # DISABLED: max.s $f0, $f1, $f2 # encoding: [0x28,0x08,0x02,0x46]
+# max.s $f0, $f1, $f2
+#
+# # DISABLED: min.s $f3, $f4, $f5 # encoding: [0xe9,0x20,0x05,0x46]
+# min.s $f3, $f4, $f5
+#
+# #------------------------------------------------------------------------------
+# # R5900-Specific: RSQRT.S (3-operand: fd = fs / sqrt(ft))
+# # Note: differs from MIPS IV 2-operand rsqrt.s
+# #------------------------------------------------------------------------------
+#
+# # DISABLED: rsqrt.s $f6, $f7, $f8 # encoding: [0x96,0x39,0x08,0x46]
+# rsqrt.s $f6, $f7, $f8
>From ee4222711dab751a434e2872e9aba0ee15164f40 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Fri, 30 Jan 2026 17:21:17 +0100
Subject: [PATCH 3/4] [MIPS] R5900: Invalidate unsupported FPU instructions
Add predicates to exclude FPU instructions not supported by the R5900:
- Add C_COND_NOTR5900_M multiclass for unsupported compare conditions
(C.UN, C.UEQ, C.ULT, C.ULE, C.SF, C.NGLE, C.SEQ, C.NGL, C.LT, C.NGE,
C.LE, C.NGT)
- Add C_COND_NOTR5900_ALIASES multiclass for corresponding aliases
- Add ISA_MIPS2_NOT_R5900 predicate for ROUND/TRUNC/CEIL/FLOOR.W.S
- Update MicroMips to use C_COND_NOTR5900_ALIASES
- Add comprehensive tests for invalid R5900 FPU instructions
The R5900 FPU only supports 4 compare conditions (C.F, C.EQ, C.LT, C.LE)
and lacks the MIPS II rounding instructions.
---
llvm/lib/Target/Mips/MicroMipsInstrFPU.td | 6 ++
llvm/lib/Target/Mips/MipsInstrFPU.td | 92 +++++++++++------
llvm/test/MC/Mips/r5900-invalid.s | 118 ++++++++++++++++++++++
3 files changed, 184 insertions(+), 32 deletions(-)
diff --git a/llvm/lib/Target/Mips/MicroMipsInstrFPU.td b/llvm/lib/Target/Mips/MicroMipsInstrFPU.td
index 22890dc35baa3..bdf931a52dadb 100644
--- a/llvm/lib/Target/Mips/MicroMipsInstrFPU.td
+++ b/llvm/lib/Target/Mips/MicroMipsInstrFPU.td
@@ -405,10 +405,16 @@ let DecoderNamespace = "Mips64" in
defm S_MM : C_COND_ALIASES<"s", FGR32Opnd>, HARDFLOAT,
ISA_MICROMIPS32_NOT_MIPS32R6;
+defm S_MM : C_COND_NOTR5900_ALIASES<"s", FGR32Opnd>, HARDFLOAT,
+ ISA_MICROMIPS32_NOT_MIPS32R6;
defm D32_MM : C_COND_ALIASES<"d", AFGR64Opnd>, HARDFLOAT,
ISA_MICROMIPS32_NOT_MIPS32R6, FGR_32;
+defm D32_MM : C_COND_NOTR5900_ALIASES<"d", AFGR64Opnd>, HARDFLOAT,
+ ISA_MICROMIPS32_NOT_MIPS32R6, FGR_32;
defm D64_MM : C_COND_ALIASES<"d", FGR64Opnd>, HARDFLOAT,
ISA_MICROMIPS32_NOT_MIPS32R6, FGR_64;
+defm D64_MM : C_COND_NOTR5900_ALIASES<"d", FGR64Opnd>, HARDFLOAT,
+ ISA_MICROMIPS32_NOT_MIPS32R6, FGR_64;
defm : BC1_ALIASES<BC1T_MM, "bc1t", BC1F_MM, "bc1f">,
ISA_MICROMIPS32_NOT_MIPS32R6, HARDFLOAT;
diff --git a/llvm/lib/Target/Mips/MipsInstrFPU.td b/llvm/lib/Target/Mips/MipsInstrFPU.td
index da0887cdcc232..cad27f3b5dd6a 100644
--- a/llvm/lib/Target/Mips/MipsInstrFPU.td
+++ b/llvm/lib/Target/Mips/MipsInstrFPU.td
@@ -297,33 +297,37 @@ multiclass C_COND_M<string TypeStr, RegisterOperand RC, bits<5> fmt,
let BaseOpcode = "c.f."#NAME;
let isCommutable = 1;
}
- def C_UN_#NAME : MMRel, C_COND_FT<"un", TypeStr, RC, itin>,
- C_COND_FM<fmt, 1> {
- let BaseOpcode = "c.un."#NAME;
- let isCommutable = 1;
- }
def C_EQ_#NAME : MMRel, C_COND_FT<"eq", TypeStr, RC, itin>,
C_COND_FM<fmt, 2> {
let BaseOpcode = "c.eq."#NAME;
let isCommutable = 1;
}
+ def C_OLT_#NAME : MMRel, C_COND_FT<"olt", TypeStr, RC, itin>,
+ C_COND_FM<fmt, 4> {
+ let BaseOpcode = "c.olt."#NAME;
+ }
+ def C_OLE_#NAME : MMRel, C_COND_FT<"ole", TypeStr, RC, itin>,
+ C_COND_FM<fmt, 6> {
+ let BaseOpcode = "c.ole."#NAME;
+ }
+}
+
+multiclass C_COND_NOTR5900_M<string TypeStr, RegisterOperand RC, bits<5> fmt,
+ InstrItinClass itin> {
+ def C_UN_#NAME : MMRel, C_COND_FT<"un", TypeStr, RC, itin>,
+ C_COND_FM<fmt, 1> {
+ let BaseOpcode = "c.un."#NAME;
+ let isCommutable = 1;
+ }
def C_UEQ_#NAME : MMRel, C_COND_FT<"ueq", TypeStr, RC, itin>,
C_COND_FM<fmt, 3> {
let BaseOpcode = "c.ueq."#NAME;
let isCommutable = 1;
}
- def C_OLT_#NAME : MMRel, C_COND_FT<"olt", TypeStr, RC, itin>,
- C_COND_FM<fmt, 4> {
- let BaseOpcode = "c.olt."#NAME;
- }
def C_ULT_#NAME : MMRel, C_COND_FT<"ult", TypeStr, RC, itin>,
C_COND_FM<fmt, 5> {
let BaseOpcode = "c.ult."#NAME;
}
- def C_OLE_#NAME : MMRel, C_COND_FT<"ole", TypeStr, RC, itin>,
- C_COND_FM<fmt, 6> {
- let BaseOpcode = "c.ole."#NAME;
- }
def C_ULE_#NAME : MMRel, C_COND_FT<"ule", TypeStr, RC, itin>,
C_COND_FM<fmt, 7> {
let BaseOpcode = "c.ule."#NAME;
@@ -376,29 +380,44 @@ multiclass C_COND_M<string TypeStr, RegisterOperand RC, bits<5> fmt,
let AdditionalPredicates = [NotInMicroMips] in {
defm S : C_COND_M<"s", FGR32Opnd, 16, II_C_CC_S>, ISA_MIPS1_NOT_32R6_64R6;
+defm S : C_COND_NOTR5900_M<"s", FGR32Opnd, 16, II_C_CC_S>,
+ ISA_MIPS1_NOT_32R6_64R6_R5900;
defm D32 : C_COND_M<"d", AFGR64Opnd, 17, II_C_CC_D>, ISA_MIPS1_NOT_32R6_64R6,
FGR_32;
-let DecoderNamespace = "MipsFP64" in
+defm D32 : C_COND_NOTR5900_M<"d", AFGR64Opnd, 17, II_C_CC_D>,
+ ISA_MIPS1_NOT_32R6_64R6, FGR_32;
+let DecoderNamespace = "MipsFP64" in {
defm D64 : C_COND_M<"d", FGR64Opnd, 17, II_C_CC_D>, ISA_MIPS1_NOT_32R6_64R6,
FGR_64;
+defm D64 : C_COND_NOTR5900_M<"d", FGR64Opnd, 17, II_C_CC_D>,
+ ISA_MIPS1_NOT_32R6_64R6, FGR_64;
+}
}
//===----------------------------------------------------------------------===//
// Floating Point Instructions
//===----------------------------------------------------------------------===//
let AdditionalPredicates = [NotInMicroMips], mayRaiseFPException = 1 in {
- def ROUND_W_S : MMRel, StdMMR6Rel,
+ def ROUND_W_S : MMRel,
+ StdMMR6Rel,
ABSS_FT<"round.w.s", FGR32Opnd, FGR32Opnd, II_ROUND>,
- ABSS_FM<0xc, 16>, ISA_MIPS2;
+ ABSS_FM<0xc, 16>,
+ ISA_MIPS2_NOT_R5900;
defm ROUND_W : ROUND_M<"round.w.d", II_ROUND>, ABSS_FM<0xc, 17>, ISA_MIPS2;
- def TRUNC_W_S : MMRel, StdMMR6Rel,
+ def TRUNC_W_S : MMRel,
+ StdMMR6Rel,
ABSS_FT<"trunc.w.s", FGR32Opnd, FGR32Opnd, II_TRUNC>,
- ABSS_FM<0xd, 16>, ISA_MIPS2;
- def CEIL_W_S : MMRel, StdMMR6Rel,
- ABSS_FT<"ceil.w.s", FGR32Opnd, FGR32Opnd, II_CEIL>,
- ABSS_FM<0xe, 16>, ISA_MIPS2;
- def FLOOR_W_S : MMRel, StdMMR6Rel,
+ ABSS_FM<0xd, 16>,
+ ISA_MIPS2_NOT_R5900;
+ def CEIL_W_S : MMRel,
+ StdMMR6Rel,
+ ABSS_FT<"ceil.w.s", FGR32Opnd, FGR32Opnd, II_CEIL>,
+ ABSS_FM<0xe, 16>,
+ ISA_MIPS2_NOT_R5900;
+ def FLOOR_W_S : MMRel,
+ StdMMR6Rel,
ABSS_FT<"floor.w.s", FGR32Opnd, FGR32Opnd, II_FLOOR>,
- ABSS_FM<0xf, 16>, ISA_MIPS2;
+ ABSS_FM<0xf, 16>,
+ ISA_MIPS2_NOT_R5900;
defm TRUNC_W : ROUND_M<"trunc.w.d", II_TRUNC>, ABSS_FM<0xd, 17>, ISA_MIPS2;
defm CEIL_W : ROUND_M<"ceil.w.d", II_CEIL>, ABSS_FM<0xe, 17>, ISA_MIPS2;
@@ -888,24 +907,27 @@ multiclass C_COND_ALIASES<string TypeStr, RegisterOperand RC> {
def : MipsInstAlias<!strconcat("c.f.", TypeStr, " $fs, $ft"),
(!cast<Instruction>("C_F_"#NAME) FCC0,
RC:$fs, RC:$ft), 1>;
- def : MipsInstAlias<!strconcat("c.un.", TypeStr, " $fs, $ft"),
- (!cast<Instruction>("C_UN_"#NAME) FCC0,
- RC:$fs, RC:$ft), 1>;
def : MipsInstAlias<!strconcat("c.eq.", TypeStr, " $fs, $ft"),
(!cast<Instruction>("C_EQ_"#NAME) FCC0,
RC:$fs, RC:$ft), 1>;
- def : MipsInstAlias<!strconcat("c.ueq.", TypeStr, " $fs, $ft"),
- (!cast<Instruction>("C_UEQ_"#NAME) FCC0,
- RC:$fs, RC:$ft), 1>;
def : MipsInstAlias<!strconcat("c.olt.", TypeStr, " $fs, $ft"),
(!cast<Instruction>("C_OLT_"#NAME) FCC0,
RC:$fs, RC:$ft), 1>;
- def : MipsInstAlias<!strconcat("c.ult.", TypeStr, " $fs, $ft"),
- (!cast<Instruction>("C_ULT_"#NAME) FCC0,
- RC:$fs, RC:$ft), 1>;
def : MipsInstAlias<!strconcat("c.ole.", TypeStr, " $fs, $ft"),
(!cast<Instruction>("C_OLE_"#NAME) FCC0,
RC:$fs, RC:$ft), 1>;
+}
+
+multiclass C_COND_NOTR5900_ALIASES<string TypeStr, RegisterOperand RC> {
+ def : MipsInstAlias<!strconcat("c.un.", TypeStr, " $fs, $ft"),
+ (!cast<Instruction>("C_UN_"#NAME) FCC0,
+ RC:$fs, RC:$ft), 1>;
+ def : MipsInstAlias<!strconcat("c.ueq.", TypeStr, " $fs, $ft"),
+ (!cast<Instruction>("C_UEQ_"#NAME) FCC0,
+ RC:$fs, RC:$ft), 1>;
+ def : MipsInstAlias<!strconcat("c.ult.", TypeStr, " $fs, $ft"),
+ (!cast<Instruction>("C_ULT_"#NAME) FCC0,
+ RC:$fs, RC:$ft), 1>;
def : MipsInstAlias<!strconcat("c.ule.", TypeStr, " $fs, $ft"),
(!cast<Instruction>("C_ULE_"#NAME) FCC0,
RC:$fs, RC:$ft), 1>;
@@ -947,10 +969,16 @@ multiclass BC1_ALIASES<Instruction BCTrue, string BCTrueString,
let AdditionalPredicates = [NotInMicroMips] in {
defm S : C_COND_ALIASES<"s", FGR32Opnd>, HARDFLOAT,
ISA_MIPS1_NOT_32R6_64R6;
+ defm S : C_COND_NOTR5900_ALIASES<"s", FGR32Opnd>, HARDFLOAT,
+ ISA_MIPS1_NOT_32R6_64R6_R5900;
defm D32 : C_COND_ALIASES<"d", AFGR64Opnd>, HARDFLOAT,
ISA_MIPS1_NOT_32R6_64R6, FGR_32;
+ defm D32 : C_COND_NOTR5900_ALIASES<"d", AFGR64Opnd>, HARDFLOAT,
+ ISA_MIPS1_NOT_32R6_64R6, FGR_32;
defm D64 : C_COND_ALIASES<"d", FGR64Opnd>, HARDFLOAT,
ISA_MIPS1_NOT_32R6_64R6, FGR_64;
+ defm D64 : C_COND_NOTR5900_ALIASES<"d", FGR64Opnd>, HARDFLOAT,
+ ISA_MIPS1_NOT_32R6_64R6, FGR_64;
defm : BC1_ALIASES<BC1T, "bc1t", BC1F, "bc1f">, ISA_MIPS1_NOT_32R6_64R6,
HARDFLOAT;
diff --git a/llvm/test/MC/Mips/r5900-invalid.s b/llvm/test/MC/Mips/r5900-invalid.s
index 4b1eed2f56bcc..74e8e8ff44241 100644
--- a/llvm/test/MC/Mips/r5900-invalid.s
+++ b/llvm/test/MC/Mips/r5900-invalid.s
@@ -4,6 +4,8 @@
# - No 64-bit multiply/divide (DMULT/DMULTU/DDIV/DDIVU)
# - No LL/SC atomic instructions
# - No COP3 instructions (LWC3, SWC3, LDC3, SDC3)
+# - No ROUND/TRUNC/CEIL/FLOOR.W.S (uses CVT.W.S instead)
+# - No double-precision FPU (single float only)
#
# RUN: not llvm-mc %s -triple=mips64el-unknown-linux -mcpu=r5900 2>%t1
# RUN: FileCheck %s < %t1
@@ -57,3 +59,119 @@
# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
sdc3 $4, 0($5)
+
+# =============================================================================
+# Single-precision FP compare instructions that R5900 does NOT support
+# (R5900 only supports c.f.s, c.eq.s, c.olt.s, c.ole.s)
+# =============================================================================
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.un.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.ueq.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.ult.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.ule.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.sf.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.ngle.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.seq.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.ngl.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.lt.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.nge.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.le.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.ngt.s $f4, $f6
+
+# =============================================================================
+# FPU rounding instructions that R5900 does NOT support
+# (R5900 uses CVT.W.S which always truncates toward zero)
+# =============================================================================
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ round.w.s $f4, $f5
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ trunc.w.s $f6, $f7
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ ceil.w.s $f8, $f9
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ floor.w.s $f10, $f11
+
+# =============================================================================
+# Double-precision FPU instructions that R5900 does NOT support
+# (R5900 has single-precision FPU only)
+# =============================================================================
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ add.d $f4, $f6, $f8
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ sub.d $f4, $f6, $f8
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ mul.d $f4, $f6, $f8
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ div.d $f4, $f6, $f8
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ abs.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ neg.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ sqrt.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ mov.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ cvt.s.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ cvt.d.s $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ cvt.d.w $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ cvt.w.d $f4, $f6
+
+# 64-bit FP load/store
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ ldc1 $f4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ sdc1 $f4, 0($5)
+
+# Double-precision comparisons
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.eq.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.lt.d $f4, $f6
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ c.le.d $f4, $f6
>From 197832d0537e093c3115dcd38632f873ee548134 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 1 Feb 2026 12:51:16 +0100
Subject: [PATCH 4/4] [MIPS] Add R5900 FPU compare CodeGen test
---
llvm/test/CodeGen/Mips/r5900-fpu-compare.ll | 210 ++++++++++++++++++++
1 file changed, 210 insertions(+)
create mode 100644 llvm/test/CodeGen/Mips/r5900-fpu-compare.ll
diff --git a/llvm/test/CodeGen/Mips/r5900-fpu-compare.ll b/llvm/test/CodeGen/Mips/r5900-fpu-compare.ll
new file mode 100644
index 0000000000000..d63a0d25b301d
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/r5900-fpu-compare.ll
@@ -0,0 +1,210 @@
+; RUN: llc -mtriple=mips64el -mcpu=r5900 < %s | FileCheck %s
+;
+; Test that R5900 FPU comparisons only generate supported instructions.
+; R5900 only supports: C.F (0x30), C.EQ (0x32), C.OLT (0x34), C.OLE (0x36)
+;
+; Unsupported conditions are transformed:
+; - Unordered (ULT, ULE, UEQ, etc.) → Ordered equivalents (R5900 has no NaN)
+; - Greater-than → Less-than with swapped operands
+
+;-----------------------------------------------------------------------------
+; Ordered comparisons
+;-----------------------------------------------------------------------------
+
+define i32 @test_oeq(float %a, float %b) {
+; CHECK-LABEL: test_oeq:
+; CHECK: c.eq.s $f12, $f13
+; CHECK-NOT: c.ueq
+ %cmp = fcmp oeq float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_olt(float %a, float %b) {
+; CHECK-LABEL: test_olt:
+; CHECK: c.olt.s $f12, $f13
+; CHECK-NOT: c.ult
+ %cmp = fcmp olt float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_ole(float %a, float %b) {
+; CHECK-LABEL: test_ole:
+; CHECK: c.ole.s $f12, $f13
+; CHECK-NOT: c.ule
+ %cmp = fcmp ole float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_ogt(float %a, float %b) {
+; CHECK-LABEL: test_ogt:
+; CHECK: c.olt.s $f13, $f12
+; Swapped operands: a > b → b < a
+ %cmp = fcmp ogt float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_oge(float %a, float %b) {
+; CHECK-LABEL: test_oge:
+; CHECK: c.ole.s $f13, $f12
+; Swapped operands: a >= b → b <= a
+ %cmp = fcmp oge float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+;-----------------------------------------------------------------------------
+; Unordered comparisons - mapped to ordered (R5900 FPU has no NaN)
+;-----------------------------------------------------------------------------
+
+define i32 @test_ueq(float %a, float %b) {
+; CHECK-LABEL: test_ueq:
+; CHECK: c.eq.s $f12, $f13
+; CHECK-NOT: c.ueq
+ %cmp = fcmp ueq float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_ult(float %a, float %b) {
+; CHECK-LABEL: test_ult:
+; CHECK: c.olt.s $f12, $f13
+; CHECK-NOT: c.ult
+ %cmp = fcmp ult float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_ule(float %a, float %b) {
+; CHECK-LABEL: test_ule:
+; CHECK: c.ole.s $f12, $f13
+; CHECK-NOT: c.ule
+ %cmp = fcmp ule float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_ugt(float %a, float %b) {
+; CHECK-LABEL: test_ugt:
+; CHECK: c.olt.s $f13, $f12
+; Swapped operands: a > b → b < a
+ %cmp = fcmp ugt float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_uge(float %a, float %b) {
+; CHECK-LABEL: test_uge:
+; CHECK: c.ole.s $f13, $f12
+; Swapped operands: a >= b → b <= a
+ %cmp = fcmp uge float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+;-----------------------------------------------------------------------------
+; Simple comparisons (non-prefixed)
+;-----------------------------------------------------------------------------
+
+define i32 @test_eq(float %a, float %b) {
+; CHECK-LABEL: test_eq:
+; CHECK: c.eq.s $f12, $f13
+ %cmp = fcmp oeq float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_lt(float %a, float %b) {
+; CHECK-LABEL: test_lt:
+; CHECK: c.olt.s $f12, $f13
+ %cmp = fcmp olt float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_le(float %a, float %b) {
+; CHECK-LABEL: test_le:
+; CHECK: c.ole.s $f12, $f13
+ %cmp = fcmp ole float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_gt(float %a, float %b) {
+; CHECK-LABEL: test_gt:
+; CHECK: c.olt.s $f13, $f12
+ %cmp = fcmp ogt float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_ge(float %a, float %b) {
+; CHECK-LABEL: test_ge:
+; CHECK: c.ole.s $f13, $f12
+ %cmp = fcmp oge float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+;-----------------------------------------------------------------------------
+; Not-equal comparisons
+;-----------------------------------------------------------------------------
+
+define i32 @test_one(float %a, float %b) {
+; CHECK-LABEL: test_one:
+; CHECK: c.eq.s
+; Uses EQ and inverts result via bc1f/bc1t
+ %cmp = fcmp one float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_une(float %a, float %b) {
+; CHECK-LABEL: test_une:
+; CHECK: c.eq.s
+; Uses EQ and inverts result via bc1f/bc1t
+ %cmp = fcmp une float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+;-----------------------------------------------------------------------------
+; Ordered/Unordered predicates
+;-----------------------------------------------------------------------------
+
+define i32 @test_ord(float %a, float %b) {
+; CHECK-LABEL: test_ord:
+; R5900 has no NaN, so ordered is always true
+; Should optimize or use FCOND_T (inverted F)
+ %cmp = fcmp ord float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+define i32 @test_uno(float %a, float %b) {
+; CHECK-LABEL: test_uno:
+; R5900 has no NaN, so unordered is always false
+; CHECK: c.f.s
+ %cmp = fcmp uno float %a, %b
+ %result = zext i1 %cmp to i32
+ ret i32 %result
+}
+
+;-----------------------------------------------------------------------------
+; Verify no unsupported instructions are generated anywhere
+;-----------------------------------------------------------------------------
+; CHECK-NOT: c.un.s
+; CHECK-NOT: c.ueq.s
+; CHECK-NOT: c.ult.s
+; CHECK-NOT: c.ule.s
+; CHECK-NOT: c.sf.s
+; CHECK-NOT: c.ngle.s
+; CHECK-NOT: c.seq.s
+; CHECK-NOT: c.ngl.s
+; CHECK-NOT: c.lt.s
+; CHECK-NOT: c.nge.s
+; CHECK-NOT: c.le.s
+; CHECK-NOT: c.ngt.s
More information about the llvm-commits
mailing list