Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -12722,7 +12722,7 @@ declare @llvm.experimental.constrained.fadd( , , metadata , - metadata ) + metadata ) Overview: """"""""" @@ -12759,7 +12759,7 @@ declare @llvm.experimental.constrained.fsub( , , metadata , - metadata ) + metadata ) Overview: """"""""" @@ -12796,7 +12796,7 @@ declare @llvm.experimental.constrained.fmul( , , metadata , - metadata ) + metadata ) Overview: """"""""" @@ -12833,7 +12833,7 @@ declare @llvm.experimental.constrained.fdiv( , , metadata , - metadata ) + metadata ) Overview: """"""""" @@ -12870,7 +12870,7 @@ declare @llvm.experimental.constrained.frem( , , metadata , - metadata ) + metadata ) Overview: """"""""" @@ -12899,6 +12899,461 @@ same sign as the dividend. +Constrained libm-equivalent Intrinsics +-------------------------------------- + +In addition to the basic floating point operations for which constrained +intrinsics are described above, there are constrained versions of various +operations which provide equivalent behavior to a corresponding libm function. +These intrinsics allow the precise behavior of these operations with respect to +rounding mode and exception behavior to be controlled. + +As with the basic constrained floating point intrinsics, the rounding mode +and exception behavior arguments only control the behavior of the optimizer. +They do not change the runtime floating point environment. + + +'``llvm.experimental.constrained.sqrt``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.sqrt( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.sqrt``' intrinsic returns the square root +of the specified value, returning the same value as the libm '``sqrt``' +functions would, but without setting ``errno``. + +Arguments: +"""""""""" + +The first argument and the return type are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the nonnegative square root of the specified value. +If the value is less than negative zero, a floating point exception occurs +and the the return value is architecture specific. + + +'``llvm.experimental.constrained.pow``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.pow( , , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.pow``' intrinsic returns the first operand +raised to the (positive or negative) power specified by the second operand. + +Arguments: +"""""""""" + +The first two arguments and the return value are floating point numbers of the +same type. The second argument specifies the power to which the first argument +should be raised. + +The third and fourth arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the first value raised to the second power, +returning the same values as the libm ``pow`` functions would, and +handles error conditions in the same way. + + +'``llvm.experimental.constrained.powi``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.powi( , i32 , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.powi``' intrinsic returns the first operand +raised to the (positive or negative) power specified by the second operand. The +order of evaluation of multiplications is not defined. When a vector of floating +point type is used, the second argument remains a scalar integer value. + + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. The second argument is a 32-bit signed integer specifying the power to +which the first argument should be raised. + +The third and fourth arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the first value raised to the second power with an +unspecified sequence of rounding operations. + + +'``llvm.experimental.constrained.sin``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.sin( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.sin``' intrinsic returns the sine of the +first operand. + +Arguments: +"""""""""" + +The first argument and the return type are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the sine of the specified operand, returning the +same values as the libm ``sin`` functions would, and handles error +conditions in the same way. + + +'``llvm.experimental.constrained.cos``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.cos( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.cos``' intrinsic returns the cosine of the +first operand. + +Arguments: +"""""""""" + +The first argument and the return type are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the cosine of the specified operand, returning the +same values as the libm ``cos`` functions would, and handles error +conditions in the same way. + + +'``llvm.experimental.constrained.exp``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.exp( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.exp``' intrinsic computes the base-e +exponential of the specified value. + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``exp`` functions +would, and handles error conditions in the same way. + + +'``llvm.experimental.constrained.exp2``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.exp2( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.exp2``' intrinsic computes the base-2 +exponential of the specified value. + + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``exp2`` functions +would, and handles error conditions in the same way. + + +'``llvm.experimental.constrained.log``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.log( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.log``' intrinsic computes the base-e +logarithm of the specified value. + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + + +Semantics: +"""""""""" + +This function returns the same values as the libm ``log`` functions +would, and handles error conditions in the same way. + + +'``llvm.experimental.constrained.log10``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.log10( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.log10``' intrinsic computes the base-10 +logarithm of the specified value. + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``log10`` functions +would, and handles error conditions in the same way. + + +'``llvm.experimental.constrained.log2``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.log2( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.log2``' intrinsic computes the base-2 +logarithm of the specified value. + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``log2`` functions +would, and handles error conditions in the same way. + + +'``llvm.experimental.constrained.rint``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.rint( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.rint``' intrinsic returns the first +operand rounded to the nearest integer. It may raise an inexact floating point +exception if the operand is not an integer. + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``rint`` functions +would, and handles error conditions in the same way. The rounding mode is +described, not determined, by the rounding mode argument. The actual rounding +mode is determined by the runtime floating point environment. The rounding +mode argument is only intended as information to the compiler. + + +'``llvm.experimental.constrained.nearbyint``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare + @llvm.experimental.constrained.nearbyint( , + metadata , + metadata ) + +Overview: +""""""""" + +The '``llvm.experimental.constrained.nearbyint``' intrinsic returns the first +operand rounded to the nearest integer. It will not raise an inexact floating +point exception if the operand is not an integer. + + +Arguments: +"""""""""" + +The first argument and the return value are floating point numbers of the same +type. + +The second and third arguments specify the rounding mode and exception +behavior as described above. + +Semantics: +"""""""""" + +This function returns the same values as the libm ``nearbyint`` functions +would, and handles error conditions in the same way. The rounding mode is +described, not determined, by the rounding mode argument. The actual rounding +mode is determined by the runtime floating point environment. The rounding +mode argument is only intended as information to the compiler. + + General Intrinsics ------------------ Index: include/llvm/CodeGen/ISDOpcodes.h =================================================================== --- include/llvm/CodeGen/ISDOpcodes.h +++ include/llvm/CodeGen/ISDOpcodes.h @@ -264,6 +264,14 @@ /// optimized. STRICT_FADD, STRICT_FSUB, STRICT_FMUL, STRICT_FDIV, STRICT_FREM, + /// Constrained versions of libm-equivalent floating point intrinsics. + /// These will be lowered to the equivalent non-constrained pseudo-op + /// (or expanded to the equivalent library call) before final selection. + /// They are used to limit optimizations while the DAG is being optimized. + STRICT_FSQRT, STRICT_FPOW, STRICT_FPOWI, STRICT_FSIN, STRICT_FCOS, + STRICT_FEXP, STRICT_FEXP2, STRICT_FLOG, STRICT_FLOG10, STRICT_FLOG2, + STRICT_FRINT, STRICT_FNEARBYINT, + /// FMA - Perform a * b + c with no intermediate rounding step. FMA, Index: include/llvm/CodeGen/SelectionDAG.h =================================================================== --- include/llvm/CodeGen/SelectionDAG.h +++ include/llvm/CodeGen/SelectionDAG.h @@ -1070,6 +1070,11 @@ SDNode *MorphNodeTo(SDNode *N, unsigned Opc, SDVTList VTs, ArrayRef Ops); + /// Mutate the specified strict FP node to its non-strict equivalent, + /// unlinking the node from its chain and dropping the metadata arguments. + /// The node must be a strict FP node. + SDNode *mutateStrictFPToFP(SDNode *Node); + /// These are used for target selectors to create a new node /// with specified return type(s), MachineInstr opcode, and operands. /// Index: include/llvm/CodeGen/SelectionDAGNodes.h =================================================================== --- include/llvm/CodeGen/SelectionDAGNodes.h +++ include/llvm/CodeGen/SelectionDAGNodes.h @@ -612,6 +612,32 @@ SDNodeBits.IsMemIntrinsic; } + /// Test if this node is a strict floating point pseudo-op. + bool isStrictFPOpcode() { + switch (NodeType) { + default: + return false; + case ISD::STRICT_FADD: + case ISD::STRICT_FSUB: + case ISD::STRICT_FMUL: + case ISD::STRICT_FDIV: + case ISD::STRICT_FREM: + case ISD::STRICT_FSQRT: + case ISD::STRICT_FPOW: + case ISD::STRICT_FPOWI: + case ISD::STRICT_FSIN: + case ISD::STRICT_FCOS: + case ISD::STRICT_FEXP: + case ISD::STRICT_FEXP2: + case ISD::STRICT_FLOG: + case ISD::STRICT_FLOG10: + case ISD::STRICT_FLOG2: + case ISD::STRICT_FRINT: + case ISD::STRICT_FNEARBYINT: + return true; + } + } + /// Test if this node has a post-isel opcode, directly /// corresponding to a MachineInstr opcode. bool isMachineOpcode() const { return NodeType < 0; } Index: include/llvm/IR/IntrinsicInst.h =================================================================== --- include/llvm/IR/IntrinsicInst.h +++ include/llvm/IR/IntrinsicInst.h @@ -171,6 +171,7 @@ ebStrict }; + bool isUnaryOp() const; RoundingMode getRoundingMode() const; ExceptionBehavior getExceptionBehavior() const; @@ -182,6 +183,18 @@ case Intrinsic::experimental_constrained_fmul: case Intrinsic::experimental_constrained_fdiv: case Intrinsic::experimental_constrained_frem: + case Intrinsic::experimental_constrained_sqrt: + case Intrinsic::experimental_constrained_pow: + case Intrinsic::experimental_constrained_powi: + case Intrinsic::experimental_constrained_sin: + case Intrinsic::experimental_constrained_cos: + case Intrinsic::experimental_constrained_exp: + case Intrinsic::experimental_constrained_exp2: + case Intrinsic::experimental_constrained_log: + case Intrinsic::experimental_constrained_log10: + case Intrinsic::experimental_constrained_log2: + case Intrinsic::experimental_constrained_rint: + case Intrinsic::experimental_constrained_nearbyint: return true; default: return false; } Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -489,8 +489,64 @@ LLVMMatchType<0>, llvm_metadata_ty, llvm_metadata_ty ]>; + + // These intrinsics are sensitive to the rounding mode so we need constrained + // versions of each of them. When strict rounding and exception control are + // not required the non-constrained versions of these intrinsics should be + // used. + def int_experimental_constrained_sqrt : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_powi : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_i32_ty, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_sin : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_cos : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_pow : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_log : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_log10: Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_log2 : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_exp : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_exp2 : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_rint : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; + def int_experimental_constrained_nearbyint : Intrinsic<[ llvm_anyfloat_ty ], + [ LLVMMatchType<0>, + llvm_metadata_ty, + llvm_metadata_ty ]>; } -// FIXME: Add intrinsic for fcmp, fptrunc, fpext, fptoui and fptosi. +// FIXME: Add intrinsics for fcmp, fptrunc, fpext, fptoui and fptosi. +// FIXME: Add intrinsics for fabs, copysign, floor, ceil, trunc and round? //===------------------------- Expect Intrinsics --------------------------===// Index: lib/CodeGen/SelectionDAG/LegalizeDAG.cpp =================================================================== --- lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -899,6 +899,39 @@ } } +static TargetLowering::LegalizeAction +getStrictFPOpcodeAction(const TargetLowering &TLI, unsigned Opcode, EVT VT) { + unsigned EqOpc; + switch (Opcode) { + default: llvm_unreachable("Unexpected FP pseudo-opcode"); + case ISD::STRICT_FSQRT: EqOpc = ISD::FSQRT; break; + case ISD::STRICT_FPOW: EqOpc = ISD::FPOW; break; + case ISD::STRICT_FPOWI: EqOpc = ISD::FPOWI; break; + case ISD::STRICT_FSIN: EqOpc = ISD::FSIN; break; + case ISD::STRICT_FCOS: EqOpc = ISD::FCOS; break; + case ISD::STRICT_FEXP: EqOpc = ISD::FEXP; break; + case ISD::STRICT_FEXP2: EqOpc = ISD::FEXP2; break; + case ISD::STRICT_FLOG: EqOpc = ISD::FLOG; break; + case ISD::STRICT_FLOG10: EqOpc = ISD::FLOG10; break; + case ISD::STRICT_FLOG2: EqOpc = ISD::FLOG2; break; + case ISD::STRICT_FRINT: EqOpc = ISD::FRINT; break; + case ISD::STRICT_FNEARBYINT: EqOpc = ISD::FNEARBYINT; break; + } + + auto Action = TLI.getOperationAction(EqOpc, VT); + + // We don't currently handle Custom or Promote for strict FP pseudo-ops. + // For now, we just expand for those cases. + if (Action != TargetLowering::Legal) + Action = TargetLowering::Expand; + + // ISD::FPOWI returns 'Legal' even though it should be expanded. + if (Opcode == ISD::STRICT_FPOWI && Action == TargetLowering::Legal) + Action = TargetLowering::Expand; + + return Action; +} + /// Return a legal replacement for the given operation, with all legal operands. void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { DEBUG(dbgs() << "\nLegalizing: "; Node->dump(&DAG)); @@ -1043,6 +1076,25 @@ return; } break; + case ISD::STRICT_FSQRT: + case ISD::STRICT_FPOW: + case ISD::STRICT_FPOWI: + case ISD::STRICT_FSIN: + case ISD::STRICT_FCOS: + case ISD::STRICT_FEXP: + case ISD::STRICT_FEXP2: + case ISD::STRICT_FLOG: + case ISD::STRICT_FLOG10: + case ISD::STRICT_FLOG2: + case ISD::STRICT_FRINT: + case ISD::STRICT_FNEARBYINT: + // These pseudo-ops get legalized as if they were their non-strict + // equivalent. For instance, if ISD::FSQRT is legal then ISD::STRICT_FSQRT + // is also legal, but if ISD::FSQRT requires expansion then so does + // ISD::STRICT_FSQRT. + Action = getStrictFPOpcodeAction(TLI, Node->getOpcode(), + Node->getValueType(0)); + break; default: if (Node->getOpcode() >= ISD::BUILTIN_OP_END) { @@ -2032,6 +2084,9 @@ RTLIB::Libcall Call_F80, RTLIB::Libcall Call_F128, RTLIB::Libcall Call_PPCF128) { + if (Node->isStrictFPOpcode()) + Node = DAG.mutateStrictFPToFP(Node); + RTLIB::Libcall LC; switch (Node->getSimpleValueType(0).SimpleTy) { default: llvm_unreachable("Unexpected request for libcall!"); @@ -3907,16 +3962,19 @@ RTLIB::FMAX_PPCF128)); break; case ISD::FSQRT: + case ISD::STRICT_FSQRT: Results.push_back(ExpandFPLibCall(Node, RTLIB::SQRT_F32, RTLIB::SQRT_F64, RTLIB::SQRT_F80, RTLIB::SQRT_F128, RTLIB::SQRT_PPCF128)); break; case ISD::FSIN: + case ISD::STRICT_FSIN: Results.push_back(ExpandFPLibCall(Node, RTLIB::SIN_F32, RTLIB::SIN_F64, RTLIB::SIN_F80, RTLIB::SIN_F128, RTLIB::SIN_PPCF128)); break; case ISD::FCOS: + case ISD::STRICT_FCOS: Results.push_back(ExpandFPLibCall(Node, RTLIB::COS_F32, RTLIB::COS_F64, RTLIB::COS_F80, RTLIB::COS_F128, RTLIB::COS_PPCF128)); @@ -3926,26 +3984,31 @@ ExpandSinCosLibCall(Node, Results); break; case ISD::FLOG: + case ISD::STRICT_FLOG: Results.push_back(ExpandFPLibCall(Node, RTLIB::LOG_F32, RTLIB::LOG_F64, RTLIB::LOG_F80, RTLIB::LOG_F128, RTLIB::LOG_PPCF128)); break; case ISD::FLOG2: + case ISD::STRICT_FLOG2: Results.push_back(ExpandFPLibCall(Node, RTLIB::LOG2_F32, RTLIB::LOG2_F64, RTLIB::LOG2_F80, RTLIB::LOG2_F128, RTLIB::LOG2_PPCF128)); break; case ISD::FLOG10: + case ISD::STRICT_FLOG10: Results.push_back(ExpandFPLibCall(Node, RTLIB::LOG10_F32, RTLIB::LOG10_F64, RTLIB::LOG10_F80, RTLIB::LOG10_F128, RTLIB::LOG10_PPCF128)); break; case ISD::FEXP: + case ISD::STRICT_FEXP: Results.push_back(ExpandFPLibCall(Node, RTLIB::EXP_F32, RTLIB::EXP_F64, RTLIB::EXP_F80, RTLIB::EXP_F128, RTLIB::EXP_PPCF128)); break; case ISD::FEXP2: + case ISD::STRICT_FEXP2: Results.push_back(ExpandFPLibCall(Node, RTLIB::EXP2_F32, RTLIB::EXP2_F64, RTLIB::EXP2_F80, RTLIB::EXP2_F128, RTLIB::EXP2_PPCF128)); @@ -3966,11 +4029,13 @@ RTLIB::CEIL_PPCF128)); break; case ISD::FRINT: + case ISD::STRICT_FRINT: Results.push_back(ExpandFPLibCall(Node, RTLIB::RINT_F32, RTLIB::RINT_F64, RTLIB::RINT_F80, RTLIB::RINT_F128, RTLIB::RINT_PPCF128)); break; case ISD::FNEARBYINT: + case ISD::STRICT_FNEARBYINT: Results.push_back(ExpandFPLibCall(Node, RTLIB::NEARBYINT_F32, RTLIB::NEARBYINT_F64, RTLIB::NEARBYINT_F80, @@ -3985,11 +4050,13 @@ RTLIB::ROUND_PPCF128)); break; case ISD::FPOWI: + case ISD::STRICT_FPOWI: Results.push_back(ExpandFPLibCall(Node, RTLIB::POWI_F32, RTLIB::POWI_F64, RTLIB::POWI_F80, RTLIB::POWI_F128, RTLIB::POWI_PPCF128)); break; case ISD::FPOW: + case ISD::STRICT_FPOW: Results.push_back(ExpandFPLibCall(Node, RTLIB::POW_F32, RTLIB::POW_F64, RTLIB::POW_F80, RTLIB::POW_F128, RTLIB::POW_PPCF128)); Index: lib/CodeGen/SelectionDAG/SelectionDAG.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -6542,6 +6542,63 @@ return N; } +SDNode* SelectionDAG::mutateStrictFPToFP(SDNode *Node) { + unsigned OrigOpc = Node->getOpcode(); + unsigned NewOpc; + bool IsUnary = false; + switch (OrigOpc) { + default: + llvm_unreachable("mutateStrictFPToFP called with unexpected opcode!"); + case ISD::STRICT_FADD: NewOpc = ISD::FADD; break; + case ISD::STRICT_FSUB: NewOpc = ISD::FSUB; break; + case ISD::STRICT_FMUL: NewOpc = ISD::FMUL; break; + case ISD::STRICT_FDIV: NewOpc = ISD::FDIV; break; + case ISD::STRICT_FREM: NewOpc = ISD::FREM; break; + case ISD::STRICT_FSQRT: NewOpc = ISD::FSQRT; IsUnary = true; break; + case ISD::STRICT_FPOW: NewOpc = ISD::FPOW; break; + case ISD::STRICT_FPOWI: NewOpc = ISD::FPOWI; break; + case ISD::STRICT_FSIN: NewOpc = ISD::FSIN; IsUnary = true; break; + case ISD::STRICT_FCOS: NewOpc = ISD::FCOS; IsUnary = true; break; + case ISD::STRICT_FEXP: NewOpc = ISD::FEXP; IsUnary = true; break; + case ISD::STRICT_FEXP2: NewOpc = ISD::FEXP2; IsUnary = true; break; + case ISD::STRICT_FLOG: NewOpc = ISD::FLOG; IsUnary = true; break; + case ISD::STRICT_FLOG10: NewOpc = ISD::FLOG10; IsUnary = true; break; + case ISD::STRICT_FLOG2: NewOpc = ISD::FLOG2; IsUnary = true; break; + case ISD::STRICT_FRINT: NewOpc = ISD::FRINT; IsUnary = true; break; + case ISD::STRICT_FNEARBYINT: + NewOpc = ISD::FNEARBYINT; + IsUnary = true; + break; + } + + // We're taking this node out of the chain, so we need to re-link things. + SDValue InputChain = Node->getOperand(0); + SDValue OutputChain = SDValue(Node, 1); + ReplaceAllUsesOfValueWith(OutputChain, InputChain); + + SDVTList VTs = getVTList(Node->getOperand(1).getValueType()); + SDNode *Res = nullptr; + if (IsUnary) + Res = MorphNodeTo(Node, NewOpc, VTs, { Node->getOperand(1) }); + else + Res = MorphNodeTo(Node, NewOpc, VTs, { Node->getOperand(1), + Node->getOperand(2) }); + + // MorphNodeTo can operate in two ways: if an existing node with the + // specified operands exists, it can just return it. Otherwise, it + // updates the node in place to have the requested operands. + if (Res == Node) { + // If we updated the node in place, reset the node ID. To the isel, + // this should be just like a newly allocated machine node. + Res->setNodeId(-1); + } else { + ReplaceAllUsesWith(Node, Res); + RemoveDeadNode(Node); + } + + return Res; +} + /// getMachineNode - These are used for target selectors to create a new node /// with specified return type(s), MachineInstr opcode, and operands. Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -895,7 +895,7 @@ void visitInlineAsm(ImmutableCallSite CS); const char *visitIntrinsicCall(const CallInst &I, unsigned Intrinsic); void visitTargetIntrinsic(const CallInst &I, unsigned Intrinsic); - void visitConstrainedFPIntrinsic(const CallInst &I, unsigned Intrinsic); + void visitConstrainedFPIntrinsic(const ConstrainedFPIntrinsic &FPI); void visitVAStart(const CallInst &I); void visitVAArg(const VAArgInst &I); Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5254,7 +5254,19 @@ case Intrinsic::experimental_constrained_fmul: case Intrinsic::experimental_constrained_fdiv: case Intrinsic::experimental_constrained_frem: - visitConstrainedFPIntrinsic(I, Intrinsic); + case Intrinsic::experimental_constrained_sqrt: + case Intrinsic::experimental_constrained_pow: + case Intrinsic::experimental_constrained_powi: + case Intrinsic::experimental_constrained_sin: + case Intrinsic::experimental_constrained_cos: + case Intrinsic::experimental_constrained_exp: + case Intrinsic::experimental_constrained_exp2: + case Intrinsic::experimental_constrained_log: + case Intrinsic::experimental_constrained_log10: + case Intrinsic::experimental_constrained_log2: + case Intrinsic::experimental_constrained_rint: + case Intrinsic::experimental_constrained_nearbyint: + visitConstrainedFPIntrinsic(cast(I)); return nullptr; case Intrinsic::fmuladd: { EVT VT = TLI.getValueType(DAG.getDataLayout(), I.getType()); @@ -5752,11 +5764,11 @@ } } -void SelectionDAGBuilder::visitConstrainedFPIntrinsic(const CallInst &I, - unsigned Intrinsic) { +void SelectionDAGBuilder::visitConstrainedFPIntrinsic( + const ConstrainedFPIntrinsic &FPI) { SDLoc sdl = getCurSDLoc(); unsigned Opcode; - switch (Intrinsic) { + switch (FPI.getIntrinsicID()) { default: llvm_unreachable("Impossible intrinsic"); // Can't reach here. case Intrinsic::experimental_constrained_fadd: Opcode = ISD::STRICT_FADD; @@ -5773,23 +5785,64 @@ case Intrinsic::experimental_constrained_frem: Opcode = ISD::STRICT_FREM; break; + case Intrinsic::experimental_constrained_sqrt: + Opcode = ISD::STRICT_FSQRT; + break; + case Intrinsic::experimental_constrained_pow: + Opcode = ISD::STRICT_FPOW; + break; + case Intrinsic::experimental_constrained_powi: + Opcode = ISD::STRICT_FPOWI; + break; + case Intrinsic::experimental_constrained_sin: + Opcode = ISD::STRICT_FSIN; + break; + case Intrinsic::experimental_constrained_cos: + Opcode = ISD::STRICT_FCOS; + break; + case Intrinsic::experimental_constrained_exp: + Opcode = ISD::STRICT_FEXP; + break; + case Intrinsic::experimental_constrained_exp2: + Opcode = ISD::STRICT_FEXP2; + break; + case Intrinsic::experimental_constrained_log: + Opcode = ISD::STRICT_FLOG; + break; + case Intrinsic::experimental_constrained_log10: + Opcode = ISD::STRICT_FLOG10; + break; + case Intrinsic::experimental_constrained_log2: + Opcode = ISD::STRICT_FLOG2; + break; + case Intrinsic::experimental_constrained_rint: + Opcode = ISD::STRICT_FRINT; + break; + case Intrinsic::experimental_constrained_nearbyint: + Opcode = ISD::STRICT_FNEARBYINT; + break; } const TargetLowering &TLI = DAG.getTargetLoweringInfo(); SDValue Chain = getRoot(); - SDValue Ops[3] = { Chain, getValue(I.getArgOperand(0)), - getValue(I.getArgOperand(1)) }; SmallVector ValueVTs; - ComputeValueVTs(TLI, DAG.getDataLayout(), I.getType(), ValueVTs); + ComputeValueVTs(TLI, DAG.getDataLayout(), FPI.getType(), ValueVTs); ValueVTs.push_back(MVT::Other); // Out chain SDVTList VTs = DAG.getVTList(ValueVTs); - SDValue Result = DAG.getNode(Opcode, sdl, VTs, Ops); + SDValue Result; + if (FPI.isUnaryOp()) + Result = DAG.getNode(Opcode, sdl, VTs, + { Chain, getValue(FPI.getArgOperand(0)) }); + else + Result = DAG.getNode(Opcode, sdl, VTs, + { Chain, getValue(FPI.getArgOperand(0)), + getValue(FPI.getArgOperand(1)) }); assert(Result.getNode()->getNumValues() == 2); SDValue OutChain = Result.getValue(1); DAG.setRoot(OutChain); SDValue FPResult = Result.getValue(0); - setValue(&I, FPResult); + setValue(&FPI, FPResult); } std::pair Index: lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -905,50 +905,6 @@ } // end anonymous namespace -static bool isStrictFPOp(SDNode *Node, unsigned &NewOpc) { - unsigned OrigOpc = Node->getOpcode(); - switch (OrigOpc) { - case ISD::STRICT_FADD: NewOpc = ISD::FADD; return true; - case ISD::STRICT_FSUB: NewOpc = ISD::FSUB; return true; - case ISD::STRICT_FMUL: NewOpc = ISD::FMUL; return true; - case ISD::STRICT_FDIV: NewOpc = ISD::FDIV; return true; - case ISD::STRICT_FREM: NewOpc = ISD::FREM; return true; - default: return false; - } -} - -SDNode* SelectionDAGISel::MutateStrictFPToFP(SDNode *Node, unsigned NewOpc) { - assert(((Node->getOpcode() == ISD::STRICT_FADD && NewOpc == ISD::FADD) || - (Node->getOpcode() == ISD::STRICT_FSUB && NewOpc == ISD::FSUB) || - (Node->getOpcode() == ISD::STRICT_FMUL && NewOpc == ISD::FMUL) || - (Node->getOpcode() == ISD::STRICT_FDIV && NewOpc == ISD::FDIV) || - (Node->getOpcode() == ISD::STRICT_FREM && NewOpc == ISD::FREM)) && - "Unexpected StrictFP opcode!"); - - // We're taking this node out of the chain, so we need to re-link things. - SDValue InputChain = Node->getOperand(0); - SDValue OutputChain = SDValue(Node, 1); - CurDAG->ReplaceAllUsesOfValueWith(OutputChain, InputChain); - - SDVTList VTs = CurDAG->getVTList(Node->getOperand(1).getValueType()); - SDValue Ops[2] = { Node->getOperand(1), Node->getOperand(2) }; - SDNode *Res = CurDAG->MorphNodeTo(Node, NewOpc, VTs, Ops); - - // MorphNodeTo can operate in two ways: if an existing node with the - // specified operands exists, it can just return it. Otherwise, it - // updates the node in place to have the requested operands. - if (Res == Node) { - // If we updated the node in place, reset the node ID. To the isel, - // this should be just like a newly allocated machine node. - Res->setNodeId(-1); - } else { - CurDAG->ReplaceAllUsesWith(Node, Res); - CurDAG->RemoveDeadNode(Node); - } - - return Res; -} - void SelectionDAGISel::DoInstructionSelection() { DEBUG(dbgs() << "===== Instruction selection begins: BB#" << FuncInfo->MBB->getNumber() @@ -992,15 +948,12 @@ // If the current node is a strict FP pseudo-op, the isStrictFPOp() // function will provide the corresponding normal FP opcode to which the // node should be mutated. - unsigned NormalFPOpc = ISD::UNDEF; - bool IsStrictFPOp = isStrictFPOp(Node, NormalFPOpc); - if (IsStrictFPOp) - Node = MutateStrictFPToFP(Node, NormalFPOpc); + // + // FIXME: The backends need a way to handle FP constraints. + if (Node->isStrictFPOpcode()) + Node = CurDAG->mutateStrictFPToFP(Node); Select(Node); - - // FIXME: Add code here to attach an implicit def and use of - // target-specific FP environment registers. } CurDAG->setRoot(Dummy.getValue()); Index: lib/IR/IntrinsicInst.cpp =================================================================== --- lib/IR/IntrinsicInst.cpp +++ lib/IR/IntrinsicInst.cpp @@ -97,7 +97,9 @@ ConstrainedFPIntrinsic::RoundingMode ConstrainedFPIntrinsic::getRoundingMode() const { - Metadata *MD = dyn_cast(getOperand(2))->getMetadata(); + unsigned NumOperands = getNumArgOperands(); + Metadata *MD = + dyn_cast(getArgOperand(NumOperands - 2))->getMetadata(); if (!MD || !isa(MD)) return rmInvalid; StringRef RoundingArg = cast(MD)->getString(); @@ -115,7 +117,9 @@ ConstrainedFPIntrinsic::ExceptionBehavior ConstrainedFPIntrinsic::getExceptionBehavior() const { - Metadata *MD = dyn_cast(getOperand(3))->getMetadata(); + unsigned NumOperands = getNumArgOperands(); + Metadata *MD = + dyn_cast(getArgOperand(NumOperands - 1))->getMetadata(); if (!MD || !isa(MD)) return ebInvalid; StringRef ExceptionArg = cast(MD)->getString(); @@ -125,3 +129,21 @@ .Case("fpexcept.strict", ebStrict) .Default(ebInvalid); } + +bool ConstrainedFPIntrinsic::isUnaryOp() const { + switch (getIntrinsicID()) { + default: + return false; + case Intrinsic::experimental_constrained_sqrt: + case Intrinsic::experimental_constrained_sin: + case Intrinsic::experimental_constrained_cos: + case Intrinsic::experimental_constrained_exp: + case Intrinsic::experimental_constrained_exp2: + case Intrinsic::experimental_constrained_log: + case Intrinsic::experimental_constrained_log10: + case Intrinsic::experimental_constrained_log2: + case Intrinsic::experimental_constrained_rint: + case Intrinsic::experimental_constrained_nearbyint: + return true; + } +} Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -3967,6 +3967,18 @@ case Intrinsic::experimental_constrained_fmul: case Intrinsic::experimental_constrained_fdiv: case Intrinsic::experimental_constrained_frem: + case Intrinsic::experimental_constrained_sqrt: + case Intrinsic::experimental_constrained_pow: + case Intrinsic::experimental_constrained_powi: + case Intrinsic::experimental_constrained_sin: + case Intrinsic::experimental_constrained_cos: + case Intrinsic::experimental_constrained_exp: + case Intrinsic::experimental_constrained_exp2: + case Intrinsic::experimental_constrained_log: + case Intrinsic::experimental_constrained_log10: + case Intrinsic::experimental_constrained_log2: + case Intrinsic::experimental_constrained_rint: + case Intrinsic::experimental_constrained_nearbyint: visitConstrainedFPIntrinsic( cast(*CS.getInstruction())); break; @@ -4336,7 +4348,12 @@ } void Verifier::visitConstrainedFPIntrinsic(ConstrainedFPIntrinsic &FPI) { - Assert(isa(FPI.getOperand(2)), + unsigned NumOperands = FPI.getNumArgOperands(); + Assert(((NumOperands == 3 && FPI.isUnaryOp()) || (NumOperands == 4)), + "invalid arguments for constrained FP intrinsic", &FPI); + Assert(isa(FPI.getArgOperand(NumOperands-1)), + "invalid exception behavior argument", &FPI); + Assert(isa(FPI.getArgOperand(NumOperands-2)), "invalid rounding mode argument", &FPI); Assert(FPI.getRoundingMode() != ConstrainedFPIntrinsic::rmInvalid, "invalid rounding mode argument", &FPI); Index: test/CodeGen/X86/fp-intrinsics.ll =================================================================== --- test/CodeGen/X86/fp-intrinsics.ll +++ test/CodeGen/X86/fp-intrinsics.ll @@ -103,9 +103,156 @@ ret double %a.0 } +; Verify that sqrt(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f5 +; CHECK: sqrtsd +define double @f5() { +entry: + %result = call double @llvm.experimental.constrained.sqrt.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that pow(42.1, 3.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f6 +; CHECK: pow +define double @f6() { +entry: + %result = call double @llvm.experimental.constrained.pow.f64(double 42.1, + double 3.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that powi(42.1, 3) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f7 +; CHECK: powi +define double @f7() { +entry: + %result = call double @llvm.experimental.constrained.powi.f64(double 42.1, + i32 3, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that sin(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f8 +; CHECK: sin +define double @f8() { +entry: + %result = call double @llvm.experimental.constrained.sin.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that cos(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f9 +; CHECK: cos +define double @f9() { +entry: + %result = call double @llvm.experimental.constrained.cos.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that exp(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f10 +; CHECK: exp +define double @f10() { +entry: + %result = call double @llvm.experimental.constrained.exp.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that exp2(42.1) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f11 +; CHECK: exp2 +define double @f11() { +entry: + %result = call double @llvm.experimental.constrained.exp2.f64(double 42.1, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that log(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f12 +; CHECK: log +define double @f12() { +entry: + %result = call double @llvm.experimental.constrained.log.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that log10(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f13 +; CHECK: log10 +define double @f13() { +entry: + %result = call double @llvm.experimental.constrained.log10.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that log2(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f14 +; CHECK: log2 +define double @f14() { +entry: + %result = call double @llvm.experimental.constrained.log2.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that rint(42.1) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f15 +; CHECK: rint +define double @f15() { +entry: + %result = call double @llvm.experimental.constrained.rint.f64(double 42.1, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that nearbyint(42.1) isn't simplified when the rounding mode is +; unknown. +; CHECK-LABEL: f16 +; CHECK: nearbyint +define double @f16() { +entry: + %result = call double @llvm.experimental.constrained.nearbyint.f64( + double 42.1, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} @llvm.fp.env = thread_local global i8 zeroinitializer, section "llvm.metadata" declare double @llvm.experimental.constrained.fdiv.f64(double, double, metadata, metadata) declare double @llvm.experimental.constrained.fmul.f64(double, double, metadata, metadata) declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) declare double @llvm.experimental.constrained.fsub.f64(double, double, metadata, metadata) +declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.pow.f64(double, double, metadata, metadata) +declare double @llvm.experimental.constrained.powi.f64(double, i32, metadata, metadata) +declare double @llvm.experimental.constrained.sin.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.cos.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.exp.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.exp2.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.log.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.log10.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.log2.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.rint.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.nearbyint.f64(double, metadata, metadata) Index: test/Feature/fp-intrinsics.ll =================================================================== --- test/Feature/fp-intrinsics.ll +++ test/Feature/fp-intrinsics.ll @@ -95,8 +95,156 @@ } +; Verify that sqrt(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f5 +; CHECK: call double @llvm.experimental.constrained.sqrt +define double @f5() { +entry: + %result = call double @llvm.experimental.constrained.sqrt.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that pow(42.1, 3.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f6 +; CHECK: call double @llvm.experimental.constrained.pow +define double @f6() { +entry: + %result = call double @llvm.experimental.constrained.pow.f64(double 42.1, + double 3.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that powi(42.1, 3) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f7 +; CHECK: call double @llvm.experimental.constrained.powi +define double @f7() { +entry: + %result = call double @llvm.experimental.constrained.powi.f64(double 42.1, + i32 3, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that sin(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f8 +; CHECK: call double @llvm.experimental.constrained.sin +define double @f8() { +entry: + %result = call double @llvm.experimental.constrained.sin.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that cos(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f9 +; CHECK: call double @llvm.experimental.constrained.cos +define double @f9() { +entry: + %result = call double @llvm.experimental.constrained.cos.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that exp(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f10 +; CHECK: call double @llvm.experimental.constrained.exp +define double @f10() { +entry: + %result = call double @llvm.experimental.constrained.exp.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that exp2(42.1) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f11 +; CHECK: call double @llvm.experimental.constrained.exp2 +define double @f11() { +entry: + %result = call double @llvm.experimental.constrained.exp2.f64(double 42.1, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that log(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f12 +; CHECK: call double @llvm.experimental.constrained.log +define double @f12() { +entry: + %result = call double @llvm.experimental.constrained.log.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that log10(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f13 +; CHECK: call double @llvm.experimental.constrained.log10 +define double @f13() { +entry: + %result = call double @llvm.experimental.constrained.log10.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that log2(42.0) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f14 +; CHECK: call double @llvm.experimental.constrained.log2 +define double @f14() { +entry: + %result = call double @llvm.experimental.constrained.log2.f64(double 42.0, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that rint(42.1) isn't simplified when the rounding mode is unknown. +; CHECK-LABEL: f15 +; CHECK: call double @llvm.experimental.constrained.rint +define double @f15() { +entry: + %result = call double @llvm.experimental.constrained.rint.f64(double 42.1, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + +; Verify that nearbyint(42.1) isn't simplified when the rounding mode is +; unknown. +; CHECK-LABEL: f16 +; CHECK: call double @llvm.experimental.constrained.nearbyint +define double @f16() { +entry: + %result = call double @llvm.experimental.constrained.nearbyint.f64( + double 42.1, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %result +} + @llvm.fp.env = thread_local global i8 zeroinitializer, section "llvm.metadata" declare double @llvm.experimental.constrained.fdiv.f64(double, double, metadata, metadata) declare double @llvm.experimental.constrained.fmul.f64(double, double, metadata, metadata) declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) declare double @llvm.experimental.constrained.fsub.f64(double, double, metadata, metadata) +declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.pow.f64(double, double, metadata, metadata) +declare double @llvm.experimental.constrained.powi.f64(double, i32, metadata, metadata) +declare double @llvm.experimental.constrained.sin.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.cos.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.exp.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.exp2.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.log.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.log10.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.log2.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.rint.f64(double, metadata, metadata) +declare double @llvm.experimental.constrained.nearbyint.f64(double, metadata, metadata) Index: test/Verifier/fp-intrinsics.ll =================================================================== --- test/Verifier/fp-intrinsics.ll +++ test/Verifier/fp-intrinsics.ll @@ -1,13 +1,17 @@ ; RUN: opt -verify -S < %s 2>&1 | FileCheck --check-prefix=CHECK1 %s ; RUN: sed -e s/.T2:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK2 %s ; RUN: sed -e s/.T3:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK3 %s +; RUN: sed -e s/.T4:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK4 %s +; RUN: sed -e s/.T5:// %s | not opt -verify -disable-output 2>&1 | FileCheck --check-prefix=CHECK5 %s -; Common declaration used for all runs. +; Common declarations used for all runs. declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) +declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) ; Test that the verifier accepts legal code, and that the correct attributes are ; attached to the FP intrinsic. ; CHECK1: declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) #[[ATTR:[0-9]+]] +; CHECK1: declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) #[[ATTR]] ; CHECK1: attributes #[[ATTR]] = { inaccessiblememonly nounwind } ; Note: FP exceptions aren't usually caught through normal unwind mechanisms, ; but we may want to revisit this for asynchronous exception handling. @@ -20,6 +24,15 @@ ret double %fadd } +define double @f1u(double %a) { +entry: + %fsqrt = call double @llvm.experimental.constrained.sqrt.f64( + double %a, + metadata !"round.dynamic", + metadata !"fpexcept.strict") + ret double %fsqrt +} + ; Test an illegal value for the rounding mode argument. ; CHECK2: invalid rounding mode argument ;T2: define double @f2(double %a, double %b) { @@ -33,7 +46,7 @@ ; Test an illegal value for the exception behavior argument. ; CHECK3: invalid exception behavior argument -;T3: define double @f2(double %a, double %b) { +;T3: define double @f3(double %a, double %b) { ;T3: entry: ;T3: %fadd = call double @llvm.experimental.constrained.fadd.f64( ;T3: double %a, double %b, @@ -41,3 +54,25 @@ ;T3: metadata !"fpexcept.restrict") ;T3: ret double %fadd ;T3: } + +; Test an illegal value for the rounding mode argument. +; CHECK4: invalid rounding mode argument +;T4: define double @f4(double %a) { +;T4: entry: +;T4: %fadd = call double @llvm.experimental.constrained.sqrt.f64( +;T4: double %a, +;T4: metadata !"round.dynomite", +;T4: metadata !"fpexcept.strict") +;T4: ret double %fadd +;T4: } + +; Test an illegal value for the exception behavior argument. +; CHECK5: invalid exception behavior argument +;T5: define double @f5(double %a) { +;T5: entry: +;T5: %fadd = call double @llvm.experimental.constrained.sqrt.f64( +;T5: double %a, +;T5: metadata !"round.dynamic", +;T5: metadata !"fpexcept.restrict") +;T5: ret double %fadd +;T5: }