-
Notifications
You must be signed in to change notification settings - Fork 13.6k
InstCombine: Try to fold ldexp with select of power operand #97354
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
InstCombine: Try to fold ldexp with select of power operand #97354
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-llvm-ir Author: Matt Arsenault (arsenm) ChangesThis makes it more likely a constant value can fold into the source Full diff: https://github.com/llvm/llvm-project/pull/97354.diff 3 Files Affected:
diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h
index f81cfc0dce972..d4b7f21b60a4b 100644
--- a/llvm/include/llvm/IR/IRBuilder.h
+++ b/llvm/include/llvm/IR/IRBuilder.h
@@ -1023,6 +1023,13 @@ class IRBuilderBase {
Name);
}
+ /// Create call to the ldexp intrinsic.
+ Value *CreateLdexp(Value *Src, Value *Exp, const Twine &Name = "") {
+ assert(!IsFPConstrained && "TODO: Support strictfp");
+ return CreateIntrinsic(Intrinsic::ldexp, {Src->getType(), Exp->getType()},
+ {Src, Exp}, nullptr, Name);
+ }
+
/// Create a call to the arithmetic_fence intrinsic.
CallInst *CreateArithmeticFence(Value *Val, Type *DstType,
const Twine &Name = "") {
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 9291e6e67ef71..b6642b8bd6a39 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -2641,6 +2641,31 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
return BinaryOperator::CreateFMulFMF(Src, Select, II);
}
+ // ldexp(x, c ? exp : 0) -> c ? ldexp(x, exp) : x
+ // ldexp(x, c ? 0 : exp) -> c ? x : ldexp(x, exp)
+ ///
+ // TODO: If we cared, should insert a canonicalize for x
+ Value *SelectCond, *SelectLHS, *SelectRHS;
+ if (match(II->getArgOperand(1),
+ m_OneUse(m_Select(m_Value(SelectCond), m_Value(SelectLHS),
+ m_Value(SelectRHS))))) {
+ Value *NewLdexp = nullptr;
+ Value *Select = nullptr;
+ if (match(SelectRHS, m_ZeroInt())) {
+ NewLdexp = Builder.CreateLdexp(Src, SelectLHS);
+ Select = Builder.CreateSelect(SelectCond, NewLdexp, Src);
+ } else if (match(SelectLHS, m_ZeroInt())) {
+ NewLdexp = Builder.CreateLdexp(Src, SelectRHS);
+ Select = Builder.CreateSelect(SelectCond, Src, NewLdexp);
+ }
+
+ if (NewLdexp) {
+ Select->takeName(II);
+ cast<Instruction>(NewLdexp)->copyIRFlags(II);
+ return replaceInstUsesWith(*II, Select);
+ }
+ }
+
break;
}
case Intrinsic::ptrauth_auth:
diff --git a/llvm/test/Transforms/InstCombine/ldexp.ll b/llvm/test/Transforms/InstCombine/ldexp.ll
index 3ede7d913b525..12d160ed4f788 100644
--- a/llvm/test/Transforms/InstCombine/ldexp.ll
+++ b/llvm/test/Transforms/InstCombine/ldexp.ll
@@ -870,7 +870,7 @@ define float @ldexp_2_flags(float %x) {
define float @ldexp_metadata(float %x) {
; CHECK-LABEL: define float @ldexp_metadata
; CHECK-SAME: (float [[X:%.*]]) {
-; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 2), !foo !2
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 2), !foo [[META2:![0-9]+]]
; CHECK-NEXT: ret float [[LDEXP]]
;
%ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 2), !foo !2
@@ -889,6 +889,132 @@ define float @ldexp_8_contractable(float %x, float %y) {
ret float %fadd
}
+define float @ldexp_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_nnan_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_nnan_f32_mask_select_0
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call nnan float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_flags_f32_mask_select_0(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_flags_f32_mask_select_0
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call ninf nsz float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[TMP1]], float [[X]]
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call nsz ninf float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_swap(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select i1 [[COND]], float [[X]], float [[TMP1]]
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 0, i32 %y
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_1(i1 %cond, float %x, i32 %y) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_1
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 1
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 1
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_multi_use(i1 %cond, float %x, i32 %y, ptr addrspace(1) %ptr) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_multi_use
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: store i32 [[SELECT]], ptr addrspace(1) [[PTR]], align 4
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ store i32 %select, ptr addrspace(1) %ptr
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_swap_multi_use(i1 %cond, float %x, i32 %y, ptr addrspace(1) %ptr) {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_swap_multi_use
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]], ptr addrspace(1) [[PTR:%.*]]) {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 0, i32 [[Y]]
+; CHECK-NEXT: store i32 [[SELECT]], ptr addrspace(1) [[PTR]], align 4
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.ldexp.f32.i32(float [[X]], i32 [[SELECT]])
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 0, i32 %y
+ store i32 %select, ptr addrspace(1) %ptr
+ %ldexp = call float @llvm.ldexp.f32.i32(float %x, i32 %select)
+ ret float %ldexp
+}
+
+define float @ldexp_f32_mask_select_0_strictfp(i1 %cond, float %x, i32 %y) #0 {
+; CHECK-LABEL: define float @ldexp_f32_mask_select_0_strictfp
+; CHECK-SAME: (i1 [[COND:%.*]], float [[X:%.*]], i32 [[Y:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], i32 [[Y]], i32 0
+; CHECK-NEXT: [[LDEXP:%.*]] = call float @llvm.experimental.constrained.ldexp.f32.i32(float [[X]], i32 [[SELECT]], metadata !"round.dynamic", metadata !"fpexcept.strict")
+; CHECK-NEXT: ret float [[LDEXP]]
+;
+ %select = select i1 %cond, i32 %y, i32 0
+ %ldexp = call float @llvm.experimental.constrained.ldexp.f32.i32(float %x, i32 %select, metadata !"round.dynamic", metadata !"fpexcept.strict")
+ ret float %ldexp
+}
+
+define <2 x float> @ldexp_v2f32_mask_select_0(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0
+; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select <2 x i1> [[COND]], <2 x float> [[TMP1]], <2 x float> [[X]]
+; CHECK-NEXT: ret <2 x float> [[LDEXP]]
+;
+ %select = select <2 x i1> %cond, <2 x i32> %y, <2 x i32> zeroinitializer
+ %ldexp = call nsz nnan <2 x float> @llvm.ldexp.f32.v2i32(<2 x float> %x, <2 x i32> %select)
+ ret <2 x float> %ldexp
+}
+
+define <2 x float> @ldexp_v2f32_mask_select_0_swap(<2 x i1> %cond, <2 x float> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x float> @ldexp_v2f32_mask_select_0_swap
+; CHECK-SAME: (<2 x i1> [[COND:%.*]], <2 x float> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.ldexp.v2f32.v2i32(<2 x float> [[X]], <2 x i32> [[Y]])
+; CHECK-NEXT: [[LDEXP:%.*]] = select <2 x i1> [[COND]], <2 x float> [[X]], <2 x float> [[TMP1]]
+; CHECK-NEXT: ret <2 x float> [[LDEXP]]
+;
+ %select = select <2 x i1> %cond, <2 x i32> zeroinitializer, <2 x i32> %y
+ %ldexp = call nsz nnan <2 x float> @llvm.ldexp.f32.v2i32(<2 x float> %x, <2 x i32> %select)
+ ret <2 x float> %ldexp
+}
+
+attributes #0 = { strictfp }
+
!0 = !{i32 -127, i32 0}
!1 = !{i32 0, i32 127}
!2 = !{}
|
|
||
if (NewLdexp) { | ||
Select->takeName(II); | ||
cast<Instruction>(NewLdexp)->copyIRFlags(II); |
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.
cast<Instruction>(NewLdexp)->copyIRFlags(II); | |
cast<Instruction>(NewLdexp)->copyFastMathFlags(II); |
BTW, it will assert if NewLdexp
get folded into a constant in the future.
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.
I know, but that probably shouldn't change
This makes it more likely a constant value can fold into the source operand.
d768626
to
d7c4202
Compare
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.
LGTM.
This makes it more likely a constant value can fold into the source operand.
This makes it more likely a constant value can fold into the source operand.
This makes it more likely a constant value can fold into the source
operand.