[llvm] 03ad654 - [DAGCombiner] Combine (fshl A, X, Y) | (shl X, Y) --> fshl (A|X), X, Y (#180887)

via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 17 09:00:49 PST 2026


Author: Craig Topper
Date: 2026-02-17T09:00:45-08:00
New Revision: 03ad6549fcc49bcdc3c93cad293e603f2d05e96c

URL: https://github.com/llvm/llvm-project/commit/03ad6549fcc49bcdc3c93cad293e603f2d05e96c
DIFF: https://github.com/llvm/llvm-project/commit/03ad6549fcc49bcdc3c93cad293e603f2d05e96c.diff

LOG: [DAGCombiner] Combine (fshl A, X, Y) | (shl X, Y) --> fshl (A|X), X, Y (#180887)

Similar for (fshr X, B, Y) | (srl X, Y) --> fshr X, (X|B), Y

This is similar to the FSHL/FSHR handling in
hoistLogicOpWithSameOpcodeHands but here we treat a shl/shr like a
fshl/fshr with 0.

The pattern doesn't require X to be the same in both sides, but that's
what occurred in the case I was looking at so that's what is
implemented.

Alive2: https://alive2.llvm.org/ce/z/eUou-u

Added: 
    

Modified: 
    llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
    llvm/test/CodeGen/AArch64/funnel-shift.ll
    llvm/test/CodeGen/X86/funnel-shift.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 51fbe62b4aff0..d832b675a2f86 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -8500,17 +8500,35 @@ static SDValue visitORCommutative(SelectionDAG &DAG, SDValue N0, SDValue N1,
     return V;
   };
 
-  // (fshl X, ?, Y) | (shl X, Y) --> fshl X, ?, Y
   if (N0.getOpcode() == ISD::FSHL && N1.getOpcode() == ISD::SHL &&
-      N0.getOperand(0) == N1.getOperand(0) &&
-      peekThroughZext(N0.getOperand(2)) == peekThroughZext(N1.getOperand(1)))
-    return N0;
+      peekThroughZext(N0.getOperand(2)) == peekThroughZext(N1.getOperand(1))) {
+    // (fshl X, ?, Y) | (shl X, Y) --> fshl X, ?, Y
+    if (N0.getOperand(0) == N1.getOperand(0))
+      return N0;
+    // (fshl A, X, Y) | (shl X, Y) --> fshl (A|X), X, Y
+    if (N0.getOperand(1) == N1.getOperand(0) && N0.hasOneUse() &&
+        N1.hasOneUse()) {
+      SDValue A = N0.getOperand(0);
+      SDValue X = N1.getOperand(0);
+      SDValue NewLHS = DAG.getNode(ISD::OR, DL, VT, A, X);
+      return DAG.getNode(ISD::FSHL, DL, VT, NewLHS, X, N0.getOperand(2));
+    }
+  }
 
-  // (fshr ?, X, Y) | (srl X, Y) --> fshr ?, X, Y
   if (N0.getOpcode() == ISD::FSHR && N1.getOpcode() == ISD::SRL &&
-      N0.getOperand(1) == N1.getOperand(0) &&
-      peekThroughZext(N0.getOperand(2)) == peekThroughZext(N1.getOperand(1)))
-    return N0;
+      peekThroughZext(N0.getOperand(2)) == peekThroughZext(N1.getOperand(1))) {
+    // (fshr ?, X, Y) | (srl X, Y) --> fshr ?, X, Y
+    if (N0.getOperand(1) == N1.getOperand(0))
+      return N0;
+    // (fshr X, B, Y) | (srl X, Y) --> fshr X, (X|B), Y
+    if (N0.getOperand(0) == N1.getOperand(0) && N0.hasOneUse() &&
+        N1.hasOneUse()) {
+      SDValue X = N1.getOperand(0);
+      SDValue B = N0.getOperand(1);
+      SDValue NewRHS = DAG.getNode(ISD::OR, DL, VT, X, B);
+      return DAG.getNode(ISD::FSHR, DL, VT, X, NewRHS, N0.getOperand(2));
+    }
+  }
 
   // Attempt to match a legalized build_pair-esque pattern:
   // or(shl(aext(Hi),BW/2),zext(Lo))

diff  --git a/llvm/test/CodeGen/AArch64/funnel-shift.ll b/llvm/test/CodeGen/AArch64/funnel-shift.ll
index e0bbfc620e2f8..9dd5dff896624 100644
--- a/llvm/test/CodeGen/AArch64/funnel-shift.ll
+++ b/llvm/test/CodeGen/AArch64/funnel-shift.ll
@@ -541,14 +541,12 @@ define <4 x i32> @fshr_v4i32_shift_by_bitwidth(<4 x i32> %x, <4 x i32> %y) {
 define i32 @or_shl_fshl(i32 %x, i32 %y, i32 %s) {
 ; CHECK-SD-LABEL: or_shl_fshl:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    mov w8, w2
-; CHECK-SD-NEXT:    lsr w9, w1, #1
-; CHECK-SD-NEXT:    lsl w10, w1, w2
-; CHECK-SD-NEXT:    mvn w11, w2
-; CHECK-SD-NEXT:    lsl w8, w0, w8
-; CHECK-SD-NEXT:    lsr w9, w9, w11
-; CHECK-SD-NEXT:    orr w8, w8, w10
-; CHECK-SD-NEXT:    orr w0, w8, w9
+; CHECK-SD-NEXT:    lsr w8, w1, #1
+; CHECK-SD-NEXT:    orr w9, w0, w1
+; CHECK-SD-NEXT:    mvn w10, w2
+; CHECK-SD-NEXT:    lsl w9, w9, w2
+; CHECK-SD-NEXT:    lsr w8, w8, w10
+; CHECK-SD-NEXT:    orr w0, w9, w8
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: or_shl_fshl:
@@ -586,14 +584,12 @@ define i32 @or_shl_rotl(i32 %x, i32 %y, i32 %s) {
 define i32 @or_shl_fshl_commute(i32 %x, i32 %y, i32 %s) {
 ; CHECK-SD-LABEL: or_shl_fshl_commute:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    mov w8, w2
-; CHECK-SD-NEXT:    lsr w9, w1, #1
-; CHECK-SD-NEXT:    lsl w10, w1, w2
-; CHECK-SD-NEXT:    mvn w11, w2
-; CHECK-SD-NEXT:    lsl w8, w0, w8
-; CHECK-SD-NEXT:    lsr w9, w9, w11
-; CHECK-SD-NEXT:    orr w8, w10, w8
-; CHECK-SD-NEXT:    orr w0, w8, w9
+; CHECK-SD-NEXT:    lsr w8, w1, #1
+; CHECK-SD-NEXT:    orr w9, w0, w1
+; CHECK-SD-NEXT:    mvn w10, w2
+; CHECK-SD-NEXT:    lsl w9, w9, w2
+; CHECK-SD-NEXT:    lsr w8, w8, w10
+; CHECK-SD-NEXT:    orr w0, w9, w8
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: or_shl_fshl_commute:
@@ -631,14 +627,12 @@ define i32 @or_shl_rotl_commute(i32 %x, i32 %y, i32 %s) {
 define i32 @or_lshr_fshr(i32 %x, i32 %y, i32 %s) {
 ; CHECK-SD-LABEL: or_lshr_fshr:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    mov w8, w2
-; CHECK-SD-NEXT:    lsl w9, w1, #1
-; CHECK-SD-NEXT:    lsr w10, w1, w2
-; CHECK-SD-NEXT:    lsr w8, w0, w8
-; CHECK-SD-NEXT:    mvn w11, w2
-; CHECK-SD-NEXT:    lsl w9, w9, w11
-; CHECK-SD-NEXT:    orr w8, w8, w10
-; CHECK-SD-NEXT:    orr w0, w9, w8
+; CHECK-SD-NEXT:    lsl w8, w1, #1
+; CHECK-SD-NEXT:    orr w9, w1, w0
+; CHECK-SD-NEXT:    mvn w10, w2
+; CHECK-SD-NEXT:    lsr w9, w9, w2
+; CHECK-SD-NEXT:    lsl w8, w8, w10
+; CHECK-SD-NEXT:    orr w0, w8, w9
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: or_lshr_fshr:
@@ -675,13 +669,11 @@ define i32 @or_lshr_rotr(i32 %x, i32 %y, i32 %s) {
 define i32 @or_lshr_fshr_commute(i32 %x, i32 %y, i32 %s) {
 ; CHECK-SD-LABEL: or_lshr_fshr_commute:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    mov w8, w2
-; CHECK-SD-NEXT:    lsl w9, w1, #1
-; CHECK-SD-NEXT:    lsr w10, w1, w2
-; CHECK-SD-NEXT:    lsr w8, w0, w8
-; CHECK-SD-NEXT:    mvn w11, w2
-; CHECK-SD-NEXT:    lsl w9, w9, w11
-; CHECK-SD-NEXT:    orr w8, w10, w8
+; CHECK-SD-NEXT:    lsl w8, w1, #1
+; CHECK-SD-NEXT:    orr w9, w1, w0
+; CHECK-SD-NEXT:    mvn w10, w2
+; CHECK-SD-NEXT:    lsr w9, w9, w2
+; CHECK-SD-NEXT:    lsl w8, w8, w10
 ; CHECK-SD-NEXT:    orr w0, w8, w9
 ; CHECK-SD-NEXT:    ret
 ;

diff  --git a/llvm/test/CodeGen/X86/funnel-shift.ll b/llvm/test/CodeGen/X86/funnel-shift.ll
index 78d7e7eb3c136..4ba161783fff2 100644
--- a/llvm/test/CodeGen/X86/funnel-shift.ll
+++ b/llvm/test/CodeGen/X86/funnel-shift.ll
@@ -1150,25 +1150,20 @@ declare dso_local void @_Z3foov()
 define i32 @or_shl_fshl(i32 %x, i32 %y, i32 %s) nounwind {
 ; X86-SSE2-LABEL: or_shl_fshl:
 ; X86-SSE2:       # %bb.0:
-; X86-SSE2-NEXT:    pushl %esi
-; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
 ; X86-SSE2-NEXT:    movzbl {{[0-9]+}}(%esp), %ecx
 ; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %edx
-; X86-SSE2-NEXT:    movl %edx, %esi
-; X86-SSE2-NEXT:    shll %cl, %esi
+; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE2-NEXT:    orl %edx, %eax
 ; X86-SSE2-NEXT:    shldl %cl, %edx, %eax
-; X86-SSE2-NEXT:    orl %esi, %eax
-; X86-SSE2-NEXT:    popl %esi
 ; X86-SSE2-NEXT:    retl
 ;
 ; X64-AVX-LABEL: or_shl_fshl:
 ; X64-AVX:       # %bb.0:
 ; X64-AVX-NEXT:    movl %edx, %ecx
-; X64-AVX-NEXT:    movl %esi, %eax
-; X64-AVX-NEXT:    shll %cl, %eax
+; X64-AVX-NEXT:    movl %edi, %eax
+; X64-AVX-NEXT:    orl %esi, %eax
 ; X64-AVX-NEXT:    # kill: def $cl killed $cl killed $ecx
-; X64-AVX-NEXT:    shldl %cl, %esi, %edi
-; X64-AVX-NEXT:    orl %edi, %eax
+; X64-AVX-NEXT:    shldl %cl, %esi, %eax
 ; X64-AVX-NEXT:    retq
   %shy = shl i32 %y, %s
   %fun = call i32 @llvm.fshl.i32(i32 %x, i32 %y, i32 %s)
@@ -1205,25 +1200,20 @@ define i32 @or_shl_rotl(i32 %x, i32 %y, i32 %s) nounwind {
 define i32 @or_shl_fshl_commute(i32 %x, i32 %y, i32 %s) nounwind {
 ; X86-SSE2-LABEL: or_shl_fshl_commute:
 ; X86-SSE2:       # %bb.0:
-; X86-SSE2-NEXT:    pushl %esi
-; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
 ; X86-SSE2-NEXT:    movzbl {{[0-9]+}}(%esp), %ecx
 ; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %edx
-; X86-SSE2-NEXT:    movl %edx, %esi
-; X86-SSE2-NEXT:    shll %cl, %esi
+; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE2-NEXT:    orl %edx, %eax
 ; X86-SSE2-NEXT:    shldl %cl, %edx, %eax
-; X86-SSE2-NEXT:    orl %esi, %eax
-; X86-SSE2-NEXT:    popl %esi
 ; X86-SSE2-NEXT:    retl
 ;
 ; X64-AVX-LABEL: or_shl_fshl_commute:
 ; X64-AVX:       # %bb.0:
 ; X64-AVX-NEXT:    movl %edx, %ecx
-; X64-AVX-NEXT:    movl %esi, %eax
-; X64-AVX-NEXT:    shll %cl, %eax
+; X64-AVX-NEXT:    movl %edi, %eax
+; X64-AVX-NEXT:    orl %esi, %eax
 ; X64-AVX-NEXT:    # kill: def $cl killed $cl killed $ecx
-; X64-AVX-NEXT:    shldl %cl, %esi, %edi
-; X64-AVX-NEXT:    orl %edi, %eax
+; X64-AVX-NEXT:    shldl %cl, %esi, %eax
 ; X64-AVX-NEXT:    retq
   %shy = shl i32 %y, %s
   %fun = call i32 @llvm.fshl.i32(i32 %x, i32 %y, i32 %s)
@@ -1260,25 +1250,20 @@ define i32 @or_shl_rotl_commute(i32 %x, i32 %y, i32 %s) nounwind {
 define i32 @or_lshr_fshr(i32 %x, i32 %y, i32 %s) nounwind {
 ; X86-SSE2-LABEL: or_lshr_fshr:
 ; X86-SSE2:       # %bb.0:
-; X86-SSE2-NEXT:    pushl %esi
-; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
 ; X86-SSE2-NEXT:    movzbl {{[0-9]+}}(%esp), %ecx
 ; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %edx
-; X86-SSE2-NEXT:    movl %edx, %esi
-; X86-SSE2-NEXT:    shrl %cl, %esi
+; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE2-NEXT:    orl %edx, %eax
 ; X86-SSE2-NEXT:    shrdl %cl, %edx, %eax
-; X86-SSE2-NEXT:    orl %esi, %eax
-; X86-SSE2-NEXT:    popl %esi
 ; X86-SSE2-NEXT:    retl
 ;
 ; X64-AVX-LABEL: or_lshr_fshr:
 ; X64-AVX:       # %bb.0:
 ; X64-AVX-NEXT:    movl %edx, %ecx
-; X64-AVX-NEXT:    movl %esi, %eax
-; X64-AVX-NEXT:    shrl %cl, %eax
+; X64-AVX-NEXT:    movl %edi, %eax
+; X64-AVX-NEXT:    orl %esi, %eax
 ; X64-AVX-NEXT:    # kill: def $cl killed $cl killed $ecx
-; X64-AVX-NEXT:    shrdl %cl, %esi, %edi
-; X64-AVX-NEXT:    orl %edi, %eax
+; X64-AVX-NEXT:    shrdl %cl, %esi, %eax
 ; X64-AVX-NEXT:    retq
   %shy = lshr i32 %y, %s
   %fun = call i32 @llvm.fshr.i32(i32 %y, i32 %x, i32 %s)
@@ -1315,25 +1300,20 @@ define i32 @or_lshr_rotr(i32 %x, i32 %y, i32 %s) nounwind {
 define i32 @or_lshr_fshr_commute(i32 %x, i32 %y, i32 %s) nounwind {
 ; X86-SSE2-LABEL: or_lshr_fshr_commute:
 ; X86-SSE2:       # %bb.0:
-; X86-SSE2-NEXT:    pushl %esi
-; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
 ; X86-SSE2-NEXT:    movzbl {{[0-9]+}}(%esp), %ecx
 ; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %edx
-; X86-SSE2-NEXT:    movl %edx, %esi
-; X86-SSE2-NEXT:    shrl %cl, %esi
+; X86-SSE2-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; X86-SSE2-NEXT:    orl %edx, %eax
 ; X86-SSE2-NEXT:    shrdl %cl, %edx, %eax
-; X86-SSE2-NEXT:    orl %esi, %eax
-; X86-SSE2-NEXT:    popl %esi
 ; X86-SSE2-NEXT:    retl
 ;
 ; X64-AVX-LABEL: or_lshr_fshr_commute:
 ; X64-AVX:       # %bb.0:
 ; X64-AVX-NEXT:    movl %edx, %ecx
-; X64-AVX-NEXT:    movl %esi, %eax
-; X64-AVX-NEXT:    shrl %cl, %eax
+; X64-AVX-NEXT:    movl %edi, %eax
+; X64-AVX-NEXT:    orl %esi, %eax
 ; X64-AVX-NEXT:    # kill: def $cl killed $cl killed $ecx
-; X64-AVX-NEXT:    shrdl %cl, %esi, %edi
-; X64-AVX-NEXT:    orl %edi, %eax
+; X64-AVX-NEXT:    shrdl %cl, %esi, %eax
 ; X64-AVX-NEXT:    retq
   %shy = lshr i32 %y, %s
   %fun = call i32 @llvm.fshr.i32(i32 %y, i32 %x, i32 %s)


        


More information about the llvm-commits mailing list