[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