[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