[llvm] InstCombine: Try to fold ldexp with select of power operand (PR #97354)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 1 14:46:38 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Matt Arsenault (arsenm)
<details>
<summary>Changes</summary>
This makes it more likely a constant value can fold into the source
operand.
---
Full diff: https://github.com/llvm/llvm-project/pull/97354.diff
3 Files Affected:
- (modified) llvm/include/llvm/IR/IRBuilder.h (+7)
- (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+25)
- (modified) llvm/test/Transforms/InstCombine/ldexp.ll (+127-1)
``````````diff
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 = !{}
``````````
</details>
https://github.com/llvm/llvm-project/pull/97354
More information about the llvm-commits
mailing list