[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