Skip to content

GlobalISel: neg (and x, 1) --> SIGN_EXTEND_INREG x, 1 #131367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 31, 2025

Conversation

MatzeB
Copy link
Contributor

@MatzeB MatzeB commented Mar 14, 2025

The pattern

%shl = shl i32 %x, 31
%ashr = ashr i32 %shl, 31

would be combined to G_EXT_INREG %x, 1 by GlobalISel. However InstCombine normalizes this pattern to:

%and = and i32 %x, 1
%neg = sub i32 0, %and

This adds a combiner for this variant as well.

@MatzeB
Copy link
Contributor Author

MatzeB commented Mar 14, 2025

SelectionDAG version of this combine is in #131239

@llvmbot
Copy link
Member

llvmbot commented Mar 14, 2025

@llvm/pr-subscribers-backend-amdgpu

@llvm/pr-subscribers-backend-aarch64

Author: Matthias Braun (MatzeB)

Changes

The pattern

%shl = shl i32 %x, 31
%ashr = ashr i32 %shl, 31

would be combined to G_EXT_INREG %x, 1 by GlobalISel. However InstCombine normalizes this pattern to:

%and = and i32 %x, 1
%neg = sub i32 0, %and

This adds a combiner for this variant as well.


Full diff: https://github.com/llvm/llvm-project/pull/131367.diff

2 Files Affected:

  • (modified) llvm/include/llvm/Target/GlobalISel/Combine.td (+11-1)
  • (added) llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir (+20)
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 3590ab221ad44..d43046ec5d282 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -747,6 +747,16 @@ def shl_ashr_to_sext_inreg : GICombineRule<
   (apply [{ Helper.applyAshShlToSextInreg(*${root}, ${info});}])
 >;
 
+// Fold sub 0, (and x, 1) -> sext_inreg x, 1
+def neg_and_one_to_sext_inreg : GICombineRule<
+  (defs root:$dst),
+  (match (G_AND $and, $x, 1),
+         (G_SUB $dst, 0, $and),
+         [{ return Helper.isLegalOrBeforeLegalizer(
+            {TargetOpcode::G_SEXT_INREG, {MRI.getType(${x}.getReg())}}); }]),
+  (apply (G_SEXT_INREG $dst, $x, 1))
+>;
+
 // Fold and(and(x, C1), C2) -> C1&C2 ? and(x, C1&C2) : 0
 def overlapping_and: GICombineRule <
   (defs root:$root, build_fn_matchinfo:$info),
@@ -2013,7 +2023,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
     undef_combines, identity_combines, phi_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, sext_inreg_of_load,
+    shl_ashr_to_sext_inreg, neg_and_one_to_sext_inreg, sext_inreg_of_load,
     width_reduction_combines, select_combines,
     known_bits_simplifications, trunc_shift,
     not_cmp_fold, opt_brcond_by_inverting_cond,
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir
new file mode 100644
index 0000000000000..aeb7ec3d9b9ee
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir
@@ -0,0 +1,20 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -o - -mtriple aarch64-- -run-pass=aarch64-prelegalizer-combiner %s | FileCheck %s
+---
+name: test_combine_neg_and_one_to_sext_inreg
+body: |
+  bb.1:
+  liveins: $w0
+    ; CHECK-LABEL: name: test_combine_neg_and_one_to_sext_inreg
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s32) = G_SEXT_INREG [[COPY]], 1
+    ; CHECK-NEXT: $w0 = COPY [[SEXT_INREG]](s32)
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    %2:_(s32) = G_AND %0:_, %1:_
+    %4:_(s32) = G_SUB %3:_, %2:_
+    $w0 = COPY %4:_(s32)
+...

@llvmbot
Copy link
Member

llvmbot commented Mar 14, 2025

@llvm/pr-subscribers-llvm-globalisel

Author: Matthias Braun (MatzeB)

Changes

The pattern

%shl = shl i32 %x, 31
%ashr = ashr i32 %shl, 31

would be combined to G_EXT_INREG %x, 1 by GlobalISel. However InstCombine normalizes this pattern to:

%and = and i32 %x, 1
%neg = sub i32 0, %and

This adds a combiner for this variant as well.


Full diff: https://github.com/llvm/llvm-project/pull/131367.diff

2 Files Affected:

  • (modified) llvm/include/llvm/Target/GlobalISel/Combine.td (+11-1)
  • (added) llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir (+20)
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 3590ab221ad44..d43046ec5d282 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -747,6 +747,16 @@ def shl_ashr_to_sext_inreg : GICombineRule<
   (apply [{ Helper.applyAshShlToSextInreg(*${root}, ${info});}])
 >;
 
+// Fold sub 0, (and x, 1) -> sext_inreg x, 1
+def neg_and_one_to_sext_inreg : GICombineRule<
+  (defs root:$dst),
+  (match (G_AND $and, $x, 1),
+         (G_SUB $dst, 0, $and),
+         [{ return Helper.isLegalOrBeforeLegalizer(
+            {TargetOpcode::G_SEXT_INREG, {MRI.getType(${x}.getReg())}}); }]),
+  (apply (G_SEXT_INREG $dst, $x, 1))
+>;
+
 // Fold and(and(x, C1), C2) -> C1&C2 ? and(x, C1&C2) : 0
 def overlapping_and: GICombineRule <
   (defs root:$root, build_fn_matchinfo:$info),
@@ -2013,7 +2023,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
     undef_combines, identity_combines, phi_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, sext_inreg_of_load,
+    shl_ashr_to_sext_inreg, neg_and_one_to_sext_inreg, sext_inreg_of_load,
     width_reduction_combines, select_combines,
     known_bits_simplifications, trunc_shift,
     not_cmp_fold, opt_brcond_by_inverting_cond,
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir
new file mode 100644
index 0000000000000..aeb7ec3d9b9ee
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-neg-and-one-to-sext-inreg.mir
@@ -0,0 +1,20 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -o - -mtriple aarch64-- -run-pass=aarch64-prelegalizer-combiner %s | FileCheck %s
+---
+name: test_combine_neg_and_one_to_sext_inreg
+body: |
+  bb.1:
+  liveins: $w0
+    ; CHECK-LABEL: name: test_combine_neg_and_one_to_sext_inreg
+    ; CHECK: liveins: $w0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0
+    ; CHECK-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s32) = G_SEXT_INREG [[COPY]], 1
+    ; CHECK-NEXT: $w0 = COPY [[SEXT_INREG]](s32)
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    %2:_(s32) = G_AND %0:_, %1:_
+    %4:_(s32) = G_SUB %3:_, %2:_
+    $w0 = COPY %4:_(s32)
+...

@arsenm arsenm requested a review from Pierre-vh March 16, 2025 07:17
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
# RUN: llc -o - -mtriple=amdgcn -run-pass=amdgpu-prelegalizer-combiner %s | FileCheck %s
---
name: test_combine_neg_and_one_to_sext_inreg_v2i32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the tests in separate file?
Can you put them all in one file of either target ?

Copy link
Contributor Author

@MatzeB MatzeB Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a generic combine rule not tied to a target. Given that people wanted to see scalar and vectorized versions I wondered whether people maybe also wanted to see two targets especially given that for aarch64 the combine isn't really helpful in the vectorized version[1]

Anyway moved the test to AArch64 now.

[1] The combine helps scalar AArch64 because G_SEXT_INREG can be implement with an sbfx instruction; for the vectorized case a G_SEXT_INREG will just be broken to two instructions (SHL + SHRS) which isn't really helpful (and hopefully doesn't hurt compared to the original AND + SUB instructions). Unfortunately as far as I can tell, the GlobalISel legalization interface however does not provide enough information for the target to differentiate between those two cases.

@MatzeB MatzeB merged commit 5d1f27f into llvm:main Mar 31, 2025
11 checks passed
@MatzeB MatzeB deleted the aarch64_gisel_sbfx_zero_one branch April 7, 2025 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants