[llvm] [RISCV][FPEnv] Lowering of fpmode intrinsics (PR #148569)
Serge Pavlov via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 14 22:59:57 PDT 2025
https://github.com/spavloff updated https://github.com/llvm/llvm-project/pull/148569
>From d2eb5276fc5110848d1e1e6c93dd96528d5f902a Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Thu, 10 Jul 2025 15:37:46 +0700
Subject: [PATCH 1/3] [RISCV][FPEnv] Lowering of fpmode intrinsics
The change implements custom lowering of `get_fpmode`, `set_fpmode` and
`reset_fpmode` for RISCV target. The implementation is aligned with
the functions `fegetmode` and `fesetmode` in GLIBC.
---
.../Target/RISCV/MCTargetDesc/RISCVBaseInfo.h | 11 ++++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 58 +++++++++++++++++++
llvm/lib/Target/RISCV/RISCVISelLowering.h | 3 +
llvm/lib/Target/RISCV/RISCVInstrInfo.td | 54 +++++++++++++++++
llvm/test/CodeGen/RISCV/fpenv-xlen.ll | 35 +++++++++++
5 files changed, 161 insertions(+)
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index f41ad419db1a7..29261ccb39848 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -495,6 +495,17 @@ inline static bool isValidRoundingMode(unsigned Mode) {
}
} // namespace RISCVVXRndMode
+namespace RISCVExceptFlags {
+enum ExceptionFlag {
+ NX = 0x01, // Inexact
+ UF = 0x02, // Underflow
+ OF = 0x04, // Overflow
+ DZ = 0x08, // Divide by zero
+ NV = 0x10, // Invalid operation
+ ALL = 0x1F // Mask for all accrued exception flags
+};
+}
+
//===----------------------------------------------------------------------===//
// Floating-point Immediates
//
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 7c72d074a35b6..dbc6d684f997d 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -656,6 +656,9 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::GET_FPENV, XLenVT, Custom);
setOperationAction(ISD::SET_FPENV, XLenVT, Custom);
setOperationAction(ISD::RESET_FPENV, MVT::Other, Custom);
+ setOperationAction(ISD::GET_FPMODE, XLenVT, Custom);
+ setOperationAction(ISD::SET_FPMODE, XLenVT, Custom);
+ setOperationAction(ISD::RESET_FPMODE, MVT::Other, Custom);
}
setOperationAction({ISD::GlobalAddress, ISD::BlockAddress, ISD::ConstantPool,
@@ -8226,6 +8229,12 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op,
return lowerSET_FPENV(Op, DAG);
case ISD::RESET_FPENV:
return lowerRESET_FPENV(Op, DAG);
+ case ISD::GET_FPMODE:
+ return lowerGET_FPMODE(Op, DAG);
+ case ISD::SET_FPMODE:
+ return lowerSET_FPMODE(Op, DAG);
+ case ISD::RESET_FPMODE:
+ return lowerRESET_FPMODE(Op, DAG);
case ISD::EH_DWARF_CFA:
return lowerEH_DWARF_CFA(Op, DAG);
case ISD::VP_MERGE:
@@ -13998,6 +14007,55 @@ SDValue RISCVTargetLowering::lowerRESET_FPENV(SDValue Op,
EnvValue);
}
+const uint64_t ModeMask64 = ~RISCVExceptFlags::ALL;
+const uint32_t ModeMask32 = ~RISCVExceptFlags::ALL;
+
+SDValue RISCVTargetLowering::lowerGET_FPMODE(SDValue Op,
+ SelectionDAG &DAG) const {
+ const MVT XLenVT = Subtarget.getXLenVT();
+ const uint64_t ModeMaskValue = Subtarget.is64Bit() ? ModeMask64 : ModeMask32;
+ SDLoc DL(Op);
+ SDValue Chain = Op->getOperand(0);
+ SDValue SysRegNo = DAG.getTargetConstant(RISCVSysReg::fcsr, DL, XLenVT);
+ SDValue ModeMask = DAG.getConstant(ModeMaskValue, DL, XLenVT);
+ SDVTList VTs = DAG.getVTList(XLenVT, MVT::Other);
+ SDValue Result = DAG.getNode(RISCVISD::READ_CSR, DL, VTs, Chain, SysRegNo);
+ Chain = Result.getValue(1);
+ Result = DAG.getNode(ISD::AND, DL, XLenVT, Result, ModeMask);
+ return DAG.getMergeValues({Result, Chain}, DL);
+}
+
+SDValue RISCVTargetLowering::lowerSET_FPMODE(SDValue Op,
+ SelectionDAG &DAG) const {
+ const MVT XLenVT = Subtarget.getXLenVT();
+ const uint64_t ModeMaskValue = Subtarget.is64Bit() ? ModeMask64 : ModeMask32;
+ SDLoc DL(Op);
+ SDValue Chain = Op->getOperand(0);
+ SDValue EnvValue = Op->getOperand(1);
+ SDValue SysRegNo = DAG.getTargetConstant(RISCVSysReg::fcsr, DL, XLenVT);
+ SDValue ModeMask = DAG.getConstant(ModeMaskValue, DL, XLenVT);
+
+ EnvValue = DAG.getNode(ISD::ZERO_EXTEND, DL, XLenVT, EnvValue);
+ EnvValue = DAG.getNode(ISD::AND, DL, XLenVT, EnvValue, ModeMask);
+ Chain = DAG.getNode(RISCVISD::CLEAR_CSR, DL, MVT::Other, Chain, SysRegNo,
+ ModeMask);
+ return DAG.getNode(RISCVISD::SET_CSR, DL, MVT::Other, Chain, SysRegNo,
+ EnvValue);
+}
+
+SDValue RISCVTargetLowering::lowerRESET_FPMODE(SDValue Op,
+ SelectionDAG &DAG) const {
+ const MVT XLenVT = Subtarget.getXLenVT();
+ const uint64_t ModeMaskValue = Subtarget.is64Bit() ? ModeMask64 : ModeMask32;
+ SDLoc DL(Op);
+ SDValue Chain = Op->getOperand(0);
+ SDValue SysRegNo = DAG.getTargetConstant(RISCVSysReg::fcsr, DL, XLenVT);
+ SDValue ModeMask = DAG.getConstant(ModeMaskValue, DL, XLenVT);
+
+ return DAG.getNode(RISCVISD::CLEAR_CSR, DL, MVT::Other, Chain, SysRegNo,
+ ModeMask);
+}
+
SDValue RISCVTargetLowering::lowerEH_DWARF_CFA(SDValue Op,
SelectionDAG &DAG) const {
MachineFunction &MF = DAG.getMachineFunction();
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 00e969056df7d..1963cfeca59c8 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -562,6 +562,9 @@ class RISCVTargetLowering : public TargetLowering {
SDValue lowerGET_FPENV(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerSET_FPENV(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerRESET_FPENV(SDValue Op, SelectionDAG &DAG) const;
+ SDValue lowerGET_FPMODE(SDValue Op, SelectionDAG &DAG) const;
+ SDValue lowerSET_FPMODE(SDValue Op, SelectionDAG &DAG) const;
+ SDValue lowerRESET_FPMODE(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerEH_DWARF_CFA(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerCTLZ_CTTZ_ZERO_UNDEF(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index f63531a0109b0..653607827282e 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -120,6 +120,20 @@ def riscv_swap_csr : RVSDNode<"SWAP_CSR",
SDTCisInt<2>]>,
[SDNPHasChain]>;
+// Clear bits of CSR. The first operand is the address of the required CSR,
+// the second is the bitmask of cleared bits.
+def riscv_clear_csr : RVSDNode<"CLEAR_CSR",
+ SDTypeProfile<0, 2, [SDTCisInt<0>,
+ SDTCisInt<1>]>,
+ [SDNPHasChain]>;
+
+// Set bits of CSR. The first operand is the address of the required CSR,
+// the second is the bitmask of bits to set.
+def riscv_set_csr : RVSDNode<"SET_CSR",
+ SDTypeProfile<0, 2, [SDTCisInt<0>,
+ SDTCisInt<1>]>,
+ [SDNPHasChain]>;
+
// A read of the 64-bit counter CSR on a 32-bit target (returns (Lo, Hi)).
// It takes a chain operand and another two target constant operands (the
// CSR numbers of the low and high parts of the counter).
@@ -2038,6 +2052,42 @@ class SwapSysRegImm<SysReg SR, list<Register> Regs>
let Defs = Regs;
}
+class ClearSysReg<SysReg SR, list<Register> Regs>
+ : Pseudo<(outs), (ins GPR:$val),
+ [(riscv_clear_csr (XLenVT SR.Encoding), (XLenVT GPR:$val))]>,
+ PseudoInstExpansion<(CSRRC X0, SR.Encoding, GPR:$val)> {
+ let hasSideEffects = 0;
+ let Uses = Regs;
+ let Defs = Regs;
+}
+
+class ClearSysRegImm<SysReg SR, list<Register> Regs>
+ : Pseudo<(outs), (ins uimm5:$val),
+ [(riscv_clear_csr (XLenVT SR.Encoding), uimm5:$val)]>,
+ PseudoInstExpansion<(CSRRCI X0, SR.Encoding, uimm5:$val)> {
+ let hasSideEffects = 0;
+ let Uses = Regs;
+ let Defs = Regs;
+}
+
+class SetSysReg<SysReg SR, list<Register> Regs>
+ : Pseudo<(outs), (ins GPR:$val),
+ [(riscv_set_csr (XLenVT SR.Encoding), (XLenVT GPR:$val))]>,
+ PseudoInstExpansion<(CSRRS X0, SR.Encoding, GPR:$val)> {
+ let hasSideEffects = 0;
+ let Uses = Regs;
+ let Defs = Regs;
+}
+
+class SetSysRegImm<SysReg SR, list<Register> Regs>
+ : Pseudo<(outs), (ins uimm5:$val),
+ [(riscv_set_csr (XLenVT SR.Encoding), uimm5:$val)]>,
+ PseudoInstExpansion<(CSRRSI X0, SR.Encoding, uimm5:$val)> {
+ let hasSideEffects = 0;
+ let Uses = Regs;
+ let Defs = Regs;
+}
+
def ReadFRM : ReadSysReg<SysRegFRM, [FRM]>;
let hasPostISelHook = 1 in {
def WriteFRM : WriteSysReg<SysRegFRM, [FRM]>;
@@ -2056,6 +2106,10 @@ let hasPostISelHook = 1 in {
def ReadFCSR : ReadSysReg<SysRegFCSR, [FRM, FFLAGS]>;
def WriteFCSR : WriteSysReg<SysRegFCSR, [FRM, FFLAGS]>;
def WriteFCSRImm : WriteSysRegImm<SysRegFCSR, [FRM, FFLAGS]>;
+def ClearFCSR : ClearSysReg<SysRegFCSR, [FRM, FFLAGS]>;
+def ClearFCSRImm : ClearSysRegImm<SysRegFCSR, [FRM, FFLAGS]>;
+def SetFCSR : SetSysReg<SysRegFCSR, [FRM, FFLAGS]>;
+def SetFCSRImm : SetSysRegImm<SysRegFCSR, [FRM, FFLAGS]>;
}
/// Other pseudo-instructions
diff --git a/llvm/test/CodeGen/RISCV/fpenv-xlen.ll b/llvm/test/CodeGen/RISCV/fpenv-xlen.ll
index 148186b21c125..c762f2ba41043 100644
--- a/llvm/test/CodeGen/RISCV/fpenv-xlen.ll
+++ b/llvm/test/CodeGen/RISCV/fpenv-xlen.ll
@@ -35,3 +35,38 @@ entry:
call void @llvm.reset.fpenv()
ret void
}
+
+define iXLen @func_get_fpmode() {
+; CHECK-LABEL: func_get_fpmode:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: frcsr a0
+; CHECK-NEXT: andi a0, a0, -32
+; CHECK-NEXT: ret
+entry:
+ %fpenv = call iXLen @llvm.get.fpmode.iXLen()
+ ret iXLen %fpenv
+}
+
+define void @func_set_fpmode(iXLen %fpmode) {
+; CHECK-LABEL: func_set_fpmode:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: li a1, -32
+; CHECK-NEXT: csrc fcsr, a1
+; CHECK-NEXT: andi a0, a0, -32
+; CHECK-NEXT: csrs fcsr, a0
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.set.fpmode.iXLen(iXLen %fpmode)
+ ret void
+}
+
+define void @func_reset_fpmode() {
+; CHECK-LABEL: func_reset_fpmode:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: li a0, -32
+; CHECK-NEXT: csrc fcsr, a0
+; CHECK-NEXT: ret
+entry:
+ call void @llvm.reset.fpmode()
+ ret void
+}
>From f62c7d4603dc64f0572b204ad9db7e51faa5d1de Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Tue, 15 Jul 2025 12:31:23 +0700
Subject: [PATCH 2/3] Make implementation more close to GLIBC
---
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 1 -
llvm/test/CodeGen/RISCV/fpenv-xlen.ll | 1 -
2 files changed, 2 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index dbc6d684f997d..d26eb5eac3b70 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -14021,7 +14021,6 @@ SDValue RISCVTargetLowering::lowerGET_FPMODE(SDValue Op,
SDVTList VTs = DAG.getVTList(XLenVT, MVT::Other);
SDValue Result = DAG.getNode(RISCVISD::READ_CSR, DL, VTs, Chain, SysRegNo);
Chain = Result.getValue(1);
- Result = DAG.getNode(ISD::AND, DL, XLenVT, Result, ModeMask);
return DAG.getMergeValues({Result, Chain}, DL);
}
diff --git a/llvm/test/CodeGen/RISCV/fpenv-xlen.ll b/llvm/test/CodeGen/RISCV/fpenv-xlen.ll
index c762f2ba41043..255c120434f34 100644
--- a/llvm/test/CodeGen/RISCV/fpenv-xlen.ll
+++ b/llvm/test/CodeGen/RISCV/fpenv-xlen.ll
@@ -40,7 +40,6 @@ define iXLen @func_get_fpmode() {
; CHECK-LABEL: func_get_fpmode:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: frcsr a0
-; CHECK-NEXT: andi a0, a0, -32
; CHECK-NEXT: ret
entry:
%fpenv = call iXLen @llvm.get.fpmode.iXLen()
>From 46a95d61476ac9ab177523e020e86b54faed2c43 Mon Sep 17 00:00:00 2001
From: Serge Pavlov <sepavloff at gmail.com>
Date: Tue, 15 Jul 2025 12:59:34 +0700
Subject: [PATCH 3/3] Fix clang-format errors
---
llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index 29261ccb39848..18b28f5521034 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -497,12 +497,12 @@ inline static bool isValidRoundingMode(unsigned Mode) {
namespace RISCVExceptFlags {
enum ExceptionFlag {
- NX = 0x01, // Inexact
- UF = 0x02, // Underflow
- OF = 0x04, // Overflow
- DZ = 0x08, // Divide by zero
- NV = 0x10, // Invalid operation
- ALL = 0x1F // Mask for all accrued exception flags
+ NX = 0x01, // Inexact
+ UF = 0x02, // Underflow
+ OF = 0x04, // Overflow
+ DZ = 0x08, // Divide by zero
+ NV = 0x10, // Invalid operation
+ ALL = 0x1F // Mask for all accrued exception flags
};
}
More information about the llvm-commits
mailing list