[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