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

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 24 06:10:06 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/2] [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 2736ff3f8a299d..13e95bb46fc936 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/2] [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 13e95bb46fc936..f6e6ddd1093d94 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.



More information about the llvm-commits mailing list