Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,9 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,

setOperationAction(ISD::GET_ROUNDING, XLenVT, Custom);
setOperationAction(ISD::SET_ROUNDING, MVT::Other, Custom);
setOperationAction(ISD::GET_FPENV, XLenVT, Legal);
setOperationAction(ISD::SET_FPENV, XLenVT, Legal);
setOperationAction(ISD::RESET_FPENV, MVT::Other, Legal);
}

setOperationAction({ISD::GlobalAddress, ISD::BlockAddress, ISD::ConstantPool,
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoF.td
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,13 @@ def : LdPat<load, LW_INX, f32>;
def : StPat<store, SW_INX, GPRF32, f32>;
} // Predicates = [HasStdExtZfinx]

/// Floating-point environment
let Predicates = [HasStdExtFOrZfinx] in {
def : Pat<(XLenVT (get_fpenv)), (CSRRS SysRegFCSR.Encoding, (XLenVT X0))>;
def : Pat<(set_fpenv (XLenVT GPR:$rs)), (CSRRW SysRegFCSR.Encoding, GPR:$rs)>;
def : Pat<(reset_fpenv), (CSRRW SysRegFCSR.Encoding, (XLenVT X0))>;
}

let Predicates = [HasStdExtF, IsRV32] in {
// Moves (no conversion)
def : Pat<(bitconvert (i32 GPR:$rs1)), (FMV_W_X GPR:$rs1)>;
Expand Down
35 changes: 35 additions & 0 deletions llvm/test/CodeGen/RISCV/fpenv-xlen.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+f -verify-machineinstrs | FileCheck %s
; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+f -verify-machineinstrs | FileCheck %s
; RUN: sed 's/iXLen/i32/g' %s | llc -mtriple=riscv32 -mattr=+zfinx -verify-machineinstrs | FileCheck %s
; RUN: sed 's/iXLen/i64/g' %s | llc -mtriple=riscv64 -mattr=+zfinx -verify-machineinstrs | FileCheck %s

define iXLen @func_get_fpenv() {
; CHECK-LABEL: func_get_fpenv:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: frcsr a0
; CHECK-NEXT: ret
entry:
%fpenv = call iXLen @llvm.get.fpenv.iXLen()
ret iXLen %fpenv
}

define void @func_set_fpenv(iXLen %fpenv) {
; CHECK-LABEL: func_set_fpenv:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: fscsr a0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to decide how concerned we should be that this generates fscsr a0, a0 with -O0. The replacement of the destination with x0 to discard it is treated as an optimization.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The choice here is determined by compatibility with GLIBC, which uses this form:

https://github.com/bminor/glibc/blob/08d7243a6179d5a1f3f65a53aba1ec0803895aeb/sysdeps/riscv/fpu_control.h#L44

Copy link
Collaborator

@topperc topperc May 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gcc always uses fscsr a0. With the current patch, -O0 will use fscsr a0, a0 and -O1 and above will use fscsr a0. The one operand form implicitly uses X0 as the destination which is a special encoding to hardware that indicates that no read needs to be performed. The X0 destination is currently being set by RISCVDeadRegisterDefinitions.cpp.

To fix -O0 to always use X0 destination, we need a pseudo instruction that doesn't have a destination that we will expand to to use X0 during RISCVAsmPrinter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for explanation. The new inplementation uses custom lowering, which is also necessary to maintain correct implicit reg dependencies.

; CHECK-NEXT: ret
entry:
call void @llvm.set.fpenv.iXLen(iXLen %fpenv)
ret void
}

define void @func_reset_fpenv() {
; CHECK-LABEL: func_reset_fpenv:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: fscsr zero
; CHECK-NEXT: ret
entry:
call void @llvm.reset.fpenv()
ret void
}