[llvm] [SCEV] Print use-specific no-wrap flags (NFC). (PR #216663)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 18 02:46:38 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/216663
>From b00a7102218cdf5e04ab6e6bcc21c518e6e842e8 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 16 Aug 2026 14:00:05 +0100
Subject: [PATCH] [SCEV] Print use-specific no-wrap flags (NFC).
Add support for printing use-specific no-wrap flags.
Currently no in-tree user sets use-specific flags yet, hence the C++
based unit test.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 4 +-
llvm/lib/Analysis/ScalarEvolution.cpp | 38 ++++++------
.../Analysis/ScalarEvolutionTest.cpp | 62 +++++++++++++++++++
3 files changed, 82 insertions(+), 22 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 0d7f9ae298e2a..4e1aeea26f943 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2756,9 +2756,9 @@ void SCEVUseT<SCEVPtrT>::print(raw_ostream &OS) const {
getPointer()->print(OS);
SCEV::NoWrapFlags Flags = getUseNoWrapFlags();
if (any(Flags & SCEV::FlagNUW))
- OS << "(u nuw)";
+ OS << "<u nuw>";
if (any(Flags & SCEV::FlagNSW))
- OS << "(u nsw)";
+ OS << "<u nsw>";
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 27a1a20bcdf79..dab6f9a4e00f3 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -354,37 +354,37 @@ void SCEV::print(raw_ostream &OS) const {
return;
case scPtrToAddr: {
const SCEVCastExpr *PtrCast = cast<SCEVCastExpr>(this);
- const SCEV *Op = PtrCast->getOperand();
- OS << "(ptrtoaddr " << *Op->getType() << " " << *Op << " to "
+ SCEVUse Op = PtrCast->getOperand();
+ OS << "(ptrtoaddr " << *Op->getType() << " " << Op << " to "
<< *PtrCast->getType() << ")";
return;
}
case scTruncate: {
const SCEVTruncateExpr *Trunc = cast<SCEVTruncateExpr>(this);
- const SCEV *Op = Trunc->getOperand();
- OS << "(trunc " << *Op->getType() << " " << *Op << " to "
+ SCEVUse Op = Trunc->getOperand();
+ OS << "(trunc " << *Op->getType() << " " << Op << " to "
<< *Trunc->getType() << ")";
return;
}
case scZeroExtend: {
const SCEVZeroExtendExpr *ZExt = cast<SCEVZeroExtendExpr>(this);
- const SCEV *Op = ZExt->getOperand();
- OS << "(zext " << *Op->getType() << " " << *Op << " to "
- << *ZExt->getType() << ")";
+ SCEVUse Op = ZExt->getOperand();
+ OS << "(zext " << *Op->getType() << " " << Op << " to " << *ZExt->getType()
+ << ")";
return;
}
case scSignExtend: {
const SCEVSignExtendExpr *SExt = cast<SCEVSignExtendExpr>(this);
- const SCEV *Op = SExt->getOperand();
- OS << "(sext " << *Op->getType() << " " << *Op << " to "
- << *SExt->getType() << ")";
+ SCEVUse Op = SExt->getOperand();
+ OS << "(sext " << *Op->getType() << " " << Op << " to " << *SExt->getType()
+ << ")";
return;
}
case scAddRecExpr: {
const SCEVAddRecExpr *AR = cast<SCEVAddRecExpr>(this);
- OS << "{" << *AR->getOperand(0);
+ OS << "{" << AR->getOperand(0);
for (unsigned i = 1, e = AR->getNumOperands(); i != e; ++i)
- OS << ",+," << *AR->getOperand(i);
+ OS << ",+," << AR->getOperand(i);
OS << "}<";
if (AR->hasNoUnsignedWrap())
OS << "nuw><";
@@ -423,9 +423,7 @@ void SCEV::print(raw_ostream &OS) const {
default:
llvm_unreachable("There are no other nary expression types.");
}
- OS << "("
- << llvm::interleaved(llvm::make_pointee_range(NAry->operands()), OpStr)
- << ")";
+ OS << "(" << llvm::interleaved(NAry->operands(), OpStr) << ")";
switch (NAry->getSCEVType()) {
case scAddExpr:
case scMulExpr:
@@ -442,7 +440,7 @@ void SCEV::print(raw_ostream &OS) const {
}
case scUDivExpr: {
const SCEVUDivExpr *UDiv = cast<SCEVUDivExpr>(this);
- OS << "(" << *UDiv->getLHS() << " /u " << *UDiv->getRHS() << ")";
+ OS << "(" << UDiv->getLHS() << " /u " << UDiv->getRHS() << ")";
return;
}
case scUnknown:
@@ -14401,10 +14399,10 @@ void ScalarEvolution::print(raw_ostream &OS) const {
const Loop *L = LI.getLoopFor(I.getParent());
- const SCEV *AtUse = SE.getSCEVAtScope(SV, L);
+ SCEVUse AtUse = SE.getSCEVAtScope(SV, L);
if (AtUse != SV) {
OS << " --> ";
- AtUse->print(OS);
+ OS << AtUse;
if (!isa<SCEVCouldNotCompute>(AtUse)) {
OS << " U: ";
SE.getUnsignedRange(AtUse).print(OS);
@@ -14415,11 +14413,11 @@ void ScalarEvolution::print(raw_ostream &OS) const {
if (L) {
OS << "\t\t" "Exits: ";
- const SCEV *ExitValue = SE.getSCEVAtScope(SV, L->getParentLoop());
+ SCEVUse ExitValue = SE.getSCEVAtScope(SV, L->getParentLoop());
if (!SE.isLoopInvariant(ExitValue, L)) {
OS << "<<Unknown>>";
} else {
- OS << *ExitValue;
+ OS << ExitValue;
}
ListSeparator LS(", ", "\t\tLoopDispositions: { ");
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 621c4897a39d7..d4fe433a3e598 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -23,6 +23,7 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
namespace llvm {
@@ -2064,4 +2065,65 @@ TEST_F(ScalarEvolutionsTest, SimplifyICmpOperands) {
});
}
+// An operand of a SCEV expression is a SCEVUse and may carry use-specific
+// no-wrap flags. Check that SCEV::print renders the operands as uses, so such a
+// flag is visible in the printout of the expression using it.
+TEST_F(ScalarEvolutionsTest, PrintUseFlagsOfOperands) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M = parseAssemblyString(
+ R"(define void @f(i32 %a, i32 %b) {
+ entry:
+ br label %loop
+
+ loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %iv.next = add i32 %iv, 1
+ %c = icmp ult i32 %iv.next, 10
+ br i1 %c, label %loop, label %exit
+
+ exit:
+ ret void
+ })",
+ Err, C);
+
+ if (!M) {
+ Err.print("ScalarEvolutionTest", errs());
+ ASSERT_TRUE(M && "Could not parse module?");
+ }
+ ASSERT_TRUE(!verifyModule(*M, &errs()) && "Must have been well formed!");
+
+ runWithSE(*M, "f", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ auto Rendered = [](SCEVUse U) {
+ std::string S;
+ raw_string_ostream OS(S);
+ OS << U;
+ return S;
+ };
+
+ SCEVUse A = SE.getSCEV(getArgByName(F, "a"));
+ SCEVUse B = SE.getSCEV(getArgByName(F, "b"));
+ const Loop *L = LI.getLoopFor(getInstructionByName(F, "iv")->getParent());
+
+ // Uses of (%a + %b) and (4 + %a) carrying use-specific no-wrap flags.
+ SCEVUse Add = SE.getAddExpr(A, B);
+ SCEVUse Add4 = SE.getAddExpr(A, SE.getConstant(APInt(32, 4)));
+ SCEVUse NUWAdd(Add, SCEV::FlagNUW);
+ SCEVUse NSWAdd4(Add4, SCEV::FlagNSW);
+ EXPECT_EQ(Rendered(Add), "(%a + %b)");
+ EXPECT_EQ(Rendered(Add4), "(4 + %a)");
+ EXPECT_EQ(Rendered(NUWAdd), "(%a + %b)<u nuw>");
+ EXPECT_EQ(Rendered(NSWAdd4), "(4 + %a)<u nsw>");
+
+ SCEVUse Max = SE.getUMaxExpr(NUWAdd, NSWAdd4);
+ EXPECT_EQ(Rendered(Max), "((4 + %a)<u nsw> umax (%a + %b)<u nuw>)");
+
+ SCEVUse UDiv = SE.getUDivExpr(NUWAdd, NSWAdd4);
+ EXPECT_EQ(Rendered(UDiv), "((%a + %b)<u nuw> /u (4 + %a)<u nsw>)");
+
+ SCEVUse AR = SE.getAddRecExpr(NUWAdd, NSWAdd4, L, SCEV::FlagAnyWrap);
+ EXPECT_EQ(Rendered(AR), "{(%a + %b)<u nuw>,+,(4 + %a)<u nsw>}<%loop>");
+ });
+}
+
} // end namespace llvm
More information about the llvm-commits
mailing list