[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:
parent
828a867ee0
commit
905bb5bddb
@ -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
|
||||
//
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user