[llvm] Test changes (PR #214439)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 6 02:40:34 PDT 2026
https://github.com/Ineshmcw created https://github.com/llvm/llvm-project/pull/214439
None
>From 6c45e7c6870e9d69d43a72c2c2a88c3ab0741fa4 Mon Sep 17 00:00:00 2001
From: Sree Inesh Murugan Palanisamy <inesh.murugan at multicorewareinc.com>
Date: Thu, 6 Aug 2026 15:08:50 +0530
Subject: [PATCH] Test changes
---
.../InstCombine/InstCombineCasts.cpp | 18 ++
.../InstCombine/trunc-Ishr-add-shl.ll | 167 ++++++++++++++++++
2 files changed, 185 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/trunc-Ishr-add-shl.ll
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 2db6396b9d661..0eaae3c2dc729 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -1216,6 +1216,24 @@ Instruction *InstCombinerImpl::visitTrunc(TruncInst &Trunc) {
// TODO: Mask high bits with 'and'.
}
+ // trunc (lshr (add (shl X, ShAmt), AddC), ShrAmt) -->
+ // trunc (add (shl X, ShAmt - ShrAmt), AddC >> ShrAmt)
+ {
+ const APInt *ShAmt, *AddC, *ShrAmt;
+ if (match(Src, m_OneUse(m_LShr(m_Add(m_Shl(m_Value(X), m_APInt(ShAmt)),
+ m_APInt(AddC)),
+ m_APInt(ShrAmt)))) &&
+ ShrAmt->ule(*ShAmt) &&
+ ShrAmt->getZExtValue() <= SrcWidth - DestWidth &&
+ AddC->countr_zero() >= ShrAmt->getZExtValue()) {
+ Value *NewShl = Builder.CreateShl(
+ X, ConstantInt::get(SrcTy, *ShAmt - *ShrAmt));
+ Value *NewAdd =
+ Builder.CreateAdd(NewShl, ConstantInt::get(SrcTy, AddC->lshr(*ShrAmt)));
+ return new TruncInst(NewAdd, DestTy);
+ }
+ }
+
if (Instruction *I = narrowBinOp(Trunc))
return I;
diff --git a/llvm/test/Transforms/InstCombine/trunc-Ishr-add-shl.ll b/llvm/test/Transforms/InstCombine/trunc-Ishr-add-shl.ll
new file mode 100644
index 0000000000000..de2a6064b53f4
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/trunc-Ishr-add-shl.ll
@@ -0,0 +1,167 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; trunc(lshr(add(shl(X, ShAmt), AddC), ShrAmt))
+; --> trunc(add(shl(X, ShAmt - ShrAmt), AddC >> ShrAmt))
+;
+; %x is the result of an opaque call in most tests below purely so the
+; RUN line's output is stable and easy to read; InstCombine's generic
+; "shrink the whole expression to a smaller type" machinery still applies
+; on top of this fold once the lshr is gone, which is expected -- it just
+; means the fold enables further cleanup, not that it's being bypassed.
+declare i32 @opaque(i32)
+declare void @use32(i32)
+
+define i8 @src_from_issue(i32 %x) {
+; CHECK-LABEL: define i8 @src_from_issue(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X]] to i8
+; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[X_TR]], 4
+; CHECK-NEXT: [[V17:%.*]] = add i8 [[TMP1]], -120
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %v2 = shl i32 %x, 12
+ %v3 = add i32 %v2, 34816
+ %v16 = lshr i32 %v3, 8
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+define i8 @positive_basic(i32 %y) {
+; CHECK-LABEL: define i8 @positive_basic(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X]] to i8
+; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[X_TR]], 4
+; CHECK-NEXT: [[V17:%.*]] = add i8 [[TMP1]], -120
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 12
+ %v3 = add i32 %v2, 34816
+ %v16 = lshr i32 %v3, 8
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+; ShrAmt == ShAmt: the new shift amount is 0.
+define i8 @positive_boundary_shramt_eq_shamt(i32 %y) {
+; CHECK-LABEL: define i8 @positive_boundary_shramt_eq_shamt(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[X]] to i8
+; CHECK-NEXT: [[V17:%.*]] = add i8 [[TMP1]], -120
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 8
+ %v3 = add i32 %v2, 34816
+ %v16 = lshr i32 %v3, 8
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+; ShrAmt == SrcWidth - DestWidth (24): still safe, right at the boundary.
+define i8 @positive_boundary_shramt_eq_srcwidth_minus_destwidth(i32 %y) {
+; CHECK-LABEL: define i8 @positive_boundary_shramt_eq_srcwidth_minus_destwidth(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[X]] to i8
+; CHECK-NEXT: [[V17:%.*]] = add i8 [[TMP1]], 1
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 24
+ %v3 = add i32 %v2, 16777216
+ %v16 = lshr i32 %v3, 24
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+; Negative test: ShrAmt (4) < ShAmt is fine, but here ShrAmt (8) > ShAmt (4),
+; which would require a negative shift amount, so the fold must not fire.
+define i8 @negative_shramt_gt_shamt(i32 %y) {
+; CHECK-LABEL: define i8 @negative_shramt_gt_shamt(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[V2:%.*]] = shl i32 [[X]], 4
+; CHECK-NEXT: [[V3:%.*]] = add i32 [[V2]], 34816
+; CHECK-NEXT: [[V16:%.*]] = lshr i32 [[V3]], 8
+; CHECK-NEXT: [[V17:%.*]] = trunc i32 [[V16]] to i8
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 4
+ %v3 = add i32 %v2, 34816
+ %v16 = lshr i32 %v3, 8
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+; Negative test: AddC's low ShrAmt bits are not zero (34817 is not a
+; multiple of 256), so the lshr can't be distributed over the add exactly.
+; %v3 is kept alive via @use32 so an unrelated demanded-bits simplification
+; can't quietly clear that low bit before this fold's check ever runs.
+define i8 @negative_addc_not_aligned(i32 %y) {
+; CHECK-LABEL: define i8 @negative_addc_not_aligned(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[V2:%.*]] = shl i32 [[X]], 12
+; CHECK-NEXT: [[V3:%.*]] = add i32 [[V2]], 34817
+; CHECK-NEXT: call void @use32(i32 [[V3]])
+; CHECK-NEXT: [[V16:%.*]] = lshr i32 [[V3]], 8
+; CHECK-NEXT: [[V17:%.*]] = trunc i32 [[V16]] to i8
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 12
+ %v3 = add i32 %v2, 34817
+ call void @use32(i32 %v3)
+ %v16 = lshr i32 %v3, 8
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+; Negative test: ShrAmt (25) > SrcWidth - DestWidth (24), even though AddC
+; (2^25) is still a multiple of 2^25. A freshly computed "X << (ShAmt -
+; ShrAmt)" would disagree with "(X << ShAmt) >> ShrAmt" in a bit that the
+; final trunc to i8 would still observe, so the fold must not fire.
+define i8 @negative_shramt_too_large(i32 %y) {
+; CHECK-LABEL: define i8 @negative_shramt_too_large(
+; CHECK-SAME: i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[X_TR:%.*]] = trunc i32 [[X]] to i8
+; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[X_TR]], 5
+; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], 96
+; CHECK-NEXT: [[V17:%.*]] = or disjoint i8 [[TMP2]], 1
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 30
+ %v3 = add i32 %v2, 33554432
+ %v16 = lshr i32 %v3, 25
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
+
+; Negative test: the lshr has another use, so replacing the trunc's operand
+; would leave the original computation behind rather than eliminating it.
+define i8 @negative_lshr_multi_use(i32 %y, ptr %p) {
+; CHECK-LABEL: define i8 @negative_lshr_multi_use(
+; CHECK-SAME: i32 [[Y:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 @opaque(i32 [[Y]])
+; CHECK-NEXT: [[V2:%.*]] = shl i32 [[X]], 12
+; CHECK-NEXT: [[V3:%.*]] = add i32 [[V2]], 34816
+; CHECK-NEXT: [[V16:%.*]] = lshr exact i32 [[V3]], 8
+; CHECK-NEXT: store i32 [[V16]], ptr [[P]], align 4
+; CHECK-NEXT: [[V17:%.*]] = trunc i32 [[V16]] to i8
+; CHECK-NEXT: ret i8 [[V17]]
+;
+ %x = call i32 @opaque(i32 %y)
+ %v2 = shl i32 %x, 12
+ %v3 = add i32 %v2, 34816
+ %v16 = lshr i32 %v3, 8
+ store i32 %v16, ptr %p
+ %v17 = trunc i32 %v16 to i8
+ ret i8 %v17
+}
More information about the llvm-commits
mailing list