diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -25569,6 +25569,101 @@ 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 @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( ) + +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 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -1004,6 +1004,19 @@ /// 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 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1073,6 +1073,9 @@ 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 --- a/llvm/include/llvm/IR/RuntimeLibcalls.def +++ b/llvm/include/llvm/IR/RuntimeLibcalls.def @@ -294,6 +294,10 @@ 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 --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -176,6 +176,12 @@ 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_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 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -1007,6 +1007,7 @@ 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 @@ 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(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(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 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6665,6 +6665,25 @@ 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 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -439,6 +439,9 @@ 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 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -942,6 +942,12 @@ 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 --- a/llvm/test/CodeGen/X86/fpenv.ll +++ b/llvm/test/CodeGen/X86/fpenv.ll @@ -7,6 +7,9 @@ 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 @@ 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@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@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@PLT +; X64-NEXT: popq %rax +; X64-NEXT: retq +entry: + call void @llvm.reset.fpmode() + ret void +} + attributes #0 = { nounwind "use-soft-float"="true" }