[llvm] [InstCombine] Recognize more rotation patterns (Part 1) (PR #78107)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Sun Jan 14 09:21:14 PST 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/78107

InstCombine already handles the pattern `(shl ShVal, (X & (Width - 1))) | (lshr ShVal, ((-X) & (Width - 1)))`. Under certain circumstances, `X & (Width - 1)` will be simplified to `X`. Therefore, this patch adds support for the pattern `(shl ShVal, X) | (lshr ShVal, ((-X) & (Width - 1)))`.

Alive2: https://alive2.llvm.org/ce/z/P7JQ2V

>From 753349c480b8b1880f00aba19ea5905dff954f9f Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 15 Jan 2024 01:08:28 +0800
Subject: [PATCH 1/2] [InstCombine] Add pre-commit tests. NFC.

---
 llvm/test/Transforms/InstCombine/funnel.ll | 88 ++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/funnel.ll b/llvm/test/Transforms/InstCombine/funnel.ll
index 772a052b3a4f84..1f808270632253 100644
--- a/llvm/test/Transforms/InstCombine/funnel.ll
+++ b/llvm/test/Transforms/InstCombine/funnel.ll
@@ -559,3 +559,91 @@ define i8 @unmasked_shlop_masked_shift_amount(i16 %x, i16 %y, i16 %shamt) {
   %t8 = trunc i16 %t7 to i8
   ret i8 %t8
 }
+
+define i32 @test_rotl_and_neg(i32 %x, i32 %shamt) {
+; CHECK-LABEL: @test_rotl_and_neg(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], [[SHAMT:%.*]]
+; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], [[AND]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHL]], [[SHR]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %shl = shl i32 %x, %shamt
+  %neg = sub i32 0, %shamt
+  %and = and i32 %neg, 31
+  %shr = lshr i32 %x, %and
+  %or = or i32 %shl, %shr
+  ret i32 %or
+}
+
+define i32 @test_rotl_and_neg_commuted(i32 %x, i32 %shamt) {
+; CHECK-LABEL: @test_rotl_and_neg_commuted(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], [[SHAMT:%.*]]
+; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], [[AND]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHR]], [[SHL]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %shl = shl i32 %x, %shamt
+  %neg = sub i32 0, %shamt
+  %and = and i32 %neg, 31
+  %shr = lshr i32 %x, %and
+  %or = or i32 %shr, %shl
+  ret i32 %or
+}
+
+define i32 @test_rotr_and_neg(i32 %x, i32 %shamt) {
+; CHECK-LABEL: @test_rotr_and_neg(
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X:%.*]], [[SHAMT:%.*]]
+; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X]], [[AND]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHL]], [[SHR]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %shr = lshr i32 %x, %shamt
+  %neg = sub i32 0, %shamt
+  %and = and i32 %neg, 31
+  %shl = shl i32 %x, %and
+  %or = or i32 %shl, %shr
+  ret i32 %or
+}
+
+; Negative tests
+
+; Only work for rotation patterns
+define i32 @test_fshl_and_neg(i32 %x, i32 %y, i32 %shamt) {
+; CHECK-LABEL: @test_fshl_and_neg(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], [[SHAMT:%.*]]
+; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[Y:%.*]], [[AND]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHL]], [[SHR]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %shl = shl i32 %x, %shamt
+  %neg = sub i32 0, %shamt
+  %and = and i32 %neg, 31
+  %shr = lshr i32 %y, %and
+  %or = or i32 %shl, %shr
+  ret i32 %or
+}
+
+define i32 @test_rotl_and_neg_wrong_mask(i32 %x, i32 %shamt) {
+; CHECK-LABEL: @test_rotl_and_neg_wrong_mask(
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], [[SHAMT:%.*]]
+; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 15
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], [[AND]]
+; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHL]], [[SHR]]
+; CHECK-NEXT:    ret i32 [[OR]]
+;
+  %shl = shl i32 %x, %shamt
+  %neg = sub i32 0, %shamt
+  %and = and i32 %neg, 15
+  %shr = lshr i32 %x, %and
+  %or = or i32 %shl, %shr
+  ret i32 %or
+}

>From d3d89544e50b0a87d2424b6f9727c3576d390ace Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 15 Jan 2024 01:10:14 +0800
Subject: [PATCH 2/2] [InstCombine] Recognize more rotation patterns

---
 .../InstCombine/InstCombineAndOrXor.cpp        |  4 ++++
 llvm/test/Transforms/InstCombine/funnel.ll     | 18 +++---------------
 2 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 0620752e321394..c97fd418e1b11d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2809,6 +2809,10 @@ static Instruction *matchFunnelShift(Instruction &Or, InstCombinerImpl &IC,
           match(R, m_And(m_Neg(m_Specific(X)), m_SpecificInt(Mask))))
         return X;
 
+      // (shl ShVal, X) | (lshr ShVal, ((-X) & (Width - 1)))
+      if (match(R, m_And(m_Neg(m_Specific(L)), m_SpecificInt(Mask))))
+        return L;
+
       // Similar to above, but the shift amount may be extended after masking,
       // so return the extended value as the parameter for the intrinsic.
       if (match(L, m_ZExt(m_And(m_Value(X), m_SpecificInt(Mask)))) &&
diff --git a/llvm/test/Transforms/InstCombine/funnel.ll b/llvm/test/Transforms/InstCombine/funnel.ll
index 1f808270632253..162519e648f3e4 100644
--- a/llvm/test/Transforms/InstCombine/funnel.ll
+++ b/llvm/test/Transforms/InstCombine/funnel.ll
@@ -562,11 +562,7 @@ define i8 @unmasked_shlop_masked_shift_amount(i16 %x, i16 %y, i16 %shamt) {
 
 define i32 @test_rotl_and_neg(i32 %x, i32 %shamt) {
 ; CHECK-LABEL: @test_rotl_and_neg(
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], [[SHAMT:%.*]]
-; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], [[AND]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHL]], [[SHR]]
+; CHECK-NEXT:    [[OR:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[SHAMT:%.*]])
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %shl = shl i32 %x, %shamt
@@ -579,11 +575,7 @@ define i32 @test_rotl_and_neg(i32 %x, i32 %shamt) {
 
 define i32 @test_rotl_and_neg_commuted(i32 %x, i32 %shamt) {
 ; CHECK-LABEL: @test_rotl_and_neg_commuted(
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X:%.*]], [[SHAMT:%.*]]
-; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X]], [[AND]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHR]], [[SHL]]
+; CHECK-NEXT:    [[OR:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[SHAMT:%.*]])
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %shl = shl i32 %x, %shamt
@@ -596,11 +588,7 @@ define i32 @test_rotl_and_neg_commuted(i32 %x, i32 %shamt) {
 
 define i32 @test_rotr_and_neg(i32 %x, i32 %shamt) {
 ; CHECK-LABEL: @test_rotr_and_neg(
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[X:%.*]], [[SHAMT:%.*]]
-; CHECK-NEXT:    [[NEG:%.*]] = sub i32 0, [[SHAMT]]
-; CHECK-NEXT:    [[AND:%.*]] = and i32 [[NEG]], 31
-; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X]], [[AND]]
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[SHL]], [[SHR]]
+; CHECK-NEXT:    [[OR:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[X]], i32 [[SHAMT:%.*]])
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %shr = lshr i32 %x, %shamt



More information about the llvm-commits mailing list