[llvm] [InstComb] Fold shr ([x]or x, y), x -> shr y, x (PR #165866)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 31 08:40:38 PDT 2025


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/165866

>From abb1953b82b6319b9a5d166c5896ec64ff971c2e Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <ramkumar.ramachandra at codasip.com>
Date: Fri, 31 Oct 2025 13:57:42 +0000
Subject: [PATCH 1/2] [InstComb] Pre-commit (l|a)shr-[x]or test

---
 .../Transforms/InstCombine/shift-logic.ll     | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/shift-logic.ll b/llvm/test/Transforms/InstCombine/shift-logic.ll
index ab8d98a9523ba..5cb24b37684fc 100644
--- a/llvm/test/Transforms/InstCombine/shift-logic.ll
+++ b/llvm/test/Transforms/InstCombine/shift-logic.ll
@@ -186,6 +186,7 @@ define i32 @ashr_xor(i32 %x, i32 %py) {
   ret i32 %sh1
 }
 
+
 define i32 @shr_mismatch_xor(i32 %x, i32 %y) {
 ; CHECK-LABEL: @shr_mismatch_xor(
 ; CHECK-NEXT:    [[SH0:%.*]] = ashr i32 [[X:%.*]], 5
@@ -546,3 +547,104 @@ define <2 x i64> @lshr_sub_poison(<2 x i64> %x, <2 x i64> %py) {
   %sh1 = lshr <2 x i64> %r, <i64 7, i64 poison>
   ret <2 x i64> %sh1
 }
+
+define i32 @ashr_xor_operand_match(i32 %x, i32 %y) {
+; CHECK-LABEL: @ashr_xor_operand_match(
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R]], [[X]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = xor i32 %x, %y
+  %ret = ashr i32 %r, %x
+  ret i32 %ret
+}
+
+define i32 @ashr_xor_operand_mismtach(i32 %x, i32 %y) {
+; CHECK-LABEL: @ashr_xor_operand_mismtach(
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = xor i32 %x, %y
+  %ret = ashr i32 %r, %y
+  ret i32 %ret
+}
+
+define i32 @lshr_xor_operand_match(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_xor_operand_match(
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R]], [[X]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = xor i32 %x, %y
+  %ret = lshr i32 %r, %x
+  ret i32 %ret
+}
+
+define i32 @lshr_xor_operand_mismtach(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_xor_operand_mismtach(
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = xor i32 %x, %y
+  %ret = lshr i32 %r, %y
+  ret i32 %ret
+}
+
+define i32 @ashr_or_operand_match(i32 %x, i32 %y) {
+; CHECK-LABEL: @ashr_or_operand_match(
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R]], [[X]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = or i32 %x, %y
+  %ret = ashr i32 %r, %x
+  ret i32 %ret
+}
+
+define i32 @ashr_or_operand_mismtach(i32 %x, i32 %y) {
+; CHECK-LABEL: @ashr_or_operand_mismtach(
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = or i32 %x, %y
+  %ret = ashr i32 %r, %y
+  ret i32 %ret
+}
+
+define i32 @lshr_or_operand_match(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_or_operand_match(
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R]], [[X]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = or i32 %x, %y
+  %ret = lshr i32 %r, %x
+  ret i32 %ret
+}
+
+define i32 @lshr_or_operand_mismtach(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_or_operand_mismtach(
+; CHECK-NEXT:    [[R:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R]], [[Y]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = or i32 %x, %y
+  %ret = lshr i32 %r, %y
+  ret i32 %ret
+}
+
+define i32 @ashr_xor_operand_match_multiuse(i32 %x, i32 %y) {
+; CHECK-LABEL: @ashr_xor_operand_match_multiuse(
+; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    [[Q:%.*]] = ashr i32 [[R]], [[X]]
+; CHECK-NEXT:    [[RET:%.*]] = xor i32 [[R]], [[Q]]
+; CHECK-NEXT:    ret i32 [[RET]]
+;
+  %r = xor i32 %x, %y
+  %q = ashr i32 %r, %x
+  %ret = xor i32 %r, %q
+  ret i32 %ret
+}

>From 65a88fa1e5671fd35af75ad9646e338626fe94b5 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <ramkumar.ramachandra at codasip.com>
Date: Fri, 31 Oct 2025 13:59:20 +0000
Subject: [PATCH 2/2] [InstComb] Fold shr ([x]or x, y), x -> shr y, x

Proof: https://alive2.llvm.org/ce/z/yWCmMd

Co-authored-by: John Regehr <regehr at cs.utah.edu>
---
 .../InstCombine/InstCombineShifts.cpp         | 34 ++++++++++++-------
 .../Transforms/InstCombine/shift-logic.ll     | 18 ++++------
 2 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
index 899a3c16554c9..10bf908108aef 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
@@ -512,19 +512,27 @@ Instruction *InstCombinerImpl::commonShiftTransforms(BinaryOperator &I) {
   if (match(Op1, m_Or(m_Value(), m_SpecificInt(BitWidth - 1))))
     return replaceOperand(I, 1, ConstantInt::get(Ty, BitWidth - 1));
 
-  Instruction *CmpIntr;
-  if ((I.getOpcode() == Instruction::LShr ||
-       I.getOpcode() == Instruction::AShr) &&
-      match(Op0, m_OneUse(m_Instruction(CmpIntr))) &&
-      isa<CmpIntrinsic>(CmpIntr) &&
-      match(Op1, m_SpecificInt(Ty->getScalarSizeInBits() - 1))) {
-    Value *Cmp =
-        Builder.CreateICmp(cast<CmpIntrinsic>(CmpIntr)->getLTPredicate(),
-                           CmpIntr->getOperand(0), CmpIntr->getOperand(1));
-    return CastInst::Create(I.getOpcode() == Instruction::LShr
-                                ? Instruction::ZExt
-                                : Instruction::SExt,
-                            Cmp, Ty);
+  if (I.getOpcode() == Instruction::LShr ||
+      I.getOpcode() == Instruction::AShr) {
+    // (l|a)shr ([x]or x, y), x --> (l|a)shr y, x
+    Value *X;
+    if (match(Op0, m_CombineOr(m_Xor(m_Value(X), m_Value(Y)),
+                               m_Or(m_Value(X), m_Value(Y)))) &&
+        Op1 == X)
+      return BinaryOperator::Create(I.getOpcode(), Y, X);
+
+    Instruction *CmpIntr;
+    if (match(Op0, m_OneUse(m_Instruction(CmpIntr))) &&
+        isa<CmpIntrinsic>(CmpIntr) &&
+        match(Op1, m_SpecificInt(Ty->getScalarSizeInBits() - 1))) {
+      Value *Cmp =
+          Builder.CreateICmp(cast<CmpIntrinsic>(CmpIntr)->getLTPredicate(),
+                             CmpIntr->getOperand(0), CmpIntr->getOperand(1));
+      return CastInst::Create(I.getOpcode() == Instruction::LShr
+                                  ? Instruction::ZExt
+                                  : Instruction::SExt,
+                              Cmp, Ty);
+    }
   }
 
   return nullptr;
diff --git a/llvm/test/Transforms/InstCombine/shift-logic.ll b/llvm/test/Transforms/InstCombine/shift-logic.ll
index 5cb24b37684fc..3b58af47b84ae 100644
--- a/llvm/test/Transforms/InstCombine/shift-logic.ll
+++ b/llvm/test/Transforms/InstCombine/shift-logic.ll
@@ -550,8 +550,7 @@ define <2 x i64> @lshr_sub_poison(<2 x i64> %x, <2 x i64> %py) {
 
 define i32 @ashr_xor_operand_match(i32 %x, i32 %y) {
 ; CHECK-LABEL: @ashr_xor_operand_match(
-; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R]], [[X]]
+; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret i32 [[RET]]
 ;
   %r = xor i32 %x, %y
@@ -572,8 +571,7 @@ define i32 @ashr_xor_operand_mismtach(i32 %x, i32 %y) {
 
 define i32 @lshr_xor_operand_match(i32 %x, i32 %y) {
 ; CHECK-LABEL: @lshr_xor_operand_match(
-; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R]], [[X]]
+; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret i32 [[RET]]
 ;
   %r = xor i32 %x, %y
@@ -594,8 +592,7 @@ define i32 @lshr_xor_operand_mismtach(i32 %x, i32 %y) {
 
 define i32 @ashr_or_operand_match(i32 %x, i32 %y) {
 ; CHECK-LABEL: @ashr_or_operand_match(
-; CHECK-NEXT:    [[R:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R]], [[X]]
+; CHECK-NEXT:    [[RET:%.*]] = ashr i32 [[R:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret i32 [[RET]]
 ;
   %r = or i32 %x, %y
@@ -616,8 +613,7 @@ define i32 @ashr_or_operand_mismtach(i32 %x, i32 %y) {
 
 define i32 @lshr_or_operand_match(i32 %x, i32 %y) {
 ; CHECK-LABEL: @lshr_or_operand_match(
-; CHECK-NEXT:    [[R:%.*]] = or i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R]], [[X]]
+; CHECK-NEXT:    [[RET:%.*]] = lshr i32 [[R:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    ret i32 [[RET]]
 ;
   %r = or i32 %x, %y
@@ -638,10 +634,10 @@ define i32 @lshr_or_operand_mismtach(i32 %x, i32 %y) {
 
 define i32 @ashr_xor_operand_match_multiuse(i32 %x, i32 %y) {
 ; CHECK-LABEL: @ashr_xor_operand_match_multiuse(
-; CHECK-NEXT:    [[R:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]]
-; CHECK-NEXT:    [[Q:%.*]] = ashr i32 [[R]], [[X]]
+; CHECK-NEXT:    [[Q:%.*]] = ashr i32 [[R:%.*]], [[X:%.*]]
 ; CHECK-NEXT:    [[RET:%.*]] = xor i32 [[R]], [[Q]]
-; CHECK-NEXT:    ret i32 [[RET]]
+; CHECK-NEXT:    [[RET1:%.*]] = xor i32 [[RET]], [[X]]
+; CHECK-NEXT:    ret i32 [[RET1]]
 ;
   %r = xor i32 %x, %y
   %q = ashr i32 %r, %x



More information about the llvm-commits mailing list