[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