[llvm] [InstCombine] Fold (X + C) + (Y & ~C) to X + (Y | C) (PR #191334)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 9 20:16:35 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: hanbeom (ParkHanbum)
<details>
<summary>Changes</summary>
Add an InstCombine fold for masked overwrite patterns where the add
constant matches the cleared bits in the mask:
(X + C) + (Y & ~C) -> X + (Y | C)
Since `Y & ~C` clears all bits set in C, adding C cannot generate carry
through those bits and is equivalent to setting them with `or`.
Proof: https://alive2.llvm.org/ce/z/MVyLay
Fixed: https://github.com/llvm/llvm-project/issues/191171
---
Full diff: https://github.com/llvm/llvm-project/pull/191334.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp (+34)
- (modified) llvm/test/Transforms/InstCombine/add.ll (+38)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index c781c6978b275..96010b66af468 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -1523,6 +1523,36 @@ static Instruction *foldBoxMultiply(BinaryOperator &I) {
return nullptr;
}
+static Instruction *
+foldAddWithMaskedOverwrite(BinaryOperator &Add,
+ InstCombiner::BuilderTy &Builder) {
+ Value *LHS = Add.getOperand(0), *RHS = Add.getOperand(1);
+ Value *X, *Y;
+ const APInt *C, *Mask;
+
+ auto Match = [&](Value *AddOp, Value *AndOp) -> Instruction * {
+ if (!match(AddOp, m_c_Add(m_Value(X), m_APInt(C))) ||
+ !match(AndOp, m_c_And(m_Value(Y), m_APInt(Mask))) || *Mask != ~(*C))
+ return nullptr;
+
+ // Replacing one add with {or, add}. Avoid growth if both sides are shared.
+ if (!AddOp->hasOneUse() && !AndOp->hasOneUse())
+ return nullptr;
+
+
+ Value *NewOr =
+ Builder.CreateOr(Y, Constant::getIntegerValue(Add.getType(), *C));
+ Instruction *NewAdd = cast<Instruction>(Builder.CreateAdd(X, NewOr));
+ NewAdd->setHasNoSignedWrap(Add.hasNoSignedWrap());
+ NewAdd->setHasNoUnsignedWrap(Add.hasNoUnsignedWrap());
+ return NewAdd;
+ };
+
+ if (Instruction *I = Match(LHS, RHS))
+ return I;
+ return Match(RHS, LHS);
+}
+
Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Value *V = simplifyAddInst(I.getOperand(0), I.getOperand(1),
I.hasNoSignedWrap(), I.hasNoUnsignedWrap(),
@@ -1604,6 +1634,10 @@ Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Value *V = checkForNegativeOperand(I, Builder))
return replaceInstUsesWith(I, V);
+ // (X + C) + (Y & ~C) == X + (Y | C)
+ if (Value *V = foldAddWithMaskedOverwrite(I, Builder))
+ return replaceInstUsesWith(I, V);
+
// (A + 1) + ~B --> A - B
// ~B + (A + 1) --> A - B
// (~B + A) + 1 --> A - B
diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index aa68dfb540064..3899fe88eb0cb 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -5,6 +5,44 @@
declare void @use(i8)
declare void @use_i1(i1)
+; fold (X + C) + (Y & ~C) -> X + (Y | C)
+
+define i32 @add_masked_overwrite_basic(i32 %x, i32 %y) {
+; CHECK-LABEL: @add_masked_overwrite_basic(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], 1
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], [[OR]]
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %a = add i32 %x, 1
+ %b = and i32 %y, -2
+ %r = add i32 %a, %b
+ ret i32 %r
+}
+
+define i32 @add_masked_overwrite_commuted(i32 %x, i32 %y) {
+; CHECK-LABEL: @add_masked_overwrite_commuted(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], 1
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], [[OR]]
+; CHECK-NEXT: ret i32 [[ADD]]
+;
+ %a = add i32 %x, 1
+ %b = and i32 %y, -2
+ %r = add i32 %b, %a
+ ret i32 %r
+}
+
+define <4 x i32> @add_masked_overwrite_vec_splat(<4 x i32> %x, <4 x i32> %y) {
+; CHECK-LABEL: @add_masked_overwrite_vec_splat(
+; CHECK-NEXT: [[OR:%.*]] = or <4 x i32> [[Y:%.*]], splat (i32 1)
+; CHECK-NEXT: [[ADD:%.*]] = add <4 x i32> [[X:%.*]], [[OR]]
+; CHECK-NEXT: ret <4 x i32> [[ADD]]
+;
+ %a = add <4 x i32> %x, splat (i32 1)
+ %b = and <4 x i32> %y, splat (i32 -2)
+ %r = add <4 x i32> %a, %b
+ ret <4 x i32> %r
+}
+
define i32 @select_0_or_1_from_bool(i1 %x) {
; CHECK-LABEL: @select_0_or_1_from_bool(
; CHECK-NEXT: [[NOT_X:%.*]] = xor i1 [[X:%.*]], true
``````````
</details>
https://github.com/llvm/llvm-project/pull/191334
More information about the llvm-commits
mailing list