[llvm-branch-commits] [llvm] [InstCombine] Move sext(trunc(...)) combines before EvaluateInDifferentType (PR #227321)
Luke Lau via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Oct 7 14:55:48 PDT 2026
https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/227321
>From 822f165d50e900282a47556f771bba1f56c8c583 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 29 Sep 2026 12:22:06 +0100
Subject: [PATCH 1/2] Precommit tests
---
.../AArch64/lshr-trunc-sext-to-ashr-sext.ll | 34 +++++++++++++++++++
.../RISCV/lshr-trunc-sext-to-ashr-sext.ll | 34 +++++++++++++++++++
.../X86/lshr-trunc-sext-to-ashr-sext.ll | 34 +++++++++++++++++++
3 files changed, 102 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll
create mode 100644 llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll
create mode 100644 llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll
diff --git a/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll b/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll
new file mode 100644
index 00000000000000..b721dfa14db627
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -p instcombine -mtriple aarch64 -S | FileCheck %s
+
+define i64 @t0(i32 %x) {
+; CHECK-LABEL: define i64 @t0(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X]], 16
+; CHECK-NEXT: [[B:%.*]] = zext nneg i32 [[A]] to i64
+; CHECK-NEXT: [[SEXT:%.*]] = shl nuw i64 [[B]], 48
+; CHECK-NEXT: [[C:%.*]] = ashr exact i64 [[SEXT]], 48
+; CHECK-NEXT: ret i64 [[C]]
+;
+ %a = lshr i32 %x, 16
+ %b = trunc i32 %a to i16
+ %c = sext i16 %b to i64
+ ret i64 %c
+}
+
+define i32 @t1(i16 %x) {
+; CHECK-LABEL: define i32 @t1(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32
+; CHECK-NEXT: [[Y:%.*]] = shl nsw i32 [[S]], 10
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y]], 32768
+; CHECK-NEXT: [[C:%.*]] = ashr i32 [[TMP1]], 16
+; CHECK-NEXT: ret i32 [[C]]
+;
+ %s = sext i16 %x to i32
+ %y = add i32 %s, 32
+ %a = ashr i32 %y, 6
+ %b = trunc i32 %a to i16
+ %c = sext i16 %b to i32
+ ret i32 %c
+}
diff --git a/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll b/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll
new file mode 100644
index 00000000000000..4a95ddf26b4d70
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -p instcombine -mtriple riscv64 -S | FileCheck %s
+
+define i64 @t0(i32 %x) {
+; CHECK-LABEL: define i64 @t0(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X]], 16
+; CHECK-NEXT: [[B:%.*]] = zext nneg i32 [[A]] to i64
+; CHECK-NEXT: [[SEXT:%.*]] = shl nuw i64 [[B]], 48
+; CHECK-NEXT: [[C:%.*]] = ashr exact i64 [[SEXT]], 48
+; CHECK-NEXT: ret i64 [[C]]
+;
+ %a = lshr i32 %x, 16
+ %b = trunc i32 %a to i16
+ %c = sext i16 %b to i64
+ ret i64 %c
+}
+
+define i32 @t1(i16 %x) {
+; CHECK-LABEL: define i32 @t1(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32
+; CHECK-NEXT: [[Y:%.*]] = shl nsw i32 [[S]], 10
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y]], 32768
+; CHECK-NEXT: [[C:%.*]] = ashr i32 [[TMP1]], 16
+; CHECK-NEXT: ret i32 [[C]]
+;
+ %s = sext i16 %x to i32
+ %y = add i32 %s, 32
+ %a = ashr i32 %y, 6
+ %b = trunc i32 %a to i16
+ %c = sext i16 %b to i32
+ ret i32 %c
+}
diff --git a/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll b/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll
new file mode 100644
index 00000000000000..8a93a37ee3e400
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -p instcombine -mtriple x86_64 -S | FileCheck %s
+
+define i64 @t0(i32 %x) {
+; CHECK-LABEL: define i64 @t0(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X]], 16
+; CHECK-NEXT: [[B:%.*]] = zext nneg i32 [[A]] to i64
+; CHECK-NEXT: [[SEXT:%.*]] = shl nuw i64 [[B]], 48
+; CHECK-NEXT: [[C:%.*]] = ashr exact i64 [[SEXT]], 48
+; CHECK-NEXT: ret i64 [[C]]
+;
+ %a = lshr i32 %x, 16
+ %b = trunc i32 %a to i16
+ %c = sext i16 %b to i64
+ ret i64 %c
+}
+
+define i32 @t1(i16 %x) {
+; CHECK-LABEL: define i32 @t1(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT: [[S:%.*]] = sext i16 [[X]] to i32
+; CHECK-NEXT: [[Y:%.*]] = shl nsw i32 [[S]], 10
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y]], 32768
+; CHECK-NEXT: [[C:%.*]] = ashr i32 [[TMP1]], 16
+; CHECK-NEXT: ret i32 [[C]]
+;
+ %s = sext i16 %x to i32
+ %y = add i32 %s, 32
+ %a = ashr i32 %y, 6
+ %b = trunc i32 %a to i16
+ %c = sext i16 %b to i32
+ ret i32 %c
+}
>From 31d2f8d80453e9c4f45c3c199957e77e296d2519 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 29 Sep 2026 14:14:48 +0100
Subject: [PATCH 2/2] [InstCombine] Move sext(trunc(...)) combines before
EvaluateInDifferentType
---
.../InstCombine/InstCombineCasts.cpp | 62 +++++++++----------
.../AArch64/lshr-trunc-sext-to-ashr-sext.ll | 6 +-
.../RISCV/lshr-trunc-sext-to-ashr-sext.ll | 6 +-
.../X86/lshr-trunc-sext-to-ashr-sext.ll | 6 +-
4 files changed, 37 insertions(+), 43 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
index 0275bf52a13f25..f3741e9b16b94e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
@@ -1945,37 +1945,8 @@ Instruction *InstCombinerImpl::visitSExt(SExtInst &Sext) {
return CI;
}
- // Try to extend the entire expression tree to the wide destination type.
- bool ShouldExtendExpression = true;
- Value *TruncSrc = nullptr;
- // It is not desirable to extend expression in the trunc + sext pattern when
- // destination type is narrower than original (pre-trunc) type.
- if (match(Src, m_Trunc(m_Value(TruncSrc))))
- if (TruncSrc->getType()->getScalarSizeInBits() > DestBitSize)
- ShouldExtendExpression = false;
- if (ShouldExtendExpression && shouldChangeType(SrcTy, DestTy) &&
- TypeEvaluationHelper::canEvaluateSExtd(Src, DestTy)) {
- // Okay, we can transform this! Insert the new expression now.
- LLVM_DEBUG(
- dbgs() << "ICE: EvaluateInDifferentType converting expression type"
- " to avoid sign extend: "
- << Sext << '\n');
- Value *Res = EvaluateInDifferentType(Src, DestTy, true);
- assert(Res->getType() == DestTy);
-
- // If the high bits are already filled with sign bit, just replace this
- // cast with the result.
- if (ComputeNumSignBits(Res, &Sext) > DestBitSize - SrcBitSize)
- return replaceInstUsesWith(Sext, Res);
-
- // We need to emit a shl + ashr to do the sign extend.
- Value *ShAmt = ConstantInt::get(DestTy, DestBitSize - SrcBitSize);
- return BinaryOperator::CreateAShr(Builder.CreateShl(Res, ShAmt, "sext"),
- ShAmt);
- }
-
- Value *X = TruncSrc;
- if (X) {
+ Value *X;
+ if (match(Src, m_Trunc(m_Value(X)))) {
// If the input has more sign bits than bits truncated, then convert
// directly to final type.
unsigned XBitSize = X->getType()->getScalarSizeInBits();
@@ -2012,6 +1983,35 @@ Instruction *InstCombinerImpl::visitSExt(SExtInst &Sext) {
}
}
+ // Try to extend the entire expression tree to the wide destination type.
+ bool ShouldExtendExpression = true;
+ Value *TruncSrc = nullptr;
+ // It is not desirable to extend expression in the trunc + sext pattern when
+ // destination type is narrower than original (pre-trunc) type.
+ if (match(Src, m_Trunc(m_Value(TruncSrc))))
+ if (TruncSrc->getType()->getScalarSizeInBits() > DestBitSize)
+ ShouldExtendExpression = false;
+ if (ShouldExtendExpression && shouldChangeType(SrcTy, DestTy) &&
+ TypeEvaluationHelper::canEvaluateSExtd(Src, DestTy)) {
+ // Okay, we can transform this! Insert the new expression now.
+ LLVM_DEBUG(
+ dbgs() << "ICE: EvaluateInDifferentType converting expression type"
+ " to avoid sign extend: "
+ << Sext << '\n');
+ Value *Res = EvaluateInDifferentType(Src, DestTy, true);
+ assert(Res->getType() == DestTy);
+
+ // If the high bits are already filled with sign bit, just replace this
+ // cast with the result.
+ if (ComputeNumSignBits(Res, &Sext) > DestBitSize - SrcBitSize)
+ return replaceInstUsesWith(Sext, Res);
+
+ // We need to emit a shl + ashr to do the sign extend.
+ Value *ShAmt = ConstantInt::get(DestTy, DestBitSize - SrcBitSize);
+ return BinaryOperator::CreateAShr(Builder.CreateShl(Res, ShAmt, "sext"),
+ ShAmt);
+ }
+
if (auto *Cmp = dyn_cast<ICmpInst>(Src))
return transformSExtICmp(Cmp, Sext);
diff --git a/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll b/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll
index b721dfa14db627..6451486688a123 100644
--- a/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll
+++ b/llvm/test/Transforms/InstCombine/AArch64/lshr-trunc-sext-to-ashr-sext.ll
@@ -4,10 +4,8 @@
define i64 @t0(i32 %x) {
; CHECK-LABEL: define i64 @t0(
; CHECK-SAME: i32 [[X:%.*]]) {
-; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X]], 16
-; CHECK-NEXT: [[B:%.*]] = zext nneg i32 [[A]] to i64
-; CHECK-NEXT: [[SEXT:%.*]] = shl nuw i64 [[B]], 48
-; CHECK-NEXT: [[C:%.*]] = ashr exact i64 [[SEXT]], 48
+; CHECK-NEXT: [[TMP1:%.*]] = ashr i32 [[X]], 16
+; CHECK-NEXT: [[C:%.*]] = sext i32 [[TMP1]] to i64
; CHECK-NEXT: ret i64 [[C]]
;
%a = lshr i32 %x, 16
diff --git a/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll b/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll
index 4a95ddf26b4d70..09d35c2cfa2c3b 100644
--- a/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll
+++ b/llvm/test/Transforms/InstCombine/RISCV/lshr-trunc-sext-to-ashr-sext.ll
@@ -4,10 +4,8 @@
define i64 @t0(i32 %x) {
; CHECK-LABEL: define i64 @t0(
; CHECK-SAME: i32 [[X:%.*]]) {
-; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X]], 16
-; CHECK-NEXT: [[B:%.*]] = zext nneg i32 [[A]] to i64
-; CHECK-NEXT: [[SEXT:%.*]] = shl nuw i64 [[B]], 48
-; CHECK-NEXT: [[C:%.*]] = ashr exact i64 [[SEXT]], 48
+; CHECK-NEXT: [[TMP1:%.*]] = ashr i32 [[X]], 16
+; CHECK-NEXT: [[C:%.*]] = sext i32 [[TMP1]] to i64
; CHECK-NEXT: ret i64 [[C]]
;
%a = lshr i32 %x, 16
diff --git a/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll b/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll
index 8a93a37ee3e400..852b6bc0daefea 100644
--- a/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll
+++ b/llvm/test/Transforms/InstCombine/X86/lshr-trunc-sext-to-ashr-sext.ll
@@ -4,10 +4,8 @@
define i64 @t0(i32 %x) {
; CHECK-LABEL: define i64 @t0(
; CHECK-SAME: i32 [[X:%.*]]) {
-; CHECK-NEXT: [[A:%.*]] = lshr i32 [[X]], 16
-; CHECK-NEXT: [[B:%.*]] = zext nneg i32 [[A]] to i64
-; CHECK-NEXT: [[SEXT:%.*]] = shl nuw i64 [[B]], 48
-; CHECK-NEXT: [[C:%.*]] = ashr exact i64 [[SEXT]], 48
+; CHECK-NEXT: [[TMP1:%.*]] = ashr i32 [[X]], 16
+; CHECK-NEXT: [[C:%.*]] = sext i32 [[TMP1]] to i64
; CHECK-NEXT: ret i64 [[C]]
;
%a = lshr i32 %x, 16
More information about the llvm-branch-commits
mailing list