[llvm] [InstSimplify] Improve coverage for zero-shift-guard folding for rotate (PR #217033)

Vimal Patel via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 05:13:08 PDT 2026


https://github.com/pvimal816a updated https://github.com/llvm/llvm-project/pull/217033

>From f233abb41b116335ef3e6da07c7ca92e38191a53 Mon Sep 17 00:00:00 2001
From: Vimal Patel <vimal.patel at arm.com>
Date: Tue, 18 Aug 2026 10:19:11 +0000
Subject: [PATCH 1/3] [InstSimplify] Improve coverage for zero-shift-guard
 folding for rotate

The current logic responsible to fold zero-shift-guard performed the
folding only when the shift amount is exactly zero. But, the guard could
be eliminated as long as the funnel shift instruction is performing a
bitwise rotate and the shift amount is a multiple of the bitwidth of the
value being rotated. This commit makes this change.
---
 llvm/lib/Analysis/InstructionSimplify.cpp | 22 ++++++++++++++----
 llvm/test/Transforms/InstSimplify/call.ll | 28 +++++++++++++++++++++++
 2 files changed, 45 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index c76f4d2e9f327..d2cb2b694c25a 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4903,11 +4903,23 @@ static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
     auto isRotate =
         m_CombineOr(m_FShl(m_Value(X), m_Deferred(X), m_Value(ShAmt)),
                     m_FShr(m_Value(X), m_Deferred(X), m_Value(ShAmt)));
-    // (ShAmt == 0) ? X : fshl(X, X, ShAmt) --> fshl(X, X, ShAmt)
-    // (ShAmt == 0) ? X : fshr(X, X, ShAmt) --> fshr(X, X, ShAmt)
-    if (match(FalseVal, isRotate) && TrueVal == X && CmpLHS == ShAmt &&
-        Pred == ICmpInst::ICMP_EQ)
-      return FalseVal;
+    if (match(FalseVal, isRotate) && TrueVal == X) {
+      // (ShAmt == 0) ? X : fshl(X, X, ShAmt) --> fshl(X, X, ShAmt)
+      // (ShAmt == 0) ? X : fshr(X, X, ShAmt) --> fshr(X, X, ShAmt)
+      if (CmpLHS == ShAmt)
+        return FalseVal;
+      // Compute the bitwidth of the value being rotated.
+      unsigned BW = X->getType()->getScalarSizeInBits();
+      // Handle the cases where the expression to be checked for zero is not the
+      // shift amount but the `shAmt % bitwidth` which is equivalent to `shAmt &
+      // (bitwidth - 1)` provided the bitwidth is a power of 2.
+      //
+      // ((ShAmt & (BW-1)) == 0) ? X : fshl(X, X, ShAmt) --> fshl(X, X, ShAmt)
+      // ((ShAmt & (BW-1)) == 0) ? X : fshr(X, X, ShAmt) --> fshr(X, X, ShAmt)
+      if (isPowerOf2_32(BW) &&
+          match(CmpLHS, m_c_And(m_Specific(ShAmt), m_SpecificInt(BW - 1))))
+        return FalseVal;
+    }
 
     // X == 0 ? abs(X) : -abs(X) --> -abs(X)
     // X == 0 ? -abs(X) : abs(X) --> abs(X)
diff --git a/llvm/test/Transforms/InstSimplify/call.ll b/llvm/test/Transforms/InstSimplify/call.ll
index 300c27c98ef99..e1c34f9a45823 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -867,6 +867,34 @@ define i9 @rotr_zero_shift_guard_inverted_swapped(i9 %x, i9 %sh) {
   ret i9 %s
 }
 
+; Guard on rotate could be removed even when the shift amount is multiple of bitwidth and not necessarily zero.
+define i8 @rotr_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
+; CHECK-LABEL: @rotr_shift_amount_multiple_of_bitwidth(
+; CHECK-NEXT:    [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[X]], i8 [[SH]])
+; CHECK-NEXT:    ret i8 [[F]]
+;
+  %r = and i8 %sh, 7
+  %c = icmp eq i8 %r, 0
+  %f = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 %sh)
+  %s = select i1 %c, i8 %x, i8 %f
+  ret i8 %s
+}
+
+; Vector typed variant.
+define <4 x i8> @rotr_shift_amount_multiple_of_bitwidth_vector(
+    <4 x i8> %x, <4 x i8> %sh) {
+; CHECK-LABEL: @rotr_shift_amount_multiple_of_bitwidth_vector(
+; CHECK-NEXT:    [[F:%.*]] = call <4 x i8> @llvm.fshl.v4i8(
+; CHECK-SAME:        <4 x i8> [[X:%.*]], <4 x i8> [[X]], <4 x i8> [[SH:%.*]])
+; CHECK-NEXT:    ret <4 x i8> [[F]]
+;
+  %r = and <4 x i8> %sh, <i8 7, i8 7, i8 7, i8 7>
+  %c = icmp eq <4 x i8> %r, zeroinitializer
+  %f = call <4 x i8> @llvm.fshl.v4i8(<4 x i8> %x, <4 x i8> %x, <4 x i8> %sh)
+  %s = select <4 x i1> %c, <4 x i8> %x, <4 x i8> %f
+  ret <4 x i8> %s
+}
+
 ; Negative test - make sure we're matching the correct parameter of fshl.
 
 define i8 @fshl_zero_shift_guard_wrong_select_op(i8 %x, i8 %y, i8 %sh) {

>From 0788cd72176c443e5c61cc4962b45a1b236cb1b2 Mon Sep 17 00:00:00 2001
From: Vimal Patel <vimal.patel at arm.com>
Date: Tue, 18 Aug 2026 14:27:07 +0000
Subject: [PATCH 2/3] Add pre-commit tests

Change-Id: Ib4797c2692c59b658f06953653a9f1c6a6034d5d
---
 llvm/test/Transforms/InstSimplify/call.ll | 46 +++++++++++++++++------
 1 file changed, 35 insertions(+), 11 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/call.ll b/llvm/test/Transforms/InstSimplify/call.ll
index e1c34f9a45823..d5b93b233a02b 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -868,10 +868,14 @@ define i9 @rotr_zero_shift_guard_inverted_swapped(i9 %x, i9 %sh) {
 }
 
 ; Guard on rotate could be removed even when the shift amount is multiple of bitwidth and not necessarily zero.
-define i8 @rotr_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
-; CHECK-LABEL: @rotr_shift_amount_multiple_of_bitwidth(
-; CHECK-NEXT:    [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[X]], i8 [[SH]])
-; CHECK-NEXT:    ret i8 [[F]]
+define i8 @rotl_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
+; CHECK-LABEL: define i8 @rotl_shift_amount_multiple_of_bitwidth(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[SH:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = and i8 [[SH]], 7
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X]], i8 [[X]], i8 [[SH]])
+; CHECK-NEXT:    [[S:%.*]] = select i1 [[C]], i8 [[X]], i8 [[F]]
+; CHECK-NEXT:    ret i8 [[S]]
 ;
   %r = and i8 %sh, 7
   %c = icmp eq i8 %r, 0
@@ -881,13 +885,16 @@ define i8 @rotr_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
 }
 
 ; Vector typed variant.
-define <4 x i8> @rotr_shift_amount_multiple_of_bitwidth_vector(
-    <4 x i8> %x, <4 x i8> %sh) {
-; CHECK-LABEL: @rotr_shift_amount_multiple_of_bitwidth_vector(
-; CHECK-NEXT:    [[F:%.*]] = call <4 x i8> @llvm.fshl.v4i8(
-; CHECK-SAME:        <4 x i8> [[X:%.*]], <4 x i8> [[X]], <4 x i8> [[SH:%.*]])
-; CHECK-NEXT:    ret <4 x i8> [[F]]
-;
+define <4 x i8> @rotl_shift_amount_multiple_of_bitwidth_vector(
+; CHECK-LABEL: define <4 x i8> @rotl_shift_amount_multiple_of_bitwidth_vector(
+; CHECK-SAME: <4 x i8> [[X:%.*]], <4 x i8> [[SH:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = and <4 x i8> [[SH]], splat (i8 7)
+; CHECK-NEXT:    [[C:%.*]] = icmp eq <4 x i8> [[R]], zeroinitializer
+; CHECK-NEXT:    [[F:%.*]] = call <4 x i8> @llvm.fshl.v4i8(<4 x i8> [[X]], <4 x i8> [[X]], <4 x i8> [[SH]])
+; CHECK-NEXT:    [[S:%.*]] = select <4 x i1> [[C]], <4 x i8> [[X]], <4 x i8> [[F]]
+; CHECK-NEXT:    ret <4 x i8> [[S]]
+;
+  <4 x i8> %x, <4 x i8> %sh) {
   %r = and <4 x i8> %sh, <i8 7, i8 7, i8 7, i8 7>
   %c = icmp eq <4 x i8> %r, zeroinitializer
   %f = call <4 x i8> @llvm.fshl.v4i8(<4 x i8> %x, <4 x i8> %x, <4 x i8> %sh)
@@ -895,6 +902,23 @@ define <4 x i8> @rotr_shift_amount_multiple_of_bitwidth_vector(
   ret <4 x i8> %s
 }
 
+; Test rotr as well.
+define i8 @rotr_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
+; CHECK-LABEL: define i8 @rotr_shift_amount_multiple_of_bitwidth(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[SH:%.*]]) {
+; CHECK-NEXT:    [[R:%.*]] = and i8 [[SH]], 7
+; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
+; CHECK-NEXT:    [[F:%.*]] = call i8 @llvm.fshr.i8(i8 [[X]], i8 [[X]], i8 [[SH]])
+; CHECK-NEXT:    [[S:%.*]] = select i1 [[C]], i8 [[X]], i8 [[F]]
+; CHECK-NEXT:    ret i8 [[S]]
+;
+  %r = and i8 %sh, 7
+  %c = icmp eq i8 %r, 0
+  %f = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 %sh)
+  %s = select i1 %c, i8 %x, i8 %f
+  ret i8 %s
+}
+
 ; Negative test - make sure we're matching the correct parameter of fshl.
 
 define i8 @fshl_zero_shift_guard_wrong_select_op(i8 %x, i8 %y, i8 %sh) {

>From 7efb43de62c81cd70a68f9cb0b6435ad1815de59 Mon Sep 17 00:00:00 2001
From: Vimal Patel <vimal.patel at arm.com>
Date: Tue, 18 Aug 2026 14:31:10 +0000
Subject: [PATCH 3/3] Update tests

Change-Id: I294c80bdeab001f25a0a79a40dd4c52df583402f
---
 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 d5b93b233a02b..e03759712071c 100644
--- a/llvm/test/Transforms/InstSimplify/call.ll
+++ b/llvm/test/Transforms/InstSimplify/call.ll
@@ -871,11 +871,8 @@ define i9 @rotr_zero_shift_guard_inverted_swapped(i9 %x, i9 %sh) {
 define i8 @rotl_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
 ; CHECK-LABEL: define i8 @rotl_shift_amount_multiple_of_bitwidth(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[SH:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = and i8 [[SH]], 7
-; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
 ; CHECK-NEXT:    [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X]], i8 [[X]], i8 [[SH]])
-; CHECK-NEXT:    [[S:%.*]] = select i1 [[C]], i8 [[X]], i8 [[F]]
-; CHECK-NEXT:    ret i8 [[S]]
+; CHECK-NEXT:    ret i8 [[F]]
 ;
   %r = and i8 %sh, 7
   %c = icmp eq i8 %r, 0
@@ -888,11 +885,8 @@ define i8 @rotl_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
 define <4 x i8> @rotl_shift_amount_multiple_of_bitwidth_vector(
 ; CHECK-LABEL: define <4 x i8> @rotl_shift_amount_multiple_of_bitwidth_vector(
 ; CHECK-SAME: <4 x i8> [[X:%.*]], <4 x i8> [[SH:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = and <4 x i8> [[SH]], splat (i8 7)
-; CHECK-NEXT:    [[C:%.*]] = icmp eq <4 x i8> [[R]], zeroinitializer
 ; CHECK-NEXT:    [[F:%.*]] = call <4 x i8> @llvm.fshl.v4i8(<4 x i8> [[X]], <4 x i8> [[X]], <4 x i8> [[SH]])
-; CHECK-NEXT:    [[S:%.*]] = select <4 x i1> [[C]], <4 x i8> [[X]], <4 x i8> [[F]]
-; CHECK-NEXT:    ret <4 x i8> [[S]]
+; CHECK-NEXT:    ret <4 x i8> [[F]]
 ;
   <4 x i8> %x, <4 x i8> %sh) {
   %r = and <4 x i8> %sh, <i8 7, i8 7, i8 7, i8 7>
@@ -906,11 +900,8 @@ define <4 x i8> @rotl_shift_amount_multiple_of_bitwidth_vector(
 define i8 @rotr_shift_amount_multiple_of_bitwidth(i8 %x, i8 %sh) {
 ; CHECK-LABEL: define i8 @rotr_shift_amount_multiple_of_bitwidth(
 ; CHECK-SAME: i8 [[X:%.*]], i8 [[SH:%.*]]) {
-; CHECK-NEXT:    [[R:%.*]] = and i8 [[SH]], 7
-; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[R]], 0
 ; CHECK-NEXT:    [[F:%.*]] = call i8 @llvm.fshr.i8(i8 [[X]], i8 [[X]], i8 [[SH]])
-; CHECK-NEXT:    [[S:%.*]] = select i1 [[C]], i8 [[X]], i8 [[F]]
-; CHECK-NEXT:    ret i8 [[S]]
+; CHECK-NEXT:    ret i8 [[F]]
 ;
   %r = and i8 %sh, 7
   %c = icmp eq i8 %r, 0



More information about the llvm-commits mailing list