[llvm] [InstCombine] Simplify zext(sub(0, trunc(x))) -> and(sub(0, x), mask) (Fixes #165306) (PR #167101)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 7 22:48:54 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: V S Susi Krishna (Susikrishna)
<details>
<summary>Changes</summary>
This patch adds a new InstCombine transformation that simplifies the pattern:
zext (sub (0, trunc X))
→ and (sub (0, X), (bitwidth - 1))
This canonicalization removes redundant trunc/zext pairs surrounding a negate
operation and replaces them with a masked negate of the original operand.
The transform helps expose rotate idioms in vector code, enabling targets such
as X86 (AVX2/AVX-512) to generate more efficient vpror/vpsllvq/vpsrlvq
instructions.
This change is motivated by issue #<!-- -->165306:
[AVX-512] Look for vector bit rotates on vectors larger than 16 bytes
#### Implementation details
* Added pattern matching logic to `InstCombineCasts.cpp`.
* Added a dedicated test file `rotate-trunc-zext.ll` covering:
- Scalar case (i64)
- Vector cases (<2 x i64>, <4 x i64>, <8 x i64>)
---
Full diff: https://github.com/llvm/llvm-project/pull/167101.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp (+16)
- (added) llvm/test/Transforms/InstCombine/rotate-trunc-zext.ll (+55)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 614c6ebd63be6..ebe1b747e6be4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -1366,6 +1366,22 @@ Instruction *InstCombinerImpl::visitZExt(ZExtInst &Zext) {
}
}
+ {
+ Value *TruncSrc = nullptr;
+ if (match(&Zext, m_ZExt(m_Sub(m_Zero(), m_Trunc(m_Value(TruncSrc)))))) {
+ IRBuilder<> Builder(&Zext);
+ Type *Ty = TruncSrc->getType();
+ unsigned BitWidth = Ty->getScalarSizeInBits();
+ unsigned MaskVal = BitWidth - 1;
+
+ Value *Zero = ConstantInt::get(Ty, 0);
+ Value *Neg = Builder.CreateSub(Zero, TruncSrc);
+ Value *Mask = ConstantInt::get(Ty, MaskVal);
+ Value *Masked = Builder.CreateAnd(Neg, Mask);
+ return replaceInstUsesWith(Zext, Masked);
+ }
+ }
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/rotate-trunc-zext.ll b/llvm/test/Transforms/InstCombine/rotate-trunc-zext.ll
new file mode 100644
index 0000000000000..31c7ba4a26796
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/rotate-trunc-zext.ll
@@ -0,0 +1,55 @@
+; RUN: opt -passes=instcombine -S %s | FileCheck %s
+
+; ================================================================
+; Test: Simplify zext(sub(0, trunc(x))) -> and(sub(0, x), (bitwidth-1))
+; Purpose: Check that InstCombine detects and simplifies the pattern
+; seen in rotate idioms, enabling backend rotate lowering.
+; ================================================================
+
+; === Scalar Case (i64) =========================================
+define i64 @neg_trunc_zext(i64 %a) {
+; CHECK-LABEL: @neg_trunc_zext(
+; CHECK-NEXT: %[[NEG:[0-9]+]] = sub i64 0, %a
+; CHECK-NEXT: %[[MASKED:[0-9A-Za-z_]+]] = and i64 %[[NEG]], 63
+; CHECK-NEXT: ret i64 %[[MASKED]]
+ %t = trunc i64 %a to i6
+ %n = sub i6 0, %t
+ %z = zext i6 %n to i64
+ ret i64 %z
+}
+
+; === Vector Case 1: <2 x i64> ==================================
+define <2 x i64> @foo(<2 x i64> %x, <2 x i64> %n) {
+; CHECK-LABEL: @foo(
+; CHECK: %[[NEG:[0-9A-Za-z_]+]] = sub <2 x i64> zeroinitializer, %n
+; CHECK: %[[MASK:[0-9A-Za-z_]+]] = and <2 x i64> %[[NEG]], splat (i64 63)
+; CHECK: ret <2 x i64> %[[MASK]]
+ %t = trunc <2 x i64> %n to <2 x i6>
+ %neg = sub <2 x i6> zeroinitializer, %t
+ %z = zext <2 x i6> %neg to <2 x i64>
+ ret <2 x i64> %z
+}
+
+; === Vector Case 2: <4 x i64> ==================================
+define <4 x i64> @bar(<4 x i64> %x, <4 x i64> %n) {
+; CHECK-LABEL: @bar(
+; CHECK: %[[NEG:[0-9A-Za-z_]+]] = sub <4 x i64> zeroinitializer, %n
+; CHECK: %[[MASK:[0-9A-Za-z_]+]] = and <4 x i64> %[[NEG]], splat (i64 63)
+; CHECK: ret <4 x i64> %[[MASK]]
+ %t = trunc <4 x i64> %n to <4 x i6>
+ %neg = sub <4 x i6> zeroinitializer, %t
+ %z = zext <4 x i6> %neg to <4 x i64>
+ ret <4 x i64> %z
+}
+
+; === Vector Case 3: <8 x i64> ==================================
+define <8 x i64> @baz(<8 x i64> %x, <8 x i64> %n) {
+; CHECK-LABEL: @baz(
+; CHECK: %[[NEG:[0-9A-Za-z_]+]] = sub <8 x i64> zeroinitializer, %n
+; CHECK: %[[MASK:[0-9A-Za-z_]+]] = and <8 x i64> %[[NEG]], splat (i64 63)
+; CHECK: ret <8 x i64> %[[MASK]]
+ %t = trunc <8 x i64> %n to <8 x i6>
+ %neg = sub <8 x i6> zeroinitializer, %t
+ %z = zext <8 x i6> %neg to <8 x i64>
+ ret <8 x i64> %z
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/167101
More information about the llvm-commits
mailing list