[clang] [llvm] [Mips] Add r5900 (PlayStation 2 Emotion Engine) CPU support (PR #176666)
Rick Gaiser via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 23 00:15:59 PST 2026
https://github.com/rickgaiser updated https://github.com/llvm/llvm-project/pull/176666
>From 71b10943ce67885375dfa9bdd4ed767cb59b3943 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 18 Jan 2026 15:53:30 +0100
Subject: [PATCH 1/7] [Mips] Add r5900 CPU definition and feature flag
---
llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp | 2 ++
llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp | 2 ++
llvm/lib/Target/Mips/Mips.td | 5 +++++
llvm/lib/Target/Mips/MipsSubtarget.cpp | 8 ++++----
llvm/lib/Target/Mips/MipsSubtarget.h | 4 ++++
llvm/test/CodeGen/Mips/cpus.ll | 4 ++++
6 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
index 6675db755d1a1..f91c378ad0afa 100644
--- a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
+++ b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
@@ -693,6 +693,8 @@ class MipsAsmParser : public MCTargetAsmParser {
return (getSTI().hasFeature(Mips::FeatureCnMipsP));
}
+ bool isR5900() const { return (getSTI().hasFeature(Mips::FeatureR5900)); }
+
bool inPicMode() {
return IsPicEnabled;
}
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
index 01f18acf050d7..12a5b20cfb9ff 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
@@ -921,6 +921,8 @@ MipsTargetELFStreamer::MipsTargetELFStreamer(MCStreamer &S,
// Machine
if (Features[Mips::FeatureCnMips])
EFlags |= ELF::EF_MIPS_MACH_OCTEON;
+ else if (Features[Mips::FeatureR5900])
+ EFlags |= ELF::EF_MIPS_MACH_5900;
// Other options.
if (Features[Mips::FeatureNaN2008])
diff --git a/llvm/lib/Target/Mips/Mips.td b/llvm/lib/Target/Mips/Mips.td
index 8b0d87b5c5376..7cfa0800454c6 100644
--- a/llvm/lib/Target/Mips/Mips.td
+++ b/llvm/lib/Target/Mips/Mips.td
@@ -186,6 +186,10 @@ def FeatureCnMipsP : SubtargetFeature<"cnmipsp", "HasCnMipsP",
"true", "Octeon+ cnMIPS Support",
[FeatureCnMips]>;
+def FeatureR5900 : SubtargetFeature<"r5900", "IsR5900", "true",
+ "R5900 (PS2 Emotion Engine) Support",
+ [FeatureMips3, FeatureSoftFloat]>;
+
def FeatureUseTCCInDIV : SubtargetFeature<
"use-tcc-in-div",
"UseTCCInDIV", "false",
@@ -296,6 +300,7 @@ def : Proc<"mips64r6", [FeatureMips64r6]>;
def : Proc<"octeon", [FeatureMips64r2, FeatureCnMips]>;
def : Proc<"octeon+", [FeatureMips64r2, FeatureCnMips, FeatureCnMipsP]>;
def : ProcessorModel<"p5600", MipsP5600Model, [ImplP5600]>;
+def : Proc<"r5900", [FeatureR5900]>;
def : ProcessorModel<"i6400", MipsI6400Model, [ImplI6400]>;
def : ProcessorModel<"i6500", MipsI6400Model, [ImplI6500]>;
diff --git a/llvm/lib/Target/Mips/MipsSubtarget.cpp b/llvm/lib/Target/Mips/MipsSubtarget.cpp
index aef9382d3c1dc..5134e16a8d78b 100644
--- a/llvm/lib/Target/Mips/MipsSubtarget.cpp
+++ b/llvm/lib/Target/Mips/MipsSubtarget.cpp
@@ -86,10 +86,10 @@ MipsSubtarget::MipsSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
IsSingleFloat(false), IsFPXX(false), NoABICalls(false), Abs2008(false),
IsFP64bit(false), UseOddSPReg(true), IsNaN2008bit(false),
IsGP64bit(false), HasVFPU(false), HasCnMips(false), HasCnMipsP(false),
- HasMips3_32(false), HasMips3_32r2(false), HasMips4_32(false),
- HasMips4_32r2(false), HasMips5_32r2(false), InMips16Mode(false),
- InMips16HardFloat(Mips16HardFloat), InMicroMipsMode(false), HasDSP(false),
- HasDSPR2(false), HasDSPR3(false),
+ IsR5900(false), HasMips3_32(false), HasMips3_32r2(false),
+ HasMips4_32(false), HasMips4_32r2(false), HasMips5_32r2(false),
+ InMips16Mode(false), InMips16HardFloat(Mips16HardFloat),
+ InMicroMipsMode(false), HasDSP(false), HasDSPR2(false), HasDSPR3(false),
AllowMixed16_32(Mixed16_32 || Mips_Os16), Os16(Mips_Os16), HasMSA(false),
UseTCCInDIV(false), HasSym32(false), HasEVA(false), DisableMadd4(false),
HasMT(false), HasCRC(false), HasVirt(false), HasGINV(false),
diff --git a/llvm/lib/Target/Mips/MipsSubtarget.h b/llvm/lib/Target/Mips/MipsSubtarget.h
index b09cfb3ac4a09..3c8b088421208 100644
--- a/llvm/lib/Target/Mips/MipsSubtarget.h
+++ b/llvm/lib/Target/Mips/MipsSubtarget.h
@@ -124,6 +124,9 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
// CPU supports cnMIPSP (Cavium Networks Octeon+ CPU).
bool HasCnMipsP;
+ // IsR5900 - CPU is R5900 (PlayStation 2 Emotion Engine).
+ bool IsR5900;
+
// isLinux - Target system is Linux. Is false we consider ELFOS for now.
bool IsLinux;
@@ -297,6 +300,7 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
bool hasCnMips() const { return HasCnMips; }
bool hasCnMipsP() const { return HasCnMipsP; }
+ bool isR5900() const { return IsR5900; }
bool isLittle() const { return IsLittle; }
bool isABICalls() const { return !NoABICalls; }
diff --git a/llvm/test/CodeGen/Mips/cpus.ll b/llvm/test/CodeGen/Mips/cpus.ll
index 077f8fb48bae0..14e7476d8f9ef 100644
--- a/llvm/test/CodeGen/Mips/cpus.ll
+++ b/llvm/test/CodeGen/Mips/cpus.ll
@@ -58,6 +58,10 @@
; OCTEONP: ISA: MIPS64r2
; OCTEONP: ISA Extension: Cavium Networks OcteonP
+; RUN: llc -mtriple=mips64el -mcpu=r5900 -filetype=obj < %s \
+; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=R5900
+; R5900: ISA: MIPS3
+
; Check that we reject CPUs that are not implemented.
; RUN: not llc < %s -o /dev/null -mtriple=mips64 -mcpu=mips5 2>&1 \
>From c6a8b4ebde607615880fbb1d912f96f5e11e6054 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 18 Jan 2026 13:24:24 +0100
Subject: [PATCH 2/7] [Mips] Disable unsupported instructions for r5900
The R5900 is based on MIPS-III but lacks several instructions that were
also removed or relocated in MIPS32R6/MIPS64R6:
- LL/SC atomic instructions
- COP3 instructions (LWC3, SWC3, LDC3, SDC3)
- 64-bit multiply/divide (DMULT, DMULTU, DDIV, DDIVU)
Implementation uses combined ISA classes (e.g., ISA_MIPS2_NOT_32R6_64R6_R5900)
with InsnPredicates to properly combine the NotR5900 predicate with
existing ISA and ASE predicates.
- Add NotR5900 predicate
- Add ISA_MIPS*_NOT_*_R5900 combined classes
- Set MaxAtomicSizeInBitsSupported to 0 for R5900
- Expand 64-bit multiply/divide operations to 32-bit equivalents
- Add assembly tests for invalid instructions
---
llvm/lib/Target/Mips/Mips64InstrInfo.td | 175 +++++++++++---------
llvm/lib/Target/Mips/MipsISelLowering.cpp | 5 +-
llvm/lib/Target/Mips/MipsInstrInfo.td | 68 ++++++--
llvm/lib/Target/Mips/MipsSEISelLowering.cpp | 13 +-
llvm/test/MC/Mips/r5900-invalid.s | 59 +++++++
5 files changed, 224 insertions(+), 96 deletions(-)
create mode 100644 llvm/test/MC/Mips/r5900-invalid.s
diff --git a/llvm/lib/Target/Mips/Mips64InstrInfo.td b/llvm/lib/Target/Mips/Mips64InstrInfo.td
index ef4eea8c50dc6..b22b0acf49507 100644
--- a/llvm/lib/Target/Mips/Mips64InstrInfo.td
+++ b/llvm/lib/Target/Mips/Mips64InstrInfo.td
@@ -245,18 +245,23 @@ def SDR : StoreLeftRight<"sdr", MipsSDR, GPR64Opnd, II_SDR>, LW_FM<0x2d>,
/// Load-linked, Store-conditional
let AdditionalPredicates = [NotInMicroMips] in {
- def LLD : LLBase<"lld", GPR64Opnd, mem_simmptr>, LW_FM<0x34>,
- ISA_MIPS3_NOT_32R6_64R6;
+ def LLD : LLBase<"lld", GPR64Opnd, mem_simmptr>,
+ LW_FM<0x34>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
}
-def SCD : SCBase<"scd", GPR64Opnd>, LW_FM<0x3c>, ISA_MIPS3_NOT_32R6_64R6;
+def SCD : SCBase<"scd", GPR64Opnd>, LW_FM<0x3c>, ISA_MIPS3_NOT_32R6_64R6_R5900;
let AdditionalPredicates = [NotInMicroMips],
DecoderNamespace = "Mips32_64_PTR64" in {
-def LL64 : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_64,
- ISA_MIPS2_NOT_32R6_64R6;
-def SC64 : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_64,
- ISA_MIPS2_NOT_32R6_64R6;
-def JR64 : IndirectBranch<"jr", GPR64Opnd>, MTLO_FM<8>, PTR_64;
+ def LL64 : LLBase<"ll", GPR32Opnd>,
+ LW_FM<0x30>,
+ PTR_64,
+ ISA_MIPS2_NOT_32R6_64R6_R5900;
+ def SC64 : SCBase<"sc", GPR32Opnd>,
+ LW_FM<0x38>,
+ PTR_64,
+ ISA_MIPS2_NOT_32R6_64R6_R5900;
+ def JR64 : IndirectBranch<"jr", GPR64Opnd>, MTLO_FM<8>, PTR_64;
}
def JALR64 : JumpLinkReg<"jalr", GPR64Opnd>, JALR_FM, PTR_64;
@@ -305,25 +310,32 @@ let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips,
/// Multiply and Divide Instructions.
let AdditionalPredicates = [NotInMicroMips] in {
- def DMULT : Mult<"dmult", II_DMULT, GPR64Opnd, [HI0_64, LO0_64]>,
- MULT_FM<0, 0x1c>, ISA_MIPS3_NOT_32R6_64R6;
+ def DMULT : Mult<"dmult", II_DMULT, GPR64Opnd, [HI0_64, LO0_64]>,
+ MULT_FM<0, 0x1c>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DMULTu : Mult<"dmultu", II_DMULTU, GPR64Opnd, [HI0_64, LO0_64]>,
- MULT_FM<0, 0x1d>, ISA_MIPS3_NOT_32R6_64R6;
+ MULT_FM<0, 0x1d>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
}
-def PseudoDMULT : MultDivPseudo<DMULT, ACC128, GPR64Opnd, MipsMult,
- II_DMULT>, ISA_MIPS3_NOT_32R6_64R6;
-def PseudoDMULTu : MultDivPseudo<DMULTu, ACC128, GPR64Opnd, MipsMultu,
- II_DMULTU>, ISA_MIPS3_NOT_32R6_64R6;
+def PseudoDMULT : MultDivPseudo<DMULT, ACC128, GPR64Opnd, MipsMult, II_DMULT>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+def PseudoDMULTu
+ : MultDivPseudo<DMULTu, ACC128, GPR64Opnd, MipsMultu, II_DMULTU>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
let AdditionalPredicates = [NotInMicroMips] in {
def DSDIV : Div<"ddiv", II_DDIV, GPR64Opnd, [HI0_64, LO0_64]>,
- MULT_FM<0, 0x1e>, ISA_MIPS3_NOT_32R6_64R6;
+ MULT_FM<0, 0x1e>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DUDIV : Div<"ddivu", II_DDIVU, GPR64Opnd, [HI0_64, LO0_64]>,
- MULT_FM<0, 0x1f>, ISA_MIPS3_NOT_32R6_64R6;
+ MULT_FM<0, 0x1f>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
}
-def PseudoDSDIV : MultDivPseudo<DSDIV, ACC128, GPR64Opnd, MipsDivRem,
- II_DDIV, 0, 1, 1>, ISA_MIPS3_NOT_32R6_64R6;
-def PseudoDUDIV : MultDivPseudo<DUDIV, ACC128, GPR64Opnd, MipsDivRemU,
- II_DDIVU, 0, 1, 1>, ISA_MIPS3_NOT_32R6_64R6;
+def PseudoDSDIV
+ : MultDivPseudo<DSDIV, ACC128, GPR64Opnd, MipsDivRem, II_DDIV, 0, 1, 1>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+def PseudoDUDIV
+ : MultDivPseudo<DUDIV, ACC128, GPR64Opnd, MipsDivRemU, II_DDIVU, 0, 1, 1>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
let isCodeGenOnly = 1 in {
def MTHI64 : MoveToLOHI<"mthi", GPR64Opnd, [HI0_64]>, MTLO_FM<0x11>,
@@ -1119,100 +1131,103 @@ def LoadAddrReg64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rt), (ins mem:$addr),
def LoadAddrImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rt), (ins imm64:$imm64),
"dla\t$rt, $imm64">;
-def DMULImmMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt,
- simm32_relaxed:$imm),
- "dmul\t$rs, $rt, $imm">,
- ISA_MIPS3_NOT_32R6_64R6;
-def DMULOMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt,
- GPR64Opnd:$rd),
- "dmulo\t$rs, $rt, $rd">,
- ISA_MIPS3_NOT_32R6_64R6;
-def DMULOUMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt,
- GPR64Opnd:$rd),
- "dmulou\t$rs, $rt, $rd">,
- ISA_MIPS3_NOT_32R6_64R6;
+def DMULImmMacro
+ : MipsAsmPseudoInst<(outs),
+ (ins GPR64Opnd:$rs, GPR64Opnd:$rt, simm32_relaxed:$imm),
+ "dmul\t$rs, $rt, $imm">,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+def DMULOMacro
+ : MipsAsmPseudoInst<(outs),
+ (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd),
+ "dmulo\t$rs, $rt, $rd">,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+def DMULOUMacro
+ : MipsAsmPseudoInst<(outs),
+ (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd),
+ "dmulou\t$rs, $rt, $rd">,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DMULMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt,
GPR64Opnd:$rd),
"dmul\t$rs, $rt, $rd"> {
- let InsnPredicates = [HasMips3, NotMips64r6, NotCnMips];
+ let InsnPredicates = [HasMips3, NotMips64r6, NotCnMips, NotR5900];
}
let AdditionalPredicates = [NotInMicroMips] in {
def DSDivMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
(ins GPR64Opnd:$rs, GPR64Opnd:$rt),
"ddiv\t$rd, $rs, $rt">,
- ISA_MIPS3_NOT_32R6_64R6;
- def DSDivIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
- (ins GPR64Opnd:$rs, imm64:$imm),
- "ddiv\t$rd, $rs, $imm">,
- ISA_MIPS3_NOT_32R6_64R6;
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def DSDivIMacro
+ : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm),
+ "ddiv\t$rd, $rs, $imm">,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DUDivMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
(ins GPR64Opnd:$rs, GPR64Opnd:$rt),
"ddivu\t$rd, $rs, $rt">,
- ISA_MIPS3_NOT_32R6_64R6;
- def DUDivIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
- (ins GPR64Opnd:$rs, imm64:$imm),
- "ddivu\t$rd, $rs, $imm">,
- ISA_MIPS3_NOT_32R6_64R6;
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def DUDivIMacro
+ : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm),
+ "ddivu\t$rd, $rs, $imm">,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
// GAS expands 'div' and 'ddiv' differently when the destination
// register is $zero and the instruction is in the two operand
// form. 'ddiv' gets expanded, while 'div' is not expanded.
- def : MipsInstAlias<"ddiv $rs, $rt", (DSDivMacro GPR64Opnd:$rs,
- GPR64Opnd:$rs,
- GPR64Opnd:$rt), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
- def : MipsInstAlias<"ddiv $rd, $imm", (DSDivIMacro GPR64Opnd:$rd,
- GPR64Opnd:$rd,
- imm64:$imm), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
+ def : MipsInstAlias<"ddiv $rs, $rt",
+ (DSDivMacro GPR64Opnd:$rs, GPR64Opnd:$rs, GPR64Opnd:$rt),
+ 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def : MipsInstAlias<"ddiv $rd, $imm",
+ (DSDivIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, imm64:$imm),
+ 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
// GAS expands 'divu' and 'ddivu' differently when the destination
// register is $zero and the instruction is in the two operand
// form. 'ddivu' gets expanded, while 'divu' is not expanded.
- def : MipsInstAlias<"ddivu $rt, $rs", (DUDivMacro GPR64Opnd:$rt,
- GPR64Opnd:$rt,
- GPR64Opnd:$rs), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
- def : MipsInstAlias<"ddivu $rd, $imm", (DUDivIMacro GPR64Opnd:$rd,
- GPR64Opnd:$rd,
- imm64:$imm), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
+ def : MipsInstAlias<"ddivu $rt, $rs",
+ (DUDivMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs),
+ 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def : MipsInstAlias<"ddivu $rd, $imm",
+ (DUDivIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, imm64:$imm),
+ 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DSRemMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
(ins GPR64Opnd:$rs, GPR64Opnd:$rt),
"drem\t$rd, $rs, $rt">,
- ISA_MIPS3_NOT_32R6_64R6;
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DSRemIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
(ins GPR64Opnd:$rs, simm32_relaxed:$imm),
"drem\t$rd, $rs, $imm">,
- ISA_MIPS3_NOT_32R6_64R6;
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DURemMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
(ins GPR64Opnd:$rs, GPR64Opnd:$rt),
"dremu\t$rd, $rs, $rt">,
- ISA_MIPS3_NOT_32R6_64R6;
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
def DURemIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd),
(ins GPR64Opnd:$rs, simm32_relaxed:$imm),
"dremu\t$rd, $rs, $imm">,
- ISA_MIPS3_NOT_32R6_64R6;
- def : MipsInstAlias<"drem $rt, $rs", (DSRemMacro GPR64Opnd:$rt,
- GPR64Opnd:$rt,
- GPR64Opnd:$rs), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
- def : MipsInstAlias<"drem $rd, $imm", (DSRemIMacro GPR64Opnd:$rd,
- GPR64Opnd:$rd,
- simm32_relaxed:$imm), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
- def : MipsInstAlias<"dremu $rt, $rs", (DURemMacro GPR64Opnd:$rt,
- GPR64Opnd:$rt,
- GPR64Opnd:$rs), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
- def : MipsInstAlias<"dremu $rd, $imm", (DURemIMacro GPR64Opnd:$rd,
- GPR64Opnd:$rd,
- simm32_relaxed:$imm), 0>,
- ISA_MIPS3_NOT_32R6_64R6;
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def : MipsInstAlias<"drem $rt, $rs",
+ (DSRemMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs),
+ 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def : MipsInstAlias<
+ "drem $rd, $imm",
+ (DSRemIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, simm32_relaxed:$imm), 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def : MipsInstAlias<"dremu $rt, $rs",
+ (DURemMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs),
+ 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
+ def : MipsInstAlias<
+ "dremu $rd, $imm",
+ (DURemIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, simm32_relaxed:$imm), 0>,
+ ISA_MIPS3_NOT_32R6_64R6_R5900;
}
def NORImm64 : NORIMM_DESC_BASE<GPR64Opnd, imm64>, GPR_64;
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index dbbeb75e673c3..b62669d5d5fa6 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -412,7 +412,10 @@ MipsTargetLowering::MipsTargetLowering(const MipsTargetMachine &TM,
ISD::OR, ISD::ADD, ISD::SUB, ISD::AssertZext, ISD::SHL,
ISD::SIGN_EXTEND});
- if (Subtarget.isGP64bit())
+ // R5900 has no LL/SC instructions for atomic operations
+ if (Subtarget.isR5900())
+ setMaxAtomicSizeInBitsSupported(0);
+ else if (Subtarget.isGP64bit())
setMaxAtomicSizeInBitsSupported(64);
else
setMaxAtomicSizeInBitsSupported(32);
diff --git a/llvm/lib/Target/Mips/MipsInstrInfo.td b/llvm/lib/Target/Mips/MipsInstrInfo.td
index 5b464ff2957e4..2a476e789032c 100644
--- a/llvm/lib/Target/Mips/MipsInstrInfo.td
+++ b/llvm/lib/Target/Mips/MipsInstrInfo.td
@@ -259,6 +259,10 @@ 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 NotR5900 : Predicate<"!Subtarget->isR5900()">,
+ AssemblerPredicate<(all_of (not FeatureR5900))>;
def HasCRC : Predicate<"Subtarget->hasCRC()">,
AssemblerPredicate<(all_of FeatureCRC)>;
def HasVirt : Predicate<"Subtarget->hasVirt()">,
@@ -312,14 +316,27 @@ class ISA_MIPS1_NOT_32R6_64R6 {
list<Predicate> InsnPredicates = [NotMips32r6, NotMips64r6];
list<Predicate> EncodingPredicates = [HasStdEnc];
}
+class ISA_MIPS1_NOT_32R6_64R6_R5900 {
+ list<Predicate> InsnPredicates = [NotMips32r6, NotMips64r6, NotR5900];
+ list<Predicate> EncodingPredicates = [HasStdEnc];
+}
class ISA_MIPS2 {
list<Predicate> InsnPredicates = [HasMips2];
list<Predicate> EncodingPredicates = [HasStdEnc];
}
+class ISA_MIPS2_NOT_R5900 {
+ list<Predicate> InsnPredicates = [HasMips2, NotR5900];
+ list<Predicate> EncodingPredicates = [HasStdEnc];
+}
class ISA_MIPS2_NOT_32R6_64R6 {
list<Predicate> InsnPredicates = [HasMips2, NotMips32r6, NotMips64r6];
list<Predicate> EncodingPredicates = [HasStdEnc];
}
+class ISA_MIPS2_NOT_32R6_64R6_R5900 {
+ list<Predicate> InsnPredicates = [HasMips2, NotMips32r6, NotMips64r6,
+ NotR5900];
+ list<Predicate> EncodingPredicates = [HasStdEnc];
+}
class ISA_MIPS3 {
list<Predicate> InsnPredicates = [HasMips3];
list<Predicate> EncodingPredicates = [HasStdEnc];
@@ -328,6 +345,11 @@ class ISA_MIPS3_NOT_32R6_64R6 {
list<Predicate> InsnPredicates = [HasMips3, NotMips32r6, NotMips64r6];
list<Predicate> EncodingPredicates = [HasStdEnc];
}
+class ISA_MIPS3_NOT_32R6_64R6_R5900 {
+ list<Predicate> InsnPredicates = [HasMips3, NotMips32r6, NotMips64r6,
+ NotR5900];
+ list<Predicate> EncodingPredicates = [HasStdEnc];
+}
class ISA_MIPS32 {
list<Predicate> InsnPredicates = [HasMips32];
list<Predicate> EncodingPredicates = [HasStdEnc];
@@ -1837,11 +1859,15 @@ class Atomic2OpsPostRA<RegisterClass RC> :
PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$incr), []> {
let mayLoad = 1;
let mayStore = 1;
+ let Predicates = [NotR5900];
}
-class Atomic2OpsSubwordPostRA<RegisterClass RC> :
- PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$incr, RC:$mask, RC:$mask2,
- RC:$shiftamnt), []>;
+class Atomic2OpsSubwordPostRA<RegisterClass RC>
+ : PseudoSE<
+ (outs RC:$dst),
+ (ins PtrRC:$ptr, RC:$incr, RC:$mask, RC:$mask2, RC:$shiftamnt), []> {
+ let Predicates = [NotR5900];
+}
// Atomic Compare & Swap.
// Atomic compare and swap is lowered into two stages. The first stage happens
@@ -1856,6 +1882,7 @@ class AtomicCmpSwapPostRA<RegisterClass RC> :
PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$cmp, RC:$swap), []> {
let mayLoad = 1;
let mayStore = 1;
+ let Predicates = [NotR5900];
}
class AtomicCmpSwapSubwordPostRA<RegisterClass RC> :
@@ -1863,6 +1890,7 @@ class AtomicCmpSwapSubwordPostRA<RegisterClass RC> :
RC:$mask2, RC:$ShiftNewVal, RC:$ShiftAmt), []> {
let mayLoad = 1;
let mayStore = 1;
+ let Predicates = [NotR5900];
}
class LLBase<string opstr, RegisterOperand RO, DAGOperand MO = mem> :
@@ -2166,14 +2194,22 @@ def SDC2 : StdMMR6Rel, SW_FT2<"sdc2", COP2Opnd, II_SDC2, store>,
// COP3 Memory Instructions
let DecoderNamespace = "COP3_" in {
- def LWC3 : LW_FT3<"lwc3", COP3Opnd, II_LWC3, load>, LW_FM<0x33>,
- ISA_MIPS1_NOT_32R6_64R6, NOT_ASE_CNMIPS;
- def SWC3 : SW_FT3<"swc3", COP3Opnd, II_SWC3, store>, LW_FM<0x3b>,
- ISA_MIPS1_NOT_32R6_64R6, NOT_ASE_CNMIPS;
- def LDC3 : LW_FT3<"ldc3", COP3Opnd, II_LDC3, load>, LW_FM<0x37>,
- ISA_MIPS2, NOT_ASE_CNMIPS;
- def SDC3 : SW_FT3<"sdc3", COP3Opnd, II_SDC3, store>, LW_FM<0x3f>,
- ISA_MIPS2, NOT_ASE_CNMIPS;
+ def LWC3 : LW_FT3<"lwc3", COP3Opnd, II_LWC3, load>,
+ LW_FM<0x33>,
+ ISA_MIPS1_NOT_32R6_64R6_R5900,
+ NOT_ASE_CNMIPS;
+ def SWC3 : SW_FT3<"swc3", COP3Opnd, II_SWC3, store>,
+ LW_FM<0x3b>,
+ ISA_MIPS1_NOT_32R6_64R6_R5900,
+ NOT_ASE_CNMIPS;
+ def LDC3 : LW_FT3<"ldc3", COP3Opnd, II_LDC3, load>,
+ LW_FM<0x37>,
+ ISA_MIPS2_NOT_R5900,
+ NOT_ASE_CNMIPS;
+ def SDC3 : SW_FT3<"sdc3", COP3Opnd, II_SDC3, store>,
+ LW_FM<0x3f>,
+ ISA_MIPS2_NOT_R5900,
+ NOT_ASE_CNMIPS;
}
def SYNC : MMRel, StdMMR6Rel, SYNC_FT<"sync">, SYNC_FM, ISA_MIPS2;
@@ -2232,8 +2268,14 @@ let AdditionalPredicates = [NotInMicroMips] in {
let AdditionalPredicates = [NotInMicroMips] in {
/// Load-linked, Store-conditional
-def LL : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_32, ISA_MIPS2_NOT_32R6_64R6;
-def SC : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_32, ISA_MIPS2_NOT_32R6_64R6;
+def LL : LLBase<"ll", GPR32Opnd>,
+ LW_FM<0x30>,
+ PTR_32,
+ ISA_MIPS2_NOT_32R6_64R6_R5900;
+def SC : SCBase<"sc", GPR32Opnd>,
+ LW_FM<0x38>,
+ PTR_32,
+ ISA_MIPS2_NOT_32R6_64R6_R5900;
}
/// Jump and Branch Instructions
let AdditionalPredicates = [NotInMicroMips, RelocNotPIC] in
diff --git a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
index 0906ab56e1671..10885d0b01376 100644
--- a/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsSEISelLowering.cpp
@@ -235,10 +235,19 @@ MipsSETargetLowering::MipsSETargetLowering(const MipsTargetMachine &TM,
if (Subtarget.hasCnMips())
setOperationAction(ISD::MUL, MVT::i64, Legal);
- else if (Subtarget.isGP64bit())
+ else if (Subtarget.isR5900()) {
+ // R5900 doesn't have DMULT/DMULTU/DDIV/DDIVU - expand to 32-bit ops
+ setOperationAction(ISD::MUL, MVT::i64, Expand);
+ setOperationAction(ISD::SMUL_LOHI, MVT::i64, Expand);
+ setOperationAction(ISD::UMUL_LOHI, MVT::i64, Expand);
+ setOperationAction(ISD::MULHS, MVT::i64, Expand);
+ setOperationAction(ISD::MULHU, MVT::i64, Expand);
+ setOperationAction(ISD::SDIVREM, MVT::i64, Expand);
+ setOperationAction(ISD::UDIVREM, MVT::i64, Expand);
+ } else if (Subtarget.isGP64bit())
setOperationAction(ISD::MUL, MVT::i64, Custom);
- if (Subtarget.isGP64bit()) {
+ if (Subtarget.isGP64bit() && !Subtarget.isR5900()) {
setOperationAction(ISD::SMUL_LOHI, MVT::i64, Custom);
setOperationAction(ISD::UMUL_LOHI, MVT::i64, Custom);
setOperationAction(ISD::MULHS, MVT::i64, Custom);
diff --git a/llvm/test/MC/Mips/r5900-invalid.s b/llvm/test/MC/Mips/r5900-invalid.s
new file mode 100644
index 0000000000000..4b1eed2f56bcc
--- /dev/null
+++ b/llvm/test/MC/Mips/r5900-invalid.s
@@ -0,0 +1,59 @@
+# Instructions that are invalid on R5900
+#
+# R5900 is a MIPS III-based processor with specific limitations:
+# - No 64-bit multiply/divide (DMULT/DMULTU/DDIV/DDIVU)
+# - No LL/SC atomic instructions
+# - No COP3 instructions (LWC3, SWC3, LDC3, SDC3)
+#
+# RUN: not llvm-mc %s -triple=mips64el-unknown-linux -mcpu=r5900 2>%t1
+# RUN: FileCheck %s < %t1
+
+ .set noat
+
+# =============================================================================
+# MIPS3 64-bit multiply/divide instructions that R5900 does NOT support
+# =============================================================================
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ dmult $4, $5
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ dmultu $6, $7
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ ddiv $zero, $8, $9
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ ddivu $zero, $10, $11
+
+# =============================================================================
+# LL/SC atomic instructions that R5900 does NOT support
+# =============================================================================
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ ll $4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ sc $4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ lld $4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ scd $4, 0($5)
+
+# =============================================================================
+# COP3 instructions that R5900 does NOT support
+# =============================================================================
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ lwc3 $4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ swc3 $4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ ldc3 $4, 0($5)
+
+# CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: instruction requires a CPU feature not currently enabled
+ sdc3 $4, 0($5)
>From 038de93597eb2d1b5b0ef40e5e41d9054c2675e1 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 18 Jan 2026 13:24:29 +0100
Subject: [PATCH 3/7] [Mips] Add r5900 short loop delay slot fix
---
llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp | 79 +++++++++++++++++++-
llvm/test/CodeGen/Mips/r5900-short-loop.ll | 59 +++++++++++++++
2 files changed, 134 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/Mips/r5900-short-loop.ll
diff --git a/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp b/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
index 850d3b59be5de..da15423853559 100644
--- a/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
+++ b/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
@@ -52,6 +52,8 @@ using namespace llvm;
STATISTIC(FilledSlots, "Number of delay slots filled");
STATISTIC(UsefulSlots, "Number of delay slots filled with instructions that"
" are not NOP.");
+STATISTIC(R5900ShortLoopNops, "Number of delay slots left as NOP for R5900 "
+ "short loop fix");
static cl::opt<bool> DisableDelaySlotFiller(
"disable-mips-delay-filler",
@@ -278,6 +280,65 @@ static bool hasUnoccupiedSlot(const MachineInstr *MI) {
return MI->hasDelaySlot() && !MI->isBundledWithSucc();
}
+/// Check if a branch is a short backward loop that triggers the R5900 erratum.
+/// The erratum occurs when:
+/// - A loop has 6 or fewer instructions (including branch + delay slot)
+/// - The branch is a conditional backward branch
+/// - The delay slot is not NOP
+/// - No other branches exist in the loop body
+static bool isR5900ShortLoopBranch(const MachineInstr *MI,
+ const MachineBasicBlock &MBB) {
+ // Must be a conditional branch (not jump or indirect branch)
+ if (!MI->isBranch() || MI->isIndirectBranch())
+ return false;
+
+ // Check if this is a conditional branch by looking for an MBB operand
+ const MachineBasicBlock *TargetMBB = nullptr;
+ for (const MachineOperand &MO : MI->operands()) {
+ if (MO.isMBB()) {
+ TargetMBB = MO.getMBB();
+ break;
+ }
+ }
+
+ // Must have a target and must target the same basic block (backward branch)
+ if (!TargetMBB || TargetMBB != &MBB)
+ return false;
+
+ // Count instructions from the beginning of the block to the branch
+ // A short loop is 6 instructions or fewer (including branch + delay slot)
+ // The delay slot adds 1 more, so we check if instructions before branch <= 5
+ unsigned InstrCount = 0;
+ bool HasOtherBranch = false;
+
+ for (const MachineInstr &Instr : MBB) {
+ if (&Instr == MI)
+ break;
+
+ // Skip debug and pseudo instructions
+ if (Instr.isDebugInstr() || Instr.isTransient())
+ continue;
+
+ ++InstrCount;
+
+ // If there's another branch in the loop, the erratum doesn't apply
+ if (Instr.isBranch() || Instr.isCall()) {
+ HasOtherBranch = true;
+ break;
+ }
+ }
+
+ // If there's another branch/call in the loop, erratum doesn't apply
+ if (HasOtherBranch)
+ return false;
+
+ // Add 1 for the branch itself, +1 for delay slot = InstrCount + 2
+ // Erratum triggers when total <= 6, so InstrCount + 2 <= 6 => InstrCount <= 4
+ // But we're conservative: if InstrCount <= 5 (total <= 7), skip filling
+ // to match the exact condition from r5900check: offset -5 to -1 (2-6 instrs)
+ return InstrCount <= 5;
+}
+
INITIALIZE_PASS(MipsDelaySlotFiller, DEBUG_TYPE,
"Fill delay slot for MIPS", false, false)
@@ -587,10 +648,20 @@ bool MipsDelaySlotFiller::runOnMachineBasicBlock(MachineBasicBlock &MBB) {
if (!hasUnoccupiedSlot(&*I))
continue;
- // Delay slot filling is disabled at -O0, or in microMIPS32R6.
- if (!DisableDelaySlotFiller &&
- (TM->getOptLevel() != CodeGenOptLevel::None) &&
- !(InMicroMipsMode && STI.hasMips32r6())) {
+ // R5900 short loop erratum fix: skip delay slot filling for short backward
+ // loops to avoid triggering a hardware bug where short loops may exit
+ // early.
+ if (STI.isR5900() && isR5900ShortLoopBranch(&*I, MBB)) {
+ LLVM_DEBUG(dbgs() << DEBUG_TYPE ": skipping delay slot fill for R5900 "
+ "short loop branch.\n");
+ ++R5900ShortLoopNops;
+ // Fall through to insert NOP in delay slot
+ }
+ // Delay slot filling is disabled at -O0, in microMIPS32R6, or for R5900
+ // short loop branches.
+ else if (!DisableDelaySlotFiller &&
+ (TM->getOptLevel() != CodeGenOptLevel::None) &&
+ !(InMicroMipsMode && STI.hasMips32r6())) {
bool Filled = false;
diff --git a/llvm/test/CodeGen/Mips/r5900-short-loop.ll b/llvm/test/CodeGen/Mips/r5900-short-loop.ll
new file mode 100644
index 0000000000000..a66f417f0c854
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/r5900-short-loop.ll
@@ -0,0 +1,59 @@
+; RUN: llc -mtriple=mips64el -mcpu=r5900 < %s | FileCheck %s -check-prefix=FIX
+;
+; Test R5900 short loop erratum fix.
+; The R5900 has a hardware bug where short loops (6 instructions or fewer)
+; with a branch may exit after 1-2 iterations instead of the expected count.
+; The fix ensures the delay slot contains a NOP for such short backward branches.
+; The fix is always enabled for R5900 - there is no way to disable it.
+
+; Short loop test with store - delay slot NOT filled due to short loop fix
+; bnez followed by nop (short loop workaround)
+
+; FIX-LABEL: test_short_loop_store:
+; FIX: .LBB0_1:
+; FIX: bnez
+; FIX-NEXT: nop
+define void @test_short_loop_store(ptr %arr, i32 %n) {
+entry:
+ br label %loop
+
+loop:
+ %i = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %ptr = getelementptr i32, ptr %arr, i32 %i
+ store i32 %i, ptr %ptr
+ %inc = add i32 %i, 1
+ %cmp = icmp slt i32 %inc, %n
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; Long loop - should NOT be affected by the fix (too many instructions)
+; Delay slot can be filled since loop is longer than 6 instructions
+; The scheduler may choose different instructions for the delay slot
+; FIX-LABEL: test_long_loop:
+; FIX: .LBB1_1:
+; FIX: bnez ${{[0-9]+}}, .LBB1_1
+; Delay slot should be filled with actual instruction, not nop
+; FIX-NEXT: {{sw|daddiu|addu}}
+define i32 @test_long_loop(ptr %arr, i32 %n) {
+entry:
+ br label %loop
+
+loop:
+ %i = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %sum = phi i32 [ 0, %entry ], [ %add, %loop ]
+ %ptr = getelementptr i32, ptr %arr, i32 %i
+ %val = load i32, ptr %ptr
+ %mul = mul i32 %val, %i
+ %add = add i32 %sum, %mul
+ %ptr2 = getelementptr i32, ptr %arr, i32 %add
+ store i32 %add, ptr %ptr2
+ %inc = add i32 %i, 1
+ %cmp = icmp slt i32 %inc, %n
+ br i1 %cmp, label %loop, label %exit
+
+exit:
+ ret i32 %add
+}
>From e80982d27cfc71d04a420371a5e2d1ce0fdfa4f4 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 18 Jan 2026 13:25:11 +0100
Subject: [PATCH 4/7] [Mips] Set ISA extension AFL_EXT_5900 for r5900
Set the ISA extension field in .MIPS.abiflags to AFL_EXT_5900 when
targeting the R5900, matching binutils behavior. This marks object
files as compiled for the R5900, enabling linker compatibility checking.
Also rename the display name in llvm-readobj from "MIPS R5900" to
"Toshiba R5900" to match binutils readelf output.
---
llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h | 2 ++
llvm/test/CodeGen/Mips/cpus.ll | 1 +
llvm/tools/llvm-readobj/ELFDumper.cpp | 6 ++++--
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h b/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h
index 1a5bb64863ee8..f7b2fa5537d31 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsABIFlagsSection.h
@@ -143,6 +143,8 @@ struct MipsABIFlagsSection {
ISAExtension = Mips::AFL_EXT_OCTEONP;
else if (P.hasCnMips())
ISAExtension = Mips::AFL_EXT_OCTEON;
+ else if (P.isR5900())
+ ISAExtension = Mips::AFL_EXT_5900;
else
ISAExtension = Mips::AFL_EXT_NONE;
}
diff --git a/llvm/test/CodeGen/Mips/cpus.ll b/llvm/test/CodeGen/Mips/cpus.ll
index 14e7476d8f9ef..05d9a89660b7b 100644
--- a/llvm/test/CodeGen/Mips/cpus.ll
+++ b/llvm/test/CodeGen/Mips/cpus.ll
@@ -61,6 +61,7 @@
; RUN: llc -mtriple=mips64el -mcpu=r5900 -filetype=obj < %s \
; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=R5900
; R5900: ISA: MIPS3
+; R5900: ISA Extension: Toshiba R5900
; Check that we reject CPUs that are not implemented.
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 96c4668984965..0189c04bd1f03 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -3326,6 +3326,7 @@ MipsGOTParser<ELFT>::getPltSym(const Entry *E) const {
}
}
+// clang-format off
const EnumEntry<unsigned> ElfMipsISAExtType[] = {
{"None", Mips::AFL_EXT_NONE},
{"Broadcom SB-1", Mips::AFL_EXT_SB1},
@@ -3338,7 +3339,6 @@ const EnumEntry<unsigned> ElfMipsISAExtType[] = {
{"Loongson 2F", Mips::AFL_EXT_LOONGSON_2F},
{"Loongson 3A", Mips::AFL_EXT_LOONGSON_3A},
{"MIPS R4650", Mips::AFL_EXT_4650},
- {"MIPS R5900", Mips::AFL_EXT_5900},
{"MIPS R10000", Mips::AFL_EXT_10000},
{"NEC VR4100", Mips::AFL_EXT_4100},
{"NEC VR4111/VR4181", Mips::AFL_EXT_4111},
@@ -3346,8 +3346,10 @@ const EnumEntry<unsigned> ElfMipsISAExtType[] = {
{"NEC VR5400", Mips::AFL_EXT_5400},
{"NEC VR5500", Mips::AFL_EXT_5500},
{"RMI Xlr", Mips::AFL_EXT_XLR},
- {"Toshiba R3900", Mips::AFL_EXT_3900}
+ {"Toshiba R3900", Mips::AFL_EXT_3900},
+ {"Toshiba R5900", Mips::AFL_EXT_5900},
};
+// clang-format on
const EnumEntry<unsigned> ElfMipsASEFlags[] = {
{"DSP", Mips::AFL_ASE_DSP},
>From 1bbe1086a9268df7e620add679d6f3910ac25525 Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 18 Jan 2026 13:22:18 +0100
Subject: [PATCH 5/7] [clang][Mips] Add missing i6400 and i6500 to CPU test
The test for valid MIPS CPU names was missing entries for i6400 and
i6500, which are valid CPUs defined in LLVM.
---
clang/test/Misc/target-invalid-cpu-note/mips.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/test/Misc/target-invalid-cpu-note/mips.c b/clang/test/Misc/target-invalid-cpu-note/mips.c
index 17b6ff9c57084..e9d21b2253485 100644
--- a/clang/test/Misc/target-invalid-cpu-note/mips.c
+++ b/clang/test/Misc/target-invalid-cpu-note/mips.c
@@ -23,4 +23,6 @@
// CHECK-SAME: {{^}}, octeon
// CHECK-SAME: {{^}}, octeon+
// CHECK-SAME: {{^}}, p5600
+// CHECK-SAME: {{^}}, i6400
+// CHECK-SAME: {{^}}, i6500
// CHECK-SAME: {{$}}
>From 92358eb1733b6876cabc62314086a6920e583aae Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Sun, 18 Jan 2026 15:53:34 +0100
Subject: [PATCH 6/7] [clang][Mips] Add r5900 CPU support
---
clang/lib/Basic/Targets/Mips.cpp | 4 +++-
clang/test/Misc/target-invalid-cpu-note/mips.c | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Basic/Targets/Mips.cpp b/clang/lib/Basic/Targets/Mips.cpp
index a999d1410d254..76c9081567053 100644
--- a/clang/lib/Basic/Targets/Mips.cpp
+++ b/clang/lib/Basic/Targets/Mips.cpp
@@ -46,6 +46,7 @@ bool MipsTargetInfo::processorSupportsGPR64() const {
.Case("mips64r6", true)
.Case("octeon", true)
.Case("octeon+", true)
+ .Case("r5900", true)
.Case("i6400", true)
.Case("i6500", true)
.Default(false);
@@ -55,7 +56,8 @@ static constexpr llvm::StringLiteral ValidCPUNames[] = {
{"mips1"}, {"mips2"}, {"mips3"}, {"mips4"}, {"mips5"},
{"mips32"}, {"mips32r2"}, {"mips32r3"}, {"mips32r5"}, {"mips32r6"},
{"mips64"}, {"mips64r2"}, {"mips64r3"}, {"mips64r5"}, {"mips64r6"},
- {"octeon"}, {"octeon+"}, {"p5600"}, {"i6400"}, {"i6500"}};
+ {"octeon"}, {"octeon+"}, {"p5600"}, {"r5900"}, {"i6400"},
+ {"i6500"}};
bool MipsTargetInfo::isValidCPUName(StringRef Name) const {
return llvm::is_contained(ValidCPUNames, Name);
diff --git a/clang/test/Misc/target-invalid-cpu-note/mips.c b/clang/test/Misc/target-invalid-cpu-note/mips.c
index e9d21b2253485..738362edf3c80 100644
--- a/clang/test/Misc/target-invalid-cpu-note/mips.c
+++ b/clang/test/Misc/target-invalid-cpu-note/mips.c
@@ -23,6 +23,7 @@
// CHECK-SAME: {{^}}, octeon
// CHECK-SAME: {{^}}, octeon+
// CHECK-SAME: {{^}}, p5600
+// CHECK-SAME: {{^}}, r5900
// CHECK-SAME: {{^}}, i6400
// CHECK-SAME: {{^}}, i6500
// CHECK-SAME: {{$}}
>From d9c25fc42d5cc10a05fc860d500aa3705f29a05b Mon Sep 17 00:00:00 2001
From: Rick Gaiser <rgaiser at gmail.com>
Date: Fri, 23 Jan 2026 09:04:00 +0100
Subject: [PATCH 7/7] [Mips] Add -mfix-r5900 option
---
clang/include/clang/Options/Options.td | 4 ++++
clang/lib/Driver/ToolChains/Arch/Mips.cpp | 2 ++
llvm/lib/Target/Mips/Mips.td | 6 +++++-
llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp | 4 ++--
llvm/lib/Target/Mips/MipsSubtarget.cpp | 2 +-
llvm/lib/Target/Mips/MipsSubtarget.h | 4 ++++
llvm/test/CodeGen/Mips/r5900-short-loop.ll | 17 ++++++++++++++---
7 files changed, 32 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 188739e72434a..1ce92c06bdfa5 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5932,6 +5932,10 @@ def mcheck_zero_division : Flag<["-"], "mcheck-zero-division">,
def mno_check_zero_division : Flag<["-"], "mno-check-zero-division">,
Group<m_mips_Features_Group>;
def mfix4300 : Flag<["-"], "mfix4300">, Group<m_mips_Features_Group>;
+def mfix_r5900 : Flag<["-"], "mfix-r5900">, Group<m_mips_Features_Group>,
+ HelpText<"Enable R5900 short loop erratum fix">;
+def mno_fix_r5900 : Flag<["-"], "mno-fix-r5900">, Group<m_mips_Features_Group>,
+ HelpText<"Disable R5900 short loop erratum fix">;
def mcompact_branches_EQ : Joined<["-"], "mcompact-branches=">,
Group<m_mips_Features_Group>;
} // let Flags = [TargetSpecific]
diff --git a/clang/lib/Driver/ToolChains/Arch/Mips.cpp b/clang/lib/Driver/ToolChains/Arch/Mips.cpp
index 103aae7018fbf..b72d654291658 100644
--- a/clang/lib/Driver/ToolChains/Arch/Mips.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/Mips.cpp
@@ -388,6 +388,8 @@ void mips::getMIPSTargetFeatures(const Driver &D, const llvm::Triple &Triple,
"virt");
AddTargetFeature(Args, Features, options::OPT_mginv, options::OPT_mno_ginv,
"ginv");
+ AddTargetFeature(Args, Features, options::OPT_mfix_r5900,
+ options::OPT_mno_fix_r5900, "fix-r5900");
if (Arg *A = Args.getLastArg(options::OPT_mindirect_jump_EQ)) {
StringRef Val = StringRef(A->getValue());
diff --git a/llvm/lib/Target/Mips/Mips.td b/llvm/lib/Target/Mips/Mips.td
index 7cfa0800454c6..2f3ec5d051a79 100644
--- a/llvm/lib/Target/Mips/Mips.td
+++ b/llvm/lib/Target/Mips/Mips.td
@@ -186,9 +186,13 @@ def FeatureCnMipsP : SubtargetFeature<"cnmipsp", "HasCnMipsP",
"true", "Octeon+ cnMIPS Support",
[FeatureCnMips]>;
+def FeatureFixR5900 : SubtargetFeature<"fix-r5900", "FixR5900", "true",
+ "Enable R5900 short loop erratum fix">;
+
def FeatureR5900 : SubtargetFeature<"r5900", "IsR5900", "true",
"R5900 (PS2 Emotion Engine) Support",
- [FeatureMips3, FeatureSoftFloat]>;
+ [FeatureMips3, FeatureSoftFloat,
+ FeatureFixR5900]>;
def FeatureUseTCCInDIV : SubtargetFeature<
"use-tcc-in-div",
diff --git a/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp b/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
index da15423853559..f572ae38c8ab7 100644
--- a/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
+++ b/llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
@@ -650,8 +650,8 @@ bool MipsDelaySlotFiller::runOnMachineBasicBlock(MachineBasicBlock &MBB) {
// R5900 short loop erratum fix: skip delay slot filling for short backward
// loops to avoid triggering a hardware bug where short loops may exit
- // early.
- if (STI.isR5900() && isR5900ShortLoopBranch(&*I, MBB)) {
+ // early. The fix can be controlled with -mfix-r5900 / -mno-fix-r5900.
+ if (STI.fixR5900() && isR5900ShortLoopBranch(&*I, MBB)) {
LLVM_DEBUG(dbgs() << DEBUG_TYPE ": skipping delay slot fill for R5900 "
"short loop branch.\n");
++R5900ShortLoopNops;
diff --git a/llvm/lib/Target/Mips/MipsSubtarget.cpp b/llvm/lib/Target/Mips/MipsSubtarget.cpp
index 5134e16a8d78b..75f7a6c6a1118 100644
--- a/llvm/lib/Target/Mips/MipsSubtarget.cpp
+++ b/llvm/lib/Target/Mips/MipsSubtarget.cpp
@@ -86,7 +86,7 @@ MipsSubtarget::MipsSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
IsSingleFloat(false), IsFPXX(false), NoABICalls(false), Abs2008(false),
IsFP64bit(false), UseOddSPReg(true), IsNaN2008bit(false),
IsGP64bit(false), HasVFPU(false), HasCnMips(false), HasCnMipsP(false),
- IsR5900(false), HasMips3_32(false), HasMips3_32r2(false),
+ IsR5900(false), FixR5900(false), HasMips3_32(false), HasMips3_32r2(false),
HasMips4_32(false), HasMips4_32r2(false), HasMips5_32r2(false),
InMips16Mode(false), InMips16HardFloat(Mips16HardFloat),
InMicroMipsMode(false), HasDSP(false), HasDSPR2(false), HasDSPR3(false),
diff --git a/llvm/lib/Target/Mips/MipsSubtarget.h b/llvm/lib/Target/Mips/MipsSubtarget.h
index 3c8b088421208..9d2f4d3c2d458 100644
--- a/llvm/lib/Target/Mips/MipsSubtarget.h
+++ b/llvm/lib/Target/Mips/MipsSubtarget.h
@@ -127,6 +127,9 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
// IsR5900 - CPU is R5900 (PlayStation 2 Emotion Engine).
bool IsR5900;
+ // FixR5900 - Enable R5900 short loop erratum fix.
+ bool FixR5900;
+
// isLinux - Target system is Linux. Is false we consider ELFOS for now.
bool IsLinux;
@@ -301,6 +304,7 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
bool hasCnMips() const { return HasCnMips; }
bool hasCnMipsP() const { return HasCnMipsP; }
bool isR5900() const { return IsR5900; }
+ bool fixR5900() const { return FixR5900; }
bool isLittle() const { return IsLittle; }
bool isABICalls() const { return !NoABICalls; }
diff --git a/llvm/test/CodeGen/Mips/r5900-short-loop.ll b/llvm/test/CodeGen/Mips/r5900-short-loop.ll
index a66f417f0c854..2904cfa21e075 100644
--- a/llvm/test/CodeGen/Mips/r5900-short-loop.ll
+++ b/llvm/test/CodeGen/Mips/r5900-short-loop.ll
@@ -1,18 +1,24 @@
; RUN: llc -mtriple=mips64el -mcpu=r5900 < %s | FileCheck %s -check-prefix=FIX
+; RUN: llc -mtriple=mips64el -mcpu=r5900 -mattr=-fix-r5900 < %s | FileCheck %s -check-prefix=NOFIX
;
; Test R5900 short loop erratum fix.
; The R5900 has a hardware bug where short loops (6 instructions or fewer)
; with a branch may exit after 1-2 iterations instead of the expected count.
; The fix ensures the delay slot contains a NOP for such short backward branches.
-; The fix is always enabled for R5900 - there is no way to disable it.
-; Short loop test with store - delay slot NOT filled due to short loop fix
-; bnez followed by nop (short loop workaround)
+; Short loop test with store - delay slot can be filled when fix is disabled
+; With fix enabled: bnez followed by nop
+; With fix disabled: bnez followed by daddiu (delay slot filled)
; FIX-LABEL: test_short_loop_store:
; FIX: .LBB0_1:
; FIX: bnez
; FIX-NEXT: nop
+
+; NOFIX-LABEL: test_short_loop_store:
+; NOFIX: .LBB0_1:
+; NOFIX: bnez
+; NOFIX-NEXT: daddiu
define void @test_short_loop_store(ptr %arr, i32 %n) {
entry:
br label %loop
@@ -37,6 +43,11 @@ exit:
; FIX: bnez ${{[0-9]+}}, .LBB1_1
; Delay slot should be filled with actual instruction, not nop
; FIX-NEXT: {{sw|daddiu|addu}}
+
+; NOFIX-LABEL: test_long_loop:
+; NOFIX: .LBB1_1:
+; NOFIX: bnez ${{[0-9]+}}, .LBB1_1
+; NOFIX-NEXT: {{sw|daddiu|addu}}
define i32 @test_long_loop(ptr %arr, i32 %n) {
entry:
br label %loop
More information about the cfe-commits
mailing list