[llvm] [Mips] Custom lowering of SET_ROUNDING, GET_ROUNDING (PR #170047)

Erik Enikeev via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 9 07:08:34 PST 2025


https://github.com/Varnike updated https://github.com/llvm/llvm-project/pull/170047

>From 3e9f5c33cd710ec316d705d36da77ef83022c7ab Mon Sep 17 00:00:00 2001
From: Erik Enikeev <evonatarius at gmail.com>
Date: Fri, 28 Nov 2025 18:31:35 +0300
Subject: [PATCH] [Mips] Custom lowering of SET_ROUNDING, GET_ROUNDING

---
 llvm/lib/Target/Mips/MicroMipsInstrFPU.td |  2 +
 llvm/lib/Target/Mips/MipsISelLowering.cpp | 66 +++++++++++++++++++++++
 llvm/lib/Target/Mips/MipsISelLowering.h   |  2 +
 llvm/lib/Target/Mips/MipsInstrFPU.td      | 28 ++++++++++
 llvm/lib/Target/Mips/MipsSEInstrInfo.cpp  | 29 ++++++++++
 llvm/lib/Target/Mips/MipsSEInstrInfo.h    |  4 ++
 llvm/test/CodeGen/Mips/frounds.ll         | 58 ++++++++++++++++++++
 7 files changed, 189 insertions(+)
 create mode 100644 llvm/test/CodeGen/Mips/frounds.ll

diff --git a/llvm/lib/Target/Mips/MicroMipsInstrFPU.td b/llvm/lib/Target/Mips/MicroMipsInstrFPU.td
index d5fc30cef695c..67d4c82a32ef4 100644
--- a/llvm/lib/Target/Mips/MicroMipsInstrFPU.td
+++ b/llvm/lib/Target/Mips/MicroMipsInstrFPU.td
@@ -445,6 +445,8 @@ def : MipsPat<(MipsTruncIntFP FGR64Opnd:$src),
               FGR_64;
 def : MipsPat<(MipsTruncIntFP FGR32Opnd:$src),
               (TRUNC_W_S_MM FGR32Opnd:$src)>, ISA_MICROMIPS32_NOT_MIPS32R6;
+def PseudoReadFCSR_MM : ReadFCSRImpl<[FCR31]>;
+def PseudoWriteFCSR_MM : WriteFCSRImpl<[FCR31]>;
 
 // Selects
 defm : MovzPats0<GPR32, FGR32, MOVZ_I_S_MM, SLT_MM, SLTu_MM, SLTi_MM, SLTiu_MM>,
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index d518fb65e7d3a..f293e0ba076c4 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -236,6 +236,8 @@ MipsTargetLowering::MipsTargetLowering(const MipsTargetMachine &TM,
   setOperationAction(ISD::FP_TO_SINT,         MVT::i32,   Custom);
   setOperationAction(ISD::STRICT_FP_TO_SINT,  MVT::i32,   Custom);
   setOperationAction(ISD::STRICT_FP_TO_UINT,  MVT::i32,   Custom);
+  setOperationAction(ISD::GET_ROUNDING,       MVT::i32,   Custom);
+  setOperationAction(ISD::SET_ROUNDING,       MVT::Other, Custom);
 
   setOperationAction(ISD::STRICT_FSETCC, MVT::f32, Custom);
   setOperationAction(ISD::STRICT_FSETCCS, MVT::f32, Custom);
@@ -1250,6 +1252,8 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) const
   case ISD::STRICT_FP_TO_UINT:
     return lowerSTRICT_FP_TO_INT(Op, DAG);
   case ISD::FP_TO_SINT:         return lowerFP_TO_SINT(Op, DAG);
+  case ISD::GET_ROUNDING:       return lowerGET_ROUNDING(Op, DAG);
+  case ISD::SET_ROUNDING:       return lowerSET_ROUNDING(Op, DAG);
   case ISD::READCYCLECOUNTER:
     return lowerREADCYCLECOUNTER(Op, DAG);
   }
@@ -2889,6 +2893,68 @@ static SDValue lowerFP_TO_SINT_STORE(StoreSDNode *SD, SelectionDAG &DAG,
                       SD->getMemOperand()->getFlags());
 }
 
+SDValue MipsTargetLowering::lowerGET_ROUNDING(SDValue Op,
+                                             SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  SDValue Chain = Op.getOperand(0);
+  
+  // Use formula: 
+  //   ((FCSR & 0x3) ^ ((~FCSR & 0x3) >> 1)) 
+  // to transform Mips rounding mode value stored in bits 1:0 of FCSR 
+  // (FCR31) to LLVM rounding mode format: 1->0, 0->1, 2->2, 3->3.
+  SDValue FCSR = DAG.getNode(MipsISD::ReadFCSR, DL, {MVT::i32, MVT::Other}, Chain);
+  Chain = FCSR.getValue(1);
+  FCSR = FCSR.getValue(0);
+
+  SDValue Expr1 = DAG.getNode(ISD::AND, DL, MVT::i32,
+              FCSR, DAG.getConstant(3, DL, MVT::i32));
+  SDValue Expr2 =
+  DAG.getNode(ISD::SRL, DL, MVT::i32,
+              DAG.getNode(ISD::AND, DL, MVT::i32,
+                          DAG.getNode(ISD::XOR, DL, MVT::i32,
+                                      FCSR, DAG.getConstant(3, DL, MVT::i32)),
+                          DAG.getConstant(3, DL, MVT::i32)),
+              DAG.getConstant(1, DL, MVT::i32));
+
+  SDValue RM = DAG.getNode(ISD::XOR, DL, MVT::i32, Expr1, Expr2);
+
+  return DAG.getMergeValues({RM, Chain}, DL);
+}
+
+SDValue MipsTargetLowering::lowerSET_ROUNDING(SDValue Op,
+                                             SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  SDValue Chain = Op->getOperand(0);
+  SDValue RMValue = Op->getOperand(1);
+
+  // Use formula x ^ (~(x >> 1) & 1) to transform LLVM rounding mode to Mips 
+  // FCSR (FCR31) format: 0->1, 1->0, 2->2, 3->3.
+  //
+  // It is expected that the argument of llvm.set.rounding is within the
+  // segment [0, 3], so NearestTiesToAway (4) is not handled here. It is
+  // responsibility of the code generated llvm.set.rounding to ensure this
+  // condition.
+  SDValue One = DAG.getConstant(1, DL, MVT::i32);
+  SDValue NewRM = DAG.getNode(
+      ISD::XOR, DL, MVT::i32, RMValue,
+      DAG.getNode(ISD::AND, DL, MVT::i32,
+                  DAG.getNOT(DL,
+                             DAG.getNode(ISD::SRL, DL, MVT::i32, RMValue, One),
+                             MVT::i32),
+                  One));
+
+  // Put calculated rounding mode into FCSR[1:0]: 
+  //  FCSR = (FCSR & FFFFFFFC) | NewRM
+  SDValue FCSR = DAG.getNode(MipsISD::ReadFCSR, DL, {MVT::i32, MVT::Other}, Chain);
+  Chain = FCSR.getValue(1);
+  FCSR = FCSR.getValue(0);
+
+  FCSR = DAG.getNode(ISD::AND, DL, MVT::i32, FCSR, DAG.getConstant(0xFFFFFFFC, DL, MVT::i32));
+  FCSR = DAG.getNode(ISD::OR, DL, MVT::i32, FCSR, NewRM);
+
+  return DAG.getNode(MipsISD::WriteFCSR, DL, MVT::Other, Chain, FCSR);
+}
+
 SDValue MipsTargetLowering::lowerSTORE(SDValue Op, SelectionDAG &DAG) const {
   StoreSDNode *SD = cast<StoreSDNode>(Op);
   EVT MemVT = SD->getMemoryVT();
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h
index 7d79b8d753bd4..70d92611f398d 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.h
+++ b/llvm/lib/Target/Mips/MipsISelLowering.h
@@ -359,6 +359,8 @@ class TargetRegisterClass;
     SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const;
     SDValue lowerSETCC(SDValue Op, SelectionDAG &DAG) const;
     SDValue lowerFSETCC(SDValue Op, SelectionDAG &DAG) const;
+    SDValue lowerGET_ROUNDING(SDValue Op, SelectionDAG &DAG) const;
+    SDValue lowerSET_ROUNDING(SDValue Op, SelectionDAG &DAG) const;
     SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const;
     SDValue lowerVAARG(SDValue Op, SelectionDAG &DAG) const;
     SDValue lowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/lib/Target/Mips/MipsInstrFPU.td b/llvm/lib/Target/Mips/MipsInstrFPU.td
index 8827027f12c95..b7462ac361b4d 100644
--- a/llvm/lib/Target/Mips/MipsInstrFPU.td
+++ b/llvm/lib/Target/Mips/MipsInstrFPU.td
@@ -41,6 +41,9 @@ def SDT_MipsExtractElementF64 : SDTypeProfile<1, 2, [SDTCisVT<0, i32>,
 def SDT_MipsMTC1_D64 : SDTypeProfile<1, 1, [SDTCisVT<0, f64>,
                                             SDTCisVT<1, i32>]>;
 
+def SDT_MipsReadFCSR  : SDTypeProfile<1, 0, [SDTCisInt<0>]>;
+def SDT_MipsWriteFCSR : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
+
 // Floating Point Compare
 def MipsFPCmp : SDNode<"MipsISD::FPCmp", SDT_MipsFPCmp, [SDNPOutGlue]>;
 
@@ -62,6 +65,10 @@ def MipsExtractElementF64 : SDNode<"MipsISD::ExtractElementF64",
 
 // Node used to generate an MTC1 i32 to f64 instruction
 def MipsMTC1_D64 : SDNode<"MipsISD::MTC1_D64", SDT_MipsMTC1_D64>;
+def MipsReadFCSR : SDNode<"MipsISD::ReadFCSR", SDT_MipsReadFCSR,
+                             [SDNPHasChain]>;
+def MipsWriteFCSR : SDNode<"MipsISD::WriteFCSR", SDT_MipsWriteFCSR,
+                              [SDNPHasChain, SDNPSideEffect]>;
 
 // Operand for printing out a condition code.
 let PrintMethod = "printFCCOperand", DecoderMethod = "DecodeCondCode" in
@@ -796,6 +803,27 @@ class ExtractElementF64Base<RegisterOperand RO> :
 def ExtractElementF64 : ExtractElementF64Base<AFGR64Opnd>, FGR_32, HARDFLOAT;
 def ExtractElementF64_64 : ExtractElementF64Base<FGR64Opnd>, FGR_64, HARDFLOAT;
 
+class ReadFCSRImpl<list<Register> Regs> : 
+  PseudoSE<(outs GPR32Opnd:$rd), (ins),
+          [(set GPR32Opnd:$rd, (MipsReadFCSR))]> {
+  let Uses = Regs;
+  let hasSideEffects = 0;
+  let hasNoSchedulingInfo = 1;
+}
+
+class WriteFCSRImpl<list<Register> Regs> : 
+  PseudoSE<(outs), (ins GPR32Opnd:$val),
+           [(MipsWriteFCSR GPR32Opnd:$val)]> {
+  let Defs = Regs;
+  let hasSideEffects = 1;
+  let hasNoSchedulingInfo = 1;
+}
+
+let AdditionalPredicates = [NotInMicroMips] in {
+  def PseudoReadFCSR : ReadFCSRImpl<[FCR31]>;
+  def PseudoWriteFCSR : WriteFCSRImpl<[FCR31]>;
+}
+
 def PseudoTRUNC_W_S : MipsAsmPseudoInst<(outs FGR32Opnd:$fd),
                                         (ins FGR32Opnd:$fs, GPR32Opnd:$rs),
                                         "trunc.w.s\t$fd, $fs, $rs">;
diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp
index a1d0aa089c089..a5df5201c2efd 100644
--- a/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp
+++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.cpp
@@ -444,6 +444,18 @@ bool MipsSEInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
   case Mips::MIPSeh_return64:
     expandEhReturn(MBB, MI);
     break;
+  case Mips::PseudoReadFCSR:
+    expandReadFCSR(MBB, MI, Mips::CFC1);
+    break;
+  case Mips::PseudoWriteFCSR:
+    expandWriteFCSR(MBB, MI, Mips::CTC1);
+    break;
+  case Mips::PseudoReadFCSR_MM:
+    expandReadFCSR(MBB, MI, Mips::CFC1_MM);
+    break;
+  case Mips::PseudoWriteFCSR_MM:
+    expandWriteFCSR(MBB, MI, Mips::CTC1_MM);
+    break;
   }
 
   MBB.erase(MI);
@@ -877,6 +889,23 @@ void MipsSEInstrInfo::expandEhReturn(MachineBasicBlock &MBB,
   expandRetRA(MBB, I);
 }
 
+void MipsSEInstrInfo::expandReadFCSR(MachineBasicBlock &MBB,
+                                     MachineBasicBlock::iterator I,
+                                     unsigned Opc) const {
+  Register Dst = I->getOperand(0).getReg();
+  BuildMI(MBB, I, I->getDebugLoc(), get(Opc), Dst)
+      .addImm(31);
+}
+
+void MipsSEInstrInfo::expandWriteFCSR(MachineBasicBlock &MBB,
+                                      MachineBasicBlock::iterator I,
+                                      unsigned Opc) const {
+  Register Src = I->getOperand(0).getReg();
+  BuildMI(MBB, I, I->getDebugLoc(), get(Opc))
+      .addImm(31)
+      .addReg(Src);
+}
+
 const MipsInstrInfo *llvm::createMipsSEInstrInfo(const MipsSubtarget &STI) {
   return new MipsSEInstrInfo(STI);
 }
diff --git a/llvm/lib/Target/Mips/MipsSEInstrInfo.h b/llvm/lib/Target/Mips/MipsSEInstrInfo.h
index 5c48ccdc27f02..a9fe9bf36ac1a 100644
--- a/llvm/lib/Target/Mips/MipsSEInstrInfo.h
+++ b/llvm/lib/Target/Mips/MipsSEInstrInfo.h
@@ -120,6 +120,10 @@ class MipsSEInstrInfo : public MipsInstrInfo {
                           bool FP64) const;
   void expandEhReturn(MachineBasicBlock &MBB,
                       MachineBasicBlock::iterator I) const;
+  void expandWriteFCSR(MachineBasicBlock &MBB,
+                       MachineBasicBlock::iterator I, unsigned Opc) const;
+  void expandReadFCSR(MachineBasicBlock &MBB,
+                      MachineBasicBlock::iterator I, unsigned Opc) const;
 };
 
 }
diff --git a/llvm/test/CodeGen/Mips/frounds.ll b/llvm/test/CodeGen/Mips/frounds.ll
new file mode 100644
index 0000000000000..06e60fb3dce20
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/frounds.ll
@@ -0,0 +1,58 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=mips -mcpu=mips32r2 < %s | FileCheck %s
+; RUN: llc -mtriple=mips -mattr=+micromips < %s | FileCheck %s --check-prefix=CHECK-MM
+
+define i32 @get_rounding() {
+; CHECK-LABEL: get_rounding:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    cfc1 $1, 31
+; CHECK-NEXT:    andi $2, $1, 3
+; CHECK-NEXT:    not $1, $1
+; CHECK-NEXT:    andi $1, $1, 2
+; CHECK-NEXT:    srl $1, $1, 1
+; CHECK-NEXT:    jr $ra
+; CHECK-NEXT:    xor $2, $2, $1
+;
+; CHECK-MM-LABEL: get_rounding:
+; CHECK-MM:       # %bb.0: # %entry
+; CHECK-MM-NEXT:    cfc1 $2, 31
+; CHECK-MM-NEXT:    andi16 $3, $2, 3
+; CHECK-MM-NEXT:    not16 $2, $2
+; CHECK-MM-NEXT:    andi16 $2, $2, 2
+; CHECK-MM-NEXT:    srl16 $2, $2, 1
+; CHECK-MM-NEXT:    xor16 $2, $3
+; CHECK-MM-NEXT:    jrc $ra
+entry:
+  %0 = call i32 @llvm.get.rounding()
+  ret i32 %0
+}
+
+define void @set_rounding(i32 %a) {
+; CHECK-LABEL: set_rounding:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addiu $1, $zero, -4
+; CHECK-NEXT:    cfc1 $2, 31
+; CHECK-NEXT:    and $1, $2, $1
+; CHECK-NEXT:    andi $2, $4, 2
+; CHECK-NEXT:    sltiu $2, $2, 1
+; CHECK-NEXT:    xor $2, $4, $2
+; CHECK-NEXT:    or $1, $1, $2
+; CHECK-NEXT:    ctc1 $1, 31
+; CHECK-NEXT:    jr $ra
+; CHECK-NEXT:    nop
+;
+; CHECK-MM-LABEL: set_rounding:
+; CHECK-MM:       # %bb.0: # %entry
+; CHECK-MM-NEXT:    addiu $2, $zero, -4
+; CHECK-MM-NEXT:    cfc1 $3, 31
+; CHECK-MM-NEXT:    and16 $3, $2
+; CHECK-MM-NEXT:    andi16 $2, $4, 2
+; CHECK-MM-NEXT:    sltiu $2, $2, 1
+; CHECK-MM-NEXT:    xor16 $2, $4
+; CHECK-MM-NEXT:    or16 $2, $3
+; CHECK-MM-NEXT:    ctc1 $2, 31
+; CHECK-MM-NEXT:    jrc $ra
+entry:
+  call void @llvm.set.rounding(i32 %a)
+  ret void
+}



More information about the llvm-commits mailing list