[llvm] [AMDGPU][GlobalISel] Improve combining on v_ldexp (PR #190236)

Jasmine Tang via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 22 12:57:32 PDT 2026


https://github.com/badumbatish updated https://github.com/llvm/llvm-project/pull/190236

>From ffa5746cbdc6ffe1f99790324f78c0989c5b3176 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Mon, 13 Apr 2026 16:58:37 -0700
Subject: [PATCH 1/5] [AMDGPU] Precommit test for fmul+select to fldexp combine
 on SALU float targets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add test for divergent fmul with power-of-2 select constants on GFX12
(hasSALUFloat). This exercises the fmul+select→fldexp combine path.
---
 .../AMDGPU/pseudo-scalar-transcendental.ll    | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll b/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
index f001b26030896..8b6eea149b927 100644
--- a/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
+++ b/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
@@ -447,3 +447,26 @@ define amdgpu_cs half @fdiv_f16_i16(half inreg %a, i16 inreg %b) {
   %result = fdiv afn half %a, %uint
   ret half %result
 }
+
+; Test that the fmul+select -> fldexp combine works for divergent values.
+define amdgpu_cs float @fmul_select_pow2_divergent_f32(float %x, i1 %cond) {
+; GFX12-SDAG-LABEL: fmul_select_pow2_divergent_f32:
+; GFX12-SDAG:       ; %bb.0:
+; GFX12-SDAG-NEXT:    v_and_b32_e32 v1, 1, v1
+; GFX12-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX12-SDAG-NEXT:    v_cmp_eq_u32_e32 vcc_lo, 1, v1
+; GFX12-SDAG-NEXT:    v_cndmask_b32_e64 v1, 0, 8, vcc_lo
+; GFX12-SDAG-NEXT:    v_ldexp_f32 v0, v0, v1
+; GFX12-SDAG-NEXT:    ; return to shader part epilog
+;
+; GFX12-GISEL-LABEL: fmul_select_pow2_divergent_f32:
+; GFX12-GISEL:       ; %bb.0:
+; GFX12-GISEL-NEXT:    v_and_b32_e32 v1, 1, v1
+; GFX12-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX12-GISEL-NEXT:    v_lshlrev_b32_e32 v1, 3, v1
+; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, v0, v1
+; GFX12-GISEL-NEXT:    ; return to shader part epilog
+  %sel = select i1 %cond, float 256.0, float 1.0
+  %result = fmul float %x, %sel
+  ret float %result
+}

>From 2b552e0b560a893d634c3cb7fa4506d874248830 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Mon, 13 Apr 2026 21:04:45 -0700
Subject: [PATCH 2/5] [AMDGPU] Add fmul select to fldexp selection to regbank
 combiner

Sharing the code between the post legalizer combiner and the regbank
combiner by refactoring out the combine function but gating them behind
an additional check on SALU and VGPR respectively.
---
 llvm/lib/Target/AMDGPU/AMDGPUCombine.td       |  11 +-
 .../Target/AMDGPU/AMDGPUCombinerHelper.cpp    |  30 +++-
 llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h |   7 +
 .../Target/AMDGPU/AMDGPURegBankCombiner.cpp   |  17 ++
 .../AMDGPU/pseudo-scalar-transcendental.ll    | 154 +++++++-----------
 5 files changed, 114 insertions(+), 105 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombine.td b/llvm/lib/Target/AMDGPU/AMDGPUCombine.td
index 7f00eadbf3f3f..164aff23514c6 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombine.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombine.td
@@ -232,9 +232,18 @@ def AMDGPUPostLegalizerCombiner: GICombiner<
   let CombineAllMethodName = "tryCombineAllImpl";
 }
 
+// Like combine_fmul_with_select_to_fldexp but with VGPR detection for RegBankCombiner.
+def combine_fmul_select_to_fldexp_regbank : GICombineRule<
+  (defs root:$root, build_fn_matchinfo:$matchinfo),
+  (match  (G_FMUL $dst, $x, $select):$root,
+          (G_SELECT $select, $y, $A, $B):$sel,
+          [{ return matchFmulSelectToFldexpVgpr(*${root}, *${sel}, ${matchinfo}); }]),
+  (apply  [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
 def AMDGPURegBankCombiner : GICombiner<
   "AMDGPURegBankCombinerImpl",
-  [unmerge_merge, unmerge_cst, unmerge_undef,
+  [combine_fmul_select_to_fldexp_regbank,
+   unmerge_merge, unmerge_cst, unmerge_undef,
    zext_trunc_fold, int_minmax_to_med3, ptr_add_immed_chain,
    fp_minmax_to_clamp, fp_minmax_to_med3, fmed3_intrinsic_to_clamp,
    identity_combines, redundant_and, constant_fold_cast_op,
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
index 1a158b335321d..82da8a2aee7a9 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
@@ -452,9 +452,10 @@ void AMDGPUCombinerHelper::applyExpandPromotedF16FMed3(MachineInstr &MI,
   MI.eraseFromParent();
 }
 
-bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
+bool llvm::matchFmulWithSelectToFldexpImpl(
     MachineInstr &MI, MachineInstr &Sel,
-    std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+    std::function<void(MachineIRBuilder &)> &MatchInfo,
+    const MachineRegisterInfo &MRI, const SIInstrInfo &TII) {
   assert(MI.getOpcode() == TargetOpcode::G_FMUL);
   assert(Sel.getOpcode() == TargetOpcode::G_SELECT);
   assert(MI.getOperand(2).getReg() == Sel.getOperand(0).getReg());
@@ -470,8 +471,11 @@ bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
     return false;
 
   Register SelectCondReg = Sel.getOperand(1).getReg();
-  MachineInstr *SelectTrue = MRI.getVRegDef(Sel.getOperand(2).getReg());
-  MachineInstr *SelectFalse = MRI.getVRegDef(Sel.getOperand(3).getReg());
+  Register SelectTrueReg = Sel.getOperand(2).getReg();
+  Register SelectFalseReg = Sel.getOperand(3).getReg();
+  // Look through copies (needed in RegBankCombiner where regbank copies exist).
+  MachineInstr *SelectTrue = getDefIgnoringCopies(SelectTrueReg, MRI);
+  MachineInstr *SelectFalse = getDefIgnoringCopies(SelectFalseReg, MRI);
 
   const auto SelectTrueVal =
       isConstantOrConstantSplatVectorFP(*SelectTrue, MRI);
@@ -498,7 +502,7 @@ bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
   if (SelectFalseLog2Val == INT_MIN)
     return false;
 
-  MatchInfo = [=, &MI](MachineIRBuilder &Builder) {
+  MatchInfo = [=, &MI, &MRI](MachineIRBuilder &Builder) {
     LLT IntDestTy = DestTy.changeElementType(LLT::scalar(32));
     auto NewSel = Builder.buildSelect(
         IntDestTy, SelectCondReg,
@@ -518,6 +522,22 @@ bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
   return true;
 }
 
+bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
+    MachineInstr &MI, MachineInstr &Sel,
+    std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+  Register Dst = MI.getOperand(0).getReg();
+  LLT ScalarDestTy = MRI.getType(Dst).getScalarType();
+
+  // fldexp has no SALU form. On targets with SALU float, defer this combine
+  // to the RegBankCombiner where register banks are known and we can limit it
+  // to VGPR (divergent) values only.
+  if (STI.hasSALUFloatInsts() &&
+      (ScalarDestTy == LLT::scalar(32) || ScalarDestTy == LLT::scalar(16)))
+    return false;
+
+  return matchFmulWithSelectToFldexpImpl(MI, Sel, MatchInfo, MRI, TII);
+}
+
 bool AMDGPUCombinerHelper::matchConstantIs32BitMask(Register Reg) const {
   auto Res = getIConstantVRegValWithLookThrough(Reg, MRI);
   if (!Res)
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h
index 243474a57cfab..73a53ef15834c 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h
@@ -47,6 +47,13 @@ class AMDGPUCombinerHelper : public CombinerHelper {
   bool matchConstantIs32BitMask(Register Reg) const;
 };
 
+// Shared helper used by both
+// PostLegalizerCombiner and RegBankCombiner 
+bool matchFmulWithSelectToFldexpImpl(
+    MachineInstr &MI, MachineInstr &Sel,
+    std::function<void(MachineIRBuilder &)> &MatchInfo,
+    const MachineRegisterInfo &MRI, const SIInstrInfo &TII);
+
 } // namespace llvm
 
 #endif // LLVM_LIB_TARGET_AMDGPU_AMDGPUCOMBINERHELPER_H
diff --git a/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp b/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp
index 0e1d4075ed92e..cddfccfadd7e3 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPURegBankCombiner.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "AMDGPU.h"
+#include "AMDGPUCombinerHelper.h"
 #include "AMDGPULegalizerInfo.h"
 #include "AMDGPURegisterBankInfo.h"
 #include "GCNSubtarget.h"
@@ -89,6 +90,10 @@ class AMDGPURegBankCombinerImpl : public Combiner {
 
   void applyCanonicalizeZextShiftAmt(MachineInstr &MI, MachineInstr &Ext) const;
 
+  bool matchFmulSelectToFldexpVgpr(
+      MachineInstr &MI, MachineInstr &Sel,
+      std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
   bool combineD16Load(MachineInstr &MI) const;
   bool applyD16Load(unsigned D16Opc, MachineInstr &DstMI,
                     MachineInstr *SmallLoad, Register ToOverwriteD16) const;
@@ -150,6 +155,18 @@ Register AMDGPURegBankCombinerImpl::getAsVgpr(Register Reg) const {
   return VgprReg;
 }
 
+bool AMDGPURegBankCombinerImpl::matchFmulSelectToFldexpVgpr(
+    MachineInstr &MI, MachineInstr &Sel,
+    std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+  // Only combine for VGPR (divergent) operands. SGPR operands benefit from
+  // keeping fmul which has an SALU form (S_FMUL), while fldexp does not.
+  Register Dst = MI.getOperand(0).getReg();
+  if (!isVgprRegBank(Dst))
+    return false;
+
+  return matchFmulWithSelectToFldexpImpl(MI, Sel, MatchInfo, MRI, TII);
+}
+
 AMDGPURegBankCombinerImpl::MinMaxMedOpc
 AMDGPURegBankCombinerImpl::getMinMaxPair(unsigned Opc) const {
   switch (Opc) {
diff --git a/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll b/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
index 8b6eea149b927..d90f4d30b40f3 100644
--- a/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
+++ b/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
@@ -2,34 +2,20 @@
 ; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1200 < %s | FileCheck -check-prefixes=GFX12,GFX12-SDAG %s
 ; RUN: llc -global-isel=1 -mtriple=amdgcn -mcpu=gfx1200 < %s | FileCheck -check-prefixes=GFX12,GFX12-GISEL %s
 
-; TODO: GlobalISel should avoid generating v_ldexp_f32.
 define amdgpu_cs float @v_s_exp_f32(float inreg %src) {
-; GFX12-SDAG-LABEL: v_s_exp_f32:
-; GFX12-SDAG:       ; %bb.0:
-; GFX12-SDAG-NEXT:    s_cmp_lt_f32 s0, 0xc2fc0000
-; GFX12-SDAG-NEXT:    s_cselect_b32 s1, 0x42800000, 0
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
-; GFX12-SDAG-NEXT:    s_add_f32 s0, s0, s1
-; GFX12-SDAG-NEXT:    s_cselect_b32 s1, 0x1f800000, 1.0
-; GFX12-SDAG-NEXT:    v_s_exp_f32 s0, s0
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(TRANS32_DEP_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
-; GFX12-SDAG-NEXT:    s_mul_f32 s0, s0, s1
-; GFX12-SDAG-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-SDAG-NEXT:    v_mov_b32_e32 v0, s0
-; GFX12-SDAG-NEXT:    ; return to shader part epilog
-;
-; GFX12-GISEL-LABEL: v_s_exp_f32:
-; GFX12-GISEL:       ; %bb.0:
-; GFX12-GISEL-NEXT:    s_cmp_lt_f32 s0, 0xc2fc0000
-; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 0x42800000, 0
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
-; GFX12-GISEL-NEXT:    s_add_f32 s0, s0, s1
-; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 0xffffffc0, 0
-; GFX12-GISEL-NEXT:    v_s_exp_f32 s0, s0
-; GFX12-GISEL-NEXT:    s_wait_alu depctr_va_sdst(0)
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(TRANS32_DEP_1)
-; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, s0, s1
-; GFX12-GISEL-NEXT:    ; return to shader part epilog
+; GFX12-LABEL: v_s_exp_f32:
+; GFX12:       ; %bb.0:
+; GFX12-NEXT:    s_cmp_lt_f32 s0, 0xc2fc0000
+; GFX12-NEXT:    s_cselect_b32 s1, 0x42800000, 0
+; GFX12-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
+; GFX12-NEXT:    s_add_f32 s0, s0, s1
+; GFX12-NEXT:    s_cselect_b32 s1, 0x1f800000, 1.0
+; GFX12-NEXT:    v_s_exp_f32 s0, s0
+; GFX12-NEXT:    s_delay_alu instid0(TRANS32_DEP_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
+; GFX12-NEXT:    s_mul_f32 s0, s0, s1
+; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT:    v_mov_b32_e32 v0, s0
+; GFX12-NEXT:    ; return to shader part epilog
   %result = call float @llvm.exp2.f32(float %src)
   ret float %result
 }
@@ -71,34 +57,19 @@ define amdgpu_cs half @v_s_amdgcn_exp_f16(half inreg %src) {
 }
 
 define amdgpu_cs float @v_s_log_f32(float inreg %src) {
-; GFX12-SDAG-LABEL: v_s_log_f32:
-; GFX12-SDAG:       ; %bb.0:
-; GFX12-SDAG-NEXT:    s_cmp_lt_f32 s0, 0x800000
-; GFX12-SDAG-NEXT:    s_cselect_b32 s1, 0x4f800000, 1.0
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
-; GFX12-SDAG-NEXT:    s_mul_f32 s0, s0, s1
-; GFX12-SDAG-NEXT:    s_cselect_b32 s1, 0x42000000, 0
-; GFX12-SDAG-NEXT:    v_s_log_f32 s0, s0
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(TRANS32_DEP_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
-; GFX12-SDAG-NEXT:    s_sub_f32 s0, s0, s1
-; GFX12-SDAG-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-SDAG-NEXT:    v_mov_b32_e32 v0, s0
-; GFX12-SDAG-NEXT:    ; return to shader part epilog
-;
-; GFX12-GISEL-LABEL: v_s_log_f32:
-; GFX12-GISEL:       ; %bb.0:
-; GFX12-GISEL-NEXT:    s_cmp_lt_f32 s0, 0x800000
-; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 1, 0
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_3) | instid1(VALU_DEP_1)
-; GFX12-GISEL-NEXT:    s_lshl_b32 s2, s1, 5
-; GFX12-GISEL-NEXT:    s_cmp_lg_u32 s1, 0
-; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, s0, s2
-; GFX12-GISEL-NEXT:    s_cselect_b32 s0, 0x42000000, 0
-; GFX12-GISEL-NEXT:    v_log_f32_e32 v0, v0
-; GFX12-GISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(TRANS32_DEP_1)
-; GFX12-GISEL-NEXT:    v_subrev_f32_e32 v0, s0, v0
-; GFX12-GISEL-NEXT:    ; return to shader part epilog
+; GFX12-LABEL: v_s_log_f32:
+; GFX12:       ; %bb.0:
+; GFX12-NEXT:    s_cmp_lt_f32 s0, 0x800000
+; GFX12-NEXT:    s_cselect_b32 s1, 0x4f800000, 1.0
+; GFX12-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
+; GFX12-NEXT:    s_mul_f32 s0, s0, s1
+; GFX12-NEXT:    s_cselect_b32 s1, 0x42000000, 0
+; GFX12-NEXT:    v_s_log_f32 s0, s0
+; GFX12-NEXT:    s_delay_alu instid0(TRANS32_DEP_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_2)
+; GFX12-NEXT:    s_sub_f32 s0, s0, s1
+; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT:    v_mov_b32_e32 v0, s0
+; GFX12-NEXT:    ; return to shader part epilog
   %result = call float @llvm.log2.f32(float %src)
   ret float %result
 }
@@ -312,37 +283,21 @@ define amdgpu_cs half @v_amdgcn_sqrt_f16(half inreg %src)  {
 }
 
 define amdgpu_cs float @srcmods_abs_f32(float inreg %src) {
-; GFX12-SDAG-LABEL: srcmods_abs_f32:
-; GFX12-SDAG:       ; %bb.0:
-; GFX12-SDAG-NEXT:    s_bitset0_b32 s0, 31
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
-; GFX12-SDAG-NEXT:    s_cmp_lt_f32 s0, 0x800000
-; GFX12-SDAG-NEXT:    s_cselect_b32 s1, 0x4f800000, 1.0
-; GFX12-SDAG-NEXT:    s_mul_f32 s0, s0, s1
-; GFX12-SDAG-NEXT:    s_cselect_b32 s1, 0x42000000, 0
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(SALU_CYCLE_2) | instskip(NEXT) | instid1(TRANS32_DEP_1)
-; GFX12-SDAG-NEXT:    v_s_log_f32 s0, s0
-; GFX12-SDAG-NEXT:    s_sub_f32 s0, s0, s1
-; GFX12-SDAG-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-SDAG-NEXT:    s_delay_alu instid0(SALU_CYCLE_2)
-; GFX12-SDAG-NEXT:    v_mov_b32_e32 v0, s0
-; GFX12-SDAG-NEXT:    ; return to shader part epilog
-;
-; GFX12-GISEL-LABEL: srcmods_abs_f32:
-; GFX12-GISEL:       ; %bb.0:
-; GFX12-GISEL-NEXT:    s_and_b32 s1, s0, 0x7fffffff
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
-; GFX12-GISEL-NEXT:    s_cmp_lt_f32 s1, 0x800000
-; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 1, 0
-; GFX12-GISEL-NEXT:    s_lshl_b32 s2, s1, 5
-; GFX12-GISEL-NEXT:    s_cmp_lg_u32 s1, 0
-; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, |s0|, s2
-; GFX12-GISEL-NEXT:    s_cselect_b32 s0, 0x42000000, 0
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(TRANS32_DEP_1)
-; GFX12-GISEL-NEXT:    v_log_f32_e32 v0, v0
-; GFX12-GISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-GISEL-NEXT:    v_subrev_f32_e32 v0, s0, v0
-; GFX12-GISEL-NEXT:    ; return to shader part epilog
+; GFX12-LABEL: srcmods_abs_f32:
+; GFX12:       ; %bb.0:
+; GFX12-NEXT:    s_bitset0_b32 s0, 31
+; GFX12-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
+; GFX12-NEXT:    s_cmp_lt_f32 s0, 0x800000
+; GFX12-NEXT:    s_cselect_b32 s1, 0x4f800000, 1.0
+; GFX12-NEXT:    s_mul_f32 s0, s0, s1
+; GFX12-NEXT:    s_cselect_b32 s1, 0x42000000, 0
+; GFX12-NEXT:    s_delay_alu instid0(SALU_CYCLE_2) | instskip(NEXT) | instid1(TRANS32_DEP_1)
+; GFX12-NEXT:    v_s_log_f32 s0, s0
+; GFX12-NEXT:    s_sub_f32 s0, s0, s1
+; GFX12-NEXT:    s_wait_alu depctr_sa_sdst(0)
+; GFX12-NEXT:    s_delay_alu instid0(SALU_CYCLE_2)
+; GFX12-NEXT:    v_mov_b32_e32 v0, s0
+; GFX12-NEXT:    ; return to shader part epilog
   %abs = call float @llvm.fabs.f32(float %src)
   %result = call float @llvm.log2.f32(float %abs)
   ret float %result
@@ -366,18 +321,18 @@ define amdgpu_cs float @srcmods_neg_f32(float inreg %src) {
 ;
 ; GFX12-GISEL-LABEL: srcmods_neg_f32:
 ; GFX12-GISEL:       ; %bb.0:
-; GFX12-GISEL-NEXT:    s_xor_b32 s1, s0, 0x80000000
+; GFX12-GISEL-NEXT:    s_xor_b32 s0, s0, 0x80000000
 ; GFX12-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
-; GFX12-GISEL-NEXT:    s_cmp_lt_f32 s1, 0x800000
-; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 1, 0
-; GFX12-GISEL-NEXT:    s_lshl_b32 s2, s1, 5
-; GFX12-GISEL-NEXT:    s_cmp_lg_u32 s1, 0
-; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, -s0, s2
-; GFX12-GISEL-NEXT:    s_cselect_b32 s0, 0x42000000, 0
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(TRANS32_DEP_1)
-; GFX12-GISEL-NEXT:    v_log_f32_e32 v0, v0
+; GFX12-GISEL-NEXT:    s_cmp_lt_f32 s0, 0x800000
+; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 0x4f800000, 1.0
+; GFX12-GISEL-NEXT:    s_mul_f32 s0, s0, s1
+; GFX12-GISEL-NEXT:    s_cselect_b32 s1, 0x42000000, 0
+; GFX12-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_2) | instskip(NEXT) | instid1(TRANS32_DEP_1)
+; GFX12-GISEL-NEXT:    v_s_log_f32 s0, s0
+; GFX12-GISEL-NEXT:    s_sub_f32 s0, s0, s1
 ; GFX12-GISEL-NEXT:    s_wait_alu depctr_sa_sdst(0)
-; GFX12-GISEL-NEXT:    v_subrev_f32_e32 v0, s0, v0
+; GFX12-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_2)
+; GFX12-GISEL-NEXT:    v_mov_b32_e32 v0, s0
 ; GFX12-GISEL-NEXT:    ; return to shader part epilog
   %neg = fneg float %src
   %result = call float @llvm.log2.f32(float %neg)
@@ -461,9 +416,10 @@ define amdgpu_cs float @fmul_select_pow2_divergent_f32(float %x, i1 %cond) {
 ;
 ; GFX12-GISEL-LABEL: fmul_select_pow2_divergent_f32:
 ; GFX12-GISEL:       ; %bb.0:
-; GFX12-GISEL-NEXT:    v_and_b32_e32 v1, 1, v1
-; GFX12-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX12-GISEL-NEXT:    v_lshlrev_b32_e32 v1, 3, v1
+; GFX12-GISEL-NEXT:    v_and_b16 v1.l, 1, v1.l
+; GFX12-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX12-GISEL-NEXT:    v_cmp_ne_u16_e32 vcc_lo, 0, v1.l
+; GFX12-GISEL-NEXT:    v_cndmask_b32_e64 v1, 0, 8, vcc_lo
 ; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, v0, v1
 ; GFX12-GISEL-NEXT:    ; return to shader part epilog
   %sel = select i1 %cond, float 256.0, float 1.0

>From 273569c5ee9c7b0d0b2ba47837f663c2cf3be006 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Mon, 13 Apr 2026 21:10:56 -0700
Subject: [PATCH 3/5] Formatting

---
 llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h
index 73a53ef15834c..10c761ba726a0 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.h
@@ -48,7 +48,7 @@ class AMDGPUCombinerHelper : public CombinerHelper {
 };
 
 // Shared helper used by both
-// PostLegalizerCombiner and RegBankCombiner 
+// PostLegalizerCombiner and RegBankCombiner
 bool matchFmulWithSelectToFldexpImpl(
     MachineInstr &MI, MachineInstr &Sel,
     std::function<void(MachineIRBuilder &)> &MatchInfo,

>From 01f213cb620c719e0c3e3282f5fdcb69700ce402 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Mon, 20 Apr 2026 14:02:54 -0700
Subject: [PATCH 4/5] Addresses PR reviews

---
 llvm/lib/Target/AMDGPU/AMDGPUCombine.td        | 18 ++++++++++--------
 .../lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp |  3 ++-
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombine.td b/llvm/lib/Target/AMDGPU/AMDGPUCombine.td
index 164aff23514c6..a617e144d12a0 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombine.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombine.td
@@ -140,6 +140,16 @@ def combine_fmul_with_select_to_fldexp : GICombineRule<
           [{ return Helper.matchCombineFmulWithSelectToFldexp(*${root}, *${sel}, ${matchinfo}); }]),
   (apply  [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
 
+// Like combine_fmul_with_select_to_fldexp but with VGPR detection for RegBankCombiner.
+// Note that the combine for s64, s32, and s16 would not trigger under the combine_fmul_with_select_to_fldexp
+// in the pre/post legalizer (it is only gating s{64,32,16} with SALU for the regcombiner).
+def combine_fmul_select_to_fldexp_regbank : GICombineRule<
+  (defs root:$root, build_fn_matchinfo:$matchinfo),
+  (match  (G_FMUL $dst, $x, $select):$root,
+          (G_SELECT $select, $y, $A, $B):$sel,
+          [{ return matchFmulSelectToFldexpVgpr(*${root}, *${sel}, ${matchinfo}); }]),
+  (apply  [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
 // (shift x, (zext amt)) -> (shift x, (and (anyext amt), mask)
 //
 // The pattern is longer, but is better for matching during ISel.
@@ -232,14 +242,6 @@ def AMDGPUPostLegalizerCombiner: GICombiner<
   let CombineAllMethodName = "tryCombineAllImpl";
 }
 
-// Like combine_fmul_with_select_to_fldexp but with VGPR detection for RegBankCombiner.
-def combine_fmul_select_to_fldexp_regbank : GICombineRule<
-  (defs root:$root, build_fn_matchinfo:$matchinfo),
-  (match  (G_FMUL $dst, $x, $select):$root,
-          (G_SELECT $select, $y, $A, $B):$sel,
-          [{ return matchFmulSelectToFldexpVgpr(*${root}, *${sel}, ${matchinfo}); }]),
-  (apply  [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
-
 def AMDGPURegBankCombiner : GICombiner<
   "AMDGPURegBankCombinerImpl",
   [combine_fmul_select_to_fldexp_regbank,
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
index 82da8a2aee7a9..2c3e3abb9ab40 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
@@ -532,7 +532,8 @@ bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
   // to the RegBankCombiner where register banks are known and we can limit it
   // to VGPR (divergent) values only.
   if (STI.hasSALUFloatInsts() &&
-      (ScalarDestTy == LLT::scalar(32) || ScalarDestTy == LLT::scalar(16)))
+      (ScalarDestTy == LLT::scalar(64) || ScalarDestTy == LLT::scalar(32) ||
+       ScalarDestTy == LLT::scalar(16)))
     return false;
 
   return matchFmulWithSelectToFldexpImpl(MI, Sel, MatchInfo, MRI, TII);

>From 557c2a5f4b016fa4d4714dfca46d7a71b167e900 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Wed, 22 Apr 2026 12:53:06 -0700
Subject: [PATCH 5/5] Gate only 32, add reg bank

---
 .../Target/AMDGPU/AMDGPUCombinerHelper.cpp    | 16 +++++++++---
 .../AMDGPU/pseudo-scalar-transcendental.ll    | 25 +++++++++++++++++++
 2 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
index 2c3e3abb9ab40..4a1d1113a3db3 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUCombinerHelper.cpp
@@ -509,10 +509,22 @@ bool llvm::matchFmulWithSelectToFldexpImpl(
         Builder.buildConstant(IntDestTy, SelectTrueLog2Val),
         Builder.buildConstant(IntDestTy, SelectFalseLog2Val));
 
+    const auto *RegBank = MRI.getRegBankOrNull(Dst);
+    if (RegBank) {
+      auto &MutMRI = Builder.getMF().getRegInfo();
+      MutMRI.setRegBank(NewSel.getReg(0), *RegBank);
+      MutMRI.setRegBank(NewSel->getOperand(2).getReg(), *RegBank);
+      MutMRI.setRegBank(NewSel->getOperand(3).getReg(), *RegBank);
+    }
+
     Register XReg = MI.getOperand(1).getReg();
     if (SelectTrueVal->isNegative()) {
       auto NegX =
           Builder.buildFNeg(DestTy, XReg, MRI.getVRegDef(XReg)->getFlags());
+      if (RegBank) {
+        auto &MutMRI = Builder.getMF().getRegInfo();
+        MutMRI.setRegBank(NegX.getReg(0), *RegBank);
+      }
       Builder.buildFLdexp(Dst, NegX, NewSel, MI.getFlags());
     } else {
       Builder.buildFLdexp(Dst, XReg, NewSel, MI.getFlags());
@@ -531,9 +543,7 @@ bool AMDGPUCombinerHelper::matchCombineFmulWithSelectToFldexp(
   // fldexp has no SALU form. On targets with SALU float, defer this combine
   // to the RegBankCombiner where register banks are known and we can limit it
   // to VGPR (divergent) values only.
-  if (STI.hasSALUFloatInsts() &&
-      (ScalarDestTy == LLT::scalar(64) || ScalarDestTy == LLT::scalar(32) ||
-       ScalarDestTy == LLT::scalar(16)))
+  if (STI.hasSALUFloatInsts() && ScalarDestTy == LLT::scalar(32))
     return false;
 
   return matchFmulWithSelectToFldexpImpl(MI, Sel, MatchInfo, MRI, TII);
diff --git a/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll b/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
index d90f4d30b40f3..6a077be1cf623 100644
--- a/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
+++ b/llvm/test/CodeGen/AMDGPU/pseudo-scalar-transcendental.ll
@@ -426,3 +426,28 @@ define amdgpu_cs float @fmul_select_pow2_divergent_f32(float %x, i1 %cond) {
   %result = fmul float %x, %sel
   ret float %result
 }
+
+; Test that the fmul+select -> fldexp combine works for divergent values
+; with negative constants
+define amdgpu_cs float @fmul_select_neg_pow2_divergent_f32(float %x, i1 %cond) {
+; GFX12-SDAG-LABEL: fmul_select_neg_pow2_divergent_f32:
+; GFX12-SDAG:       ; %bb.0:
+; GFX12-SDAG-NEXT:    v_and_b32_e32 v1, 1, v1
+; GFX12-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX12-SDAG-NEXT:    v_cmp_eq_u32_e32 vcc_lo, 1, v1
+; GFX12-SDAG-NEXT:    v_cndmask_b32_e64 v1, 0, 8, vcc_lo
+; GFX12-SDAG-NEXT:    v_ldexp_f32 v0, -v0, v1
+; GFX12-SDAG-NEXT:    ; return to shader part epilog
+;
+; GFX12-GISEL-LABEL: fmul_select_neg_pow2_divergent_f32:
+; GFX12-GISEL:       ; %bb.0:
+; GFX12-GISEL-NEXT:    v_and_b16 v1.l, 1, v1.l
+; GFX12-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_1)
+; GFX12-GISEL-NEXT:    v_cmp_ne_u16_e32 vcc_lo, 0, v1.l
+; GFX12-GISEL-NEXT:    v_cndmask_b32_e64 v1, 0, 8, vcc_lo
+; GFX12-GISEL-NEXT:    v_ldexp_f32 v0, -v0, v1
+; GFX12-GISEL-NEXT:    ; return to shader part epilog
+  %sel = select i1 %cond, float -256.0, float -1.0
+  %result = fmul float %x, %sel
+  ret float %result
+}



More information about the llvm-commits mailing list