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

Osman Yasar via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 22 06:33:47 PST 2026


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

This PR adds the combine rule `fold (add x, shl(0 - y, n)) -> sub(x, shl(y, n))` to GlobalISel, corresponding to an existing SelectionDAG combine in [DAGCombiner::visitADDLikeCommutative](https://github.com/llvm/llvm-project/blame/fcba3040107944604904aeb146c26ec0628160f4/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp#L3367).

Co-authored-by: Sarah Kuhn <sarahlinhkuhn at gmail.com>

>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] [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



More information about the llvm-commits mailing list