diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -20106,6 +20106,9 @@ These functions read or write floating point environment, such as rounding mode or state of floating point exceptions. Altering the floating point environment requires special care. See :ref:`Floating Point Environment `. +Functions that access floating point environment are treated as having side effect: +they are ordered with other functions that depend on the environment, including +constrained FP intrinsics. '``llvm.flt.rounds``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20171,6 +20174,100 @@ modes. +'``llvm.get.fpmode``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. The overloaded type is an integer type used by +particular target to represent floating-point control modes. + +:: + + declare @llvm.get.fpmode.() + +Overview: +""""""""" + +The '``llvm.get.fpmode``' intrinsic reads floating-point control modes. + +Arguments: +"""""""""" + +None. + +Semantics: +"""""""""" + +The '``llvm.get.fpmode``' intrinsic reads the current floating-point control +modes, such as rounding direction, precision, treatment of denormals and so on. +It is similar to 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: +""""""" + +This is an overloaded intrinsic. The overloaded type is an integer type used by +particular target to represent floating-point control modes. + +:: + + declare void @llvm.set.fpmode.( ) + +Overview: +""""""""" + +The '``llvm.set.fpmode``' intrinsic sets 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 +call to '``llvm.get.fpmode``' or constructed in a target-specific way. It is +similar to 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. + + 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 @@ -891,6 +891,19 @@ /// FSINCOS - Compute both fsin and fcos as a single operation. FSINCOS, + /// Reads the current dynamic floating-point control modes. The operand is + /// the token chain. + GET_FPMODE, + + /// Sets the current dynamic floating-point control modes. The first operand + /// is the token chain, the second is control modes set represented as integer + /// value. + SET_FPMODE, + + /// Sets default dynamic floating-point control modes. The operand is the + /// 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/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -2315,6 +2315,18 @@ return CreateFCmpHelper(P, LHS, RHS, Name, FPMathTag, true); } + /// Create call to the get_fpmode intrinsic. + CallInst *createGetFPMode(const Twine &Name = ""); + + /// Create call to the set_fpmode intrinsic. + /// \param Mode Set of floating point modes. + CallInst *createSetFPMode(Value *Mode, const Twine &Name = ""); + + /// Create call to the reset_fpmode intrinsic. + CallInst *createResetFPMode(const Twine &Name = "") { + return CreateIntrinsic(Intrinsic::reset_fpmode, {}, {}, nullptr, Name); + } + private: // Helper routine to create either a signaling or a quiet FP comparison. Value *CreateFCmpHelper(CmpInst::Predicate P, Value *LHS, Value *RHS, 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 @@ -697,6 +697,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_fpmode : DefaultAttrsIntrinsic<[llvm_anyint_ty], []>; + def int_set_fpmode : DefaultAttrsIntrinsic<[], [llvm_anyint_ty]>; + def int_reset_fpmode : DefaultAttrsIntrinsic<[], []>; } //===--------------- Constrained Floating Point Intrinsics ----------------===// 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(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/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 @@ -187,6 +187,9 @@ SDValue ExpandInsertToVectorThroughStack(SDValue Op); SDValue ExpandVectorBuildThroughStack(SDNode* Node); + SDValue makeStateFunctionCall(const SDLoc &DL, RTLIB::Libcall LC, + SDValue Chain, SDValue MemOp); + SDValue ExpandConstantFP(ConstantFPSDNode *CFP, bool UseCP); SDValue ExpandConstant(ConstantSDNode *CP); @@ -1186,6 +1189,10 @@ Action = TLI.getOperationAction( Node->getOpcode(), Node->getOperand(1).getValueType()); break; + case ISD::SET_FPMODE: + Action = TLI.getOperationAction(Node->getOpcode(), + Node->getOperand(1).getValueType()); + break; default: if (Node->getOpcode() >= ISD::BUILTIN_OP_END) { Action = TargetLowering::Legal; @@ -1472,6 +1479,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 state. Return value of the library function is ignored. +/// +/// \param DL Debug location of the new call. +/// \param LC Identifies library function. +/// \param Chain Ingoing token chain. +/// \param MemOp Memory operand provided as an argument for the function call. +/// \returns Outgoing token chain. +/// +SDValue SelectionDAGLegalize::makeStateFunctionCall(const SDLoc &DL, + RTLIB::Libcall LC, + SDValue Chain, + SDValue MemOp) { + 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(DL).setChain(Chain).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. @@ -4486,6 +4522,46 @@ break; } break; + case ISD::GET_FPMODE: { + // Call fegetmode, which saves control modes 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(SDLoc(Node), RTLIB::FEGETMODE, + Node->getOperand(0), 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_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( + makeStateFunctionCall(SDLoc(Node), RTLIB::FESETMODE, StInst, StackPtr)); + break; + } + case ISD::RESET_FPMODE: { + // It is legalized to call 'fesetmode(FE_DFL_MODE)'. 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 Mode = DAG.getConstant(-1LL, dl, MVT::getVT(IntTy)); + Results.push_back(makeStateFunctionCall(SDLoc(Node), RTLIB::FESETMODE, + Node->getOperand(0), Mode)); + 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 @@ -6325,6 +6325,25 @@ setValue(&I, Res); DAG.setRoot(Res.getValue(0)); 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 @@ -415,6 +415,9 @@ // Floating point environment manipulation case ISD::FLT_ROUNDS_: return "flt_rounds"; case ISD::SET_ROUNDING: return "set_rounding"; + 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 @@ -898,6 +898,13 @@ 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::i8, MVT::i16, MVT::i32, MVT::i64}) + setOperationAction(ISD::GET_FPMODE, VT, Expand); + for (MVT VT : {MVT::i8, MVT::i16, MVT::i32, MVT::i64}) + setOperationAction(ISD::SET_FPMODE, VT, Expand); + setOperationAction(ISD::RESET_FPMODE, MVT::Other, Expand); } MVT TargetLoweringBase::getScalarShiftAmountTy(const DataLayout &DL, diff --git a/llvm/lib/IR/IRBuilder.cpp b/llvm/lib/IR/IRBuilder.cpp --- a/llvm/lib/IR/IRBuilder.cpp +++ b/llvm/lib/IR/IRBuilder.cpp @@ -912,6 +912,21 @@ return C; } +CallInst *IRBuilderBase::createGetFPMode(const Twine &Name) { + assert(BB && BB->getParent() && "No current function!"); + Module *M = BB->getModule(); + const DataLayout &DL = M->getDataLayout(); + unsigned FPModeSize = DL.getFPControlModeSize(); + assert(FPModeSize > 0 && "ControlModesSize is not defined in DataLayout"); + auto ModeTy = IntegerType::get(M->getContext(), FPModeSize); + return CreateIntrinsic(Intrinsic::get_fpmode, {ModeTy}, {}, nullptr, Name); +} + +CallInst *IRBuilderBase::createSetFPMode(Value *Mode, const Twine &Name) { + return CreateIntrinsic(Intrinsic::set_fpmode, {Mode->getType()}, {Mode}, + nullptr, Name); +} + Value *IRBuilderBase::CreateSelect(Value *C, Value *True, Value *False, const Twine &Name, Instruction *MDFrom) { if (auto *CC = dyn_cast(C)) 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,54 @@ +; RUN: llc -mtriple=msp430 -o - < %s | FileCheck %s + +; This test checks default lowering of the intrinsics operating floating point +; environment. MSP430 is used as a target in this test because it does not have +; native FP support, so it hopefully won't get custom lowering for these +; intrinsics. +; +; REQUIRES: msp430-registered-target + +target datalayout = "e-p:16:16:16-i8:8:8-i16:16:16-i32:16:32-n8:16" +target triple = "msp430---elf" + + +define i16 @func_01() { +entry: + %fpenv = call i16 @llvm.get.fpmode.i16() + ret i16 %fpenv +} +; CHECK-LABEL: func_01: +; CHECK: sub #2, r1 +; CHECK-NEXT: mov r1, r12 +; CHECK-NEXT: call #fegetmode +; CHECK-NEXT: mov 0(r1), r12 +; CHECK-NEXT: add #2, r1 +; CHECK-NEXT: ret + + +define void @func_02(i16 %fpenv) { +entry: + call void @llvm.set.fpmode.i16(i16 %fpenv) + ret void +} +; CHECK-LABEL: func_02: +; CHECK: sub #2, r1 +; CHECK-NEXT: mov r12, 0(r1) +; CHECK-NEXT: mov r1, r12 +; CHECK-NEXT: call #fesetmode +; CHECK-NEXT: add #2, r1 +; CHECK-NEXT: ret + + +define void @func_03() { +entry: + call void @llvm.reset.fpmode() + ret void +} +; CHECK-LABEL: func_03: +; CHECK: mov #-1, r12 +; CHECK-NEXT: call #fesetmode +; CHECK-NEXT: ret + +declare i16 @llvm.get.fpmode.i16() +declare void @llvm.set.fpmode.i16(i16 %fpenv) +declare void @llvm.reset.fpmode() 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 @@ -995,4 +995,26 @@ 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; + + M->setDataLayout("S32-fe:64"); + + Call = Builder.createResetFPMode(); + II = cast(Call); + EXPECT_EQ(Intrinsic::reset_fpmode, II->getIntrinsicID()); + + Call = Builder.createGetFPMode(); + II = cast(Call); + EXPECT_EQ(Intrinsic::get_fpmode, II->getIntrinsicID()); + EXPECT_TRUE(isa(Call->getType())); + EXPECT_EQ(64U, cast(Call->getType())->getBitWidth()); + + Call = Builder.createSetFPMode(Call); + II = cast(Call); + EXPECT_EQ(Intrinsic::set_fpmode, II->getIntrinsicID()); +} }