[llvm] [LLVM] Add FP instruction intrinsics (PR #193588)
Princeton Ferro via llvm-commits
llvm-commits at lists.llvm.org
Wed May 6 14:53:24 PDT 2026
https://github.com/Prince781 updated https://github.com/llvm/llvm-project/pull/193588
>From 85c8877cd4549346e6da65e81eba6ea1d3c66ff7 Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 10:50:34 -0700
Subject: [PATCH 01/16] [LLVM] Add FP instruction intrinsics (llvm.fadd,
llvm.fsub, llvm.fcmp, etc.)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Introduce call-form equivalents of the LLVM floating-point instructions as
proper intrinsics: llvm.fadd, llvm.fsub, llvm.fmul, llvm.fdiv, llvm.frem,
llvm.fneg, llvm.fptrunc, llvm.fpext, llvm.sitofp, llvm.uitofp, llvm.fptosi,
llvm.fptoui, llvm.fcmp, and llvm.fcmps.
These intrinsics carry fast-math flags as call-site attributes (nnan, nsz,
fast, etc.) and lower identically to their instruction counterparts — to the
same ISD and G_ opcodes — with no observable behavior change when no
fp.control/fp.except bundles are present.
Changes:
- llvm/include/llvm/IR/FloatingPointOps.def: new X-macro table mapping each
intrinsic to its ISD opcode name
- llvm/include/llvm/IR/Intrinsics.td: definitions for all 14 intrinsics; the
13 arithmetic/conversion ops use IntrNoMem+IntrSpeculatable; fcmps uses
IntrInaccessibleMemOnly to model the signaling FP exception side-effect
- SelectionDAGBuilder.cpp: lowering cases that emit plain ISD::FADD/FSUB/...
nodes; fcmp/fcmps parse the predicate metadata string via StringSwitch
- IRTranslator.cpp: GlobalISel lowering via translateBinaryOp/translateUnaryOp
helpers and explicit buildFPTrunc/buildFPExt/buildSITOFP/etc. calls;
fcmps always lowers to G_STRICT_FCMPS
- InstructionSimplify.cpp: simplify fadd/fsub/fmul/fdiv/frem using existing
simplifyFAddInst etc. helpers (rounding mode = NearestTiesToEven, ebIgnore)
- ConstantFolding.cpp: constant-fold arithmetic intrinsics via APFloat ops
- LangRef.rst: documentation for all new intrinsics with cross-references from
the corresponding instruction sections
Tests added:
- llvm/test/Assembler/fp-instruction-intrinsics.ll: bitcode roundtrip + attr
- llvm/test/Transforms/InstSimplify/fp-intrinsics.ll: simplification cases
- llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll: x86_64 lowering
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 696 ++++++++++++++++++
llvm/include/llvm/IR/FloatingPointOps.def | 121 +++
llvm/include/llvm/IR/Intrinsics.td | 44 +-
llvm/lib/Analysis/ConstantFolding.cpp | 40 +
llvm/lib/Analysis/InstructionSimplify.cpp | 27 +
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 117 +++
.../SelectionDAG/SelectionDAGBuilder.cpp | 142 ++++
.../Assembler/fp-instruction-intrinsics.ll | 139 ++++
.../Generic/fp-instruction-intrinsics.ll | 86 +++
.../Transforms/InstSimplify/fp-intrinsics.ll | 111 +++
10 files changed, 1521 insertions(+), 2 deletions(-)
create mode 100644 llvm/include/llvm/IR/FloatingPointOps.def
create mode 100644 llvm/test/Assembler/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/Transforms/InstSimplify/fp-intrinsics.ll
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 6f34005f3e945..2c596133c1dd8 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -10459,6 +10459,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
+See also the :ref:`llvm.fneg <int_fneg>` intrinsic, which is a call-form
+equivalent of this instruction.
+
Example:
""""""""
@@ -10564,6 +10567,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
+See also the :ref:`llvm.fadd <int_fadd>` intrinsic, which is a call-form
+equivalent of this instruction.
+
Example:
""""""""
@@ -10660,6 +10666,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
+See also the :ref:`llvm.fsub <int_fsub>` intrinsic, which is a call-form
+equivalent of this instruction.
+
Example:
""""""""
@@ -10757,6 +10766,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
+See also the :ref:`llvm.fmul <int_fmul>` intrinsic, which is a call-form
+equivalent of this instruction.
+
Example:
""""""""
@@ -10895,6 +10907,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
+See also the :ref:`llvm.fdiv <int_fdiv>` intrinsic, which is a call-form
+equivalent of this instruction.
+
Example:
""""""""
@@ -11051,6 +11066,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
+See also the :ref:`llvm.frem <int_frem>` intrinsic, which is a call-form
+equivalent of this instruction.
+
Example:
""""""""
@@ -12715,6 +12733,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations.
+See also the :ref:`llvm.fptrunc <int_fptrunc_intr>` intrinsic, which is a
+call-form equivalent of this instruction.
+
Example:
""""""""
@@ -12766,6 +12787,9 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations.
+See also the :ref:`llvm.fpext <int_fpext_intr>` intrinsic, which is a
+call-form equivalent of this instruction.
+
Example:
""""""""
@@ -12807,6 +12831,9 @@ The '``fptoui``' instruction converts its :ref:`floating-point
unsigned integer value. If the value cannot fit in ``ty2``, the result
is a :ref:`poison value <poisonvalues>`.
+See also the :ref:`llvm.fptoui <int_fptoui_intr>` intrinsic, which is a
+call-form equivalent of this instruction.
+
Example:
""""""""
@@ -12849,6 +12876,9 @@ The '``fptosi``' instruction converts its :ref:`floating-point
signed integer value. If the value cannot fit in ``ty2``, the result
is a :ref:`poison value <poisonvalues>`.
+See also the :ref:`llvm.fptosi <int_fptosi_intr>` intrinsic, which is a
+call-form equivalent of this instruction.
+
Example:
""""""""
@@ -12898,6 +12928,8 @@ the default rounding mode.
If the ``nneg`` flag is set, and the ``uitofp`` argument is negative,
the result is a poison value.
+See also the :ref:`llvm.uitofp <int_uitofp_intr>` intrinsic, which is a
+call-form equivalent of this instruction.
Example:
""""""""
@@ -12943,6 +12975,9 @@ quantity and converts it to the corresponding floating-point value. If the
value cannot be exactly represented, it is rounded using the default rounding
mode.
+See also the :ref:`llvm.sitofp <int_sitofp_intr>` intrinsic, which is a
+call-form equivalent of this instruction.
+
Example:
""""""""
@@ -13479,6 +13514,10 @@ only flags that have any effect on its semantics are those that allow
assumptions to be made about the values of input arguments; namely
``nnan``, ``ninf``, and ``reassoc``. See :ref:`fastmath` for more information.
+See also the :ref:`llvm.fcmp <int_fcmp>` intrinsic (quiet comparison, call-form
+equivalent of this instruction) and the :ref:`llvm.fcmps <int_fcmps>` intrinsic
+(signaling comparison that always raises on NaN operands).
+
Example:
""""""""
@@ -30244,6 +30283,663 @@ Semantics:
This function returns the same values as the libm ``trunc`` functions
would and handles error conditions in the same way.
+.. _fpintrin:
+
+FP Arithmetic Intrinsics
+------------------------
+
+These intrinsics are call-form equivalents of the standard floating-point
+instructions (:ref:`fadd <i_fadd>`, :ref:`fsub <i_fsub>`, :ref:`fmul <i_fmul>`,
+:ref:`fdiv <i_fdiv>`, :ref:`frem <i_frem>`, :ref:`fneg <i_fneg>`,
+:ref:`fcmp <i_fcmp>`, :ref:`fptrunc <i_fptrunc>`, :ref:`fpext <i_fpext>`,
+and the ``fptoui``, ``fptosi``, ``uitofp``, ``sitofp`` conversion instructions).
+They behave identically to the corresponding instruction and accept the same
+:ref:`fast-math flags <fastmath>`.
+
+The primary purpose of these intrinsics is to allow per-call FP environment
+control in a future extension; for now they are direct equivalents of their
+instruction counterparts.
+
+``llvm.fcmps`` is always ``memory(inaccessiblemem: readwrite)`` and not
+speculatable because a signaling comparison raises an FP Invalid Operation
+exception whenever either operand is any NaN, including quiet NaNs.
+
+.. _int_fadd:
+
+'``llvm.fadd.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fadd`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare half @llvm.fadd.f16(half %op1, half %op2)
+ declare bfloat @llvm.fadd.bf16(bfloat %op1, bfloat %op2)
+ declare float @llvm.fadd.f32(float %op1, float %op2)
+ declare double @llvm.fadd.f64(double %op1, double %op2)
+ declare x86_fp80 @llvm.fadd.f80(x86_fp80 %op1, x86_fp80 %op2)
+ declare fp128 @llvm.fadd.f128(fp128 %op1, fp128 %op2)
+ declare ppc_fp128 @llvm.fadd.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fadd <i_fadd>` instruction. Returns the
+floating-point sum of its two operands.
+
+Arguments:
+""""""""""
+
+The arguments and return value are floating-point numbers of the same type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fadd <i_fadd>` instruction. Always ``memory(none)``
+and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+
+.. _int_fsub:
+
+'``llvm.fsub.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fsub`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare half @llvm.fsub.f16(half %op1, half %op2)
+ declare bfloat @llvm.fsub.bf16(bfloat %op1, bfloat %op2)
+ declare float @llvm.fsub.f32(float %op1, float %op2)
+ declare double @llvm.fsub.f64(double %op1, double %op2)
+ declare x86_fp80 @llvm.fsub.f80(x86_fp80 %op1, x86_fp80 %op2)
+ declare fp128 @llvm.fsub.f128(fp128 %op1, fp128 %op2)
+ declare ppc_fp128 @llvm.fsub.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fsub <i_fsub>` instruction. Returns the
+floating-point difference of its two operands.
+
+Arguments:
+""""""""""
+
+The arguments and return value are floating-point numbers of the same type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fsub <i_fsub>` instruction. Always ``memory(none)``
+and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+
+.. _int_fmul:
+
+'``llvm.fmul.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fmul`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare half @llvm.fmul.f16(half %op1, half %op2)
+ declare bfloat @llvm.fmul.bf16(bfloat %op1, bfloat %op2)
+ declare float @llvm.fmul.f32(float %op1, float %op2)
+ declare double @llvm.fmul.f64(double %op1, double %op2)
+ declare x86_fp80 @llvm.fmul.f80(x86_fp80 %op1, x86_fp80 %op2)
+ declare fp128 @llvm.fmul.f128(fp128 %op1, fp128 %op2)
+ declare ppc_fp128 @llvm.fmul.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fmul <i_fmul>` instruction. Returns the
+floating-point product of its two operands.
+
+Arguments:
+""""""""""
+
+The arguments and return value are floating-point numbers of the same type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fmul <i_fmul>` instruction. Always ``memory(none)``
+and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+
+.. _int_fdiv:
+
+'``llvm.fdiv.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fdiv`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare half @llvm.fdiv.f16(half %op1, half %op2)
+ declare bfloat @llvm.fdiv.bf16(bfloat %op1, bfloat %op2)
+ declare float @llvm.fdiv.f32(float %op1, float %op2)
+ declare double @llvm.fdiv.f64(double %op1, double %op2)
+ declare x86_fp80 @llvm.fdiv.f80(x86_fp80 %op1, x86_fp80 %op2)
+ declare fp128 @llvm.fdiv.f128(fp128 %op1, fp128 %op2)
+ declare ppc_fp128 @llvm.fdiv.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fdiv <i_fdiv>` instruction. Returns the
+floating-point quotient of its two operands.
+
+Arguments:
+""""""""""
+
+The arguments and return value are floating-point numbers of the same type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fdiv <i_fdiv>` instruction. Always ``memory(none)``
+and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+
+.. _int_frem:
+
+'``llvm.frem.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.frem`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare half @llvm.frem.f16(half %op1, half %op2)
+ declare bfloat @llvm.frem.bf16(bfloat %op1, bfloat %op2)
+ declare float @llvm.frem.f32(float %op1, float %op2)
+ declare double @llvm.frem.f64(double %op1, double %op2)
+ declare x86_fp80 @llvm.frem.f80(x86_fp80 %op1, x86_fp80 %op2)
+ declare fp128 @llvm.frem.f128(fp128 %op1, fp128 %op2)
+ declare ppc_fp128 @llvm.frem.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`frem <i_frem>` instruction. Returns the
+floating-point remainder of dividing the first operand by the second.
+
+Arguments:
+""""""""""
+
+The arguments and return value are floating-point numbers of the same type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`frem <i_frem>` instruction. Always ``memory(none)``
+and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.frem.f32(float %a, float %b)
+
+.. _int_fneg:
+
+'``llvm.fneg.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fneg`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare half @llvm.fneg.f16(half %op1)
+ declare bfloat @llvm.fneg.bf16(bfloat %op1)
+ declare float @llvm.fneg.f32(float %op1)
+ declare double @llvm.fneg.f64(double %op1)
+ declare x86_fp80 @llvm.fneg.f80(x86_fp80 %op1)
+ declare fp128 @llvm.fneg.f128(fp128 %op1)
+ declare ppc_fp128 @llvm.fneg.ppcf128(ppc_fp128 %op1)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fneg <i_fneg>` instruction. Returns the
+floating-point negation of its operand.
+
+Arguments:
+""""""""""
+
+The argument and return value are floating-point numbers of the same type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fneg <i_fneg>` instruction. Always ``memory(none)``
+and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.fneg.f32(float %x)
+
+.. _int_fcmp:
+
+'``llvm.fcmp.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fcmp`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare i1 @llvm.fcmp.f16(half %op1, half %op2, metadata %cc)
+ declare i1 @llvm.fcmp.bf16(bfloat %op1, bfloat %op2, metadata %cc)
+ declare i1 @llvm.fcmp.f32(float %op1, float %op2, metadata %cc)
+ declare i1 @llvm.fcmp.f64(double %op1, double %op2, metadata %cc)
+ declare i1 @llvm.fcmp.f80(x86_fp80 %op1, x86_fp80 %op2, metadata %cc)
+ declare i1 @llvm.fcmp.f128(fp128 %op1, fp128 %op2, metadata %cc)
+ declare i1 @llvm.fcmp.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2, metadata %cc)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fcmp <i_fcmp>` instruction. Performs a **quiet**
+floating-point comparison. For vector types the return type is a vector of
+``i1`` with the same number of elements as the operands.
+
+Arguments:
+""""""""""
+
+The two operands must be floating-point numbers of the same type. The third
+argument is a metadata string giving the comparison predicate; valid values are
+:ref:`the same as for the fcmp instruction <fcmp_md_cc>`: ``"oeq"``,
+``"ogt"``, ``"oge"``, ``"olt"``, ``"ole"``, ``"one"``, ``"ord"``, ``"ueq"``,
+``"ugt"``, ``"uge"``, ``"ult"``, ``"ule"``, ``"une"``, ``"uno"``, ``"true"``,
+and ``"false"``.
+
+Semantics:
+""""""""""
+
+The comparison semantics :ref:`follow those of the fcmp instruction
+<fcmp_md_cc_sem>`. Always ``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %c = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+
+.. _int_fcmps:
+
+'``llvm.fcmps.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``llvm.fcmps`` on any
+floating-point or vector of floating-point type. Not all targets support
+all types however.
+
+::
+
+ declare i1 @llvm.fcmps.f16(half %op1, half %op2, metadata %cc)
+ declare i1 @llvm.fcmps.bf16(bfloat %op1, bfloat %op2, metadata %cc)
+ declare i1 @llvm.fcmps.f32(float %op1, float %op2, metadata %cc)
+ declare i1 @llvm.fcmps.f64(double %op1, double %op2, metadata %cc)
+ declare i1 @llvm.fcmps.f80(x86_fp80 %op1, x86_fp80 %op2, metadata %cc)
+ declare i1 @llvm.fcmps.f128(fp128 %op1, fp128 %op2, metadata %cc)
+ declare i1 @llvm.fcmps.ppcf128(ppc_fp128 %op1, ppc_fp128 %op2, metadata %cc)
+
+Overview:
+"""""""""
+
+Performs a **signaling** floating-point comparison. Unlike
+:ref:`llvm.fcmp <int_fcmp>`, a signaling comparison raises an FP Invalid
+Operation exception whenever either operand is any NaN — including quiet NaNs
+(qNaN). There is no corresponding plain instruction; the closest equivalent is
+:ref:`fcmp <i_fcmp>`. For vector types the return type is a vector of ``i1``
+with the same number of elements as the operands.
+
+Arguments:
+""""""""""
+
+Same as :ref:`llvm.fcmp <int_fcmp>`.
+
+Semantics:
+""""""""""
+
+``llvm.fcmps`` is always ``memory(inaccessiblemem: readwrite) willreturn``
+because it can always raise an FP Invalid Operation exception when a NaN
+operand is encountered.
+
+Example:
+""""""""
+
+::
+
+ %r = call i1 @llvm.fcmps.f32(float %a, float %b, metadata !"oeq")
+
+.. _int_fptrunc_intr:
+
+'``llvm.fptrunc.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. The source and destination types must both
+be floating-point types; not all targets support all type combinations.
+
+::
+
+ declare half @llvm.fptrunc.f32.f16(float %val)
+ declare float @llvm.fptrunc.f64.f32(double %val)
+ declare half @llvm.fptrunc.f64.f16(double %val)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fptrunc <i_fptrunc>` instruction. Truncates
+a floating-point value to a smaller floating-point type.
+
+Arguments:
+""""""""""
+
+The argument is a floating-point number and the return type is a smaller
+floating-point type. The source type must be larger than the destination type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fptrunc <i_fptrunc>` instruction. Always
+``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.fptrunc.f64.f32(double %x)
+
+.. _int_fpext_intr:
+
+'``llvm.fpext.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. The source and destination types must both
+be floating-point types; not all targets support all type combinations.
+
+::
+
+ declare double @llvm.fpext.f32.f64(float %val)
+ declare float @llvm.fpext.f16.f32(half %val)
+ declare double @llvm.fpext.f16.f64(half %val)
+
+Overview:
+"""""""""
+
+Intrinsic form of the :ref:`fpext <i_fpext>` instruction. Extends a
+floating-point value to a larger floating-point type.
+
+Arguments:
+""""""""""
+
+The argument is a floating-point number and the return type is a larger
+floating-point type. The source type must be smaller than the destination type.
+
+Semantics:
+""""""""""
+
+Equivalent to the :ref:`fpext <i_fpext>` instruction. Always
+``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call double @llvm.fpext.f32.f64(float %x)
+
+.. _int_sitofp_intr:
+
+'``llvm.sitofp.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. The source type must be an integer type and
+the destination type must be a floating-point type.
+
+::
+
+ declare float @llvm.sitofp.i32.f32(i32 %val)
+ declare double @llvm.sitofp.i64.f64(i64 %val)
+
+Overview:
+"""""""""
+
+Intrinsic form of the ``sitofp`` instruction. Converts a signed integer
+value to a floating-point value.
+
+Arguments:
+""""""""""
+
+The argument is a signed integer value and the return type is a floating-point
+type. If the source is a vector, the destination must be a vector with the same
+number of elements.
+
+Semantics:
+""""""""""
+
+Equivalent to the ``sitofp`` instruction. If the integer value cannot be
+represented exactly in the destination type, it is rounded using the default
+rounding mode. Always ``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.sitofp.i32.f32(i32 %x)
+
+.. _int_uitofp_intr:
+
+'``llvm.uitofp.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. The source type must be an integer type and
+the destination type must be a floating-point type.
+
+::
+
+ declare float @llvm.uitofp.i32.f32(i32 %val)
+ declare double @llvm.uitofp.i64.f64(i64 %val)
+
+Overview:
+"""""""""
+
+Intrinsic form of the ``uitofp`` instruction. Converts an unsigned integer
+value to a floating-point value.
+
+Arguments:
+""""""""""
+
+The argument is an unsigned integer value and the return type is a floating-point
+type. If the source is a vector, the destination must be a vector with the same
+number of elements.
+
+Semantics:
+""""""""""
+
+Equivalent to the ``uitofp`` instruction. If the integer value cannot be
+represented exactly in the destination type, it is rounded using the default
+rounding mode. Always ``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call float @llvm.uitofp.i32.f32(i32 %x)
+
+.. _int_fptosi_intr:
+
+'``llvm.fptosi.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. The source type must be a floating-point
+type and the destination type must be an integer type.
+
+::
+
+ declare i32 @llvm.fptosi.f32.i32(float %val)
+ declare i64 @llvm.fptosi.f64.i64(double %val)
+
+Overview:
+"""""""""
+
+Intrinsic form of the ``fptosi`` instruction. Converts a floating-point
+value to a signed integer value by truncating toward zero.
+
+Arguments:
+""""""""""
+
+The argument is a floating-point value and the return type is a signed integer
+type. If the source is a vector, the destination must be a vector with the same
+number of elements.
+
+Semantics:
+""""""""""
+
+Equivalent to the ``fptosi`` instruction. If the floating-point value cannot
+be represented in the destination integer type, the result is a
+:ref:`poison value <poisonvalues>`. Always ``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call i32 @llvm.fptosi.f32.i32(float %x)
+
+.. _int_fptoui_intr:
+
+'``llvm.fptoui.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. The source type must be a floating-point
+type and the destination type must be an integer type.
+
+::
+
+ declare i32 @llvm.fptoui.f32.i32(float %val)
+ declare i64 @llvm.fptoui.f64.i64(double %val)
+
+Overview:
+"""""""""
+
+Intrinsic form of the ``fptoui`` instruction. Converts a floating-point
+value to an unsigned integer value by truncating toward zero.
+
+Arguments:
+""""""""""
+
+The argument is a floating-point value and the return type is an unsigned
+integer type. If the source is a vector, the destination must be a vector with
+the same number of elements.
+
+Semantics:
+""""""""""
+
+Equivalent to the ``fptoui`` instruction. If the floating-point value cannot
+be represented in the destination integer type, the result is a
+:ref:`poison value <poisonvalues>`. Always ``memory(none)`` and speculatable.
+
+Example:
+""""""""
+
+::
+
+ %r = call i32 @llvm.fptoui.f32.i32(float %x)
+
+
.. _int_experimental_noalias_scope_decl:
'``llvm.experimental.noalias.scope.decl``' Intrinsic
diff --git a/llvm/include/llvm/IR/FloatingPointOps.def b/llvm/include/llvm/IR/FloatingPointOps.def
new file mode 100644
index 0000000000000..41a16743bd370
--- /dev/null
+++ b/llvm/include/llvm/IR/FloatingPointOps.def
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the set of intrinsics classified as floating-point operations.
+//
+// Two macro forms:
+//
+// FP_OP(intrinsic_name, isd_opcode_name)
+// - An FP intrinsic that lowers to a single ISD opcode.
+// - isFloatingPointOperation() uses column 1 (Intrinsic::NAME).
+// - ISD::isFPOpcode() uses column 2 (ISD::ISD_NAME).
+//
+// FP_INTRINSIC(intrinsic_name)
+// - An FP intrinsic whose ISD opcode is already covered by another FP_OP
+// entry, or maps to a shared opcode (e.g. SETCC) not exclusively FP.
+// - isFloatingPointOperation() uses it; ISD::isFPOpcode() does not.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FP_OP
+#define FP_OP(N, D)
+#endif
+
+#ifndef FP_INTRINSIC
+#define FP_INTRINSIC(N)
+#endif
+
+// Basic FP arithmetic
+FP_OP(fadd, FADD)
+FP_OP(fsub, FSUB)
+FP_OP(fmul, FMUL)
+FP_OP(fdiv, FDIV)
+FP_OP(frem, FREM)
+FP_OP(fneg, FNEG)
+FP_OP(fabs, FABS)
+
+// FMA family
+FP_OP(fma, FMA)
+FP_OP(fmuladd, FMULADD)
+
+// FP type conversions (float <-> float)
+FP_OP(fptrunc, FP_ROUND)
+FP_OP(fpext, FP_EXTEND)
+FP_INTRINSIC(fptrunc_round) // also lowers to FP_ROUND; ISD already covered above
+
+// Integer/FP conversions
+FP_OP(sitofp, SINT_TO_FP)
+FP_OP(uitofp, UINT_TO_FP)
+FP_OP(fptosi, FP_TO_SINT)
+FP_OP(fptoui, FP_TO_UINT)
+
+// FP comparison (SETCC is shared with integer; kept as FP_INTRINSIC)
+FP_INTRINSIC(fcmp)
+
+// Math / transcendental
+FP_OP(sqrt, FSQRT)
+// cbrt: no llvm.cbrt intrinsic; FCBRT is ISD-only (handled in isFPOpcode)
+FP_OP(sin, FSIN)
+FP_OP(cos, FCOS)
+FP_OP(tan, FTAN)
+FP_OP(asin, FASIN)
+FP_OP(acos, FACOS)
+FP_OP(atan, FATAN)
+FP_OP(atan2, FATAN2)
+FP_OP(sinh, FSINH)
+FP_OP(cosh, FCOSH)
+FP_OP(tanh, FTANH)
+FP_OP(pow, FPOW)
+FP_OP(powi, FPOWI)
+FP_OP(ldexp, FLDEXP)
+FP_OP(frexp, FFREXP)
+FP_OP(log, FLOG)
+FP_OP(log2, FLOG2)
+FP_OP(log10, FLOG10)
+FP_OP(exp, FEXP)
+FP_OP(exp2, FEXP2)
+FP_OP(exp10, FEXP10)
+
+// FP rounding
+FP_OP(ceil, FCEIL)
+FP_OP(trunc, FTRUNC)
+FP_OP(rint, FRINT)
+FP_OP(nearbyint, FNEARBYINT)
+FP_OP(round, FROUND)
+FP_OP(roundeven, FROUNDEVEN)
+FP_OP(floor, FFLOOR)
+FP_OP(lround, LROUND)
+FP_OP(llround, LLROUND)
+FP_OP(lrint, LRINT)
+FP_OP(llrint, LLRINT)
+
+// FP min/max
+FP_OP(minnum, FMINNUM)
+FP_OP(maxnum, FMAXNUM)
+FP_OP(minimum, FMINIMUM)
+FP_OP(maximum, FMAXIMUM)
+FP_OP(minimumnum, FMINIMUMNUM)
+FP_OP(maximumnum, FMAXIMUMNUM)
+
+// FP special / decomposition
+FP_OP(copysign, FCOPYSIGN)
+FP_OP(canonicalize, FCANONICALIZE)
+FP_OP(is_fpclass, IS_FPCLASS)
+FP_OP(sincos, FSINCOS)
+FP_OP(modf, FMODF)
+
+// Vector FP reductions
+FP_OP(vector_reduce_fadd, VECREDUCE_FADD)
+FP_OP(vector_reduce_fmul, VECREDUCE_FMUL)
+FP_OP(vector_reduce_fmax, VECREDUCE_FMAX)
+FP_OP(vector_reduce_fmin, VECREDUCE_FMIN)
+FP_OP(vector_reduce_fmaximum, VECREDUCE_FMAXIMUM)
+FP_OP(vector_reduce_fminimum, VECREDUCE_FMINIMUM)
+
+#undef FP_OP
+#undef FP_INTRINSIC
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 80ef58f51e7bb..2a3979e8ecf23 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1134,10 +1134,41 @@ def int_experimental_memset_pattern
NoCapture<ArgIndex<0>>, WriteOnly<ArgIndex<0>>,
ImmArg<ArgIndex<3>>]>;
-// FIXME: Add version of these floating point intrinsics which allow non-default
-// rounding modes and FP exception handling.
+// Intrinsic call-form equivalents of FP instructions, allowing operand bundles
+// (e.g. "fp.control") to carry per-call FP environment flags such as
+// flush-to-zero denormal mode that cannot be attached to plain IR instructions
+// (fadd/fsub/fmul/...). Without any operand bundles these lower identically
+// to the corresponding FP instructions.
let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrNoCreateUndefOrPoison] in {
+ def int_fadd : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
+ [LLVMMatchType<0>, LLVMMatchType<0>]>;
+ def int_fsub : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
+ [LLVMMatchType<0>, LLVMMatchType<0>]>;
+ def int_fmul : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
+ [LLVMMatchType<0>, LLVMMatchType<0>]>;
+ def int_fneg : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
+ [LLVMMatchType<0>]>;
+ def int_fdiv : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
+ [LLVMMatchType<0>, LLVMMatchType<0>]>;
+ def int_frem : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
+ [LLVMMatchType<0>, LLVMMatchType<0>]>;
+
+ // fptrunc/fpext: unary; result type differs from operand type
+ def int_fptrunc : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyfloat_ty]>;
+ def int_fpext : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyfloat_ty]>;
+
+ // Integer/FP conversions
+ def int_sitofp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyint_ty]>;
+ def int_uitofp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyint_ty]>;
+ def int_fptosi : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty]>;
+ def int_fptoui : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty]>;
+
+ // fcmp: quiet FP compare; predicate passed as metadata string (e.g. !"oeq")
+ def int_fcmp : DefaultAttrsIntrinsic<
+ [LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
+ [llvm_anyfloat_ty, LLVMMatchType<0>, llvm_metadata_ty]>;
+
def int_fma : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>,
LLVMMatchType<0>]>;
@@ -1212,6 +1243,15 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrNoCreateUndefOrPoison] in
def int_frexp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty, llvm_anyint_ty], [LLVMMatchType<0>]>;
}
+// fcmps: signaling FP compare. Unlike the quiet llvm.fcmp, this raises an
+// Invalid Operation exception for *any* NaN operand (not just sNaN).
+// Exception observability is always strict — this intrinsic has
+// IntrInaccessibleMemOnly so it cannot be reordered or eliminated.
+def int_fcmps : Intrinsic<
+ [LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
+ [llvm_anyfloat_ty, LLVMMatchType<0>, llvm_metadata_ty],
+ [IntrInaccessibleMemOnly, IntrWillReturn, IntrNoCreateUndefOrPoison]>;
+
// TODO: Move all of these into the IntrNoCreateUndefOrPoison case above.
let IntrProperties = [IntrNoMem, IntrSpeculatable] in {
// These functions do not read memory, but are sensitive to the
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index cb8ecc3872c0c..e32253d1416d0 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1946,6 +1946,14 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
case Intrinsic::rint:
case Intrinsic::canonicalize:
+ // FP instruction intrinsics: without operand bundles they use default
+ // rounding and exception behavior, so they can always be folded.
+ case Intrinsic::fadd:
+ case Intrinsic::fsub:
+ case Intrinsic::fmul:
+ case Intrinsic::fdiv:
+ case Intrinsic::frem:
+
// Constrained intrinsics can be folded if FP environment is known
// to compiler.
case Intrinsic::experimental_constrained_fma:
@@ -3340,6 +3348,38 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
return nullptr;
}
+ // FP instruction intrinsics: without bundles, always use default
+ // rounding/exception behavior (NearestTiesToEven / ebIgnore).
+ switch (IntrinsicID) {
+ default:
+ break;
+ case Intrinsic::fadd: {
+ APFloat Res = Op1V;
+ Res.add(Op2V, RoundingMode::NearestTiesToEven);
+ return ConstantFP::get(Ty, Res);
+ }
+ case Intrinsic::fsub: {
+ APFloat Res = Op1V;
+ Res.subtract(Op2V, RoundingMode::NearestTiesToEven);
+ return ConstantFP::get(Ty, Res);
+ }
+ case Intrinsic::fmul: {
+ APFloat Res = Op1V;
+ Res.multiply(Op2V, RoundingMode::NearestTiesToEven);
+ return ConstantFP::get(Ty, Res);
+ }
+ case Intrinsic::fdiv: {
+ APFloat Res = Op1V;
+ Res.divide(Op2V, RoundingMode::NearestTiesToEven);
+ return ConstantFP::get(Ty, Res);
+ }
+ case Intrinsic::frem: {
+ APFloat Res = Op1V;
+ Res.mod(Op2V);
+ return ConstantFP::get(Ty, Res);
+ }
+ }
+
switch (IntrinsicID) {
default:
break;
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index e925f7bdc5df7..ba555781e8881 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7297,6 +7297,33 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
return nullptr;
}
+ // FP instruction intrinsics: without operand bundles these have default
+ // rounding mode and exception behavior, equivalent to FP instructions.
+ case Intrinsic::fadd: {
+ FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
+ return simplifyFAddInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
+ RoundingMode::NearestTiesToEven);
+ }
+ case Intrinsic::fsub: {
+ FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
+ return simplifyFSubInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
+ RoundingMode::NearestTiesToEven);
+ }
+ case Intrinsic::fmul: {
+ FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
+ return simplifyFMulInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
+ RoundingMode::NearestTiesToEven);
+ }
+ case Intrinsic::fdiv: {
+ FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
+ return simplifyFDivInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
+ RoundingMode::NearestTiesToEven);
+ }
+ case Intrinsic::frem: {
+ FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
+ return simplifyFRemInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
+ RoundingMode::NearestTiesToEven);
+ }
case Intrinsic::experimental_constrained_fma: {
auto *FPI = cast<ConstrainedFPIntrinsic>(Call);
if (Value *V = simplifyFPOp(Args, {}, Q, *FPI->getExceptionBehavior(),
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 89daec05f7135..9eb26378510ab 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -12,6 +12,7 @@
#include "llvm/CodeGen/GlobalISel/IRTranslator.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
@@ -2703,6 +2704,122 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
.addExternalSymbol(SymbolName.data());
return true;
}
+
+ // FP instruction intrinsics: call-form equivalents of FP instructions.
+ // These have no operand bundles in PR A and always lower to plain G_* nodes.
+ case Intrinsic::fadd:
+ return translateBinaryOp(TargetOpcode::G_FADD, CI, MIRBuilder);
+ case Intrinsic::fsub:
+ return translateBinaryOp(TargetOpcode::G_FSUB, CI, MIRBuilder);
+ case Intrinsic::fmul:
+ return translateBinaryOp(TargetOpcode::G_FMUL, CI, MIRBuilder);
+ case Intrinsic::fdiv:
+ return translateBinaryOp(TargetOpcode::G_FDIV, CI, MIRBuilder);
+ case Intrinsic::frem:
+ return translateBinaryOp(TargetOpcode::G_FREM, CI, MIRBuilder);
+ case Intrinsic::fneg:
+ return translateUnaryOp(TargetOpcode::G_FNEG, CI, MIRBuilder);
+ case Intrinsic::fptrunc: {
+ Register Src = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildFPTrunc(Res, Src,
+ MachineInstr::copyFlagsFromInstruction(CI));
+ return true;
+ }
+ case Intrinsic::fpext: {
+ Register Src = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildFPExt(Res, Src,
+ MachineInstr::copyFlagsFromInstruction(CI));
+ return true;
+ }
+ case Intrinsic::sitofp: {
+ Register Src = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildSITOFP(Res, Src);
+ return true;
+ }
+ case Intrinsic::uitofp: {
+ Register Src = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildUITOFP(Res, Src);
+ return true;
+ }
+ case Intrinsic::fptosi: {
+ Register Src = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildFPTOSI(Res, Src);
+ return true;
+ }
+ case Intrinsic::fptoui: {
+ Register Src = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildFPTOUI(Res, Src);
+ return true;
+ }
+ case Intrinsic::fcmp: {
+ // Quiet comparison — no exception raised on NaN operands.
+ auto *MD = cast<MetadataAsValue>(CI.getArgOperand(2))->getMetadata();
+ FCmpInst::Predicate Pred =
+ StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
+ .Case("oeq", FCmpInst::FCMP_OEQ)
+ .Case("ogt", FCmpInst::FCMP_OGT)
+ .Case("oge", FCmpInst::FCMP_OGE)
+ .Case("olt", FCmpInst::FCMP_OLT)
+ .Case("ole", FCmpInst::FCMP_OLE)
+ .Case("one", FCmpInst::FCMP_ONE)
+ .Case("ord", FCmpInst::FCMP_ORD)
+ .Case("uno", FCmpInst::FCMP_UNO)
+ .Case("ueq", FCmpInst::FCMP_UEQ)
+ .Case("ugt", FCmpInst::FCMP_UGT)
+ .Case("uge", FCmpInst::FCMP_UGE)
+ .Case("ult", FCmpInst::FCMP_ULT)
+ .Case("ule", FCmpInst::FCMP_ULE)
+ .Case("une", FCmpInst::FCMP_UNE)
+ .Case("true", FCmpInst::FCMP_TRUE)
+ .Case("false", FCmpInst::FCMP_FALSE)
+ .Default(FCmpInst::BAD_FCMP_PREDICATE);
+ assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
+ "invalid predicate in llvm.fcmp");
+ uint32_t Flags = MachineInstr::copyFlagsFromInstruction(CI);
+ Register Op0 = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Op1 = getOrCreateVReg(*CI.getArgOperand(1));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildFCmp(Pred, Res, Op0, Op1, Flags);
+ return true;
+ }
+ case Intrinsic::fcmps: {
+ // Signaling comparison — always raises on any NaN, so always strict.
+ auto *MD = cast<MetadataAsValue>(CI.getArgOperand(2))->getMetadata();
+ FCmpInst::Predicate Pred =
+ StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
+ .Case("oeq", FCmpInst::FCMP_OEQ)
+ .Case("ogt", FCmpInst::FCMP_OGT)
+ .Case("oge", FCmpInst::FCMP_OGE)
+ .Case("olt", FCmpInst::FCMP_OLT)
+ .Case("ole", FCmpInst::FCMP_OLE)
+ .Case("one", FCmpInst::FCMP_ONE)
+ .Case("ord", FCmpInst::FCMP_ORD)
+ .Case("uno", FCmpInst::FCMP_UNO)
+ .Case("ueq", FCmpInst::FCMP_UEQ)
+ .Case("ugt", FCmpInst::FCMP_UGT)
+ .Case("uge", FCmpInst::FCMP_UGE)
+ .Case("ult", FCmpInst::FCMP_ULT)
+ .Case("ule", FCmpInst::FCMP_ULE)
+ .Case("une", FCmpInst::FCMP_UNE)
+ .Default(FCmpInst::BAD_FCMP_PREDICATE);
+ assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
+ "invalid predicate in llvm.fcmps");
+ uint32_t Flags = MachineInstr::copyFlagsFromInstruction(CI);
+ Register Op0 = getOrCreateVReg(*CI.getArgOperand(0));
+ Register Op1 = getOrCreateVReg(*CI.getArgOperand(1));
+ Register Res = getOrCreateVReg(CI);
+ MIRBuilder.buildInstr(TargetOpcode::G_STRICT_FCMPS, {Res}, {}, Flags)
+ .addPredicate(Pred)
+ .addUse(Op0)
+ .addUse(Op1);
+ return true;
+ }
}
return false;
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 1f3b099c9c577..730851ddfc516 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -19,6 +19,7 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
@@ -7090,6 +7091,147 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
getValue(I.getArgOperand(0)), Flags));
return;
}
+ // FP instruction intrinsics: call-form equivalents of FP instructions.
+ // Without fp.control/fp.except bundles these lower to the same ISD nodes
+ // as the corresponding instructions.
+ case Intrinsic::fadd:
+ setValue(&I, DAG.getNode(ISD::FADD, sdl,
+ getValue(I.getArgOperand(0)).getValueType(),
+ getValue(I.getArgOperand(0)),
+ getValue(I.getArgOperand(1)), Flags));
+ return;
+ case Intrinsic::fsub:
+ setValue(&I, DAG.getNode(ISD::FSUB, sdl,
+ getValue(I.getArgOperand(0)).getValueType(),
+ getValue(I.getArgOperand(0)),
+ getValue(I.getArgOperand(1)), Flags));
+ return;
+ case Intrinsic::fmul:
+ setValue(&I, DAG.getNode(ISD::FMUL, sdl,
+ getValue(I.getArgOperand(0)).getValueType(),
+ getValue(I.getArgOperand(0)),
+ getValue(I.getArgOperand(1)), Flags));
+ return;
+ case Intrinsic::fneg:
+ setValue(&I, DAG.getNode(ISD::FNEG, sdl,
+ getValue(I.getArgOperand(0)).getValueType(),
+ getValue(I.getArgOperand(0)), Flags));
+ return;
+ case Intrinsic::fdiv:
+ setValue(&I, DAG.getNode(ISD::FDIV, sdl,
+ getValue(I.getArgOperand(0)).getValueType(),
+ getValue(I.getArgOperand(0)),
+ getValue(I.getArgOperand(1)), Flags));
+ return;
+ case Intrinsic::frem:
+ setValue(&I, DAG.getNode(ISD::FREM, sdl,
+ getValue(I.getArgOperand(0)).getValueType(),
+ getValue(I.getArgOperand(0)),
+ getValue(I.getArgOperand(1)), Flags));
+ return;
+ case Intrinsic::fptrunc: {
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getNode(ISD::FP_ROUND, sdl, DestVT,
+ getValue(I.getArgOperand(0)),
+ DAG.getTargetConstant(0, sdl, MVT::i32), Flags));
+ return;
+ }
+ case Intrinsic::fpext: {
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getNode(ISD::FP_EXTEND, sdl, DestVT,
+ getValue(I.getArgOperand(0)), Flags));
+ return;
+ }
+ case Intrinsic::uitofp: {
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getNode(ISD::UINT_TO_FP, sdl, DestVT,
+ getValue(I.getArgOperand(0)), Flags));
+ return;
+ }
+ case Intrinsic::sitofp: {
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getNode(ISD::SINT_TO_FP, sdl, DestVT,
+ getValue(I.getArgOperand(0)), Flags));
+ return;
+ }
+ case Intrinsic::fptosi: {
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getNode(ISD::FP_TO_SINT, sdl, DestVT,
+ getValue(I.getArgOperand(0))));
+ return;
+ }
+ case Intrinsic::fptoui: {
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getNode(ISD::FP_TO_UINT, sdl, DestVT,
+ getValue(I.getArgOperand(0))));
+ return;
+ }
+ case Intrinsic::fcmp: {
+ Metadata *MD = cast<MetadataAsValue>(I.getArgOperand(2))->getMetadata();
+ FCmpInst::Predicate Pred =
+ StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
+ .Case("oeq", FCmpInst::FCMP_OEQ)
+ .Case("ogt", FCmpInst::FCMP_OGT)
+ .Case("oge", FCmpInst::FCMP_OGE)
+ .Case("olt", FCmpInst::FCMP_OLT)
+ .Case("ole", FCmpInst::FCMP_OLE)
+ .Case("one", FCmpInst::FCMP_ONE)
+ .Case("ord", FCmpInst::FCMP_ORD)
+ .Case("uno", FCmpInst::FCMP_UNO)
+ .Case("ueq", FCmpInst::FCMP_UEQ)
+ .Case("ugt", FCmpInst::FCMP_UGT)
+ .Case("uge", FCmpInst::FCMP_UGE)
+ .Case("ult", FCmpInst::FCMP_ULT)
+ .Case("ule", FCmpInst::FCMP_ULE)
+ .Case("une", FCmpInst::FCMP_UNE)
+ .Default(FCmpInst::BAD_FCMP_PREDICATE);
+ assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
+ "invalid predicate in llvm.fcmp");
+ ISD::CondCode Condition = getFCmpCondCode(Pred);
+ EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ setValue(&I, DAG.getSetCC(sdl, DestVT, getValue(I.getArgOperand(0)),
+ getValue(I.getArgOperand(1)), Condition));
+ return;
+ }
+ case Intrinsic::fcmps: {
+ // fcmps raises an Invalid exception for any NaN; always strictly ordered.
+ SDValue Chain = getFPOperationRoot(fp::ebStrict);
+ SDValue LHS = getValue(I.getArgOperand(0));
+ SDValue RHS = getValue(I.getArgOperand(1));
+ Metadata *MD = cast<MetadataAsValue>(I.getArgOperand(2))->getMetadata();
+ FCmpInst::Predicate Pred =
+ StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
+ .Case("oeq", FCmpInst::FCMP_OEQ)
+ .Case("ogt", FCmpInst::FCMP_OGT)
+ .Case("oge", FCmpInst::FCMP_OGE)
+ .Case("olt", FCmpInst::FCMP_OLT)
+ .Case("ole", FCmpInst::FCMP_OLE)
+ .Case("one", FCmpInst::FCMP_ONE)
+ .Case("ord", FCmpInst::FCMP_ORD)
+ .Case("uno", FCmpInst::FCMP_UNO)
+ .Case("ueq", FCmpInst::FCMP_UEQ)
+ .Case("ugt", FCmpInst::FCMP_UGT)
+ .Case("uge", FCmpInst::FCMP_UGE)
+ .Case("ult", FCmpInst::FCMP_ULT)
+ .Case("ule", FCmpInst::FCMP_ULE)
+ .Case("une", FCmpInst::FCMP_UNE)
+ .Default(FCmpInst::BAD_FCMP_PREDICATE);
+ assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
+ "invalid predicate in llvm.fcmps");
+ ISD::CondCode Condition = getFCmpCondCode(Pred);
+ if (DAG.isKnownNeverNaN(LHS) && DAG.isKnownNeverNaN(RHS))
+ Condition = getFCmpCodeWithoutNaN(Condition);
+ EVT VT = TLI.getValueType(DAG.getDataLayout(), I.getType());
+ SDVTList VTs = DAG.getVTList(VT, MVT::Other);
+ SDNodeFlags StrictFlags;
+ StrictFlags.copyFMF(*cast<FPMathOperator>(&I));
+ SDValue Result = DAG.getNode(ISD::STRICT_FSETCCS, sdl, VTs,
+ {Chain, LHS, RHS, DAG.getCondCode(Condition)},
+ StrictFlags);
+ pushFPOpOutChain(Result, fp::ebStrict);
+ setValue(&I, Result.getValue(0));
+ return;
+ }
case Intrinsic::fma:
setValue(&I, DAG.getNode(
ISD::FMA, sdl, getValue(I.getArgOperand(0)).getValueType(),
diff --git a/llvm/test/Assembler/fp-instruction-intrinsics.ll b/llvm/test/Assembler/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..758433a7ab4a5
--- /dev/null
+++ b/llvm/test/Assembler/fp-instruction-intrinsics.ll
@@ -0,0 +1,139 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+; RUN: verify-uselistorder %s
+
+; Test that the new FP instruction intrinsics (llvm.fadd, llvm.fsub, etc.)
+; parse correctly, round-trip through bitcode, and have the expected attributes.
+
+define void @test_arithmetic(float %a, float %b, double %da, double %db) {
+; CHECK-LABEL: define void @test_arithmetic
+
+ %fadd = call float @llvm.fadd.f32(float %a, float %b)
+ %fsub = call float @llvm.fsub.f32(float %a, float %b)
+ %fmul = call float @llvm.fmul.f32(float %a, float %b)
+ %fdiv = call float @llvm.fdiv.f32(float %a, float %b)
+ %frem = call float @llvm.frem.f32(float %a, float %b)
+ %fneg = call float @llvm.fneg.f32(float %a)
+
+ %dfadd = call double @llvm.fadd.f64(double %da, double %db)
+ %dfsub = call double @llvm.fsub.f64(double %da, double %db)
+ %dfmul = call double @llvm.fmul.f64(double %da, double %db)
+ %dfdiv = call double @llvm.fdiv.f64(double %da, double %db)
+
+; CHECK: call float @llvm.fadd.f32(float %a, float %b)
+; CHECK: call float @llvm.fsub.f32(float %a, float %b)
+; CHECK: call float @llvm.fmul.f32(float %a, float %b)
+; CHECK: call float @llvm.fdiv.f32(float %a, float %b)
+; CHECK: call float @llvm.frem.f32(float %a, float %b)
+; CHECK: call float @llvm.fneg.f32(float %a)
+; CHECK: call double @llvm.fadd.f64(double %da, double %db)
+
+ ret void
+}
+
+define void @test_fast_math_flags(float %a, float %b) {
+; CHECK-LABEL: define void @test_fast_math_flags
+
+ %r1 = call fast float @llvm.fadd.f32(float %a, float %b)
+ %r2 = call nnan nsz float @llvm.fsub.f32(float %a, float %b)
+ %r3 = call reassoc float @llvm.fmul.f32(float %a, float %b)
+
+; CHECK: call fast float @llvm.fadd.f32(float %a, float %b)
+; CHECK: call nnan nsz float @llvm.fsub.f32(float %a, float %b)
+; CHECK: call reassoc float @llvm.fmul.f32(float %a, float %b)
+
+ ret void
+}
+
+define void @test_conversions(double %d, float %f, i32 %i) {
+; CHECK-LABEL: define void @test_conversions
+
+ %fptrunc = call float @llvm.fptrunc.f64.f32(double %d)
+ %fpext = call double @llvm.fpext.f32.f64(float %f)
+ %sitofp = call float @llvm.sitofp.i32.f32(i32 %i)
+ %uitofp = call float @llvm.uitofp.i32.f32(i32 %i)
+ %fptosi = call i32 @llvm.fptosi.f32.i32(float %f)
+ %fptoui = call i32 @llvm.fptoui.f32.i32(float %f)
+
+; CHECK: call float @llvm.fptrunc.f64.f32(double %d)
+; CHECK: call double @llvm.fpext.f32.f64(float %f)
+; CHECK: call float @llvm.sitofp.i32.f32(i32 %i)
+; CHECK: call float @llvm.uitofp.i32.f32(i32 %i)
+; CHECK: call i32 @llvm.fptosi.f32.i32(float %f)
+; CHECK: call i32 @llvm.fptoui.f32.i32(float %f)
+
+ ret void
+}
+
+define void @test_fcmp(float %a, float %b) {
+; CHECK-LABEL: define void @test_fcmp
+
+ %oeq = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ %ogt = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ogt")
+ %oge = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oge")
+ %olt = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"olt")
+ %ole = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ole")
+ %one = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"one")
+ %ord = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ord")
+ %ueq = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ueq")
+ %ugt = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ugt")
+ %uge = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"uge")
+ %ult = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ult")
+ %ule = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ule")
+ %une = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"une")
+ %uno = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"uno")
+ %t = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"true")
+ %f1 = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"false")
+
+; CHECK: call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+; CHECK: call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"ogt")
+
+ ret void
+}
+
+define void @test_fcmps(float %a, float %b) {
+; CHECK-LABEL: define void @test_fcmps
+
+ %r = call i1 @llvm.fcmps.f32(float %a, float %b, metadata !"oeq")
+
+; CHECK: call i1 @llvm.fcmps.f32(float %a, float %b, metadata !"oeq")
+
+ ret void
+}
+
+; Arithmetic intrinsics are memory(none) and speculatable (no strictfp needed).
+; CHECK-DAG: declare float @llvm.fadd.f32(float, float)
+; CHECK-DAG: declare float @llvm.fsub.f32(float, float)
+; CHECK-DAG: declare float @llvm.fmul.f32(float, float)
+; CHECK-DAG: declare float @llvm.fdiv.f32(float, float)
+; CHECK-DAG: declare float @llvm.frem.f32(float, float)
+; CHECK-DAG: declare float @llvm.fneg.f32(float)
+; CHECK-DAG: declare double @llvm.fadd.f64(double, double)
+; CHECK-DAG: declare float @llvm.fptrunc.f64.f32(double)
+; CHECK-DAG: declare double @llvm.fpext.f32.f64(float)
+; CHECK-DAG: declare float @llvm.sitofp.i32.f32(i32)
+; CHECK-DAG: declare float @llvm.uitofp.i32.f32(i32)
+; CHECK-DAG: declare i32 @llvm.fptosi.f32.i32(float)
+; CHECK-DAG: declare i32 @llvm.fptoui.f32.i32(float)
+; CHECK-DAG: declare i1 @llvm.fcmp.f32(float, float, metadata)
+; fcmps has memory(inaccessiblemem: readwrite) + willreturn
+; CHECK-DAG: declare i1 @llvm.fcmps.f32(float, float, metadata) #[[ATTR:[0-9]+]]
+; CHECK-DAG: attributes #[[ATTR]] = { nocreateundeforpoison nounwind willreturn memory(inaccessiblemem: readwrite) }
+
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.frem.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare float @llvm.fptrunc.f64.f32(double)
+declare double @llvm.fpext.f32.f64(float)
+declare float @llvm.sitofp.i32.f32(i32)
+declare float @llvm.uitofp.i32.f32(i32)
+declare i32 @llvm.fptosi.f32.i32(float)
+declare i32 @llvm.fptoui.f32.i32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
+declare i1 @llvm.fcmps.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..2235283048b0b
--- /dev/null
+++ b/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
@@ -0,0 +1,86 @@
+; RUN: llc -O0 -mtriple=x86_64-pc-linux < %s | FileCheck %s
+
+; Verify that each new FP instruction intrinsic lowers to the expected
+; machine instruction on x86_64.
+
+; llvm.fadd lowers to addsd (double SSE add)
+define double @test_fadd(double %a, double %b) {
+; CHECK-LABEL: test_fadd:
+; CHECK: addsd
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+; llvm.fsub lowers to subsd
+define double @test_fsub(double %a, double %b) {
+; CHECK-LABEL: test_fsub:
+; CHECK: subsd
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+; llvm.fmul lowers to mulsd
+define double @test_fmul(double %a, double %b) {
+; CHECK-LABEL: test_fmul:
+; CHECK: mulsd
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+; llvm.fdiv lowers to divsd
+define double @test_fdiv(double %a, double %b) {
+; CHECK-LABEL: test_fdiv:
+; CHECK: divsd
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+; llvm.fptrunc lowers to cvtsd2ss
+define float @test_fptrunc(double %a) {
+; CHECK-LABEL: test_fptrunc:
+; CHECK: cvtsd2ss
+ %r = call float @llvm.fptrunc.f64.f32(double %a)
+ ret float %r
+}
+
+; llvm.fpext lowers to cvtss2sd
+define double @test_fpext(float %a) {
+; CHECK-LABEL: test_fpext:
+; CHECK: cvtss2sd
+ %r = call double @llvm.fpext.f32.f64(float %a)
+ ret double %r
+}
+
+; llvm.sitofp lowers to cvtsi2ss
+define float @test_sitofp(i32 %a) {
+; CHECK-LABEL: test_sitofp:
+; CHECK: cvtsi2ss
+ %r = call float @llvm.sitofp.i32.f32(i32 %a)
+ ret float %r
+}
+
+; llvm.fptosi lowers to cvttss2si
+define i32 @test_fptosi(float %a) {
+; CHECK-LABEL: test_fptosi:
+; CHECK: cvttss2si
+ %r = call i32 @llvm.fptosi.f32.i32(float %a)
+ ret i32 %r
+}
+
+; llvm.fcmp (quiet compare) lowers to ucomiss + conditional flag use
+define i1 @test_fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: test_fcmp_oeq:
+; CHECK: ucomiss
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare float @llvm.fptrunc.f64.f32(double)
+declare double @llvm.fpext.f32.f64(float)
+declare float @llvm.sitofp.i32.f32(i32)
+declare i32 @llvm.fptosi.f32.i32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/Transforms/InstSimplify/fp-intrinsics.ll b/llvm/test/Transforms/InstSimplify/fp-intrinsics.ll
new file mode 100644
index 0000000000000..3ed2c5b5b73ed
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/fp-intrinsics.ll
@@ -0,0 +1,111 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instsimplify -S | FileCheck %s
+
+; Verify that llvm.fadd / llvm.fsub / etc. participate in the same
+; simplifications as their instruction counterparts.
+
+; llvm.fadd(x, 0.0) => x (with nnan/nsz)
+define float @fadd_x_zero(float %x) {
+; CHECK-LABEL: @fadd_x_zero(
+; CHECK-NEXT: ret float [[X:%.*]]
+;
+ %r = call nsz nnan float @llvm.fadd.f32(float %x, float 0.0)
+ ret float %r
+}
+
+; llvm.fadd(0.0, x) => x (with nnan/nsz)
+define float @fadd_zero_x(float %x) {
+; CHECK-LABEL: @fadd_zero_x(
+; CHECK-NEXT: ret float [[X:%.*]]
+;
+ %r = call nsz nnan float @llvm.fadd.f32(float 0.0, float %x)
+ ret float %r
+}
+
+; llvm.fsub(x, 0.0) => x (with nnan/nsz)
+define float @fsub_x_zero(float %x) {
+; CHECK-LABEL: @fsub_x_zero(
+; CHECK-NEXT: ret float [[X:%.*]]
+;
+ %r = call nsz nnan float @llvm.fsub.f32(float %x, float 0.0)
+ ret float %r
+}
+
+; llvm.fmul(x, 1.0) => x (with nnan/nsz)
+define float @fmul_x_one(float %x) {
+; CHECK-LABEL: @fmul_x_one(
+; CHECK-NEXT: ret float [[X:%.*]]
+;
+ %r = call nsz nnan float @llvm.fmul.f32(float %x, float 1.0)
+ ret float %r
+}
+
+; llvm.fdiv(x, 1.0) => x (with nnan/nsz)
+define float @fdiv_x_one(float %x) {
+; CHECK-LABEL: @fdiv_x_one(
+; CHECK-NEXT: ret float [[X:%.*]]
+;
+ %r = call nsz nnan float @llvm.fdiv.f32(float %x, float 1.0)
+ ret float %r
+}
+
+; llvm.fcmp with "true" predicate always returns true
+define i1 @fcmp_true(float %x, float %y) {
+; CHECK-LABEL: @fcmp_true(
+; CHECK-NEXT: ret i1 true
+;
+ %r = call i1 @llvm.fcmp.f32(float %x, float %y, metadata !"true")
+ ret i1 %r
+}
+
+; llvm.fcmp with "false" predicate always returns false
+define i1 @fcmp_false(float %x, float %y) {
+; CHECK-LABEL: @fcmp_false(
+; CHECK-NEXT: ret i1 false
+;
+ %r = call i1 @llvm.fcmp.f32(float %x, float %y, metadata !"false")
+ ret i1 %r
+}
+
+; Constant folding: llvm.fadd(1.0, 2.0) => 3.0
+define float @fadd_const() {
+; CHECK-LABEL: @fadd_const(
+; CHECK-NEXT: ret float 3.000000e+00
+;
+ %r = call float @llvm.fadd.f32(float 1.0, float 2.0)
+ ret float %r
+}
+
+; Constant folding: llvm.fsub(5.0, 2.0) => 3.0
+define float @fsub_const() {
+; CHECK-LABEL: @fsub_const(
+; CHECK-NEXT: ret float 3.000000e+00
+;
+ %r = call float @llvm.fsub.f32(float 5.0, float 2.0)
+ ret float %r
+}
+
+; Constant folding: llvm.fmul(2.0, 3.0) => 6.0
+define float @fmul_const() {
+; CHECK-LABEL: @fmul_const(
+; CHECK-NEXT: ret float 6.000000e+00
+;
+ %r = call float @llvm.fmul.f32(float 2.0, float 3.0)
+ ret float %r
+}
+
+; Constant folding: llvm.fdiv(6.0, 2.0) => 3.0
+define float @fdiv_const() {
+; CHECK-LABEL: @fdiv_const(
+; CHECK-NEXT: ret float 3.000000e+00
+;
+ %r = call float @llvm.fdiv.f32(float 6.0, float 2.0)
+ ret float %r
+}
+
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.frem.f32(float, float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
>From cfda46e81c33a32183c11080dba6d4bbfcf3eb3f Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 12:08:44 -0700
Subject: [PATCH 02/16] [LLVM] Fix FP instruction intrinsic test naming and
simplify dispatch
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Three fixes to the new FP instruction intrinsics introduced in the
previous commit:
1. InstructionSimplify.cpp: Move llvm.fadd/fsub/fmul/fdiv/frem cases
into simplifyBinaryIntrinsic (they have 2 operands so the 3-arg
switch they were in was never reached). Add llvm.fcmp "true"/"false"
predicate folding to the 3-arg switch.
2. Test files: Fix conversion intrinsic naming throughout — LLVM's
overloaded intrinsic naming convention is return-type-first, so
e.g. fptrunc double->float is llvm.fptrunc.f32.f64 (not f64.f32).
Fixed in Assembler test, CodeGen test, and LangRef docs.
3. CodeGen/Generic test: Add REQUIRES: x86-registered-target since
the test checks x86-specific instruction mnemonics (addsd, subsd,
ucomiss, etc.). Skipped in NVPTX-only builds; runs on upstream CI.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 24 ++++----
llvm/lib/Analysis/InstructionSimplify.cpp | 59 +++++++++++--------
.../Assembler/fp-instruction-intrinsics.ll | 50 ++++++++--------
.../Generic/fp-instruction-intrinsics.ll | 17 +++---
4 files changed, 79 insertions(+), 71 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 2c596133c1dd8..2e06343492f3c 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -30697,7 +30697,7 @@ be floating-point types; not all targets support all type combinations.
::
declare half @llvm.fptrunc.f32.f16(float %val)
- declare float @llvm.fptrunc.f64.f32(double %val)
+ declare float @llvm.fptrunc.f32.f64(double %val)
declare half @llvm.fptrunc.f64.f16(double %val)
Overview:
@@ -30723,7 +30723,7 @@ Example:
::
- %r = call float @llvm.fptrunc.f64.f32(double %x)
+ %r = call float @llvm.fptrunc.f32.f64(double %x)
.. _int_fpext_intr:
@@ -30738,7 +30738,7 @@ be floating-point types; not all targets support all type combinations.
::
- declare double @llvm.fpext.f32.f64(float %val)
+ declare double @llvm.fpext.f64.f32(float %val)
declare float @llvm.fpext.f16.f32(half %val)
declare double @llvm.fpext.f16.f64(half %val)
@@ -30765,7 +30765,7 @@ Example:
::
- %r = call double @llvm.fpext.f32.f64(float %x)
+ %r = call double @llvm.fpext.f64.f32(float %x)
.. _int_sitofp_intr:
@@ -30780,7 +30780,7 @@ the destination type must be a floating-point type.
::
- declare float @llvm.sitofp.i32.f32(i32 %val)
+ declare float @llvm.sitofp.f32.i32(i32 %val)
declare double @llvm.sitofp.i64.f64(i64 %val)
Overview:
@@ -30808,7 +30808,7 @@ Example:
::
- %r = call float @llvm.sitofp.i32.f32(i32 %x)
+ %r = call float @llvm.sitofp.f32.i32(i32 %x)
.. _int_uitofp_intr:
@@ -30823,7 +30823,7 @@ the destination type must be a floating-point type.
::
- declare float @llvm.uitofp.i32.f32(i32 %val)
+ declare float @llvm.uitofp.f32.i32(i32 %val)
declare double @llvm.uitofp.i64.f64(i64 %val)
Overview:
@@ -30851,7 +30851,7 @@ Example:
::
- %r = call float @llvm.uitofp.i32.f32(i32 %x)
+ %r = call float @llvm.uitofp.f32.i32(i32 %x)
.. _int_fptosi_intr:
@@ -30866,7 +30866,7 @@ type and the destination type must be an integer type.
::
- declare i32 @llvm.fptosi.f32.i32(float %val)
+ declare i32 @llvm.fptosi.i32.f32(float %val)
declare i64 @llvm.fptosi.f64.i64(double %val)
Overview:
@@ -30894,7 +30894,7 @@ Example:
::
- %r = call i32 @llvm.fptosi.f32.i32(float %x)
+ %r = call i32 @llvm.fptosi.i32.f32(float %x)
.. _int_fptoui_intr:
@@ -30909,7 +30909,7 @@ type and the destination type must be an integer type.
::
- declare i32 @llvm.fptoui.f32.i32(float %val)
+ declare i32 @llvm.fptoui.i32.f32(float %val)
declare i64 @llvm.fptoui.f64.i64(double %val)
Overview:
@@ -30937,7 +30937,7 @@ Example:
::
- %r = call i32 @llvm.fptoui.f32.i32(float %x)
+ %r = call i32 @llvm.fptoui.i32.f32(float %x)
.. _int_experimental_noalias_scope_decl:
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ba555781e8881..84a86a75b9b08 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7214,6 +7214,31 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
case Intrinsic::aarch64_sve_umaxv:
case Intrinsic::aarch64_sve_uminv:
return simplifySVEIntReduction(IID, ReturnType, Op0, Op1);
+ case Intrinsic::fadd: {
+ FastMathFlags FMF =
+ Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
+ return llvm::simplifyFAddInst(Op0, Op1, FMF, Q);
+ }
+ case Intrinsic::fsub: {
+ FastMathFlags FMF =
+ Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
+ return llvm::simplifyFSubInst(Op0, Op1, FMF, Q);
+ }
+ case Intrinsic::fmul: {
+ FastMathFlags FMF =
+ Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
+ return llvm::simplifyFMulInst(Op0, Op1, FMF, Q);
+ }
+ case Intrinsic::fdiv: {
+ FastMathFlags FMF =
+ Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
+ return llvm::simplifyFDivInst(Op0, Op1, FMF, Q);
+ }
+ case Intrinsic::frem: {
+ FastMathFlags FMF =
+ Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
+ return llvm::simplifyFRemInst(Op0, Op1, FMF, Q);
+ }
default:
break;
}
@@ -7297,32 +7322,14 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
return nullptr;
}
- // FP instruction intrinsics: without operand bundles these have default
- // rounding mode and exception behavior, equivalent to FP instructions.
- case Intrinsic::fadd: {
- FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
- return simplifyFAddInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
- RoundingMode::NearestTiesToEven);
- }
- case Intrinsic::fsub: {
- FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
- return simplifyFSubInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
- RoundingMode::NearestTiesToEven);
- }
- case Intrinsic::fmul: {
- FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
- return simplifyFMulInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
- RoundingMode::NearestTiesToEven);
- }
- case Intrinsic::fdiv: {
- FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
- return simplifyFDivInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
- RoundingMode::NearestTiesToEven);
- }
- case Intrinsic::frem: {
- FastMathFlags FMF = cast<FPMathOperator>(Call)->getFastMathFlags();
- return simplifyFRemInst(Args[0], Args[1], FMF, Q, fp::ebIgnore,
- RoundingMode::NearestTiesToEven);
+ case Intrinsic::fcmp: {
+ auto *MD = cast<MetadataAsValue>(Args[2])->getMetadata();
+ StringRef PredStr = cast<MDString>(MD)->getString();
+ if (PredStr == "true")
+ return ConstantInt::getTrue(F->getReturnType());
+ if (PredStr == "false")
+ return ConstantInt::getFalse(F->getReturnType());
+ return nullptr;
}
case Intrinsic::experimental_constrained_fma: {
auto *FPI = cast<ConstrainedFPIntrinsic>(Call);
diff --git a/llvm/test/Assembler/fp-instruction-intrinsics.ll b/llvm/test/Assembler/fp-instruction-intrinsics.ll
index 758433a7ab4a5..2c183cd8c8e63 100644
--- a/llvm/test/Assembler/fp-instruction-intrinsics.ll
+++ b/llvm/test/Assembler/fp-instruction-intrinsics.ll
@@ -47,19 +47,19 @@ define void @test_fast_math_flags(float %a, float %b) {
define void @test_conversions(double %d, float %f, i32 %i) {
; CHECK-LABEL: define void @test_conversions
- %fptrunc = call float @llvm.fptrunc.f64.f32(double %d)
- %fpext = call double @llvm.fpext.f32.f64(float %f)
- %sitofp = call float @llvm.sitofp.i32.f32(i32 %i)
- %uitofp = call float @llvm.uitofp.i32.f32(i32 %i)
- %fptosi = call i32 @llvm.fptosi.f32.i32(float %f)
- %fptoui = call i32 @llvm.fptoui.f32.i32(float %f)
-
-; CHECK: call float @llvm.fptrunc.f64.f32(double %d)
-; CHECK: call double @llvm.fpext.f32.f64(float %f)
-; CHECK: call float @llvm.sitofp.i32.f32(i32 %i)
-; CHECK: call float @llvm.uitofp.i32.f32(i32 %i)
-; CHECK: call i32 @llvm.fptosi.f32.i32(float %f)
-; CHECK: call i32 @llvm.fptoui.f32.i32(float %f)
+ %fptrunc = call float @llvm.fptrunc.f32.f64(double %d)
+ %fpext = call double @llvm.fpext.f64.f32(float %f)
+ %sitofp = call float @llvm.sitofp.f32.i32(i32 %i)
+ %uitofp = call float @llvm.uitofp.f32.i32(i32 %i)
+ %fptosi = call i32 @llvm.fptosi.i32.f32(float %f)
+ %fptoui = call i32 @llvm.fptoui.i32.f32(float %f)
+
+; CHECK: call float @llvm.fptrunc.f32.f64(double %d)
+; CHECK: call double @llvm.fpext.f64.f32(float %f)
+; CHECK: call float @llvm.sitofp.f32.i32(i32 %i)
+; CHECK: call float @llvm.uitofp.f32.i32(i32 %i)
+; CHECK: call i32 @llvm.fptosi.i32.f32(float %f)
+; CHECK: call i32 @llvm.fptoui.i32.f32(float %f)
ret void
}
@@ -108,12 +108,12 @@ define void @test_fcmps(float %a, float %b) {
; CHECK-DAG: declare float @llvm.frem.f32(float, float)
; CHECK-DAG: declare float @llvm.fneg.f32(float)
; CHECK-DAG: declare double @llvm.fadd.f64(double, double)
-; CHECK-DAG: declare float @llvm.fptrunc.f64.f32(double)
-; CHECK-DAG: declare double @llvm.fpext.f32.f64(float)
-; CHECK-DAG: declare float @llvm.sitofp.i32.f32(i32)
-; CHECK-DAG: declare float @llvm.uitofp.i32.f32(i32)
-; CHECK-DAG: declare i32 @llvm.fptosi.f32.i32(float)
-; CHECK-DAG: declare i32 @llvm.fptoui.f32.i32(float)
+; CHECK-DAG: declare float @llvm.fptrunc.f32.f64(double)
+; CHECK-DAG: declare double @llvm.fpext.f64.f32(float)
+; CHECK-DAG: declare float @llvm.sitofp.f32.i32(i32)
+; CHECK-DAG: declare float @llvm.uitofp.f32.i32(i32)
+; CHECK-DAG: declare i32 @llvm.fptosi.i32.f32(float)
+; CHECK-DAG: declare i32 @llvm.fptoui.i32.f32(float)
; CHECK-DAG: declare i1 @llvm.fcmp.f32(float, float, metadata)
; fcmps has memory(inaccessiblemem: readwrite) + willreturn
; CHECK-DAG: declare i1 @llvm.fcmps.f32(float, float, metadata) #[[ATTR:[0-9]+]]
@@ -129,11 +129,11 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare float @llvm.fptrunc.f64.f32(double)
-declare double @llvm.fpext.f32.f64(float)
-declare float @llvm.sitofp.i32.f32(i32)
-declare float @llvm.uitofp.i32.f32(i32)
-declare i32 @llvm.fptosi.f32.i32(float)
-declare i32 @llvm.fptoui.f32.i32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
declare i1 @llvm.fcmp.f32(float, float, metadata)
declare i1 @llvm.fcmps.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
index 2235283048b0b..4cb51016f2352 100644
--- a/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86-registered-target
; RUN: llc -O0 -mtriple=x86_64-pc-linux < %s | FileCheck %s
; Verify that each new FP instruction intrinsic lowers to the expected
@@ -39,7 +40,7 @@ define double @test_fdiv(double %a, double %b) {
define float @test_fptrunc(double %a) {
; CHECK-LABEL: test_fptrunc:
; CHECK: cvtsd2ss
- %r = call float @llvm.fptrunc.f64.f32(double %a)
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
ret float %r
}
@@ -47,7 +48,7 @@ define float @test_fptrunc(double %a) {
define double @test_fpext(float %a) {
; CHECK-LABEL: test_fpext:
; CHECK: cvtss2sd
- %r = call double @llvm.fpext.f32.f64(float %a)
+ %r = call double @llvm.fpext.f64.f32(float %a)
ret double %r
}
@@ -55,7 +56,7 @@ define double @test_fpext(float %a) {
define float @test_sitofp(i32 %a) {
; CHECK-LABEL: test_sitofp:
; CHECK: cvtsi2ss
- %r = call float @llvm.sitofp.i32.f32(i32 %a)
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
ret float %r
}
@@ -63,7 +64,7 @@ define float @test_sitofp(i32 %a) {
define i32 @test_fptosi(float %a) {
; CHECK-LABEL: test_fptosi:
; CHECK: cvttss2si
- %r = call i32 @llvm.fptosi.f32.i32(float %a)
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
ret i32 %r
}
@@ -79,8 +80,8 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare float @llvm.fptrunc.f64.f32(double)
-declare double @llvm.fpext.f32.f64(float)
-declare float @llvm.sitofp.i32.f32(i32)
-declare i32 @llvm.fptosi.f32.i32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
declare i1 @llvm.fcmp.f32(float, float, metadata)
>From cc6a4a4b38b1afe3e98e878d31e5ff8d1e233c5c Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 13:19:11 -0700
Subject: [PATCH 03/16] [LLVM][test] Add per-target CodeGen tests for FP
instruction intrinsics
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add fp-instruction-intrinsics.ll test files for 13 LLVM targets (NVPTX,
X86, AArch64, RISCV, ARM, AMDGPU, PowerPC, Mips, WebAssembly, SystemZ,
Hexagon, LoongArch, SPARC) verifying that llvm.fadd/fsub/fmul/fdiv/fneg,
llvm.fptrunc/fpext/sitofp/uitofp/fptosi/fptoui, and llvm.fcmp intrinsics
lower to the same machine instructions as their corresponding LLVM IR
instructions.
Each file tests:
- f32 and f64 arithmetic intrinsics (where supported in hardware)
- Conversion intrinsics (fptrunc, fpext, sitofp, uitofp, fptosi, fptoui)
- Compare (fcmp oeq)
- Fast-math flag variants (fast, nnan nsz, reassoc/arcp)
Also adds intrinsic fast-math variants to NVPTX/fast-math.ll (verifying
afn on fdiv → div.approx.f32, fast on fadd → add.f32 without .rn) and
AArch64/recp-fastmath.ll (verifying intrinsic form produces identical
reciprocal estimate sequences to instruction form).
NVPTX tests verified locally; other targets gated by REQUIRES: directives
so they only run on full multi-target builds.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
.../AArch64/fp-instruction-intrinsics.ll | 173 ++++++++++++++++
llvm/test/CodeGen/AArch64/recp-fastmath.ll | 27 +++
.../AMDGPU/fp-instruction-intrinsics.ll | 140 +++++++++++++
.../CodeGen/ARM/fp-instruction-intrinsics.ll | 174 ++++++++++++++++
.../Hexagon/fp-instruction-intrinsics.ll | 120 +++++++++++
.../LoongArch/fp-instruction-intrinsics.ll | 173 ++++++++++++++++
.../CodeGen/Mips/fp-instruction-intrinsics.ll | 99 +++++++++
llvm/test/CodeGen/NVPTX/fast-math.ll | 35 ++++
.../NVPTX/fp-instruction-intrinsics.ll | 193 ++++++++++++++++++
.../PowerPC/fp-instruction-intrinsics.ll | 174 ++++++++++++++++
.../RISCV/fp-instruction-intrinsics.ll | 173 ++++++++++++++++
.../SPARC/fp-instruction-intrinsics.ll | 157 ++++++++++++++
.../SystemZ/fp-instruction-intrinsics.ll | 173 ++++++++++++++++
.../WebAssembly/fp-instruction-intrinsics.ll | 173 ++++++++++++++++
.../CodeGen/X86/fp-instruction-intrinsics.ll | 174 ++++++++++++++++
15 files changed, 2158 insertions(+)
create mode 100644 llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
create mode 100644 llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
diff --git a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..4a2e73d2ef0b6
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
@@ -0,0 +1,173 @@
+; REQUIRES: aarch64-registered-target
+; RUN: llc < %s -mtriple=aarch64 -verify-machineinstrs | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: fadd d0, d0, d1
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: fsub d0, d0, d1
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: fmul d0, d0, d1
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: fdiv d0, d0, d1
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: fneg d0, d0
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: fadd s0, s0, s1
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: fsub s0, s0, s1
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: fmul s0, s0, s1
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: fdiv s0, s0, s1
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: fneg s0, s0
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: fcvt s0, d0
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: fcvt d0, s0
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: scvtf s0, w0
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: ucvtf s0, w0
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: fcvtzs w0, s0
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: fcvtzu w0, s0
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: fcmp s0, s1
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same fadd instruction on AArch64
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: fadd s0, s0, s1
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same fmul instruction on AArch64
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: fmul s0, s0, s1
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same fdiv instruction on AArch64
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: fdiv s0, s0, s1
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/AArch64/recp-fastmath.ll b/llvm/test/CodeGen/AArch64/recp-fastmath.ll
index fa1da33007667..d9ec54b1e1659 100644
--- a/llvm/test/CodeGen/AArch64/recp-fastmath.ll
+++ b/llvm/test/CodeGen/AArch64/recp-fastmath.ll
@@ -164,5 +164,32 @@ define <4 x double> @d4recp1(<4 x double> %x) #1 {
; CHECK-NOT: frecps {{v[0-7]\.2d}}, {{v[0-7]\.2d}}, {{v[0-7]\.2d}}
}
+; Intrinsic variants: llvm.fdiv.f32 with fast flag should lower identically to
+; 'fdiv fast float', producing fdiv (no estimate) or frecpe (with estimate).
+
+define float @frecp0_intrinsic(float %x) #0 {
+ %div = call fast float @llvm.fdiv.f32(float 1.0, float %x)
+ ret float %div
+
+; CHECK-LABEL: frecp0_intrinsic:
+; CHECK-NEXT: %bb.0
+; CHECK-NEXT: fmov
+; CHECK-NEXT: fdiv
+}
+
+define float @frecp1_intrinsic(float %x) #1 {
+ %div = call fast float @llvm.fdiv.f32(float 1.0, float %x)
+ ret float %div
+
+; CHECK-LABEL: frecp1_intrinsic:
+; CHECK-NEXT: %bb.0
+; CHECK-NEXT: frecpe [[R:s[0-7]]]
+; CHECK-NEXT: frecps {{s[0-7](, s[0-7])?}}, [[R]]
+; CHECK: frecps {{s[0-7]}}, {{s[0-7]}}, {{s[0-7]}}
+; CHECK-NOT: frecps {{s[0-7]}}, {{s[0-7]}}, {{s[0-7]}}
+}
+
+declare float @llvm.fdiv.f32(float, float)
+
attributes #0 = { nounwind }
attributes #1 = { nounwind "reciprocal-estimates"="div,vec-div" }
diff --git a/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..ba2b589d8640c
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
@@ -0,0 +1,140 @@
+; REQUIRES: amdgpu-registered-target
+; RUN: llc -mtriple=amdgcn -mcpu=gfx900 < %s | FileCheck %s
+
+;;; f32 arithmetic
+
+; CHECK-LABEL: {{^}}fadd_f32:
+; CHECK: v_add_f32
+define amdgpu_kernel void @fadd_f32(ptr addrspace(1) %out, float %a, float %b) {
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}fsub_f32:
+; CHECK: v_sub_f32
+define amdgpu_kernel void @fsub_f32(ptr addrspace(1) %out, float %a, float %b) {
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}fmul_f32:
+; CHECK: v_mul_f32
+define amdgpu_kernel void @fmul_f32(ptr addrspace(1) %out, float %a, float %b) {
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+;;; f64 arithmetic
+
+; CHECK-LABEL: {{^}}fadd_f64:
+; CHECK: v_add_f64
+define amdgpu_kernel void @fadd_f64(ptr addrspace(1) %out, double %a, double %b) {
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ store double %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}fmul_f64:
+; CHECK: v_mul_f64
+define amdgpu_kernel void @fmul_f64(ptr addrspace(1) %out, double %a, double %b) {
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ store double %r, ptr addrspace(1) %out
+ ret void
+}
+
+;;; Conversions
+
+; CHECK-LABEL: {{^}}fptrunc_f64_f32:
+; CHECK: v_cvt_f32_f64
+define amdgpu_kernel void @fptrunc_f64_f32(ptr addrspace(1) %out, double %a) {
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}fpext_f32_f64:
+; CHECK: v_cvt_f64_f32
+define amdgpu_kernel void @fpext_f32_f64(ptr addrspace(1) %out, float %a) {
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ store double %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}sitofp_i32_f32:
+; CHECK: v_cvt_f32_i32
+define amdgpu_kernel void @sitofp_i32_f32(ptr addrspace(1) %out, i32 %a) {
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}uitofp_i32_f32:
+; CHECK: v_cvt_f32_u32
+define amdgpu_kernel void @uitofp_i32_f32(ptr addrspace(1) %out, i32 %a) {
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}fptosi_f32_i32:
+; CHECK: v_cvt_i32_f32
+define amdgpu_kernel void @fptosi_f32_i32(ptr addrspace(1) %out, float %a) {
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ store i32 %r, ptr addrspace(1) %out
+ ret void
+}
+
+; CHECK-LABEL: {{^}}fptoui_f32_i32:
+; CHECK: v_cvt_u32_f32
+define amdgpu_kernel void @fptoui_f32_i32(ptr addrspace(1) %out, float %a) {
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ store i32 %r, ptr addrspace(1) %out
+ ret void
+}
+
+;;; Compare
+
+; CHECK-LABEL: {{^}}fcmp_oeq:
+; CHECK: v_cmp_eq_f32
+define amdgpu_kernel void @fcmp_oeq(ptr addrspace(1) %out, float %a, float %b) {
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ %ext = zext i1 %r to i32
+ store i32 %ext, ptr addrspace(1) %out
+ ret void
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same v_add_f32 on AMDGPU
+; CHECK-LABEL: {{^}}fadd_fast_f32:
+; CHECK: v_add_f32
+define amdgpu_kernel void @fadd_fast_f32(ptr addrspace(1) %out, float %a, float %b) {
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+; nnan nsz on fmul.f32 — same v_mul_f32 on AMDGPU
+; CHECK-LABEL: {{^}}fmul_nnan_nsz_f32:
+; CHECK: v_mul_f32
+define amdgpu_kernel void @fmul_nnan_nsz_f32(ptr addrspace(1) %out, float %a, float %b) {
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ store float %r, ptr addrspace(1) %out
+ ret void
+}
+
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..39b10ac868c62
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
@@ -0,0 +1,174 @@
+; REQUIRES: arm-registered-target
+; RUN: llc < %s -mtriple=arm-apple-ios -mattr=+vfp2 | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: vadd.f64
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: vsub.f64
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: vmul.f64
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: vdiv.f64
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: vneg.f64
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: vadd.f32
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: vsub.f32
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: vmul.f32
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: vdiv.f32
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: vneg.f32
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: vcvt.f32.f64
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: vcvt.f64.f32
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: vcvt.f32.s32
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: vcvt.f32.u32
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: vcvt.s32.f32
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: vcvt.u32.f32
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: vcmp.f32
+; CHECK: vmrs
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same vadd.f32 on ARM VFP2
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: vadd.f32
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same vmul.f32 on ARM VFP2
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: vmul.f32
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same vdiv.f32 on ARM VFP2
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: vdiv.f32
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..b3a328a80d0c9
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
@@ -0,0 +1,120 @@
+; REQUIRES: hexagon-registered-target
+; RUN: llc -mtriple=hexagon -O2 < %s | FileCheck %s
+
+;;; f64 arithmetic (hexagonv66 native ops)
+
+define double @fadd_f64(double %a, double %b) #0 {
+; CHECK-LABEL: fadd_f64:
+; CHECK: dfadd
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) #0 {
+; CHECK-LABEL: fsub_f64:
+; CHECK: dfsub
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) #0 {
+; CHECK-LABEL: fadd_f32:
+; CHECK: sfadd
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) #0 {
+; CHECK-LABEL: fsub_f32:
+; CHECK: sfsub
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) #0 {
+; CHECK-LABEL: fmul_f32:
+; CHECK: sfmpy
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) #0 {
+; CHECK-LABEL: fptrunc:
+; CHECK: convert_df2sf
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) #0 {
+; CHECK-LABEL: fpext:
+; CHECK: convert_sf2df
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) #0 {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: convert_w2sf
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) #0 {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: convert_uw2sf
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) #0 {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: convert_sf2w
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i32 @fcmp_oeq(float %a, float %b) #0 {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: sfcmp.eq
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ %ext = zext i1 %r to i32
+ ret i32 %ext
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same sfadd instruction on Hexagon
+define float @fadd_fast_f32(float %a, float %b) #0 {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: sfadd
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same sfmpy instruction on Hexagon
+define float @fmul_nnan_nsz_f32(float %a, float %b) #0 {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: sfmpy
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
+
+attributes #0 = { nounwind "target-cpu"="hexagonv66" }
diff --git a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..e896169d3765c
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
@@ -0,0 +1,173 @@
+; REQUIRES: loongarch-registered-target
+; RUN: llc < %s -mtriple=loongarch64 -mattr=+d | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: fadd.d $fa0, $fa0, $fa1
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: fsub.d $fa0, $fa0, $fa1
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: fmul.d $fa0, $fa0, $fa1
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: fdiv.d $fa0, $fa0, $fa1
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: fneg.d $fa0, $fa0
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: fadd.s $fa0, $fa0, $fa1
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: fsub.s $fa0, $fa0, $fa1
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: fmul.s $fa0, $fa0, $fa1
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: fdiv.s $fa0, $fa0, $fa1
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: fneg.s $fa0, $fa0
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: fcvt.s.d $fa0, $fa0
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: fcvt.d.s $fa0, $fa0
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: ffint.s.w $fa0
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: ffint.s.wu $fa0
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: ftintrz.w.s $fa{{[0-9]+}}, $fa0
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: ftintrz.wu.s $fa{{[0-9]+}}, $fa0
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: fcmp.ceq.s
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same fadd.s instruction on LoongArch
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: fadd.s $fa0, $fa0, $fa1
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same fmul.s instruction on LoongArch
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: fmul.s $fa0, $fa0, $fa1
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same fdiv.s instruction on LoongArch
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: fdiv.s $fa0, $fa0, $fa1
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..c79d74cd2707d
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
@@ -0,0 +1,99 @@
+; REQUIRES: mips-registered-target
+; RUN: llc < %s -mtriple=mips -mcpu=mips32r2 | FileCheck %s
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: add.s
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: sub.s
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: mul.s
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: div.s
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: neg.s
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: cvt.s.w
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: trunc.w.s
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: c.eq.s
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same add.s instruction on MIPS
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: add.s
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same mul.s instruction on MIPS
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: mul.s
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same div.s instruction on MIPS
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: div.s
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/NVPTX/fast-math.ll b/llvm/test/CodeGen/NVPTX/fast-math.ll
index cc6301bccbdf8..9f12c3145fb77 100644
--- a/llvm/test/CodeGen/NVPTX/fast-math.ll
+++ b/llvm/test/CodeGen/NVPTX/fast-math.ll
@@ -549,4 +549,39 @@ define double @frem_f64(double %a, double %b) {
ret double %rem
}
+; llvm.fdiv.f32 with afn flag lowers to div.approx.f32, same as 'fdiv afn float'
+define float @fdiv_afn_intrinsic(float %a, float %b) {
+; CHECK-LABEL: fdiv_afn_intrinsic(
+; CHECK: {
+; CHECK-NEXT: .reg .b32 %r<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT: // %bb.0:
+; CHECK-NEXT: ld.param.b32 %r1, [fdiv_afn_intrinsic_param_0];
+; CHECK-NEXT: ld.param.b32 %r2, [fdiv_afn_intrinsic_param_1];
+; CHECK-NEXT: div.approx.f32 %r3, %r1, %r2;
+; CHECK-NEXT: st.param.b32 [func_retval0], %r3;
+; CHECK-NEXT: ret;
+ %r = call afn float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+; llvm.fadd.f32 with fast flag removes the .rn rounding qualifier, same as 'fadd fast float'
+define float @fadd_fast_intrinsic(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_intrinsic(
+; CHECK: {
+; CHECK-NEXT: .reg .b32 %r<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT: // %bb.0:
+; CHECK-NEXT: ld.param.b32 %r1, [fadd_fast_intrinsic_param_0];
+; CHECK-NEXT: ld.param.b32 %r2, [fadd_fast_intrinsic_param_1];
+; CHECK-NEXT: add.f32 %r3, %r1, %r2;
+; CHECK-NEXT: st.param.b32 [func_retval0], %r3;
+; CHECK-NEXT: ret;
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fadd.f32(float, float)
+
attributes #1 = { denormal_fpenv(float: preservesign) }
diff --git a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..314f58d62e5a3
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
@@ -0,0 +1,193 @@
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_20 | FileCheck %s
+; RUN: %if ptxas %{ llc < %s -mtriple=nvptx64 -mcpu=sm_20 | %ptxas-verify %}
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64(
+; CHECK: add.rn.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64(
+; CHECK: sub.rn.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64(
+; CHECK: mul.rn.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64(
+; CHECK: div.rn.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64(
+; CHECK: neg.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32(
+; CHECK: add.rn.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32(
+; CHECK: sub.rn.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32(
+; CHECK: mul.rn.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32(
+; CHECK: div.rn.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32(
+; CHECK: neg.f32 %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc(
+; CHECK: cvt.rn.f32.f64 %r{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext(
+; CHECK: cvt.f64.f32 %rd{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32(
+; CHECK: cvt.rn.f32.s32 %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32(
+; CHECK: cvt.rn.f32.u32 %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32(
+; CHECK: cvt.rzi.s32.f32 %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32(
+; CHECK: cvt.rzi.u32.f32 %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq(
+; CHECK: setp.eq.f32 %p{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; afn on fdiv.f32 lowers to div.approx.f32 instead of div.rn.f32
+define float @fdiv_afn_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_afn_f32(
+; CHECK: div.approx.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call afn float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+; fast on fadd.f32 removes the .rn rounding qualifier
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32(
+; CHECK: add.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same as regular mul.rn.f32 for NVPTX
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32(
+; CHECK: mul{{.*}}f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..f0dcd2d82d17b
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
@@ -0,0 +1,174 @@
+; REQUIRES: powerpc-registered-target
+; RUN: llc < %s -mtriple=powerpc64le-unknown-linux -mcpu=pwr8 | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: fadd
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: fsub
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: fmul
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: fdiv
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: fneg
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: fadds
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: fsubs
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: fmuls
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: fdivs
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: fneg
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: frsp
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; fpext is a no-op register move on PowerPC — just verify it compiles
+; CHECK: blr
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: fcfids
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: fcfidus
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: fctiwz
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: fctiwuz
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: fcmpu
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same fadds instruction on PowerPC
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: fadds
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same fmuls instruction on PowerPC
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: fmuls
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same fdivs instruction on PowerPC
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: fdivs
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..c498b3120541f
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
@@ -0,0 +1,173 @@
+; REQUIRES: riscv-registered-target
+; RUN: llc < %s -mtriple=riscv64 -mattr=+d -verify-machineinstrs -target-abi=lp64d | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: fadd.d fa0, fa0, fa1
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: fsub.d fa0, fa0, fa1
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: fmul.d fa0, fa0, fa1
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: fdiv.d fa0, fa0, fa1
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: fneg.d fa0, fa0
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: fadd.s fa0, fa0, fa1
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: fsub.s fa0, fa0, fa1
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: fmul.s fa0, fa0, fa1
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: fdiv.s fa0, fa0, fa1
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: fneg.s fa0, fa0
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: fcvt.s.d fa0, fa0
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: fcvt.d.s fa0, fa0
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: fcvt.s.w fa0, a0
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: fcvt.s.wu fa0, a0
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: fcvt.w.s a0, fa0
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: fcvt.wu.s a0, fa0
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: feq.s a0, fa0, fa1
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same fadd.s instruction on RISCV
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: fadd.s fa0, fa0, fa1
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same fmul.s instruction on RISCV
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: fmul.s fa0, fa0, fa1
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same fdiv.s instruction on RISCV
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: fdiv.s fa0, fa0, fa1
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..bdcd16e4ca1c4
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
@@ -0,0 +1,157 @@
+; REQUIRES: sparc-registered-target
+; RUN: llc < %s -mtriple=sparcv9 | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: faddd
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: fsubd
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: fmuld
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: fdivd
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: fnegd
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: fadds
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: fsubs
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: fmuls
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: fdivs
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: fnegs
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: fdtos
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: fstod
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: fitos
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: fstoi
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: fcmps
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same fadds instruction on SPARC
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: fadds
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same fmuls instruction on SPARC
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: fmuls
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same fdivs instruction on SPARC
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: fdivs
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..eda6edaa56691
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
@@ -0,0 +1,173 @@
+; REQUIRES: systemz-registered-target
+; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z14 | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: adbr
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: sdbr
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: mdbr
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: ddbr
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: lcdbr
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: aebr
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: sebr
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: meebr
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: debr
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: lcebr
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: ledbr
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: ldebr
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: cefbr
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: celfbr
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: cfebr
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: clfebr
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: cebr
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same aebr instruction on SystemZ
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: aebr
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same meebr instruction on SystemZ
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: meebr
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same debr instruction on SystemZ
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: debr
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..06c8df7665bd1
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
@@ -0,0 +1,173 @@
+; REQUIRES: webassembly-registered-target
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: f64.add
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: f64.sub
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: f64.mul
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: f64.div
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: f64.neg
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: f32.add
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: f32.sub
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: f32.mul
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: f32.div
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: f32.neg
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: f32.demote_f64
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: f64.promote_f32
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: f32.convert_i32_s
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: f32.convert_i32_u
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: i32.trunc_f32_s
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: i32.trunc_f32_u
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: f32.eq
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; fast on fadd.f32 — same f32.add on WebAssembly
+define float @fadd_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_fast_f32:
+; CHECK: f32.add
+ %r = call fast float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; nnan nsz on fmul.f32 — same f32.mul on WebAssembly
+define float @fmul_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_nnan_nsz_f32:
+; CHECK: f32.mul
+ %r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv.f32 — same f32.div on WebAssembly
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: f32.div
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
new file mode 100644
index 0000000000000..5d86f48c8630a
--- /dev/null
+++ b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
@@ -0,0 +1,174 @@
+; REQUIRES: x86-registered-target
+; RUN: llc < %s -mtriple=x86_64-pc-linux -mattr=+sse2 | FileCheck %s
+
+;;; f64 arithmetic
+
+define double @fadd_f64(double %a, double %b) {
+; CHECK-LABEL: fadd_f64:
+; CHECK: addsd
+ %r = call double @llvm.fadd.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fsub_f64(double %a, double %b) {
+; CHECK-LABEL: fsub_f64:
+; CHECK: subsd
+ %r = call double @llvm.fsub.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fmul_f64(double %a, double %b) {
+; CHECK-LABEL: fmul_f64:
+; CHECK: mulsd
+ %r = call double @llvm.fmul.f64(double %a, double %b)
+ ret double %r
+}
+
+define double @fdiv_f64(double %a, double %b) {
+; CHECK-LABEL: fdiv_f64:
+; CHECK: divsd
+ %r = call double @llvm.fdiv.f64(double %a, double %b)
+ ret double %r
+}
+
+; FP negation on SSE2 is an XOR with the sign bit
+define double @fneg_f64(double %a) {
+; CHECK-LABEL: fneg_f64:
+; CHECK: xorpd
+ %r = call double @llvm.fneg.f64(double %a)
+ ret double %r
+}
+
+;;; f32 arithmetic
+
+define float @fadd_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_f32:
+; CHECK: addss
+ %r = call float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fsub_f32(float %a, float %b) {
+; CHECK-LABEL: fsub_f32:
+; CHECK: subss
+ %r = call float @llvm.fsub.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fmul_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_f32:
+; CHECK: mulss
+ %r = call float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fdiv_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_f32:
+; CHECK: divss
+ %r = call float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+define float @fneg_f32(float %a) {
+; CHECK-LABEL: fneg_f32:
+; CHECK: xorps
+ %r = call float @llvm.fneg.f32(float %a)
+ ret float %r
+}
+
+;;; Conversions
+
+define float @fptrunc(double %a) {
+; CHECK-LABEL: fptrunc:
+; CHECK: cvtsd2ss
+ %r = call float @llvm.fptrunc.f32.f64(double %a)
+ ret float %r
+}
+
+define double @fpext(float %a) {
+; CHECK-LABEL: fpext:
+; CHECK: cvtss2sd
+ %r = call double @llvm.fpext.f64.f32(float %a)
+ ret double %r
+}
+
+define float @sitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: sitofp_i32_f32:
+; CHECK: cvtsi2ss
+ %r = call float @llvm.sitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define float @uitofp_i32_f32(i32 %a) {
+; CHECK-LABEL: uitofp_i32_f32:
+; CHECK: cvtsi2ss
+ %r = call float @llvm.uitofp.f32.i32(i32 %a)
+ ret float %r
+}
+
+define i32 @fptosi_f32_i32(float %a) {
+; CHECK-LABEL: fptosi_f32_i32:
+; CHECK: cvttss2si
+ %r = call i32 @llvm.fptosi.i32.f32(float %a)
+ ret i32 %r
+}
+
+define i32 @fptoui_f32_i32(float %a) {
+; CHECK-LABEL: fptoui_f32_i32:
+; CHECK: cvttss2si
+ %r = call i32 @llvm.fptoui.i32.f32(float %a)
+ ret i32 %r
+}
+
+;;; Compare
+
+define i1 @fcmp_oeq(float %a, float %b) {
+; CHECK-LABEL: fcmp_oeq:
+; CHECK: ucomiss
+ %r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
+ ret i1 %r
+}
+
+;;; Fast-math flags
+
+; nnan nsz flags — same codegen as plain fadd on x86 SSE2
+define float @fadd_nnan_nsz_f32(float %a, float %b) {
+; CHECK-LABEL: fadd_nnan_nsz_f32:
+; CHECK: addss
+ %r = call nnan nsz float @llvm.fadd.f32(float %a, float %b)
+ ret float %r
+}
+
+; fast flags — same codegen as plain fmul on x86 SSE2
+define float @fmul_fast_f32(float %a, float %b) {
+; CHECK-LABEL: fmul_fast_f32:
+; CHECK: mulss
+ %r = call fast float @llvm.fmul.f32(float %a, float %b)
+ ret float %r
+}
+
+; reassoc on fdiv — still generates divss on plain SSE2
+define float @fdiv_reassoc_f32(float %a, float %b) {
+; CHECK-LABEL: fdiv_reassoc_f32:
+; CHECK: divss
+ %r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
+ ret float %r
+}
+
+declare double @llvm.fadd.f64(double, double)
+declare double @llvm.fsub.f64(double, double)
+declare double @llvm.fmul.f64(double, double)
+declare double @llvm.fdiv.f64(double, double)
+declare double @llvm.fneg.f64(double)
+declare float @llvm.fadd.f32(float, float)
+declare float @llvm.fsub.f32(float, float)
+declare float @llvm.fmul.f32(float, float)
+declare float @llvm.fdiv.f32(float, float)
+declare float @llvm.fneg.f32(float)
+declare float @llvm.fptrunc.f32.f64(double)
+declare double @llvm.fpext.f64.f32(float)
+declare float @llvm.sitofp.f32.i32(i32)
+declare float @llvm.uitofp.f32.i32(i32)
+declare i32 @llvm.fptosi.i32.f32(float)
+declare i32 @llvm.fptoui.i32.f32(float)
+declare i1 @llvm.fcmp.f32(float, float, metadata)
>From 0c347bc69894a35480725b6d9903bece7565b13a Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 13:25:28 -0700
Subject: [PATCH 04/16] [LLVM][test] Add contract FMF tests to FP instruction
intrinsic CodeGen tests
Add fmadd_contract_f32/f64 test functions to each per-target
fp-instruction-intrinsics.ll using 'contract' fast-math flags on both
llvm.fmul and llvm.fadd intrinsics. For targets with FMA hardware support,
verifies that the contract FMF triggers FMA fusion:
NVPTX: fma.rn.f32 / fma.rn.f64
AArch64: fmadd s0, s0, s1, s2 / fmadd d0, d0, d1, d2
RISCV: fmadd.s / fmadd.d
PowerPC: fmadds (f32) / fmadd (f64)
LoongArch: fmadd.s / fmadd.d
SystemZ: maebr (f32) / madbr (f64)
AMDGPU: v_fma_f32 / v_mac_f32
Hexagon: rx += sfmpy(ry, rz) accumulate form
For targets without scalar FMA (X86 SSE2, ARM VFPv2, WebAssembly, Mips,
SPARC), the test verifies that contract compiles cleanly and still
produces separate mul+add instructions.
NVPTX contract FMA tests verified locally.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
.../AArch64/fp-instruction-intrinsics.ll | 18 +++++++++++++++++
.../AMDGPU/fp-instruction-intrinsics.ll | 10 ++++++++++
.../CodeGen/ARM/fp-instruction-intrinsics.ll | 10 ++++++++++
.../Hexagon/fp-instruction-intrinsics.ll | 9 +++++++++
.../LoongArch/fp-instruction-intrinsics.ll | 18 +++++++++++++++++
.../CodeGen/Mips/fp-instruction-intrinsics.ll | 10 ++++++++++
.../NVPTX/fp-instruction-intrinsics.ll | 20 +++++++++++++++++++
.../PowerPC/fp-instruction-intrinsics.ll | 18 +++++++++++++++++
.../RISCV/fp-instruction-intrinsics.ll | 18 +++++++++++++++++
.../SPARC/fp-instruction-intrinsics.ll | 10 ++++++++++
.../SystemZ/fp-instruction-intrinsics.ll | 18 +++++++++++++++++
.../WebAssembly/fp-instruction-intrinsics.ll | 10 ++++++++++
.../CodeGen/X86/fp-instruction-intrinsics.ll | 10 ++++++++++
13 files changed, 179 insertions(+)
diff --git a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
index 4a2e73d2ef0b6..d2cf896caebf4 100644
--- a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
@@ -154,6 +154,24 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd → fmadd (FMA contraction)
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: fmadd s0, s0, s1, s2
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
+; contract on fmul+fadd for f64 → fmadd d
+define double @fmadd_contract_f64(double %a, double %b, double %c) {
+; CHECK-LABEL: fmadd_contract_f64:
+; CHECK: fmadd d0, d0, d1, d2
+ %mul = call contract double @llvm.fmul.f64(double %a, double %b)
+ %add = call contract double @llvm.fadd.f64(double %mul, double %c)
+ ret double %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
index ba2b589d8640c..fcac8a47e77b1 100644
--- a/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
@@ -126,6 +126,16 @@ define amdgpu_kernel void @fmul_nnan_nsz_f32(ptr addrspace(1) %out, float %a, fl
ret void
}
+; contract on fmul+fadd → v_fma_f32 or v_mac_f32 (FMA contraction)
+; CHECK-LABEL: {{^}}fmadd_contract_f32:
+; CHECK: v_{{fma|mac}}_f32
+define amdgpu_kernel void @fmadd_contract_f32(ptr addrspace(1) %out, float %a, float %b, float %c) {
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ store float %add, ptr addrspace(1) %out
+ ret void
+}
+
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
diff --git a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
index 39b10ac868c62..dd89a7b0e4698 100644
--- a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
@@ -155,6 +155,16 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd — VFPv2 has no FMA, produces separate vmul.f32 + vadd.f32
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: vmul.f32
+; CHECK: vadd.f32
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
index b3a328a80d0c9..2f71ebb868f3e 100644
--- a/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
@@ -105,6 +105,15 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) #0 {
ret float %r
}
+; contract on fmul+fadd → FMA accumulate: rx += sfmpy(ry, rz)
+define float @fmadd_contract_f32(float %a, float %b, float %c) #0 {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: += sfmpy(
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare float @llvm.fadd.f32(float, float)
diff --git a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
index e896169d3765c..c31bdb75ec179 100644
--- a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
@@ -154,6 +154,24 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd → fmadd.s (FMA contraction)
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: fmadd.s $fa0, $fa0, $fa1, $fa2
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
+; contract on fmul+fadd for f64 → fmadd.d
+define double @fmadd_contract_f64(double %a, double %b, double %c) {
+; CHECK-LABEL: fmadd_contract_f64:
+; CHECK: fmadd.d $fa0, $fa0, $fa1, $fa2
+ %mul = call contract double @llvm.fmul.f64(double %a, double %b)
+ %add = call contract double @llvm.fadd.f64(double %mul, double %c)
+ ret double %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
index c79d74cd2707d..f7c66c60cf902 100644
--- a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
@@ -89,6 +89,16 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd — mips32r2 has no FMA for f32, produces separate mul.s + add.s
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: mul.s
+; CHECK: add.s
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
diff --git a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
index 314f58d62e5a3..7fa6e583da91f 100644
--- a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
@@ -174,6 +174,26 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd → fma.rn.f32 (FMA contraction)
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32(
+; CHECK: fma.rn.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
+; CHECK: ret
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
+; contract on fmul+fadd for f64 → fma.rn.f64
+define double @fmadd_contract_f64(double %a, double %b, double %c) {
+; CHECK-LABEL: fmadd_contract_f64(
+; CHECK: fma.rn.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}
+; CHECK: ret
+ %mul = call contract double @llvm.fmul.f64(double %a, double %b)
+ %add = call contract double @llvm.fadd.f64(double %mul, double %c)
+ ret double %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
index f0dcd2d82d17b..79edde792c19e 100644
--- a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
@@ -155,6 +155,24 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd → fmadds (FMA contraction, f32)
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: fmadds
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
+; contract on fmul+fadd for f64 → fmadd
+define double @fmadd_contract_f64(double %a, double %b, double %c) {
+; CHECK-LABEL: fmadd_contract_f64:
+; CHECK: fmadd
+ %mul = call contract double @llvm.fmul.f64(double %a, double %b)
+ %add = call contract double @llvm.fadd.f64(double %mul, double %c)
+ ret double %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
index c498b3120541f..4aeb0e9814a41 100644
--- a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
@@ -154,6 +154,24 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd → fmadd.s (FMA contraction)
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: fmadd.s fa0, fa0, fa1, fa2
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
+; contract on fmul+fadd for f64 → fmadd.d
+define double @fmadd_contract_f64(double %a, double %b, double %c) {
+; CHECK-LABEL: fmadd_contract_f64:
+; CHECK: fmadd.d fa0, fa0, fa1, fa2
+ %mul = call contract double @llvm.fmul.f64(double %a, double %b)
+ %add = call contract double @llvm.fadd.f64(double %mul, double %c)
+ ret double %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
index bdcd16e4ca1c4..1e5baea3ccfbc 100644
--- a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
@@ -140,6 +140,16 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd — SPARC v9 has no FMA, produces separate fmuls + fadds
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: fmuls
+; CHECK: fadds
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
index eda6edaa56691..c51182d129664 100644
--- a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
@@ -154,6 +154,24 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd → maebr (FMA contraction, f32)
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: maebr
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
+; contract on fmul+fadd for f64 → madbr
+define double @fmadd_contract_f64(double %a, double %b, double %c) {
+; CHECK-LABEL: fmadd_contract_f64:
+; CHECK: madbr
+ %mul = call contract double @llvm.fmul.f64(double %a, double %b)
+ %add = call contract double @llvm.fadd.f64(double %mul, double %c)
+ ret double %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
index 06c8df7665bd1..9babbc4759cf3 100644
--- a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
@@ -154,6 +154,16 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd — WebAssembly MVP has no scalar FMA, produces f32.mul + f32.add
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: f32.mul
+; CHECK: f32.add
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
index 5d86f48c8630a..3d56065569f64 100644
--- a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
@@ -155,6 +155,16 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
+; contract on fmul+fadd — SSE2 has no FMA, produces separate mulss + addss
+define float @fmadd_contract_f32(float %a, float %b, float %c) {
+; CHECK-LABEL: fmadd_contract_f32:
+; CHECK: mulss
+; CHECK: addss
+ %mul = call contract float @llvm.fmul.f32(float %a, float %b)
+ %add = call contract float @llvm.fadd.f32(float %mul, float %c)
+ ret float %add
+}
+
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
>From 238f3834f7df1f0927793fe75f54bb5f5f866bcc Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 13:27:02 -0700
Subject: [PATCH 05/16] [LLVM][test] Remove contract FMF tests from targets
without scalar FMA
Drop the fmadd_contract_f32 function from X86 (SSE2), ARM (VFPv2),
WebAssembly, Mips, and SPARC, none of which have scalar FMA support and
none of which have existing contract FMF tests to mirror. Keep the
contract tests only on targets with hardware FMA (NVPTX, AArch64, RISCV,
PowerPC, LoongArch, SystemZ, AMDGPU, Hexagon).
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll | 10 ----------
llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll | 10 ----------
llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll | 10 ----------
.../CodeGen/WebAssembly/fp-instruction-intrinsics.ll | 10 ----------
llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll | 10 ----------
5 files changed, 50 deletions(-)
diff --git a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
index dd89a7b0e4698..39b10ac868c62 100644
--- a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
@@ -155,16 +155,6 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd — VFPv2 has no FMA, produces separate vmul.f32 + vadd.f32
-define float @fmadd_contract_f32(float %a, float %b, float %c) {
-; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: vmul.f32
-; CHECK: vadd.f32
- %mul = call contract float @llvm.fmul.f32(float %a, float %b)
- %add = call contract float @llvm.fadd.f32(float %mul, float %c)
- ret float %add
-}
-
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
index f7c66c60cf902..c79d74cd2707d 100644
--- a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
@@ -89,16 +89,6 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd — mips32r2 has no FMA for f32, produces separate mul.s + add.s
-define float @fmadd_contract_f32(float %a, float %b, float %c) {
-; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: mul.s
-; CHECK: add.s
- %mul = call contract float @llvm.fmul.f32(float %a, float %b)
- %add = call contract float @llvm.fadd.f32(float %mul, float %c)
- ret float %add
-}
-
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
diff --git a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
index 1e5baea3ccfbc..bdcd16e4ca1c4 100644
--- a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
@@ -140,16 +140,6 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd — SPARC v9 has no FMA, produces separate fmuls + fadds
-define float @fmadd_contract_f32(float %a, float %b, float %c) {
-; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: fmuls
-; CHECK: fadds
- %mul = call contract float @llvm.fmul.f32(float %a, float %b)
- %add = call contract float @llvm.fadd.f32(float %mul, float %c)
- ret float %add
-}
-
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
index 9babbc4759cf3..06c8df7665bd1 100644
--- a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
@@ -154,16 +154,6 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd — WebAssembly MVP has no scalar FMA, produces f32.mul + f32.add
-define float @fmadd_contract_f32(float %a, float %b, float %c) {
-; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: f32.mul
-; CHECK: f32.add
- %mul = call contract float @llvm.fmul.f32(float %a, float %b)
- %add = call contract float @llvm.fadd.f32(float %mul, float %c)
- ret float %add
-}
-
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
index 3d56065569f64..5d86f48c8630a 100644
--- a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
@@ -155,16 +155,6 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd — SSE2 has no FMA, produces separate mulss + addss
-define float @fmadd_contract_f32(float %a, float %b, float %c) {
-; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: mulss
-; CHECK: addss
- %mul = call contract float @llvm.fmul.f32(float %a, float %b)
- %add = call contract float @llvm.fadd.f32(float %mul, float %c)
- ret float %add
-}
-
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
>From c7365e5aa5d1348a2017d81eb1618f7e7c19b731 Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 13:43:06 -0700
Subject: [PATCH 06/16] [LLVM] Fix clang-format issues in FP instruction
intrinsic lowering
- IRTranslator.cpp: sort StringSwitch.h include alphabetically after
ScopeExit.h/SmallVector.h; fix continuation indent on buildFPTrunc
and collapse buildFPExt to a single line
- SelectionDAGBuilder.cpp: reflow STRICT_FSETCCS getNode call to
respect the 80-column line limit
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 7 +++----
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 6 +++---
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 9eb26378510ab..a0b3842433d09 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -12,9 +12,9 @@
#include "llvm/CodeGen/GlobalISel/IRTranslator.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/BranchProbabilityInfo.h"
@@ -2723,14 +2723,13 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
Register Src = getOrCreateVReg(*CI.getArgOperand(0));
Register Res = getOrCreateVReg(CI);
MIRBuilder.buildFPTrunc(Res, Src,
- MachineInstr::copyFlagsFromInstruction(CI));
+ MachineInstr::copyFlagsFromInstruction(CI));
return true;
}
case Intrinsic::fpext: {
Register Src = getOrCreateVReg(*CI.getArgOperand(0));
Register Res = getOrCreateVReg(CI);
- MIRBuilder.buildFPExt(Res, Src,
- MachineInstr::copyFlagsFromInstruction(CI));
+ MIRBuilder.buildFPExt(Res, Src, MachineInstr::copyFlagsFromInstruction(CI));
return true;
}
case Intrinsic::sitofp: {
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 730851ddfc516..7a3aaa139dfaf 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -7225,9 +7225,9 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
SDVTList VTs = DAG.getVTList(VT, MVT::Other);
SDNodeFlags StrictFlags;
StrictFlags.copyFMF(*cast<FPMathOperator>(&I));
- SDValue Result = DAG.getNode(ISD::STRICT_FSETCCS, sdl, VTs,
- {Chain, LHS, RHS, DAG.getCondCode(Condition)},
- StrictFlags);
+ SDValue Result =
+ DAG.getNode(ISD::STRICT_FSETCCS, sdl, VTs,
+ {Chain, LHS, RHS, DAG.getCondCode(Condition)}, StrictFlags);
pushFPOpOutChain(Result, fp::ebStrict);
setValue(&I, Result.getValue(0));
return;
>From 768f133864291baa4e39e7311d2fe0a723e647cb Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 14:47:10 -0700
Subject: [PATCH 07/16] [LLVM] Fix test CHECK patterns and doc wording for FP
instruction intrinsics
- Generic/X86: fcmp i1 result uses cmpeqss (mask form), not ucomiss
- X86: fneg f64 uses xorp{s,d} pattern (backend may pick either)
- ARM: switch to armv7-linux-gnueabihf (hard-float ABI) so VFP registers
are used directly and vneg.f{32,64} are generated
- SystemZ z14: fneg uses lcdfr; FMA uses wfmasb/wfmadb (vector FP form)
- PowerPC pwr8 VSX: f32 ops use xsaddsp/xssubsp/xsmulsp/xsdivsp/etc.
instead of classic fadds/fsubs/fmuls/fdivs scalar instructions
- WebAssembly: fptosi/fptoui emit i32.trunc_sat_f32_{s,u} by default
- LoongArch: uitofp/fptoui for i32 go through 64-bit path (ffint.s.l /
ftintrz.l.s)
- LangRef: replace "call-form equivalent" with "intrinsic variant"
throughout; remove "primary purpose" sentence from FP intrinsics intro
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 40 +++++++------------
.../CodeGen/ARM/fp-instruction-intrinsics.ll | 2 +-
.../Generic/fp-instruction-intrinsics.ll | 4 +-
.../LoongArch/fp-instruction-intrinsics.ll | 4 +-
.../PowerPC/fp-instruction-intrinsics.ll | 34 ++++++++--------
.../SystemZ/fp-instruction-intrinsics.ll | 12 +++---
.../WebAssembly/fp-instruction-intrinsics.ll | 4 +-
.../CodeGen/X86/fp-instruction-intrinsics.ll | 4 +-
8 files changed, 47 insertions(+), 57 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 2e06343492f3c..c4dc7bcc6b18c 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -10459,8 +10459,7 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fneg <int_fneg>` intrinsic, which is a call-form
-equivalent of this instruction.
+See also the :ref:`llvm.fneg <int_fneg>` intrinsic, which is a intrinsic variant of this instruction.
Example:
""""""""
@@ -10567,8 +10566,7 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fadd <int_fadd>` intrinsic, which is a call-form
-equivalent of this instruction.
+See also the :ref:`llvm.fadd <int_fadd>` intrinsic, which is a intrinsic variant of this instruction.
Example:
""""""""
@@ -10666,8 +10664,7 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fsub <int_fsub>` intrinsic, which is a call-form
-equivalent of this instruction.
+See also the :ref:`llvm.fsub <int_fsub>` intrinsic, which is a intrinsic variant of this instruction.
Example:
""""""""
@@ -10766,8 +10763,7 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fmul <int_fmul>` intrinsic, which is a call-form
-equivalent of this instruction.
+See also the :ref:`llvm.fmul <int_fmul>` intrinsic, which is a intrinsic variant of this instruction.
Example:
""""""""
@@ -10907,8 +10903,7 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fdiv <int_fdiv>` intrinsic, which is a call-form
-equivalent of this instruction.
+See also the :ref:`llvm.fdiv <int_fdiv>` intrinsic, which is a intrinsic variant of this instruction.
Example:
""""""""
@@ -11066,8 +11061,7 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.frem <int_frem>` intrinsic, which is a call-form
-equivalent of this instruction.
+See also the :ref:`llvm.frem <int_frem>` intrinsic, which is a intrinsic variant of this instruction.
Example:
""""""""
@@ -12734,7 +12728,7 @@ flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations.
See also the :ref:`llvm.fptrunc <int_fptrunc_intr>` intrinsic, which is a
-call-form equivalent of this instruction.
+intrinsic variant of this instruction.
Example:
""""""""
@@ -12788,7 +12782,7 @@ flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations.
See also the :ref:`llvm.fpext <int_fpext_intr>` intrinsic, which is a
-call-form equivalent of this instruction.
+intrinsic variant of this instruction.
Example:
""""""""
@@ -12832,7 +12826,7 @@ unsigned integer value. If the value cannot fit in ``ty2``, the result
is a :ref:`poison value <poisonvalues>`.
See also the :ref:`llvm.fptoui <int_fptoui_intr>` intrinsic, which is a
-call-form equivalent of this instruction.
+intrinsic variant of this instruction.
Example:
""""""""
@@ -12877,7 +12871,7 @@ signed integer value. If the value cannot fit in ``ty2``, the result
is a :ref:`poison value <poisonvalues>`.
See also the :ref:`llvm.fptosi <int_fptosi_intr>` intrinsic, which is a
-call-form equivalent of this instruction.
+intrinsic variant of this instruction.
Example:
""""""""
@@ -12929,7 +12923,7 @@ If the ``nneg`` flag is set, and the ``uitofp`` argument is negative,
the result is a poison value.
See also the :ref:`llvm.uitofp <int_uitofp_intr>` intrinsic, which is a
-call-form equivalent of this instruction.
+intrinsic variant of this instruction.
Example:
""""""""
@@ -12976,7 +12970,7 @@ value cannot be exactly represented, it is rounded using the default rounding
mode.
See also the :ref:`llvm.sitofp <int_sitofp_intr>` intrinsic, which is a
-call-form equivalent of this instruction.
+intrinsic variant of this instruction.
Example:
""""""""
@@ -13514,8 +13508,8 @@ only flags that have any effect on its semantics are those that allow
assumptions to be made about the values of input arguments; namely
``nnan``, ``ninf``, and ``reassoc``. See :ref:`fastmath` for more information.
-See also the :ref:`llvm.fcmp <int_fcmp>` intrinsic (quiet comparison, call-form
-equivalent of this instruction) and the :ref:`llvm.fcmps <int_fcmps>` intrinsic
+See also the :ref:`llvm.fcmp <int_fcmp>` intrinsic (quiet comparison, intrinsic
+variant of this instruction) and the :ref:`llvm.fcmps <int_fcmps>` intrinsic
(signaling comparison that always raises on NaN operands).
Example:
@@ -30288,7 +30282,7 @@ would and handles error conditions in the same way.
FP Arithmetic Intrinsics
------------------------
-These intrinsics are call-form equivalents of the standard floating-point
+These intrinsics are intrinsic variants of the standard floating-point
instructions (:ref:`fadd <i_fadd>`, :ref:`fsub <i_fsub>`, :ref:`fmul <i_fmul>`,
:ref:`fdiv <i_fdiv>`, :ref:`frem <i_frem>`, :ref:`fneg <i_fneg>`,
:ref:`fcmp <i_fcmp>`, :ref:`fptrunc <i_fptrunc>`, :ref:`fpext <i_fpext>`,
@@ -30296,10 +30290,6 @@ and the ``fptoui``, ``fptosi``, ``uitofp``, ``sitofp`` conversion instructions).
They behave identically to the corresponding instruction and accept the same
:ref:`fast-math flags <fastmath>`.
-The primary purpose of these intrinsics is to allow per-call FP environment
-control in a future extension; for now they are direct equivalents of their
-instruction counterparts.
-
``llvm.fcmps`` is always ``memory(inaccessiblemem: readwrite)`` and not
speculatable because a signaling comparison raises an FP Invalid Operation
exception whenever either operand is any NaN, including quiet NaNs.
diff --git a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
index 39b10ac868c62..dfaeea0c569b8 100644
--- a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
@@ -1,5 +1,5 @@
; REQUIRES: arm-registered-target
-; RUN: llc < %s -mtriple=arm-apple-ios -mattr=+vfp2 | FileCheck %s
+; RUN: llc < %s -mtriple=armv7-linux-gnueabihf | FileCheck %s
;;; f64 arithmetic
diff --git a/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
index 4cb51016f2352..629876f57fe89 100644
--- a/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Generic/fp-instruction-intrinsics.ll
@@ -68,10 +68,10 @@ define i32 @test_fptosi(float %a) {
ret i32 %r
}
-; llvm.fcmp (quiet compare) lowers to ucomiss + conditional flag use
+; llvm.fcmp (quiet compare) lowers to cmpeqss (SSE mask form for i1 result)
define i1 @test_fcmp_oeq(float %a, float %b) {
; CHECK-LABEL: test_fcmp_oeq:
-; CHECK: ucomiss
+; CHECK: cmpeqss
%r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
ret i1 %r
}
diff --git a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
index c31bdb75ec179..5c90282feedca 100644
--- a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
@@ -100,7 +100,7 @@ define float @sitofp_i32_f32(i32 %a) {
define float @uitofp_i32_f32(i32 %a) {
; CHECK-LABEL: uitofp_i32_f32:
-; CHECK: ffint.s.wu $fa0
+; CHECK: ffint.s.l
%r = call float @llvm.uitofp.f32.i32(i32 %a)
ret float %r
}
@@ -114,7 +114,7 @@ define i32 @fptosi_f32_i32(float %a) {
define i32 @fptoui_f32_i32(float %a) {
; CHECK-LABEL: fptoui_f32_i32:
-; CHECK: ftintrz.wu.s $fa{{[0-9]+}}, $fa0
+; CHECK: ftintrz.l.s
%r = call i32 @llvm.fptoui.i32.f32(float %a)
ret i32 %r
}
diff --git a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
index 79edde792c19e..29aaef7de9270 100644
--- a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
@@ -42,28 +42,28 @@ define double @fneg_f64(double %a) {
define float @fadd_f32(float %a, float %b) {
; CHECK-LABEL: fadd_f32:
-; CHECK: fadds
+; CHECK: xsaddsp
%r = call float @llvm.fadd.f32(float %a, float %b)
ret float %r
}
define float @fsub_f32(float %a, float %b) {
; CHECK-LABEL: fsub_f32:
-; CHECK: fsubs
+; CHECK: xssubsp
%r = call float @llvm.fsub.f32(float %a, float %b)
ret float %r
}
define float @fmul_f32(float %a, float %b) {
; CHECK-LABEL: fmul_f32:
-; CHECK: fmuls
+; CHECK: xsmulsp
%r = call float @llvm.fmul.f32(float %a, float %b)
ret float %r
}
define float @fdiv_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_f32:
-; CHECK: fdivs
+; CHECK: xsdivsp
%r = call float @llvm.fdiv.f32(float %a, float %b)
ret float %r
}
@@ -79,7 +79,7 @@ define float @fneg_f32(float %a) {
define float @fptrunc(double %a) {
; CHECK-LABEL: fptrunc:
-; CHECK: frsp
+; CHECK: xscvdpsp
%r = call float @llvm.fptrunc.f32.f64(double %a)
ret float %r
}
@@ -94,28 +94,28 @@ define double @fpext(float %a) {
define float @sitofp_i32_f32(i32 %a) {
; CHECK-LABEL: sitofp_i32_f32:
-; CHECK: fcfids
+; CHECK: xscvsxdsp
%r = call float @llvm.sitofp.f32.i32(i32 %a)
ret float %r
}
define float @uitofp_i32_f32(i32 %a) {
; CHECK-LABEL: uitofp_i32_f32:
-; CHECK: fcfidus
+; CHECK: xscvuxdsp
%r = call float @llvm.uitofp.f32.i32(i32 %a)
ret float %r
}
define i32 @fptosi_f32_i32(float %a) {
; CHECK-LABEL: fptosi_f32_i32:
-; CHECK: fctiwz
+; CHECK: xscvdpsxws
%r = call i32 @llvm.fptosi.i32.f32(float %a)
ret i32 %r
}
define i32 @fptoui_f32_i32(float %a) {
; CHECK-LABEL: fptoui_f32_i32:
-; CHECK: fctiwuz
+; CHECK: xscvdpuxws
%r = call i32 @llvm.fptoui.i32.f32(float %a)
ret i32 %r
}
@@ -131,34 +131,34 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same fadds instruction on PowerPC
+; fast on fadd.f32 — same xsaddsp instruction on PowerPC VSX
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
-; CHECK: fadds
+; CHECK: xsaddsp
%r = call fast float @llvm.fadd.f32(float %a, float %b)
ret float %r
}
-; nnan nsz on fmul.f32 — same fmuls instruction on PowerPC
+; nnan nsz on fmul.f32 — same xsmulsp instruction on PowerPC VSX
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
-; CHECK: fmuls
+; CHECK: xsmulsp
%r = call nnan nsz float @llvm.fmul.f32(float %a, float %b)
ret float %r
}
-; reassoc on fdiv.f32 — same fdivs instruction on PowerPC
+; reassoc on fdiv.f32 — same xsdivsp instruction on PowerPC VSX
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
-; CHECK: fdivs
+; CHECK: xsdivsp
%r = call reassoc float @llvm.fdiv.f32(float %a, float %b)
ret float %r
}
-; contract on fmul+fadd → fmadds (FMA contraction, f32)
+; contract on fmul+fadd → xsmaddasp (FMA contraction, f32, VSX)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: fmadds
+; CHECK: xsmaddasp
%mul = call contract float @llvm.fmul.f32(float %a, float %b)
%add = call contract float @llvm.fadd.f32(float %mul, float %c)
ret float %add
diff --git a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
index c51182d129664..93fdc2c2d8a6b 100644
--- a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
@@ -33,7 +33,7 @@ define double @fdiv_f64(double %a, double %b) {
define double @fneg_f64(double %a) {
; CHECK-LABEL: fneg_f64:
-; CHECK: lcdbr
+; CHECK: lcdfr
%r = call double @llvm.fneg.f64(double %a)
ret double %r
}
@@ -70,7 +70,7 @@ define float @fdiv_f32(float %a, float %b) {
define float @fneg_f32(float %a) {
; CHECK-LABEL: fneg_f32:
-; CHECK: lcebr
+; CHECK: lcdfr
%r = call float @llvm.fneg.f32(float %a)
ret float %r
}
@@ -154,19 +154,19 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → maebr (FMA contraction, f32)
+; contract on fmul+fadd → wfmasb (FMA contraction, f32, z14 vector FP)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
-; CHECK: maebr
+; CHECK: wfmasb
%mul = call contract float @llvm.fmul.f32(float %a, float %b)
%add = call contract float @llvm.fadd.f32(float %mul, float %c)
ret float %add
}
-; contract on fmul+fadd for f64 → madbr
+; contract on fmul+fadd for f64 → wfmadb (z14 vector FP)
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64:
-; CHECK: madbr
+; CHECK: wfmadb
%mul = call contract double @llvm.fmul.f64(double %a, double %b)
%add = call contract double @llvm.fadd.f64(double %mul, double %c)
ret double %add
diff --git a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
index 06c8df7665bd1..3cb1fed3d5ef9 100644
--- a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
@@ -107,14 +107,14 @@ define float @uitofp_i32_f32(i32 %a) {
define i32 @fptosi_f32_i32(float %a) {
; CHECK-LABEL: fptosi_f32_i32:
-; CHECK: i32.trunc_f32_s
+; CHECK: i32.trunc_sat_f32_s
%r = call i32 @llvm.fptosi.i32.f32(float %a)
ret i32 %r
}
define i32 @fptoui_f32_i32(float %a) {
; CHECK-LABEL: fptoui_f32_i32:
-; CHECK: i32.trunc_f32_u
+; CHECK: i32.trunc_sat_f32_u
%r = call i32 @llvm.fptoui.i32.f32(float %a)
ret i32 %r
}
diff --git a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
index 5d86f48c8630a..5140e1fa5f215 100644
--- a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
@@ -34,7 +34,7 @@ define double @fdiv_f64(double %a, double %b) {
; FP negation on SSE2 is an XOR with the sign bit
define double @fneg_f64(double %a) {
; CHECK-LABEL: fneg_f64:
-; CHECK: xorpd
+; CHECK: xorp{{[sd]}}
%r = call double @llvm.fneg.f64(double %a)
ret double %r
}
@@ -124,7 +124,7 @@ define i32 @fptoui_f32_i32(float %a) {
define i1 @fcmp_oeq(float %a, float %b) {
; CHECK-LABEL: fcmp_oeq:
-; CHECK: ucomiss
+; CHECK: cmpeqss
%r = call i1 @llvm.fcmp.f32(float %a, float %b, metadata !"oeq")
ret i1 %r
}
>From 9e41d1331dc2b8fe5f00392353f1e81759d9470e Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 15:08:29 -0700
Subject: [PATCH 08/16] [LLVM] Fix PowerPC fptrunc CHECK: xsrsp not xscvdpsp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
pwr8 VSX emits xsrsp (Round to Single-Precision) for fptrunc double→float,
not xscvdpsp (Convert DP to SP).
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
index 29aaef7de9270..5bdb5a1f1dfe0 100644
--- a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
@@ -79,7 +79,7 @@ define float @fneg_f32(float %a) {
define float @fptrunc(double %a) {
; CHECK-LABEL: fptrunc:
-; CHECK: xscvdpsp
+; CHECK: xsrsp
%r = call float @llvm.fptrunc.f32.f64(double %a)
ret float %r
}
>From 74f041460b975a1dc0de1d78b03f618918d4612e Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 16:02:16 -0700
Subject: [PATCH 09/16] [LLVM] Replace non-ASCII characters in FP instruction
intrinsics
Replace em-dashes (U+2014) with -- and arrows (U+2192) with ->
in all files added or modified by this patch series.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 2 +-
llvm/include/llvm/IR/Intrinsics.td | 2 +-
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 4 ++--
.../CodeGen/AArch64/fp-instruction-intrinsics.ll | 10 +++++-----
.../test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll | 6 +++---
llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll | 6 +++---
.../CodeGen/Hexagon/fp-instruction-intrinsics.ll | 6 +++---
.../CodeGen/LoongArch/fp-instruction-intrinsics.ll | 10 +++++-----
llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll | 6 +++---
llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll | 6 +++---
.../CodeGen/PowerPC/fp-instruction-intrinsics.ll | 12 ++++++------
llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll | 10 +++++-----
llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll | 6 +++---
.../CodeGen/SystemZ/fp-instruction-intrinsics.ll | 10 +++++-----
.../CodeGen/WebAssembly/fp-instruction-intrinsics.ll | 6 +++---
llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll | 6 +++---
16 files changed, 54 insertions(+), 54 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index c4dc7bcc6b18c..1b3c3e4a723fe 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -30649,7 +30649,7 @@ Overview:
Performs a **signaling** floating-point comparison. Unlike
:ref:`llvm.fcmp <int_fcmp>`, a signaling comparison raises an FP Invalid
-Operation exception whenever either operand is any NaN — including quiet NaNs
+Operation exception whenever either operand is any NaN -- including quiet NaNs
(qNaN). There is no corresponding plain instruction; the closest equivalent is
:ref:`fcmp <i_fcmp>`. For vector types the return type is a vector of ``i1``
with the same number of elements as the operands.
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 2a3979e8ecf23..a39e14ee5cc85 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1245,7 +1245,7 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrNoCreateUndefOrPoison] in
// fcmps: signaling FP compare. Unlike the quiet llvm.fcmp, this raises an
// Invalid Operation exception for *any* NaN operand (not just sNaN).
-// Exception observability is always strict — this intrinsic has
+// Exception observability is always strict -- this intrinsic has
// IntrInaccessibleMemOnly so it cannot be reordered or eliminated.
def int_fcmps : Intrinsic<
[LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>],
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index a0b3842433d09..dad9929ed3377 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2757,7 +2757,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
return true;
}
case Intrinsic::fcmp: {
- // Quiet comparison — no exception raised on NaN operands.
+ // Quiet comparison -- no exception raised on NaN operands.
auto *MD = cast<MetadataAsValue>(CI.getArgOperand(2))->getMetadata();
FCmpInst::Predicate Pred =
StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
@@ -2788,7 +2788,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
return true;
}
case Intrinsic::fcmps: {
- // Signaling comparison — always raises on any NaN, so always strict.
+ // Signaling comparison -- always raises on any NaN, so always strict.
auto *MD = cast<MetadataAsValue>(CI.getArgOperand(2))->getMetadata();
FCmpInst::Predicate Pred =
StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
diff --git a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
index d2cf896caebf4..cb92621656693 100644
--- a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
@@ -130,7 +130,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same fadd instruction on AArch64
+; fast on fadd.f32 -- same fadd instruction on AArch64
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: fadd s0, s0, s1
@@ -138,7 +138,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same fmul instruction on AArch64
+; nnan nsz on fmul.f32 -- same fmul instruction on AArch64
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: fmul s0, s0, s1
@@ -146,7 +146,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same fdiv instruction on AArch64
+; reassoc on fdiv.f32 -- same fdiv instruction on AArch64
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: fdiv s0, s0, s1
@@ -154,7 +154,7 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → fmadd (FMA contraction)
+; contract on fmul+fadd -> fmadd (FMA contraction)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
; CHECK: fmadd s0, s0, s1, s2
@@ -163,7 +163,7 @@ define float @fmadd_contract_f32(float %a, float %b, float %c) {
ret float %add
}
-; contract on fmul+fadd for f64 → fmadd d
+; contract on fmul+fadd for f64 -> fmadd d
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64:
; CHECK: fmadd d0, d0, d1, d2
diff --git a/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
index fcac8a47e77b1..f0efea9972185 100644
--- a/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/AMDGPU/fp-instruction-intrinsics.ll
@@ -108,7 +108,7 @@ define amdgpu_kernel void @fcmp_oeq(ptr addrspace(1) %out, float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same v_add_f32 on AMDGPU
+; fast on fadd.f32 -- same v_add_f32 on AMDGPU
; CHECK-LABEL: {{^}}fadd_fast_f32:
; CHECK: v_add_f32
define amdgpu_kernel void @fadd_fast_f32(ptr addrspace(1) %out, float %a, float %b) {
@@ -117,7 +117,7 @@ define amdgpu_kernel void @fadd_fast_f32(ptr addrspace(1) %out, float %a, float
ret void
}
-; nnan nsz on fmul.f32 — same v_mul_f32 on AMDGPU
+; nnan nsz on fmul.f32 -- same v_mul_f32 on AMDGPU
; CHECK-LABEL: {{^}}fmul_nnan_nsz_f32:
; CHECK: v_mul_f32
define amdgpu_kernel void @fmul_nnan_nsz_f32(ptr addrspace(1) %out, float %a, float %b) {
@@ -126,7 +126,7 @@ define amdgpu_kernel void @fmul_nnan_nsz_f32(ptr addrspace(1) %out, float %a, fl
ret void
}
-; contract on fmul+fadd → v_fma_f32 or v_mac_f32 (FMA contraction)
+; contract on fmul+fadd -> v_fma_f32 or v_mac_f32 (FMA contraction)
; CHECK-LABEL: {{^}}fmadd_contract_f32:
; CHECK: v_{{fma|mac}}_f32
define amdgpu_kernel void @fmadd_contract_f32(ptr addrspace(1) %out, float %a, float %b, float %c) {
diff --git a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
index dfaeea0c569b8..ccc7db64c287a 100644
--- a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
@@ -131,7 +131,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same vadd.f32 on ARM VFP2
+; fast on fadd.f32 -- same vadd.f32 on ARM VFP2
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: vadd.f32
@@ -139,7 +139,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same vmul.f32 on ARM VFP2
+; nnan nsz on fmul.f32 -- same vmul.f32 on ARM VFP2
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: vmul.f32
@@ -147,7 +147,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same vdiv.f32 on ARM VFP2
+; reassoc on fdiv.f32 -- same vdiv.f32 on ARM VFP2
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: vdiv.f32
diff --git a/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
index 2f71ebb868f3e..fde01914e5da3 100644
--- a/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Hexagon/fp-instruction-intrinsics.ll
@@ -89,7 +89,7 @@ define i32 @fcmp_oeq(float %a, float %b) #0 {
;;; Fast-math flags
-; fast on fadd.f32 — same sfadd instruction on Hexagon
+; fast on fadd.f32 -- same sfadd instruction on Hexagon
define float @fadd_fast_f32(float %a, float %b) #0 {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: sfadd
@@ -97,7 +97,7 @@ define float @fadd_fast_f32(float %a, float %b) #0 {
ret float %r
}
-; nnan nsz on fmul.f32 — same sfmpy instruction on Hexagon
+; nnan nsz on fmul.f32 -- same sfmpy instruction on Hexagon
define float @fmul_nnan_nsz_f32(float %a, float %b) #0 {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: sfmpy
@@ -105,7 +105,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) #0 {
ret float %r
}
-; contract on fmul+fadd → FMA accumulate: rx += sfmpy(ry, rz)
+; contract on fmul+fadd -> FMA accumulate: rx += sfmpy(ry, rz)
define float @fmadd_contract_f32(float %a, float %b, float %c) #0 {
; CHECK-LABEL: fmadd_contract_f32:
; CHECK: += sfmpy(
diff --git a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
index 5c90282feedca..b1f95eea4e194 100644
--- a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
@@ -130,7 +130,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same fadd.s instruction on LoongArch
+; fast on fadd.f32 -- same fadd.s instruction on LoongArch
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: fadd.s $fa0, $fa0, $fa1
@@ -138,7 +138,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same fmul.s instruction on LoongArch
+; nnan nsz on fmul.f32 -- same fmul.s instruction on LoongArch
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: fmul.s $fa0, $fa0, $fa1
@@ -146,7 +146,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same fdiv.s instruction on LoongArch
+; reassoc on fdiv.f32 -- same fdiv.s instruction on LoongArch
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: fdiv.s $fa0, $fa0, $fa1
@@ -154,7 +154,7 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → fmadd.s (FMA contraction)
+; contract on fmul+fadd -> fmadd.s (FMA contraction)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
; CHECK: fmadd.s $fa0, $fa0, $fa1, $fa2
@@ -163,7 +163,7 @@ define float @fmadd_contract_f32(float %a, float %b, float %c) {
ret float %add
}
-; contract on fmul+fadd for f64 → fmadd.d
+; contract on fmul+fadd for f64 -> fmadd.d
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64:
; CHECK: fmadd.d $fa0, $fa0, $fa1, $fa2
diff --git a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
index c79d74cd2707d..dba7964cf4a81 100644
--- a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
@@ -65,7 +65,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same add.s instruction on MIPS
+; fast on fadd.f32 -- same add.s instruction on MIPS
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: add.s
@@ -73,7 +73,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same mul.s instruction on MIPS
+; nnan nsz on fmul.f32 -- same mul.s instruction on MIPS
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: mul.s
@@ -81,7 +81,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same div.s instruction on MIPS
+; reassoc on fdiv.f32 -- same div.s instruction on MIPS
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: div.s
diff --git a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
index 7fa6e583da91f..f651b70906251 100644
--- a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
@@ -165,7 +165,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same as regular mul.rn.f32 for NVPTX
+; nnan nsz on fmul.f32 -- same as regular mul.rn.f32 for NVPTX
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32(
; CHECK: mul{{.*}}f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
@@ -174,7 +174,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → fma.rn.f32 (FMA contraction)
+; contract on fmul+fadd -> fma.rn.f32 (FMA contraction)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32(
; CHECK: fma.rn.f32 %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}, %r{{[0-9]+}}
@@ -184,7 +184,7 @@ define float @fmadd_contract_f32(float %a, float %b, float %c) {
ret float %add
}
-; contract on fmul+fadd for f64 → fma.rn.f64
+; contract on fmul+fadd for f64 -> fma.rn.f64
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64(
; CHECK: fma.rn.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}, %rd{{[0-9]+}}
diff --git a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
index 5bdb5a1f1dfe0..0c3295a0d4bc0 100644
--- a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
@@ -86,7 +86,7 @@ define float @fptrunc(double %a) {
define double @fpext(float %a) {
; CHECK-LABEL: fpext:
-; fpext is a no-op register move on PowerPC — just verify it compiles
+; fpext is a no-op register move on PowerPC -- just verify it compiles
; CHECK: blr
%r = call double @llvm.fpext.f64.f32(float %a)
ret double %r
@@ -131,7 +131,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same xsaddsp instruction on PowerPC VSX
+; fast on fadd.f32 -- same xsaddsp instruction on PowerPC VSX
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: xsaddsp
@@ -139,7 +139,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same xsmulsp instruction on PowerPC VSX
+; nnan nsz on fmul.f32 -- same xsmulsp instruction on PowerPC VSX
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: xsmulsp
@@ -147,7 +147,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same xsdivsp instruction on PowerPC VSX
+; reassoc on fdiv.f32 -- same xsdivsp instruction on PowerPC VSX
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: xsdivsp
@@ -155,7 +155,7 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → xsmaddasp (FMA contraction, f32, VSX)
+; contract on fmul+fadd -> xsmaddasp (FMA contraction, f32, VSX)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
; CHECK: xsmaddasp
@@ -164,7 +164,7 @@ define float @fmadd_contract_f32(float %a, float %b, float %c) {
ret float %add
}
-; contract on fmul+fadd for f64 → fmadd
+; contract on fmul+fadd for f64 -> fmadd
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64:
; CHECK: fmadd
diff --git a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
index 4aeb0e9814a41..256e2c95d08de 100644
--- a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
@@ -130,7 +130,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same fadd.s instruction on RISCV
+; fast on fadd.f32 -- same fadd.s instruction on RISCV
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: fadd.s fa0, fa0, fa1
@@ -138,7 +138,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same fmul.s instruction on RISCV
+; nnan nsz on fmul.f32 -- same fmul.s instruction on RISCV
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: fmul.s fa0, fa0, fa1
@@ -146,7 +146,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same fdiv.s instruction on RISCV
+; reassoc on fdiv.f32 -- same fdiv.s instruction on RISCV
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: fdiv.s fa0, fa0, fa1
@@ -154,7 +154,7 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → fmadd.s (FMA contraction)
+; contract on fmul+fadd -> fmadd.s (FMA contraction)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
; CHECK: fmadd.s fa0, fa0, fa1, fa2
@@ -163,7 +163,7 @@ define float @fmadd_contract_f32(float %a, float %b, float %c) {
ret float %add
}
-; contract on fmul+fadd for f64 → fmadd.d
+; contract on fmul+fadd for f64 -> fmadd.d
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64:
; CHECK: fmadd.d fa0, fa0, fa1, fa2
diff --git a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
index bdcd16e4ca1c4..0ae2e1720a939 100644
--- a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
@@ -116,7 +116,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same fadds instruction on SPARC
+; fast on fadd.f32 -- same fadds instruction on SPARC
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: fadds
@@ -124,7 +124,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same fmuls instruction on SPARC
+; nnan nsz on fmul.f32 -- same fmuls instruction on SPARC
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: fmuls
@@ -132,7 +132,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same fdivs instruction on SPARC
+; reassoc on fdiv.f32 -- same fdivs instruction on SPARC
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: fdivs
diff --git a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
index 93fdc2c2d8a6b..000ee1b6c30ef 100644
--- a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
@@ -130,7 +130,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same aebr instruction on SystemZ
+; fast on fadd.f32 -- same aebr instruction on SystemZ
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: aebr
@@ -138,7 +138,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same meebr instruction on SystemZ
+; nnan nsz on fmul.f32 -- same meebr instruction on SystemZ
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: meebr
@@ -146,7 +146,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same debr instruction on SystemZ
+; reassoc on fdiv.f32 -- same debr instruction on SystemZ
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: debr
@@ -154,7 +154,7 @@ define float @fdiv_reassoc_f32(float %a, float %b) {
ret float %r
}
-; contract on fmul+fadd → wfmasb (FMA contraction, f32, z14 vector FP)
+; contract on fmul+fadd -> wfmasb (FMA contraction, f32, z14 vector FP)
define float @fmadd_contract_f32(float %a, float %b, float %c) {
; CHECK-LABEL: fmadd_contract_f32:
; CHECK: wfmasb
@@ -163,7 +163,7 @@ define float @fmadd_contract_f32(float %a, float %b, float %c) {
ret float %add
}
-; contract on fmul+fadd for f64 → wfmadb (z14 vector FP)
+; contract on fmul+fadd for f64 -> wfmadb (z14 vector FP)
define double @fmadd_contract_f64(double %a, double %b, double %c) {
; CHECK-LABEL: fmadd_contract_f64:
; CHECK: wfmadb
diff --git a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
index 3cb1fed3d5ef9..c31db8ea525ab 100644
--- a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
@@ -130,7 +130,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; fast on fadd.f32 — same f32.add on WebAssembly
+; fast on fadd.f32 -- same f32.add on WebAssembly
define float @fadd_fast_f32(float %a, float %b) {
; CHECK-LABEL: fadd_fast_f32:
; CHECK: f32.add
@@ -138,7 +138,7 @@ define float @fadd_fast_f32(float %a, float %b) {
ret float %r
}
-; nnan nsz on fmul.f32 — same f32.mul on WebAssembly
+; nnan nsz on fmul.f32 -- same f32.mul on WebAssembly
define float @fmul_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fmul_nnan_nsz_f32:
; CHECK: f32.mul
@@ -146,7 +146,7 @@ define float @fmul_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv.f32 — same f32.div on WebAssembly
+; reassoc on fdiv.f32 -- same f32.div on WebAssembly
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: f32.div
diff --git a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
index 5140e1fa5f215..962365ad10a1e 100644
--- a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
@@ -131,7 +131,7 @@ define i1 @fcmp_oeq(float %a, float %b) {
;;; Fast-math flags
-; nnan nsz flags — same codegen as plain fadd on x86 SSE2
+; nnan nsz flags -- same codegen as plain fadd on x86 SSE2
define float @fadd_nnan_nsz_f32(float %a, float %b) {
; CHECK-LABEL: fadd_nnan_nsz_f32:
; CHECK: addss
@@ -139,7 +139,7 @@ define float @fadd_nnan_nsz_f32(float %a, float %b) {
ret float %r
}
-; fast flags — same codegen as plain fmul on x86 SSE2
+; fast flags -- same codegen as plain fmul on x86 SSE2
define float @fmul_fast_f32(float %a, float %b) {
; CHECK-LABEL: fmul_fast_f32:
; CHECK: mulss
@@ -147,7 +147,7 @@ define float @fmul_fast_f32(float %a, float %b) {
ret float %r
}
-; reassoc on fdiv — still generates divss on plain SSE2
+; reassoc on fdiv -- still generates divss on plain SSE2
define float @fdiv_reassoc_f32(float %a, float %b) {
; CHECK-LABEL: fdiv_reassoc_f32:
; CHECK: divss
>From 43a8a33600d5d229d0ae4d9e21ca540164e2d43d Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 16:10:41 -0700
Subject: [PATCH 10/16] [LLVM] Add FPCmpIntrinsic class; verify fcmp/fcmps
predicate in Verifier
Introduce FPCmpIntrinsic (parallel to ConstrainedFPCmpIntrinsic) with a
getPredicate() method that decodes the metadata string argument. This
replaces four identical StringSwitch blocks in SelectionDAGBuilder and
IRTranslator with a single shared implementation.
Also extend getFPPredicateFromMD to handle "true" and "false" predicates
(valid for llvm.fcmp, consistent with the fcmp instruction), and add a
Verifier check for llvm.fcmp/llvm.fcmps that mirrors the existing check
for llvm.experimental.constrained.fcmp/fcmps.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/include/llvm/IR/IntrinsicInst.h | 17 +++++++
llvm/lib/Analysis/InstructionSimplify.cpp | 7 ++-
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 44 +------------------
.../SelectionDAG/SelectionDAGBuilder.cpp | 42 +-----------------
llvm/lib/IR/IntrinsicInst.cpp | 6 +++
llvm/lib/IR/Verifier.cpp | 7 +++
6 files changed, 37 insertions(+), 86 deletions(-)
diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h
index 78a0cd569d5bd..31e562ced7938 100644
--- a/llvm/include/llvm/IR/IntrinsicInst.h
+++ b/llvm/include/llvm/IR/IntrinsicInst.h
@@ -766,6 +766,23 @@ class ConstrainedFPCmpIntrinsic : public ConstrainedFPIntrinsic {
}
};
+/// Represents the llvm.fcmp and llvm.fcmps intrinsics.
+class FPCmpIntrinsic : public IntrinsicInst {
+public:
+ LLVM_ABI FCmpInst::Predicate getPredicate() const;
+ bool isSignaling() const {
+ return getIntrinsicID() == Intrinsic::fcmps;
+ }
+
+ static bool classof(const IntrinsicInst *I) {
+ return I->getIntrinsicID() == Intrinsic::fcmp ||
+ I->getIntrinsicID() == Intrinsic::fcmps;
+ }
+ static bool classof(const Value *V) {
+ return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
+ }
+};
+
/// This class represents min/max intrinsics.
class MinMaxIntrinsic : public IntrinsicInst {
public:
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 84a86a75b9b08..0853363c7c8dd 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7323,11 +7323,10 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
return nullptr;
}
case Intrinsic::fcmp: {
- auto *MD = cast<MetadataAsValue>(Args[2])->getMetadata();
- StringRef PredStr = cast<MDString>(MD)->getString();
- if (PredStr == "true")
+ FCmpInst::Predicate Pred = cast<FPCmpIntrinsic>(Call)->getPredicate();
+ if (Pred == FCmpInst::FCMP_TRUE)
return ConstantInt::getTrue(F->getReturnType());
- if (PredStr == "false")
+ if (Pred == FCmpInst::FCMP_FALSE)
return ConstantInt::getFalse(F->getReturnType());
return nullptr;
}
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index dad9929ed3377..2a819953d2803 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2758,28 +2758,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
}
case Intrinsic::fcmp: {
// Quiet comparison -- no exception raised on NaN operands.
- auto *MD = cast<MetadataAsValue>(CI.getArgOperand(2))->getMetadata();
- FCmpInst::Predicate Pred =
- StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
- .Case("oeq", FCmpInst::FCMP_OEQ)
- .Case("ogt", FCmpInst::FCMP_OGT)
- .Case("oge", FCmpInst::FCMP_OGE)
- .Case("olt", FCmpInst::FCMP_OLT)
- .Case("ole", FCmpInst::FCMP_OLE)
- .Case("one", FCmpInst::FCMP_ONE)
- .Case("ord", FCmpInst::FCMP_ORD)
- .Case("uno", FCmpInst::FCMP_UNO)
- .Case("ueq", FCmpInst::FCMP_UEQ)
- .Case("ugt", FCmpInst::FCMP_UGT)
- .Case("uge", FCmpInst::FCMP_UGE)
- .Case("ult", FCmpInst::FCMP_ULT)
- .Case("ule", FCmpInst::FCMP_ULE)
- .Case("une", FCmpInst::FCMP_UNE)
- .Case("true", FCmpInst::FCMP_TRUE)
- .Case("false", FCmpInst::FCMP_FALSE)
- .Default(FCmpInst::BAD_FCMP_PREDICATE);
- assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
- "invalid predicate in llvm.fcmp");
+ FCmpInst::Predicate Pred = cast<FPCmpIntrinsic>(&CI)->getPredicate();
uint32_t Flags = MachineInstr::copyFlagsFromInstruction(CI);
Register Op0 = getOrCreateVReg(*CI.getArgOperand(0));
Register Op1 = getOrCreateVReg(*CI.getArgOperand(1));
@@ -2789,26 +2768,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
}
case Intrinsic::fcmps: {
// Signaling comparison -- always raises on any NaN, so always strict.
- auto *MD = cast<MetadataAsValue>(CI.getArgOperand(2))->getMetadata();
- FCmpInst::Predicate Pred =
- StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
- .Case("oeq", FCmpInst::FCMP_OEQ)
- .Case("ogt", FCmpInst::FCMP_OGT)
- .Case("oge", FCmpInst::FCMP_OGE)
- .Case("olt", FCmpInst::FCMP_OLT)
- .Case("ole", FCmpInst::FCMP_OLE)
- .Case("one", FCmpInst::FCMP_ONE)
- .Case("ord", FCmpInst::FCMP_ORD)
- .Case("uno", FCmpInst::FCMP_UNO)
- .Case("ueq", FCmpInst::FCMP_UEQ)
- .Case("ugt", FCmpInst::FCMP_UGT)
- .Case("uge", FCmpInst::FCMP_UGE)
- .Case("ult", FCmpInst::FCMP_ULT)
- .Case("ule", FCmpInst::FCMP_ULE)
- .Case("une", FCmpInst::FCMP_UNE)
- .Default(FCmpInst::BAD_FCMP_PREDICATE);
- assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
- "invalid predicate in llvm.fcmps");
+ FCmpInst::Predicate Pred = cast<FPCmpIntrinsic>(&CI)->getPredicate();
uint32_t Flags = MachineInstr::copyFlagsFromInstruction(CI);
Register Op0 = getOrCreateVReg(*CI.getArgOperand(0));
Register Op1 = getOrCreateVReg(*CI.getArgOperand(1));
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 7a3aaa139dfaf..000fc89bf782b 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -7167,26 +7167,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
return;
}
case Intrinsic::fcmp: {
- Metadata *MD = cast<MetadataAsValue>(I.getArgOperand(2))->getMetadata();
- FCmpInst::Predicate Pred =
- StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
- .Case("oeq", FCmpInst::FCMP_OEQ)
- .Case("ogt", FCmpInst::FCMP_OGT)
- .Case("oge", FCmpInst::FCMP_OGE)
- .Case("olt", FCmpInst::FCMP_OLT)
- .Case("ole", FCmpInst::FCMP_OLE)
- .Case("one", FCmpInst::FCMP_ONE)
- .Case("ord", FCmpInst::FCMP_ORD)
- .Case("uno", FCmpInst::FCMP_UNO)
- .Case("ueq", FCmpInst::FCMP_UEQ)
- .Case("ugt", FCmpInst::FCMP_UGT)
- .Case("uge", FCmpInst::FCMP_UGE)
- .Case("ult", FCmpInst::FCMP_ULT)
- .Case("ule", FCmpInst::FCMP_ULE)
- .Case("une", FCmpInst::FCMP_UNE)
- .Default(FCmpInst::BAD_FCMP_PREDICATE);
- assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
- "invalid predicate in llvm.fcmp");
+ FCmpInst::Predicate Pred = cast<FPCmpIntrinsic>(&I)->getPredicate();
ISD::CondCode Condition = getFCmpCondCode(Pred);
EVT DestVT = TLI.getValueType(DAG.getDataLayout(), I.getType());
setValue(&I, DAG.getSetCC(sdl, DestVT, getValue(I.getArgOperand(0)),
@@ -7198,26 +7179,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
SDValue Chain = getFPOperationRoot(fp::ebStrict);
SDValue LHS = getValue(I.getArgOperand(0));
SDValue RHS = getValue(I.getArgOperand(1));
- Metadata *MD = cast<MetadataAsValue>(I.getArgOperand(2))->getMetadata();
- FCmpInst::Predicate Pred =
- StringSwitch<FCmpInst::Predicate>(cast<MDString>(MD)->getString())
- .Case("oeq", FCmpInst::FCMP_OEQ)
- .Case("ogt", FCmpInst::FCMP_OGT)
- .Case("oge", FCmpInst::FCMP_OGE)
- .Case("olt", FCmpInst::FCMP_OLT)
- .Case("ole", FCmpInst::FCMP_OLE)
- .Case("one", FCmpInst::FCMP_ONE)
- .Case("ord", FCmpInst::FCMP_ORD)
- .Case("uno", FCmpInst::FCMP_UNO)
- .Case("ueq", FCmpInst::FCMP_UEQ)
- .Case("ugt", FCmpInst::FCMP_UGT)
- .Case("uge", FCmpInst::FCMP_UGE)
- .Case("ult", FCmpInst::FCMP_ULT)
- .Case("ule", FCmpInst::FCMP_ULE)
- .Case("une", FCmpInst::FCMP_UNE)
- .Default(FCmpInst::BAD_FCMP_PREDICATE);
- assert(Pred != FCmpInst::BAD_FCMP_PREDICATE &&
- "invalid predicate in llvm.fcmps");
+ FCmpInst::Predicate Pred = cast<FPCmpIntrinsic>(&I)->getPredicate();
ISD::CondCode Condition = getFCmpCondCode(Pred);
if (DAG.isKnownNeverNaN(LHS) && DAG.isKnownNeverNaN(RHS))
Condition = getFCmpCodeWithoutNaN(Condition);
diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp
index 5b3e3cf45397f..fd169e2a4dfe5 100644
--- a/llvm/lib/IR/IntrinsicInst.cpp
+++ b/llvm/lib/IR/IntrinsicInst.cpp
@@ -332,6 +332,8 @@ static FCmpInst::Predicate getFPPredicateFromMD(const Value *Op) {
.Case("ult", FCmpInst::FCMP_ULT)
.Case("ule", FCmpInst::FCMP_ULE)
.Case("une", FCmpInst::FCMP_UNE)
+ .Case("true", FCmpInst::FCMP_TRUE)
+ .Case("false", FCmpInst::FCMP_FALSE)
.Default(FCmpInst::BAD_FCMP_PREDICATE);
}
@@ -339,6 +341,10 @@ FCmpInst::Predicate ConstrainedFPCmpIntrinsic::getPredicate() const {
return getFPPredicateFromMD(getArgOperand(2));
}
+FCmpInst::Predicate FPCmpIntrinsic::getPredicate() const {
+ return getFPPredicateFromMD(getArgOperand(2));
+}
+
unsigned ConstrainedFPIntrinsic::getNonMetadataArgCount() const {
// All constrained fp intrinsics have "fpexcept" metadata.
unsigned NumArgs = arg_size() - 1;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 9dba574f6e9eb..c91512e6e3367 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6157,6 +6157,13 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
#undef BEGIN_REGISTER_VP_INTRINSIC
visitVPIntrinsic(cast<VPIntrinsic>(Call));
break;
+ case Intrinsic::fcmp:
+ case Intrinsic::fcmps: {
+ auto Pred = cast<FPCmpIntrinsic>(&Call)->getPredicate();
+ Check(CmpInst::isFPPredicate(Pred),
+ "invalid predicate for FP comparison intrinsic", &Call);
+ break;
+ }
#define INSTRUCTION(NAME, NARGS, ROUND_MODE, INTRINSIC) \
case Intrinsic::INTRINSIC:
#include "llvm/IR/ConstrainedOps.def"
>From 35a4de721cf446a47e93d19ad5711a69d026a951 Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 22 Apr 2026 16:16:29 -0700
Subject: [PATCH 11/16] [LLVM] clang-format: collapse
FPCmpIntrinsic::isSignaling to one line
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/include/llvm/IR/IntrinsicInst.h | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h
index 31e562ced7938..0b7e7b1335930 100644
--- a/llvm/include/llvm/IR/IntrinsicInst.h
+++ b/llvm/include/llvm/IR/IntrinsicInst.h
@@ -770,9 +770,7 @@ class ConstrainedFPCmpIntrinsic : public ConstrainedFPIntrinsic {
class FPCmpIntrinsic : public IntrinsicInst {
public:
LLVM_ABI FCmpInst::Predicate getPredicate() const;
- bool isSignaling() const {
- return getIntrinsicID() == Intrinsic::fcmps;
- }
+ bool isSignaling() const { return getIntrinsicID() == Intrinsic::fcmps; }
static bool classof(const IntrinsicInst *I) {
return I->getIntrinsicID() == Intrinsic::fcmp ||
>From 869208ae71ab278b17fd40f55834c833785e42f2 Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 29 Apr 2026 02:54:40 -0700
Subject: [PATCH 12/16] [LLVM] Address PR review: FP instruction intrinsics doc
+ folding fixes
- Remove "See also the llvm.X intrinsic" cross-references from the 13
FP instruction pages (andykaylor: "not necessary")
- Rewrite the FP Arithmetic Intrinsics section intro to explain WHY the
intrinsics exist (as call-instruction equivalents that support operand
bundles for non-default FP environment behavior) and remove the
duplicated llvm.fcmps note that belonged only in the llvm.fcmps entry
- Qualify each intrinsic's "Always memory(none) and speculatable" claim
with "without operand bundles that change FP environment behavior"
- In canConstantFoldCallTo, guard the fadd/fsub/fmul/fdiv/frem cases
with !Call->hasOperandBundles() so we don't fold when the FP
environment is non-default
- In simplifyBinaryIntrinsic, bail out early for fadd/fsub/fmul/fdiv/
frem when operand bundles are present rather than silently using the
default rounding mode and exception behavior
- In simplifyIntrinsic, guard the llvm.fcmp FCMP_TRUE/FCMP_FALSE folds
against operand bundles that could enable FP exceptions
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 92 +++++++++--------------
llvm/lib/Analysis/ConstantFolding.cpp | 22 ++++--
llvm/lib/Analysis/InstructionSimplify.cpp | 14 ++++
3 files changed, 62 insertions(+), 66 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 1b3c3e4a723fe..cc358de2cc786 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -10459,8 +10459,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fneg <int_fneg>` intrinsic, which is a intrinsic variant of this instruction.
-
Example:
""""""""
@@ -10566,8 +10564,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fadd <int_fadd>` intrinsic, which is a intrinsic variant of this instruction.
-
Example:
""""""""
@@ -10664,8 +10660,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fsub <int_fsub>` intrinsic, which is a intrinsic variant of this instruction.
-
Example:
""""""""
@@ -10763,8 +10757,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fmul <int_fmul>` intrinsic, which is a intrinsic variant of this instruction.
-
Example:
""""""""
@@ -10903,8 +10895,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.fdiv <int_fdiv>` intrinsic, which is a intrinsic variant of this instruction.
-
Example:
""""""""
@@ -11061,8 +11051,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations:
-See also the :ref:`llvm.frem <int_frem>` intrinsic, which is a intrinsic variant of this instruction.
-
Example:
""""""""
@@ -12727,9 +12715,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations.
-See also the :ref:`llvm.fptrunc <int_fptrunc_intr>` intrinsic, which is a
-intrinsic variant of this instruction.
-
Example:
""""""""
@@ -12781,9 +12766,6 @@ This instruction can also take any number of :ref:`fast-math
flags <fastmath>`, which are optimization hints to enable otherwise
unsafe floating-point optimizations.
-See also the :ref:`llvm.fpext <int_fpext_intr>` intrinsic, which is a
-intrinsic variant of this instruction.
-
Example:
""""""""
@@ -12825,9 +12807,6 @@ The '``fptoui``' instruction converts its :ref:`floating-point
unsigned integer value. If the value cannot fit in ``ty2``, the result
is a :ref:`poison value <poisonvalues>`.
-See also the :ref:`llvm.fptoui <int_fptoui_intr>` intrinsic, which is a
-intrinsic variant of this instruction.
-
Example:
""""""""
@@ -12870,9 +12849,6 @@ The '``fptosi``' instruction converts its :ref:`floating-point
signed integer value. If the value cannot fit in ``ty2``, the result
is a :ref:`poison value <poisonvalues>`.
-See also the :ref:`llvm.fptosi <int_fptosi_intr>` intrinsic, which is a
-intrinsic variant of this instruction.
-
Example:
""""""""
@@ -12922,9 +12898,6 @@ the default rounding mode.
If the ``nneg`` flag is set, and the ``uitofp`` argument is negative,
the result is a poison value.
-See also the :ref:`llvm.uitofp <int_uitofp_intr>` intrinsic, which is a
-intrinsic variant of this instruction.
-
Example:
""""""""
@@ -12969,9 +12942,6 @@ quantity and converts it to the corresponding floating-point value. If the
value cannot be exactly represented, it is rounded using the default rounding
mode.
-See also the :ref:`llvm.sitofp <int_sitofp_intr>` intrinsic, which is a
-intrinsic variant of this instruction.
-
Example:
""""""""
@@ -13508,10 +13478,6 @@ only flags that have any effect on its semantics are those that allow
assumptions to be made about the values of input arguments; namely
``nnan``, ``ninf``, and ``reassoc``. See :ref:`fastmath` for more information.
-See also the :ref:`llvm.fcmp <int_fcmp>` intrinsic (quiet comparison, intrinsic
-variant of this instruction) and the :ref:`llvm.fcmps <int_fcmps>` intrinsic
-(signaling comparison that always raises on NaN operands).
-
Example:
""""""""
@@ -30282,17 +30248,14 @@ would and handles error conditions in the same way.
FP Arithmetic Intrinsics
------------------------
-These intrinsics are intrinsic variants of the standard floating-point
-instructions (:ref:`fadd <i_fadd>`, :ref:`fsub <i_fsub>`, :ref:`fmul <i_fmul>`,
-:ref:`fdiv <i_fdiv>`, :ref:`frem <i_frem>`, :ref:`fneg <i_fneg>`,
-:ref:`fcmp <i_fcmp>`, :ref:`fptrunc <i_fptrunc>`, :ref:`fpext <i_fpext>`,
-and the ``fptoui``, ``fptosi``, ``uitofp``, ``sitofp`` conversion instructions).
-They behave identically to the corresponding instruction and accept the same
-:ref:`fast-math flags <fastmath>`.
-
-``llvm.fcmps`` is always ``memory(inaccessiblemem: readwrite)`` and not
-speculatable because a signaling comparison raises an FP Invalid Operation
-exception whenever either operand is any NaN, including quiet NaNs.
+These intrinsics are call-instruction equivalents of the standard
+floating-point instructions. They accept the same
+:ref:`fast-math flags <fastmath>` as the corresponding instruction and
+lower to it in the common case (no operand bundles). Clients that need
+to express non-default FP environment behavior (such as a specific
+rounding mode or exception-handling policy) can attach operand bundles
+to these calls; when such bundles are present the call may no longer be
+``memory(none)`` or speculatable.
.. _int_fadd:
@@ -30330,7 +30293,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fadd <i_fadd>` instruction. Always ``memory(none)``
+Equivalent to the :ref:`fadd <i_fadd>` instruction. Without operand
+bundles that change FP environment behavior, always ``memory(none)``
and speculatable.
Example:
@@ -30376,7 +30340,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fsub <i_fsub>` instruction. Always ``memory(none)``
+Equivalent to the :ref:`fsub <i_fsub>` instruction. Without operand
+bundles that change FP environment behavior, always ``memory(none)``
and speculatable.
Example:
@@ -30422,7 +30387,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fmul <i_fmul>` instruction. Always ``memory(none)``
+Equivalent to the :ref:`fmul <i_fmul>` instruction. Without operand
+bundles that change FP environment behavior, always ``memory(none)``
and speculatable.
Example:
@@ -30468,7 +30434,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fdiv <i_fdiv>` instruction. Always ``memory(none)``
+Equivalent to the :ref:`fdiv <i_fdiv>` instruction. Without operand
+bundles that change FP environment behavior, always ``memory(none)``
and speculatable.
Example:
@@ -30514,7 +30481,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`frem <i_frem>` instruction. Always ``memory(none)``
+Equivalent to the :ref:`frem <i_frem>` instruction. Without operand
+bundles that change FP environment behavior, always ``memory(none)``
and speculatable.
Example:
@@ -30560,7 +30528,8 @@ The argument and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fneg <i_fneg>` instruction. Always ``memory(none)``
+Equivalent to the :ref:`fneg <i_fneg>` instruction. Without operand
+bundles that change FP environment behavior, always ``memory(none)``
and speculatable.
Example:
@@ -30613,7 +30582,8 @@ Semantics:
""""""""""
The comparison semantics :ref:`follow those of the fcmp instruction
-<fcmp_md_cc_sem>`. Always ``memory(none)`` and speculatable.
+<fcmp_md_cc_sem>`. Without operand bundles that enable FP exceptions,
+always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30705,7 +30675,8 @@ floating-point type. The source type must be larger than the destination type.
Semantics:
""""""""""
-Equivalent to the :ref:`fptrunc <i_fptrunc>` instruction. Always
+Equivalent to the :ref:`fptrunc <i_fptrunc>` instruction. Without
+operand bundles that change FP environment behavior, always
``memory(none)`` and speculatable.
Example:
@@ -30747,7 +30718,8 @@ floating-point type. The source type must be smaller than the destination type.
Semantics:
""""""""""
-Equivalent to the :ref:`fpext <i_fpext>` instruction. Always
+Equivalent to the :ref:`fpext <i_fpext>` instruction. Without
+operand bundles that change FP environment behavior, always
``memory(none)`` and speculatable.
Example:
@@ -30791,7 +30763,8 @@ Semantics:
Equivalent to the ``sitofp`` instruction. If the integer value cannot be
represented exactly in the destination type, it is rounded using the default
-rounding mode. Always ``memory(none)`` and speculatable.
+rounding mode. Without operand bundles that change FP environment behavior,
+always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30834,7 +30807,8 @@ Semantics:
Equivalent to the ``uitofp`` instruction. If the integer value cannot be
represented exactly in the destination type, it is rounded using the default
-rounding mode. Always ``memory(none)`` and speculatable.
+rounding mode. Without operand bundles that change FP environment behavior,
+always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30877,7 +30851,8 @@ Semantics:
Equivalent to the ``fptosi`` instruction. If the floating-point value cannot
be represented in the destination integer type, the result is a
-:ref:`poison value <poisonvalues>`. Always ``memory(none)`` and speculatable.
+:ref:`poison value <poisonvalues>`. Without operand bundles that change FP
+environment behavior, always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30920,7 +30895,8 @@ Semantics:
Equivalent to the ``fptoui`` instruction. If the floating-point value cannot
be represented in the destination integer type, the result is a
-:ref:`poison value <poisonvalues>`. Always ``memory(none)`` and speculatable.
+:ref:`poison value <poisonvalues>`. Without operand bundles that change FP
+environment behavior, always ``memory(none)`` and speculatable.
Example:
""""""""
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index e32253d1416d0..eecedc4faf958 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1611,6 +1611,20 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
})))
return false;
+ // FP instruction intrinsics without operand bundles use the default rounding
+ // and exception behavior, so they can be folded. If operand bundles are
+ // present the FP environment may differ from the default.
+ switch (F->getIntrinsicID()) {
+ case Intrinsic::fadd:
+ case Intrinsic::fsub:
+ case Intrinsic::fmul:
+ case Intrinsic::fdiv:
+ case Intrinsic::frem:
+ return !Call->hasOperandBundles();
+ default:
+ break;
+ }
+
switch (F->getIntrinsicID()) {
// Operations that do not operate floating-point numbers and do not depend on
// FP environment can be folded even in strictfp functions.
@@ -1946,14 +1960,6 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
case Intrinsic::rint:
case Intrinsic::canonicalize:
- // FP instruction intrinsics: without operand bundles they use default
- // rounding and exception behavior, so they can always be folded.
- case Intrinsic::fadd:
- case Intrinsic::fsub:
- case Intrinsic::fmul:
- case Intrinsic::fdiv:
- case Intrinsic::frem:
-
// Constrained intrinsics can be folded if FP environment is known
// to compiler.
case Intrinsic::experimental_constrained_fma:
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 0853363c7c8dd..a815dd3dcb71a 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7215,26 +7215,36 @@ Value *llvm::simplifyBinaryIntrinsic(Intrinsic::ID IID, Type *ReturnType,
case Intrinsic::aarch64_sve_uminv:
return simplifySVEIntReduction(IID, ReturnType, Op0, Op1);
case Intrinsic::fadd: {
+ if (Call && Call->hasOperandBundles())
+ return nullptr;
FastMathFlags FMF =
Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
return llvm::simplifyFAddInst(Op0, Op1, FMF, Q);
}
case Intrinsic::fsub: {
+ if (Call && Call->hasOperandBundles())
+ return nullptr;
FastMathFlags FMF =
Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
return llvm::simplifyFSubInst(Op0, Op1, FMF, Q);
}
case Intrinsic::fmul: {
+ if (Call && Call->hasOperandBundles())
+ return nullptr;
FastMathFlags FMF =
Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
return llvm::simplifyFMulInst(Op0, Op1, FMF, Q);
}
case Intrinsic::fdiv: {
+ if (Call && Call->hasOperandBundles())
+ return nullptr;
FastMathFlags FMF =
Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
return llvm::simplifyFDivInst(Op0, Op1, FMF, Q);
}
case Intrinsic::frem: {
+ if (Call && Call->hasOperandBundles())
+ return nullptr;
FastMathFlags FMF =
Call ? cast<FPMathOperator>(Call)->getFastMathFlags() : FastMathFlags();
return llvm::simplifyFRemInst(Op0, Op1, FMF, Q);
@@ -7323,6 +7333,10 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
return nullptr;
}
case Intrinsic::fcmp: {
+ // If operand bundles are present they may enable FP exceptions, in which
+ // case even FCMP_TRUE/FCMP_FALSE could signal on sNaN inputs.
+ if (Call->hasOperandBundles())
+ return nullptr;
FCmpInst::Predicate Pred = cast<FPCmpIntrinsic>(Call)->getPredicate();
if (Pred == FCmpInst::FCMP_TRUE)
return ConstantInt::getTrue(F->getReturnType());
>From 9e4cdd75c799baf3ff554e99601d09747e0839bd Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 29 Apr 2026 02:58:11 -0700
Subject: [PATCH 13/16] [LLVM] Revise FP instruction intrinsics docs per review
- Revert overly forward-looking operand-bundle language from the section
intro; replace with a clear statement of the current (no-bundle)
semantics and why the intrinsic form is useful (allows attaching
metadata, using result as a first-class value, etc.)
- Add a note in the section intro pointing readers to llvm.fcmps for
the exception to the "always memory(none) and speculatable" rule
- Revert per-intrinsic "Without operand bundles" qualification back to
the simpler "Always memory(none) and speculatable" since these
intrinsics do not yet support FP environment operand bundles
- Clarify llvm.fcmps overview: the signaling vs. quiet distinction is
only observable when FP exception tracking is active; in the default
LLVM FP model both produce the same result
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 78 ++++++++++++++++++++-----------------------
1 file changed, 36 insertions(+), 42 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index cc358de2cc786..9f9c4e801b0e6 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -30249,13 +30249,15 @@ FP Arithmetic Intrinsics
------------------------
These intrinsics are call-instruction equivalents of the standard
-floating-point instructions. They accept the same
-:ref:`fast-math flags <fastmath>` as the corresponding instruction and
-lower to it in the common case (no operand bundles). Clients that need
-to express non-default FP environment behavior (such as a specific
-rounding mode or exception-handling policy) can attach operand bundles
-to these calls; when such bundles are present the call may no longer be
-``memory(none)`` or speculatable.
+floating-point instructions, accepting the same
+:ref:`fast-math flags <fastmath>`. Expressing a floating-point
+operation as a call allows clients to attach metadata and to refer to
+the result value like any other value (without resorting to
+``extractvalue`` or similar workarounds). Without operand bundles they
+lower identically to the corresponding instruction.
+
+Note: ``llvm.fcmps`` is an exception -- see its entry below for
+details.
.. _int_fadd:
@@ -30293,9 +30295,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fadd <i_fadd>` instruction. Without operand
-bundles that change FP environment behavior, always ``memory(none)``
-and speculatable.
+Equivalent to the :ref:`fadd <i_fadd>` instruction. Always
+``memory(none)`` and speculatable.
Example:
""""""""
@@ -30340,9 +30341,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fsub <i_fsub>` instruction. Without operand
-bundles that change FP environment behavior, always ``memory(none)``
-and speculatable.
+Equivalent to the :ref:`fsub <i_fsub>` instruction. Always
+``memory(none)`` and speculatable.
Example:
""""""""
@@ -30387,9 +30387,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fmul <i_fmul>` instruction. Without operand
-bundles that change FP environment behavior, always ``memory(none)``
-and speculatable.
+Equivalent to the :ref:`fmul <i_fmul>` instruction. Always
+``memory(none)`` and speculatable.
Example:
""""""""
@@ -30434,9 +30433,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fdiv <i_fdiv>` instruction. Without operand
-bundles that change FP environment behavior, always ``memory(none)``
-and speculatable.
+Equivalent to the :ref:`fdiv <i_fdiv>` instruction. Always
+``memory(none)`` and speculatable.
Example:
""""""""
@@ -30481,9 +30479,8 @@ The arguments and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`frem <i_frem>` instruction. Without operand
-bundles that change FP environment behavior, always ``memory(none)``
-and speculatable.
+Equivalent to the :ref:`frem <i_frem>` instruction. Always
+``memory(none)`` and speculatable.
Example:
""""""""
@@ -30528,9 +30525,8 @@ The argument and return value are floating-point numbers of the same type.
Semantics:
""""""""""
-Equivalent to the :ref:`fneg <i_fneg>` instruction. Without operand
-bundles that change FP environment behavior, always ``memory(none)``
-and speculatable.
+Equivalent to the :ref:`fneg <i_fneg>` instruction. Always
+``memory(none)`` and speculatable.
Example:
""""""""
@@ -30582,8 +30578,7 @@ Semantics:
""""""""""
The comparison semantics :ref:`follow those of the fcmp instruction
-<fcmp_md_cc_sem>`. Without operand bundles that enable FP exceptions,
-always ``memory(none)`` and speculatable.
+<fcmp_md_cc_sem>`. Always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30618,9 +30613,14 @@ Overview:
"""""""""
Performs a **signaling** floating-point comparison. Unlike
-:ref:`llvm.fcmp <int_fcmp>`, a signaling comparison raises an FP Invalid
-Operation exception whenever either operand is any NaN -- including quiet NaNs
-(qNaN). There is no corresponding plain instruction; the closest equivalent is
+:ref:`llvm.fcmp <int_fcmp>`, ``llvm.fcmps`` signals the FP Invalid Operation
+exception for any NaN operand (including quiet NaNs), whereas
+:ref:`llvm.fcmp <int_fcmp>` signals only for sNaN operands. The difference
+is only observable when FP exception tracking is active (e.g. via
+``strictfp`` or a future operand bundle); in the default LLVM FP model,
+both intrinsics produce the same result.
+
+There is no corresponding plain instruction; the closest equivalent is
:ref:`fcmp <i_fcmp>`. For vector types the return type is a vector of ``i1``
with the same number of elements as the operands.
@@ -30675,8 +30675,7 @@ floating-point type. The source type must be larger than the destination type.
Semantics:
""""""""""
-Equivalent to the :ref:`fptrunc <i_fptrunc>` instruction. Without
-operand bundles that change FP environment behavior, always
+Equivalent to the :ref:`fptrunc <i_fptrunc>` instruction. Always
``memory(none)`` and speculatable.
Example:
@@ -30718,8 +30717,7 @@ floating-point type. The source type must be smaller than the destination type.
Semantics:
""""""""""
-Equivalent to the :ref:`fpext <i_fpext>` instruction. Without
-operand bundles that change FP environment behavior, always
+Equivalent to the :ref:`fpext <i_fpext>` instruction. Always
``memory(none)`` and speculatable.
Example:
@@ -30763,8 +30761,7 @@ Semantics:
Equivalent to the ``sitofp`` instruction. If the integer value cannot be
represented exactly in the destination type, it is rounded using the default
-rounding mode. Without operand bundles that change FP environment behavior,
-always ``memory(none)`` and speculatable.
+rounding mode. Always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30807,8 +30804,7 @@ Semantics:
Equivalent to the ``uitofp`` instruction. If the integer value cannot be
represented exactly in the destination type, it is rounded using the default
-rounding mode. Without operand bundles that change FP environment behavior,
-always ``memory(none)`` and speculatable.
+rounding mode. Always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30851,8 +30847,7 @@ Semantics:
Equivalent to the ``fptosi`` instruction. If the floating-point value cannot
be represented in the destination integer type, the result is a
-:ref:`poison value <poisonvalues>`. Without operand bundles that change FP
-environment behavior, always ``memory(none)`` and speculatable.
+:ref:`poison value <poisonvalues>`. Always ``memory(none)`` and speculatable.
Example:
""""""""
@@ -30895,8 +30890,7 @@ Semantics:
Equivalent to the ``fptoui`` instruction. If the floating-point value cannot
be represented in the destination integer type, the result is a
-:ref:`poison value <poisonvalues>`. Without operand bundles that change FP
-environment behavior, always ``memory(none)`` and speculatable.
+:ref:`poison value <poisonvalues>`. Always ``memory(none)`` and speculatable.
Example:
""""""""
>From 167ff520775e89eee6cb8f955f139a7b9772d6ad Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 29 Apr 2026 03:05:38 -0700
Subject: [PATCH 14/16] [LLVM] Simplify FP instruction intrinsics section intro
wording
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 9f9c4e801b0e6..359e733820934 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -30251,9 +30251,8 @@ FP Arithmetic Intrinsics
These intrinsics are call-instruction equivalents of the standard
floating-point instructions, accepting the same
:ref:`fast-math flags <fastmath>`. Expressing a floating-point
-operation as a call allows clients to attach metadata and to refer to
-the result value like any other value (without resorting to
-``extractvalue`` or similar workarounds). Without operand bundles they
+operation as a call allows clients to attach metadata and to use the
+result value like any other call result. Without operand bundles they
lower identically to the corresponding instruction.
Note: ``llvm.fcmps`` is an exception -- see its entry below for
>From 4fa081e183ee592150449abc49d1fa904288bbd1 Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Fri, 1 May 2026 01:52:01 -0700
Subject: [PATCH 15/16] [LLVM] Remove llvm.fneg intrinsic; clarify llvm.fcmp
sNaN behavior in docs
Remove llvm.fneg per review feedback: fneg/fabs/copysign are pure bit
operations and do not belong as FP-environment-affecting intrinsics.
Callers needing FTZ-style negation should use fsub -0.0, x.
Also adds one sentence to the llvm.fcmp Semantics section noting that
as a quiet comparison it raises FP Invalid Operation for sNaN (but not
qNaN) when exception tracking is active.
Co-Authored-By: Claude Sonnet 4.6 <noreply at anthropic.com>
---
llvm/docs/LangRef.rst | 50 ++-----------------
llvm/include/llvm/IR/Intrinsics.td | 2 -
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 2 -
.../SelectionDAG/SelectionDAGBuilder.cpp | 5 --
.../Assembler/fp-instruction-intrinsics.ll | 5 --
.../AArch64/fp-instruction-intrinsics.ll | 16 ------
.../CodeGen/ARM/fp-instruction-intrinsics.ll | 16 ------
.../LoongArch/fp-instruction-intrinsics.ll | 16 ------
.../CodeGen/Mips/fp-instruction-intrinsics.ll | 8 ---
.../NVPTX/fp-instruction-intrinsics.ll | 18 -------
.../PowerPC/fp-instruction-intrinsics.ll | 16 ------
.../RISCV/fp-instruction-intrinsics.ll | 16 ------
.../SPARC/fp-instruction-intrinsics.ll | 16 ------
.../SystemZ/fp-instruction-intrinsics.ll | 16 ------
.../WebAssembly/fp-instruction-intrinsics.ll | 16 ------
.../CodeGen/X86/fp-instruction-intrinsics.ll | 17 -------
16 files changed, 3 insertions(+), 232 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 359e733820934..8b714a9e6d0bf 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -30488,52 +30488,6 @@ Example:
%r = call float @llvm.frem.f32(float %a, float %b)
-.. _int_fneg:
-
-'``llvm.fneg.*``' Intrinsic
-^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Syntax:
-"""""""
-
-This is an overloaded intrinsic. You can use ``llvm.fneg`` on any
-floating-point or vector of floating-point type. Not all targets support
-all types however.
-
-::
-
- declare half @llvm.fneg.f16(half %op1)
- declare bfloat @llvm.fneg.bf16(bfloat %op1)
- declare float @llvm.fneg.f32(float %op1)
- declare double @llvm.fneg.f64(double %op1)
- declare x86_fp80 @llvm.fneg.f80(x86_fp80 %op1)
- declare fp128 @llvm.fneg.f128(fp128 %op1)
- declare ppc_fp128 @llvm.fneg.ppcf128(ppc_fp128 %op1)
-
-Overview:
-"""""""""
-
-Intrinsic form of the :ref:`fneg <i_fneg>` instruction. Returns the
-floating-point negation of its operand.
-
-Arguments:
-""""""""""
-
-The argument and return value are floating-point numbers of the same type.
-
-Semantics:
-""""""""""
-
-Equivalent to the :ref:`fneg <i_fneg>` instruction. Always
-``memory(none)`` and speculatable.
-
-Example:
-""""""""
-
-::
-
- %r = call float @llvm.fneg.f32(float %x)
-
.. _int_fcmp:
'``llvm.fcmp.*``' Intrinsic
@@ -30577,7 +30531,9 @@ Semantics:
""""""""""
The comparison semantics :ref:`follow those of the fcmp instruction
-<fcmp_md_cc_sem>`. Always ``memory(none)`` and speculatable.
+<fcmp_md_cc_sem>`. As a quiet comparison, it does not raise FP Invalid
+Operation for qNaN operands; it will raise for sNaN operands when FP
+exception tracking is active. Always ``memory(none)`` and speculatable.
Example:
""""""""
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index a39e14ee5cc85..850df0e41e048 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1147,8 +1147,6 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrNoCreateUndefOrPoison] in
[LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_fmul : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>]>;
- def int_fneg : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
- [LLVMMatchType<0>]>;
def int_fdiv : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_frem : DefaultAttrsIntrinsic<[llvm_anyfloat_ty],
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 2a819953d2803..1aa5e798033e3 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2717,8 +2717,6 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
return translateBinaryOp(TargetOpcode::G_FDIV, CI, MIRBuilder);
case Intrinsic::frem:
return translateBinaryOp(TargetOpcode::G_FREM, CI, MIRBuilder);
- case Intrinsic::fneg:
- return translateUnaryOp(TargetOpcode::G_FNEG, CI, MIRBuilder);
case Intrinsic::fptrunc: {
Register Src = getOrCreateVReg(*CI.getArgOperand(0));
Register Res = getOrCreateVReg(CI);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 000fc89bf782b..e61c3c858d4d0 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -7112,11 +7112,6 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
getValue(I.getArgOperand(0)),
getValue(I.getArgOperand(1)), Flags));
return;
- case Intrinsic::fneg:
- setValue(&I, DAG.getNode(ISD::FNEG, sdl,
- getValue(I.getArgOperand(0)).getValueType(),
- getValue(I.getArgOperand(0)), Flags));
- return;
case Intrinsic::fdiv:
setValue(&I, DAG.getNode(ISD::FDIV, sdl,
getValue(I.getArgOperand(0)).getValueType(),
diff --git a/llvm/test/Assembler/fp-instruction-intrinsics.ll b/llvm/test/Assembler/fp-instruction-intrinsics.ll
index 2c183cd8c8e63..1ecac7a340899 100644
--- a/llvm/test/Assembler/fp-instruction-intrinsics.ll
+++ b/llvm/test/Assembler/fp-instruction-intrinsics.ll
@@ -12,8 +12,6 @@ define void @test_arithmetic(float %a, float %b, double %da, double %db) {
%fmul = call float @llvm.fmul.f32(float %a, float %b)
%fdiv = call float @llvm.fdiv.f32(float %a, float %b)
%frem = call float @llvm.frem.f32(float %a, float %b)
- %fneg = call float @llvm.fneg.f32(float %a)
-
%dfadd = call double @llvm.fadd.f64(double %da, double %db)
%dfsub = call double @llvm.fsub.f64(double %da, double %db)
%dfmul = call double @llvm.fmul.f64(double %da, double %db)
@@ -24,7 +22,6 @@ define void @test_arithmetic(float %a, float %b, double %da, double %db) {
; CHECK: call float @llvm.fmul.f32(float %a, float %b)
; CHECK: call float @llvm.fdiv.f32(float %a, float %b)
; CHECK: call float @llvm.frem.f32(float %a, float %b)
-; CHECK: call float @llvm.fneg.f32(float %a)
; CHECK: call double @llvm.fadd.f64(double %da, double %db)
ret void
@@ -106,7 +103,6 @@ define void @test_fcmps(float %a, float %b) {
; CHECK-DAG: declare float @llvm.fmul.f32(float, float)
; CHECK-DAG: declare float @llvm.fdiv.f32(float, float)
; CHECK-DAG: declare float @llvm.frem.f32(float, float)
-; CHECK-DAG: declare float @llvm.fneg.f32(float)
; CHECK-DAG: declare double @llvm.fadd.f64(double, double)
; CHECK-DAG: declare float @llvm.fptrunc.f32.f64(double)
; CHECK-DAG: declare double @llvm.fpext.f64.f32(float)
@@ -124,7 +120,6 @@ declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
declare float @llvm.frem.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
diff --git a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
index cb92621656693..2ac8be1f75bc1 100644
--- a/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/AArch64/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: fneg d0, d0
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: fneg s0, s0
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -176,12 +162,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
index ccc7db64c287a..6a7271ae11292 100644
--- a/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/ARM/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: vneg.f64
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: vneg.f32
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -159,12 +145,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
index b1f95eea4e194..311f09323c3fa 100644
--- a/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/LoongArch/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: fneg.d $fa0, $fa0
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: fneg.s $fa0, $fa0
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -176,12 +162,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
index dba7964cf4a81..73ddc901e6326 100644
--- a/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/Mips/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: neg.s
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @sitofp_i32_f32(i32 %a) {
@@ -93,7 +86,6 @@ declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
declare i32 @llvm.fptosi.i32.f32(float)
declare i1 @llvm.fcmp.f32(float, float, metadata)
diff --git a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
index f651b70906251..b80a27885cc04 100644
--- a/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/NVPTX/fp-instruction-intrinsics.ll
@@ -35,14 +35,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64(
-; CHECK: neg.f64 %rd{{[0-9]+}}, %rd{{[0-9]+}}
-; CHECK: ret
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -77,14 +69,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32(
-; CHECK: neg.f32 %r{{[0-9]+}}, %r{{[0-9]+}}
-; CHECK: ret
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -198,12 +182,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
index 0c3295a0d4bc0..d21689f2cb51a 100644
--- a/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/PowerPC/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: fneg
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: fneg
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -177,12 +163,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
index 256e2c95d08de..7486f15eea5fc 100644
--- a/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/RISCV/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: fneg.d fa0, fa0
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: fneg.s fa0, fa0
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -176,12 +162,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
index 0ae2e1720a939..13a1e4df4f5ea 100644
--- a/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SPARC/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: fnegd
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: fnegs
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -144,12 +130,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
index 000ee1b6c30ef..bcfe53fdbb2a6 100644
--- a/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/SystemZ/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: lcdfr
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: lcdfr
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -176,12 +162,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
index c31db8ea525ab..7aa5216d70a91 100644
--- a/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/WebAssembly/fp-instruction-intrinsics.ll
@@ -31,13 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: f64.neg
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -68,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: f32.neg
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -158,12 +144,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
diff --git a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
index 962365ad10a1e..c6a452132d426 100644
--- a/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
+++ b/llvm/test/CodeGen/X86/fp-instruction-intrinsics.ll
@@ -31,14 +31,6 @@ define double @fdiv_f64(double %a, double %b) {
ret double %r
}
-; FP negation on SSE2 is an XOR with the sign bit
-define double @fneg_f64(double %a) {
-; CHECK-LABEL: fneg_f64:
-; CHECK: xorp{{[sd]}}
- %r = call double @llvm.fneg.f64(double %a)
- ret double %r
-}
-
;;; f32 arithmetic
define float @fadd_f32(float %a, float %b) {
@@ -69,13 +61,6 @@ define float @fdiv_f32(float %a, float %b) {
ret float %r
}
-define float @fneg_f32(float %a) {
-; CHECK-LABEL: fneg_f32:
-; CHECK: xorps
- %r = call float @llvm.fneg.f32(float %a)
- ret float %r
-}
-
;;; Conversions
define float @fptrunc(double %a) {
@@ -159,12 +144,10 @@ declare double @llvm.fadd.f64(double, double)
declare double @llvm.fsub.f64(double, double)
declare double @llvm.fmul.f64(double, double)
declare double @llvm.fdiv.f64(double, double)
-declare double @llvm.fneg.f64(double)
declare float @llvm.fadd.f32(float, float)
declare float @llvm.fsub.f32(float, float)
declare float @llvm.fmul.f32(float, float)
declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.fneg.f32(float)
declare float @llvm.fptrunc.f32.f64(double)
declare double @llvm.fpext.f64.f32(float)
declare float @llvm.sitofp.f32.i32(i32)
>From 084a964821e4c594e350d9b69d17031bf52d4107 Mon Sep 17 00:00:00 2001
From: Princeton Ferro <pferro at nvidia.com>
Date: Wed, 6 May 2026 14:52:59 -0700
Subject: [PATCH 16/16] [LLVM] Address dtcxzyw review: drop unused .def, fix
fptosi/fptoui poison attrs
- Remove unused llvm/include/llvm/IR/FloatingPointOps.def (no #include
references anywhere in the tree).
- Reword IRTranslator.cpp comment that referenced an undefined "PR A".
- Strip redundant `declare` lines from the assembler test (the auto-
generated declarations are already verified by the CHECK-DAG block).
- Move int_fptosi and int_fptoui out of the
`IntrNoCreateUndefOrPoison` `let` block: per LangRef, fptosi/fptoui
yield poison when the source value doesn't fit in the destination
integer type, so they must not advertise nocreateundeforpoison.
Verified with `llvm-as | llvm-dis` that the attribute is gone for
fptosi/fptoui while sitofp/uitofp/fadd/etc. retain it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply at anthropic.com>
---
llvm/include/llvm/IR/FloatingPointOps.def | 121 ------------------
llvm/include/llvm/IR/Intrinsics.td | 13 +-
llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp | 2 +-
.../Assembler/fp-instruction-intrinsics.ll | 18 ---
4 files changed, 11 insertions(+), 143 deletions(-)
delete mode 100644 llvm/include/llvm/IR/FloatingPointOps.def
diff --git a/llvm/include/llvm/IR/FloatingPointOps.def b/llvm/include/llvm/IR/FloatingPointOps.def
deleted file mode 100644
index 41a16743bd370..0000000000000
--- a/llvm/include/llvm/IR/FloatingPointOps.def
+++ /dev/null
@@ -1,121 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Defines the set of intrinsics classified as floating-point operations.
-//
-// Two macro forms:
-//
-// FP_OP(intrinsic_name, isd_opcode_name)
-// - An FP intrinsic that lowers to a single ISD opcode.
-// - isFloatingPointOperation() uses column 1 (Intrinsic::NAME).
-// - ISD::isFPOpcode() uses column 2 (ISD::ISD_NAME).
-//
-// FP_INTRINSIC(intrinsic_name)
-// - An FP intrinsic whose ISD opcode is already covered by another FP_OP
-// entry, or maps to a shared opcode (e.g. SETCC) not exclusively FP.
-// - isFloatingPointOperation() uses it; ISD::isFPOpcode() does not.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef FP_OP
-#define FP_OP(N, D)
-#endif
-
-#ifndef FP_INTRINSIC
-#define FP_INTRINSIC(N)
-#endif
-
-// Basic FP arithmetic
-FP_OP(fadd, FADD)
-FP_OP(fsub, FSUB)
-FP_OP(fmul, FMUL)
-FP_OP(fdiv, FDIV)
-FP_OP(frem, FREM)
-FP_OP(fneg, FNEG)
-FP_OP(fabs, FABS)
-
-// FMA family
-FP_OP(fma, FMA)
-FP_OP(fmuladd, FMULADD)
-
-// FP type conversions (float <-> float)
-FP_OP(fptrunc, FP_ROUND)
-FP_OP(fpext, FP_EXTEND)
-FP_INTRINSIC(fptrunc_round) // also lowers to FP_ROUND; ISD already covered above
-
-// Integer/FP conversions
-FP_OP(sitofp, SINT_TO_FP)
-FP_OP(uitofp, UINT_TO_FP)
-FP_OP(fptosi, FP_TO_SINT)
-FP_OP(fptoui, FP_TO_UINT)
-
-// FP comparison (SETCC is shared with integer; kept as FP_INTRINSIC)
-FP_INTRINSIC(fcmp)
-
-// Math / transcendental
-FP_OP(sqrt, FSQRT)
-// cbrt: no llvm.cbrt intrinsic; FCBRT is ISD-only (handled in isFPOpcode)
-FP_OP(sin, FSIN)
-FP_OP(cos, FCOS)
-FP_OP(tan, FTAN)
-FP_OP(asin, FASIN)
-FP_OP(acos, FACOS)
-FP_OP(atan, FATAN)
-FP_OP(atan2, FATAN2)
-FP_OP(sinh, FSINH)
-FP_OP(cosh, FCOSH)
-FP_OP(tanh, FTANH)
-FP_OP(pow, FPOW)
-FP_OP(powi, FPOWI)
-FP_OP(ldexp, FLDEXP)
-FP_OP(frexp, FFREXP)
-FP_OP(log, FLOG)
-FP_OP(log2, FLOG2)
-FP_OP(log10, FLOG10)
-FP_OP(exp, FEXP)
-FP_OP(exp2, FEXP2)
-FP_OP(exp10, FEXP10)
-
-// FP rounding
-FP_OP(ceil, FCEIL)
-FP_OP(trunc, FTRUNC)
-FP_OP(rint, FRINT)
-FP_OP(nearbyint, FNEARBYINT)
-FP_OP(round, FROUND)
-FP_OP(roundeven, FROUNDEVEN)
-FP_OP(floor, FFLOOR)
-FP_OP(lround, LROUND)
-FP_OP(llround, LLROUND)
-FP_OP(lrint, LRINT)
-FP_OP(llrint, LLRINT)
-
-// FP min/max
-FP_OP(minnum, FMINNUM)
-FP_OP(maxnum, FMAXNUM)
-FP_OP(minimum, FMINIMUM)
-FP_OP(maximum, FMAXIMUM)
-FP_OP(minimumnum, FMINIMUMNUM)
-FP_OP(maximumnum, FMAXIMUMNUM)
-
-// FP special / decomposition
-FP_OP(copysign, FCOPYSIGN)
-FP_OP(canonicalize, FCANONICALIZE)
-FP_OP(is_fpclass, IS_FPCLASS)
-FP_OP(sincos, FSINCOS)
-FP_OP(modf, FMODF)
-
-// Vector FP reductions
-FP_OP(vector_reduce_fadd, VECREDUCE_FADD)
-FP_OP(vector_reduce_fmul, VECREDUCE_FMUL)
-FP_OP(vector_reduce_fmax, VECREDUCE_FMAX)
-FP_OP(vector_reduce_fmin, VECREDUCE_FMIN)
-FP_OP(vector_reduce_fmaximum, VECREDUCE_FMAXIMUM)
-FP_OP(vector_reduce_fminimum, VECREDUCE_FMINIMUM)
-
-#undef FP_OP
-#undef FP_INTRINSIC
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 850df0e41e048..b19622e0757bb 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1156,11 +1156,11 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrNoCreateUndefOrPoison] in
def int_fptrunc : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyfloat_ty]>;
def int_fpext : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyfloat_ty]>;
- // Integer/FP conversions
+ // Integer/FP conversions. fptosi/fptoui yield poison when the value doesn't
+ // fit in the integer type, so they are defined separately below without
+ // IntrNoCreateUndefOrPoison.
def int_sitofp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyint_ty]>;
def int_uitofp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [llvm_anyint_ty]>;
- def int_fptosi : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty]>;
- def int_fptoui : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty]>;
// fcmp: quiet FP compare; predicate passed as metadata string (e.g. !"oeq")
def int_fcmp : DefaultAttrsIntrinsic<
@@ -1241,6 +1241,13 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrNoCreateUndefOrPoison] in
def int_frexp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty, llvm_anyint_ty], [LLVMMatchType<0>]>;
}
+// fptosi/fptoui: yield poison when the FP value doesn't fit in the integer
+// type, so they cannot have IntrNoCreateUndefOrPoison.
+def int_fptosi : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty],
+ [IntrNoMem, IntrSpeculatable]>;
+def int_fptoui : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyfloat_ty],
+ [IntrNoMem, IntrSpeculatable]>;
+
// fcmps: signaling FP compare. Unlike the quiet llvm.fcmp, this raises an
// Invalid Operation exception for *any* NaN operand (not just sNaN).
// Exception observability is always strict -- this intrinsic has
diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
index 1aa5e798033e3..bfc03ae895704 100644
--- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp
@@ -2706,7 +2706,7 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
}
// FP instruction intrinsics: call-form equivalents of FP instructions.
- // These have no operand bundles in PR A and always lower to plain G_* nodes.
+ // Without operand bundles these lower to plain G_* nodes.
case Intrinsic::fadd:
return translateBinaryOp(TargetOpcode::G_FADD, CI, MIRBuilder);
case Intrinsic::fsub:
diff --git a/llvm/test/Assembler/fp-instruction-intrinsics.ll b/llvm/test/Assembler/fp-instruction-intrinsics.ll
index 1ecac7a340899..05e6b99b78c5f 100644
--- a/llvm/test/Assembler/fp-instruction-intrinsics.ll
+++ b/llvm/test/Assembler/fp-instruction-intrinsics.ll
@@ -114,21 +114,3 @@ define void @test_fcmps(float %a, float %b) {
; fcmps has memory(inaccessiblemem: readwrite) + willreturn
; CHECK-DAG: declare i1 @llvm.fcmps.f32(float, float, metadata) #[[ATTR:[0-9]+]]
; CHECK-DAG: attributes #[[ATTR]] = { nocreateundeforpoison nounwind willreturn memory(inaccessiblemem: readwrite) }
-
-declare float @llvm.fadd.f32(float, float)
-declare float @llvm.fsub.f32(float, float)
-declare float @llvm.fmul.f32(float, float)
-declare float @llvm.fdiv.f32(float, float)
-declare float @llvm.frem.f32(float, float)
-declare double @llvm.fadd.f64(double, double)
-declare double @llvm.fsub.f64(double, double)
-declare double @llvm.fmul.f64(double, double)
-declare double @llvm.fdiv.f64(double, double)
-declare float @llvm.fptrunc.f32.f64(double)
-declare double @llvm.fpext.f64.f32(float)
-declare float @llvm.sitofp.f32.i32(i32)
-declare float @llvm.uitofp.f32.i32(i32)
-declare i32 @llvm.fptosi.i32.f32(float)
-declare i32 @llvm.fptoui.i32.f32(float)
-declare i1 @llvm.fcmp.f32(float, float, metadata)
-declare i1 @llvm.fcmps.f32(float, float, metadata)
More information about the llvm-commits
mailing list