Skip to content

Conversation

@AZero13
Copy link
Contributor

@AZero13 AZero13 commented Nov 26, 2024

Alive2 Proof:
https://alive2.llvm.org/ce/z/gS9Wb7

This is based on and closes #108451

Note that no nsw or nuw saving works because they all result in poison or undef mismatches.

@AZero13 AZero13 requested a review from nikic as a code owner November 26, 2024 15:16
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Nov 26, 2024
@AZero13 AZero13 changed the title [InstCombine] Fold X - Y + Y * N -> X + Y * (N - 1) [InstCombine] Fold X - Y + Y * C -> X + Y * (C - 1) Nov 26, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Rose (AreaZR)

Changes

Alive2 Proof:
https://alive2.llvm.org/ce/z/gS9Wb7

This is based on and closes #108451

Note that no nsw or nuw saving works because they all result in poison or undef mismatches.


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

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp (+12)
  • (modified) llvm/test/Transforms/InstCombine/addsub-constant-folding.ll (+104)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 6fe96935818531..b127f88090e289 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1612,6 +1612,18 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
       Value *Sub = Builder.CreateSub(B, A, "reass.sub");
       return BinaryOperator::CreateAdd(Sub, C1);
     }
+
+    // A - B + B * C -> A + B * (C - 1)
+    const APInt *C;
+    if (((match(LHS, m_Sub(m_Value(A), m_Value(B))) &&
+          match(RHS, m_Mul(m_Specific(B), m_APInt(C)))) ||
+         (match(RHS, m_Sub(m_Value(A), m_Value(B))) &&
+          match(LHS, m_Mul(m_Specific(B), m_APInt(C))))) &&
+        LHS->hasOneUse() && RHS->hasOneUse()) {
+      Value *Mul =
+          Builder.CreateMul(B, ConstantInt::get(RHS->getType(), *C - 1));
+      return BinaryOperator::CreateAdd(A, Mul);
+    }
   }
 
   // X % C0 + (( X / C0 ) % C1) * C0 => X % (C0 * C1)
diff --git a/llvm/test/Transforms/InstCombine/addsub-constant-folding.ll b/llvm/test/Transforms/InstCombine/addsub-constant-folding.ll
index b38466f6cf0ec2..26b4ff3e400198 100644
--- a/llvm/test/Transforms/InstCombine/addsub-constant-folding.ll
+++ b/llvm/test/Transforms/InstCombine/addsub-constant-folding.ll
@@ -750,3 +750,107 @@ define i8 @sub_from_constant_extra_use(i8 %x, i8 %y) {
   %r = add i8 %sub, %y
   ret i8 %r
 }
+
+define i32 @sub_plus_mul(i32 %x, i32 %y) {
+; CHECK-LABEL: @sub_plus_mul(
+; CHECK-NEXT:    [[B:%.*]] = mul i32 [[Y:%.*]], 9
+; CHECK-NEXT:    [[C:%.*]] = add i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[C]]
+;
+  %a = sub i32 %x, %y
+  %b = mul i32 %y, 10
+  %c = add i32 %a, %b
+  ret i32 %c
+}
+
+define i32 @sub_plus_mul_2(i32 %x, i32 %y) {
+; CHECK-LABEL: @sub_plus_mul_2(
+; CHECK-NEXT:    [[B:%.*]] = mul i32 [[Y:%.*]], -11
+; CHECK-NEXT:    [[C:%.*]] = add i32 [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret i32 [[C]]
+;
+  %a = sub i32 %x, %y
+  %b = mul i32 %y, -10
+  %c = add i32 %a, %b
+  ret i32 %c
+}
+
+define i32 @sub_plus_mul3(i32 %x, i32 %y) {
+; CHECK-LABEL: @sub_plus_mul3(
+; CHECK-NEXT:    [[A:%.*]] = mul i32 [[Y:%.*]], 9
+; CHECK-NEXT:    [[C:%.*]] = add i32 [[B:%.*]], [[A]]
+; CHECK-NEXT:    ret i32 [[C]]
+;
+  %a = mul i32 %y, 10
+  %b = sub i32 %x, %y
+  %c = add i32 %b, %a
+  ret i32 %c
+}
+
+define i32 @sub_plus_mul4(i32 %x, i32 %y) {
+; CHECK-LABEL: @sub_plus_mul4(
+; CHECK-NEXT:    [[A:%.*]] = mul i32 [[Y:%.*]], -11
+; CHECK-NEXT:    [[C:%.*]] = add i32 [[B:%.*]], [[A]]
+; CHECK-NEXT:    ret i32 [[C]]
+;
+  %a = mul i32 %y, -10
+  %b = sub i32 %x, %y
+  %c = add i32 %b, %a
+  ret i32 %c
+}
+
+define <2 x i8> @sub_plus_mul_splat(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: @sub_plus_mul_splat(
+; CHECK-NEXT:    [[B:%.*]] = mul <2 x i8> [[Y:%.*]], splat (i8 6)
+; CHECK-NEXT:    [[C:%.*]] = add <2 x i8> [[A:%.*]], [[B]]
+; CHECK-NEXT:    ret <2 x i8> [[C]]
+;
+  %a = sub <2 x i8> %x, %y
+  %b = mul <2 x i8> %y, splat(i8 7)
+  %c = add <2 x i8> %a, %b
+  ret <2 x i8> %c
+}
+
+;; <- Negative Tests ->
+
+;; mul does not work if it is a powerof2, because it results in worse codegen
+define i32 @neg_sub_plus_mul_pow2(i32 %x, i32 %y) {
+; CHECK-LABEL: @neg_sub_plus_mul_pow2(
+; CHECK-NEXT:    [[A:%.*]] = sub i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[B:%.*]] = shl i32 [[Y]], 3
+; CHECK-NEXT:    [[C:%.*]] = add i32 [[A]], [[B]]
+; CHECK-NEXT:    ret i32 [[C]]
+;
+  %a = sub i32 %x, %y
+  %b = mul i32 %y, 8
+  %c = add i32 %a, %b
+  ret i32 %c
+}
+
+define <2 x i8> @sub_plus_mul_splat_shl(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: @sub_plus_mul_splat_shl(
+; CHECK-NEXT:    [[A:%.*]] = sub <2 x i8> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[B:%.*]] = shl <2 x i8> [[Y]], splat (i8 3)
+; CHECK-NEXT:    [[C:%.*]] = add <2 x i8> [[A]], [[B]]
+; CHECK-NEXT:    ret <2 x i8> [[C]]
+;
+  %a = sub <2 x i8> %x, %y
+  %b = mul <2 x i8> %y, splat(i8 8)
+  %c = add <2 x i8> %a, %b
+  ret <2 x i8> %c
+}
+
+define i32 @neg_sub_plus_mul_multiuse(i32 %x, i32 %y) {
+; CHECK-LABEL: @neg_sub_plus_mul_multiuse(
+; CHECK-NEXT:    [[A:%.*]] = sub i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[B:%.*]] = mul i32 [[Y]], 7
+; CHECK-NEXT:    call void @use(i32 [[B]])
+; CHECK-NEXT:    [[C:%.*]] = add i32 [[A]], [[B]]
+; CHECK-NEXT:    ret i32 [[C]]
+;
+  %a = sub i32 %x, %y
+  %b = mul i32 %y, 7
+  call void @use(i32 %b)
+  %c = add i32 %a, %b
+  ret i32 %c
+}

@AZero13 AZero13 force-pushed the saturated-add branch 2 times, most recently from 911129a to 8e48b77 Compare November 26, 2024 15:18
Alive2 Proof:
https://alive2.llvm.org/ce/z/gS9Wb7

This is based on and closes llvm#108451

Note that no nsw or nuw saving works because they all result in poison or undef mismatches.
@AZero13
Copy link
Contributor Author

AZero13 commented Nov 26, 2024

cc @regehr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missed optimization: X - Y + Y * N = X + Y * (N -1)

2 participants