[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