-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-ir @llvm/pr-subscribers-llvm-transforms Author: Fawdlstty (fawdlstty) ChangesThis patch adds constant folding support for scalbxx. part2 of #112631 Full diff: https://github.com/llvm/llvm-project/pull/114417.diff 3 Files Affected:
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
+}
|
There was a problem hiding this 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
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this 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
There was a problem hiding this 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 | ||
} |
There was a problem hiding this comment.
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a strictfp test
This patch adds constant folding support for scalbxx.
Reference: https://en.cppreference.com/w/cpp/numeric/math/scalbn
part2 of #112631