[llvm] [AMDGPU] Add OMOD folding for TRANS bfloat16 instructions (PR #208595)

Changpeng Fang via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 9 17:51:13 PDT 2026


https://github.com/changpeng updated https://github.com/llvm/llvm-project/pull/208595

>From 57142918905ed8b3fedf1e5af9ddde8aa2f53562 Mon Sep 17 00:00:00 2001
From: Changpeng Fang <changpeng.fang at amd.com>
Date: Thu, 9 Jul 2026 17:23:16 -0700
Subject: [PATCH] [AMDGPU] Add OMOD folding for TRANS bfloat16 instructions

Enable output modifier (OMOD) folding optimization for transcendental
bfloat16 instructions (cos, exp, log, sin, sqrt, rcp, rsq, tanh).

This patch extends SIFoldOperands to recognize and fold multiplication
or division patterns into the OMOD field of TRANS instructions, allowing
operations like `cos(x) * 2.0` to be encoded as `cos(x) mul:2`.

Key changes:
- Add OMOD folding for V_FMA_MIXLO_BF16 and V_FMA_MIX_BF16_t16 when one
  operand is a constant (2.0, 4.0, 0.5) and the other is zero (effectively
  a multiplication)
- Add folding for V_CVT_PK_BF16_F32_e64 when it follows a specific FMA
  pattern that represents a *2 operation
- Handle REG_SEQUENCE to look through packing operations commonly used
  with BF16 results

The optimization requires denormal mode preservesign and no FP exceptions.
---
 llvm/lib/Target/AMDGPU/SIFoldOperands.cpp   | 130 ++++++++++++++
 llvm/test/CodeGen/AMDGPU/trans-bf16-omod.ll | 185 ++++++++++++++++++++
 2 files changed, 315 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/trans-bf16-omod.ll

diff --git a/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp b/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
index ee20f138f245f..1bc922cd4e865 100644
--- a/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
@@ -2358,6 +2358,117 @@ SIFoldOperandsImpl::isOMod(const MachineInstr &MI) const {
 
     return std::pair(nullptr, SIOutMods::NONE);
   }
+  case AMDGPU::V_FMA_MIXLO_BF16:
+  case AMDGPU::V_FMA_MIX_BF16_t16: {
+    if (MFI->getMode().FP32Denormals.Output != DenormalMode::PreserveSign ||
+        MI.mayRaiseFPException())
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    const MachineOperand *Src0 = TII->getNamedOperand(MI, AMDGPU::OpName::src0);
+    const MachineOperand *Src1 = TII->getNamedOperand(MI, AMDGPU::OpName::src1);
+    const MachineOperand *Src2 = TII->getNamedOperand(MI, AMDGPU::OpName::src2);
+
+    if (!Src2 || !Src2->isImm() || Src2->getImm() != 0)
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    // op_sel bits are in src*_modifiers but don't affect folding
+    const MachineOperand *Src0Mods =
+        TII->getNamedOperand(MI, AMDGPU::OpName::src0_modifiers);
+    const MachineOperand *Src1Mods =
+        TII->getNamedOperand(MI, AMDGPU::OpName::src1_modifiers);
+    if ((Src0Mods &&
+         (Src0Mods->getImm() & (SISrcMods::NEG | SISrcMods::ABS))) ||
+        (Src1Mods &&
+         (Src1Mods->getImm() & (SISrcMods::NEG | SISrcMods::ABS))) ||
+        TII->hasModifiersSet(MI, AMDGPU::OpName::omod) ||
+        TII->hasModifiersSet(MI, AMDGPU::OpName::clamp))
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    if (!Src0->isReg() || !Src1->isReg())
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    const MachineOperand *RegOp = nullptr;
+    int64_t ImmValue = 0;
+
+    const MachineInstr *Src1Def = MRI->getVRegDef(Src1->getReg());
+    if (Src1Def && Src1Def->isMoveImmediate() &&
+        Src1Def->getOperand(1).isImm()) {
+      ImmValue = Src1Def->getOperand(1).getImm();
+      RegOp = Src0;
+    } else {
+      const MachineInstr *Src0Def = MRI->getVRegDef(Src0->getReg());
+      if (Src0Def && Src0Def->isMoveImmediate() &&
+          Src0Def->getOperand(1).isImm()) {
+        ImmValue = Src0Def->getOperand(1).getImm();
+        RegOp = Src1;
+      } else {
+        return std::pair(nullptr, SIOutMods::NONE);
+      }
+    }
+
+    int OMod = getOModValue(AMDGPU::V_MUL_F32_e64, ImmValue);
+    if (OMod == SIOutMods::NONE)
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    return std::pair(RegOp, OMod);
+  }
+  case AMDGPU::V_CVT_PK_BF16_F32_e64: {
+    // Fold cvt_pk_bf16_f32(fma_mix_f32_bf16(x, 1.0, x)) -> x with MUL2 omod
+    if (MFI->getMode().FP32Denormals.Output != DenormalMode::PreserveSign ||
+        MI.mayRaiseFPException())
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    if (TII->hasModifiersSet(MI, AMDGPU::OpName::omod) ||
+        TII->hasModifiersSet(MI, AMDGPU::OpName::clamp))
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    const MachineOperand *Src0 = TII->getNamedOperand(MI, AMDGPU::OpName::src0);
+    if (!Src0 || !Src0->isReg())
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    MachineInstr *FmaDef = MRI->getVRegDef(Src0->getReg());
+    if (!FmaDef || FmaDef->getOpcode() != AMDGPU::V_FMA_MIX_F32_BF16)
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    if (!MRI->hasOneNonDBGUse(Src0->getReg()))
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    const MachineOperand *FmaSrc0 =
+        TII->getNamedOperand(*FmaDef, AMDGPU::OpName::src0);
+    const MachineOperand *FmaSrc1 =
+        TII->getNamedOperand(*FmaDef, AMDGPU::OpName::src1);
+    const MachineOperand *FmaSrc2 =
+        TII->getNamedOperand(*FmaDef, AMDGPU::OpName::src2);
+
+    if (!FmaSrc0 || !FmaSrc1 || !FmaSrc2)
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    // FmaSrc1 == 1.0?
+    if (!FmaSrc1->isImm() || FmaSrc1->getImm() != 0x3f80)
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    if (!FmaSrc0->isReg() || !FmaSrc2->isReg() ||
+        FmaSrc0->getReg() != FmaSrc2->getReg())
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    const MachineOperand *Src0Mods =
+        TII->getNamedOperand(*FmaDef, AMDGPU::OpName::src0_modifiers);
+    const MachineOperand *Src1Mods =
+        TII->getNamedOperand(*FmaDef, AMDGPU::OpName::src1_modifiers);
+    const MachineOperand *Src2Mods =
+        TII->getNamedOperand(*FmaDef, AMDGPU::OpName::src2_modifiers);
+    if ((Src0Mods &&
+         (Src0Mods->getImm() & (SISrcMods::NEG | SISrcMods::ABS))) ||
+        (Src1Mods &&
+         (Src1Mods->getImm() & (SISrcMods::NEG | SISrcMods::ABS))) ||
+        (Src2Mods &&
+         (Src2Mods->getImm() & (SISrcMods::NEG | SISrcMods::ABS))) ||
+        TII->hasModifiersSet(*FmaDef, AMDGPU::OpName::omod) ||
+        TII->hasModifiersSet(*FmaDef, AMDGPU::OpName::clamp))
+      return std::pair(nullptr, SIOutMods::NONE);
+
+    return std::pair(FmaSrc0, SIOutMods::MUL2);
+  }
   default:
     return std::pair(nullptr, SIOutMods::NONE);
   }
@@ -2374,6 +2485,25 @@ bool SIFoldOperandsImpl::tryFoldOMod(MachineInstr &MI) {
     return false;
 
   MachineInstr *Def = MRI->getVRegDef(RegOp->getReg());
+
+  // Look through REG_SEQUENCE to find the actual instruction.
+  // For BF16 operations, the result is often packed via REG_SEQUENCE with
+  // lo16 subreg for the actual value.
+  if (Def->isRegSequence()) {
+    if (Def->getNumOperands() < 2 || !Def->getOperand(1).isReg())
+      return false;
+
+    Register SrcReg = Def->getOperand(1).getReg();
+    // Only fold if this is the only use of the source register, otherwise
+    // we would need to update all uses.
+    if (!MRI->hasOneNonDBGUse(SrcReg))
+      return false;
+
+    Def = MRI->getVRegDef(SrcReg);
+    if (!Def)
+      return false;
+  }
+
   MachineOperand *DefOMod = TII->getNamedOperand(*Def, AMDGPU::OpName::omod);
   if (!DefOMod || DefOMod->getImm() != SIOutMods::NONE)
     return false;
diff --git a/llvm/test/CodeGen/AMDGPU/trans-bf16-omod.ll b/llvm/test/CodeGen/AMDGPU/trans-bf16-omod.ll
new file mode 100644
index 0000000000000..aa71a4c738260
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/trans-bf16-omod.ll
@@ -0,0 +1,185 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1250 -mattr=-real-true16 < %s | FileCheck -check-prefix=SDAG-FAKE16 %s
+; xUN: llc -global-isel=1 -mtriple=amdgcn -mcpu=gfx1250 -mattr=-real-true16 < %s | FileCheck -check-prefix=GISEL-FAKE16 %s
+; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1250 -mattr=+real-true16 < %s | FileCheck -check-prefix=SDAG-REAL16 %s
+; xUN: llc -global-isel=1 -mtriple=amdgcn -mcpu=gfx1250 -mattr=+real-true16 < %s | FileCheck -check-prefix=GISEL-REAL16 %s
+
+; FIXME: GlobalISel does not work with bf16
+
+define amdgpu_ps void @v_cos_bf16_mul2(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_cos_bf16_mul2:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_cos_bf16_e64 v0, v0 mul:2
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_cos_bf16_mul2:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_cos_bf16_e64 v0.l, v0.l mul:2
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %cos = call bfloat @llvm.amdgcn.cos.bf16(bfloat %in)
+  %mul2 = fmul nsz bfloat %cos, 2.0
+  store bfloat %mul2, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_exp_bf16_mul4(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_exp_bf16_mul4:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_exp_bf16_e64 v0, v0 mul:4
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_exp_bf16_mul4:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_exp_bf16_e64 v0.l, v0.l mul:4
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %exp = call bfloat @llvm.amdgcn.exp2.bf16(bfloat %in)
+  %mul4 = fmul nsz bfloat %exp, 4.0
+  store bfloat %mul4, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_log_bf16_div2(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_log_bf16_div2:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_log_bf16_e64 v0, v0 div:2
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_log_bf16_div2:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_log_bf16_e64 v0.l, v0.l div:2
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %log = call bfloat @llvm.amdgcn.log.bf16(bfloat %in)
+  %div2 = fmul nsz bfloat %log, 0.5
+  store bfloat %div2, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_rcp_bf16_mul2(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_rcp_bf16_mul2:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_rcp_bf16_e64 v0, v0 mul:2
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_rcp_bf16_mul2:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_rcp_bf16_e64 v0.l, v0.l mul:2
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %rcp = call bfloat @llvm.amdgcn.rcp.bf16(bfloat %in)
+  %mul2 = fmul nsz bfloat %rcp, 2.0
+  store bfloat %mul2, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_rsq_bf16_mul4(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_rsq_bf16_mul4:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_rsq_bf16_e64 v0, v0 mul:4
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_rsq_bf16_mul4:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_rsq_bf16_e64 v0.l, v0.l mul:4
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %rsq = call bfloat @llvm.amdgcn.rsq.bf16(bfloat %in)
+  %mul4 = fmul nsz bfloat %rsq, 4.0
+  store bfloat %mul4, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_sin_bf16_div2(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_sin_bf16_div2:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_sin_bf16_e64 v0, v0 div:2
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_sin_bf16_div2:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_sin_bf16_e64 v0.l, v0.l div:2
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %sin = call bfloat @llvm.amdgcn.sin.bf16(bfloat %in)
+  %div2 = fmul nsz bfloat %sin, 0.5
+  store bfloat %div2, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_sqrt_bf16_mul2(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_sqrt_bf16_mul2:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_sqrt_bf16_e64 v0, v0 mul:2
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_sqrt_bf16_mul2:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_sqrt_bf16_e64 v0.l, v0.l mul:2
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %sqrt = call bfloat @llvm.amdgcn.sqrt.bf16(bfloat %in)
+  %mul2 = fmul nsz bfloat %sqrt, 2.0
+  store bfloat %mul2, ptr addrspace(1) %out
+  ret void
+}
+
+define amdgpu_ps void @v_tanh_bf16_mul4(bfloat %in, ptr addrspace(1) %out) #0 {
+; SDAG-FAKE16-LABEL: v_tanh_bf16_mul4:
+; SDAG-FAKE16:       ; %bb.0:
+; SDAG-FAKE16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-FAKE16-NEXT:    v_tanh_bf16_e64 v0, v0 mul:4
+; SDAG-FAKE16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-FAKE16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-FAKE16-NEXT:    s_endpgm
+;
+; SDAG-REAL16-LABEL: v_tanh_bf16_mul4:
+; SDAG-REAL16:       ; %bb.0:
+; SDAG-REAL16-NEXT:    s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 1 ; msbs: dst=0 src0=0 src1=0 src2=0
+; SDAG-REAL16-NEXT:    v_tanh_bf16_e64 v0.l, v0.l mul:4
+; SDAG-REAL16-NEXT:    v_dual_mov_b32 v3, v2 :: v_dual_mov_b32 v2, v1
+; SDAG-REAL16-NEXT:    global_store_b16 v[2:3], v0, off
+; SDAG-REAL16-NEXT:    s_endpgm
+  %tanh = call bfloat @llvm.amdgcn.tanh.bf16(bfloat %in)
+  %mul4 = fmul nsz bfloat %tanh, 4.0
+  store bfloat %mul4, ptr addrspace(1) %out
+  ret void
+}
+
+attributes #0 = { nounwind denormal_fpenv(preservesign) }



More information about the llvm-commits mailing list