[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