[llvm] [InstCombine] Relax the requirements for sub-xor equivalence (PR #193680)

Piotr Fusik via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 00:11:57 PDT 2026


https://github.com/pfusik created https://github.com/llvm/llvm-project/pull/193680

A sub with no borrow between bits is equivalent to a xor.
A borrow occurs when lhs_bit=0 and rhs_bit=1.
It follows that lhs_bit=1 or rhs_bit=0 means no borrow.

Remove an artificial condition that LHS must be a low bits mask.

>From 29940597bcdea77da47868950b747640af61eeca Mon Sep 17 00:00:00 2001
From: Piotr Fusik <p.fusik at samsung.com>
Date: Thu, 23 Apr 2026 09:10:18 +0200
Subject: [PATCH] [InstCombine] Relax the requirements for sub-xor equivalence

A sub with no borrow between bits is equivalent to a xor.
A borrow occurs when lhs_bit=0 and rhs_bit=1.
It follows that lhs_bit=1 or rhs_bit=0 means no borrow.

Remove an artificial condition that LHS must be a low bits mask.
---
 .../InstCombine/InstCombineAddSub.cpp         | 29 +++++++++----------
 llvm/test/Transforms/InstCombine/sub-xor.ll   |  4 +--
 2 files changed, 15 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index a7ba1592ad307..3435ee1be828a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -951,13 +951,12 @@ Instruction *InstCombinerImpl::foldAddWithConstant(BinaryOperator &Add) {
     if (C2->isSignMask())
       return BinaryOperator::CreateAdd(X, ConstantInt::get(Ty, *C2 ^ *C));
 
-    // If X has no high-bits set above an xor mask:
-    // add (xor X, LowMaskC), C --> sub (LowMaskC + C), X
-    if (C2->isMask()) {
-      KnownBits LHSKnown = computeKnownBits(X, &Add);
-      if ((*C2 | LHSKnown.Zero).isAllOnes())
-        return BinaryOperator::CreateSub(ConstantInt::get(Ty, *C2 + *C), X);
-    }
+    // If X has no bits set other than an xor mask,
+    // xor is equivalent to sub with no borrow between bits:
+    // add (xor X, C2), C --> sub (C2 + C), X
+    KnownBits LHSKnown = computeKnownBits(X, &Add);
+    if ((*C2 | LHSKnown.Zero).isAllOnes())
+      return BinaryOperator::CreateSub(ConstantInt::get(Ty, *C2 + *C), X);
 
     // Look for a math+logic pattern that corresponds to sext-in-register of a
     // value with cleared high bits. Convert that into a pair of shifts:
@@ -2577,15 +2576,13 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
 
   const APInt *Op0C;
   if (match(Op0, m_APInt(Op0C))) {
-    if (Op0C->isMask()) {
-      // Turn this into a xor if LHS is 2^n-1 and the remaining bits are known
-      // zero. We don't use information from dominating conditions so this
-      // transform is easier to reverse if necessary.
-      KnownBits RHSKnown = llvm::computeKnownBits(
-          Op1, SQ.getWithInstruction(&I).getWithoutDomCondCache());
-      if ((*Op0C | RHSKnown.Zero).isAllOnes())
-        return BinaryOperator::CreateXor(Op1, Op0);
-    }
+    // Turn this into a xor if no borrow between bits.
+    // We don't use information from dominating conditions so this
+    // transform is easier to reverse if necessary.
+    KnownBits RHSKnown = llvm::computeKnownBits(
+        Op1, SQ.getWithInstruction(&I).getWithoutDomCondCache());
+    if ((*Op0C | RHSKnown.Zero).isAllOnes())
+      return BinaryOperator::CreateXor(Op1, Op0);
 
     // C - ((C3 -nuw X) & C2) --> (C - (C2 & C3)) + (X & C2) when:
     // (C3 - ((C2 & C3) - 1)) is pow2
diff --git a/llvm/test/Transforms/InstCombine/sub-xor.ll b/llvm/test/Transforms/InstCombine/sub-xor.ll
index a4135e0b51453..aa62969a3141b 100644
--- a/llvm/test/Transforms/InstCombine/sub-xor.ll
+++ b/llvm/test/Transforms/InstCombine/sub-xor.ll
@@ -28,7 +28,7 @@ define <2 x i32> @low_mask_nsw_nuw_vec(<2 x i32> %x) {
 define i8 @arbitrary_mask_sub_i8(i8 %x) {
 ; CHECK-LABEL: @arbitrary_mask_sub_i8(
 ; CHECK-NEXT:    [[A:%.*]] = and i8 [[X:%.*]], 10
-; CHECK-NEXT:    [[M:%.*]] = sub nuw nsw i8 11, [[A]]
+; CHECK-NEXT:    [[M:%.*]] = xor i8 [[A]], 11
 ; CHECK-NEXT:    ret i8 [[M]]
 ;
   %a = and i8 %x, 10 ; 0b00001010
@@ -74,7 +74,7 @@ define i8 @arbitrary_mask_sub_nuw_high_bit_dont_care_i8(i8 %x) {
 define <2 x i5> @arbitrary_mask_sub_v2i5(<2 x i5> %x) {
 ; CHECK-LABEL: @arbitrary_mask_sub_v2i5(
 ; CHECK-NEXT:    [[A:%.*]] = and <2 x i5> [[X:%.*]], splat (i5 -8)
-; CHECK-NEXT:    [[M:%.*]] = sub nuw nsw <2 x i5> splat (i5 -6), [[A]]
+; CHECK-NEXT:    [[M:%.*]] = xor <2 x i5> [[A]], splat (i5 -6)
 ; CHECK-NEXT:    ret <2 x i5> [[M]]
 ;
   %a = and <2 x i5> %x, <i5 24, i5 24> ; 0b11000



More information about the llvm-commits mailing list