[llvm] 6862f0f - [FPEnv] Intrinsics for access to FP control modes
Serge Pavlov via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 24 01:52:54 PDT 2023
Author: Serge Pavlov
Date: 2023-08-24T15:52:19+07:00
New Revision: 6862f0fab12176c7e4cdc095409deea7c26dde04
URL: https://github.com/llvm/llvm-project/commit/6862f0fab12176c7e4cdc095409deea7c26dde04
DIFF: https://github.com/llvm/llvm-project/commit/6862f0fab12176c7e4cdc095409deea7c26dde04.diff
LOG: [FPEnv] Intrinsics for access to FP control modes
The change introduces intrinsics 'get_fpmode', 'set_fpmode' and
'reset_fpmode'. They manage all target dynamic floating-point control
modes, which include, for instance, rounding direction, precision,
treatment of denormals and so on. The intrinsics do the same
operations as the C library functions 'fegetmode' and 'fesetmode'. By
default they are lowered to calls to these functions.
Two main use cases are supported by this implementation.
1. Local modification of the control modes. In this case the code
usually has a pattern (in pseudocode):
saved_modes = get_fpmode()
set_fpmode(<new_modes>)
...
<do operations under the new modes>
...
set_fpmode(saved_modes)
In the case when it is known that the current FP environment is default,
the code may be shorter:
set_fpmode(<new_modes>)
...
<do operations under the new modes>
...
reset_fpmode()
Such patterns appear not only in user code but also in implementations
of various FP controlling pragmas. In particular, the implementation of
`#pragma STDC FENV_ROUND` requires similar code if the target does not
support static rounding mode.
2. Portable control of FP modes. Usually FP control modes are set by
writing to some control register. Different targets have different
layout of this register, the way the register is accessed also may be
different. Using set of target-specific definitions for the control
register bits together with these intrinsic functions provides enough
portable way to handle control modes across wide range of hardware.
This change defines only llvm intrinsic function, which implement the
access required for the aforementioned use cases.
Differential Revision: https://reviews.llvm.org/D82525
Added:
Modified:
llvm/docs/LangRef.rst
llvm/include/llvm/CodeGen/ISDOpcodes.h
llvm/include/llvm/IR/Intrinsics.td
llvm/include/llvm/IR/RuntimeLibcalls.def
llvm/include/llvm/Target/TargetSelectionDAG.td
llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
llvm/lib/CodeGen/TargetLoweringBase.cpp
llvm/test/CodeGen/X86/fpenv.ll
Removed:
################################################################################
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 59631fd988f95a..9aed930e1c81b2 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -25569,6 +25569,101 @@ to default state. It is similar to the call 'fesetenv(FE_DFL_ENV)', except it
does not return any value.
+'``llvm.get.fpmode``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+The '``llvm.get.fpmode``' intrinsic returns bits of the current floating-point
+control modes. The return value type is platform-specific.
+
+::
+
+ declare <integer_type> @llvm.get.fpmode()
+
+Overview:
+"""""""""
+
+The '``llvm.get.fpmode``' intrinsic reads the current dynamic floating-point
+control modes and returns it as an integer value.
+
+Arguments:
+""""""""""
+
+None.
+
+Semantics:
+""""""""""
+
+The '``llvm.get.fpmode``' intrinsic reads the current dynamic floating-point
+control modes, such as rounding direction, precision, treatment of denormals and
+so on. It is similar to the C library function 'fegetmode', however this
+function does not store the set of control modes into memory but returns it as
+an integer value. Interpretation of the bits in this value is target-dependent.
+
+'``llvm.set.fpmode``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+The '``llvm.set.fpmode``' intrinsic sets the current floating-point control modes.
+
+::
+
+ declare void @llvm.set.fpmode(<integer_type> <val>)
+
+Overview:
+"""""""""
+
+The '``llvm.set.fpmode``' intrinsic sets the current dynamic floating-point
+control modes.
+
+Arguments:
+""""""""""
+
+The argument is a set of floating-point control modes, represented as an integer
+value in a target-dependent way.
+
+Semantics:
+""""""""""
+
+The '``llvm.set.fpmode``' intrinsic sets the current dynamic floating-point
+control modes to the state specified by the argument, which must be obtained by
+a call to '``llvm.get.fpmode``' or constructed in a target-specific way. It is
+similar to the C library function 'fesetmode', however this function does not
+read the set of control modes from memory but gets it as integer value.
+
+'``llvm.reset.fpmode``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+ declare void @llvm.reset.fpmode()
+
+Overview:
+"""""""""
+
+The '``llvm.reset.fpmode``' intrinsic sets the default dynamic floating-point
+control modes.
+
+Arguments:
+""""""""""
+
+None.
+
+Semantics:
+""""""""""
+
+The '``llvm.reset.fpmode``' intrinsic sets the current dynamic floating-point
+environment to default state. It is similar to the C library function call
+'fesetmode(FE_DFL_MODE)', however this function does not return any value.
+
+
Floating-Point Test Intrinsics
------------------------------
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index 45ff99b9c973e9..8876a6966a58d2 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1004,6 +1004,19 @@ enum NodeType {
/// from. The result is a token chain.
SET_FPENV_MEM,
+ /// Reads the current dynamic floating-point control modes. The operand is
+ /// a token chain.
+ GET_FPMODE,
+
+ /// Sets the current dynamic floating-point control modes. The first operand
+ /// is a token chain, the second is control modes set represented as integer
+ /// value.
+ SET_FPMODE,
+
+ /// Sets default dynamic floating-point control modes. The operand is a
+ /// token chain.
+ RESET_FPMODE,
+
/// LOAD and STORE have token chains as their first operand, then the same
/// operands as an LLVM load/store instruction, then an offset node that
/// is added / subtracted from the base pointer to form the address (for
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index ce68e0e1e47597..696b17901a0fcf 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1073,6 +1073,9 @@ let IntrProperties = [IntrInaccessibleMemOnly, IntrWillReturn] in {
def int_get_fpenv : DefaultAttrsIntrinsic<[llvm_anyint_ty], []>;
def int_set_fpenv : DefaultAttrsIntrinsic<[], [llvm_anyint_ty]>;
def int_reset_fpenv : DefaultAttrsIntrinsic<[], []>;
+ def int_get_fpmode : DefaultAttrsIntrinsic<[llvm_anyint_ty], []>;
+ def int_set_fpmode : DefaultAttrsIntrinsic<[], [llvm_anyint_ty]>;
+ def int_reset_fpmode : DefaultAttrsIntrinsic<[], []>;
}
//===--------------- Floating Point Properties ----------------------------===//
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def
index 228cb0aecb8803..6816d19993d3db 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.def
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.def
@@ -294,6 +294,10 @@ HANDLE_LIBCALL(FREXP_PPCF128, "frexpl")
HANDLE_LIBCALL(FEGETENV, "fegetenv")
HANDLE_LIBCALL(FESETENV, "fesetenv")
+// Floating point control modes
+HANDLE_LIBCALL(FEGETMODE, "fegetmode")
+HANDLE_LIBCALL(FESETMODE, "fesetmode")
+
// Conversion
HANDLE_LIBCALL(FPEXT_F32_PPCF128, "__gcc_stoq")
HANDLE_LIBCALL(FPEXT_F64_PPCF128, "__gcc_dtoq")
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index c9024c8fa82632..6ef59cf47b839c 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -176,6 +176,12 @@ def SDTFPToIntSatOp : SDTypeProfile<1, 2, [ // fp_to_[su]int_sat
def SDTFPExpOp : SDTypeProfile<1, 2, [ // ldexp
SDTCisSameAs<0, 1>, SDTCisFP<0>, SDTCisInt<2>
]>;
+def SDTGetFPStateOp : SDTypeProfile<1, 0, [ // get_fpenv, get_fpmode
+ SDTCisInt<0>
+]>;
+def SDTSetFPStateOp : SDTypeProfile<0, 1, [ // set_fpenv, set_fpmode
+ SDTCisInt<0>
+]>;
def SDTExtInreg : SDTypeProfile<1, 2, [ // sext_inreg
SDTCisSameAs<0, 1>, SDTCisInt<0>, SDTCisVT<2, OtherVT>,
SDTCisVTSmallerThanOp<2, 1>
@@ -610,6 +616,10 @@ def strict_uint_to_fp : SDNode<"ISD::STRICT_UINT_TO_FP",
def strict_fsetcc : SDNode<"ISD::STRICT_FSETCC", SDTSetCC, [SDNPHasChain]>;
def strict_fsetccs : SDNode<"ISD::STRICT_FSETCCS", SDTSetCC, [SDNPHasChain]>;
+def get_fpmode : SDNode<"ISD::GET_FPMODE", SDTGetFPStateOp, [SDNPHasChain]>;
+def set_fpmode : SDNode<"ISD::SET_FPMODE", SDTSetFPStateOp, [SDNPHasChain]>;
+def reset_fpmode : SDNode<"ISD::RESET_FPMODE", SDTNone, [SDNPHasChain]>;
+
def setcc : SDNode<"ISD::SETCC" , SDTSetCC>;
def select : SDNode<"ISD::SELECT" , SDTSelect>;
def vselect : SDNode<"ISD::VSELECT" , SDTVSelect>;
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
index 0b25118c889751..e3f8b30021e468 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
@@ -1007,6 +1007,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
Action = TLI.getOperationAction(Node->getOpcode(), MVT::Other);
break;
case ISD::SET_FPENV:
+ case ISD::SET_FPMODE:
Action = TLI.getOperationAction(Node->getOpcode(),
Node->getOperand(1).getValueType());
break;
@@ -4820,6 +4821,46 @@ void SelectionDAGLegalize::ConvertNodeToLibcall(SDNode *Node) {
DAG.makeStateFunctionCall(RTLIB::FESETENV, EnvPtr, Chain, dl));
break;
}
+ case ISD::GET_FPMODE: {
+ // Call fegetmode, which saves control modes into a stack slot. Then load
+ // the value to return from the stack.
+ EVT ModeVT = Node->getValueType(0);
+ SDValue StackPtr = DAG.CreateStackTemporary(ModeVT);
+ int SPFI = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+ SDValue Chain = DAG.makeStateFunctionCall(RTLIB::FEGETMODE, StackPtr,
+ Node->getOperand(0), dl);
+ SDValue LdInst = DAG.getLoad(
+ ModeVT, dl, Chain, StackPtr,
+ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SPFI));
+ Results.push_back(LdInst);
+ Results.push_back(LdInst.getValue(1));
+ break;
+ }
+ case ISD::SET_FPMODE: {
+ // Move control modes to stack slot and then call fesetmode with the pointer
+ // to the slot as argument.
+ SDValue Mode = Node->getOperand(1);
+ EVT ModeVT = Mode.getValueType();
+ SDValue StackPtr = DAG.CreateStackTemporary(ModeVT);
+ int SPFI = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+ SDValue StInst = DAG.getStore(
+ Node->getOperand(0), dl, Mode, StackPtr,
+ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SPFI));
+ Results.push_back(
+ DAG.makeStateFunctionCall(RTLIB::FESETMODE, StackPtr, StInst, dl));
+ break;
+ }
+ case ISD::RESET_FPMODE: {
+ // It is legalized to a call 'fesetmode(FE_DFL_MODE)'. On most targets
+ // FE_DFL_MODE is defined as '((const femode_t *) -1)' in glibc. If not, the
+ // target must provide custom lowering.
+ const DataLayout &DL = DAG.getDataLayout();
+ EVT PtrTy = TLI.getPointerTy(DL);
+ SDValue Mode = DAG.getConstant(-1LL, dl, PtrTy);
+ Results.push_back(DAG.makeStateFunctionCall(RTLIB::FESETMODE, Mode,
+ Node->getOperand(0), dl));
+ break;
+ }
}
// Replace the original node with the legalized result.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index ce5ada80987e31..938aa2c60a4d6a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -6665,6 +6665,25 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
case Intrinsic::reset_fpenv:
DAG.setRoot(DAG.getNode(ISD::RESET_FPENV, sdl, MVT::Other, getRoot()));
return;
+ case Intrinsic::get_fpmode:
+ Res = DAG.getNode(
+ ISD::GET_FPMODE, sdl,
+ DAG.getVTList(TLI.getValueType(DAG.getDataLayout(), I.getType()),
+ MVT::Other),
+ DAG.getRoot());
+ setValue(&I, Res);
+ DAG.setRoot(Res.getValue(1));
+ return;
+ case Intrinsic::set_fpmode:
+ Res = DAG.getNode(ISD::SET_FPMODE, sdl, MVT::Other, {DAG.getRoot()},
+ getValue(I.getArgOperand(0)));
+ DAG.setRoot(Res);
+ return;
+ case Intrinsic::reset_fpmode: {
+ Res = DAG.getNode(ISD::RESET_FPMODE, sdl, MVT::Other, getRoot());
+ DAG.setRoot(Res);
+ return;
+ }
case Intrinsic::pcmarker: {
SDValue Tmp = getValue(I.getArgOperand(0));
DAG.setRoot(DAG.getNode(ISD::PCMARKER, sdl, MVT::Other, getRoot(), Tmp));
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
index 03a1ead5bbb4b0..c5a269ec1b96cf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
@@ -439,6 +439,9 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
case ISD::RESET_FPENV: return "reset_fpenv";
case ISD::GET_FPENV_MEM: return "get_fpenv_mem";
case ISD::SET_FPENV_MEM: return "set_fpenv_mem";
+ case ISD::GET_FPMODE: return "get_fpmode";
+ case ISD::SET_FPMODE: return "set_fpmode";
+ case ISD::RESET_FPMODE: return "reset_fpmode";
// Bit manipulation
case ISD::ABS: return "abs";
diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp
index 68a4616fe4b833..3d87e82644836c 100644
--- a/llvm/lib/CodeGen/TargetLoweringBase.cpp
+++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp
@@ -942,6 +942,12 @@ void TargetLoweringBase::initActions() {
setOperationAction(ISD::GET_FPENV_MEM, MVT::Other, Expand);
setOperationAction(ISD::SET_FPENV_MEM, MVT::Other, Expand);
+
+ for (MVT VT : {MVT::i8, MVT::i16, MVT::i32, MVT::i64}) {
+ setOperationAction(ISD::GET_FPMODE, VT, Expand);
+ setOperationAction(ISD::SET_FPMODE, VT, Expand);
+ }
+ setOperationAction(ISD::RESET_FPMODE, MVT::Other, Expand);
}
MVT TargetLoweringBase::getScalarShiftAmountTy(const DataLayout &DL,
diff --git a/llvm/test/CodeGen/X86/fpenv.ll b/llvm/test/CodeGen/X86/fpenv.ll
index 8f6b5b65e47baf..1f01af92ca9261 100644
--- a/llvm/test/CodeGen/X86/fpenv.ll
+++ b/llvm/test/CodeGen/X86/fpenv.ll
@@ -7,6 +7,9 @@ declare void @llvm.set.rounding(i32 %x)
declare i256 @llvm.get.fpenv.i256()
declare void @llvm.set.fpenv.i256(i256 %fpenv)
declare void @llvm.reset.fpenv()
+declare i32 @llvm.get.fpmode.i32()
+declare void @llvm.set.fpmode.i32(i32 %fpmode)
+declare void @llvm.reset.fpmode()
define void @func_01() nounwind {
; X86-NOSSE-LABEL: func_01:
@@ -420,4 +423,104 @@ entry:
ret void
}
+define i32 @func_get_fpmode() #0 {
+; X86-NOSSE-LABEL: func_get_fpmode:
+; X86-NOSSE: # %bb.0: # %entry
+; X86-NOSSE-NEXT: subl $12, %esp
+; X86-NOSSE-NEXT: leal {{[0-9]+}}(%esp), %eax
+; X86-NOSSE-NEXT: movl %eax, (%esp)
+; X86-NOSSE-NEXT: calll fegetmode
+; X86-NOSSE-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NOSSE-NEXT: addl $12, %esp
+; X86-NOSSE-NEXT: retl
+;
+; X86-SSE-LABEL: func_get_fpmode:
+; X86-SSE: # %bb.0: # %entry
+; X86-SSE-NEXT: subl $12, %esp
+; X86-SSE-NEXT: leal {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT: movl %eax, (%esp)
+; X86-SSE-NEXT: calll fegetmode
+; X86-SSE-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT: addl $12, %esp
+; X86-SSE-NEXT: retl
+;
+; X64-LABEL: func_get_fpmode:
+; X64: # %bb.0: # %entry
+; X64-NEXT: pushq %rax
+; X64-NEXT: leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT: callq fegetmode at PLT
+; X64-NEXT: movl {{[0-9]+}}(%rsp), %eax
+; X64-NEXT: popq %rcx
+; X64-NEXT: retq
+entry:
+ %fpmode = call i32 @llvm.get.fpmode.i32()
+ ret i32 %fpmode
+}
+
+define void @func_set_fpmode(i32 %fpmode) #0 {
+; X86-NOSSE-LABEL: func_set_fpmode:
+; X86-NOSSE: # %bb.0: # %entry
+; X86-NOSSE-NEXT: subl $12, %esp
+; X86-NOSSE-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-NOSSE-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; X86-NOSSE-NEXT: leal {{[0-9]+}}(%esp), %eax
+; X86-NOSSE-NEXT: movl %eax, (%esp)
+; X86-NOSSE-NEXT: calll fesetmode
+; X86-NOSSE-NEXT: addl $12, %esp
+; X86-NOSSE-NEXT: retl
+;
+; X86-SSE-LABEL: func_set_fpmode:
+; X86-SSE: # %bb.0: # %entry
+; X86-SSE-NEXT: subl $12, %esp
+; X86-SSE-NEXT: movl {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT: movl %eax, {{[0-9]+}}(%esp)
+; X86-SSE-NEXT: leal {{[0-9]+}}(%esp), %eax
+; X86-SSE-NEXT: movl %eax, (%esp)
+; X86-SSE-NEXT: calll fesetmode
+; X86-SSE-NEXT: addl $12, %esp
+; X86-SSE-NEXT: retl
+;
+; X64-LABEL: func_set_fpmode:
+; X64: # %bb.0: # %entry
+; X64-NEXT: pushq %rax
+; X64-NEXT: movl %edi, {{[0-9]+}}(%rsp)
+; X64-NEXT: leaq {{[0-9]+}}(%rsp), %rdi
+; X64-NEXT: callq fesetmode at PLT
+; X64-NEXT: popq %rax
+; X64-NEXT: retq
+entry:
+ call void @llvm.set.fpmode.i32(i32 %fpmode)
+ ret void
+}
+
+
+define void @func_reset() #0 {
+; X86-NOSSE-LABEL: func_reset:
+; X86-NOSSE: # %bb.0: # %entry
+; X86-NOSSE-NEXT: subl $12, %esp
+; X86-NOSSE-NEXT: movl $-1, (%esp)
+; X86-NOSSE-NEXT: calll fesetmode
+; X86-NOSSE-NEXT: addl $12, %esp
+; X86-NOSSE-NEXT: retl
+;
+; X86-SSE-LABEL: func_reset:
+; X86-SSE: # %bb.0: # %entry
+; X86-SSE-NEXT: subl $12, %esp
+; X86-SSE-NEXT: movl $-1, (%esp)
+; X86-SSE-NEXT: calll fesetmode
+; X86-SSE-NEXT: addl $12, %esp
+; X86-SSE-NEXT: retl
+;
+; X64-LABEL: func_reset:
+; X64: # %bb.0: # %entry
+; X64-NEXT: pushq %rax
+; X64-NEXT: movq $-1, %rdi
+; X64-NEXT: callq fesetmode at PLT
+; X64-NEXT: popq %rax
+; X64-NEXT: retq
+entry:
+ call void @llvm.reset.fpmode()
+ ret void
+}
+
attributes #0 = { nounwind "use-soft-float"="true" }
More information about the llvm-commits
mailing list