From 81952c68f547e2c6284d06f56398de885760c539 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 1 Jan 2021 21:57:34 +0100 Subject: [PATCH] [ConstantRange] Handle wrapping ranges in min/max (PR48643) When one of the inputs is a wrapping range, intersect with the union of the two inputs. The union of the two inputs corresponds to the result we would get if we treated the min/max as a simple select. This fixes PR48643. --- lib/IR/ConstantRange.cpp | 20 ++++++++++--- unittests/IR/ConstantRangeTest.cpp | 45 ++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/lib/IR/ConstantRange.cpp b/lib/IR/ConstantRange.cpp index 275996dd02d..4dbe1a1b902 100644 --- a/lib/IR/ConstantRange.cpp +++ b/lib/IR/ConstantRange.cpp @@ -1058,7 +1058,10 @@ ConstantRange::smax(const ConstantRange &Other) const { return getEmpty(); APInt NewL = APIntOps::smax(getSignedMin(), Other.getSignedMin()); APInt NewU = APIntOps::smax(getSignedMax(), Other.getSignedMax()) + 1; - return getNonEmpty(std::move(NewL), std::move(NewU)); + ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU)); + if (isSignWrappedSet() || Other.isSignWrappedSet()) + return Res.intersectWith(unionWith(Other, Signed), Signed); + return Res; } ConstantRange @@ -1069,7 +1072,10 @@ ConstantRange::umax(const ConstantRange &Other) const { return getEmpty(); APInt NewL = APIntOps::umax(getUnsignedMin(), Other.getUnsignedMin()); APInt NewU = APIntOps::umax(getUnsignedMax(), Other.getUnsignedMax()) + 1; - return getNonEmpty(std::move(NewL), std::move(NewU)); + ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU)); + if (isWrappedSet() || Other.isWrappedSet()) + return Res.intersectWith(unionWith(Other, Unsigned), Unsigned); + return Res; } ConstantRange @@ -1080,7 +1086,10 @@ ConstantRange::smin(const ConstantRange &Other) const { return getEmpty(); APInt NewL = APIntOps::smin(getSignedMin(), Other.getSignedMin()); APInt NewU = APIntOps::smin(getSignedMax(), Other.getSignedMax()) + 1; - return getNonEmpty(std::move(NewL), std::move(NewU)); + ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU)); + if (isSignWrappedSet() || Other.isSignWrappedSet()) + return Res.intersectWith(unionWith(Other, Signed), Signed); + return Res; } ConstantRange @@ -1091,7 +1100,10 @@ ConstantRange::umin(const ConstantRange &Other) const { return getEmpty(); APInt NewL = APIntOps::umin(getUnsignedMin(), Other.getUnsignedMin()); APInt NewU = APIntOps::umin(getUnsignedMax(), Other.getUnsignedMax()) + 1; - return getNonEmpty(std::move(NewL), std::move(NewU)); + ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU)); + if (isWrappedSet() || Other.isWrappedSet()) + return Res.intersectWith(unionWith(Other, Unsigned), Unsigned); + return Res; } ConstantRange diff --git a/unittests/IR/ConstantRangeTest.cpp b/unittests/IR/ConstantRangeTest.cpp index 2b9c56b4134..12362b9460f 100644 --- a/unittests/IR/ConstantRangeTest.cpp +++ b/unittests/IR/ConstantRangeTest.cpp @@ -1080,10 +1080,18 @@ TEST_F(ConstantRangeTest, UMax) { EXPECT_EQ(Some.umax(Some), Some); EXPECT_EQ(Some.umax(Wrap), ConstantRange(APInt(16, 0xa), APInt(16, 0))); EXPECT_EQ(Some.umax(One), Some); - // TODO: ConstantRange is currently over-conservative here. - EXPECT_EQ(Wrap.umax(Wrap), Full); + EXPECT_EQ(Wrap.umax(Wrap), Wrap); EXPECT_EQ(Wrap.umax(One), ConstantRange(APInt(16, 0xa), APInt(16, 0))); EXPECT_EQ(One.umax(One), One); + + TestBinaryOpExhaustive( + [](const ConstantRange &CR1, const ConstantRange &CR2) { + return CR1.umax(CR2); + }, + [](const APInt &N1, const APInt &N2) { + return APIntOps::umax(N1, N2); + }, + PreferSmallestNonFullUnsigned); } TEST_F(ConstantRangeTest, SMax) { @@ -1105,6 +1113,15 @@ TEST_F(ConstantRangeTest, SMax) { EXPECT_EQ(Wrap.smax(One), ConstantRange(APInt(16, 0xa), APInt(16, (uint64_t)INT16_MIN))); EXPECT_EQ(One.smax(One), One); + + TestBinaryOpExhaustive( + [](const ConstantRange &CR1, const ConstantRange &CR2) { + return CR1.smax(CR2); + }, + [](const APInt &N1, const APInt &N2) { + return APIntOps::smax(N1, N2); + }, + PreferSmallestNonFullSigned); } TEST_F(ConstantRangeTest, UMin) { @@ -1119,10 +1136,18 @@ TEST_F(ConstantRangeTest, UMin) { EXPECT_EQ(Some.umin(Some), Some); EXPECT_EQ(Some.umin(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa))); EXPECT_EQ(Some.umin(One), One); - // TODO: ConstantRange is currently over-conservative here. - EXPECT_EQ(Wrap.umin(Wrap), Full); + EXPECT_EQ(Wrap.umin(Wrap), Wrap); EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb))); EXPECT_EQ(One.umin(One), One); + + TestBinaryOpExhaustive( + [](const ConstantRange &CR1, const ConstantRange &CR2) { + return CR1.umin(CR2); + }, + [](const APInt &N1, const APInt &N2) { + return APIntOps::umin(N1, N2); + }, + PreferSmallestNonFullUnsigned); } TEST_F(ConstantRangeTest, SMin) { @@ -1139,11 +1164,19 @@ TEST_F(ConstantRangeTest, SMin) { EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN), APInt(16, 0xaaa))); EXPECT_EQ(Some.smin(One), One); - // TODO: ConstantRange is currently over-conservative here. - EXPECT_EQ(Wrap.smin(Wrap), Full); + EXPECT_EQ(Wrap.smin(Wrap), Wrap); EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN), APInt(16, 0xb))); EXPECT_EQ(One.smin(One), One); + + TestBinaryOpExhaustive( + [](const ConstantRange &CR1, const ConstantRange &CR2) { + return CR1.smin(CR2); + }, + [](const APInt &N1, const APInt &N2) { + return APIntOps::smin(N1, N2); + }, + PreferSmallestNonFullSigned); } TEST_F(ConstantRangeTest, UDiv) {