[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