[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