[llvm] [InstCombine] Fold select with signbit idiom into fabs (PR #76342)

via llvm-commits llvm-commits at lists.llvm.org
Sun Dec 24 14:50:32 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

This patch folds:
```
((bitcast X to int) <s 0 ? -X : X) -> fabs(X)
((bitcast X to int) >s -1 ? X : -X) -> fabs(X)
((bitcast X to int) <s 0 ? X : -X) -> -fabs(X)
((bitcast X to int) >s -1 ? -X : X) -> -fabs(X)
```
Alive2: https://alive2.llvm.org/ce/z/rGepow

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


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+35) 
- (modified) llvm/test/Transforms/InstCombine/fabs.ll (+187) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 20bf00344b144b..53ed0e528cfc09 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2763,6 +2763,41 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
     }
   }
 
+  // Match select with (icmp slt (bitcast X to int), 0)
+  //                or (icmp sgt (bitcast X to int), -1)
+  if (ICmpInst::makeCmpResultType(SI.getType()) != CondVal->getType())
+    return ChangedFMF ? &SI : nullptr;
+
+  for (bool Swap : {false, true}) {
+    Value *TrueVal = SI.getTrueValue();
+    Value *X = SI.getFalseValue();
+
+    if (Swap)
+      std::swap(TrueVal, X);
+
+    CmpInst::Predicate Pred;
+    const APInt *C;
+    bool TrueIfSigned;
+    if (!match(CondVal, m_ICmp(Pred, m_BitCast(m_Specific(X)), m_APInt(C))) ||
+        !IC.isSignBitCheck(Pred, *C, TrueIfSigned))
+      continue;
+    if (!match(TrueVal, m_FNeg(m_Specific(X))))
+      return nullptr;
+    if (!CondVal->hasOneUse() && !TrueVal->hasOneUse())
+      return nullptr;
+
+    // Fold (IsSigned ? -X : X) or (!IsSigned ? X : -X) to fabs(X)
+    // Fold (IsSigned ? X : -X) or (!IsSigned ? -X : X) to -fabs(X)
+    Value *Fabs = IC.Builder.CreateUnaryIntrinsic(Intrinsic::fabs, X, &SI);
+    if (Swap != TrueIfSigned)
+      return IC.replaceInstUsesWith(SI, Fabs);
+    else {
+      Instruction *NewFNeg = UnaryOperator::CreateFNeg(Fabs);
+      NewFNeg->setFastMathFlags(SI.getFastMathFlags());
+      return NewFNeg;
+    }
+  }
+
   return ChangedFMF ? &SI : nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/fabs.ll b/llvm/test/Transforms/InstCombine/fabs.ll
index acffc88380954c..080660b16f1e78 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -15,6 +15,7 @@ declare float @llvm.fma.f32(float, float, float)
 declare float @llvm.fmuladd.f32(float, float, float)
 
 declare void @use(float)
+declare void @usebool(i1)
 
 define float @replace_fabs_call_f32(float %x) {
 ; CHECK-LABEL: @replace_fabs_call_f32(
@@ -1034,3 +1035,189 @@ define <2 x float> @select_fneg_vec(<2 x i1> %c, <2 x float> %x) {
   %fabs = call <2 x float> @llvm.fabs.v2f32(<2 x float> %s)
   ret <2 x float> %fabs
 }
+
+define float @test_select_neg_negx_x(float %value) {
+; CHECK-LABEL: @test_select_neg_negx_x(
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = call float @llvm.fabs.f32(float [[VALUE:%.*]])
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_nneg_negx_x(float %value) {
+; CHECK-LABEL: @test_select_nneg_negx_x(
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[VALUE:%.*]])
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp sgt i32 %a0, -1
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_x_negx(float %value) {
+; CHECK-LABEL: @test_select_neg_x_negx(
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[VALUE:%.*]])
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %value, float %fneg.i
+  ret float %value.addr.0.i
+}
+
+define float @test_select_nneg_x_negx(float %value) {
+; CHECK-LABEL: @test_select_nneg_x_negx(
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = call float @llvm.fabs.f32(float [[VALUE:%.*]])
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp sgt i32 %a0, -1
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %value, float %fneg.i
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_negx_x_multiuse1(float %value) {
+; CHECK-LABEL: @test_select_neg_negx_x_multiuse1(
+; CHECK-NEXT:    [[A0:%.*]] = bitcast float [[VALUE:%.*]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = icmp slt i32 [[A0]], 0
+; CHECK-NEXT:    call void @usebool(i1 [[A1]])
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = call float @llvm.fabs.f32(float [[VALUE]])
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  call void @usebool(i1 %a1)
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_negx_x_multiuse2(float %value) {
+; CHECK-LABEL: @test_select_neg_negx_x_multiuse2(
+; CHECK-NEXT:    [[FNEG_I:%.*]] = fneg float [[VALUE:%.*]]
+; CHECK-NEXT:    call void @use(float [[FNEG_I]])
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = call float @llvm.fabs.f32(float [[VALUE]])
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %value
+  call void @use(float %fneg.i)
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_negx_x_fmf(float %value) {
+; CHECK-LABEL: @test_select_neg_negx_x_fmf(
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = call nnan ninf nsz float @llvm.fabs.f32(float [[VALUE:%.*]])
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select nsz nnan ninf i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_nneg_negx_x_fmf(float %value) {
+; CHECK-LABEL: @test_select_nneg_negx_x_fmf(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan ninf nsz float @llvm.fabs.f32(float [[VALUE:%.*]])
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = fneg nnan ninf nsz float [[TMP1]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp sgt i32 %a0, -1
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select nsz nnan ninf i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+; Negative tests
+define float @test_select_neg_negx_x_multiuse3(float %value) {
+; CHECK-LABEL: @test_select_neg_negx_x_multiuse3(
+; CHECK-NEXT:    [[A0:%.*]] = bitcast float [[VALUE:%.*]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = icmp slt i32 [[A0]], 0
+; CHECK-NEXT:    call void @usebool(i1 [[A1]])
+; CHECK-NEXT:    [[FNEG_I:%.*]] = fneg float [[VALUE]]
+; CHECK-NEXT:    call void @use(float [[FNEG_I]])
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = select i1 [[A1]], float [[FNEG_I]], float [[VALUE]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  call void @usebool(i1 %a1)
+  %fneg.i = fneg float %value
+  call void @use(float %fneg.i)
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_negx_x_mismatched1(float %value, float %y) {
+; CHECK-LABEL: @test_select_neg_negx_x_mismatched1(
+; CHECK-NEXT:    [[A0:%.*]] = bitcast float [[Y:%.*]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = icmp slt i32 [[A0]], 0
+; CHECK-NEXT:    [[FNEG_I:%.*]] = fneg float [[VALUE:%.*]]
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = select i1 [[A1]], float [[FNEG_I]], float [[VALUE]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %y to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_negx_x_mismatched2(float %value, float %y) {
+; CHECK-LABEL: @test_select_neg_negx_x_mismatched2(
+; CHECK-NEXT:    [[A0:%.*]] = bitcast float [[VALUE:%.*]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = icmp slt i32 [[A0]], 0
+; CHECK-NEXT:    [[FNEG_I:%.*]] = fneg float [[Y:%.*]]
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = select i1 [[A1]], float [[FNEG_I]], float [[VALUE]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %y
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %value
+  ret float %value.addr.0.i
+}
+
+define float @test_select_neg_negx_x_mismatched3(float %value, float %y) {
+; CHECK-LABEL: @test_select_neg_negx_x_mismatched3(
+; CHECK-NEXT:    [[A0:%.*]] = bitcast float [[VALUE:%.*]] to i32
+; CHECK-NEXT:    [[A1:%.*]] = icmp slt i32 [[A0]], 0
+; CHECK-NEXT:    [[FNEG_I:%.*]] = fneg float [[VALUE]]
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = select i1 [[A1]], float [[FNEG_I]], float [[Y:%.*]]
+; CHECK-NEXT:    ret float [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast float %value to i32
+  %a1 = icmp slt i32 %a0, 0
+  %fneg.i = fneg float %value
+  %value.addr.0.i = select i1 %a1, float %fneg.i, float %y
+  ret float %value.addr.0.i
+}
+
+define <2 x float> @test_select_neg_negx_x_wrong_type(<2 x float> %value) {
+; CHECK-LABEL: @test_select_neg_negx_x_wrong_type(
+; CHECK-NEXT:    [[A0:%.*]] = bitcast <2 x float> [[VALUE:%.*]] to i64
+; CHECK-NEXT:    [[A1:%.*]] = icmp slt i64 [[A0]], 0
+; CHECK-NEXT:    [[FNEG_I:%.*]] = fneg <2 x float> [[VALUE]]
+; CHECK-NEXT:    [[VALUE_ADDR_0_I:%.*]] = select i1 [[A1]], <2 x float> [[FNEG_I]], <2 x float> [[VALUE]]
+; CHECK-NEXT:    ret <2 x float> [[VALUE_ADDR_0_I]]
+;
+  %a0 = bitcast <2 x float> %value to i64
+  %a1 = icmp slt i64 %a0, 0
+  %fneg.i = fneg <2 x float> %value
+  %value.addr.0.i = select i1 %a1, <2 x float> %fneg.i, <2 x float> %value
+  ret <2 x float> %value.addr.0.i
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/76342


More information about the llvm-commits mailing list