[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