[llvm] 4d3759d - [InstCombine] Fold X * ldexp(1.0, Y) -> ldexp(X, Y). (#188493)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 03:44:48 PDT 2026


Author: Ricardo Jesus
Date: 2026-03-30T11:44:43+01:00
New Revision: 4d3759d83b4e59077c6bdd3dcf88d726f7e5a8d6

URL: https://github.com/llvm/llvm-project/commit/4d3759d83b4e59077c6bdd3dcf88d726f7e5a8d6
DIFF: https://github.com/llvm/llvm-project/commit/4d3759d83b4e59077c6bdd3dcf88d726f7e5a8d6.diff

LOG: [InstCombine] Fold X * ldexp(1.0, Y) -> ldexp(X, Y). (#188493)

This would avoid the FMUL in sequences such as
[these](https://godbolt.org/z/xhqfe5sb1).

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
    llvm/test/Transforms/InstCombine/fmul.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 35ab21f6d8740..80b8bd27b7086 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1090,6 +1090,15 @@ Instruction *InstCombinerImpl::visitFMul(BinaryOperator &I) {
     return replaceInstUsesWith(I, Sin);
   }
 
+  // X * ldexp(1.0, Y) -> ldexp(X, Y)
+  if (match(&I, m_AllowReassoc(m_c_FMul(
+                    m_Value(X),
+                    m_AllowReassoc(m_OneUse(m_Intrinsic<Intrinsic::ldexp>(
+                        m_FPOne(), m_Value(Y))))))))
+    return replaceInstUsesWith(
+        I, Builder.CreateIntrinsic(Intrinsic::ldexp,
+                                   {X->getType(), Y->getType()}, {X, Y}, &I));
+
   if (SimplifyDemandedInstructionFPClass(I))
     return &I;
 

diff  --git a/llvm/test/Transforms/InstCombine/fmul.ll b/llvm/test/Transforms/InstCombine/fmul.ll
index e8019e0fab0d3..c62ccfbbc6510 100644
--- a/llvm/test/Transforms/InstCombine/fmul.ll
+++ b/llvm/test/Transforms/InstCombine/fmul.ll
@@ -1408,3 +1408,112 @@ entry:
   %ret = fmul <3 x float> %a, <float -0.0, float 0.0, float poison>
   ret <3 x float> %ret
 }
+
+; fmul X, ldexp(1.0, Y) -> ldexp X, Y
+define float @fmul_ldexp(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp(
+; CHECK-NEXT:    [[MUL:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X:%.*]], i32 [[EXP:%.*]])
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call reassoc float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul reassoc float %x, %ldexp
+  ret float %mul
+}
+
+define float @fmul_ldexp_comm(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_comm(
+; CHECK-NEXT:    [[MUL:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X:%.*]], i32 [[EXP:%.*]])
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call reassoc float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul reassoc float %ldexp, %x
+  ret float %mul
+}
+
+define double @fmul_ldexp_f64(double %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_f64(
+; CHECK-NEXT:    [[MUL:%.*]] = call reassoc double @llvm.ldexp.f64.i32(double [[X:%.*]], i32 [[EXP:%.*]])
+; CHECK-NEXT:    ret double [[MUL]]
+;
+  %ldexp = call reassoc double @llvm.ldexp(double 1.0, i32 %exp)
+  %mul = fmul reassoc double %x, %ldexp
+  ret double %mul
+}
+
+define <2 x float> @fmul_ldexp_v2f32(<2 x float> %x, <2 x i32> %exp) {
+; CHECK-LABEL: @fmul_ldexp_v2f32(
+; CHECK-NEXT:    [[MUL:%.*]] = call reassoc <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X:%.*]], <2 x i32> [[EXP:%.*]])
+; CHECK-NEXT:    ret <2 x float> [[MUL]]
+;
+  %ldexp = call reassoc <2 x float> @llvm.ldexp(<2 x float> splat (float 1.0), <2 x i32> %exp)
+  %mul = fmul reassoc <2 x float> %x, %ldexp
+  ret <2 x float> %mul
+}
+
+define float @fmul_ldexp_noreassoc_fmul(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_noreassoc_fmul(
+; CHECK-NEXT:    [[LDEXP:%.*]] = call reassoc nnan float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[EXP:%.*]])
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X:%.*]], [[LDEXP]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call reassoc float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul float %x, %ldexp
+  ret float %mul
+}
+
+define float @fmul_ldexp_noreassoc_ldexp(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_noreassoc_ldexp(
+; CHECK-NEXT:    [[LDEXP:%.*]] = call nnan float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[EXP:%.*]])
+; CHECK-NEXT:    [[MUL:%.*]] = fmul reassoc float [[X:%.*]], [[LDEXP]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul reassoc float %x, %ldexp
+  ret float %mul
+}
+
+define float @fmul_ldexp_multiuse_ldexp(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_multiuse_ldexp(
+; CHECK-NEXT:    [[LDEXP:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float 1.000000e+00, i32 [[EXP:%.*]])
+; CHECK-NEXT:    [[MUL:%.*]] = fmul reassoc float [[X:%.*]], [[LDEXP]]
+; CHECK-NEXT:    tail call void @use_f32(float [[LDEXP]])
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call reassoc float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul reassoc float %x, %ldexp
+  tail call void @use_f32(float %ldexp)
+  ret float %mul
+}
+
+define float @fmul_ldexp_multiuse_x(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_multiuse_x(
+; CHECK-NEXT:    [[MUL:%.*]] = call reassoc float @llvm.ldexp.f32.i32(float [[X:%.*]], i32 [[EXP:%.*]])
+; CHECK-NEXT:    tail call void @use_f32(float [[X]])
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call reassoc float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul reassoc float %x, %ldexp
+  tail call void @use_f32(float %x)
+  ret float %mul
+}
+
+define float @fmul_ldexp_not_fpone(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_not_fpone(
+; CHECK-NEXT:    [[LDEXP:%.*]] = call reassoc nnan float @llvm.ldexp.f32.i32(float 2.000000e+00, i32 [[EXP:%.*]])
+; CHECK-NEXT:    [[MUL:%.*]] = fmul reassoc float [[X:%.*]], [[LDEXP]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call reassoc float @llvm.ldexp(float 2.0, i32 %exp)
+  %mul = fmul reassoc float %x, %ldexp
+  ret float %mul
+}
+
+define float @fmul_ldexp_preserve_fmul_flags(float %x, i32 %exp) {
+; CHECK-LABEL: @fmul_ldexp_preserve_fmul_flags(
+; CHECK-NEXT:    [[MUL:%.*]] = call reassoc ninf arcp float @llvm.ldexp.f32.i32(float [[X:%.*]], i32 [[EXP:%.*]])
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %ldexp = call nnan reassoc float @llvm.ldexp(float 1.0, i32 %exp)
+  %mul = fmul ninf arcp reassoc float %x, %ldexp
+  ret float %mul
+}


        


More information about the llvm-commits mailing list