Skip to content

Conversation

@MatzeB
Copy link
Contributor

@MatzeB MatzeB commented Mar 13, 2025

The pattern

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

would be combined to SIGN_EXTEND_INREG %x, ValueType:ch:i1 by SelectionDAG.
However InstCombine normalizes this pattern to:

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

This adds matching code to DAGCombiner to catch this variant as well.

The pattern

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

can be combined to SIGN_EXTEND_INREG %x, ValueType:ch:i1
However InstCombine normalizes this pattern to the equivalent

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

This adds a DAGCombiner pattern to still turn this into
SIGN_EXTEND_INREG.
@MatzeB MatzeB marked this pull request as ready for review March 13, 2025 23:54
@llvmbot llvmbot added backend:AArch64 llvm:SelectionDAG SelectionDAGISel as well labels Mar 13, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 13, 2025

@llvm/pr-subscribers-llvm-selectiondag

@llvm/pr-subscribers-backend-aarch64

Author: Matthias Braun (MatzeB)

Changes

The pattern

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

is combined to SIGN_EXTEND_INREG %x, ValueType:ch:i1 by SelectionDAG.
However InstCombine normalizes this to the equivalent:

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

Add a pattern to DAGCombiner to catch this variant too.


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

3 Files Affected:

  • (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+14)
  • (modified) llvm/test/CodeGen/AArch64/arm64-bitfield-extract.ll (+15)
  • (modified) llvm/test/CodeGen/AArch64/pr61111.ll (+3-3)
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index c35838601cc9c..a54857e1037e2 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -3964,6 +3964,20 @@ SDValue DAGCombiner::visitSUB(SDNode *N) {
           isNullConstant(N1S.getOperand(0)))
         return DAG.getSplat(VT, DL, N1S.getOperand(1));
     }
+
+    // sub 0, (and x, 1)  -->  SIGN_EXTEND_INREG x, i1
+    if (N1.getOpcode() == ISD::AND && N1.hasOneUse() &&
+        isOneOrOneSplat(N1->getOperand(1))) {
+      EVT ExtVT = EVT::getIntegerVT(*DAG.getContext(), 1);
+      if (VT.isVector())
+        ExtVT = EVT::getVectorVT(*DAG.getContext(), ExtVT,
+                                 VT.getVectorElementCount());
+      if (TLI.getOperationAction(ISD::SIGN_EXTEND_INREG, ExtVT) ==
+          TargetLowering::Legal) {
+        return DAG.getNode(ISD::SIGN_EXTEND_INREG, DL, VT, N1->getOperand(0),
+                           DAG.getValueType(ExtVT));
+      }
+    }
   }
 
   // Canonicalize (sub -1, x) -> ~x, i.e. (xor x, -1)
diff --git a/llvm/test/CodeGen/AArch64/arm64-bitfield-extract.ll b/llvm/test/CodeGen/AArch64/arm64-bitfield-extract.ll
index 9c8c34558d776..62ff4bbcc9c27 100644
--- a/llvm/test/CodeGen/AArch64/arm64-bitfield-extract.ll
+++ b/llvm/test/CodeGen/AArch64/arm64-bitfield-extract.ll
@@ -1067,3 +1067,18 @@ if.else:
 end:
   ret void
 }
+
+define i64 @sign_extend_lsb(i64 %arg) nounwind {
+; LLC-LABEL: sign_extend_lsb:
+; LLC:       // %bb.0:
+; LLC-NEXT:    sbfx x0, x0, #0, #1
+; LLC-NEXT:    ret
+; OPT-LABEL: @sign_extend_lsb(
+; OPT-NEXT:    [[AND:%.*]] = and i64 [[ARG:%.*]], 1
+; OPT-NEXT:    [[NEG:%.*]] = sub i64 0, [[AND]]
+; OPT-NEXT:    ret i64 [[NEG]]
+;
+  %and = and i64 %arg, 1
+  %neg = sub i64 0, %and
+  ret i64 %neg
+}
diff --git a/llvm/test/CodeGen/AArch64/pr61111.ll b/llvm/test/CodeGen/AArch64/pr61111.ll
index d2c72921a9229..be7c5981332e7 100644
--- a/llvm/test/CodeGen/AArch64/pr61111.ll
+++ b/llvm/test/CodeGen/AArch64/pr61111.ll
@@ -5,9 +5,9 @@ define i62 @f(i1 %0) {
 ; CHECK-LABEL: f:
 ; CHECK:       // %bb.0:
 ; CHECK-NEXT:    // kill: def $w0 killed $w0 def $x0
-; CHECK-NEXT:    and x8, x0, #0x1
-; CHECK-NEXT:    sub x8, x8, #1
-; CHECK-NEXT:    tst x8, #0x3fffffffffffffff
+; CHECK-NEXT:    sbfx x8, x0, #0, #1
+; CHECK-NEXT:    mov x9, #4611686018427387903 // =0x3fffffffffffffff
+; CHECK-NEXT:    bics xzr, x9, x8
 ; CHECK-NEXT:    cset w0, ne
 ; CHECK-NEXT:    ret
   %2 = zext i1 %0 to i59

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

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

LGTM

@MatzeB
Copy link
Contributor Author

MatzeB commented Mar 14, 2025

GlobalISel version of this combine is in #131367

@MatzeB MatzeB merged commit e6382f2 into llvm:main Mar 14, 2025
14 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:AArch64 llvm:SelectionDAG SelectionDAGISel as well

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants