[RISCV][FPEnv] Lowering of fpmode intrinsics (#148569)

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.
This commit is contained in:
Serge Pavlov 2025-07-16 16:02:15 +07:00 committed by GitHub
parent 828a867ee0
commit 905bb5bddb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 159 additions and 0 deletions

View File

@ -494,6 +494,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
//

View File

@ -655,6 +655,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,
@ -8225,6 +8228,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:
@ -14002,6 +14011,54 @@ 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);
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();

View File

@ -563,6 +563,9 @@ private:
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;

View File

@ -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

View File

@ -35,3 +35,37 @@ 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: 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
}