[llvm] [GlobalISel] Add `or_and_xor_to_or` pattern from SelectionDAG (PR #201108)

Osman Yasar via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 2 05:54:21 PDT 2026


https://github.com/osmanyasar05 created https://github.com/llvm/llvm-project/pull/201108

This PR adds the `fold (or (and X, (xor Y, -1)), Y) -> (or X, Y)` pattern from SelectionDAG to GlobalISel.

>From c8b8ff9c99a8c5073534d1c678275b1f7832dbc0 Mon Sep 17 00:00:00 2001
From: osmanyasar05 <osmanyas05 at gmail.com>
Date: Tue, 2 Jun 2026 13:04:40 +0100
Subject: [PATCH] add rewrite

---
 .../include/llvm/Target/GlobalISel/Combine.td | 10 +++-
 .../AArch64/GlobalISel/combine-or-and-xor.ll  | 46 ++++++++++++++++
 .../AArch64/GlobalISel/combine-or-and-xor.mir | 52 +++++++++++++++++++
 3 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.ll
 create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.mir

diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index cf2195b421cab..911b58e820314 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1270,6 +1270,14 @@ def and_or_disjoint_mask : GICombineRule<
          [{ return Helper.matchAndOrDisjointMask(*${root}, ${info}); }]),
   (apply [{ Helper.applyBuildFnNoErase(*${root}, ${info}); }])>;
 
+// fold (or (and X, (xor Y, -1)), Y) -> (or X, Y)
+def or_and_xor_to_or : GICombineRule<
+  (defs root:$root),
+  (match (G_XOR $not, $y, -1),
+         (G_AND $and, $x, $not),
+         (G_OR $root, $and, $y)),
+  (apply (G_OR $root, $x, $y))>;
+
 def bitfield_extract_from_and : GICombineRule<
   (defs root:$root, build_fn_matchinfo:$info),
   (match (G_CONSTANT $mask, $imm2),
@@ -2458,7 +2466,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
     intrem_combines, intdiv_combines, fdiv_repeated_divison,
     sub_add_reg, select_to_minmax,
     fsub_to_fneg, commute_constant_to_rhs, match_ands, match_ors,
-    simplify_neg_minmax, combine_concat_vector,
+    or_and_xor_to_or, simplify_neg_minmax, combine_concat_vector,
     sext_trunc, zext_trunc, prefer_sign_combines, shuffle_combines,
     combine_use_vector_truncate, merge_combines, overflow_combines,
     truncsat_combines, lshr_of_trunc_of_lshr, ctls_combines, add_shift, sub_one_from_sub,
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.ll b/llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.ll
new file mode 100644
index 0000000000000..0b0994983ba34
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SD
+; RUN: llc -mtriple=aarch64 -global-isel -verify-machineinstrs %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-GI
+
+define i32 @or_and_xor(i32 %x, i32 %y) {
+; CHECK-LABEL: or_and_xor:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    orr w0, w0, w1
+; CHECK-NEXT:    ret
+entry:
+; fold (or (and X, (xor Y, -1)), Y) -> (or X, Y)
+  %not = xor i32 %y, -1
+  %and = and i32 %x, %not
+  %or = or i32 %and, %y
+  ret i32 %or
+}
+
+define i64 @or_and_xor_i64(i64 %x, i64 %y) {
+; CHECK-LABEL: or_and_xor_i64:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    orr x0, x0, x1
+; CHECK-NEXT:    ret
+entry:
+; fold (or (and X, (xor Y, -1)), Y) -> (or X, Y)
+  %not = xor i64 %y, -1
+  %and = and i64 %x, %not
+  %or = or i64 %and, %y
+  ret i64 %or
+}
+
+define <4 x i32> @or_and_xor_vector(<4 x i32> %x, <4 x i32> %y) {
+; CHECK-LABEL: or_and_xor_vector:
+; CHECK:       // %bb.0: // %entry
+; CHECK-NEXT:    orr v0.16b, v0.16b, v1.16b
+; CHECK-NEXT:    ret
+entry:
+; fold (or (and X, (xor Y, -1)), Y) -> (or X, Y)
+  %not = xor <4 x i32> %y, <i32 -1, i32 -1, i32 -1, i32 -1>
+  %and = and <4 x i32> %x, %not
+  %or = or <4 x i32> %and, %y
+  ret <4 x i32> %or
+}
+
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK-GI: {{.*}}
+; CHECK-SD: {{.*}}
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.mir
new file mode 100644
index 0000000000000..87f97bf630ba4
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-or-and-xor.mir
@@ -0,0 +1,52 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs -mtriple aarch64-unknown-unknown %s -o - | FileCheck %s
+
+---
+name:            or_and_xor
+tracksRegLiveness: true
+machineFunctionInfo: {}
+body:             |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: or_and_xor
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %x:_(s32) = COPY $w0
+    ; CHECK-NEXT: %y:_(s32) = COPY $w1
+    ; CHECK-NEXT: %or:_(s32) = G_OR %x, %y
+    ; CHECK-NEXT: $w0 = COPY %or(s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %x:_(s32) = COPY $w0
+    %y:_(s32) = COPY $w1
+    %negone:_(s32) = G_CONSTANT i32 -1
+    %not:_(s32) = G_XOR %y, %negone
+    %and:_(s32) = G_AND %x, %not
+    %or:_(s32) = G_OR %and, %y
+    $w0 = COPY %or(s32)
+    RET_ReallyLR implicit $w0
+...
+---
+name:            or_and_xor_vector
+tracksRegLiveness: true
+machineFunctionInfo: {}
+body:             |
+  bb.0:
+    liveins: $q0, $q1
+    ; CHECK-LABEL: name: or_and_xor_vector
+    ; CHECK: liveins: $q0, $q1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %x:_(<4 x s32>) = COPY $q0
+    ; CHECK-NEXT: %y:_(<4 x s32>) = COPY $q1
+    ; CHECK-NEXT: %or:_(<4 x s32>) = G_OR %x, %y
+    ; CHECK-NEXT: $q0 = COPY %or(<4 x s32>)
+    ; CHECK-NEXT: RET_ReallyLR implicit $q0
+    %x:_(<4 x s32>) = COPY $q0
+    %y:_(<4 x s32>) = COPY $q1
+    %negone:_(s32) = G_CONSTANT i32 -1
+    %splat:_(<4 x s32>) = G_BUILD_VECTOR %negone, %negone, %negone, %negone
+    %not:_(<4 x s32>) = G_XOR %y, %splat
+    %and:_(<4 x s32>) = G_AND %x, %not
+    %or:_(<4 x s32>) = G_OR %and, %y
+    $q0 = COPY %or(<4 x s32>)
+    RET_ReallyLR implicit $q0
+...



More information about the llvm-commits mailing list