From a74c4e37ae03c317f7b7c8bc5431d16de7a5aa91 Mon Sep 17 00:00:00 2001 From: Philip Reames Date: Thu, 15 Jul 2021 10:59:02 -0700 Subject: [PATCH] [unittest] Exercise SCEV's udiv and udiv ceiling routines The ceiling variant was recently added (due to the work towards D105216), and we're spending a lot of time trying to find optimizations for the expression. This patch brute forces the space of i8 unsigned divides and checks that we get a correct (well consistent with APInt) result for both udiv and udiv ceiling. (This is basically what I've been doing locally in a hand rolled C++ program, and I realized there no good reason not to check it in as a unit test which directly exercises the logic on constants.) Differential Revision: https://reviews.llvm.org/D106083 --- include/llvm/Analysis/ScalarEvolution.h | 20 +++++++------- unittests/Analysis/ScalarEvolutionTest.cpp | 31 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/include/llvm/Analysis/ScalarEvolution.h b/include/llvm/Analysis/ScalarEvolution.h index dc8c14422f1..788e9cae497 100644 --- a/include/llvm/Analysis/ScalarEvolution.h +++ b/include/llvm/Analysis/ScalarEvolution.h @@ -643,6 +643,16 @@ public: SCEV::NoWrapFlags Flags = SCEV::FlagAnyWrap, unsigned Depth = 0); + /// Compute ceil(N / D). N and D are treated as unsigned values. + /// + /// Since SCEV doesn't have native ceiling division, this generates a + /// SCEV expression of the following form: + /// + /// umin(N, 1) + floor((N - umin(N, 1)) / D) + /// + /// A denominator of zero or poison is handled the same way as getUDivExpr(). + const SCEV *getUDivCeilSCEV(const SCEV *N, const SCEV *D); + /// Return a SCEV corresponding to a conversion of the input value to the /// specified type. If the type must be extended, it is zero extended. const SCEV *getTruncateOrZeroExtend(const SCEV *V, Type *Ty, @@ -2029,16 +2039,6 @@ private: /// that the result is undefined if it does. const SCEV *computeBECount(const SCEV *Delta, const SCEV *Stride); - /// Compute ceil(N / D). N and D are treated as unsigned values. - /// - /// Since SCEV doesn't have native ceiling division, this generates a - /// SCEV expression of the following form: - /// - /// umin(N, 1) + floor((N - umin(N, 1)) / D) - /// - /// A denominator of zero or poison is handled the same way as getUDivExpr(). - const SCEV *getUDivCeilSCEV(const SCEV *N, const SCEV *D); - /// Compute the maximum backedge count based on the range of values /// permitted by Start, End, and Stride. This is for loops of the form /// {Start, +, Stride} LT End. diff --git a/unittests/Analysis/ScalarEvolutionTest.cpp b/unittests/Analysis/ScalarEvolutionTest.cpp index 8a21646d9b6..2664ffa22fa 100644 --- a/unittests/Analysis/ScalarEvolutionTest.cpp +++ b/unittests/Analysis/ScalarEvolutionTest.cpp @@ -1507,4 +1507,35 @@ TEST_F(ScalarEvolutionsTest, MatchURem) { }); } +TEST_F(ScalarEvolutionsTest, SCEVUDivFloorCeiling) { + LLVMContext C; + SMDiagnostic Err; + std::unique_ptr M = parseAssemblyString("define void @foo() { " + " ret void " + "} ", + Err, C); + + ASSERT_TRUE(M && "Could not parse module?"); + ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!"); + + runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) { + // Check that SCEV's udiv and uceil handling produce the correct results + // for all 8 bit options. Div-by-zero is deliberately excluded. + for (unsigned N = 0; N < 256; N++) + for (unsigned D = 1; D < 256; D++) { + APInt NInt(8, N); + APInt DInt(8, D); + using namespace llvm::APIntOps; + APInt FloorInt = RoundingUDiv(NInt, DInt, APInt::Rounding::DOWN); + APInt CeilingInt = RoundingUDiv(NInt, DInt, APInt::Rounding::UP); + auto *NS = SE.getConstant(NInt); + auto *DS = SE.getConstant(DInt); + auto *FloorS = cast(SE.getUDivExpr(NS, DS)); + auto *CeilingS = cast(SE.getUDivCeilSCEV(NS, DS)); + ASSERT_TRUE(FloorS->getAPInt() == FloorInt); + ASSERT_TRUE(CeilingS->getAPInt() == CeilingInt); + } + }); +} + } // end namespace llvm