[llvm] [LLVM] Add FP instruction intrinsics (PR #193588)

Princeton Ferro via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 22 14:47:32 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 1/7] [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 2/7] [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 3/7] [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 4/7] [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 5/7] [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 6/7] [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 7/7] [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
 }



More information about the llvm-commits mailing list