[llvm] [AArch64][GlobalISel] Add select to and combines (PR #200131)
David Green via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 3 00:52:47 PDT 2026
https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/200131
>From b4885de4ab7e0e756c1f19d1a3bfa77ed0aa4542 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Thu, 28 May 2026 09:19:02 +0100
Subject: [PATCH] [AArch64][GlobalISel] Add select to and combines
This adds combines for
// select c, x, 0 -> and c, x
// select c, 0, x -> and (not c), x
// select (not c), x, y -> select c, y, x
We need to freeze the value in the first two. The second is only profitable if
hasAndNot, so it excluded from all_combines.
https://alive2.llvm.org/ce/z/eG-aHT
This helps eleviate regressions when G_SELECT is made legal for vector
operations under AArch64. The AMD tests I am not sure about - let me know if
they look worse.
---
.../include/llvm/Target/GlobalISel/Combine.td | 31 +++++++-
llvm/lib/Target/AArch64/AArch64Combine.td | 4 +-
.../GlobalISel/combine-select-zero.mir | 73 +++++++++++++++++++
...-divergent-i1-phis-no-lane-mask-merging.ll | 19 ++---
...vergence-divergent-i1-used-outside-loop.ll | 30 ++++----
.../divergence-temporal-divergent-i1.ll | 6 +-
6 files changed, 135 insertions(+), 28 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/combine-select-zero.mir
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 5f8213d9c7ec3..5f43c73f07494 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -533,6 +533,35 @@ def select_constant_cmp: GICombineRule<
(apply [{ Helper.replaceSingleDefInstWithOperand(*${root}, ${matchinfo}); }])
>;
+// select c, 0, x -> and (not c), x
+def select_zero_true: GICombineRule<
+ (defs root:$root),
+ (match (G_SELECT $dst, $c, 0, $x):$root,
+ [{ return MRI.getType(${c}.getReg()) == MRI.getType(${dst}.getReg()) &&
+ VT->computeNumSignBits(${c}.getReg()) == MRI.getType(${dst}.getReg()).getScalarSizeInBits(); }]),
+ (apply (G_XOR $xor, $c, -1),
+ (G_FREEZE $f, $x),
+ (G_AND $dst, $xor, $f))
+>;
+
+// select c, x, 0 -> and c, x
+def select_zero_false: GICombineRule<
+ (defs root:$root),
+ (match (G_SELECT $dst, $c, $x, 0):$root,
+ [{ return MRI.getType(${c}.getReg()) == MRI.getType(${dst}.getReg()) &&
+ VT->computeNumSignBits(${c}.getReg()) == MRI.getType(${dst}.getReg()).getScalarSizeInBits(); }]),
+ (apply (G_FREEZE $f, $x),
+ (G_AND $dst, $c, $f))
+>;
+
+// select (not c), x, y -> select c, y, x
+def select_not: GICombineRule<
+ (defs root:$root),
+ (match (G_XOR $c, $src, -1),
+ (G_SELECT $dst, $c, $x, $y):$root),
+ (apply (G_SELECT $dst, $src, $y, $x))
+>;
+
// Fold (C op x) -> (x op C)
// TODO: handle more isCommutable opcodes
// TODO: handle compares (currently not marked as isCommutable)
@@ -2452,7 +2481,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
simplify_add_to_sub, hoist_logic_op_with_same_opcode_hands, shifts_too_big,
reassocs, ptr_add_immed_chain, cmp_combines,
shl_ashr_to_sext_inreg, neg_and_one_to_sext_inreg, sext_inreg_of_load,
- width_reduction_combines, select_combines,
+ width_reduction_combines, select_combines, select_zero_false, select_not,
known_bits_simplifications, trunc_shift,
not_cmp_fold, opt_brcond_by_inverting_cond,
const_combines, xor_of_and_with_same_reg, ptr_add_with_zero,
diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td
index 44b86dac27df2..a9c447336cd5e 100644
--- a/llvm/lib/Target/AArch64/AArch64Combine.td
+++ b/llvm/lib/Target/AArch64/AArch64Combine.td
@@ -70,6 +70,7 @@ def simplify_uaddo : GICombineRule<
def AArch64PreLegalizerCombiner: GICombiner<
"AArch64PreLegalizerCombinerImpl", [all_combines,
+ select_zero_true,
combine_shuffle_vector,
icmp_redundant_trunc,
fold_global_offset,
@@ -384,7 +385,8 @@ def AArch64PostLegalizerCombiner
mul_const, redundant_sext_inreg,
form_bitfield_extract, rotate_out_of_range,
icmp_to_true_false_known_bits, overflow_combines,
- select_combines, fold_merge_to_zext, merge_combines,
+ select_combines, select_zero_true, select_zero_false, select_not,
+ fold_merge_to_zext, merge_combines,
constant_fold_binops, identity_combines,
ptr_add_immed_chain, overlapping_and,
split_store_zero_128, undef_combines,
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-select-zero.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-select-zero.mir
new file mode 100644
index 0000000000000..5a51136041a33
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-select-zero.mir
@@ -0,0 +1,73 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple aarch64 -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name: combine_select_zero_false
+body: |
+ bb.1:
+ liveins: $d0, $d1
+ ; CHECK-LABEL: name: combine_select_zero_false
+ ; CHECK: liveins: $d0, $d1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %x:_(<2 x i32>) = COPY $d0
+ ; CHECK-NEXT: %y:_(<2 x i32>) = COPY $d1
+ ; CHECK-NEXT: %c:_(<2 x i32>) = G_ICMP intpred(eq), %x(<2 x i32>), %y
+ ; CHECK-NEXT: [[FREEZE:%[0-9]+]]:_(<2 x i32>) = G_FREEZE %x
+ ; CHECK-NEXT: %s:_(<2 x i32>) = G_AND %c, [[FREEZE]]
+ ; CHECK-NEXT: $d0 = COPY %s(<2 x i32>)
+ %x:_(<2 x i32>) = COPY $d0
+ %y:_(<2 x i32>) = COPY $d1
+ %c:_(<2 x i32>) = G_ICMP intpred(eq), %x:_(<2 x i32>), %y
+ %zero_scalar:_(i32) = G_CONSTANT i32 0
+ %zero:_(<2 x i32>) = G_BUILD_VECTOR %zero_scalar(i32), %zero_scalar(i32)
+ %s:_(<2 x i32>) = G_SELECT %c, %x, %zero
+ $d0 = COPY %s(<2 x i32>)
+...
+---
+name: combine_select_zero_true
+body: |
+ bb.1:
+ liveins: $d0, $d1
+ ; CHECK-LABEL: name: combine_select_zero_true
+ ; CHECK: liveins: $d0, $d1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %x:_(<2 x i32>) = COPY $d0
+ ; CHECK-NEXT: %y:_(<2 x i32>) = COPY $d1
+ ; CHECK-NEXT: %c:_(<2 x i32>) = G_ICMP intpred(ne), %x(<2 x i32>), %y
+ ; CHECK-NEXT: [[FREEZE:%[0-9]+]]:_(<2 x i32>) = G_FREEZE %x
+ ; CHECK-NEXT: %s:_(<2 x i32>) = G_AND %c, [[FREEZE]]
+ ; CHECK-NEXT: $d0 = COPY %s(<2 x i32>)
+ %x:_(<2 x i32>) = COPY $d0
+ %y:_(<2 x i32>) = COPY $d1
+ %c:_(<2 x i32>) = G_ICMP intpred(eq), %x:_(<2 x i32>), %y
+ %zero_scalar:_(i32) = G_CONSTANT i32 0
+ %zero:_(<2 x i32>) = G_BUILD_VECTOR %zero_scalar(i32), %zero_scalar(i32)
+ %s:_(<2 x i32>) = G_SELECT %c, %zero, %x
+ $d0 = COPY %s(<2 x i32>)
+...
+---
+name: combine_select_not
+body: |
+ bb.1:
+ liveins: $d0, $d1
+ ; CHECK-LABEL: name: combine_select_not
+ ; CHECK: liveins: $d0, $d1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %x:_(<2 x i32>) = COPY $d0
+ ; CHECK-NEXT: %y:_(<2 x i32>) = COPY $d1
+ ; CHECK-NEXT: %s31:_(i32) = G_CONSTANT i32 31
+ ; CHECK-NEXT: %bv31:_(<2 x i32>) = G_BUILD_VECTOR %s31(i32), %s31(i32)
+ ; CHECK-NEXT: %c:_(<2 x i32>) = G_ASHR %x, %bv31(<2 x i32>)
+ ; CHECK-NEXT: %s:_(<2 x i32>) = G_SELECT %c(<2 x i32>), %y, %x
+ ; CHECK-NEXT: $d0 = COPY %s(<2 x i32>)
+ %x:_(<2 x i32>) = COPY $d0
+ %y:_(<2 x i32>) = COPY $d1
+ %s31:_(i32) = G_CONSTANT i32 31
+ %bv31:_(<2 x i32>) = G_BUILD_VECTOR %s31(i32), %s31(i32)
+ %c:_(<2 x i32>) = G_ASHR %x, %bv31
+ %m1s:_(i32) = G_CONSTANT i32 -1
+ %m1:_(<2 x i32>) = G_BUILD_VECTOR %m1s(i32), %m1s(i32)
+ %n:_(<2 x i32>) = G_XOR %c, %m1
+ %s:_(<2 x i32>) = G_SELECT %n, %x, %y
+ $d0 = COPY %s(<2 x i32>)
+...
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-phis-no-lane-mask-merging.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-phis-no-lane-mask-merging.ll
index f14738d16bc02..e73f608e47a90 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-phis-no-lane-mask-merging.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-phis-no-lane-mask-merging.ll
@@ -108,11 +108,11 @@ define void @divergent_i1_phi_used_inside_loop(float %val, ptr %addr) {
; GFX10-NEXT: .LBB2_1: ; %loop
; GFX10-NEXT: ; =>This Inner Loop Header: Depth=1
; GFX10-NEXT: v_cvt_f32_u32_e32 v3, s6
-; GFX10-NEXT: s_xor_b32 s5, s5, 1
; GFX10-NEXT: s_and_b32 s8, s5, 1
; GFX10-NEXT: s_cmp_lg_u32 s8, 0
-; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v3, v0
; GFX10-NEXT: s_cselect_b32 s8, exec_lo, 0
+; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v3, v0
+; GFX10-NEXT: s_xor_b32 s5, s5, 1
; GFX10-NEXT: s_add_i32 s6, s6, 1
; GFX10-NEXT: s_or_b32 s4, vcc_lo, s4
; GFX10-NEXT: s_andn2_b32 s7, s7, exec_lo
@@ -122,7 +122,7 @@ define void @divergent_i1_phi_used_inside_loop(float %val, ptr %addr) {
; GFX10-NEXT: s_cbranch_execnz .LBB2_1
; GFX10-NEXT: ; %bb.2: ; %exit
; GFX10-NEXT: s_or_b32 exec_lo, exec_lo, s4
-; GFX10-NEXT: v_cndmask_b32_e64 v0, 0, 1.0, s7
+; GFX10-NEXT: v_cndmask_b32_e64 v0, 1.0, 0, s7
; GFX10-NEXT: flat_store_dword v[1:2], v0
; GFX10-NEXT: s_waitcnt lgkmcnt(0)
; GFX10-NEXT: s_setpc_b64 s[30:31]
@@ -157,20 +157,21 @@ define void @divergent_i1_phi_used_inside_loop_bigger_loop_body(float %val, floa
; GFX10-NEXT: .LBB3_1: ; %loop_body
; GFX10-NEXT: ; in Loop: Header=BB3_2 Depth=1
; GFX10-NEXT: v_cvt_f32_u32_e32 v8, s6
-; GFX10-NEXT: s_mov_b32 s8, exec_lo
+; GFX10-NEXT: s_xor_b32 s4, s4, exec_lo
; GFX10-NEXT: s_add_i32 s6, s6, 1
-; GFX10-NEXT: s_xor_b32 s4, s4, s8
; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v8, v0
; GFX10-NEXT: s_or_b32 s5, vcc_lo, s5
-; GFX10-NEXT: s_andn2_b32 s7, s7, exec_lo
-; GFX10-NEXT: s_and_b32 s8, exec_lo, s4
-; GFX10-NEXT: s_or_b32 s7, s7, s8
; GFX10-NEXT: s_andn2_b32 exec_lo, exec_lo, s5
; GFX10-NEXT: s_cbranch_execz .LBB3_6
; GFX10-NEXT: .LBB3_2: ; %loop_start
; GFX10-NEXT: ; =>This Inner Loop Header: Depth=1
; GFX10-NEXT: s_cmpk_le_i32 s6, 0x3e8
+; GFX10-NEXT: s_cselect_b32 s9, 1, 0
+; GFX10-NEXT: s_andn2_b32 s7, s7, exec_lo
+; GFX10-NEXT: s_and_b32 s8, exec_lo, s4
+; GFX10-NEXT: s_or_b32 s7, s7, s8
; GFX10-NEXT: s_mov_b32 s8, 1
+; GFX10-NEXT: s_cmp_lg_u32 s9, 0
; GFX10-NEXT: s_cbranch_scc0 .LBB3_4
; GFX10-NEXT: ; %bb.3: ; %else
; GFX10-NEXT: ; in Loop: Header=BB3_2 Depth=1
@@ -187,7 +188,7 @@ define void @divergent_i1_phi_used_inside_loop_bigger_loop_body(float %val, floa
; GFX10-NEXT: s_branch .LBB3_1
; GFX10-NEXT: .LBB3_6: ; %exit
; GFX10-NEXT: s_or_b32 exec_lo, exec_lo, s5
-; GFX10-NEXT: v_cndmask_b32_e64 v0, 0, 1.0, s7
+; GFX10-NEXT: v_cndmask_b32_e64 v0, 1.0, 0, s7
; GFX10-NEXT: flat_store_dword v[2:3], v0
; GFX10-NEXT: s_waitcnt lgkmcnt(0)
; GFX10-NEXT: s_setpc_b64 s[30:31]
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-used-outside-loop.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-used-outside-loop.ll
index 121dd309fddf9..7dd8797756054 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-used-outside-loop.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-divergent-i1-used-outside-loop.ll
@@ -142,17 +142,18 @@ define void @divergent_i1_xor_used_outside_loop(float %val, float %pre.cond.val,
; GFX10-NEXT: v_cvt_f32_u32_e32 v1, s6
; GFX10-NEXT: s_mov_b32 s8, exec_lo
; GFX10-NEXT: s_add_i32 s6, s6, 1
-; GFX10-NEXT: s_xor_b32 s5, s5, s8
+; GFX10-NEXT: s_xor_b32 s8, s5, s8
; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v1, v0
; GFX10-NEXT: s_or_b32 s4, vcc_lo, s4
; GFX10-NEXT: s_andn2_b32 s7, s7, exec_lo
-; GFX10-NEXT: s_and_b32 s8, exec_lo, s5
-; GFX10-NEXT: s_or_b32 s7, s7, s8
+; GFX10-NEXT: s_and_b32 s9, exec_lo, s5
+; GFX10-NEXT: s_mov_b32 s5, s8
+; GFX10-NEXT: s_or_b32 s7, s7, s9
; GFX10-NEXT: s_andn2_b32 exec_lo, exec_lo, s4
; GFX10-NEXT: s_cbranch_execnz .LBB2_1
; GFX10-NEXT: ; %bb.2: ; %exit
; GFX10-NEXT: s_or_b32 exec_lo, exec_lo, s4
-; GFX10-NEXT: v_cndmask_b32_e64 v0, 0, 1.0, s7
+; GFX10-NEXT: v_cndmask_b32_e64 v0, 1.0, 0, s7
; GFX10-NEXT: flat_store_dword v[2:3], v0
; GFX10-NEXT: s_waitcnt lgkmcnt(0)
; GFX10-NEXT: s_setpc_b64 s[30:31]
@@ -181,25 +182,26 @@ define void @divergent_i1_xor_used_outside_loop_twice(float %val, float %pre.con
; GFX10-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
; GFX10-NEXT: v_cmp_lt_f32_e64 s5, 1.0, v1
; GFX10-NEXT: s_mov_b32 s4, 0
-; GFX10-NEXT: s_mov_b32 s7, 0
-; GFX10-NEXT: ; implicit-def: $sgpr6
+; GFX10-NEXT: s_mov_b32 s6, 0
+; GFX10-NEXT: ; implicit-def: $sgpr7
; GFX10-NEXT: .LBB3_1: ; %loop
; GFX10-NEXT: ; =>This Inner Loop Header: Depth=1
-; GFX10-NEXT: v_cvt_f32_u32_e32 v1, s7
+; GFX10-NEXT: v_cvt_f32_u32_e32 v1, s6
; GFX10-NEXT: s_mov_b32 s8, exec_lo
-; GFX10-NEXT: s_add_i32 s7, s7, 1
-; GFX10-NEXT: s_xor_b32 s5, s5, s8
+; GFX10-NEXT: s_add_i32 s6, s6, 1
+; GFX10-NEXT: s_xor_b32 s8, s5, s8
; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v1, v0
; GFX10-NEXT: s_or_b32 s4, vcc_lo, s4
-; GFX10-NEXT: s_andn2_b32 s6, s6, exec_lo
-; GFX10-NEXT: s_and_b32 s8, exec_lo, s5
-; GFX10-NEXT: s_or_b32 s6, s6, s8
+; GFX10-NEXT: s_andn2_b32 s7, s7, exec_lo
+; GFX10-NEXT: s_and_b32 s9, exec_lo, s5
+; GFX10-NEXT: s_mov_b32 s5, s8
+; GFX10-NEXT: s_or_b32 s7, s7, s9
; GFX10-NEXT: s_andn2_b32 exec_lo, exec_lo, s4
; GFX10-NEXT: s_cbranch_execnz .LBB3_1
; GFX10-NEXT: ; %bb.2: ; %exit
; GFX10-NEXT: s_or_b32 exec_lo, exec_lo, s4
-; GFX10-NEXT: v_cndmask_b32_e64 v0, 0, 1.0, s6
-; GFX10-NEXT: v_cndmask_b32_e64 v1, -1.0, 2.0, s6
+; GFX10-NEXT: v_cndmask_b32_e64 v0, 1.0, 0, s7
+; GFX10-NEXT: v_cndmask_b32_e64 v1, 2.0, -1.0, s7
; GFX10-NEXT: flat_store_dword v[2:3], v0
; GFX10-NEXT: flat_store_dword v[4:5], v1
; GFX10-NEXT: s_waitcnt lgkmcnt(0)
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-temporal-divergent-i1.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-temporal-divergent-i1.ll
index a8b27ecd7e9fc..6e0f61826a9b0 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-temporal-divergent-i1.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/divergence-temporal-divergent-i1.ll
@@ -59,11 +59,11 @@ define void @temporal_divergent_i1_non_phi(float %val, ptr %addr) {
; GFX10-NEXT: .LBB1_1: ; %loop
; GFX10-NEXT: ; =>This Inner Loop Header: Depth=1
; GFX10-NEXT: v_cvt_f32_u32_e32 v3, s6
-; GFX10-NEXT: s_xor_b32 s5, s5, 1
; GFX10-NEXT: s_and_b32 s8, s5, 1
; GFX10-NEXT: s_cmp_lg_u32 s8, 0
-; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v3, v0
; GFX10-NEXT: s_cselect_b32 s8, exec_lo, 0
+; GFX10-NEXT: v_cmp_gt_f32_e32 vcc_lo, v3, v0
+; GFX10-NEXT: s_xor_b32 s5, s5, 1
; GFX10-NEXT: s_add_i32 s6, s6, 1
; GFX10-NEXT: s_or_b32 s4, vcc_lo, s4
; GFX10-NEXT: s_andn2_b32 s7, s7, exec_lo
@@ -73,7 +73,7 @@ define void @temporal_divergent_i1_non_phi(float %val, ptr %addr) {
; GFX10-NEXT: s_cbranch_execnz .LBB1_1
; GFX10-NEXT: ; %bb.2: ; %exit
; GFX10-NEXT: s_or_b32 exec_lo, exec_lo, s4
-; GFX10-NEXT: v_cndmask_b32_e64 v0, 0, 1.0, s7
+; GFX10-NEXT: v_cndmask_b32_e64 v0, 1.0, 0, s7
; GFX10-NEXT: flat_store_dword v[1:2], v0
; GFX10-NEXT: s_waitcnt lgkmcnt(0)
; GFX10-NEXT: s_setpc_b64 s[30:31]
More information about the llvm-commits
mailing list