[llvm] [InstSimplify] Fold fshl/fshr of complementary shifts to identity (PR #196887)
Iris Shi via llvm-commits
llvm-commits at lists.llvm.org
Mon May 11 19:46:16 PDT 2026
https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/196887
>From e3c0d1149e448cd20e406553aff2c2cec3f417e2 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Mon, 11 May 2026 14:46:47 +0800
Subject: [PATCH 1/4] [InstSimplify] Fold fshl/fshr of complementary shifts to
identity
---
llvm/lib/Analysis/InstructionSimplify.cpp | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index c842bdf8ff492..e9ec3aaee6c8f 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7287,6 +7287,17 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
APInt BitWidth = APInt(ShAmtC->getBitWidth(), ShAmtC->getBitWidth());
if (ShAmtC->urem(BitWidth).isZero())
return Args[IID == Intrinsic::fshl ? 0 : 1];
+
+ // fshl (lshr X, C1), (shl X, C2), C1 -> X when C1 + C2 == BW
+ // fshr (lshr X, C1), (shl X, C2), C2 -> X when C1 + C2 == BW
+ const APInt *C1, *C2;
+ Value *X;
+ unsigned ShAmt = ShAmtC->urem(BitWidth).getZExtValue();
+ if (match(Op0, m_LShr(m_Value(X), m_APInt(C1))) &&
+ match(Op1, m_Shl(m_Specific(X), m_APInt(C2))) &&
+ *C1 + *C2 == BitWidth &&
+ ShAmt == (IID == Intrinsic::fshl ? C1 : C2)->getZExtValue())
+ return X;
}
// Rotating zero by anything is zero.
>From 0e8f87fe6049c5e7736bfe3a792eb78da6640f7e Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Mon, 11 May 2026 14:46:52 +0800
Subject: [PATCH 2/4] add test
---
llvm/test/Transforms/InstSimplify/call.ll | 81 +++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/llvm/test/Transforms/InstSimplify/call.ll b/llvm/test/Transforms/InstSimplify/call.ll
index bf6748a75966b..1e754f8cb8c98 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -590,6 +590,87 @@ define <2 x i8> @fshr_no_shift_modulo_bitwidth_splat(<2 x i8> %x, <2 x i8> %y) {
ret <2 x i8> %z
}
+declare i32 @llvm.fshl.i32(i32, i32, i32)
+declare i32 @llvm.fshr.i32(i32, i32, i32)
+
+define i32 @fshl_identity(i32 %x) {
+; CHECK-LABEL: @fshl_identity(
+; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X1:%.*]], 24
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X1]], 8
+; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 24)
+; CHECK-NEXT: ret i32 [[X]]
+;
+ %shr = lshr i32 %x, 24
+ %shl = shl i32 %x, 8
+ %r = call i32 @llvm.fshl.i32(i32 %shr, i32 %shl, i32 24)
+ ret i32 %r
+}
+
+define i32 @fshr_identity(i32 %x) {
+; CHECK-LABEL: @fshr_identity(
+; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X1:%.*]], 24
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X1]], 8
+; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.fshr.i32(i32 [[SHR]], i32 [[SHL]], i32 8)
+; CHECK-NEXT: ret i32 [[X]]
+;
+ %shr = lshr i32 %x, 24
+ %shl = shl i32 %x, 8
+ %r = call i32 @llvm.fshr.i32(i32 %shr, i32 %shl, i32 8)
+ ret i32 %r
+}
+
+define i32 @fshl_identity_modulo(i32 %x) {
+; CHECK-LABEL: @fshl_identity_modulo(
+; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X1:%.*]], 8
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X1]], 24
+; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 40)
+; CHECK-NEXT: ret i32 [[X]]
+;
+ %shr = lshr i32 %x, 8
+ %shl = shl i32 %x, 24
+ %r = call i32 @llvm.fshl.i32(i32 %shr, i32 %shl, i32 40)
+ ret i32 %r
+}
+
+define i32 @fshl_not_identity_wrong_shift(i32 %x) {
+; CHECK-LABEL: @fshl_not_identity_wrong_shift(
+; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X:%.*]], 24
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X]], 8
+; CHECK-NEXT: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 8)
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %shr = lshr i32 %x, 24
+ %shl = shl i32 %x, 8
+ %r = call i32 @llvm.fshl.i32(i32 %shr, i32 %shl, i32 8)
+ ret i32 %r
+}
+
+define i32 @fshl_not_identity_different_operands(i32 %x, i32 %y) {
+; CHECK-LABEL: @fshl_not_identity_different_operands(
+; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X:%.*]], 24
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[Y:%.*]], 8
+; CHECK-NEXT: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 24)
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %shr = lshr i32 %x, 24
+ %shl = shl i32 %y, 8
+ %r = call i32 @llvm.fshl.i32(i32 %shr, i32 %shl, i32 24)
+ ret i32 %r
+}
+
+define i32 @fshl_not_identity_wrong_sum(i32 %x) {
+; CHECK-LABEL: @fshl_not_identity_wrong_sum(
+; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X:%.*]], 24
+; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X]], 4
+; CHECK-NEXT: [[R:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 24)
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %shr = lshr i32 %x, 24
+ %shl = shl i32 %x, 4
+ %r = call i32 @llvm.fshl.i32(i32 %shr, i32 %shl, i32 24)
+ ret i32 %r
+}
+
; If y is poison, eliminating the guard is not safe.
define i8 @fshl_zero_shift_guard(i8 %x, i8 %y, i8 %sh) {
>From 7be55468f5857a48c762429971bfc2201a26e5cf Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Mon, 11 May 2026 14:47:31 +0800
Subject: [PATCH 3/4] update test
---
llvm/test/Transforms/InstSimplify/call.ll | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/llvm/test/Transforms/InstSimplify/call.ll b/llvm/test/Transforms/InstSimplify/call.ll
index 1e754f8cb8c98..b1506d676b52d 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -595,10 +595,7 @@ declare i32 @llvm.fshr.i32(i32, i32, i32)
define i32 @fshl_identity(i32 %x) {
; CHECK-LABEL: @fshl_identity(
-; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X1:%.*]], 24
-; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X1]], 8
-; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 24)
-; CHECK-NEXT: ret i32 [[X]]
+; CHECK-NEXT: ret i32 [[X:%.*]]
;
%shr = lshr i32 %x, 24
%shl = shl i32 %x, 8
@@ -608,10 +605,7 @@ define i32 @fshl_identity(i32 %x) {
define i32 @fshr_identity(i32 %x) {
; CHECK-LABEL: @fshr_identity(
-; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X1:%.*]], 24
-; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X1]], 8
-; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.fshr.i32(i32 [[SHR]], i32 [[SHL]], i32 8)
-; CHECK-NEXT: ret i32 [[X]]
+; CHECK-NEXT: ret i32 [[X:%.*]]
;
%shr = lshr i32 %x, 24
%shl = shl i32 %x, 8
@@ -621,10 +615,7 @@ define i32 @fshr_identity(i32 %x) {
define i32 @fshl_identity_modulo(i32 %x) {
; CHECK-LABEL: @fshl_identity_modulo(
-; CHECK-NEXT: [[SHR:%.*]] = lshr i32 [[X1:%.*]], 8
-; CHECK-NEXT: [[SHL:%.*]] = shl i32 [[X1]], 24
-; CHECK-NEXT: [[X:%.*]] = call i32 @llvm.fshl.i32(i32 [[SHR]], i32 [[SHL]], i32 40)
-; CHECK-NEXT: ret i32 [[X]]
+; CHECK-NEXT: ret i32 [[X:%.*]]
;
%shr = lshr i32 %x, 8
%shl = shl i32 %x, 24
>From 3af3f03d64fbdb2efd5c81d48dd3b67f3a5c08d0 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Tue, 12 May 2026 10:41:55 +0800
Subject: [PATCH 4/4] address review comments
Co-Authored-By: dtcxzyw <dtcxzyw2333 at gmail.com>
---
llvm/lib/Analysis/InstructionSimplify.cpp | 7 +++----
llvm/test/Transforms/InstSimplify/call.ll | 3 ---
2 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index e9ec3aaee6c8f..c5a6381670e5b 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -7285,18 +7285,17 @@ static Value *simplifyIntrinsic(CallBase *Call, Value *Callee,
if (match(ShAmtArg, m_APInt(ShAmtC))) {
// If there's effectively no shift, return the 1st arg or 2nd arg.
APInt BitWidth = APInt(ShAmtC->getBitWidth(), ShAmtC->getBitWidth());
- if (ShAmtC->urem(BitWidth).isZero())
+ const APInt ShAmt = ShAmtC->urem(BitWidth);
+ if (ShAmt.isZero())
return Args[IID == Intrinsic::fshl ? 0 : 1];
// fshl (lshr X, C1), (shl X, C2), C1 -> X when C1 + C2 == BW
// fshr (lshr X, C1), (shl X, C2), C2 -> X when C1 + C2 == BW
const APInt *C1, *C2;
Value *X;
- unsigned ShAmt = ShAmtC->urem(BitWidth).getZExtValue();
if (match(Op0, m_LShr(m_Value(X), m_APInt(C1))) &&
match(Op1, m_Shl(m_Specific(X), m_APInt(C2))) &&
- *C1 + *C2 == BitWidth &&
- ShAmt == (IID == Intrinsic::fshl ? C1 : C2)->getZExtValue())
+ *C1 + *C2 == BitWidth && ShAmt == *(IID == Intrinsic::fshl ? C1 : C2))
return X;
}
diff --git a/llvm/test/Transforms/InstSimplify/call.ll b/llvm/test/Transforms/InstSimplify/call.ll
index b1506d676b52d..10ccb082d9b6d 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -590,9 +590,6 @@ define <2 x i8> @fshr_no_shift_modulo_bitwidth_splat(<2 x i8> %x, <2 x i8> %y) {
ret <2 x i8> %z
}
-declare i32 @llvm.fshl.i32(i32, i32, i32)
-declare i32 @llvm.fshr.i32(i32, i32, i32)
-
define i32 @fshl_identity(i32 %x) {
; CHECK-LABEL: @fshl_identity(
; CHECK-NEXT: ret i32 [[X:%.*]]
More information about the llvm-commits
mailing list