[llvm] [GlobalISel] Correctly fold fpext->fmul with fewer uses (PR #218013)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 12:46:24 PDT 2026
https://github.com/LU-JOHN updated https://github.com/llvm/llvm-project/pull/218013
>From 7ff0f7b47c8e09d87b48758f427d99685cc42fbc Mon Sep 17 00:00:00 2001
From: John Lu <John.Lu at amd.com>
Date: Fri, 21 Aug 2026 13:59:36 -0500
Subject: [PATCH 1/3] Add testcase with current results
Signed-off-by: John Lu <John.Lu at amd.com>
---
.../GlobalISel/combine-fma-add-ext-mul.ll | 43 +++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
index 375d46459c637..9262812fbe226 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
@@ -151,3 +151,46 @@ define amdgpu_vs <6 x float> @test_6xf16_6xf32_add_ext_mul_rhs(<6 x half> inreg
%c = fadd fast <6 x float> %z, %b
ret <6 x float> %c
}
+
+; This test has two fpext(fmul) operands where the first fmul has multiple uses
+; and the second has only one use. The one with fewer uses (i.e. the second one)
+; should be folded.
+define amdgpu_vs <2 x float> @test_fpext_fmul_multiple_uses(half inreg %x1, half inreg %y1, half inreg %x2, half inreg %y2, half inreg %z) {
+; GFX9-FAST-DENORM-LABEL: test_fpext_fmul_multiple_uses:
+; GFX9-FAST-DENORM: ; %bb.0: ; %.entry
+; GFX9-FAST-DENORM-NEXT: v_mov_b32_e32 v0, s1
+; GFX9-FAST-DENORM-NEXT: v_mov_b32_e32 v1, s4
+; GFX9-FAST-DENORM-NEXT: v_mac_f16_e32 v1, s0, v0
+; GFX9-FAST-DENORM-NEXT: v_cvt_f32_f16_e32 v1, v1
+; GFX9-FAST-DENORM-NEXT: v_mov_b32_e32 v2, s3
+; GFX9-FAST-DENORM-NEXT: v_mul_f16_e32 v2, s2, v2
+; GFX9-FAST-DENORM-NEXT: v_mad_mix_f32 v0, s0, v0, v2 op_sel_hi:[1,1,1]
+; GFX9-FAST-DENORM-NEXT: ; return to shader part epilog
+;
+; GFX10-FAST-DENORM-LABEL: test_fpext_fmul_multiple_uses:
+; GFX10-FAST-DENORM: ; %bb.0: ; %.entry
+; GFX10-FAST-DENORM-NEXT: v_mul_f16_e64 v0, s0, s1
+; GFX10-FAST-DENORM-NEXT: v_mul_f16_e64 v1, s2, s3
+; GFX10-FAST-DENORM-NEXT: v_add_f16_e32 v2, s4, v0
+; GFX10-FAST-DENORM-NEXT: v_fma_mix_f32 v0, s0, s1, v1 op_sel_hi:[1,1,1]
+; GFX10-FAST-DENORM-NEXT: v_cvt_f32_f16_e32 v1, v2
+; GFX10-FAST-DENORM-NEXT: ; return to shader part epilog
+.entry:
+ ; First fpext(fmul) operand: %mul1 has multiple uses
+ %mul1 = fmul fast half %x1, %y1
+ %ext1 = fpext half %mul1 to float
+ %add_f16 = fadd fast half %mul1, %z ; Second use of %mul1 at f16 level
+
+ ; Second fpext(fmul) operand: %mul2 has only one use
+ %mul2 = fmul fast half %x2, %y2
+ %ext2 = fpext half %mul2 to float
+
+ ; This fadd has two fpext(fmul) operands
+ %add = fadd fast float %ext1, %ext2
+
+ %ext_add_f16 = fpext half %add_f16 to float
+ %ret0 = insertelement <2 x float> undef, float %add, i32 0
+ %ret1 = insertelement <2 x float> %ret0, float %ext_add_f16, i32 1
+
+ ret <2 x float> %ret1
+}
>From 3c9036ec21c2ce88b2cedcbe89b17bcdfc6c810e Mon Sep 17 00:00:00 2001
From: John Lu <John.Lu at amd.com>
Date: Fri, 21 Aug 2026 14:29:20 -0500
Subject: [PATCH 2/3] Correctly fold side with fewer uses
Signed-off-by: John Lu <John.Lu at amd.com>
---
.../lib/CodeGen/GlobalISel/CombinerHelper.cpp | 55 ++++++++-----------
.../GlobalISel/combine-fma-add-ext-mul.ll | 7 +--
2 files changed, 27 insertions(+), 35 deletions(-)
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 55c4339edc39e..95b1168142904 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -6573,46 +6573,39 @@ bool CombinerHelper::matchCombineFAddFpExtFMulToFMadOrFMA(
unsigned PreferredFusedOpcode =
HasFMAD ? TargetOpcode::G_FMAD : TargetOpcode::G_FMA;
- // If we have two choices trying to fold (fadd (fmul u, v), (fmul x, y)),
- // prefer to fold the multiply with fewer uses.
- if (Aggressive && isContractableFMul(*LHS.MI, AllowFusionGlobally) &&
- isContractableFMul(*RHS.MI, AllowFusionGlobally)) {
- if (hasMoreUses(*LHS.MI, *RHS.MI, MRI))
+ MachineInstr *LHSFpExtSrc;
+ bool LHSContractable =
+ mi_match(LHS.Reg, MRI, m_GFPExt(m_MInstr(LHSFpExtSrc))) &&
+ isContractableFMul(*LHSFpExtSrc, AllowFusionGlobally) &&
+ TLI.isFPExtFoldable(MI, PreferredFusedOpcode, DstType,
+ MRI.getType(LHSFpExtSrc->getOperand(1).getReg()));
+ MachineInstr *RHSFpExtSrc;
+ bool RHSContractable =
+ mi_match(RHS.Reg, MRI, m_GFPExt(m_MInstr(RHSFpExtSrc))) &&
+ isContractableFMul(*RHSFpExtSrc, AllowFusionGlobally) &&
+ TLI.isFPExtFoldable(MI, PreferredFusedOpcode, DstType,
+ MRI.getType(RHSFpExtSrc->getOperand(1).getReg()));
+
+ // fold (fadd (fpext (fmul x, y)), z) -> (fma (fpext x), (fpext y), z)
+ if (LHSContractable || RHSContractable) {
+ // Ensure that the contractable fmul with the fewest uses (if both are
+ // contractable) is the LHS operand.
+ if (!LHSContractable ||
+ (RHSContractable && hasMoreUses(*LHSFpExtSrc, *RHSFpExtSrc, MRI))) {
std::swap(LHS, RHS);
- }
+ LHSFpExtSrc = RHSFpExtSrc;
+ }
- // fold (fadd (fpext (fmul x, y)), z) -> (fma (fpext x), (fpext y), z)
- MachineInstr *FpExtSrc;
- if (mi_match(LHS.Reg, MRI, m_GFPExt(m_MInstr(FpExtSrc))) &&
- isContractableFMul(*FpExtSrc, AllowFusionGlobally) &&
- TLI.isFPExtFoldable(MI, PreferredFusedOpcode, DstType,
- MRI.getType(FpExtSrc->getOperand(1).getReg()))) {
- unsigned Flags = MI.getFlags() & FpExtSrc->getFlags();
+ unsigned Flags = MI.getFlags() & LHSFpExtSrc->getFlags();
MatchInfo = [=, &MI](MachineIRBuilder &B) {
- auto FpExtX = B.buildFPExt(DstType, FpExtSrc->getOperand(1).getReg());
- auto FpExtY = B.buildFPExt(DstType, FpExtSrc->getOperand(2).getReg());
+ auto FpExtX = B.buildFPExt(DstType, LHSFpExtSrc->getOperand(1).getReg());
+ auto FpExtY = B.buildFPExt(DstType, LHSFpExtSrc->getOperand(2).getReg());
B.buildInstr(PreferredFusedOpcode, {MI.getOperand(0).getReg()},
{FpExtX.getReg(0), FpExtY.getReg(0), RHS.Reg}, Flags);
};
return true;
}
- // fold (fadd z, (fpext (fmul x, y))) -> (fma (fpext x), (fpext y), z)
- // Note: Commutes FADD operands.
- if (mi_match(RHS.Reg, MRI, m_GFPExt(m_MInstr(FpExtSrc))) &&
- isContractableFMul(*FpExtSrc, AllowFusionGlobally) &&
- TLI.isFPExtFoldable(MI, PreferredFusedOpcode, DstType,
- MRI.getType(FpExtSrc->getOperand(1).getReg()))) {
- unsigned Flags = MI.getFlags() & FpExtSrc->getFlags();
- MatchInfo = [=, &MI](MachineIRBuilder &B) {
- auto FpExtX = B.buildFPExt(DstType, FpExtSrc->getOperand(1).getReg());
- auto FpExtY = B.buildFPExt(DstType, FpExtSrc->getOperand(2).getReg());
- B.buildInstr(PreferredFusedOpcode, {MI.getOperand(0).getReg()},
- {FpExtX.getReg(0), FpExtY.getReg(0), LHS.Reg}, Flags);
- };
- return true;
- }
-
return false;
}
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
index 9262812fbe226..d2bb0150d5e20 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
@@ -170,10 +170,9 @@ define amdgpu_vs <2 x float> @test_fpext_fmul_multiple_uses(half inreg %x1, half
; GFX10-FAST-DENORM-LABEL: test_fpext_fmul_multiple_uses:
; GFX10-FAST-DENORM: ; %bb.0: ; %.entry
; GFX10-FAST-DENORM-NEXT: v_mul_f16_e64 v0, s0, s1
-; GFX10-FAST-DENORM-NEXT: v_mul_f16_e64 v1, s2, s3
-; GFX10-FAST-DENORM-NEXT: v_add_f16_e32 v2, s4, v0
-; GFX10-FAST-DENORM-NEXT: v_fma_mix_f32 v0, s0, s1, v1 op_sel_hi:[1,1,1]
-; GFX10-FAST-DENORM-NEXT: v_cvt_f32_f16_e32 v1, v2
+; GFX10-FAST-DENORM-NEXT: v_add_f16_e32 v1, s4, v0
+; GFX10-FAST-DENORM-NEXT: v_fma_mix_f32 v0, s2, s3, v0 op_sel_hi:[1,1,1]
+; GFX10-FAST-DENORM-NEXT: v_cvt_f32_f16_e32 v1, v1
; GFX10-FAST-DENORM-NEXT: ; return to shader part epilog
.entry:
; First fpext(fmul) operand: %mul1 has multiple uses
>From b025750c5701784d17890c8385e587f61298cd55 Mon Sep 17 00:00:00 2001
From: John Lu <John.Lu at amd.com>
Date: Fri, 21 Aug 2026 14:46:08 -0500
Subject: [PATCH 3/3] Avoid undef
Signed-off-by: John Lu <John.Lu at amd.com>
---
llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
index d2bb0150d5e20..87d72e6e15acc 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/combine-fma-add-ext-mul.ll
@@ -188,7 +188,7 @@ define amdgpu_vs <2 x float> @test_fpext_fmul_multiple_uses(half inreg %x1, half
%add = fadd fast float %ext1, %ext2
%ext_add_f16 = fpext half %add_f16 to float
- %ret0 = insertelement <2 x float> undef, float %add, i32 0
+ %ret0 = insertelement <2 x float> poison, float %add, i32 0
%ret1 = insertelement <2 x float> %ret0, float %ext_add_f16, i32 1
ret <2 x float> %ret1
More information about the llvm-commits
mailing list