Skip to content

[SimplifyLibCalls] Constant fold scalbxx #114417

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

fawdlstty
Copy link
Contributor

This patch adds constant folding support for scalbxx.
Reference: https://en.cppreference.com/w/cpp/numeric/math/scalbn

part2 of #112631

@llvmbot
Copy link
Member

llvmbot commented Oct 31, 2024

@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-llvm-transforms

Author: Fawdlstty (fawdlstty)

Changes

This patch adds constant folding support for scalbxx.
Reference: https://en.cppreference.com/w/cpp/numeric/math/scalbn

part2 of #112631


Full diff: https://github.com/llvm/llvm-project/pull/114417.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h (+1)
  • (modified) llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp (+43)
  • (added) llvm/test/Transforms/InstCombine/scalbn.ll (+85)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 128502b99d9a37..93ab5f5974c189 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -213,6 +213,7 @@ class LibCallSimplifier {
   Value *optimizeSymmetric(CallInst *CI, LibFunc Func, IRBuilderBase &B);
   Value *optimizeRemquo(CallInst *CI, IRBuilderBase &B);
   Value *optimizeFdim(CallInst *CI, IRBuilderBase &B);
+  Value *optimizeScalbn(CallInst *CI, IRBuilderBase &B);
   // Wrapper for all floating point library call optimizations
   Value *optimizeFloatingPointLibCall(CallInst *CI, LibFunc Func,
                                       IRBuilderBase &B);
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index d85e0d99466022..afa2196243b538 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -3157,6 +3157,42 @@ Value *LibCallSimplifier::optimizeFdim(CallInst *CI, IRBuilderBase &B) {
   return ConstantFP::get(CI->getType(), MaxVal);
 }
 
+Value *LibCallSimplifier::optimizeScalbn(CallInst *CI, IRBuilderBase &B) {
+  Value *Base = CI->getArgOperand(0);
+  Value *Expo = CI->getArgOperand(1);
+  // Function *Callee = CI->getCalledFunction();
+  // StringRef Name = Callee->getName();
+  Type *Ty = CI->getType();
+  Module *M = CI->getModule();
+  // bool AllowApprox = CI->hasApproxFunc();
+  // bool Ignored;
+
+  // Propagate the math semantics from the call to any created instructions.
+  IRBuilderBase::FastMathFlagGuard Guard(B);
+  B.setFastMathFlags(CI->getFastMathFlags());
+  // Evaluate special cases related to the base.
+
+  // Scalbn(0.0, exp) -> 0.0
+  if (match(Base, m_SpecificFP(0.0)))
+    return Base;
+
+  // Scalbn(arg, 0) -> arg
+  if (match(Expo, m_Zero()))
+    return Base;
+
+  // Scalbn(arg, 1) -> arg * 2.0
+  if (match(Expo, m_SpecificInt(1)))
+    return B.CreateFMul(Base, ConstantFP::get(Ty, 2.0), "mul");
+
+  // Scalbn(1.0, exp) -> FLT_RADIX ^ exp
+  auto pow = createPowWithIntegerExponent(ConstantFP::get(Ty, 2.0), Expo, M, B);
+  if (match(Base, m_SpecificFP(1.0)))
+    return pow;
+
+  // otherwise
+  return B.CreateFMul(Base, pow, "mul");
+}
+
 //===----------------------------------------------------------------------===//
 // Integer Library Call Optimizations
 //===----------------------------------------------------------------------===//
@@ -4105,6 +4141,13 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
   case LibFunc_remquof:
   case LibFunc_remquol:
     return optimizeRemquo(CI, Builder);
+  case LibFunc_scalbln:
+  case LibFunc_scalblnf:
+  case LibFunc_scalblnl:
+  case LibFunc_scalbn:
+  case LibFunc_scalbnf:
+  case LibFunc_scalbnl:
+    return optimizeScalbn(CI, Builder);
   case LibFunc_nan:
   case LibFunc_nanf:
   case LibFunc_nanl:
diff --git a/llvm/test/Transforms/InstCombine/scalbn.ll b/llvm/test/Transforms/InstCombine/scalbn.ll
new file mode 100644
index 00000000000000..4dccef967973cf
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/scalbn.ll
@@ -0,0 +1,85 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+declare double @scalbln(double, i64)
+declare float @scalblnf(float, i64)
+declare x86_fp80 @scalblnl(x86_fp80, i64)
+declare double @scalbn(double, i32)
+declare float @scalbnf(float, i32)
+declare x86_fp80 @scalbnl(x86_fp80, i32)
+
+;---------------------------------------------------------------------
+; scalbn(scalbn(x, a), b) -> scalbn(x, a + b)
+;---------------------------------------------------------------------
+
+define double @scalbln_scalbln(double %x, i64 %a, i64 %b) {
+; CHECK-LABEL: define double @scalbln
+; CHECK-SAME: (double [[X:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[SCALBXX0:%.*]] = call double @scalbln(double [[X]], i64 [[A]])
+; CHECK-NEXT:    [[SCALBXX1:%.*]] = call double @scalbln(double [[SCALBXX0]], i64 [[B]])
+; CHECK-NEXT:    ret double [[SCALBXX1]]
+;
+  %scalbxx0 = call double @scalbln(double %x, i64 %a)
+  %scalbxx1 = call double @scalbln(double %scalbxx0, i64 %b)
+  ret double %scalbxx1
+}
+
+define float @scalblnf_scalblnf(float %x, i64 %a, i64 %b) {
+; CHECK-LABEL: define float @scalblnf
+; CHECK-SAME: (float [[X:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[SCALBXX0:%.*]] = call float @scalblnf(float [[X]], i64 [[A]])
+; CHECK-NEXT:    [[SCALBXX1:%.*]] = call float @scalblnf(float [[SCALBXX0]], i64 [[B]])
+; CHECK-NEXT:    ret float [[SCALBXX1]]
+;
+  %scalbxx0 = call float @scalblnf(float %x, i64 %a)
+  %scalbxx1 = call float @scalblnf(float %scalbxx0, i64 %b)
+  ret float %scalbxx1
+}
+
+define x86_fp80 @scalblnl_scalblnl(x86_fp80 %x, i64 %a, i64 %b) {
+; CHECK-LABEL: define x86_fp80 @scalblnl
+; CHECK-SAME: (x86_fp80 [[X:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[SCALBXX0:%.*]] = call x86_fp80 @scalblnl(x86_fp80 [[X]], i64 [[A]])
+; CHECK-NEXT:    [[SCALBXX1:%.*]] = call x86_fp80 @scalblnl(x86_fp80 [[SCALBXX0]], i64 [[B]])
+; CHECK-NEXT:    ret x86_fp80 [[SCALBXX1]]
+;
+  %scalbxx0 = call x86_fp80 @scalblnl(x86_fp80 %x, i64 %a)
+  %scalbxx1 = call x86_fp80 @scalblnl(x86_fp80 %scalbxx0, i64 %b)
+  ret x86_fp80 %scalbxx1
+}
+
+define double @scalbn_scalbn(double %x, i32 %a, i32 %b) {
+; CHECK-LABEL: define double @scalbn
+; CHECK-SAME: (double [[X:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[SCALBXX0:%.*]] = call double @scalbn(double [[X]], i32 [[A]])
+; CHECK-NEXT:    [[SCALBXX1:%.*]] = call double @scalbn(double [[SCALBXX0]], i32 [[B]])
+; CHECK-NEXT:    ret double [[SCALBXX1]]
+;
+  %scalbxx0 = call double @scalbn(double %x, i32 %a)
+  %scalbxx1 = call double @scalbn(double %scalbxx0, i32 %b)
+  ret double %scalbxx1
+}
+
+define float @scalbnf_scalbnf(float %x, i32 %a, i32 %b) {
+; CHECK-LABEL: define float @scalbnf
+; CHECK-SAME: (float [[X:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[SCALBXX0:%.*]] = call float @scalbnf(float [[X]], i32 [[A]])
+; CHECK-NEXT:    [[SCALBXX1:%.*]] = call float @scalbnf(float [[SCALBXX0]], i32 [[B]])
+; CHECK-NEXT:    ret float [[SCALBXX1]]
+;
+  %scalbxx0 = call float @scalbnf(float %x, i32 %a)
+  %scalbxx1 = call float @scalbnf(float %scalbxx0, i32 %b)
+  ret float %scalbxx1
+}
+
+define x86_fp80 @scalbnl_scalbnl(x86_fp80 %x, i32 %a, i32 %b) {
+; CHECK-LABEL: define x86_fp80 @scalbnl
+; CHECK-SAME: (x86_fp80 [[X:%.*]], i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT:    [[SCALBXX0:%.*]] = call x86_fp80 @scalbnl(x86_fp80 [[X]], i32 [[A]])
+; CHECK-NEXT:    [[SCALBXX1:%.*]] = call x86_fp80 @scalbnl(x86_fp80 [[SCALBXX0]], i32 [[B]])
+; CHECK-NEXT:    ret x86_fp80 [[SCALBXX1]]
+;
+  %scalbxx0 = call x86_fp80 @scalbnl(x86_fp80 %x, i32 %a)
+  %scalbxx1 = call x86_fp80 @scalbnl(x86_fp80 %scalbxx0, i32 %b)
+  ret x86_fp80 %scalbxx1
+}

@dtcxzyw dtcxzyw requested a review from arsenm October 31, 2024 15:45
@arsenm arsenm added the floating-point Floating-point math label Oct 31, 2024
Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is handled in the wrong place, and is much more complicated than necessary.

The natural place for this is in lib/Analysis/ConstantFolding.cpp. You also do not need to match all the special cases, you can just use APFloat::scalbn.

scalbn is really just an alias for ldexp anyway, so really we should just canonicalize these into the ldexp intrinsic

@llvmbot llvmbot added llvm:ir llvm:analysis Includes value tracking, cost tables and constant folding labels Nov 2, 2024
Copy link

github-actions bot commented Nov 2, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any foldable cases, you could just replace this with the ldexp intrinsic

@fawdlstty fawdlstty requested a review from arsenm November 5, 2024 16:15
Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In all of the cases where you are permitted to do this fold, we could also just replace this with the ldexp intrinsic

;
%call = call double @scalbn(double 1.0, i32 -1)
ret %call
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Negative test on overflow when errno is set

;
%call = call double @scalbn(double 1.0, i32 -1)
ret %call
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a strictfp test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
floating-point Floating-point math llvm:analysis Includes value tracking, cost tables and constant folding llvm:ir llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants