[llvm] [GlobalISel] Add G_SEXT_INREG(G_ZEXT) -> G_SEXT combine. (PR #213606)

David Green via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 2 23:58:14 PDT 2026


https://github.com/davemgreen created https://github.com/llvm/llvm-project/pull/213606

This converts a G_ZEXT or G_ANYEXT to a G_SEXT if we will G_SEXT_INREG the bits away.

>From d2702716f26e635a2985417a5f920cda94152ff7 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Sun, 2 Aug 2026 15:13:29 +0100
Subject: [PATCH 1/2] Test

---
 .../AArch64/GlobalISel/combine-sext-inreg.mir | 142 ++++++++++++++++++
 1 file changed, 142 insertions(+)
 create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir

diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir
new file mode 100644
index 0000000000000..cab4694e71aa5
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir
@@ -0,0 +1,142 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple aarch64 -run-pass=aarch64-postlegalizer-combiner %s -o - | FileCheck %s
+
+---
+name: zext_sextinreg_7
+tracksRegLiveness: true
+legalized:         true
+body:             |
+  bb.0:
+    liveins: $w0
+
+    ; CHECK-LABEL: name: zext_sextinreg_7
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
+    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[ASHR]](i32)
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ZEXT]], 7
+    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    %0:_(i32) = COPY $w0
+    %1:_(i32) = G_CONSTANT i32 24
+    %2:_(i32) = G_ASHR %0, %1
+    %3:_(i64) = G_ZEXT %2
+    %4:_(i64) = G_SEXT_INREG %3, 7
+    $x0 = COPY %4
+...
+---
+name: aext_sextinreg_7
+tracksRegLiveness: true
+legalized:         true
+body:             |
+  bb.0:
+    liveins: $w0
+
+    ; CHECK-LABEL: name: aext_sextinreg_7
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
+    ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(i64) = G_ANYEXT [[ASHR]](i32)
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ANYEXT]], 7
+    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    %0:_(i32) = COPY $w0
+    %1:_(i32) = G_CONSTANT i32 24
+    %2:_(i32) = G_ASHR %0, %1
+    %3:_(i64) = G_ANYEXT %2
+    %4:_(i64) = G_SEXT_INREG %3, 7
+    $x0 = COPY %4
+...
+---
+name: zext_sextinreg_8
+tracksRegLiveness: true
+legalized:         true
+body:             |
+  bb.0:
+    liveins: $w0
+
+    ; CHECK-LABEL: name: zext_sextinreg_8
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
+    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[ASHR]](i32)
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ZEXT]], 8
+    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    %0:_(i32) = COPY $w0
+    %1:_(i32) = G_CONSTANT i32 24
+    %2:_(i32) = G_ASHR %0, %1
+    %3:_(i64) = G_ZEXT %2
+    %4:_(i64) = G_SEXT_INREG %3, 8
+    $x0 = COPY %4
+...
+---
+name: aext_sextinreg_8
+tracksRegLiveness: true
+legalized:         true
+body:             |
+  bb.0:
+    liveins: $w0
+
+    ; CHECK-LABEL: name: aext_sextinreg_8
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
+    ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(i64) = G_ANYEXT [[ASHR]](i32)
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ANYEXT]], 8
+    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    %0:_(i32) = COPY $w0
+    %1:_(i32) = G_CONSTANT i32 24
+    %2:_(i32) = G_ASHR %0, %1
+    %3:_(i64) = G_ANYEXT %2
+    %4:_(i64) = G_SEXT_INREG %3, 8
+    $x0 = COPY %4
+...
+---
+name: zext_sextinreg_48
+tracksRegLiveness: true
+legalized:         true
+body:             |
+  bb.0:
+    liveins: $w0
+
+    ; CHECK-LABEL: name: zext_sextinreg_48
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
+    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[ASHR]](i32)
+    ; CHECK-NEXT: $x0 = COPY [[ZEXT]](i64)
+    %0:_(i32) = COPY $w0
+    %1:_(i32) = G_CONSTANT i32 24
+    %2:_(i32) = G_ASHR %0, %1
+    %3:_(i64) = G_ZEXT %2
+    %4:_(i64) = G_SEXT_INREG %3, 48
+    $x0 = COPY %4
+...
+---
+name: zext_sextinreg_32
+tracksRegLiveness: true
+legalized:         true
+body:             |
+  bb.0:
+    liveins: $w0
+
+    ; CHECK-LABEL: name: zext_sextinreg_32
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
+    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[COPY]](i32)
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ZEXT]], 32
+    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    %0:_(i32) = COPY $w0
+    %1:_(i64) = G_ZEXT %0
+    %2:_(i64) = G_SEXT_INREG %1, 32
+    $x0 = COPY %2
+...

>From 8ccb933b766e3c2aaae03aee8c5e3bddfca829a7 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Sun, 2 Aug 2026 18:06:43 +0100
Subject: [PATCH 2/2] [GlobalISel] Add G_SEXT_INREG(G_ZEXT) -> G_SEXT combine.

This converts a G_ZEXT or G_ANYEXT to a G_SEXT if we will G_SEXT_INREG the bits
away.
---
 llvm/include/llvm/Target/GlobalISel/Combine.td | 18 ++++++++++++++++--
 llvm/lib/Target/AArch64/AArch64Combine.td      |  1 +
 .../AArch64/GlobalISel/combine-sext-inreg.mir  | 15 ++++++---------
 llvm/test/CodeGen/AArch64/fcmp.ll              |  4 +---
 .../CodeGen/AArch64/neon-shift-left-long.ll    |  4 +---
 llvm/test/CodeGen/AArch64/select_cc.ll         |  4 +---
 .../AArch64/vec-combine-trunc-dup-ext.ll       |  4 +---
 .../GlobalISel/artifact-combiner-asserts.ll    | 10 ++++------
 8 files changed, 31 insertions(+), 29 deletions(-)

diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 830c48c10a39b..77dc627b5dfdd 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -942,6 +942,20 @@ def redundant_sext_inreg: GICombineRule <
      (apply [{ Helper.replaceSingleDefInstWithOperand(*${root}, 1); }])
 >;
 
+// Convert G_SEXT_INREG(G_ZEXT) -> G_SEXT
+class redundant_ext_sext_inreg<Instruction Ext>: GICombineRule <
+  (defs root:$root),
+  (match (Ext $tmp, $src),
+         (G_SEXT_INREG $dst, $tmp, $amt):$root,
+         [{ LLT SrcTy = MRI.getType(${src}.getReg());
+            return ${amt}.getImm() <= SrcTy.getScalarSizeInBits() &&
+                   VT->computeNumSignBits(${src}.getReg()) >=
+                   (SrcTy.getScalarSizeInBits() - ${amt}.getImm() + 1); }]),
+  (apply (G_SEXT $dst, $src))
+>;
+def redundant_zext_sext_inreg : redundant_ext_sext_inreg<G_ZEXT>;
+def redundant_aext_sext_inreg : redundant_ext_sext_inreg<G_ANYEXT>;
+
 // Fold (anyext (trunc x)) -> x if the source type is same as
 // the destination type.
 def anyext_trunc_fold: GICombineRule <
@@ -2604,8 +2618,8 @@ def const_combines : GICombineGroup<[constant_fold_fp_ops, const_ptradd_to_i2p,
                                      combine_minmax_nan, expand_const_fpowi]>;
 
 def known_bits_simplifications : GICombineGroup<[
-  redundant_and, redundant_sext_inreg, redundant_or, urem_pow2_to_mask,
-  zext_trunc_fold,
+  redundant_sext_inreg, redundant_zext_sext_inreg, redundant_aext_sext_inreg,
+  redundant_and, redundant_or, urem_pow2_to_mask, zext_trunc_fold,
   sext_inreg_to_zext_inreg]>;
 
 def width_reduction_combines : GICombineGroup<[reduce_shl_of_extend,
diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td
index 82b9cec960d89..1b2308e4f45e9 100644
--- a/llvm/lib/Target/AArch64/AArch64Combine.td
+++ b/llvm/lib/Target/AArch64/AArch64Combine.td
@@ -395,6 +395,7 @@ def AArch64PostLegalizerCombiner
                         redundant_and, xor_of_and_with_same_reg,
                         extractvecelt_pairwise_add, redundant_or,
                         mul_const, redundant_sext_inreg,
+                        redundant_zext_sext_inreg, redundant_aext_sext_inreg,
                         form_bitfield_extract, rotate_out_of_range,
                         icmp_to_true_false_known_bits, overflow_combines,
                         select_combines, select_zero_true, select_zero_false, select_not,
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir
index cab4694e71aa5..fcd824a7f6209 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-sext-inreg.mir
@@ -63,9 +63,8 @@ body:             |
     ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
     ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
     ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
-    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[ASHR]](i32)
-    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ZEXT]], 8
-    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(i64) = G_SEXT [[ASHR]](i32)
+    ; CHECK-NEXT: $x0 = COPY [[SEXT]](i64)
     %0:_(i32) = COPY $w0
     %1:_(i32) = G_CONSTANT i32 24
     %2:_(i32) = G_ASHR %0, %1
@@ -87,9 +86,8 @@ body:             |
     ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
     ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 24
     ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[COPY]], [[C]](i32)
-    ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(i64) = G_ANYEXT [[ASHR]](i32)
-    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ANYEXT]], 8
-    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(i64) = G_SEXT [[ASHR]](i32)
+    ; CHECK-NEXT: $x0 = COPY [[SEXT]](i64)
     %0:_(i32) = COPY $w0
     %1:_(i32) = G_CONSTANT i32 24
     %2:_(i32) = G_ASHR %0, %1
@@ -132,9 +130,8 @@ body:             |
     ; CHECK: liveins: $w0
     ; CHECK-NEXT: {{  $}}
     ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i32) = COPY $w0
-    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[COPY]](i32)
-    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(i64) = G_SEXT_INREG [[ZEXT]], 32
-    ; CHECK-NEXT: $x0 = COPY [[SEXT_INREG]](i64)
+    ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(i64) = G_SEXT [[COPY]](i32)
+    ; CHECK-NEXT: $x0 = COPY [[SEXT]](i64)
     %0:_(i32) = COPY $w0
     %1:_(i64) = G_ZEXT %0
     %2:_(i64) = G_SEXT_INREG %1, 32
diff --git a/llvm/test/CodeGen/AArch64/fcmp.ll b/llvm/test/CodeGen/AArch64/fcmp.ll
index cd948f231453e..56d3d0ed7deed 100644
--- a/llvm/test/CodeGen/AArch64/fcmp.ll
+++ b/llvm/test/CodeGen/AArch64/fcmp.ll
@@ -1521,9 +1521,7 @@ define <4 x i32> @v4f16_i32(<4 x half> %a, <4 x half> %b, <4 x i32> %d, <4 x i32
 ; CHECK-GI-FP16-LABEL: v4f16_i32:
 ; CHECK-GI-FP16:       // %bb.0: // %entry
 ; CHECK-GI-FP16-NEXT:    fcmgt v0.4h, v1.4h, v0.4h
-; CHECK-GI-FP16-NEXT:    ushll v0.4s, v0.4h, #0
-; CHECK-GI-FP16-NEXT:    shl v0.4s, v0.4s, #31
-; CHECK-GI-FP16-NEXT:    cmlt v0.4s, v0.4s, #0
+; CHECK-GI-FP16-NEXT:    sshll v0.4s, v0.4h, #0
 ; CHECK-GI-FP16-NEXT:    bsl v0.16b, v2.16b, v3.16b
 ; CHECK-GI-FP16-NEXT:    ret
 entry:
diff --git a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
index a8c55b476b810..2a514633cd851 100644
--- a/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
+++ b/llvm/test/CodeGen/AArch64/neon-shift-left-long.ll
@@ -463,9 +463,7 @@ define <8 x i16> @test_ushll_cmp(<8 x i8> %a, <8 x i8> %b) #0 {
 ; CHECK-GI:       // %bb.0:
 ; CHECK-GI-NEXT:    cmeq v0.8b, v0.8b, v1.8b
 ; CHECK-GI-NEXT:    movi v1.2d, #0xff00ff00ff00ff
-; CHECK-GI-NEXT:    ushll v0.8h, v0.8b, #0
-; CHECK-GI-NEXT:    shl v0.8h, v0.8h, #15
-; CHECK-GI-NEXT:    cmlt v0.8h, v0.8h, #0
+; CHECK-GI-NEXT:    sshll v0.8h, v0.8b, #0
 ; CHECK-GI-NEXT:    and v0.16b, v0.16b, v1.16b
 ; CHECK-GI-NEXT:    ret
   %cmp.i = icmp eq <8 x i8> %a, %b
diff --git a/llvm/test/CodeGen/AArch64/select_cc.ll b/llvm/test/CodeGen/AArch64/select_cc.ll
index a21307b555810..9d243ec724dd8 100644
--- a/llvm/test/CodeGen/AArch64/select_cc.ll
+++ b/llvm/test/CodeGen/AArch64/select_cc.ll
@@ -96,9 +96,7 @@ define <2 x double> @select_olt_load_cmp(<2 x double> %a, ptr %src) {
 ; CHECK-GI-NEXT:    ldr d1, [x0]
 ; CHECK-GI-NEXT:    movi v2.2d, #0000000000000000
 ; CHECK-GI-NEXT:    fcmgt v1.2s, v1.2s, #0.0
-; CHECK-GI-NEXT:    ushll v1.2d, v1.2s, #0
-; CHECK-GI-NEXT:    shl v1.2d, v1.2d, #63
-; CHECK-GI-NEXT:    cmlt v1.2d, v1.2d, #0
+; CHECK-GI-NEXT:    sshll v1.2d, v1.2s, #0
 ; CHECK-GI-NEXT:    bif v0.16b, v2.16b, v1.16b
 ; CHECK-GI-NEXT:    ret
 entry:
diff --git a/llvm/test/CodeGen/AArch64/vec-combine-trunc-dup-ext.ll b/llvm/test/CodeGen/AArch64/vec-combine-trunc-dup-ext.ll
index 01e545eb6bc7b..ac71e888cccb8 100644
--- a/llvm/test/CodeGen/AArch64/vec-combine-trunc-dup-ext.ll
+++ b/llvm/test/CodeGen/AArch64/vec-combine-trunc-dup-ext.ll
@@ -183,9 +183,7 @@ define <8 x i16> @dup_trunc_sext_v8i16(<8 x i16> %a, <8 x i16> %b, <8 x i16> %x,
 ; CHECK-GI-NEXT:    cmgt v0.8h, v1.8h, v0.8h
 ; CHECK-GI-NEXT:    xtn v0.8b, v0.8h
 ; CHECK-GI-NEXT:    dup v0.8b, v0.b[2]
-; CHECK-GI-NEXT:    ushll v0.8h, v0.8b, #0
-; CHECK-GI-NEXT:    shl v0.8h, v0.8h, #15
-; CHECK-GI-NEXT:    cmlt v0.8h, v0.8h, #0
+; CHECK-GI-NEXT:    sshll v0.8h, v0.8b, #0
 ; CHECK-GI-NEXT:    bsl v0.16b, v2.16b, v3.16b
 ; CHECK-GI-NEXT:    ret
   %cmp = icmp slt <8 x i16> %a, %b
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/artifact-combiner-asserts.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/artifact-combiner-asserts.ll
index 7fccbd78cec9c..40420b8da5ca4 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/artifact-combiner-asserts.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/artifact-combiner-asserts.ll
@@ -6,13 +6,11 @@ define hidden <2 x i64> @icmp_v2i32_sext_to_v2i64(<2 x i32> %arg) {
 ; CHECK:       ; %bb.0:
 ; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
 ; CHECK-NEXT:    v_cmp_eq_u32_e32 vcc_lo, 0, v0
-; CHECK-NEXT:    v_cndmask_b32_e64 v0, 0, 1, vcc_lo
+; CHECK-NEXT:    v_cndmask_b32_e64 v0, 0, -1, vcc_lo
 ; CHECK-NEXT:    v_cmp_eq_u32_e32 vcc_lo, 0, v1
-; CHECK-NEXT:    v_bfe_i32 v0, v0, 0, 1
-; CHECK-NEXT:    v_cndmask_b32_e64 v1, 0, 1, vcc_lo
-; CHECK-NEXT:    v_bfe_i32 v2, v1, 0, 1
-; CHECK-NEXT:    v_ashrrev_i32_e32 v1, 31, v0
-; CHECK-NEXT:    v_ashrrev_i32_e32 v3, 31, v2
+; CHECK-NEXT:    v_mov_b32_e32 v1, v0
+; CHECK-NEXT:    v_cndmask_b32_e64 v2, 0, -1, vcc_lo
+; CHECK-NEXT:    v_mov_b32_e32 v3, v2
 ; CHECK-NEXT:    s_setpc_b64 s[30:31]
   %cmp = icmp eq <2 x i32> %arg, zeroinitializer
   %sext = sext <2 x i1> %cmp to <2 x i64>



More information about the llvm-commits mailing list