[llvm] [NVPTX] Add support for f32x2 mixed-precision add/sub (PR #221957)

Srinivasa Ravi via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 22:46:25 PDT 2026


https://github.com/Wolfram70 updated https://github.com/llvm/llvm-project/pull/221957

>From cc5c3eb6149350ac83711ebbecad20b9dace509e Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Mon, 7 Sep 2026 14:39:58 +0000
Subject: [PATCH 1/7] [NVPTX] Add support for f32x2 mixed-precision additions

This change adds support for mixed precision addition of `f16x2`
and `bf16x2` with `f32x2`, where the following upconverting
patterns:
```
%e = fpext <2 x half> %h to <2 x float>
%res = fp-operation(%e, ...)
...
%e = fpext <2 x bfloat> %b to <2 x float>
%res = fp-operation(%e, ...)

where the fp-operation can be any of:
- fadd
- llvm.nvvm.fadd.v2f32
```
are lowered to `add.{rnd}.f32x2.{f16x2/bf16x2}.f32x2`, and the
following downconverting pattern:
```
%sum = llvm.nvvm.fadd{.ftz}.v2f32(%a, %b, rz)
%lo = extractelement <2 x float> %sum, i32 0
%hi = extractelement <2 x float> %sum, i32 1
%res = llvm.nvvm.ff2{f16x2/bf16x2}.rz(%hi, %lo, false)
```
is lowered to `add.rz{.ftz}.{f16x2/bf16x2}.f32x2.f32x2`. These
instructions combine the conversion and the addition into one
instruction from `sm_107f` onwards.

The tests have been verified through ptxas-13.4.

PTX spec reference:
https://docs.nvidia.com/cuda/parallel-thread-execution/#mixed-precision-floating-point-instructions
---
 llvm/lib/Target/NVPTX/NVPTXIntrinsics.td      |  61 +++
 .../mixed-precision-add-f32x2-downconvert.ll  | 330 +++++++++++
 .../mixed-precision-add-f32x2-upconvert.ll    | 517 ++++++++++++++++++
 3 files changed, 908 insertions(+)
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll

diff --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index 220ef64732830..c047c4fd4f94b 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -2495,6 +2495,19 @@ def INT_NVVM_ADD_D :
   F_MATH_2_RNDOP_TY<"add.${rnd}.f64", F64RT, int_nvvm_fadd>;
 
 // mixed precision
+
+// FP_EXTEND to v2f32 is scalarized before isel, leaving one of two shapes
+// behind depending on whether the source lanes share a register.
+def fpextend_v2f32_from_packed : PatFrag<(ops node:$a),
+                                         (v2f32 (build_vector
+                                           (f32 (fpextend (extractelt node:$a, 0))),
+                                           (f32 (fpextend (extractelt node:$a, 1)))))>;
+
+def fpextend_v2f32_from_lanes : PatFrag<(ops node:$a0, node:$a1),
+                                        (v2f32 (build_vector
+                                          (f32 (fpextend node:$a0)),
+                                          (f32 (fpextend node:$a1))))>;
+
 foreach rnd = FPRoundingModes in {
   defvar rnd_imm = !cast<TImmLeaf>(StrJoin<"_", ["fp_rnd", rnd, "imm"]>.ret);
 
@@ -2508,6 +2521,41 @@ foreach rnd = FPRoundingModes in {
              (f32 (fpextend type:$a)),
              f32:$b, rnd_imm))]>,
         Requires<[SM100]>;
+
+  foreach t = [F16X2RT, BF16X2RT] in {
+    def INT_NVVM_MIXED_ADD_ # rnd # _f32x2_ # t.PtxType :
+      BasicNVPTXInst<(outs B64:$dst), (ins t.RC:$a, B64:$b),
+        StrJoin<".", ["add", rnd, "f32x2", t.PtxType, "f32x2"]>.ret,
+        [(set v2f32:$dst,
+          (int_nvvm_fadd (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b,
+                         rnd_imm))]>,
+      Requires<[SM107f]>;
+
+    def : Pat<(v2f32 (int_nvvm_fadd
+                       (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
+                                                  t.Ty.ElementType:$a1),
+                       v2f32:$b, rnd_imm)),
+              (!cast<Instruction>("INT_NVVM_MIXED_ADD_" # rnd # "_f32x2_" #
+                                  t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>,
+      Requires<[SM107f]>;
+  }
+}
+
+// mixed precision, downconverting
+foreach t = [F16X2RT, BF16X2RT] in {
+  defvar ftz = !if(!eq(t, F16X2RT), "ftz", "");
+  defvar AddOp = !cast<Intrinsic>(StrJoin<"_", ["int_nvvm_fadd", ftz]>.ret);
+  defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
+
+  def INT_NVVM_MIXED_ADD_rz_ # t.PtxType # _f32x2 :
+    BasicNVPTXInst<(outs t.RC:$dst), (ins B64:$a, B64:$b),
+      StrJoin<".", ["add.rz", ftz, t.PtxType, "f32x2", "f32x2"]>.ret,
+      // The cvt packs its first argument into the high half of the result.
+      [(set t.Ty:$dst,
+        (CvtOp (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
+               (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
+               /*pzo=*/0))]>,
+    Requires<[SM107f]>;
 }
 
 // Pattern for fadd when there is no FTZ flag
@@ -2518,6 +2566,19 @@ let Predicates = [SM100, doNoF32FTZ] in {
             (INT_NVVM_MIXED_ADD_rn_f32_bf16 B16:$a, B32:$b)>;
 }
 
+let Predicates = [SM107f, doNoF32FTZ] in
+  foreach t = [F16X2RT, BF16X2RT] in {
+    defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_ADD_rn_f32x2_" # t.PtxType);
+
+    def : Pat<(v2f32 (fadd (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b)),
+              (Inst $a, $b)>;
+
+    def : Pat<(v2f32 (fadd (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
+                                                      t.Ty.ElementType:$a1),
+                           v2f32:$b)),
+              (Inst (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
+  }
+
 //
 // Sub
 //
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll
new file mode 100644
index 0000000000000..9dbfe1e3ab014
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll
@@ -0,0 +1,330 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+
+;
+;  F16x2
+;
+
+define <2 x half> @add_rz_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_param_1];
+; CHECK-NEXT:    add.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_commuted(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_commuted(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_commuted_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_commuted_param_1];
+; CHECK-NEXT:    add.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd2, %rd1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %b, <2 x float> %a, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_f16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    add.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+; invalid patterns
+
+define <2 x half> @add_rz_f16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_no_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_no_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_no_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_no_ftz_param_1];
+; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rn_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rn_f16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rn_f16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rn_f16x2_f32x2_param_1];
+; CHECK-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_rn_convert(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_rn_convert(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_rn_convert_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_rn_convert_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rn.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_relu(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_relu(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_relu_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_relu_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.relu.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_distinct_adds(<2 x float> %a, <2 x float> %b, <2 x float> %c) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_distinct_adds(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_distinct_adds_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_distinct_adds_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_f16x2_f32x2_distinct_adds_param_2];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd5, %rd1, %rd4;
+; CHECK-NEXT:    mov.b64 {%r1, _}, %rd3;
+; CHECK-NEXT:    mov.b64 {_, %r2}, %rd5;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum1 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %sum2 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %c, i32 0)
+  %lo = extractelement <2 x float> %sum1, i32 0
+  %hi = extractelement <2 x float> %sum2, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+;
+;  BF16x2
+;
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_param_1];
+; CHECK-NEXT:    add.rz.bf16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_commuted(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_commuted(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_commuted_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_commuted_param_1];
+; CHECK-NEXT:    add.rz.bf16x2.f32x2.f32x2 %r1, %rd2, %rd1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %b, <2 x float> %a, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_bf16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    add.rz.bf16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+; invalid patterns
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_ftz_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @add_rn_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rn_bf16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rn_bf16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rn_bf16x2_f32x2_param_1];
+; CHECK-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
new file mode 100644
index 0000000000000..35d53eebf8ef0
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
@@ -0,0 +1,517 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck --check-prefixes=CHECK,CHECK-NOFTZ %s
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | FileCheck --check-prefixes=CHECK,CHECK-FTZ %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | %ptxas-verify -arch=sm_107f %}
+
+;
+;  F16x2
+;
+
+; lanes
+
+define <2 x float> @add_f32x2_f16x2_lanes(<2 x half> %a, <2 x float> %b) {
+; CHECK-LABEL: add_f32x2_f16x2_lanes(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_lanes_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_lanes_param_1];
+; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NEXT:    add.rz.f32x2.f16x2.f32x2 %rd3, %r1, %rd2;
+; CHECK-NEXT:    add.rm.f32x2.f16x2.f32x2 %rd4, %r1, %rd3;
+; CHECK-NEXT:    add.rp.f32x2.f16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r1, i32 0)
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r2, i32 3)
+  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r3, i32 2)
+  ret <2 x float> %r4
+}
+
+define <2 x float> @add_f32x2_f16x2_lanes_commuted(<2 x half> %a, <2 x float> %b) {
+; CHECK-LABEL: add_f32x2_f16x2_lanes_commuted(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_lanes_commuted_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_lanes_commuted_param_1];
+; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %b, <2 x float> %e, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-FTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+; packed
+
+define <2 x float> @add_f32x2_f16x2_packed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_packed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_packed_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_packed_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_packed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_packed_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_packed_param_1];
+; CHECK-FTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_packed_commuted(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_packed_commuted(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_packed_commuted_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_packed_commuted_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_packed_commuted(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_packed_commuted_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_packed_commuted_param_1];
+; CHECK-FTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %b, <2 x float> %e, i32 1)
+  ret <2 x float> %r
+}
+
+; generic fadd
+
+define <2 x float> @add_f32x2_f16x2_generic_lanes(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_lanes(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lanes_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_lanes_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_lanes(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lanes_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_lanes_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = fadd <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_generic_lanes_commuted(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_lanes_commuted(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lanes_commuted_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_lanes_commuted_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_lanes_commuted(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lanes_commuted_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_lanes_commuted_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = fadd <2 x float> %b, %e
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_generic_packed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_packed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_generic_packed_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_packed_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_packed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_generic_packed_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_packed_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %r = fadd <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+;
+;  BF16x2
+;
+
+; lanes
+
+define <2 x float> @add_f32x2_bf16x2_lanes(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-LABEL: add_f32x2_bf16x2_lanes(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_lanes_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_lanes_param_1];
+; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NEXT:    add.rz.f32x2.bf16x2.f32x2 %rd3, %r1, %rd2;
+; CHECK-NEXT:    add.rm.f32x2.bf16x2.f32x2 %rd4, %r1, %rd3;
+; CHECK-NEXT:    add.rp.f32x2.bf16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r1, i32 0)
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r2, i32 3)
+  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r3, i32 2)
+  ret <2 x float> %r4
+}
+
+define <2 x float> @add_f32x2_bf16x2_lanes_commuted(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-LABEL: add_f32x2_bf16x2_lanes_commuted(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_lanes_commuted_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_lanes_commuted_param_1];
+; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %b, <2 x float> %e, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-FTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+; packed
+
+define <2 x float> @add_f32x2_bf16x2_packed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-LABEL: add_f32x2_bf16x2_packed(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<3>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_packed_param_0];
+; CHECK-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_packed_param_1];
+; CHECK-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_packed_commuted(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-LABEL: add_f32x2_bf16x2_packed_commuted(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<3>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_packed_commuted_param_0];
+; CHECK-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_packed_commuted_param_1];
+; CHECK-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %b, <2 x float> %e, i32 1)
+  ret <2 x float> %r
+}
+
+; generic fadd
+
+define <2 x float> @add_f32x2_bf16x2_generic_lanes(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_lanes(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lanes_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_lanes_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_lanes(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lanes_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_lanes_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = fadd <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_generic_lanes_commuted(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_lanes_commuted(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lanes_commuted_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_lanes_commuted_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_lanes_commuted(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lanes_commuted_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_lanes_commuted_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = fadd <2 x float> %b, %e
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_generic_packed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_packed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_generic_packed_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_packed_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_packed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_generic_packed_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_packed_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %r = fadd <2 x float> %e, %b
+  ret <2 x float> %r
+}

>From 2007ca064a9a849dafd404f00f3a67d45e138fe6 Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Tue, 8 Sep 2026 04:27:05 +0000
Subject: [PATCH 2/7] change ordering and add comment

---
 llvm/lib/Target/NVPTX/NVPTXIntrinsics.td | 38 +++++++++++++-----------
 1 file changed, 21 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index c047c4fd4f94b..a723af51740ac 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -2541,23 +2541,6 @@ foreach rnd = FPRoundingModes in {
   }
 }
 
-// mixed precision, downconverting
-foreach t = [F16X2RT, BF16X2RT] in {
-  defvar ftz = !if(!eq(t, F16X2RT), "ftz", "");
-  defvar AddOp = !cast<Intrinsic>(StrJoin<"_", ["int_nvvm_fadd", ftz]>.ret);
-  defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
-
-  def INT_NVVM_MIXED_ADD_rz_ # t.PtxType # _f32x2 :
-    BasicNVPTXInst<(outs t.RC:$dst), (ins B64:$a, B64:$b),
-      StrJoin<".", ["add.rz", ftz, t.PtxType, "f32x2", "f32x2"]>.ret,
-      // The cvt packs its first argument into the high half of the result.
-      [(set t.Ty:$dst,
-        (CvtOp (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
-               (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
-               /*pzo=*/0))]>,
-    Requires<[SM107f]>;
-}
-
 // Pattern for fadd when there is no FTZ flag
 let Predicates = [SM100, doNoF32FTZ] in {
   def : Pat<(f32 (fadd (f32 (fpextend f16:$a)), f32:$b)),
@@ -2579,6 +2562,27 @@ let Predicates = [SM107f, doNoF32FTZ] in
               (Inst (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
   }
 
+
+// mixed precision - downconverting
+
+// f16x2 - rz rounding mode, only with ftz
+// bf16x2 - rz rounding mode, without ftz
+foreach t = [F16X2RT, BF16X2RT] in {
+  defvar ftz = !if(!eq(t, F16X2RT), "ftz", "");
+  defvar AddOp = !cast<Intrinsic>(StrJoin<"_", ["int_nvvm_fadd", ftz]>.ret);
+  defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
+
+  def INT_NVVM_MIXED_ADD_rz_ # t.PtxType # _f32x2 :
+    BasicNVPTXInst<(outs t.RC:$dst), (ins B64:$a, B64:$b),
+      StrJoin<".", ["add.rz", ftz, t.PtxType, "f32x2", "f32x2"]>.ret,
+      // The cvt packs its first argument into the high half of the result.
+      [(set t.Ty:$dst,
+        (CvtOp (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
+               (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
+               /*pzo=*/0))]>,
+    Requires<[SM107f]>;
+}
+
 //
 // Sub
 //

>From 57e2cdf3743b7a3475380820c749f7732c68f28d Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Tue, 8 Sep 2026 06:14:49 +0000
Subject: [PATCH 3/7] add sub patterns

---
 llvm/lib/Target/NVPTX/NVPTXIntrinsics.td      |  54 ++
 .../mixed-precision-sub-f32x2-downconvert.ll  | 346 ++++++++++++
 .../mixed-precision-sub-f32x2-upconvert.ll    | 524 ++++++++++++++++++
 3 files changed, 924 insertions(+)
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll

diff --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index a723af51740ac..a653c70050e8b 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -2644,6 +2644,7 @@ def INT_NVVM_SUB_D :
     [(set f64:$dst, (int_nvvm_fadd f64:$a, (f64 (fneg f64:$b)), timm:$rnd))]>;
 
 // mixed precision
+
 foreach rnd = FPRoundingModes in {
   defvar rnd_imm = !cast<TImmLeaf>(StrJoin<"_", ["fp_rnd", rnd, "imm"]>.ret);
 
@@ -2657,6 +2658,26 @@ foreach rnd = FPRoundingModes in {
              (f32 (fpextend type:$a)),
              (f32 (fneg f32:$b)), rnd_imm))]>,
         Requires<[SM100]>;
+
+  foreach t = [F16X2RT, BF16X2RT] in {
+    // combineFAddWithNeg has already folded the fneg into a sub node.
+    defvar SubOp = !cast<SDNode>("sub_" # rnd);
+
+    def INT_NVVM_MIXED_SUB_ # rnd # _f32x2_ # t.PtxType :
+      BasicNVPTXInst<(outs B64:$dst), (ins t.RC:$a, B64:$b),
+        StrJoin<".", ["sub", rnd, "f32x2", t.PtxType, "f32x2"]>.ret,
+        [(set v2f32:$dst,
+          (SubOp (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b))]>,
+      Requires<[SM107f]>;
+
+    def : Pat<(v2f32 (SubOp
+                       (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
+                                                  t.Ty.ElementType:$a1),
+                       v2f32:$b)),
+              (!cast<Instruction>("INT_NVVM_MIXED_SUB_" # rnd # "_f32x2_" #
+                                  t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>,
+      Requires<[SM107f]>;
+  }
 }
 
 // Pattern for fsub when there is no FTZ flag
@@ -2667,6 +2688,39 @@ let Predicates = [SM100, doNoF32FTZ] in {
             (INT_NVVM_MIXED_SUB_rn_f32_bf16 B16:$a, B32:$b)>;
 }
 
+let Predicates = [SM107f, doNoF32FTZ] in
+  foreach t = [F16X2RT, BF16X2RT] in {
+    defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_SUB_rn_f32x2_" # t.PtxType);
+
+    def : Pat<(v2f32 (fsub (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b)),
+              (Inst $a, $b)>;
+
+    def : Pat<(v2f32 (fsub (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
+                                                      t.Ty.ElementType:$a1),
+                           v2f32:$b)),
+              (Inst (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
+  }
+
+// mixed precision - downconverting
+
+// f16x2 - rz rounding mode, only with ftz
+// bf16x2 - rz rounding mode, without ftz
+foreach t = [F16X2RT, BF16X2RT] in {
+  defvar ftz = !if(!eq(t, F16X2RT), "ftz", "");
+  defvar SubOp = !cast<SDNode>(StrJoin<"_", ["sub", "rz", ftz]>.ret);
+  defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
+
+  def INT_NVVM_MIXED_SUB_rz_ # t.PtxType # _f32x2 :
+    BasicNVPTXInst<(outs t.RC:$dst), (ins B64:$a, B64:$b),
+      StrJoin<".", ["sub.rz", ftz, t.PtxType, "f32x2", "f32x2"]>.ret,
+      // The cvt packs its first argument into the high half of the result.
+      [(set t.Ty:$dst,
+        (CvtOp (extractelt (SubOp v2f32:$a, v2f32:$b), 1),
+               (extractelt (SubOp v2f32:$a, v2f32:$b), 0),
+               /*pzo=*/0))]>,
+    Requires<[SM107f]>;
+}
+
 //
 // BFIND
 //
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll
new file mode 100644
index 0000000000000..ae71d63dc5da8
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll
@@ -0,0 +1,346 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+
+;
+;  F16x2
+;
+
+define <2 x half> @sub_rz_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_negated_minuend(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_negated_minuend(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_negated_minuend_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_negated_minuend_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd2, %rd1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %na = fneg <2 x float> %a
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %na, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [sub_rz_f16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    sub.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+; invalid patterns
+
+define <2 x half> @sub_rz_f16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_no_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_no_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_no_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_no_ftz_param_1];
+; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rn_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rn_f16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rn_f16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rn_f16x2_f32x2_param_1];
+; CHECK-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_rn_convert(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_rn_convert(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_rn_convert_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_rn_convert_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rn.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_relu(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_relu(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_relu_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_relu_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.relu.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_distinct_subs(<2 x float> %a, <2 x float> %b, <2 x float> %c) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_distinct_subs(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_distinct_subs_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_distinct_subs_param_1];
+; CHECK-NEXT:    ld.param::func.b64 %rd3, [sub_rz_f16x2_f32x2_distinct_subs_param_2];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd5, %rd1, %rd3;
+; CHECK-NEXT:    mov.b64 {%r1, _}, %rd4;
+; CHECK-NEXT:    mov.b64 {_, %r2}, %rd5;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %nc = fneg <2 x float> %c
+  %sum1 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %sum2 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nc, i32 0)
+  %lo = extractelement <2 x float> %sum1, i32 0
+  %hi = extractelement <2 x float> %sum2, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+;
+;  BF16x2
+;
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_param_1];
+; CHECK-NEXT:    sub.rz.bf16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_negated_minuend(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_negated_minuend(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_negated_minuend_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_negated_minuend_param_1];
+; CHECK-NEXT:    sub.rz.bf16x2.f32x2.f32x2 %r1, %rd2, %rd1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %na = fneg <2 x float> %a
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %na, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [sub_rz_bf16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    sub.rz.bf16x2.f32x2.f32x2 %r1, %rd1, %rd2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+; invalid patterns
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_ftz_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @sub_rn_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rn_bf16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rn_bf16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rn_bf16x2_f32x2_param_1];
+; CHECK-NEXT:    sub.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll
new file mode 100644
index 0000000000000..9872325d3a302
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll
@@ -0,0 +1,524 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck --check-prefixes=CHECK,CHECK-NOFTZ %s
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | FileCheck --check-prefixes=CHECK,CHECK-FTZ %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | %ptxas-verify -arch=sm_107f %}
+
+;
+;  F16x2
+;
+
+; lanes
+
+define <2 x float> @sub_f32x2_f16x2_lanes(<2 x half> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_f32x2_f16x2_lanes(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_lanes_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_lanes_param_1];
+; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NEXT:    sub.rz.f32x2.f16x2.f32x2 %rd3, %r1, %rd2;
+; CHECK-NEXT:    sub.rm.f32x2.f16x2.f32x2 %rd4, %r1, %rd3;
+; CHECK-NEXT:    sub.rp.f32x2.f16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %nb = fneg <2 x float> %b
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  %n1 = fneg <2 x float> %r1
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n1, i32 0)
+  %n2 = fneg <2 x float> %r2
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n2, i32 3)
+  %n3 = fneg <2 x float> %r3
+  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n3, i32 2)
+  ret <2 x float> %r4
+}
+
+define <2 x float> @sub_f32x2_f16x2_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-FTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %nb = fneg <2 x float> %b
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  ret <2 x float> %r
+}
+
+; packed
+
+define <2 x float> @sub_f32x2_f16x2_packed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_packed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_f16x2_packed_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_packed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_packed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_f16x2_packed_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_packed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %nb = fneg <2 x float> %b
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  ret <2 x float> %r
+}
+
+; generic fsub
+
+define <2 x float> @sub_f32x2_f16x2_generic_lanes(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_lanes(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_lanes_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_lanes_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_lanes(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_lanes_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_lanes_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = fsub <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_f16x2_generic_packed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_packed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_f16x2_generic_packed_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_packed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_packed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_f16x2_generic_packed_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_packed_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %r = fsub <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+; invalid patterns
+
+define <2 x float> @sub_f32x2_f16x2_reversed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %ne = fneg <2 x float> %e
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %ne, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_f16x2_generic_reversed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = fsub <2 x float> %b, %e
+  ret <2 x float> %r
+}
+
+;
+;  BF16x2
+;
+
+; lanes
+
+define <2 x float> @sub_f32x2_bf16x2_lanes(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_f32x2_bf16x2_lanes(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_lanes_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_lanes_param_1];
+; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NEXT:    sub.rz.f32x2.bf16x2.f32x2 %rd3, %r1, %rd2;
+; CHECK-NEXT:    sub.rm.f32x2.bf16x2.f32x2 %rd4, %r1, %rd3;
+; CHECK-NEXT:    sub.rp.f32x2.bf16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %nb = fneg <2 x float> %b
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  %n1 = fneg <2 x float> %r1
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n1, i32 0)
+  %n2 = fneg <2 x float> %r2
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n2, i32 3)
+  %n3 = fneg <2 x float> %r3
+  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n3, i32 2)
+  ret <2 x float> %r4
+}
+
+define <2 x float> @sub_f32x2_bf16x2_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
+; CHECK-FTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %nb = fneg <2 x float> %b
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  ret <2 x float> %r
+}
+
+; packed
+
+define <2 x float> @sub_f32x2_bf16x2_packed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_f32x2_bf16x2_packed(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<3>;
+; CHECK-NEXT:    .reg .b64 %rd<3>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_bf16x2_packed_param_0];
+; CHECK-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_packed_param_1];
+; CHECK-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %nb = fneg <2 x float> %b
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  ret <2 x float> %r
+}
+
+; generic fsub
+
+define <2 x float> @sub_f32x2_bf16x2_generic_lanes(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_lanes(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_lanes_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_lanes_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_lanes(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_lanes_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_lanes_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = fsub <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_bf16x2_generic_packed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_packed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<3>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_bf16x2_generic_packed_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_packed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd2, %r2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd2;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_packed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_bf16x2_generic_packed_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_packed_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %r = fsub <2 x float> %e, %b
+  ret <2 x float> %r
+}
+
+; invalid patterns
+
+define <2 x float> @sub_f32x2_bf16x2_reversed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %ne = fneg <2 x float> %e
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %ne, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_bf16x2_generic_reversed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = fsub <2 x float> %b, %e
+  ret <2 x float> %r
+}

>From 035c4cba53cabe4be5c7d4918bb8f2f11d4c473c Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Tue, 8 Sep 2026 09:29:42 +0000
Subject: [PATCH 4/7] add invalid pattern tests for upconvert add

---
 .../mixed-precision-add-f32x2-upconvert.ll    | 158 ++++++++++++++++++
 1 file changed, 158 insertions(+)

diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
index 35d53eebf8ef0..27708d4177786 100644
--- a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
@@ -273,6 +273,85 @@ define <2 x float> @add_f32x2_f16x2_generic_packed(<2 x half> %a, <2 x float> %b
   ret <2 x float> %r
 }
 
+; invalid patterns
+
+define <2 x float> @add_f32x2_f16x2_ftz(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_ftz(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_ftz_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_ftz_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_ftz(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_ftz_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_ftz_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_partial(<2 x half> %a, float %c, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_partial(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_partial_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_f16x2_partial_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_partial_param_2];
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_partial(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_partial_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_f16x2_partial_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_partial_param_2];
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x half> %a, i32 0
+  %e0 = fpext half %a0 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %c, i32 1
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %v1, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
 ;
 ;  BF16x2
 ;
@@ -515,3 +594,82 @@ define <2 x float> @add_f32x2_bf16x2_generic_packed(<2 x bfloat> %a, <2 x float>
   %r = fadd <2 x float> %e, %b
   ret <2 x float> %r
 }
+
+; invalid patterns
+
+define <2 x float> @add_f32x2_bf16x2_ftz(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_ftz(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_ftz_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_ftz_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_ftz(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_ftz_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_ftz_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_partial(<2 x bfloat> %a, float %c, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_partial(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_partial_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_bf16x2_partial_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_partial_param_2];
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_partial(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_partial_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_bf16x2_partial_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_partial_param_2];
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x bfloat> %a, i32 0
+  %e0 = fpext bfloat %a0 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %c, i32 1
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %v1, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}

>From a843d961edb90ac7adba8c9de6a0735129b5033f Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Wed, 9 Sep 2026 06:47:27 +0000
Subject: [PATCH 5/7] address comments

---
 llvm/lib/Target/NVPTX/NVPTXInstrInfo.td  |  1 +
 llvm/lib/Target/NVPTX/NVPTXIntrinsics.td | 96 ++++++++++++------------
 2 files changed, 49 insertions(+), 48 deletions(-)

diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
index 846bcc0557464..bd2e8dd532d87 100644
--- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
+++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
@@ -181,6 +181,7 @@ def hasTensormapReplaceSupport : SubtargetPredicate;
 //  - TMA S2G im2col_w mode support
 //  - tcgen05.commit shared mem A variants.
 //  - conversions involving ue5m3x2
+//  - f16x2/bf16x2 mixed precision support
 def hasRubinFamilySupport : PredOr<[SM107f]>;
 
 // Checks tcgen05.shift instruction support.
diff --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index a653c70050e8b..137ed64cd10b4 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -2509,7 +2509,7 @@ def fpextend_v2f32_from_lanes : PatFrag<(ops node:$a0, node:$a1),
                                           (f32 (fpextend node:$a1))))>;
 
 foreach rnd = FPRoundingModes in {
-  defvar rnd_imm = !cast<TImmLeaf>(StrJoin<"_", ["fp_rnd", rnd, "imm"]>.ret);
+  defvar rnd_imm = !cast<TImmLeaf>("fp_rnd_" # rnd # "_imm");
 
   foreach sat = ["", "sat"] in
     foreach type = [f16, bf16] in
@@ -2522,22 +2522,22 @@ foreach rnd = FPRoundingModes in {
              f32:$b, rnd_imm))]>,
         Requires<[SM100]>;
 
-  foreach t = [F16X2RT, BF16X2RT] in {
-    def INT_NVVM_MIXED_ADD_ # rnd # _f32x2_ # t.PtxType :
-      BasicNVPTXInst<(outs B64:$dst), (ins t.RC:$a, B64:$b),
-        StrJoin<".", ["add", rnd, "f32x2", t.PtxType, "f32x2"]>.ret,
-        [(set v2f32:$dst,
-          (int_nvvm_fadd (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b,
-                         rnd_imm))]>,
-      Requires<[SM107f]>;
-
-    def : Pat<(v2f32 (int_nvvm_fadd
-                       (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
-                                                  t.Ty.ElementType:$a1),
-                       v2f32:$b, rnd_imm)),
-              (!cast<Instruction>("INT_NVVM_MIXED_ADD_" # rnd # "_f32x2_" #
-                                  t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>,
-      Requires<[SM107f]>;
+  let Predicates = [hasRubinFamilySupport] in {
+    foreach t = [F16X2RT, BF16X2RT] in {
+      def INT_NVVM_MIXED_ADD_ # rnd # _f32x2_ # t.PtxType :
+        BasicNVPTXInst<(outs B64:$dst), (ins B32:$a, B64:$b),
+          "add." # rnd # ".f32x2." # t.PtxType # ".f32x2",
+          [(set v2f32:$dst,
+            (int_nvvm_fadd (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b,
+                          rnd_imm))]>;
+
+      def : Pat<(v2f32 (int_nvvm_fadd
+                        (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
+                                                    t.Ty.ElementType:$a1),
+                        v2f32:$b, rnd_imm)),
+                (!cast<Instruction>("INT_NVVM_MIXED_ADD_" # rnd # "_f32x2_" #
+                                    t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
+    }
   }
 }
 
@@ -2549,7 +2549,7 @@ let Predicates = [SM100, doNoF32FTZ] in {
             (INT_NVVM_MIXED_ADD_rn_f32_bf16 B16:$a, B32:$b)>;
 }
 
-let Predicates = [SM107f, doNoF32FTZ] in
+let Predicates = [hasRubinFamilySupport, doNoF32FTZ] in
   foreach t = [F16X2RT, BF16X2RT] in {
     defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_ADD_rn_f32x2_" # t.PtxType);
 
@@ -2568,19 +2568,19 @@ let Predicates = [SM107f, doNoF32FTZ] in
 // f16x2 - rz rounding mode, only with ftz
 // bf16x2 - rz rounding mode, without ftz
 foreach t = [F16X2RT, BF16X2RT] in {
-  defvar ftz = !if(!eq(t, F16X2RT), "ftz", "");
-  defvar AddOp = !cast<Intrinsic>(StrJoin<"_", ["int_nvvm_fadd", ftz]>.ret);
+  defvar ftz = !if(!eq(t, F16X2RT), ".ftz", "");
+  defvar AddOp = !cast<Intrinsic>("int_nvvm_fadd" # !if(!eq(ftz, ""), "", "_ftz"));
   defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
 
   def INT_NVVM_MIXED_ADD_rz_ # t.PtxType # _f32x2 :
-    BasicNVPTXInst<(outs t.RC:$dst), (ins B64:$a, B64:$b),
-      StrJoin<".", ["add.rz", ftz, t.PtxType, "f32x2", "f32x2"]>.ret,
+    BasicNVPTXInst<(outs B32:$dst), (ins B64:$a, B64:$b),
+      "add.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2",
       // The cvt packs its first argument into the high half of the result.
       [(set t.Ty:$dst,
         (CvtOp (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
                (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
                /*pzo=*/0))]>,
-    Requires<[SM107f]>;
+    Requires<[hasRubinFamilySupport]>;
 }
 
 //
@@ -2646,7 +2646,7 @@ def INT_NVVM_SUB_D :
 // mixed precision
 
 foreach rnd = FPRoundingModes in {
-  defvar rnd_imm = !cast<TImmLeaf>(StrJoin<"_", ["fp_rnd", rnd, "imm"]>.ret);
+  defvar rnd_imm = !cast<TImmLeaf>("fp_rnd_" # rnd # "_imm");
 
   foreach sat = ["", "sat"] in
     foreach type = [f16, bf16] in
@@ -2659,24 +2659,24 @@ foreach rnd = FPRoundingModes in {
              (f32 (fneg f32:$b)), rnd_imm))]>,
         Requires<[SM100]>;
 
-  foreach t = [F16X2RT, BF16X2RT] in {
-    // combineFAddWithNeg has already folded the fneg into a sub node.
-    defvar SubOp = !cast<SDNode>("sub_" # rnd);
-
-    def INT_NVVM_MIXED_SUB_ # rnd # _f32x2_ # t.PtxType :
-      BasicNVPTXInst<(outs B64:$dst), (ins t.RC:$a, B64:$b),
-        StrJoin<".", ["sub", rnd, "f32x2", t.PtxType, "f32x2"]>.ret,
-        [(set v2f32:$dst,
-          (SubOp (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b))]>,
-      Requires<[SM107f]>;
-
-    def : Pat<(v2f32 (SubOp
-                       (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
-                                                  t.Ty.ElementType:$a1),
-                       v2f32:$b)),
-              (!cast<Instruction>("INT_NVVM_MIXED_SUB_" # rnd # "_f32x2_" #
-                                  t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>,
-      Requires<[SM107f]>;
+  let Predicates = [hasRubinFamilySupport] in {
+    foreach t = [F16X2RT, BF16X2RT] in {
+      // combineFAddWithNeg has already folded the fneg into a sub node.
+      defvar SubOp = !cast<SDNode>("sub_" # rnd);
+
+      def INT_NVVM_MIXED_SUB_ # rnd # _f32x2_ # t.PtxType :
+        BasicNVPTXInst<(outs B64:$dst), (ins B32:$a, B64:$b),
+          "sub." # rnd # ".f32x2." # t.PtxType # ".f32x2",
+          [(set v2f32:$dst,
+            (SubOp (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b))]>;
+
+      def : Pat<(v2f32 (SubOp
+                        (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
+                                                    t.Ty.ElementType:$a1),
+                        v2f32:$b)),
+                (!cast<Instruction>("INT_NVVM_MIXED_SUB_" # rnd # "_f32x2_" #
+                                    t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
+    }
   }
 }
 
@@ -2688,7 +2688,7 @@ let Predicates = [SM100, doNoF32FTZ] in {
             (INT_NVVM_MIXED_SUB_rn_f32_bf16 B16:$a, B32:$b)>;
 }
 
-let Predicates = [SM107f, doNoF32FTZ] in
+let Predicates = [hasRubinFamilySupport, doNoF32FTZ] in
   foreach t = [F16X2RT, BF16X2RT] in {
     defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_SUB_rn_f32x2_" # t.PtxType);
 
@@ -2706,19 +2706,19 @@ let Predicates = [SM107f, doNoF32FTZ] in
 // f16x2 - rz rounding mode, only with ftz
 // bf16x2 - rz rounding mode, without ftz
 foreach t = [F16X2RT, BF16X2RT] in {
-  defvar ftz = !if(!eq(t, F16X2RT), "ftz", "");
-  defvar SubOp = !cast<SDNode>(StrJoin<"_", ["sub", "rz", ftz]>.ret);
+  defvar ftz = !if(!eq(t, F16X2RT), ".ftz", "");
+  defvar SubOp = !cast<SDNode>("sub_rz" # !if(!eq(ftz, ""), "", "_ftz"));
   defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
 
   def INT_NVVM_MIXED_SUB_rz_ # t.PtxType # _f32x2 :
-    BasicNVPTXInst<(outs t.RC:$dst), (ins B64:$a, B64:$b),
-      StrJoin<".", ["sub.rz", ftz, t.PtxType, "f32x2", "f32x2"]>.ret,
+    BasicNVPTXInst<(outs B32:$dst), (ins B64:$a, B64:$b),
+      "sub.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2",
       // The cvt packs its first argument into the high half of the result.
       [(set t.Ty:$dst,
         (CvtOp (extractelt (SubOp v2f32:$a, v2f32:$b), 1),
                (extractelt (SubOp v2f32:$a, v2f32:$b), 0),
                /*pzo=*/0))]>,
-    Requires<[SM107f]>;
+    Requires<[hasRubinFamilySupport]>;
 }
 
 //

>From 5b0cc6000006bda4d279c1298a9374b10404fd73 Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Wed, 9 Sep 2026 10:58:43 +0000
Subject: [PATCH 6/7] constrain folding only to single-use

---
 llvm/lib/Target/NVPTX/NVPTXIntrinsics.td      |  44 +-
 ...precision-add-f32x2-downconvert-invalid.ll | 264 +++++++
 .../mixed-precision-add-f32x2-downconvert.ll  | 243 ------
 ...d-precision-add-f32x2-upconvert-invalid.ll | 711 ++++++++++++++++++
 .../mixed-precision-add-f32x2-upconvert.ll    | 324 +++-----
 ...precision-sub-f32x2-downconvert-invalid.ll | 276 +++++++
 .../mixed-precision-sub-f32x2-downconvert.ll  | 255 -------
 ...d-precision-sub-f32x2-upconvert-invalid.ll | 535 +++++++++++++
 .../mixed-precision-sub-f32x2-upconvert.ll    | 350 +++------
 9 files changed, 2023 insertions(+), 979 deletions(-)
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert-invalid.ll
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert-invalid.ll
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert-invalid.ll
 create mode 100644 llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert-invalid.ll

diff --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index 137ed64cd10b4..424f4a80be890 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -2496,17 +2496,33 @@ def INT_NVVM_ADD_D :
 
 // mixed precision
 
+// match only when the fold removes at least one explicit conversion, and the
+// extended vector is not shared.
+class FoldableFPExtendV2F32<dag ops, dag frag>
+  : PatFrag<ops, frag, [{
+      return N->hasOneUse() && (N->getOperand(0)->hasOneUse() ||
+                                N->getOperand(1)->hasOneUse());
+    }]>;
+
 // FP_EXTEND to v2f32 is scalarized before isel, leaving one of two shapes
 // behind depending on whether the source lanes share a register.
-def fpextend_v2f32_from_packed : PatFrag<(ops node:$a),
-                                         (v2f32 (build_vector
-                                           (f32 (fpextend (extractelt node:$a, 0))),
-                                           (f32 (fpextend (extractelt node:$a, 1)))))>;
-
-def fpextend_v2f32_from_lanes : PatFrag<(ops node:$a0, node:$a1),
-                                        (v2f32 (build_vector
-                                          (f32 (fpextend node:$a0)),
-                                          (f32 (fpextend node:$a1))))>;
+def fpextend_v2f32_from_packed
+  : FoldableFPExtendV2F32<(ops node:$a),
+                          (v2f32 (build_vector
+                            (f32 (fpextend (extractelt node:$a, 0))),
+                            (f32 (fpextend (extractelt node:$a, 1)))))>;
+
+def fpextend_v2f32_from_lanes
+  : FoldableFPExtendV2F32<(ops node:$a0, node:$a1),
+                          (v2f32 (build_vector
+                            (f32 (fpextend node:$a0)),
+                            (f32 (fpextend node:$a1))))>;
+
+// fuse downconverting add/sub only when the result of the add/sub is solely
+// used for downconverting.
+def extractelt_only_for_cvt
+  : PatFrag<(ops node:$vec, node:$lane), (extractelt node:$vec, node:$lane),
+            [{ return N->hasOneUse() && N->getOperand(0)->hasNUsesOfValue(2, 0); }]>;
 
 foreach rnd = FPRoundingModes in {
   defvar rnd_imm = !cast<TImmLeaf>("fp_rnd_" # rnd # "_imm");
@@ -2577,8 +2593,10 @@ foreach t = [F16X2RT, BF16X2RT] in {
       "add.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2",
       // The cvt packs its first argument into the high half of the result.
       [(set t.Ty:$dst,
-        (CvtOp (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
-               (extractelt (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
+        (CvtOp (extractelt_only_for_cvt
+                 (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
+               (extractelt_only_for_cvt
+                 (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
                /*pzo=*/0))]>,
     Requires<[hasRubinFamilySupport]>;
 }
@@ -2715,8 +2733,8 @@ foreach t = [F16X2RT, BF16X2RT] in {
       "sub.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2",
       // The cvt packs its first argument into the high half of the result.
       [(set t.Ty:$dst,
-        (CvtOp (extractelt (SubOp v2f32:$a, v2f32:$b), 1),
-               (extractelt (SubOp v2f32:$a, v2f32:$b), 0),
+        (CvtOp (extractelt_only_for_cvt (SubOp v2f32:$a, v2f32:$b), 1),
+               (extractelt_only_for_cvt (SubOp v2f32:$a, v2f32:$b), 0),
                /*pzo=*/0))]>,
     Requires<[hasRubinFamilySupport]>;
 }
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert-invalid.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert-invalid.ll
new file mode 100644
index 0000000000000..6d450a197a86e
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert-invalid.ll
@@ -0,0 +1,264 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+
+;
+; Cases where the mixed-precision instruction must not be selected.
+;
+
+;
+;  F16x2
+;
+
+; multiple uses
+
+define <2 x half> @add_rz_f16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_f16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+; unsupported forms
+
+define <2 x half> @add_rz_f16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_no_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_no_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_no_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_no_ftz_param_1];
+; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rn_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rn_f16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rn_f16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rn_f16x2_f32x2_param_1];
+; CHECK-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_rn_convert(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_rn_convert(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_rn_convert_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_rn_convert_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rn.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_relu(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_relu(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_relu_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_relu_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.relu.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @add_rz_f16x2_f32x2_distinct_adds(<2 x float> %a, <2 x float> %b, <2 x float> %c) {
+; CHECK-LABEL: add_rz_f16x2_f32x2_distinct_adds(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_distinct_adds_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_distinct_adds_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_f16x2_f32x2_distinct_adds_param_2];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd5, %rd1, %rd4;
+; CHECK-NEXT:    mov.b64 {%r1, _}, %rd3;
+; CHECK-NEXT:    mov.b64 {_, %r2}, %rd5;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum1 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %sum2 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %c, i32 0)
+  %lo = extractelement <2 x float> %sum1, i32 0
+  %hi = extractelement <2 x float> %sum2, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+;
+;  BF16x2
+;
+
+; multiple uses
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_bf16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+; unsupported forms
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @add_rz_bf16x2_f32x2_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rz_bf16x2_f32x2_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_ftz_param_1];
+; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @add_rn_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: add_rn_bf16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rn_bf16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rn_bf16x2_f32x2_param_1];
+; CHECK-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll
index 9dbfe1e3ab014..de26b06fcbdf2 100644
--- a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-downconvert.ll
@@ -44,161 +44,6 @@ define <2 x half> @add_rz_f16x2_f32x2_commuted(<2 x float> %a, <2 x float> %b) {
   ret <2 x half> %r
 }
 
-define <2 x half> @add_rz_f16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
-; CHECK-LABEL: add_rz_f16x2_f32x2_extra_use(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<2>;
-; CHECK-NEXT:    .reg .b64 %rd<5>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_extra_use_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_extra_use_param_1];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_f16x2_f32x2_extra_use_param_2];
-; CHECK-NEXT:    st.b64 [%rd4], %rd3;
-; CHECK-NEXT:    add.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd1, %rd2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  store <2 x float> %sum, ptr %p
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-; invalid patterns
-
-define <2 x half> @add_rz_f16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rz_f16x2_f32x2_swapped_halves(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_swapped_halves_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_swapped_halves_param_1];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r1, %r2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %lo, float %hi, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @add_rz_f16x2_f32x2_no_ftz(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rz_f16x2_f32x2_no_ftz(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_no_ftz_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_no_ftz_param_1];
-; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @add_rn_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rn_f16x2_f32x2(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rn_f16x2_f32x2_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rn_f16x2_f32x2_param_1];
-; CHECK-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 1)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @add_rz_f16x2_f32x2_rn_convert(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rz_f16x2_f32x2_rn_convert(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_rn_convert_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_rn_convert_param_1];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rn.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @add_rz_f16x2_f32x2_relu(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rz_f16x2_f32x2_relu(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_relu_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_relu_param_1];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.relu.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @add_rz_f16x2_f32x2_distinct_adds(<2 x float> %a, <2 x float> %b, <2 x float> %c) {
-; CHECK-LABEL: add_rz_f16x2_f32x2_distinct_adds(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<6>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_f16x2_f32x2_distinct_adds_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_f16x2_f32x2_distinct_adds_param_1];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_f16x2_f32x2_distinct_adds_param_2];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd5, %rd1, %rd4;
-; CHECK-NEXT:    mov.b64 {%r1, _}, %rd3;
-; CHECK-NEXT:    mov.b64 {_, %r2}, %rd5;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum1 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %sum2 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %c, i32 0)
-  %lo = extractelement <2 x float> %sum1, i32 0
-  %hi = extractelement <2 x float> %sum2, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
 ;
 ;  BF16x2
 ;
@@ -240,91 +85,3 @@ define <2 x bfloat> @add_rz_bf16x2_f32x2_commuted(<2 x float> %a, <2 x float> %b
   %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
   ret <2 x bfloat> %r
 }
-
-define <2 x bfloat> @add_rz_bf16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
-; CHECK-LABEL: add_rz_bf16x2_f32x2_extra_use(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<2>;
-; CHECK-NEXT:    .reg .b64 %rd<5>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_extra_use_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_extra_use_param_1];
-; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    ld.param::func.b64 %rd4, [add_rz_bf16x2_f32x2_extra_use_param_2];
-; CHECK-NEXT:    st.b64 [%rd4], %rd3;
-; CHECK-NEXT:    add.rz.bf16x2.f32x2.f32x2 %r1, %rd1, %rd2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  store <2 x float> %sum, ptr %p
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x bfloat> %r
-}
-
-; invalid patterns
-
-define <2 x bfloat> @add_rz_bf16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rz_bf16x2_f32x2_swapped_halves(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_swapped_halves_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_swapped_halves_param_1];
-; CHECK-NEXT:    add.rz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r1, %r2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %lo, float %hi, i1 false)
-  ret <2 x bfloat> %r
-}
-
-define <2 x bfloat> @add_rz_bf16x2_f32x2_ftz(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rz_bf16x2_f32x2_ftz(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rz_bf16x2_f32x2_ftz_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rz_bf16x2_f32x2_ftz_param_1];
-; CHECK-NEXT:    add.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %b, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x bfloat> %r
-}
-
-define <2 x bfloat> @add_rn_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: add_rn_bf16x2_f32x2(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_rn_bf16x2_f32x2_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [add_rn_bf16x2_f32x2_param_1];
-; CHECK-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %b, i32 1)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x bfloat> %r
-}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert-invalid.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert-invalid.ll
new file mode 100644
index 0000000000000..e28efa99e90a1
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert-invalid.ll
@@ -0,0 +1,711 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck --check-prefixes=CHECK-NOFTZ %s
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | FileCheck --check-prefixes=CHECK-FTZ %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | %ptxas-verify -arch=sm_107f %}
+
+;
+; Cases where the mixed-precision instruction must not be selected.
+;
+
+;
+;  F16x2
+;
+
+; multiple uses of the extension
+
+define <2 x float> @add_f32x2_f16x2_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_generic_lanes_multiuse(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_lanes_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lanes_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_lanes_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd4, %rd1, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_lanes_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lanes_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_lanes_multiuse_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd4, %rd1, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r1 = fadd <2 x float> %e, %b
+  %r2 = fadd <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @add_f32x2_f16x2_generic_packed_multiuse(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_packed_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_generic_packed_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_packed_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r3, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r4, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd4, %rd2, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_packed_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_generic_packed_multiuse_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_packed_multiuse_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd4, %rd2, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %r1 = fadd <2 x float> %e, %b
+  %r2 = fadd <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @add_f32x2_f16x2_generic_both_lanes_extra_use(<2 x half> %a, <2 x float> %b, ptr %p, ptr %q) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_both_lanes_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs2;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd5, %rd4, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_both_lanes_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd5, %rd4, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x half> %a, i32 0
+  %a1 = extractelement <2 x half> %a, i32 1
+  %e0 = fpext half %a0 to float
+  %e1 = fpext half %a1 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  store float %e1, ptr %q
+  %r = fadd <2 x float> %v1, %b
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_sub_f32x2_f16x2_generic_multiuse(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_sub_f32x2_f16x2_generic_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_f16x2_generic_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_f16x2_generic_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd5, %rd3, %rd4;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_sub_f32x2_f16x2_generic_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_f16x2_generic_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_f16x2_generic_multiuse_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd5, %rd3, %rd4;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %add = fadd <2 x float> %e, %b
+  %sub = fsub <2 x float> %e, %b
+  %r = fadd <2 x float> %add, %sub
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_sub_f32x2_f16x2_multiuse(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_sub_f32x2_f16x2_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_f16x2_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_f16x2_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd5, %rd3, %rd4;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_sub_f32x2_f16x2_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_f16x2_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_f16x2_multiuse_param_1];
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd5, %rd3, %rd4;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %add = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  %nb = fneg <2 x float> %b
+  %sub = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %add, <2 x float> %sub, i32 1)
+  ret <2 x float> %r
+}
+
+; unsupported forms
+
+define <2 x float> @add_f32x2_f16x2_ftz(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_ftz(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_ftz_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_ftz_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_ftz(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_ftz_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_ftz_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_f16x2_partial(<2 x half> %a, float %c, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_partial(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_partial_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_f16x2_partial_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_partial_param_2];
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_partial(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_partial_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_f16x2_partial_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_partial_param_2];
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x half> %a, i32 0
+  %e0 = fpext half %a0 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %c, i32 1
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %v1, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+;
+;  BF16x2
+;
+
+; multiple uses of the extension
+
+define <2 x float> @add_f32x2_bf16x2_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_generic_lanes_multiuse(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_lanes_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lanes_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_lanes_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd4, %rd1, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_lanes_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lanes_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_lanes_multiuse_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd4, %rd1, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r1 = fadd <2 x float> %e, %b
+  %r2 = fadd <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @add_f32x2_bf16x2_generic_packed_multiuse(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_packed_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_generic_packed_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_packed_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r3, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r4, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd4, %rd2, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_packed_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_generic_packed_multiuse_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_packed_multiuse_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd4, %rd2, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %r1 = fadd <2 x float> %e, %b
+  %r2 = fadd <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @add_f32x2_bf16x2_generic_both_lanes_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p, ptr %q) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_both_lanes_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs2;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd5, %rd4, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_both_lanes_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd5, %rd4, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x bfloat> %a, i32 0
+  %a1 = extractelement <2 x bfloat> %a, i32 1
+  %e0 = fpext bfloat %a0 to float
+  %e1 = fpext bfloat %a1 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  store float %e1, ptr %q
+  %r = fadd <2 x float> %v1, %b
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_sub_f32x2_bf16x2_generic_multiuse(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_sub_f32x2_bf16x2_generic_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_bf16x2_generic_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_bf16x2_generic_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd5, %rd3, %rd4;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_sub_f32x2_bf16x2_generic_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_bf16x2_generic_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_bf16x2_generic_multiuse_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd5, %rd3, %rd4;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %add = fadd <2 x float> %e, %b
+  %sub = fsub <2 x float> %e, %b
+  %r = fadd <2 x float> %add, %sub
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_sub_f32x2_bf16x2_multiuse(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_sub_f32x2_bf16x2_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_bf16x2_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_bf16x2_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd5, %rd3, %rd4;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_sub_f32x2_bf16x2_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_sub_f32x2_bf16x2_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_sub_f32x2_bf16x2_multiuse_param_1];
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd5, %rd3, %rd4;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %add = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  %nb = fneg <2 x float> %b
+  %sub = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %add, <2 x float> %sub, i32 1)
+  ret <2 x float> %r
+}
+
+; unsupported forms
+
+define <2 x float> @add_f32x2_bf16x2_ftz(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_ftz(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_ftz_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_ftz_param_1];
+; CHECK-NOFTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_ftz(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_ftz_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_ftz_param_1];
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @add_f32x2_bf16x2_partial(<2 x bfloat> %a, float %c, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_partial(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_partial_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_bf16x2_partial_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_partial_param_2];
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_partial(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<2>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_partial_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_bf16x2_partial_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_partial_param_2];
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
+; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x bfloat> %a, i32 0
+  %e0 = fpext bfloat %a0 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %c, i32 1
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %v1, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
index 27708d4177786..c1acfe150289f 100644
--- a/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-add-f32x2-upconvert.ll
@@ -10,29 +10,38 @@
 
 ; lanes
 
-define <2 x float> @add_f32x2_f16x2_lanes(<2 x half> %a, <2 x float> %b) {
+define <2 x float> @add_f32x2_f16x2_lanes(<2 x half> %a0, <2 x half> %a1, <2 x half> %a2, <2 x half> %a3, <2 x float> %b) {
 ; CHECK-LABEL: add_f32x2_f16x2_lanes(
 ; CHECK:       {
-; CHECK-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b16 %rs<9>;
+; CHECK-NEXT:    .reg .b32 %r<5>;
 ; CHECK-NEXT:    .reg .b64 %rd<6>;
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  // %bb.0:
 ; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_lanes_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_lanes_param_1];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs3, %rs4}, [add_f32x2_f16x2_lanes_param_1];
 ; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_lanes_param_4];
 ; CHECK-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
-; CHECK-NEXT:    add.rz.f32x2.f16x2.f32x2 %rd3, %r1, %rd2;
-; CHECK-NEXT:    add.rm.f32x2.f16x2.f32x2 %rd4, %r1, %rd3;
-; CHECK-NEXT:    add.rp.f32x2.f16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs5, %rs6}, [add_f32x2_f16x2_lanes_param_2];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs7, %rs8}, [add_f32x2_f16x2_lanes_param_3];
+; CHECK-NEXT:    mov.b32 %r2, {%rs3, %rs4};
+; CHECK-NEXT:    add.rz.f32x2.f16x2.f32x2 %rd3, %r2, %rd2;
+; CHECK-NEXT:    mov.b32 %r3, {%rs5, %rs6};
+; CHECK-NEXT:    add.rm.f32x2.f16x2.f32x2 %rd4, %r3, %rd3;
+; CHECK-NEXT:    mov.b32 %r4, {%rs7, %rs8};
+; CHECK-NEXT:    add.rp.f32x2.f16x2.f32x2 %rd5, %r4, %rd4;
 ; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
 ; CHECK-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
-  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r1, i32 0)
-  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r2, i32 3)
-  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r3, i32 2)
-  ret <2 x float> %r4
+  %e0 = fpext <2 x half> %a0 to <2 x float>
+  %r0 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e0, <2 x float> %b, i32 1)
+  %e1 = fpext <2 x half> %a1 to <2 x float>
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e1, <2 x float> %r0, i32 0)
+  %e2 = fpext <2 x half> %a2 to <2 x float>
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e2, <2 x float> %r1, i32 3)
+  %e3 = fpext <2 x half> %a3 to <2 x float>
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e3, <2 x float> %r2, i32 2)
+  ret <2 x float> %r3
 }
 
 define <2 x float> @add_f32x2_f16x2_lanes_commuted(<2 x half> %a, <2 x float> %b) {
@@ -54,50 +63,6 @@ define <2 x float> @add_f32x2_f16x2_lanes_commuted(<2 x half> %a, <2 x float> %b
   ret <2 x float> %r
 }
 
-define <2 x float> @add_f32x2_f16x2_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
-; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_extra_use(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_extra_use_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_extra_use_param_1];
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_extra_use_param_2];
-; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: add_f32x2_f16x2_extra_use(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_extra_use_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_extra_use_param_1];
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_f16x2_extra_use_param_2];
-; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-FTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  store <2 x float> %e, ptr %p
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
-  ret <2 x float> %r
-}
-
 ; packed
 
 define <2 x float> @add_f32x2_f16x2_packed(<2 x half> %a, <2 x float> %b) {
@@ -273,82 +238,51 @@ define <2 x float> @add_f32x2_f16x2_generic_packed(<2 x half> %a, <2 x float> %b
   ret <2 x float> %r
 }
 
-; invalid patterns
+; generic fadd - partially shared extension
 
-define <2 x float> @add_f32x2_f16x2_ftz(<2 x half> %a, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_ftz(
+define <2 x float> @add_f32x2_f16x2_generic_lane_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_generic_lane_extra_use(
 ; CHECK-NOFTZ:       {
 ; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
 ; CHECK-NOFTZ-EMPTY:
 ; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_ftz_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_ftz_param_1];
-; CHECK-NOFTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lane_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_lane_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_lane_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r2, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.f16x2.f32x2 %rd3, %r2, %rd1;
 ; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
 ; CHECK-NOFTZ-NEXT:    ret;
 ;
-; CHECK-FTZ-LABEL: add_f32x2_f16x2_ftz(
+; CHECK-FTZ-LABEL: add_f32x2_f16x2_generic_lane_extra_use(
 ; CHECK-FTZ:       {
 ; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_ftz_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_ftz_param_1];
-; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  %r = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
-  ret <2 x float> %r
-}
-
-define <2 x float> @add_f32x2_f16x2_partial(<2 x half> %a, float %c, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: add_f32x2_f16x2_partial(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<2>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_partial_param_1];
-; CHECK-NOFTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_f16x2_partial_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_partial_param_2];
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: add_f32x2_f16x2_partial(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<2>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
 ; CHECK-FTZ-EMPTY:
 ; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_f16x2_partial_param_1];
-; CHECK-FTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_f16x2_partial_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_partial_param_2];
-; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
-; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_f16x2_generic_lane_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_f16x2_generic_lane_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_f16x2_generic_lane_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd3, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd4, %rd3, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
 ; CHECK-FTZ-NEXT:    ret;
   %a0 = extractelement <2 x half> %a, i32 0
+  %a1 = extractelement <2 x half> %a, i32 1
   %e0 = fpext half %a0 to float
+  %e1 = fpext half %a1 to float
   %v0 = insertelement <2 x float> poison, float %e0, i32 0
-  %v1 = insertelement <2 x float> %v0, float %c, i32 1
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %v1, <2 x float> %b, i32 1)
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  %r = fadd <2 x float> %v1, %b
   ret <2 x float> %r
 }
 
@@ -358,29 +292,38 @@ define <2 x float> @add_f32x2_f16x2_partial(<2 x half> %a, float %c, <2 x float>
 
 ; lanes
 
-define <2 x float> @add_f32x2_bf16x2_lanes(<2 x bfloat> %a, <2 x float> %b) {
+define <2 x float> @add_f32x2_bf16x2_lanes(<2 x bfloat> %a0, <2 x bfloat> %a1, <2 x bfloat> %a2, <2 x bfloat> %a3, <2 x float> %b) {
 ; CHECK-LABEL: add_f32x2_bf16x2_lanes(
 ; CHECK:       {
-; CHECK-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b16 %rs<9>;
+; CHECK-NEXT:    .reg .b32 %r<5>;
 ; CHECK-NEXT:    .reg .b64 %rd<6>;
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  // %bb.0:
 ; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_lanes_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_lanes_param_1];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs3, %rs4}, [add_f32x2_bf16x2_lanes_param_1];
 ; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_lanes_param_4];
 ; CHECK-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
-; CHECK-NEXT:    add.rz.f32x2.bf16x2.f32x2 %rd3, %r1, %rd2;
-; CHECK-NEXT:    add.rm.f32x2.bf16x2.f32x2 %rd4, %r1, %rd3;
-; CHECK-NEXT:    add.rp.f32x2.bf16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs5, %rs6}, [add_f32x2_bf16x2_lanes_param_2];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs7, %rs8}, [add_f32x2_bf16x2_lanes_param_3];
+; CHECK-NEXT:    mov.b32 %r2, {%rs3, %rs4};
+; CHECK-NEXT:    add.rz.f32x2.bf16x2.f32x2 %rd3, %r2, %rd2;
+; CHECK-NEXT:    mov.b32 %r3, {%rs5, %rs6};
+; CHECK-NEXT:    add.rm.f32x2.bf16x2.f32x2 %rd4, %r3, %rd3;
+; CHECK-NEXT:    mov.b32 %r4, {%rs7, %rs8};
+; CHECK-NEXT:    add.rp.f32x2.bf16x2.f32x2 %rd5, %r4, %rd4;
 ; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
 ; CHECK-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
-  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r1, i32 0)
-  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r2, i32 3)
-  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %r3, i32 2)
-  ret <2 x float> %r4
+  %e0 = fpext <2 x bfloat> %a0 to <2 x float>
+  %r0 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e0, <2 x float> %b, i32 1)
+  %e1 = fpext <2 x bfloat> %a1 to <2 x float>
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e1, <2 x float> %r0, i32 0)
+  %e2 = fpext <2 x bfloat> %a2 to <2 x float>
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e2, <2 x float> %r1, i32 3)
+  %e3 = fpext <2 x bfloat> %a3 to <2 x float>
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e3, <2 x float> %r2, i32 2)
+  ret <2 x float> %r3
 }
 
 define <2 x float> @add_f32x2_bf16x2_lanes_commuted(<2 x bfloat> %a, <2 x float> %b) {
@@ -402,50 +345,6 @@ define <2 x float> @add_f32x2_bf16x2_lanes_commuted(<2 x bfloat> %a, <2 x float>
   ret <2 x float> %r
 }
 
-define <2 x float> @add_f32x2_bf16x2_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
-; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_extra_use(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_extra_use_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_extra_use_param_1];
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_extra_use_param_2];
-; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-NOFTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: add_f32x2_bf16x2_extra_use(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_extra_use_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_extra_use_param_1];
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [add_f32x2_bf16x2_extra_use_param_2];
-; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-FTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  store <2 x float> %e, ptr %p
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
-  ret <2 x float> %r
-}
-
 ; packed
 
 define <2 x float> @add_f32x2_bf16x2_packed(<2 x bfloat> %a, <2 x float> %b) {
@@ -595,81 +494,50 @@ define <2 x float> @add_f32x2_bf16x2_generic_packed(<2 x bfloat> %a, <2 x float>
   ret <2 x float> %r
 }
 
-; invalid patterns
+; generic fadd - partially shared extension
 
-define <2 x float> @add_f32x2_bf16x2_ftz(<2 x bfloat> %a, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_ftz(
+define <2 x float> @add_f32x2_bf16x2_generic_lane_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_generic_lane_extra_use(
 ; CHECK-NOFTZ:       {
 ; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
 ; CHECK-NOFTZ-EMPTY:
 ; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_ftz_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_ftz_param_1];
-; CHECK-NOFTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lane_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_lane_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_lane_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r2, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    add.rn.f32x2.bf16x2.f32x2 %rd3, %r2, %rd1;
 ; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
 ; CHECK-NOFTZ-NEXT:    ret;
 ;
-; CHECK-FTZ-LABEL: add_f32x2_bf16x2_ftz(
+; CHECK-FTZ-LABEL: add_f32x2_bf16x2_generic_lane_extra_use(
 ; CHECK-FTZ:       {
 ; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_ftz_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_ftz_param_1];
-; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  %r = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %e, <2 x float> %b, i32 1)
-  ret <2 x float> %r
-}
-
-define <2 x float> @add_f32x2_bf16x2_partial(<2 x bfloat> %a, float %c, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: add_f32x2_bf16x2_partial(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<2>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_partial_param_1];
-; CHECK-NOFTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_bf16x2_partial_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_partial_param_2];
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: add_f32x2_bf16x2_partial(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<2>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
 ; CHECK-FTZ-EMPTY:
 ; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [add_f32x2_bf16x2_partial_param_1];
-; CHECK-FTZ-NEXT:    ld.param::func.b16 %rs1, [add_f32x2_bf16x2_partial_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_partial_param_2];
-; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r2, %r1};
-; CHECK-FTZ-NEXT:    add.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [add_f32x2_bf16x2_generic_lane_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [add_f32x2_bf16x2_generic_lane_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [add_f32x2_bf16x2_generic_lane_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd3, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    add.rn.ftz.f32x2 %rd4, %rd3, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
 ; CHECK-FTZ-NEXT:    ret;
   %a0 = extractelement <2 x bfloat> %a, i32 0
+  %a1 = extractelement <2 x bfloat> %a, i32 1
   %e0 = fpext bfloat %a0 to float
+  %e1 = fpext bfloat %a1 to float
   %v0 = insertelement <2 x float> poison, float %e0, i32 0
-  %v1 = insertelement <2 x float> %v0, float %c, i32 1
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %v1, <2 x float> %b, i32 1)
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  %r = fadd <2 x float> %v1, %b
   ret <2 x float> %r
 }
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert-invalid.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert-invalid.ll
new file mode 100644
index 0000000000000..b38860f261007
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert-invalid.ll
@@ -0,0 +1,276 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+
+;
+; Cases where the mixed-precision instruction must not be selected.
+;
+
+;
+;  F16x2
+;
+
+; multiple uses
+
+define <2 x half> @sub_rz_f16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [sub_rz_f16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+; unsupported forms
+
+define <2 x half> @sub_rz_f16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_no_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_no_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_no_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_no_ftz_param_1];
+; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rn_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rn_f16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rn_f16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rn_f16x2_f32x2_param_1];
+; CHECK-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_rn_convert(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_rn_convert(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_rn_convert_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_rn_convert_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rn.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_relu(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_relu(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_relu_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_relu_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.relu.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+define <2 x half> @sub_rz_f16x2_f32x2_distinct_subs(<2 x float> %a, <2 x float> %b, <2 x float> %c) {
+; CHECK-LABEL: sub_rz_f16x2_f32x2_distinct_subs(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<6>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_distinct_subs_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_distinct_subs_param_1];
+; CHECK-NEXT:    ld.param::func.b64 %rd3, [sub_rz_f16x2_f32x2_distinct_subs_param_2];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd5, %rd1, %rd3;
+; CHECK-NEXT:    mov.b64 {%r1, _}, %rd4;
+; CHECK-NEXT:    mov.b64 {_, %r2}, %rd5;
+; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %nc = fneg <2 x float> %c
+  %sum1 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %sum2 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nc, i32 0)
+  %lo = extractelement <2 x float> %sum1, i32 0
+  %hi = extractelement <2 x float> %sum2, i32 1
+  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x half> %r
+}
+
+;
+;  BF16x2
+;
+
+; multiple uses
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_extra_use(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<5>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_extra_use_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_extra_use_param_1];
+; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    ld.param::func.b64 %rd4, [sub_rz_bf16x2_f32x2_extra_use_param_2];
+; CHECK-NEXT:    st.b64 [%rd4], %rd3;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  store <2 x float> %sum, ptr %p
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+; unsupported forms
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_swapped_halves(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_swapped_halves_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_swapped_halves_param_1];
+; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r1, %r2;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %lo, float %hi, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @sub_rz_bf16x2_f32x2_ftz(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rz_bf16x2_f32x2_ftz(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_ftz_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_ftz_param_1];
+; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
+
+define <2 x bfloat> @sub_rn_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
+; CHECK-LABEL: sub_rn_bf16x2_f32x2(
+; CHECK:       {
+; CHECK-NEXT:    .reg .b32 %r<4>;
+; CHECK-NEXT:    .reg .b64 %rd<4>;
+; CHECK-EMPTY:
+; CHECK-NEXT:  // %bb.0:
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rn_bf16x2_f32x2_param_0];
+; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rn_bf16x2_f32x2_param_1];
+; CHECK-NEXT:    sub.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
+; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
+; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
+; CHECK-NEXT:    ret;
+  %nb = fneg <2 x float> %b
+  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 1)
+  %lo = extractelement <2 x float> %sum, i32 0
+  %hi = extractelement <2 x float> %sum, i32 1
+  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
+  ret <2 x bfloat> %r
+}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll
index ae71d63dc5da8..246b04173b657 100644
--- a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-downconvert.ll
@@ -46,169 +46,6 @@ define <2 x half> @sub_rz_f16x2_f32x2_negated_minuend(<2 x float> %a, <2 x float
   ret <2 x half> %r
 }
 
-define <2 x half> @sub_rz_f16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
-; CHECK-LABEL: sub_rz_f16x2_f32x2_extra_use(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<2>;
-; CHECK-NEXT:    .reg .b64 %rd<5>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_extra_use_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_extra_use_param_1];
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    ld.param::func.b64 %rd4, [sub_rz_f16x2_f32x2_extra_use_param_2];
-; CHECK-NEXT:    st.b64 [%rd4], %rd3;
-; CHECK-NEXT:    sub.rz.ftz.f16x2.f32x2.f32x2 %r1, %rd1, %rd2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  store <2 x float> %sum, ptr %p
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-; invalid patterns
-
-define <2 x half> @sub_rz_f16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rz_f16x2_f32x2_swapped_halves(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_swapped_halves_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_swapped_halves_param_1];
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r1, %r2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %lo, float %hi, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @sub_rz_f16x2_f32x2_no_ftz(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rz_f16x2_f32x2_no_ftz(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_no_ftz_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_no_ftz_param_1];
-; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @sub_rn_f16x2_f32x2(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rn_f16x2_f32x2(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rn_f16x2_f32x2_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rn_f16x2_f32x2_param_1];
-; CHECK-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 1)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @sub_rz_f16x2_f32x2_rn_convert(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rz_f16x2_f32x2_rn_convert(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_rn_convert_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_rn_convert_param_1];
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rn.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @sub_rz_f16x2_f32x2_relu(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rz_f16x2_f32x2_relu(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_relu_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_relu_param_1];
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.relu.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
-define <2 x half> @sub_rz_f16x2_f32x2_distinct_subs(<2 x float> %a, <2 x float> %b, <2 x float> %c) {
-; CHECK-LABEL: sub_rz_f16x2_f32x2_distinct_subs(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<6>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_f16x2_f32x2_distinct_subs_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_f16x2_f32x2_distinct_subs_param_1];
-; CHECK-NEXT:    ld.param::func.b64 %rd3, [sub_rz_f16x2_f32x2_distinct_subs_param_2];
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd4, %rd1, %rd2;
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd5, %rd1, %rd3;
-; CHECK-NEXT:    mov.b64 {%r1, _}, %rd4;
-; CHECK-NEXT:    mov.b64 {_, %r2}, %rd5;
-; CHECK-NEXT:    cvt.rz.f16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %nc = fneg <2 x float> %c
-  %sum1 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %sum2 = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nc, i32 0)
-  %lo = extractelement <2 x float> %sum1, i32 0
-  %hi = extractelement <2 x float> %sum2, i32 1
-  %r = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x half> %r
-}
-
 ;
 ;  BF16x2
 ;
@@ -252,95 +89,3 @@ define <2 x bfloat> @sub_rz_bf16x2_f32x2_negated_minuend(<2 x float> %a, <2 x fl
   %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
   ret <2 x bfloat> %r
 }
-
-define <2 x bfloat> @sub_rz_bf16x2_f32x2_extra_use(<2 x float> %a, <2 x float> %b, ptr %p) {
-; CHECK-LABEL: sub_rz_bf16x2_f32x2_extra_use(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<2>;
-; CHECK-NEXT:    .reg .b64 %rd<5>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_extra_use_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_extra_use_param_1];
-; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    ld.param::func.b64 %rd4, [sub_rz_bf16x2_f32x2_extra_use_param_2];
-; CHECK-NEXT:    st.b64 [%rd4], %rd3;
-; CHECK-NEXT:    sub.rz.bf16x2.f32x2.f32x2 %r1, %rd1, %rd2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r1;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  store <2 x float> %sum, ptr %p
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x bfloat> %r
-}
-
-; invalid patterns
-
-define <2 x bfloat> @sub_rz_bf16x2_f32x2_swapped_halves(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rz_bf16x2_f32x2_swapped_halves(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_swapped_halves_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_swapped_halves_param_1];
-; CHECK-NEXT:    sub.rz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r1, %r2;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %lo, float %hi, i1 false)
-  ret <2 x bfloat> %r
-}
-
-define <2 x bfloat> @sub_rz_bf16x2_f32x2_ftz(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rz_bf16x2_f32x2_ftz(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rz_bf16x2_f32x2_ftz_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rz_bf16x2_f32x2_ftz_param_1];
-; CHECK-NEXT:    sub.rz.ftz.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.ftz.v2f32(<2 x float> %a, <2 x float> %nb, i32 0)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x bfloat> %r
-}
-
-define <2 x bfloat> @sub_rn_bf16x2_f32x2(<2 x float> %a, <2 x float> %b) {
-; CHECK-LABEL: sub_rn_bf16x2_f32x2(
-; CHECK:       {
-; CHECK-NEXT:    .reg .b32 %r<4>;
-; CHECK-NEXT:    .reg .b64 %rd<4>;
-; CHECK-EMPTY:
-; CHECK-NEXT:  // %bb.0:
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_rn_bf16x2_f32x2_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd2, [sub_rn_bf16x2_f32x2_param_1];
-; CHECK-NEXT:    sub.rn.f32x2 %rd3, %rd1, %rd2;
-; CHECK-NEXT:    mov.b64 {%r1, %r2}, %rd3;
-; CHECK-NEXT:    cvt.rz.bf16x2.f32 %r3, %r2, %r1;
-; CHECK-NEXT:    st.param::func.b32 [func_retval0], %r3;
-; CHECK-NEXT:    ret;
-  %nb = fneg <2 x float> %b
-  %sum = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %a, <2 x float> %nb, i32 1)
-  %lo = extractelement <2 x float> %sum, i32 0
-  %hi = extractelement <2 x float> %sum, i32 1
-  %r = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %hi, float %lo, i1 false)
-  ret <2 x bfloat> %r
-}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert-invalid.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert-invalid.ll
new file mode 100644
index 0000000000000..daaff74f5a0c0
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert-invalid.ll
@@ -0,0 +1,535 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | FileCheck --check-prefixes=CHECK-NOFTZ %s
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | FileCheck --check-prefixes=CHECK-FTZ %s
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 | %ptxas-verify -arch=sm_107f %}
+; RUN: %if ptxas-sm_107f && ptxas-isa-9.4 %{ llc < %s -mtriple=nvptx64 -mcpu=sm_107f -mattr=+ptx94 -denormal-fp-math=preserve-sign | %ptxas-verify -arch=sm_107f %}
+
+;
+; Cases where the mixed-precision instruction must not be selected.
+;
+
+;
+;  F16x2
+;
+
+; multiple uses of the extension
+
+define <2 x float> @sub_f32x2_f16x2_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %nb = fneg <2 x float> %b
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_f16x2_generic_lanes_multiuse(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_lanes_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_lanes_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_lanes_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_lanes_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_lanes_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_lanes_multiuse_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd1, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r1 = fsub <2 x float> %e, %b
+  %r2 = fsub <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @sub_f32x2_f16x2_generic_packed_multiuse(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_packed_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_f16x2_generic_packed_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.f16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_packed_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r3, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r4, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd2, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_packed_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_f16x2_generic_packed_multiuse_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.ftz.f16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_packed_multiuse_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd2, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x half> %a, %a
+  %e = fpext <2 x half> %x to <2 x float>
+  %r1 = fsub <2 x float> %e, %b
+  %r2 = fsub <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @sub_f32x2_f16x2_generic_both_lanes_extra_use(<2 x half> %a, <2 x float> %b, ptr %p, ptr %q) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_both_lanes_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs2;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd5, %rd4, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_both_lanes_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd5, %rd4, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x half> %a, i32 0
+  %a1 = extractelement <2 x half> %a, i32 1
+  %e0 = fpext half %a0 to float
+  %e1 = fpext half %a1 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  store float %e1, ptr %q
+  %r = fsub <2 x float> %v1, %b
+  ret <2 x float> %r
+}
+
+; unsupported forms
+
+define <2 x float> @sub_f32x2_f16x2_reversed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %ne = fneg <2 x float> %e
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %ne, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_f16x2_generic_reversed(<2 x half> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x half> %a to <2 x float>
+  %r = fsub <2 x float> %b, %e
+  ret <2 x float> %r
+}
+
+;
+;  BF16x2
+;
+
+; multiple uses of the extension
+
+define <2 x float> @sub_f32x2_bf16x2_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_extra_use_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_extra_use_param_2];
+; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  store <2 x float> %e, ptr %p
+  %nb = fneg <2 x float> %b
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_bf16x2_generic_lanes_multiuse(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_lanes_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_lanes_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_lanes_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd1, %rd2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd1, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_lanes_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_lanes_multiuse_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_lanes_multiuse_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd1, %rd2;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd1, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r1 = fsub <2 x float> %e, %b
+  %r2 = fsub <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @sub_f32x2_bf16x2_generic_packed_multiuse(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_packed_multiuse(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_bf16x2_generic_packed_multiuse_param_0];
+; CHECK-NOFTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_packed_multiuse_param_1];
+; CHECK-NOFTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r3, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r4, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd4, %rd2, %rd3;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_packed_multiuse(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<5>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.b32 %r1, [sub_f32x2_bf16x2_generic_packed_multiuse_param_0];
+; CHECK-FTZ-NEXT:    mul.rn.bf16x2 %r2, %r1, %r1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_packed_multiuse_param_1];
+; CHECK-FTZ-NEXT:    mov.b32 {%rs1, %rs2}, %r2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r3, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r4, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd2, {%r4, %r3};
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd2, %rd3;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
+; CHECK-FTZ-NEXT:    ret;
+  %x = fmul <2 x bfloat> %a, %a
+  %e = fpext <2 x bfloat> %x to <2 x float>
+  %r1 = fsub <2 x float> %e, %b
+  %r2 = fsub <2 x float> %e, %r1
+  ret <2 x float> %r2
+}
+
+define <2 x float> @sub_f32x2_bf16x2_generic_both_lanes_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p, ptr %q) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_both_lanes_extra_use(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs2;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd5, %rd4, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_both_lanes_extra_use(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<6>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_generic_both_lanes_extra_use_param_3];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd4, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    st.b32 [%rd3], %r2;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd5, %rd4, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd5;
+; CHECK-FTZ-NEXT:    ret;
+  %a0 = extractelement <2 x bfloat> %a, i32 0
+  %a1 = extractelement <2 x bfloat> %a, i32 1
+  %e0 = fpext bfloat %a0 to float
+  %e1 = fpext bfloat %a1 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  store float %e1, ptr %q
+  %r = fsub <2 x float> %v1, %b
+  ret <2 x float> %r
+}
+
+; unsupported forms
+
+define <2 x float> @sub_f32x2_bf16x2_reversed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %ne = fneg <2 x float> %e
+  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %ne, <2 x float> %b, i32 1)
+  ret <2 x float> %r
+}
+
+define <2 x float> @sub_f32x2_bf16x2_generic_reversed(<2 x bfloat> %a, <2 x float> %b) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_reversed(
+; CHECK-NOFTZ:       {
+; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-NOFTZ-EMPTY:
+; CHECK-NOFTZ-NEXT:  // %bb.0:
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_reversed_param_0];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
+; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_reversed_param_1];
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-NOFTZ-NEXT:    ret;
+;
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_reversed(
+; CHECK-FTZ:       {
+; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
+; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-EMPTY:
+; CHECK-FTZ-NEXT:  // %bb.0:
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_reversed_param_0];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
+; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_reversed_param_1];
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ret;
+  %e = fpext <2 x bfloat> %a to <2 x float>
+  %r = fsub <2 x float> %b, %e
+  ret <2 x float> %r
+}
diff --git a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll
index 9872325d3a302..a2f0df199e12d 100644
--- a/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll
+++ b/llvm/test/CodeGen/NVPTX/mixed-precision-sub-f32x2-upconvert.ll
@@ -10,78 +10,42 @@
 
 ; lanes
 
-define <2 x float> @sub_f32x2_f16x2_lanes(<2 x half> %a, <2 x float> %b) {
+define <2 x float> @sub_f32x2_f16x2_lanes(<2 x half> %a0, <2 x half> %a1, <2 x half> %a2, <2 x half> %a3, <2 x float> %b) {
 ; CHECK-LABEL: sub_f32x2_f16x2_lanes(
 ; CHECK:       {
-; CHECK-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b16 %rs<9>;
+; CHECK-NEXT:    .reg .b32 %r<5>;
 ; CHECK-NEXT:    .reg .b64 %rd<6>;
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  // %bb.0:
 ; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_lanes_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_lanes_param_1];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs3, %rs4}, [sub_f32x2_f16x2_lanes_param_1];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs5, %rs6}, [sub_f32x2_f16x2_lanes_param_2];
 ; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_lanes_param_4];
 ; CHECK-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd2, %r1, %rd1;
-; CHECK-NEXT:    sub.rz.f32x2.f16x2.f32x2 %rd3, %r1, %rd2;
-; CHECK-NEXT:    sub.rm.f32x2.f16x2.f32x2 %rd4, %r1, %rd3;
-; CHECK-NEXT:    sub.rp.f32x2.f16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs7, %rs8}, [sub_f32x2_f16x2_lanes_param_3];
+; CHECK-NEXT:    mov.b32 %r2, {%rs3, %rs4};
+; CHECK-NEXT:    sub.rz.f32x2.f16x2.f32x2 %rd3, %r2, %rd2;
+; CHECK-NEXT:    mov.b32 %r3, {%rs5, %rs6};
+; CHECK-NEXT:    sub.rm.f32x2.f16x2.f32x2 %rd4, %r3, %rd3;
+; CHECK-NEXT:    mov.b32 %r4, {%rs7, %rs8};
+; CHECK-NEXT:    sub.rp.f32x2.f16x2.f32x2 %rd5, %r4, %rd4;
 ; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
 ; CHECK-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  %nb = fneg <2 x float> %b
-  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
-  %n1 = fneg <2 x float> %r1
-  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n1, i32 0)
-  %n2 = fneg <2 x float> %r2
-  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n2, i32 3)
-  %n3 = fneg <2 x float> %r3
-  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n3, i32 2)
-  ret <2 x float> %r4
-}
-
-define <2 x float> @sub_f32x2_f16x2_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
-; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_extra_use(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_extra_use_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_extra_use_param_1];
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_extra_use_param_2];
-; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: sub_f32x2_f16x2_extra_use(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_extra_use_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_extra_use_param_1];
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_f16x2_extra_use_param_2];
-; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-FTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  store <2 x float> %e, ptr %p
-  %nb = fneg <2 x float> %b
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
-  ret <2 x float> %r
+  %e0 = fpext <2 x half> %a0 to <2 x float>
+  %n0 = fneg <2 x float> %b
+  %r0 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e0, <2 x float> %n0, i32 1)
+  %e1 = fpext <2 x half> %a1 to <2 x float>
+  %n1 = fneg <2 x float> %r0
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e1, <2 x float> %n1, i32 0)
+  %e2 = fpext <2 x half> %a2 to <2 x float>
+  %n2 = fneg <2 x float> %r1
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e2, <2 x float> %n2, i32 3)
+  %e3 = fpext <2 x half> %a3 to <2 x float>
+  %n3 = fneg <2 x float> %r2
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e3, <2 x float> %n3, i32 2)
+  ret <2 x float> %r3
 }
 
 ; packed
@@ -193,80 +157,51 @@ define <2 x float> @sub_f32x2_f16x2_generic_packed(<2 x half> %a, <2 x float> %b
   ret <2 x float> %r
 }
 
-; invalid patterns
+; generic fsub - partially shared extension
 
-define <2 x float> @sub_f32x2_f16x2_reversed(<2 x half> %a, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_reversed(
+define <2 x float> @sub_f32x2_f16x2_generic_lane_extra_use(<2 x half> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_lane_extra_use(
 ; CHECK-NOFTZ:       {
 ; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
 ; CHECK-NOFTZ-EMPTY:
 ; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_reversed_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_reversed_param_1];
-; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_lane_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_lane_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_lane_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r2, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.f16x2.f32x2 %rd3, %r2, %rd1;
 ; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
 ; CHECK-NOFTZ-NEXT:    ret;
 ;
-; CHECK-FTZ-LABEL: sub_f32x2_f16x2_reversed(
+; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_lane_extra_use(
 ; CHECK-FTZ:       {
 ; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_reversed_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_reversed_param_1];
-; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  %ne = fneg <2 x float> %e
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %ne, <2 x float> %b, i32 1)
-  ret <2 x float> %r
-}
-
-define <2 x float> @sub_f32x2_f16x2_generic_reversed(<2 x half> %a, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: sub_f32x2_f16x2_generic_reversed(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_reversed_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.f16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_reversed_param_1];
-; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: sub_f32x2_f16x2_generic_reversed(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
 ; CHECK-FTZ-EMPTY:
 ; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_reversed_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_reversed_param_1];
-; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_f16x2_generic_lane_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_f16x2_generic_lane_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_f16x2_generic_lane_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.f16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd3, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd3, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
 ; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x half> %a to <2 x float>
-  %r = fsub <2 x float> %b, %e
+  %a0 = extractelement <2 x half> %a, i32 0
+  %a1 = extractelement <2 x half> %a, i32 1
+  %e0 = fpext half %a0 to float
+  %e1 = fpext half %a1 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  %r = fsub <2 x float> %v1, %b
   ret <2 x float> %r
 }
 
@@ -276,78 +211,42 @@ define <2 x float> @sub_f32x2_f16x2_generic_reversed(<2 x half> %a, <2 x float>
 
 ; lanes
 
-define <2 x float> @sub_f32x2_bf16x2_lanes(<2 x bfloat> %a, <2 x float> %b) {
+define <2 x float> @sub_f32x2_bf16x2_lanes(<2 x bfloat> %a0, <2 x bfloat> %a1, <2 x bfloat> %a2, <2 x bfloat> %a3, <2 x float> %b) {
 ; CHECK-LABEL: sub_f32x2_bf16x2_lanes(
 ; CHECK:       {
-; CHECK-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NEXT:    .reg .b32 %r<2>;
+; CHECK-NEXT:    .reg .b16 %rs<9>;
+; CHECK-NEXT:    .reg .b32 %r<5>;
 ; CHECK-NEXT:    .reg .b64 %rd<6>;
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  // %bb.0:
 ; CHECK-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_lanes_param_0];
-; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_lanes_param_1];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs3, %rs4}, [sub_f32x2_bf16x2_lanes_param_1];
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs5, %rs6}, [sub_f32x2_bf16x2_lanes_param_2];
 ; CHECK-NEXT:    mov.b32 %r1, {%rs1, %rs2};
+; CHECK-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_lanes_param_4];
 ; CHECK-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd2, %r1, %rd1;
-; CHECK-NEXT:    sub.rz.f32x2.bf16x2.f32x2 %rd3, %r1, %rd2;
-; CHECK-NEXT:    sub.rm.f32x2.bf16x2.f32x2 %rd4, %r1, %rd3;
-; CHECK-NEXT:    sub.rp.f32x2.bf16x2.f32x2 %rd5, %r1, %rd4;
+; CHECK-NEXT:    ld.param::func.v2.b16 {%rs7, %rs8}, [sub_f32x2_bf16x2_lanes_param_3];
+; CHECK-NEXT:    mov.b32 %r2, {%rs3, %rs4};
+; CHECK-NEXT:    sub.rz.f32x2.bf16x2.f32x2 %rd3, %r2, %rd2;
+; CHECK-NEXT:    mov.b32 %r3, {%rs5, %rs6};
+; CHECK-NEXT:    sub.rm.f32x2.bf16x2.f32x2 %rd4, %r3, %rd3;
+; CHECK-NEXT:    mov.b32 %r4, {%rs7, %rs8};
+; CHECK-NEXT:    sub.rp.f32x2.bf16x2.f32x2 %rd5, %r4, %rd4;
 ; CHECK-NEXT:    st.param::func.b64 [func_retval0], %rd5;
 ; CHECK-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  %nb = fneg <2 x float> %b
-  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
-  %n1 = fneg <2 x float> %r1
-  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n1, i32 0)
-  %n2 = fneg <2 x float> %r2
-  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n2, i32 3)
-  %n3 = fneg <2 x float> %r3
-  %r4 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %n3, i32 2)
-  ret <2 x float> %r4
-}
-
-define <2 x float> @sub_f32x2_bf16x2_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
-; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_extra_use(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_extra_use_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_extra_use_param_1];
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_extra_use_param_2];
-; CHECK-NOFTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-NOFTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_extra_use(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<4>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_extra_use_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_extra_use_param_1];
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd3, [sub_f32x2_bf16x2_extra_use_param_2];
-; CHECK-FTZ-NEXT:    st.b64 [%rd3], %rd1;
-; CHECK-FTZ-NEXT:    mov.b32 %r3, {%rs1, %rs2};
-; CHECK-FTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd4, %r3, %rd2;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  store <2 x float> %e, ptr %p
-  %nb = fneg <2 x float> %b
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e, <2 x float> %nb, i32 1)
-  ret <2 x float> %r
+  %e0 = fpext <2 x bfloat> %a0 to <2 x float>
+  %n0 = fneg <2 x float> %b
+  %r0 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e0, <2 x float> %n0, i32 1)
+  %e1 = fpext <2 x bfloat> %a1 to <2 x float>
+  %n1 = fneg <2 x float> %r0
+  %r1 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e1, <2 x float> %n1, i32 0)
+  %e2 = fpext <2 x bfloat> %a2 to <2 x float>
+  %n2 = fneg <2 x float> %r1
+  %r2 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e2, <2 x float> %n2, i32 3)
+  %e3 = fpext <2 x bfloat> %a3 to <2 x float>
+  %n3 = fneg <2 x float> %r2
+  %r3 = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %e3, <2 x float> %n3, i32 2)
+  ret <2 x float> %r3
 }
 
 ; packed
@@ -446,79 +345,50 @@ define <2 x float> @sub_f32x2_bf16x2_generic_packed(<2 x bfloat> %a, <2 x float>
   ret <2 x float> %r
 }
 
-; invalid patterns
+; generic fsub - partially shared extension
 
-define <2 x float> @sub_f32x2_bf16x2_reversed(<2 x bfloat> %a, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_reversed(
+define <2 x float> @sub_f32x2_bf16x2_generic_lane_extra_use(<2 x bfloat> %a, <2 x float> %b, ptr %p) {
+; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_lane_extra_use(
 ; CHECK-NOFTZ:       {
 ; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
 ; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
 ; CHECK-NOFTZ-EMPTY:
 ; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_reversed_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_reversed_param_1];
-; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
+; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_lane_extra_use_param_0];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_lane_extra_use_param_1];
+; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_lane_extra_use_param_2];
+; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs1;
+; CHECK-NOFTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-NOFTZ-NEXT:    mov.b32 %r2, {%rs1, %rs2};
+; CHECK-NOFTZ-NEXT:    sub.rn.f32x2.bf16x2.f32x2 %rd3, %r2, %rd1;
 ; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
 ; CHECK-NOFTZ-NEXT:    ret;
 ;
-; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_reversed(
+; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_lane_extra_use(
 ; CHECK-FTZ:       {
 ; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
 ; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-FTZ-EMPTY:
-; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_reversed_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_reversed_param_1];
-; CHECK-FTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  %ne = fneg <2 x float> %e
-  %r = call <2 x float> @llvm.nvvm.fadd.v2f32(<2 x float> %ne, <2 x float> %b, i32 1)
-  ret <2 x float> %r
-}
-
-define <2 x float> @sub_f32x2_bf16x2_generic_reversed(<2 x bfloat> %a, <2 x float> %b) {
-; CHECK-NOFTZ-LABEL: sub_f32x2_bf16x2_generic_reversed(
-; CHECK-NOFTZ:       {
-; CHECK-NOFTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-NOFTZ-NEXT:    .reg .b64 %rd<4>;
-; CHECK-NOFTZ-EMPTY:
-; CHECK-NOFTZ-NEXT:  // %bb.0:
-; CHECK-NOFTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_reversed_param_0];
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r1, %rs2;
-; CHECK-NOFTZ-NEXT:    cvt.f32.bf16 %r2, %rs1;
-; CHECK-NOFTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-NOFTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_reversed_param_1];
-; CHECK-NOFTZ-NEXT:    sub.rn.f32x2 %rd3, %rd2, %rd1;
-; CHECK-NOFTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
-; CHECK-NOFTZ-NEXT:    ret;
-;
-; CHECK-FTZ-LABEL: sub_f32x2_bf16x2_generic_reversed(
-; CHECK-FTZ:       {
-; CHECK-FTZ-NEXT:    .reg .b16 %rs<3>;
-; CHECK-FTZ-NEXT:    .reg .b32 %r<3>;
-; CHECK-FTZ-NEXT:    .reg .b64 %rd<4>;
+; CHECK-FTZ-NEXT:    .reg .b64 %rd<5>;
 ; CHECK-FTZ-EMPTY:
 ; CHECK-FTZ-NEXT:  // %bb.0:
-; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_reversed_param_0];
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs2;
-; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs1;
-; CHECK-FTZ-NEXT:    mov.b64 %rd1, {%r2, %r1};
-; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_reversed_param_1];
-; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd3, %rd2, %rd1;
-; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd3;
+; CHECK-FTZ-NEXT:    ld.param::func.v2.b16 {%rs1, %rs2}, [sub_f32x2_bf16x2_generic_lane_extra_use_param_0];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd1, [sub_f32x2_bf16x2_generic_lane_extra_use_param_1];
+; CHECK-FTZ-NEXT:    ld.param::func.b64 %rd2, [sub_f32x2_bf16x2_generic_lane_extra_use_param_2];
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r1, %rs1;
+; CHECK-FTZ-NEXT:    cvt.ftz.f32.bf16 %r2, %rs2;
+; CHECK-FTZ-NEXT:    mov.b64 %rd3, {%r1, %r2};
+; CHECK-FTZ-NEXT:    st.b32 [%rd2], %r1;
+; CHECK-FTZ-NEXT:    sub.rn.ftz.f32x2 %rd4, %rd3, %rd1;
+; CHECK-FTZ-NEXT:    st.param::func.b64 [func_retval0], %rd4;
 ; CHECK-FTZ-NEXT:    ret;
-  %e = fpext <2 x bfloat> %a to <2 x float>
-  %r = fsub <2 x float> %b, %e
+  %a0 = extractelement <2 x bfloat> %a, i32 0
+  %a1 = extractelement <2 x bfloat> %a, i32 1
+  %e0 = fpext bfloat %a0 to float
+  %e1 = fpext bfloat %a1 to float
+  %v0 = insertelement <2 x float> poison, float %e0, i32 0
+  %v1 = insertelement <2 x float> %v0, float %e1, i32 1
+  store float %e0, ptr %p
+  %r = fsub <2 x float> %v1, %b
   ret <2 x float> %r
 }

>From 067fd165c906cc9e95e7cda038147d48f2df6da4 Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Thu, 10 Sep 2026 05:45:37 +0000
Subject: [PATCH 7/7] address comments

---
 llvm/lib/Target/NVPTX/NVPTXIntrinsics.td | 114 +++++++++++++----------
 1 file changed, 64 insertions(+), 50 deletions(-)

diff --git a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
index 424f4a80be890..4ef4017c64422 100644
--- a/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
+++ b/llvm/lib/Target/NVPTX/NVPTXIntrinsics.td
@@ -2494,9 +2494,9 @@ foreach ftz = ["", "ftz"] in {
 def INT_NVVM_ADD_D :
   F_MATH_2_RNDOP_TY<"add.${rnd}.f64", F64RT, int_nvvm_fadd>;
 
-// mixed precision
+// Mixed Precision
 
-// match only when the fold removes at least one explicit conversion, and the
+// Match only when the fold removes at least one explicit conversion, and the
 // extended vector is not shared.
 class FoldableFPExtendV2F32<dag ops, dag frag>
   : PatFrag<ops, frag, [{
@@ -2504,7 +2504,7 @@ class FoldableFPExtendV2F32<dag ops, dag frag>
                                 N->getOperand(1)->hasOneUse());
     }]>;
 
-// FP_EXTEND to v2f32 is scalarized before isel, leaving one of two shapes
+// FP_EXTEND to v2f32 is scalarized before ISEL, leaving one of two patterns
 // behind depending on whether the source lanes share a register.
 def fpextend_v2f32_from_packed
   : FoldableFPExtendV2F32<(ops node:$a),
@@ -2518,7 +2518,7 @@ def fpextend_v2f32_from_lanes
                             (f32 (fpextend node:$a0)),
                             (f32 (fpextend node:$a1))))>;
 
-// fuse downconverting add/sub only when the result of the add/sub is solely
+// Fuse downconverting add/sub only when the result of the add/sub is solely
 // used for downconverting.
 def extractelt_only_for_cvt
   : PatFrag<(ops node:$vec, node:$lane), (extractelt node:$vec, node:$lane),
@@ -2542,17 +2542,20 @@ foreach rnd = FPRoundingModes in {
     foreach t = [F16X2RT, BF16X2RT] in {
       def INT_NVVM_MIXED_ADD_ # rnd # _f32x2_ # t.PtxType :
         BasicNVPTXInst<(outs B64:$dst), (ins B32:$a, B64:$b),
-          "add." # rnd # ".f32x2." # t.PtxType # ".f32x2",
-          [(set v2f32:$dst,
-            (int_nvvm_fadd (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b,
-                          rnd_imm))]>;
+          "add." # rnd # ".f32x2." # t.PtxType # ".f32x2">;
+
+      defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_ADD_" # rnd #
+                                       "_f32x2_" # t.PtxType);
+
+      def : Pat<(v2f32 (int_nvvm_fadd (fpextend_v2f32_from_packed t.Ty:$a),
+                                      v2f32:$b, rnd_imm)),
+                (Inst $a, $b)>;
 
       def : Pat<(v2f32 (int_nvvm_fadd
                         (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
                                                     t.Ty.ElementType:$a1),
                         v2f32:$b, rnd_imm)),
-                (!cast<Instruction>("INT_NVVM_MIXED_ADD_" # rnd # "_f32x2_" #
-                                    t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
+                (Inst (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
     }
   }
 }
@@ -2579,27 +2582,31 @@ let Predicates = [hasRubinFamilySupport, doNoF32FTZ] in
   }
 
 
-// mixed precision - downconverting
+// Mixed precision - Downconverting
 
 // f16x2 - rz rounding mode, only with ftz
 // bf16x2 - rz rounding mode, without ftz
-foreach t = [F16X2RT, BF16X2RT] in {
-  defvar ftz = !if(!eq(t, F16X2RT), ".ftz", "");
-  defvar AddOp = !cast<Intrinsic>("int_nvvm_fadd" # !if(!eq(ftz, ""), "", "_ftz"));
-  defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
-
-  def INT_NVVM_MIXED_ADD_rz_ # t.PtxType # _f32x2 :
-    BasicNVPTXInst<(outs B32:$dst), (ins B64:$a, B64:$b),
-      "add.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2",
-      // The cvt packs its first argument into the high half of the result.
-      [(set t.Ty:$dst,
-        (CvtOp (extractelt_only_for_cvt
-                 (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
-               (extractelt_only_for_cvt
-                 (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
-               /*pzo=*/0))]>,
-    Requires<[hasRubinFamilySupport]>;
-}
+let Predicates = [hasRubinFamilySupport] in
+  foreach t = [F16X2RT, BF16X2RT] in {
+    defvar ftz = !if(!eq(t, F16X2RT), ".ftz", "");
+    defvar AddOp = !cast<Intrinsic>("int_nvvm_fadd" # !if(!eq(ftz, ""), "", "_ftz"));
+    defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
+
+    def INT_NVVM_MIXED_ADD_rz_ # t.PtxType # _f32x2 :
+      BasicNVPTXInst<(outs B32:$dst), (ins B64:$a, B64:$b),
+        "add.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2">;
+
+    defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_ADD_rz_" # t.PtxType #
+                                     "_f32x2");
+
+    // The cvt packs its first argument into the upper half of the result.
+    def : Pat<(CvtOp (extractelt_only_for_cvt
+                       (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 1),
+                     (extractelt_only_for_cvt
+                       (AddOp v2f32:$a, v2f32:$b, fp_rnd_rz_imm), 0),
+                     /*pzo=*/0),
+              (Inst $a, $b)>;
+  }
 
 //
 // Sub
@@ -2661,7 +2668,7 @@ def INT_NVVM_SUB_D :
     (ins FPRoundingMode:$rnd), "sub.${rnd}.f64",
     [(set f64:$dst, (int_nvvm_fadd f64:$a, (f64 (fneg f64:$b)), timm:$rnd))]>;
 
-// mixed precision
+// Mixed Precision
 
 foreach rnd = FPRoundingModes in {
   defvar rnd_imm = !cast<TImmLeaf>("fp_rnd_" # rnd # "_imm");
@@ -2684,16 +2691,19 @@ foreach rnd = FPRoundingModes in {
 
       def INT_NVVM_MIXED_SUB_ # rnd # _f32x2_ # t.PtxType :
         BasicNVPTXInst<(outs B64:$dst), (ins B32:$a, B64:$b),
-          "sub." # rnd # ".f32x2." # t.PtxType # ".f32x2",
-          [(set v2f32:$dst,
-            (SubOp (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b))]>;
+          "sub." # rnd # ".f32x2." # t.PtxType # ".f32x2">;
+
+      defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_SUB_" # rnd #
+                                       "_f32x2_" # t.PtxType);
+
+      def : Pat<(v2f32 (SubOp (fpextend_v2f32_from_packed t.Ty:$a), v2f32:$b)),
+                (Inst $a, $b)>;
 
       def : Pat<(v2f32 (SubOp
                         (fpextend_v2f32_from_lanes t.Ty.ElementType:$a0,
                                                     t.Ty.ElementType:$a1),
                         v2f32:$b)),
-                (!cast<Instruction>("INT_NVVM_MIXED_SUB_" # rnd # "_f32x2_" #
-                                    t.PtxType) (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
+                (Inst (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
     }
   }
 }
@@ -2719,25 +2729,29 @@ let Predicates = [hasRubinFamilySupport, doNoF32FTZ] in
               (Inst (t.Ty (V2I16toI32 $a0, $a1)), $b)>;
   }
 
-// mixed precision - downconverting
+// Mixed precision - Downconverting
 
 // f16x2 - rz rounding mode, only with ftz
 // bf16x2 - rz rounding mode, without ftz
-foreach t = [F16X2RT, BF16X2RT] in {
-  defvar ftz = !if(!eq(t, F16X2RT), ".ftz", "");
-  defvar SubOp = !cast<SDNode>("sub_rz" # !if(!eq(ftz, ""), "", "_ftz"));
-  defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
-
-  def INT_NVVM_MIXED_SUB_rz_ # t.PtxType # _f32x2 :
-    BasicNVPTXInst<(outs B32:$dst), (ins B64:$a, B64:$b),
-      "sub.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2",
-      // The cvt packs its first argument into the high half of the result.
-      [(set t.Ty:$dst,
-        (CvtOp (extractelt_only_for_cvt (SubOp v2f32:$a, v2f32:$b), 1),
-               (extractelt_only_for_cvt (SubOp v2f32:$a, v2f32:$b), 0),
-               /*pzo=*/0))]>,
-    Requires<[hasRubinFamilySupport]>;
-}
+let Predicates = [hasRubinFamilySupport] in
+  foreach t = [F16X2RT, BF16X2RT] in {
+    defvar ftz = !if(!eq(t, F16X2RT), ".ftz", "");
+    defvar SubOp = !cast<SDNode>("sub_rz" # !if(!eq(ftz, ""), "", "_ftz"));
+    defvar CvtOp = !cast<Intrinsic>("int_nvvm_ff2" # t.PtxType # "_rz");
+
+    def INT_NVVM_MIXED_SUB_rz_ # t.PtxType # _f32x2 :
+      BasicNVPTXInst<(outs B32:$dst), (ins B64:$a, B64:$b),
+        "sub.rz" # ftz # "." # t.PtxType # ".f32x2.f32x2">;
+
+    defvar Inst = !cast<Instruction>("INT_NVVM_MIXED_SUB_rz_" # t.PtxType #
+                                     "_f32x2");
+
+    // The cvt packs its first argument into the upper half of the result.
+    def : Pat<(CvtOp (extractelt_only_for_cvt (SubOp v2f32:$a, v2f32:$b), 1),
+                     (extractelt_only_for_cvt (SubOp v2f32:$a, v2f32:$b), 0),
+                     /*pzo=*/0),
+              (Inst $a, $b)>;
+  }
 
 //
 // BFIND



More information about the llvm-commits mailing list