[llvm] [DAGCombiner] Fold `sub nuw C, x` -> `xor x, C` when C is a mask (PR #192692)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 09:07:25 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-selectiondag

Author: Madhur Kumar (MadhurKumar004)

<details>
<summary>Changes</summary>

fixes : https://github.com/llvm/llvm-project/issues/86874

Alive proof: https://alive2.llvm.org/ce/z/YKCPCA

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


4 Files Affected:

- (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+10) 
- (modified) llvm/test/CodeGen/AArch64/pr86717.ll (+5-6) 
- (modified) llvm/test/CodeGen/RISCV/pr84200.ll (+3-6) 
- (added) llvm/test/CodeGen/X86/sub-to-xor-masked.ll (+74) 


``````````diff
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index ec4be73a9966b..3f4e2a5b3b2f2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -4549,6 +4549,16 @@ SDValue DAGCombiner::visitSUB(SDNode *N) {
     }
   }
 
+  // sub nuw C, x --> xor x, C when C is a mask (2^k - 1)
+  if (ConstantSDNode *C0 = isConstOrConstSplat(N0)) {
+    if (N->getFlags().hasNoUnsignedWrap()) {
+      const APInt &Val = C0->getAPIntValue();
+      if (Val.isMask()) {
+        return DAG.getNode(ISD::XOR, DL, VT, N1, N0);
+      }
+    }
+  }
+
   // If there's no chance of borrowing from adjacent bits, then sub is xor:
   // sub C0, X --> xor X, C0
   if (ConstantSDNode *C0 = isConstOrConstSplat(N0)) {
diff --git a/llvm/test/CodeGen/AArch64/pr86717.ll b/llvm/test/CodeGen/AArch64/pr86717.ll
index aa8be954be72d..f1d423fb0f087 100644
--- a/llvm/test/CodeGen/AArch64/pr86717.ll
+++ b/llvm/test/CodeGen/AArch64/pr86717.ll
@@ -7,13 +7,12 @@ define <16 x i8> @f(i32 %0) {
 ; CHECK-NEXT:    sub sp, sp, #16
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
 ; CHECK-NEXT:    movi v0.2d, #0000000000000000
-; CHECK-NEXT:    mov w8, #1 // =0x1
-; CHECK-NEXT:    mov x9, sp
-; CHECK-NEXT:    sub w8, w8, w0
-; CHECK-NEXT:    bfxil x9, x8, #0, #4
-; CHECK-NEXT:    mov w8, #3 // =0x3
+; CHECK-NEXT:    mov x8, sp
+; CHECK-NEXT:    eor w9, w0, #0x1
+; CHECK-NEXT:    bfxil x8, x9, #0, #4
+; CHECK-NEXT:    mov w9, #3 // =0x3
 ; CHECK-NEXT:    str q0, [sp]
-; CHECK-NEXT:    strb w8, [x9]
+; CHECK-NEXT:    strb w9, [x8]
 ; CHECK-NEXT:    ldr q0, [sp], #16
 ; CHECK-NEXT:    ret
   %2 = sub nuw i32 1, %0
diff --git a/llvm/test/CodeGen/RISCV/pr84200.ll b/llvm/test/CodeGen/RISCV/pr84200.ll
index 19a102b84ed06..7674695790056 100644
--- a/llvm/test/CodeGen/RISCV/pr84200.ll
+++ b/llvm/test/CodeGen/RISCV/pr84200.ll
@@ -6,12 +6,9 @@
 define i64 @foo(i64 %1) {
 ; CHECK-LABEL: foo:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    li a1, 1
-; CHECK-NEXT:    sub a1, a1, a0
-; CHECK-NEXT:    sltiu a0, a0, 2
-; CHECK-NEXT:    xori a1, a1, 1
-; CHECK-NEXT:    neg a0, a0
-; CHECK-NEXT:    and a0, a0, a1
+; CHECK-NEXT:    sltiu a1, a0, 2
+; CHECK-NEXT:    neg a1, a1
+; CHECK-NEXT:    and a0, a1, a0
 ; CHECK-NEXT:    ret
 entry:
   %.urem.i = sub nuw i64 1, %1
diff --git a/llvm/test/CodeGen/X86/sub-to-xor-masked.ll b/llvm/test/CodeGen/X86/sub-to-xor-masked.ll
new file mode 100644
index 0000000000000..f8891b2804311
--- /dev/null
+++ b/llvm/test/CodeGen/X86/sub-to-xor-masked.ll
@@ -0,0 +1,74 @@
+; RUN: llc -mtriple=x86_64 < %s | FileCheck %s --check-prefix=X86
+
+define i8 @sub_nuw_mask_4bit(i8 %x) {
+; X86-LABEL: sub_nuw_mask_4bit:
+; X86:       xorb $15, %al
+  %r = sub nuw i8 15, %x
+  ret i8 %r
+}
+
+define i8 @sub_nuw_mask_7bit(i8 %x) {
+; X86-LABEL: sub_nuw_mask_7bit:
+; X86:       xorb $127, %al
+  %r = sub nuw i8 127, %x
+  ret i8 %r
+}
+
+define i8 @sub_nuw_allones_i8(i8 %x) {
+; X86-LABEL: sub_nuw_allones_i8:
+; X86:       notb %al
+  %r = sub nuw i8 255, %x
+  ret i8 %r
+}
+
+define i16 @sub_nuw_mask_i16(i16 %x) {
+; X86-LABEL: sub_nuw_mask_i16:
+; X86:       xorl $255, %eax
+  %r = sub nuw i16 255, %x
+  ret i16 %r
+}
+
+define i32 @sub_nuw_mask_i32(i32 %x) {
+; X86-LABEL: sub_nuw_mask_i32:
+; X86:       xorl $65535, %eax
+  %r = sub nuw i32 65535, %x
+  ret i32 %r
+}
+
+define i8 @sub_nuw_nonmask(i8 %x) {
+; X86-LABEL: sub_nuw_nonmask:
+; X86-NOT:   xorb
+; X86:       subb
+  %r = sub nuw i8 13, %x
+  ret i8 %r
+}
+
+define i8 @sub_no_nuw(i8 %x) {
+; X86-LABEL: sub_no_nuw:
+; X86-NOT:   xorb
+; X86:       subb
+  %r = sub i8 15, %x
+  ret i8 %r
+}
+
+define i8 @sub_nsw_only(i8 %x) {
+; X86-LABEL: sub_nsw_only:
+; X86-NOT:   xorb
+; X86:       subb
+  %r = sub nsw i8 15, %x
+  ret i8 %r
+}
+
+define i8 @sub_nuw_zero(i8 %x) {
+; X86-LABEL: sub_nuw_zero:
+; X86-NOT:   xorb
+  %r = sub nuw i8 0, %x
+  ret i8 %r
+}
+
+define <4 x i8> @sub_nuw_mask_vec_splat(<4 x i8> %x) {
+; X86-LABEL: sub_nuw_mask_vec_splat:
+; X86:       xor
+  %r = sub nuw <4 x i8> <i8 15, i8 15, i8 15, i8 15>, %x
+  ret <4 x i8> %r
+}

``````````

</details>


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


More information about the llvm-commits mailing list