diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -23626,6 +23626,81 @@ normal value. The function never raises floating-point exceptions. +'``llvm.get.fpenv``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare @llvm.get.fpenv() + +Overview: +""""""""" + +The '``llvm.get.fpenv``' intrinsic returns bits of the current floating point +environment. The return value type is platform-specific. + +Semantics: +"""""""""" + +The '``llvm.get.fpenv``' intrinsic reads the current floating point environment +and returns it as an integer value. + + +'``llvm.set.fpenv``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare void @llvm.set.fpenv( ) + +Overview: +""""""""" + +The '``llvm.set.fpenv``' intrinsic sets the current floating point environment. + +Arguments: +"""""""""" + +The argument is an integer representing the new floating point environment. The +integer type is platform-specific. + +Semantics: +"""""""""" + +The '``llvm.set.fpenv``' intrinsic sets the current floating point environment +to the state specified by the argument. The state may be previously obtained by a +call to '``llvm.get.fpenv``' or synthesised in a platform-dependent way. + + +'``llvm.reset.fpenv``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare void @llvm.reset.fpenv() + +Overview: +""""""""" + +The '``llvm.reset.fpenv``' intrinsic sets the default floating point environment. + +Semantics: +"""""""""" + +The '``llvm.reset.fpenv``' intrinsic sets the current floating point environment +to default state. It is similar to the call 'fesetenv(FE_DFL_ENV)', except it +does not return any value. + + General 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 @@ -959,6 +959,18 @@ /// FSINCOS - Compute both fsin and fcos as a single operation. FSINCOS, + /// Gets the current floating point environment. The first operand is token + /// chain. + GET_FPENV, + + /// Sets the current floating point environment. The first operand is token + /// chain, the second is FP environment, represented by integer value. + SET_FPENV, + + /// Set floating point environment to default state. The first operand is + /// token chain. + RESET_FPENV, + /// 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/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -962,6 +962,25 @@ nullptr, Name); } + /// Create call to the get_fpenv intrinsic. + /// \param EnvTy Integer type used by target to represent floating-point + /// environment. + CallInst *createGetFPEnv(Type *EnvTy, const Twine &Name = "") { + return CreateIntrinsic(Intrinsic::get_fpenv, {EnvTy}, {}, nullptr, Name); + } + + /// Create call to the set_fpenv intrinsic. + /// \param FPEnv Pointer to Value representing the FP environment. + CallInst *createSetFPEnv(Value *FPEnv, const Twine &Name = "") { + return CreateIntrinsic(Intrinsic::set_fpenv, {FPEnv->getType()}, {FPEnv}, + nullptr, Name); + } + + /// Create call to the reset_fpenv intrinsic. + CallInst *createResetFPEnv(const Twine &Name = "") { + return CreateIntrinsic(Intrinsic::reset_fpenv, {}, {}, nullptr, Name); + } + private: /// Create a call to a masked intrinsic with given Id. CallInst *CreateMaskedIntrinsic(Intrinsic::ID Id, ArrayRef Ops, 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 @@ -746,6 +746,9 @@ let IntrProperties = [IntrInaccessibleMemOnly, IntrWillReturn] in { def int_flt_rounds : DefaultAttrsIntrinsic<[llvm_i32_ty], []>; def int_set_rounding : DefaultAttrsIntrinsic<[], [llvm_i32_ty]>; + def int_get_fpenv : DefaultAttrsIntrinsic<[llvm_anyint_ty], []>; + def int_set_fpenv : DefaultAttrsIntrinsic<[], [llvm_anyint_ty]>; + def int_reset_fpenv : 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 @@ -280,6 +280,10 @@ HANDLE_LIBCALL(LLRINT_F128, "llrintl") HANDLE_LIBCALL(LLRINT_PPCF128, "llrintl") +// Floating point environment +HANDLE_LIBCALL(FEGETENV, "fegetenv") +HANDLE_LIBCALL(FESETENV, "fesetenv") + // Conversion HANDLE_LIBCALL(FPEXT_F32_PPCF128, "__gcc_stoq") HANDLE_LIBCALL(FPEXT_F64_PPCF128, "__gcc_dtoq") 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 @@ -185,6 +185,9 @@ SDValue ExpandInsertToVectorThroughStack(SDValue Op); SDValue ExpandVectorBuildThroughStack(SDNode* Node); + SDValue makeStateFunctionCall(SDNode *Node, SDValue InChain, + RTLIB::Libcall LC, SDValue MemOp); + SDValue ExpandConstantFP(ConstantFPSDNode *CFP, bool UseCP); SDValue ExpandConstant(ConstantSDNode *CP); @@ -1524,6 +1527,35 @@ return DAG.getLoad(VT, dl, StoreChain, FIPtr, PtrInfo); } +/// Helper used to make call to a library function that has one argument of +/// pointer type. +/// Such functions include 'fegetmode', 'fesetenv' and some others, which are +/// used to get or set floating-point state. +/// \param Node DAG node to be replaced with function call. +/// \param InChain Ingoing token chain. +/// \param LC Reference to library function. +/// \param MemOP Memory used to save/load state. +/// \returns Outgoing token chain. +SDValue SelectionDAGLegalize::makeStateFunctionCall(SDNode *Node, + SDValue InChain, + RTLIB::Libcall LC, + SDValue MemOp) { + assert(InChain.getValueType() == MVT::Other && "Expected token chain"); + SDLoc DLoc(Node); + TargetLowering::ArgListTy Args; + TargetLowering::ArgListEntry Entry; + Entry.Node = MemOp; + Entry.Ty = MemOp.getValueType().getTypeForEVT(*DAG.getContext()); + Args.push_back(Entry); + SDValue Callee = DAG.getExternalSymbol(TLI.getLibcallName(LC), + TLI.getPointerTy(DAG.getDataLayout())); + TargetLowering::CallLoweringInfo CLI(DAG); + CLI.setDebugLoc(DLoc).setChain(InChain).setLibCallee( + TLI.getLibcallCallingConv(LC), Type::getVoidTy(*DAG.getContext()), Callee, + std::move(Args)); + return TLI.LowerCallTo(CLI).second; +} + /// Bitcast a floating-point value to an integer value. Only bitcast the part /// containing the sign bit if the target has no integer value capable of /// holding all bits of the floating-point value. @@ -4427,6 +4459,47 @@ break; } break; + case ISD::GET_FPENV: { + // Call fegetenv, which saves environment into stack slot. Then load + // the value to return from the stack. + SDValue StackPtr = DAG.CreateStackTemporary(Node->getValueType(0)); + int SPFI = cast(StackPtr.getNode())->getIndex(); + SDValue Chain = makeStateFunctionCall(Node, Node->getOperand(0), + RTLIB::FEGETENV, StackPtr); + SDValue LdInst = DAG.getLoad( + StackPtr.getValueType(), dl, Chain, StackPtr, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SPFI)); + Results.push_back(LdInst); + Results.push_back(LdInst.getValue(1)); + break; + } + case ISD::SET_FPENV: { + // Move environment to stack slot and then call fesetenv with the pointer + // to the slot as argument. + SDValue Chain = Node->getOperand(0); + SDValue Env = Node->getOperand(1); + EVT ModeVT = Env.getValueType(); + SDValue StackPtr = DAG.CreateStackTemporary(ModeVT); + int SPFI = cast(StackPtr.getNode())->getIndex(); + Chain = DAG.getStore( + Chain, dl, Env, StackPtr, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), SPFI)); + Results.push_back( + makeStateFunctionCall(Node, Chain, RTLIB::FESETENV, StackPtr)); + break; + } + case ISD::RESET_FPENV: { + // It is legalized to call 'fesetenv(FE_DFL_ENV)'. On most targets + // FE_DFL_MODE is defined as '((const femode_t *) -1)' in glibc. + const DataLayout &DL = DAG.getDataLayout(); + Type *ByteTy = EVT(MVT::i8).getTypeForEVT(*DAG.getContext()); + auto *PtrTy = PointerType::get(ByteTy, DL.getAllocaAddrSpace()); + Type *IntTy = DL.getIntPtrType(PtrTy); + SDValue Chain = Node->getOperand(0); + SDValue Env = DAG.getConstant(-1LL, dl, MVT::getVT(IntTy)); + Results.push_back(makeStateFunctionCall(Node, Chain, RTLIB::FESETENV, Env)); + break; + } } // Replace the original node with the legalized result. diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp @@ -2558,6 +2558,22 @@ case ISD::VSCALE: ExpandIntRes_VSCALE(N, Lo, Hi); break; + + case ISD::GET_FPENV: { + // The node needs custom lowering. If the size of FP environment is long + // enough (on X86 it is 256 bits), the node type is extended and + // getOperationAction unconditionally returns Expand for it. So make custom + // lowering here. + SmallVector Results; + TLI.ReplaceNodeResults(N, Results, DAG); + if (Results.empty()) + report_fatal_error("Do not know how to expand the result of this " + "operator!"); + assert(Results.size() == N->getNumValues() && + "Custom lowering returned the wrong number of results!"); + for (unsigned i = 0, e = Results.size(); i != e; ++i) + ReplaceValueWith(SDValue(N, i), Results[i]); + } } // If Lo/Hi is null, the sub-method took care of registering results etc. 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 @@ -6495,6 +6495,22 @@ setValue(&I, V); return; } + case Intrinsic::get_fpenv: + Res = DAG.getNode( + ISD::GET_FPENV, 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_fpenv: + DAG.setRoot(DAG.getNode(ISD::SET_FPENV, sdl, MVT::Other, getRoot(), + getValue(I.getArgOperand(0)))); + return; + case Intrinsic::reset_fpenv: + DAG.setRoot(DAG.getNode(ISD::RESET_FPENV, sdl, MVT::Other, getRoot())); + 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 @@ -424,6 +424,9 @@ // Floating point environment manipulation case ISD::FLT_ROUNDS_: return "flt_rounds"; case ISD::SET_ROUNDING: return "set_rounding"; + case ISD::GET_FPENV: return "get_fpenv"; + case ISD::SET_FPENV: return "set_fpenv"; + case ISD::RESET_FPENV: return "reset_fpenv"; // 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 @@ -905,6 +905,12 @@ setOperationAction(ISD::DEBUGTRAP, MVT::Other, Expand); setOperationAction(ISD::UBSANTRAP, MVT::Other, Expand); + + // FP environment operations default to expand to library calls. + for (MVT VT : {MVT::i16, MVT::i32, MVT::i64}) + setOperationAction(ISD::GET_FPENV, VT, Expand); + setOperationAction(ISD::SET_FPENV, MVT::Other, Expand); + setOperationAction(ISD::RESET_FPENV, MVT::Other, Expand); } MVT TargetLoweringBase::getScalarShiftAmountTy(const DataLayout &DL, diff --git a/llvm/test/CodeGen/Generic/fpenv.ll b/llvm/test/CodeGen/Generic/fpenv.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Generic/fpenv.ll @@ -0,0 +1,49 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=armv7-linux -o - < %s | FileCheck %s +; REQUIRES: arm-registered-target + +define i32 @func_01() { +; CHECK-LABEL: func_01: +; CHECK: @ %bb.0: @ %entry +; CHECK-NEXT: push {r11, lr} +; CHECK-NEXT: sub sp, sp, #8 +; CHECK-NEXT: add r0, sp, #4 +; CHECK-NEXT: bl fegetenv +; CHECK-NEXT: ldr r0, [sp, #4] +; CHECK-NEXT: add sp, sp, #8 +; CHECK-NEXT: pop {r11, pc} +entry: + %fpenv = call i32 @llvm.get.fpenv.i32() + ret i32 %fpenv +} + +define void @func_02(i32 %fpenv) { +; CHECK-LABEL: func_02: +; CHECK: @ %bb.0: @ %entry +; CHECK-NEXT: push {r11, lr} +; CHECK-NEXT: sub sp, sp, #8 +; CHECK-NEXT: str r0, [sp, #4] +; CHECK-NEXT: add r0, sp, #4 +; CHECK-NEXT: bl fesetenv +; CHECK-NEXT: add sp, sp, #8 +; CHECK-NEXT: pop {r11, pc} +entry: + call void @llvm.set.fpenv.i32(i32 %fpenv) + ret void +} + +define void @func_03() { +; CHECK-LABEL: func_03: +; CHECK: @ %bb.0: @ %entry +; CHECK-NEXT: push {r11, lr} +; CHECK-NEXT: mvn r0, #0 +; CHECK-NEXT: bl fesetenv +; CHECK-NEXT: pop {r11, pc} +entry: + call void @llvm.reset.fpenv() + ret void +} + +declare i32 @llvm.get.fpenv.i32() +declare void @llvm.set.fpenv.i32(i32 %fpenv) +declare void @llvm.reset.fpenv() diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp --- a/llvm/unittests/IR/IRBuilderTest.cpp +++ b/llvm/unittests/IR/IRBuilderTest.cpp @@ -1156,4 +1156,24 @@ Builder.CreateAdd(Builder.getInt32(1), Builder.getInt32(2), "add"); EXPECT_EQ(Add->getName(), "add"); } + +TEST_F(IRBuilderTest, FPEnvironment) { + IRBuilder<> Builder(BB); + CallInst *Call; + IntrinsicInst *II; + + Call = Builder.createResetFPEnv(); + II = cast(Call); + EXPECT_EQ(Intrinsic::reset_fpenv, II->getIntrinsicID()); + + Call = Builder.createGetFPEnv(Builder.getInt32Ty()); + II = cast(Call); + EXPECT_EQ(Intrinsic::get_fpenv, II->getIntrinsicID()); + EXPECT_EQ(Call->getType(), Builder.getInt32Ty()); + + AllocaInst *Var1 = Builder.CreateAlloca(Builder.getInt32Ty()); + Call = Builder.createSetFPEnv(Var1); + II = cast(Call); + EXPECT_EQ(Intrinsic::set_fpenv, II->getIntrinsicID()); +} }