[NFCI][IR] ConstantRangeTest: refactor operation range gatherers

We do the same dance to acquire the "exact" range of an op via
an exhaustive approach in many places.
Let's not invent the wheel each time.
This commit is contained in:
Roman Lebedev 2020-09-24 17:13:09 +03:00
parent 0c0c57f7b2
commit 31177949cb
No known key found for this signature in database
GPG Key ID: 083C3EBB4A1689E0
1 changed files with 138 additions and 141 deletions

View File

@ -59,107 +59,130 @@ static void ForeachNumInConstantRange(const ConstantRange &CR, Fn TestFn) {
}
}
template<typename Fn1, typename Fn2>
static void TestUnsignedUnaryOpExhaustive(
Fn1 RangeFn, Fn2 IntFn, bool SkipSignedIntMin = false) {
struct OpRangeGathererBase {
void account(const APInt &N);
ConstantRange getRange();
};
struct UnsignedOpRangeGatherer : public OpRangeGathererBase {
APInt Min;
APInt Max;
UnsignedOpRangeGatherer(unsigned Bits)
: Min(APInt::getMaxValue(Bits)), Max(APInt::getMinValue(Bits)) {}
void account(const APInt &N) {
if (N.ult(Min))
Min = N;
if (N.ugt(Max))
Max = N;
}
ConstantRange getRange() {
if (Min.ugt(Max))
return ConstantRange::getEmpty(Min.getBitWidth());
return ConstantRange::getNonEmpty(Min, Max + 1);
}
};
struct SignedOpRangeGatherer : public OpRangeGathererBase {
APInt Min;
APInt Max;
SignedOpRangeGatherer(unsigned Bits)
: Min(APInt::getSignedMaxValue(Bits)),
Max(APInt::getSignedMinValue(Bits)) {}
void account(const APInt &N) {
if (N.slt(Min))
Min = N;
if (N.sgt(Max))
Max = N;
}
ConstantRange getRange() {
if (Min.sgt(Max))
return ConstantRange::getEmpty(Min.getBitWidth());
return ConstantRange::getNonEmpty(Min, Max + 1);
}
};
template <typename Fn1, typename Fn2>
static void TestUnsignedUnaryOpExhaustive(Fn1 RangeFn, Fn2 IntFn,
bool SkipSignedIntMin = false) {
unsigned Bits = 4;
EnumerateConstantRanges(Bits, [&](const ConstantRange &CR) {
APInt Min = APInt::getMaxValue(Bits);
APInt Max = APInt::getMinValue(Bits);
UnsignedOpRangeGatherer R(CR.getBitWidth());
ForeachNumInConstantRange(CR, [&](const APInt &N) {
if (SkipSignedIntMin && N.isMinSignedValue())
return;
APInt AbsN = IntFn(N);
if (AbsN.ult(Min))
Min = AbsN;
if (AbsN.ugt(Max))
Max = AbsN;
R.account(IntFn(N));
});
ConstantRange ResultCR = RangeFn(CR);
if (Min.ugt(Max)) {
EXPECT_TRUE(ResultCR.isEmptySet());
return;
}
ConstantRange ExactCR = R.getRange();
ConstantRange ActualCR = RangeFn(CR);
ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
EXPECT_EQ(Exact, ResultCR);
EXPECT_EQ(ExactCR, ActualCR);
});
}
template<typename Fn1, typename Fn2>
static void TestUnsignedBinOpExhaustive(
Fn1 RangeFn, Fn2 IntFn,
bool SkipZeroRHS = false, bool CorrectnessOnly = false) {
template <typename Fn1, typename Fn2>
static void TestUnsignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn,
bool SkipZeroRHS = false,
bool CorrectnessOnly = false) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
APInt Min = APInt::getMaxValue(Bits);
APInt Max = APInt::getMinValue(Bits);
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
if (SkipZeroRHS && N2 == 0)
EnumerateTwoConstantRanges(
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
UnsignedOpRangeGatherer R(CR1.getBitWidth());
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
if (SkipZeroRHS && N2 == 0)
return;
R.account(IntFn(N1, N2));
});
});
ConstantRange CR = RangeFn(CR1, CR2);
ConstantRange Exact = R.getRange();
if (!CorrectnessOnly) {
EXPECT_EQ(Exact, CR);
return;
}
APInt N = IntFn(N1, N2);
if (N.ult(Min))
Min = N;
if (N.ugt(Max))
Max = N;
EXPECT_TRUE(CR.contains(Exact));
if (Exact.isEmptySet())
EXPECT_TRUE(CR.isEmptySet());
});
});
ConstantRange CR = RangeFn(CR1, CR2);
if (Min.ugt(Max)) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
if (CorrectnessOnly) {
EXPECT_TRUE(CR.contains(Exact));
} else {
EXPECT_EQ(Exact, CR);
}
});
}
template<typename Fn1, typename Fn2>
static void TestSignedBinOpExhaustive(
Fn1 RangeFn, Fn2 IntFn,
bool SkipZeroRHS = false, bool CorrectnessOnly = false) {
template <typename Fn1, typename Fn2>
static void TestSignedBinOpExhaustive(Fn1 RangeFn, Fn2 IntFn,
bool SkipZeroRHS = false,
bool CorrectnessOnly = false) {
unsigned Bits = 4;
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
APInt Min = APInt::getSignedMaxValue(Bits);
APInt Max = APInt::getSignedMinValue(Bits);
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
if (SkipZeroRHS && N2 == 0)
return;
EnumerateTwoConstantRanges(
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
SignedOpRangeGatherer R(CR1.getBitWidth());
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
if (SkipZeroRHS && N2 == 0)
return;
APInt N = IntFn(N1, N2);
if (N.slt(Min))
Min = N;
if (N.sgt(Max))
Max = N;
R.account(IntFn(N1, N2));
});
});
ConstantRange CR = RangeFn(CR1, CR2);
ConstantRange Exact = R.getRange();
if (CorrectnessOnly) {
EXPECT_TRUE(CR.contains(Exact));
} else {
EXPECT_EQ(Exact, CR);
}
});
});
ConstantRange CR = RangeFn(CR1, CR2);
if (Min.sgt(Max)) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
if (CorrectnessOnly) {
EXPECT_TRUE(CR.contains(Exact));
} else {
EXPECT_EQ(Exact, CR);
}
});
}
ConstantRange ConstantRangeTest::Full(16, true);
@ -731,8 +754,7 @@ static void TestAddWithNoSignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
ConstantRange CR = RangeFn(CR1, CR2);
APInt Min = APInt::getSignedMaxValue(Bits);
APInt Max = APInt::getSignedMinValue(Bits);
SignedOpRangeGatherer R(CR.getBitWidth());
bool AllOverflow = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
@ -740,10 +762,7 @@ static void TestAddWithNoSignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
APInt N = IntFn(IsOverflow, N1, N2);
if (!IsOverflow) {
AllOverflow = false;
if (N.slt(Min))
Min = N;
if (N.sgt(Max))
Max = N;
R.account(N);
EXPECT_TRUE(CR.contains(N));
}
});
@ -751,15 +770,11 @@ static void TestAddWithNoSignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
EXPECT_EQ(CR.isEmptySet(), AllOverflow);
if (!CR1.isSignWrappedSet() && !CR2.isSignWrappedSet()) {
if (Min.sgt(Max)) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
if (CR1.isSignWrappedSet() || CR2.isSignWrappedSet())
return;
ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
EXPECT_EQ(Exact, CR);
}
ConstantRange Exact = R.getRange();
EXPECT_EQ(Exact, CR);
});
}
@ -769,8 +784,7 @@ static void TestAddWithNoUnsignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1,
const ConstantRange &CR2) {
ConstantRange CR = RangeFn(CR1, CR2);
APInt Min = APInt::getMaxValue(Bits);
APInt Max = APInt::getMinValue(Bits);
UnsignedOpRangeGatherer R(CR.getBitWidth());
bool AllOverflow = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
@ -778,10 +792,7 @@ static void TestAddWithNoUnsignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
APInt N = IntFn(IsOverflow, N1, N2);
if (!IsOverflow) {
AllOverflow = false;
if (N.ult(Min))
Min = N;
if (N.ugt(Max))
Max = N;
R.account(N);
EXPECT_TRUE(CR.contains(N));
}
});
@ -789,15 +800,11 @@ static void TestAddWithNoUnsignedWrapExhaustive(Fn1 RangeFn, Fn2 IntFn) {
EXPECT_EQ(CR.isEmptySet(), AllOverflow);
if (!CR1.isWrappedSet() && !CR2.isWrappedSet()) {
if (Min.ugt(Max)) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
if (CR1.isWrappedSet() || CR2.isWrappedSet())
return;
ConstantRange Exact = ConstantRange::getNonEmpty(Min, Max + 1);
EXPECT_EQ(Exact, CR);
}
ConstantRange Exact = R.getRange();
EXPECT_EQ(Exact, CR);
});
}
@ -809,10 +816,8 @@ static void TestAddWithNoSignedUnsignedWrapExhaustive(Fn1 RangeFn,
EnumerateTwoConstantRanges(
Bits, [&](const ConstantRange &CR1, const ConstantRange &CR2) {
ConstantRange CR = RangeFn(CR1, CR2);
APInt UMin = APInt::getMaxValue(Bits);
APInt UMax = APInt::getMinValue(Bits);
APInt SMin = APInt::getSignedMaxValue(Bits);
APInt SMax = APInt::getSignedMinValue(Bits);
UnsignedOpRangeGatherer UR(CR.getBitWidth());
SignedOpRangeGatherer SR(CR.getBitWidth());
bool AllOverflow = true;
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
@ -821,14 +826,8 @@ static void TestAddWithNoSignedUnsignedWrapExhaustive(Fn1 RangeFn,
(void) IntFnUnsigned(IsOverflow, N1, N2);
if (!IsSignedOverflow && !IsOverflow) {
AllOverflow = false;
if (N.slt(SMin))
SMin = N;
if (N.sgt(SMax))
SMax = N;
if (N.ult(UMin))
UMin = N;
if (N.ugt(UMax))
UMax = N;
UR.account(N);
SR.account(N);
EXPECT_TRUE(CR.contains(N));
}
});
@ -836,18 +835,20 @@ static void TestAddWithNoSignedUnsignedWrapExhaustive(Fn1 RangeFn,
EXPECT_EQ(CR.isEmptySet(), AllOverflow);
if (!CR1.isWrappedSet() && !CR2.isWrappedSet() &&
!CR1.isSignWrappedSet() && !CR2.isSignWrappedSet()) {
if (UMin.ugt(UMax) || SMin.sgt(SMax)) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
if (CR1.isWrappedSet() || CR2.isWrappedSet() ||
CR1.isSignWrappedSet() || CR2.isSignWrappedSet())
return;
ConstantRange Exact =
ConstantRange::getNonEmpty(SMin, SMax + 1)
.intersectWith(ConstantRange::getNonEmpty(UMin, UMax + 1));
EXPECT_EQ(Exact, CR);
ConstantRange ExactUnsignedCR = UR.getRange();
ConstantRange ExactSignedCR = SR.getRange();
if (ExactUnsignedCR.isEmptySet() || ExactSignedCR.isEmptySet()) {
EXPECT_TRUE(CR.isEmptySet());
return;
}
ConstantRange Exact = ExactSignedCR.intersectWith(ExactUnsignedCR);
EXPECT_EQ(Exact, CR);
});
}
@ -2229,23 +2230,19 @@ TEST_F(ConstantRangeTest, FromKnownBitsExhaustive) {
if (Known.hasConflict() || Known.isUnknown())
continue;
APInt MinUnsigned = APInt::getMaxValue(Bits);
APInt MaxUnsigned = APInt::getMinValue(Bits);
APInt MinSigned = APInt::getSignedMaxValue(Bits);
APInt MaxSigned = APInt::getSignedMinValue(Bits);
UnsignedOpRangeGatherer UR(Bits);
SignedOpRangeGatherer SR(Bits);
for (unsigned N = 0; N < Max; ++N) {
APInt Num(Bits, N);
if ((Num & Known.Zero) != 0 || (~Num & Known.One) != 0)
continue;
if (Num.ult(MinUnsigned)) MinUnsigned = Num;
if (Num.ugt(MaxUnsigned)) MaxUnsigned = Num;
if (Num.slt(MinSigned)) MinSigned = Num;
if (Num.sgt(MaxSigned)) MaxSigned = Num;
UR.account(Num);
SR.account(Num);
}
ConstantRange UnsignedCR(MinUnsigned, MaxUnsigned + 1);
ConstantRange SignedCR(MinSigned, MaxSigned + 1);
ConstantRange UnsignedCR = UR.getRange();
ConstantRange SignedCR = SR.getRange();
EXPECT_EQ(UnsignedCR, ConstantRange::fromKnownBits(Known, false));
EXPECT_EQ(SignedCR, ConstantRange::fromKnownBits(Known, true));
}