static ConstantRange Wrap;
};
+template<typename Fn>
+static void EnumerateTwoConstantRanges(unsigned Bits, Fn TestFn) {
+ unsigned Max = 1 << Bits;
+ for (unsigned Lo1 = 0; Lo1 < Max; Lo1++) {
+ for (unsigned Hi1 = 0; Hi1 < Max; Hi1++) {
+ // Enforce ConstantRange invariant.
+ if (Lo1 == Hi1 && Lo1 != 0 && Lo1 != Max - 1)
+ continue;
+
+ ConstantRange CR1(APInt(Bits, Lo1), APInt(Bits, Hi1));
+ for (unsigned Lo2 = 0; Lo2 < Max; Lo2++) {
+ for (unsigned Hi2 = 0; Hi2 < Max; Hi2++) {
+ // Enforce ConstantRange invariant.
+ if (Lo2 == Hi2 && Lo2 != 0 && Lo2 != Max - 1)
+ continue;
+
+ ConstantRange CR2(APInt(Bits, Lo2), APInt(Bits, Hi2));
+ TestFn(CR1, CR2);
+ }
+ }
+ }
+ }
+}
+
ConstantRange ConstantRangeTest::Full(16, true);
ConstantRange ConstantRangeTest::Empty(16, false);
ConstantRange ConstantRangeTest::One(APInt(16, 0xa));
EXPECT_EQ(LHS.intersectWith(RHS), ConstantRange(APInt(32, 15), APInt(32, 0)));
}
+TEST_F(ConstantRangeTest, IntersectWithExhaustive) {
+ unsigned Bits = 4;
+ EnumerateTwoConstantRanges(Bits,
+ [=](const ConstantRange &CR1, const ConstantRange &CR2) {
+ // Collect up to three contiguous unsigned ranges. The HaveInterrupt
+ // variables are used determine when we have to switch to the next
+ // range because the previous one ended.
+ APInt Lower1(Bits, 0), Upper1(Bits, 0);
+ APInt Lower2(Bits, 0), Upper2(Bits, 0);
+ APInt Lower3(Bits, 0), Upper3(Bits, 0);
+ bool HaveRange1 = false, HaveInterrupt1 = false;
+ bool HaveRange2 = false, HaveInterrupt2 = false;
+ bool HaveRange3 = false, HaveInterrupt3 = false;
+
+ APInt Num(Bits, 0);
+ for (unsigned I = 0, Limit = 1 << Bits; I < Limit; ++I, ++Num) {
+ if (!CR1.contains(Num) || !CR2.contains(Num)) {
+ if (HaveRange3)
+ HaveInterrupt3 = true;
+ else if (HaveRange2)
+ HaveInterrupt2 = true;
+ else if (HaveRange1)
+ HaveInterrupt1 = true;
+ continue;
+ }
+
+ if (HaveRange3) {
+ Upper3 = Num;
+ } else if (HaveInterrupt2) {
+ HaveRange3 = true;
+ Lower3 = Upper3 = Num;
+ } else if (HaveRange2) {
+ Upper2 = Num;
+ } else if (HaveInterrupt1) {
+ HaveRange2 = true;
+ Lower2 = Upper2 = Num;
+ } else if (HaveRange1) {
+ Upper1 = Num;
+ } else {
+ HaveRange1 = true;
+ Lower1 = Upper1 = Num;
+ }
+ }
+
+ assert(!HaveInterrupt3 && "Should have at most three ranges");
+
+ ConstantRange CR = CR1.intersectWith(CR2);
+
+ if (!HaveRange1) {
+ EXPECT_TRUE(CR.isEmptySet());
+ return;
+ }
+
+ if (!HaveRange2) {
+ if (Lower1 == Upper1 + 1) {
+ EXPECT_TRUE(CR.isFullSet());
+ } else {
+ ConstantRange Expected(Lower1, Upper1 + 1);
+ EXPECT_EQ(Expected, CR);
+ }
+ return;
+ }
+
+ ConstantRange Variant1(Bits, /*full*/ true);
+ ConstantRange Variant2(Bits, /*full*/ true);
+ if (!HaveRange3) {
+ // Compute the two possible ways to cover two disjoint ranges.
+ if (Lower1 != Upper2 + 1)
+ Variant1 = ConstantRange(Lower1, Upper2 + 1);
+ if (Lower2 != Upper1 + 1)
+ Variant2 = ConstantRange(Lower2, Upper1 + 1);
+ } else {
+ // If we have three ranges, the first and last one have to be adjacent
+ // to the unsigned domain. It's better to think of this as having two
+ // holes, and we can construct one range using each hole.
+ assert(Lower1.isNullValue() && Upper3.isMaxValue());
+ Variant1 = ConstantRange(Lower2, Upper1 + 1);
+ Variant2 = ConstantRange(Lower3, Upper2 + 1);
+ }
+
+ // The intersection should return the smaller of the two variants.
+ if (Variant1.isSizeStrictlySmallerThan(Variant2))
+ EXPECT_EQ(Variant1, CR);
+ else if (Variant2.isSizeStrictlySmallerThan(Variant1))
+ EXPECT_EQ(Variant2, CR);
+ else
+ EXPECT_TRUE(Variant1 == CR || Variant2 == CR);
+ });
+}
+
TEST_F(ConstantRangeTest, UnionWith) {
EXPECT_EQ(Wrap.unionWith(One),
ConstantRange(APInt(16, 0xaaa), APInt(16, 0xb)));
static void TestOverflowExhaustive(Fn1 OverflowFn, Fn2 MayOverflowFn) {
// Constant range overflow checks are tested exhaustively on 4-bit numbers.
unsigned Bits = 4;
- unsigned Max = 1 << Bits;
- for (unsigned Lo1 = 0; Lo1 < Max; Lo1++) {
- for (unsigned Hi1 = 0; Hi1 < Max; Hi1++) {
- // Enforce ConstantRange invariant.
- if (Lo1 == Hi1 && Lo1 != 0 && Lo1 != Max - 1)
- continue;
-
- ConstantRange CR1(APInt(Bits, Lo1), APInt(Bits, Hi1));
- unsigned Size1 = CR1.getSetSize().getLimitedValue();
-
- for (unsigned Lo2 = 0; Lo2 < Max; Lo2++) {
- for (unsigned Hi2 = 0; Hi2 < Max; Hi2++) {
- // Enforce ConstantRange invariant.
- if (Lo2 == Hi2 && Lo2 != 0 && Lo2 != Max - 1)
- continue;
-
- ConstantRange CR2(APInt(Bits, Lo2), APInt(Bits, Hi2));
- unsigned Size2 = CR2.getSetSize().getLimitedValue();
-
- // Loop over all N1 in CR1 and N2 in CR2 and check whether any of the
- // operations have overflow / have no overflow. These loops are based
- // on Size1/Size2 to properly handle empty/full ranges.
- bool RangeHasOverflow = false;
- bool RangeHasNoOverflow = false;
- APInt N1(Bits, Lo1);
- for (unsigned I1 = 0; I1 < Size1; ++I1, ++N1) {
- APInt N2(Bits, Lo2);
- for (unsigned I2 = 0; I2 < Size2; ++I2, ++N2) {
- assert(CR1.contains(N1));
- assert(CR2.contains(N2));
-
- if (OverflowFn(N1, N2))
- RangeHasOverflow = true;
- else
- RangeHasNoOverflow = true;
- }
+ EnumerateTwoConstantRanges(Bits,
+ [=](const ConstantRange &CR1, const ConstantRange &CR2) {
+ unsigned Size1 = CR1.getSetSize().getLimitedValue();
+ unsigned Size2 = CR2.getSetSize().getLimitedValue();
+
+ // Loop over all N1 in CR1 and N2 in CR2 and check whether any of the
+ // operations have overflow / have no overflow. These loops are based
+ // on Size1/Size2 to properly handle empty/full ranges.
+ bool RangeHasOverflow = false;
+ bool RangeHasNoOverflow = false;
+ APInt N1 = CR1.getLower();
+ for (unsigned I1 = 0; I1 < Size1; ++I1, ++N1) {
+ APInt N2 = CR2.getLower();
+ for (unsigned I2 = 0; I2 < Size2; ++I2, ++N2) {
+ assert(CR1.contains(N1));
+ assert(CR2.contains(N2));
+
+ if (OverflowFn(N1, N2))
+ RangeHasOverflow = true;
+ else
+ RangeHasNoOverflow = true;
}
+ }
- ConstantRange::OverflowResult OR = MayOverflowFn(CR1, CR2);
- switch (OR) {
- case ConstantRange::OverflowResult::AlwaysOverflows:
- EXPECT_TRUE(RangeHasOverflow);
- EXPECT_FALSE(RangeHasNoOverflow);
+ ConstantRange::OverflowResult OR = MayOverflowFn(CR1, CR2);
+ switch (OR) {
+ case ConstantRange::OverflowResult::AlwaysOverflows:
+ EXPECT_TRUE(RangeHasOverflow);
+ EXPECT_FALSE(RangeHasNoOverflow);
+ break;
+ case ConstantRange::OverflowResult::NeverOverflows:
+ EXPECT_FALSE(RangeHasOverflow);
+ EXPECT_TRUE(RangeHasNoOverflow);
+ break;
+ case ConstantRange::OverflowResult::MayOverflow:
+ // We return MayOverflow for empty sets as a conservative result,
+ // but of course neither the RangeHasOverflow nor the
+ // RangeHasNoOverflow flags will be set.
+ if (CR1.isEmptySet() || CR2.isEmptySet())
break;
- case ConstantRange::OverflowResult::NeverOverflows:
- EXPECT_FALSE(RangeHasOverflow);
- EXPECT_TRUE(RangeHasNoOverflow);
- break;
- case ConstantRange::OverflowResult::MayOverflow:
- // We return MayOverflow for empty sets as a conservative result,
- // but of course neither the RangeHasOverflow nor the
- // RangeHasNoOverflow flags will be set.
- if (CR1.isEmptySet() || CR2.isEmptySet())
- break;
-
- EXPECT_TRUE(RangeHasOverflow);
- EXPECT_TRUE(RangeHasNoOverflow);
- break;
- }
+
+ EXPECT_TRUE(RangeHasOverflow);
+ EXPECT_TRUE(RangeHasNoOverflow);
+ break;
}
- }
- }
- }
+ });
}
TEST_F(ConstantRangeTest, UnsignedAddOverflowExhautive) {