[llvm] [GlobalISel] Fold Add Shift combine from SelectionDAG (PR #177371)

Osman Yasar via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 23 06:48:29 PST 2026


https://github.com/osmanyasar05 updated https://github.com/llvm/llvm-project/pull/177371

>From 50992d922a9a85fd2c582cb851505eebba717598 Mon Sep 17 00:00:00 2001
From: osmanyasar05 <osmanyas05 at gmail.com>
Date: Thu, 22 Jan 2026 14:17:55 +0000
Subject: [PATCH 1/3] [GlobalISel] Fold Add Shift combine from SelectionDAG

Co-authored-by: Sarah Kuhn <sarahlinhkuhn at gmail.com>
---
 .../include/llvm/Target/GlobalISel/Combine.td | 23 ++++-
 .../AArch64/GlobalISel/combine-add.mir        | 83 +++++++++++++++++++
 2 files changed, 105 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index a9b4932b2e317..704032f3a2652 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1926,6 +1926,27 @@ def integer_reassoc_combines: GICombineGroup<[
   AMinusC1PlusC2
 ]>;
 
+// fold (A+shl(0-B, C)) -> (A-shl(B, C))
+// fold (shl(0-B, C)+A) -> (A-shl(B, C))
+def add_shl_neg_frags : GICombinePatFrag<
+  (outs root:$dst), (ins $x, $y, $n),
+  [
+    (pattern (G_CONSTANT $zero, 0),
+             (G_SUB $neg_y, $zero, $y),
+             (G_SHL $shl_neg, $neg_y, $n),
+             (G_ADD $dst, $x, $shl_neg)),
+    (pattern (G_CONSTANT $zero, 0),
+             (G_SUB $neg_y, $zero, $y),
+             (G_SHL $shl_neg, $neg_y, $n),
+             (G_ADD $dst, $shl_neg, $x))
+  ]>;
+
+def add_shift : GICombineRule<
+  (defs root:$dst),
+  (match (add_shl_neg_frags $dst, $x, $y, $n)),
+  (apply (G_SHL $new_shl, $y, $n),
+         (G_SUB $dst, $x, $new_shl))>;
+
 def freeze_of_non_undef_non_poison : GICombineRule<
    (defs root:$root),
    (match (G_FREEZE $root, $src),
@@ -2182,7 +2203,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
     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]>;
+    truncsat_combines, lshr_of_trunc_of_lshr, ctls_combines, add_shift]>;
 
 // A combine group used to for prelegalizer combiners at -O0. The combines in
 // this group have been selected based on experiments to balance code size and
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir
index a0142afd06777..a6941d02f85db 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir
@@ -330,3 +330,86 @@ body:             |
     $q1 = COPY %6(<4 x s32>)
     RET_ReallyLR implicit $q0, implicit $q1
 ...
+
+---
+name:            add_shl_neg
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1, $w2
+
+    ; CHECK-LABEL: name: add_shl_neg
+    ; CHECK: liveins: $w0, $w1, $w2
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $w2
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY1]], [[COPY2]](s32)
+    ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[SHL]]
+    ; CHECK-NEXT: $w0 = COPY [[SUB]](s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = COPY $w1
+    %2:_(s32) = COPY $w2
+    %3:_(s32) = G_CONSTANT i32 0
+    %4:_(s32) = G_SUB %3, %1
+    %5:_(s32) = G_SHL %4, %2
+    %6:_(s32) = G_ADD %0, %5
+    $w0 = COPY %6
+    RET_ReallyLR implicit $w0
+...
+
+---
+name:            add_shl_neg_commuted
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1, $w2
+
+    ; CHECK-LABEL: name: add_shl_neg_commuted
+    ; CHECK: liveins: $w0, $w1, $w2
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(s32) = COPY $w2
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY1]], [[COPY2]](s32)
+    ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[SHL]]
+    ; CHECK-NEXT: $w0 = COPY [[SUB]](s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = COPY $w1
+    %2:_(s32) = COPY $w2
+    %3:_(s32) = G_CONSTANT i32 0
+    %4:_(s32) = G_SUB %3, %1
+    %5:_(s32) = G_SHL %4, %2
+    %6:_(s32) = G_ADD %5, %0
+    $w0 = COPY %6
+    RET_ReallyLR implicit $w0
+...
+
+---
+name:            add_shl_neg_wide
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $q0, $q1, $q2
+
+    ; CHECK-LABEL: name: add_shl_neg_wide
+    ; CHECK: liveins: $q0, $q1, $q2
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s128) = COPY $q0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(s128) = COPY $q1
+    ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(s128) = COPY $q2
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s128) = G_SHL [[COPY1]], [[COPY2]](s128)
+    ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(s128) = G_SUB [[COPY]], [[SHL]]
+    ; CHECK-NEXT: $q0 = COPY [[SUB]](s128)
+    ; CHECK-NEXT: RET_ReallyLR implicit $q0
+    %0:_(s128) = COPY $q0
+    %1:_(s128) = COPY $q1
+    %2:_(s128) = COPY $q2
+    %3:_(s128) = G_CONSTANT i128 0
+    %4:_(s128) = G_SUB %3, %1
+    %5:_(s128) = G_SHL %4, %2
+    %6:_(s128) = G_ADD %0, %5
+    $q0 = COPY %6
+    RET_ReallyLR implicit $q0

>From 9e2ec8ac77f2591b90ae26f27b19f2f608605f3b Mon Sep 17 00:00:00 2001
From: Osman Yasar <37121924+osmanyasar05 at users.noreply.github.com>
Date: Thu, 22 Jan 2026 15:13:21 +0000
Subject: [PATCH 2/3] Update llvm/include/llvm/Target/GlobalISel/Combine.td

Co-authored-by: Luisa Cicolini <48860705+luisacicolini at users.noreply.github.com>
---
 llvm/include/llvm/Target/GlobalISel/Combine.td | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 704032f3a2652..1298011aac868 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1926,8 +1926,8 @@ def integer_reassoc_combines: GICombineGroup<[
   AMinusC1PlusC2
 ]>;
 
-// fold (A+shl(0-B, C)) -> (A-shl(B, C))
-// fold (shl(0-B, C)+A) -> (A-shl(B, C))
+// fold (A+(shl (0-B), C)) -> (A-(shl B, C))
+// fold ((shl (0-B), C)+A) -> (A-(shl B, C))
 def add_shl_neg_frags : GICombinePatFrag<
   (outs root:$dst), (ins $x, $y, $n),
   [

>From 7124732ad813311deb57923ff2504af9beb4a9bb Mon Sep 17 00:00:00 2001
From: osmanyasar05 <osmanyas05 at gmail.com>
Date: Fri, 23 Jan 2026 14:47:19 +0000
Subject: [PATCH 3/3] Add hasOneUse checks

---
 .../include/llvm/Target/GlobalISel/Combine.td |  8 +++-
 .../AArch64/GlobalISel/combine-add.mir        | 37 +++++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 1298011aac868..28b9d7c5edb5c 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1934,11 +1934,15 @@ def add_shl_neg_frags : GICombinePatFrag<
     (pattern (G_CONSTANT $zero, 0),
              (G_SUB $neg_y, $zero, $y),
              (G_SHL $shl_neg, $neg_y, $n),
-             (G_ADD $dst, $x, $shl_neg)),
+             (G_ADD $dst, $x, $shl_neg),
+             [{ return MRI.hasOneNonDBGUse(${shl_neg}.getReg()) &&
+                       MRI.hasOneNonDBGUse(${neg_y}.getReg()); }]),
     (pattern (G_CONSTANT $zero, 0),
              (G_SUB $neg_y, $zero, $y),
              (G_SHL $shl_neg, $neg_y, $n),
-             (G_ADD $dst, $shl_neg, $x))
+             (G_ADD $dst, $shl_neg, $x),
+             [{ return MRI.hasOneNonDBGUse(${shl_neg}.getReg()) &&
+                       MRI.hasOneNonDBGUse(${neg_y}.getReg()); }])
   ]>;
 
 def add_shift : GICombineRule<
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir
index a6941d02f85db..a5a0623354745 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-add.mir
@@ -413,3 +413,40 @@ body:             |
     %6:_(s128) = G_ADD %0, %5
     $q0 = COPY %6
     RET_ReallyLR implicit $q0
+...
+
+---
+name:            add_shl_neg_multiple_uses
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $x0, $x1, $x2, $x3
+
+    ; CHECK-LABEL: name: add_shl_neg_multiple_uses
+    ; CHECK: liveins: $x0, $x1, $x2, $x3
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %x:_(s64) = COPY $x0
+    ; CHECK-NEXT: %y:_(s64) = COPY $x1
+    ; CHECK-NEXT: %n:_(s64) = COPY $x2
+    ; CHECK-NEXT: %z:_(s64) = COPY $x3
+    ; CHECK-NEXT: %zero:_(s64) = G_CONSTANT i64 0
+    ; CHECK-NEXT: %neg_y:_(s64) = G_SUB %zero, %y
+    ; CHECK-NEXT: %shl_neg:_(s64) = G_SHL %neg_y, %n(s64)
+    ; CHECK-NEXT: %dst:_(s64) = G_ADD %x, %shl_neg
+    ; CHECK-NEXT: %other:_(s64) = G_MUL %shl_neg, %z
+    ; CHECK-NEXT: %mul:_(s64) = G_MUL %dst, %other
+    ; CHECK-NEXT: $x0 = COPY %mul(s64)
+    ; CHECK-NEXT: RET_ReallyLR implicit $x0
+    %x:_(s64) = COPY $x0
+    %y:_(s64) = COPY $x1
+    %n:_(s64) = COPY $x2
+    %z:_(s64) = COPY $x3
+    %zero:_(s64) = G_CONSTANT i64 0
+    %neg_y:_(s64) = G_SUB %zero, %y
+    %shl_neg:_(s64) = G_SHL %neg_y, %n
+    %dst:_(s64) = G_ADD %x, %shl_neg
+    %other:_(s64) = G_MUL %shl_neg, %z
+    %mul:_(s64) = G_MUL %dst, %other
+    $x0 = COPY %mul
+    RET_ReallyLR implicit $x0
+...



More information about the llvm-commits mailing list