[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