[llvm] [RISCV] Remove unneeded zext.h instructions (PR #225960)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 24 06:33:40 PDT 2026


https://github.com/bbbill42 updated https://github.com/llvm/llvm-project/pull/225960

>From bd8bb1469a4aee0fbb66455c12476fc1a47c0595 Mon Sep 17 00:00:00 2001
From: Ananth Jasty <ananth at x3ns.net>
Date: Thu, 24 Sep 2026 00:32:40 +0000
Subject: [PATCH 1/3] [RISCV] Preliminary patch for TypePromotion handling of
 sext for zext nneg.

---
 llvm/lib/CodeGen/TypePromotion.cpp | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/CodeGen/TypePromotion.cpp b/llvm/lib/CodeGen/TypePromotion.cpp
index 2736ff3f8a299..13e95bb46fc93 100644
--- a/llvm/lib/CodeGen/TypePromotion.cpp
+++ b/llvm/lib/CodeGen/TypePromotion.cpp
@@ -113,6 +113,7 @@ class IRPromoter {
   SmallPtrSet<Value *, 8> NewInsts;
   DenseMap<Value *, SmallVector<Type *, 4>> TruncTysMap;
   SmallPtrSet<Value *, 8> Promoted;
+  bool UseSExt;
 
   void ReplaceAllUsersOfWith(Value *From, Value *To);
   void ExtendSources();
@@ -125,9 +126,10 @@ class IRPromoter {
   IRPromoter(LLVMContext &C, unsigned Width, SetVector<Value *> &visited,
              SetVector<Value *> &sources, SetVector<Instruction *> &sinks,
              SmallPtrSetImpl<Instruction *> &wrap,
-             SmallPtrSetImpl<Instruction *> &instsToRemove)
+             SmallPtrSetImpl<Instruction *> &instsToRemove,
+             bool useSExt = false)
       : Ctx(C), PromotedWidth(Width), Visited(visited), Sources(sources),
-        Sinks(sinks), SafeWrap(wrap), InstsToRemove(instsToRemove) {
+        Sinks(sinks), SafeWrap(wrap), InstsToRemove(instsToRemove), UseSExt(useSExt) {
     ExtTy = IntegerType::get(Ctx, PromotedWidth);
   }
 
@@ -172,7 +174,8 @@ class TypePromotionImpl {
   // Is V an instruction thats result can trivially promoted, or has safe
   // wrapping.
   bool isLegalToPromote(Value *V);
-  bool TryToPromote(Value *V, unsigned PromotedWidth, const LoopInfo &LI);
+  bool TryToPromote(Value *V, unsigned PromotedWidth, const LoopInfo &LI,
+                    bool UseSExt = false);
 
 public:
   bool run(Function &F, const TargetMachine *TM,
@@ -453,8 +456,8 @@ void IRPromoter::ExtendSources() {
     if (auto *I = dyn_cast<Instruction>(V))
       Builder.SetCurrentDebugLocation(I->getDebugLoc());
 
-    Value *ZExt = Builder.CreateZExt(V, ExtTy);
-    if (auto *I = dyn_cast<Instruction>(ZExt)) {
+    Value *Ext = UseSExt ? Builder.CreateSExt(V, ExtTy) : Builder.CreateZExt(V, ExtTy);
+    if (auto *I = dyn_cast<Instruction>(Ext)) {
       if (isa<Argument>(V))
         I->moveBefore(InsertPt);
       else
@@ -462,7 +465,7 @@ void IRPromoter::ExtendSources() {
       NewInsts.insert(I);
     }
 
-    ReplaceAllUsersOfWith(V, ZExt);
+    ReplaceAllUsersOfWith(V, Ext);
   };
 
   // Now, insert extending instructions between the sources and their users.
@@ -814,7 +817,7 @@ bool TypePromotionImpl::isLegalToPromote(Value *V) {
 }
 
 bool TypePromotionImpl::TryToPromote(Value *V, unsigned PromotedWidth,
-                                 const LoopInfo &LI) {
+                                 const LoopInfo &LI, bool UseSExt) {
   Type *OrigTy = V->getType();
   TypeSize = OrigTy->getPrimitiveSizeInBits().getFixedValue();
   SafeToPromote.clear();
@@ -942,7 +945,7 @@ bool TypePromotionImpl::TryToPromote(Value *V, unsigned PromotedWidth,
     return false;
 
   IRPromoter Promoter(*Ctx, PromotedWidth, CurrentVisited, Sources, Sinks,
-                      SafeWrap, InstsToRemove);
+                      SafeWrap, InstsToRemove, UseSExt);
   Promoter.Mutate();
   return true;
 }
@@ -1008,6 +1011,8 @@ bool TypePromotionImpl::run(Function &F, const TargetMachine *TM,
           isa<IntegerType>(I.getType()) && BBIsInLoop(&BB)) {
         LLVM_DEBUG(dbgs() << "IR Promotion: Searching from: "
                           << *I.getOperand(0) << "\n");
+        auto *ZExt = cast<ZExtInst>(&I);
+        bool UseSExt = ZExt->hasNonNeg();
         EVT ZExtVT = TLI->getValueType(DL, I.getType());
         Instruction *Phi = static_cast<Instruction *>(I.getOperand(0));
         auto PromoteWidth = ZExtVT.getFixedSizeInBits();
@@ -1016,7 +1021,7 @@ bool TypePromotionImpl::run(Function &F, const TargetMachine *TM,
                             << "register for ZExt type\n");
           continue;
         }
-        MadeChange |= TryToPromote(Phi, PromoteWidth, LI);
+        MadeChange |= TryToPromote(Phi, PromoteWidth, LI, UseSExt);
       } else if (auto *ICmp = dyn_cast<ICmpInst>(&I)) {
         // Search up from icmps to try to promote their operands.
         // Skip signed or pointer compares

>From d4dd17e437f0ee6e3ad679ead22d298260b79796 Mon Sep 17 00:00:00 2001
From: Ananth Jasty <ananth at x3ns.net>
Date: Thu, 24 Sep 2026 13:08:58 +0000
Subject: [PATCH 2/3] [CodeGen] TypePromotion now tests signed matching before
 cleaning up zext/sext-trunc pairs correctly.

---
 llvm/lib/CodeGen/TypePromotion.cpp | 49 ++++++++++++++++++++----------
 1 file changed, 33 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/CodeGen/TypePromotion.cpp b/llvm/lib/CodeGen/TypePromotion.cpp
index 13e95bb46fc93..f6e6ddd1093d9 100644
--- a/llvm/lib/CodeGen/TypePromotion.cpp
+++ b/llvm/lib/CodeGen/TypePromotion.cpp
@@ -122,6 +122,8 @@ class IRPromoter {
   void TruncateSinks();
   void Cleanup();
 
+  bool CheckSignedMatch(const Value *V) const;
+
 public:
   IRPromoter(LLVMContext &C, unsigned Width, SetVector<Value *> &visited,
              SetVector<Value *> &sources, SetVector<Instruction *> &sinks,
@@ -272,6 +274,8 @@ bool TypePromotionImpl::isSink(Value *V) {
     return LessOrEqualTypeSize(Return->getReturnValue());
   if (auto *ZExt = dyn_cast<ZExtInst>(V))
     return GreaterThanTypeSize(ZExt);
+  if (auto *SExt = dyn_cast<SExtInst>(V))
+    return GreaterThanTypeSize(SExt);
   if (auto *Switch = dyn_cast<SwitchInst>(V))
     return LessThanTypeSize(Switch->getCondition());
   if (auto *ICmp = dyn_cast<ICmpInst>(V))
@@ -604,15 +608,14 @@ void IRPromoter::TruncateSinks() {
       continue;
     }
 
-    // Don't insert a trunc for a zext which can still legally promote.
+    // Don't insert a trunc for a (z/s)ext which can still legally promote.
     // Nor insert a trunc when the input value to that trunc has the same width
     // as the zext we are inserting it for.  When this happens the input operand
-    // for the zext will be promoted to the same width as the zext's return type
-    // rendering that zext unnecessary.  This zext gets removed before the end
+    // for the zext will be promoted to the same width as the ext's return type
+    // rendering that ext unnecessary.  This zext gets removed before the end
     // of the pass.
-    if (auto ZExt = dyn_cast<ZExtInst>(I))
-      if (ZExt->getType()->getScalarSizeInBits() >= PromotedWidth)
-        continue;
+    if (CheckSignedMatch(I) && (I->getType()->getScalarSizeInBits() >= PromotedWidth))
+      continue;
 
     // Now handle the others.
     for (unsigned i = 0; i < I->getNumOperands(); ++i) {
@@ -627,31 +630,34 @@ void IRPromoter::TruncateSinks() {
 
 void IRPromoter::Cleanup() {
   LLVM_DEBUG(dbgs() << "IR Promotion: Cleanup..\n");
-  // Some zexts will now have become redundant, along with their trunc
+  // Some (z/s)exts will now have become redundant, along with their trunc
   // operands, so remove them.
   for (auto *V : Visited) {
-    if (!isa<ZExtInst>(V))
+    if (!isa<ZExtInst>(V) && !isa<SExtInst>(V))
+      continue;
+
+    if (!CheckSignedMatch(V))
       continue;
 
-    auto ZExt = cast<ZExtInst>(V);
-    if (ZExt->getDestTy() != ExtTy)
+    auto XExt = cast<CastInst>(V);
+    if (XExt->getDestTy() != ExtTy)
       continue;
 
-    Value *Src = ZExt->getOperand(0);
-    if (ZExt->getSrcTy() == ZExt->getDestTy()) {
-      LLVM_DEBUG(dbgs() << "IR Promotion: Removing unnecessary cast: " << *ZExt
+    Value *Src = XExt->getOperand(0);
+    if (XExt->getSrcTy() == XExt->getDestTy()) {
+      LLVM_DEBUG(dbgs() << "IR Promotion: Removing unnecessary cast: " << *XExt
                         << "\n");
-      ReplaceAllUsersOfWith(ZExt, Src);
+      ReplaceAllUsersOfWith(XExt, Src);
       continue;
     }
 
-    // We've inserted a trunc for a zext sink, but we already know that the
+    // We've inserted a trunc for a (z/s)ext sink, but we already know that the
     // input is in range, negating the need for the trunc.
     if (NewInsts.count(Src) && isa<TruncInst>(Src)) {
       auto *Trunc = cast<TruncInst>(Src);
       assert(Trunc->getOperand(0)->getType() == ExtTy &&
              "expected inserted trunc to be operating on i32");
-      ReplaceAllUsersOfWith(ZExt, Trunc->getOperand(0));
+      ReplaceAllUsersOfWith(XExt, Trunc->getOperand(0));
     }
   }
 
@@ -730,6 +736,17 @@ void IRPromoter::Mutate() {
   LLVM_DEBUG(dbgs() << "IR Promotion: Mutation complete\n");
 }
 
+bool IRPromoter::CheckSignedMatch(const Value *V) const {
+  bool SignedMatch = false;
+
+  if (isa<SExtInst>(V))
+    SignedMatch = UseSExt;
+  if (auto *ZExt = dyn_cast<ZExtInst>(V))
+    SignedMatch = !UseSExt || ZExt->hasNonNeg();
+
+  return SignedMatch;
+}
+
 /// We disallow booleans to make life easier when dealing with icmps but allow
 /// any other integer that fits in a scalar register. Void types are accepted
 /// so we can handle switches.

>From 97d61b914242d9d803742a89734f604ea51614bf Mon Sep 17 00:00:00 2001
From: Ananth Jasty <ananth at x3ns.net>
Date: Thu, 24 Sep 2026 13:33:06 +0000
Subject: [PATCH 3/3] [CodeGen] Fixed lshr and constant handling for SExt
 promotion. Might still have an issue with other insts.

---
 llvm/lib/CodeGen/TypePromotion.cpp | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/CodeGen/TypePromotion.cpp b/llvm/lib/CodeGen/TypePromotion.cpp
index f6e6ddd1093d9..243cc00c49c7d 100644
--- a/llvm/lib/CodeGen/TypePromotion.cpp
+++ b/llvm/lib/CodeGen/TypePromotion.cpp
@@ -175,7 +175,7 @@ class TypePromotionImpl {
   bool isSupportedValue(Value *V);
   // Is V an instruction thats result can trivially promoted, or has safe
   // wrapping.
-  bool isLegalToPromote(Value *V);
+  bool isLegalToPromote(Value *V, bool UseSExt = false);
   bool TryToPromote(Value *V, unsigned PromotedWidth, const LoopInfo &LI,
                     bool UseSExt = false);
 
@@ -415,10 +415,13 @@ bool TypePromotionImpl::shouldPromote(Value *V) {
 
 /// Return whether we can safely mutate V's type to ExtTy without having to be
 /// concerned with zero extending or truncation.
-static bool isPromotedResultSafe(Instruction *I) {
+static bool isPromotedResultSafe(Instruction *I, bool UseSExt) {
   if (GenerateSignBits(I))
     return false;
 
+  if (UseSExt && I->getOpcode() == Instruction::LShr)
+    return false;
+
   if (!isa<OverflowingBinaryOperator>(I))
     return true;
 
@@ -526,7 +529,8 @@ void IRPromoter::PromoteTree() {
           else
             NewConst = Const->getValue().zext(PromotedWidth);
         } else
-          NewConst = Const->getValue().zext(PromotedWidth);
+          NewConst = UseSExt ? Const->getValue().sext(PromotedWidth) :
+                               Const->getValue().zext(PromotedWidth);
 
         I->setOperand(i, ConstantInt::get(Const->getContext(), NewConst));
       } else if (isa<UndefValue>(Op))
@@ -536,7 +540,8 @@ void IRPromoter::PromoteTree() {
     // For switch, also mutate case values, which are not operands.
     if (auto *SI = dyn_cast<SwitchInst>(I)) {
       for (auto Case : SI->cases()) {
-        APInt NewConst = Case.getCaseValue()->getValue().zext(PromotedWidth);
+        APInt NewConst = UseSExt ? Case.getCaseValue()->getValue().sext(PromotedWidth) :
+                                   Case.getCaseValue()->getValue().zext(PromotedWidth);
         Case.setValue(ConstantInt::get(SI->getContext(), NewConst));
       }
     }
@@ -818,7 +823,7 @@ bool TypePromotionImpl::isSupportedValue(Value *V) {
 /// Check that the type of V would be promoted and that the original type is
 /// smaller than the targeted promoted type. Check that we're not trying to
 /// promote something larger than our base 'TypeSize' type.
-bool TypePromotionImpl::isLegalToPromote(Value *V) {
+bool TypePromotionImpl::isLegalToPromote(Value *V, bool UseSExt) {
   auto *I = dyn_cast<Instruction>(V);
   if (!I)
     return true;
@@ -826,7 +831,7 @@ bool TypePromotionImpl::isLegalToPromote(Value *V) {
   if (SafeToPromote.count(I))
     return true;
 
-  if (isPromotedResultSafe(I) || isSafeWrap(I)) {
+  if (isPromotedResultSafe(I, UseSExt) || (!UseSExt && isSafeWrap(I))) {
     SafeToPromote.insert(I);
     return true;
   }
@@ -840,7 +845,7 @@ bool TypePromotionImpl::TryToPromote(Value *V, unsigned PromotedWidth,
   SafeToPromote.clear();
   SafeWrap.clear();
 
-  if (!isSupportedValue(V) || !shouldPromote(V) || !isLegalToPromote(V))
+  if (!isSupportedValue(V) || !shouldPromote(V) || !isLegalToPromote(V, UseSExt))
     return false;
 
   LLVM_DEBUG(dbgs() << "IR Promotion: TryToPromote: " << *V << ", from "
@@ -864,7 +869,7 @@ bool TypePromotionImpl::TryToPromote(Value *V, unsigned PromotedWidth,
     if (isa<GetElementPtrInst>(V))
       return false;
 
-    if (!isSupportedValue(V) || (shouldPromote(V) && !isLegalToPromote(V))) {
+    if (!isSupportedValue(V) || (shouldPromote(V) && !isLegalToPromote(V, UseSExt))) {
       LLVM_DEBUG(dbgs() << "IR Promotion: Can't handle: " << *V << "\n");
       return false;
     }



More information about the llvm-commits mailing list