Skip to content

Commit a852234

Browse files
committed
[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.
1 parent e772618 commit a852234

File tree

2 files changed

+55
-10
lines changed

2 files changed

+55
-10
lines changed

llvm/lib/IR/ConstantRange.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,10 @@ ConstantRange::smax(const ConstantRange &Other) const {
10581058
return getEmpty();
10591059
APInt NewL = APIntOps::smax(getSignedMin(), Other.getSignedMin());
10601060
APInt NewU = APIntOps::smax(getSignedMax(), Other.getSignedMax()) + 1;
1061-
return getNonEmpty(std::move(NewL), std::move(NewU));
1061+
ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU));
1062+
if (isSignWrappedSet() || Other.isSignWrappedSet())
1063+
return Res.intersectWith(unionWith(Other, Signed), Signed);
1064+
return Res;
10621065
}
10631066

10641067
ConstantRange
@@ -1069,7 +1072,10 @@ ConstantRange::umax(const ConstantRange &Other) const {
10691072
return getEmpty();
10701073
APInt NewL = APIntOps::umax(getUnsignedMin(), Other.getUnsignedMin());
10711074
APInt NewU = APIntOps::umax(getUnsignedMax(), Other.getUnsignedMax()) + 1;
1072-
return getNonEmpty(std::move(NewL), std::move(NewU));
1075+
ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU));
1076+
if (isWrappedSet() || Other.isWrappedSet())
1077+
return Res.intersectWith(unionWith(Other, Unsigned), Unsigned);
1078+
return Res;
10731079
}
10741080

10751081
ConstantRange
@@ -1080,7 +1086,10 @@ ConstantRange::smin(const ConstantRange &Other) const {
10801086
return getEmpty();
10811087
APInt NewL = APIntOps::smin(getSignedMin(), Other.getSignedMin());
10821088
APInt NewU = APIntOps::smin(getSignedMax(), Other.getSignedMax()) + 1;
1083-
return getNonEmpty(std::move(NewL), std::move(NewU));
1089+
ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU));
1090+
if (isSignWrappedSet() || Other.isSignWrappedSet())
1091+
return Res.intersectWith(unionWith(Other, Signed), Signed);
1092+
return Res;
10841093
}
10851094

10861095
ConstantRange
@@ -1091,7 +1100,10 @@ ConstantRange::umin(const ConstantRange &Other) const {
10911100
return getEmpty();
10921101
APInt NewL = APIntOps::umin(getUnsignedMin(), Other.getUnsignedMin());
10931102
APInt NewU = APIntOps::umin(getUnsignedMax(), Other.getUnsignedMax()) + 1;
1094-
return getNonEmpty(std::move(NewL), std::move(NewU));
1103+
ConstantRange Res = getNonEmpty(std::move(NewL), std::move(NewU));
1104+
if (isWrappedSet() || Other.isWrappedSet())
1105+
return Res.intersectWith(unionWith(Other, Unsigned), Unsigned);
1106+
return Res;
10951107
}
10961108

10971109
ConstantRange

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,10 +1080,18 @@ TEST_F(ConstantRangeTest, UMax) {
10801080
EXPECT_EQ(Some.umax(Some), Some);
10811081
EXPECT_EQ(Some.umax(Wrap), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
10821082
EXPECT_EQ(Some.umax(One), Some);
1083-
// TODO: ConstantRange is currently over-conservative here.
1084-
EXPECT_EQ(Wrap.umax(Wrap), Full);
1083+
EXPECT_EQ(Wrap.umax(Wrap), Wrap);
10851084
EXPECT_EQ(Wrap.umax(One), ConstantRange(APInt(16, 0xa), APInt(16, 0)));
10861085
EXPECT_EQ(One.umax(One), One);
1086+
1087+
TestBinaryOpExhaustive(
1088+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1089+
return CR1.umax(CR2);
1090+
},
1091+
[](const APInt &N1, const APInt &N2) {
1092+
return APIntOps::umax(N1, N2);
1093+
},
1094+
PreferSmallestNonFullUnsigned);
10871095
}
10881096

10891097
TEST_F(ConstantRangeTest, SMax) {
@@ -1105,6 +1113,15 @@ TEST_F(ConstantRangeTest, SMax) {
11051113
EXPECT_EQ(Wrap.smax(One), ConstantRange(APInt(16, 0xa),
11061114
APInt(16, (uint64_t)INT16_MIN)));
11071115
EXPECT_EQ(One.smax(One), One);
1116+
1117+
TestBinaryOpExhaustive(
1118+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1119+
return CR1.smax(CR2);
1120+
},
1121+
[](const APInt &N1, const APInt &N2) {
1122+
return APIntOps::smax(N1, N2);
1123+
},
1124+
PreferSmallestNonFullSigned);
11081125
}
11091126

11101127
TEST_F(ConstantRangeTest, UMin) {
@@ -1119,10 +1136,18 @@ TEST_F(ConstantRangeTest, UMin) {
11191136
EXPECT_EQ(Some.umin(Some), Some);
11201137
EXPECT_EQ(Some.umin(Wrap), ConstantRange(APInt(16, 0), APInt(16, 0xaaa)));
11211138
EXPECT_EQ(Some.umin(One), One);
1122-
// TODO: ConstantRange is currently over-conservative here.
1123-
EXPECT_EQ(Wrap.umin(Wrap), Full);
1139+
EXPECT_EQ(Wrap.umin(Wrap), Wrap);
11241140
EXPECT_EQ(Wrap.umin(One), ConstantRange(APInt(16, 0), APInt(16, 0xb)));
11251141
EXPECT_EQ(One.umin(One), One);
1142+
1143+
TestBinaryOpExhaustive(
1144+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1145+
return CR1.umin(CR2);
1146+
},
1147+
[](const APInt &N1, const APInt &N2) {
1148+
return APIntOps::umin(N1, N2);
1149+
},
1150+
PreferSmallestNonFullUnsigned);
11261151
}
11271152

11281153
TEST_F(ConstantRangeTest, SMin) {
@@ -1139,11 +1164,19 @@ TEST_F(ConstantRangeTest, SMin) {
11391164
EXPECT_EQ(Some.smin(Wrap), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
11401165
APInt(16, 0xaaa)));
11411166
EXPECT_EQ(Some.smin(One), One);
1142-
// TODO: ConstantRange is currently over-conservative here.
1143-
EXPECT_EQ(Wrap.smin(Wrap), Full);
1167+
EXPECT_EQ(Wrap.smin(Wrap), Wrap);
11441168
EXPECT_EQ(Wrap.smin(One), ConstantRange(APInt(16, (uint64_t)INT16_MIN),
11451169
APInt(16, 0xb)));
11461170
EXPECT_EQ(One.smin(One), One);
1171+
1172+
TestBinaryOpExhaustive(
1173+
[](const ConstantRange &CR1, const ConstantRange &CR2) {
1174+
return CR1.smin(CR2);
1175+
},
1176+
[](const APInt &N1, const APInt &N2) {
1177+
return APIntOps::smin(N1, N2);
1178+
},
1179+
PreferSmallestNonFullSigned);
11471180
}
11481181

11491182
TEST_F(ConstantRangeTest, UDiv) {

0 commit comments

Comments
 (0)