mirror of
https://github.com/RPCSX/llvm.git
synced 2026-01-31 01:05:23 +01:00
Add constrained intrinsics for some libm-equivalent operations
Differential revision: https://reviews.llvm.org/D32319 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@303922 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
465
docs/LangRef.rst
465
docs/LangRef.rst
@@ -12716,7 +12716,7 @@ Syntax:
|
||||
declare <type>
|
||||
@llvm.experimental.constrained.fadd(<type> <op1>, <type> <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
metadata <exception behavior>)
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
@@ -12753,7 +12753,7 @@ Syntax:
|
||||
declare <type>
|
||||
@llvm.experimental.constrained.fsub(<type> <op1>, <type> <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
metadata <exception behavior>)
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
@@ -12790,7 +12790,7 @@ Syntax:
|
||||
declare <type>
|
||||
@llvm.experimental.constrained.fmul(<type> <op1>, <type> <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
metadata <exception behavior>)
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
@@ -12827,7 +12827,7 @@ Syntax:
|
||||
declare <type>
|
||||
@llvm.experimental.constrained.fdiv(<type> <op1>, <type> <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
metadata <exception behavior>)
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
@@ -12864,7 +12864,7 @@ Syntax:
|
||||
declare <type>
|
||||
@llvm.experimental.constrained.frem(<type> <op1>, <type> <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
metadata <exception behavior>)
|
||||
|
||||
Overview:
|
||||
"""""""""
|
||||
@@ -12893,6 +12893,461 @@ value operands and has the same type as the operands. The remainder has the
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.sqrt(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.pow(<type> <op1>, <type> <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.powi(<type> <op1>, i32 <op2>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.sin(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.cos(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.exp(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.exp2(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.log(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.log10(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.log2(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.rint(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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 <type>
|
||||
@llvm.experimental.constrained.nearbyint(<type> <op1>,
|
||||
metadata <rounding mode>,
|
||||
metadata <exception behavior>)
|
||||
|
||||
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
|
||||
------------------
|
||||
|
||||
|
||||
@@ -264,6 +264,14 @@ namespace ISD {
|
||||
/// 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,
|
||||
|
||||
|
||||
@@ -1070,6 +1070,11 @@ public:
|
||||
SDNode *MorphNodeTo(SDNode *N, unsigned Opc, SDVTList VTs,
|
||||
ArrayRef<SDValue> 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.
|
||||
///
|
||||
|
||||
@@ -612,6 +612,32 @@ public:
|
||||
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; }
|
||||
|
||||
@@ -171,6 +171,7 @@ namespace llvm {
|
||||
ebStrict
|
||||
};
|
||||
|
||||
bool isUnaryOp() const;
|
||||
RoundingMode getRoundingMode() const;
|
||||
ExceptionBehavior getExceptionBehavior() const;
|
||||
|
||||
@@ -182,6 +183,18 @@ namespace llvm {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -489,8 +489,64 @@ let IntrProperties = [IntrInaccessibleMemOnly] in {
|
||||
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 --------------------------===//
|
||||
|
||||
@@ -899,6 +899,39 @@ void SelectionDAGLegalize::LegalizeLoadOps(SDNode *Node) {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
|
||||
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 @@ SDValue SelectionDAGLegalize::ExpandFPLibCall(SDNode* Node,
|
||||
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 @@ void SelectionDAGLegalize::ConvertNodeToLibcall(SDNode *Node) {
|
||||
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 @@ void SelectionDAGLegalize::ConvertNodeToLibcall(SDNode *Node) {
|
||||
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 @@ void SelectionDAGLegalize::ConvertNodeToLibcall(SDNode *Node) {
|
||||
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 @@ void SelectionDAGLegalize::ConvertNodeToLibcall(SDNode *Node) {
|
||||
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));
|
||||
|
||||
@@ -6542,6 +6542,63 @@ SDNode *SelectionDAG::MorphNodeTo(SDNode *N, unsigned Opc,
|
||||
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.
|
||||
|
||||
@@ -5245,7 +5245,19 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||
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<ConstrainedFPIntrinsic>(I));
|
||||
return nullptr;
|
||||
case Intrinsic::fmuladd: {
|
||||
EVT VT = TLI.getValueType(DAG.getDataLayout(), I.getType());
|
||||
@@ -5743,11 +5755,11 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -5764,23 +5776,64 @@ void SelectionDAGBuilder::visitConstrainedFPIntrinsic(const CallInst &I,
|
||||
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<EVT, 4> 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<SDValue, SDValue>
|
||||
|
||||
@@ -895,7 +895,7 @@ private:
|
||||
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);
|
||||
|
||||
@@ -905,50 +905,6 @@ public:
|
||||
|
||||
} // 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 @@ void SelectionDAGISel::DoInstructionSelection() {
|
||||
// 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());
|
||||
|
||||
@@ -97,7 +97,9 @@ Value *InstrProfIncrementInst::getStep() const {
|
||||
|
||||
ConstrainedFPIntrinsic::RoundingMode
|
||||
ConstrainedFPIntrinsic::getRoundingMode() const {
|
||||
Metadata *MD = dyn_cast<MetadataAsValue>(getOperand(2))->getMetadata();
|
||||
unsigned NumOperands = getNumArgOperands();
|
||||
Metadata *MD =
|
||||
dyn_cast<MetadataAsValue>(getArgOperand(NumOperands - 2))->getMetadata();
|
||||
if (!MD || !isa<MDString>(MD))
|
||||
return rmInvalid;
|
||||
StringRef RoundingArg = cast<MDString>(MD)->getString();
|
||||
@@ -115,7 +117,9 @@ ConstrainedFPIntrinsic::getRoundingMode() const {
|
||||
|
||||
ConstrainedFPIntrinsic::ExceptionBehavior
|
||||
ConstrainedFPIntrinsic::getExceptionBehavior() const {
|
||||
Metadata *MD = dyn_cast<MetadataAsValue>(getOperand(3))->getMetadata();
|
||||
unsigned NumOperands = getNumArgOperands();
|
||||
Metadata *MD =
|
||||
dyn_cast<MetadataAsValue>(getArgOperand(NumOperands - 1))->getMetadata();
|
||||
if (!MD || !isa<MDString>(MD))
|
||||
return ebInvalid;
|
||||
StringRef ExceptionArg = cast<MDString>(MD)->getString();
|
||||
@@ -125,3 +129,21 @@ ConstrainedFPIntrinsic::getExceptionBehavior() const {
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3966,6 +3966,18 @@ void Verifier::visitIntrinsicCallSite(Intrinsic::ID ID, CallSite CS) {
|
||||
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<ConstrainedFPIntrinsic>(*CS.getInstruction()));
|
||||
break;
|
||||
@@ -4335,7 +4347,12 @@ static DISubprogram *getSubprogram(Metadata *LocalScope) {
|
||||
}
|
||||
|
||||
void Verifier::visitConstrainedFPIntrinsic(ConstrainedFPIntrinsic &FPI) {
|
||||
Assert(isa<MetadataAsValue>(FPI.getOperand(2)),
|
||||
unsigned NumOperands = FPI.getNumArgOperands();
|
||||
Assert(((NumOperands == 3 && FPI.isUnaryOp()) || (NumOperands == 4)),
|
||||
"invalid arguments for constrained FP intrinsic", &FPI);
|
||||
Assert(isa<MetadataAsValue>(FPI.getArgOperand(NumOperands-1)),
|
||||
"invalid exception behavior argument", &FPI);
|
||||
Assert(isa<MetadataAsValue>(FPI.getArgOperand(NumOperands-2)),
|
||||
"invalid rounding mode argument", &FPI);
|
||||
Assert(FPI.getRoundingMode() != ConstrainedFPIntrinsic::rmInvalid,
|
||||
"invalid rounding mode argument", &FPI);
|
||||
|
||||
@@ -103,9 +103,156 @@ if.end:
|
||||
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)
|
||||
|
||||
@@ -95,8 +95,156 @@ if.end:
|
||||
}
|
||||
|
||||
|
||||
; 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)
|
||||
|
||||
@@ -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 @@ entry:
|
||||
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 @@ entry:
|
||||
|
||||
; 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 @@ entry:
|
||||
;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: }
|
||||
|
||||
Reference in New Issue
Block a user