Skip to content

Commit 3dc7c7c

Browse files
committed
[ConstantRange] Clarify makeGuaranteedNoWrapRegion() guarantees; NFC
makeGuaranteedNoWrapRegion() is actually makeExactNoWrapRegion() as long as only one of NUW or NSW is specified. This is not obvious from the current documentation, and some code seems to think that it is only exact for single-element ranges. Clarify docs and add tests to be more confident this really holds. There are currently no users of makeGuaranteedNoWrapRegion() that pass both NUW and NSW. I think it would be best to drop support for this entirely and then rename the function to makeExactNoWrapRegion(). Knowing that the no-wrap region is exact is useful, because we can backwards-constrain values. What I have in mind in particular is that LVI should be able to constrain values on edges where the with.overflow overflow flag is false. Differential Revision: https://reviews.llvm.org/D60598 llvm-svn: 358305
1 parent 2312a06 commit 3dc7c7c

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,11 @@ class LLVM_NODISCARD ConstantRange {
119119
/// Return the largest range containing all X such that "X BinOpC Y" is
120120
/// guaranteed not to wrap (overflow) for all Y in Other.
121121
///
122-
/// NB! The returned set does *not* contain **all** possible values of X for
123-
/// which "X BinOpC Y" does not wrap -- some viable values of X may be
124-
/// missing, so you cannot use this to constrain X's range. E.g. in the
122+
/// If only one of NoUnsignedWrap or NoSignedWrap is specified, the returned
123+
/// range is exact: It contains *all* possible values of X for which
124+
/// "X BinOpC Y" does not wrap. However, if both NUW and NSW are specified, it
125+
/// may return only a subset of non-wrapping values. In this case the
126+
/// returned region cannot be used to constrain X's range. E.g. in the
125127
/// fourth example, "(-2) + 1" is both nsw and nuw (so the "X" could be -2),
126128
/// but (-2) is not in the set returned.
127129
///

llvm/lib/IR/ConstantRange.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -869,8 +869,7 @@ ConstantRange::add(const ConstantRange &Other) const {
869869
ConstantRange ConstantRange::addWithNoSignedWrap(const APInt &Other) const {
870870
// Calculate the subset of this range such that "X + Other" is
871871
// guaranteed not to wrap (overflow) for all X in this subset.
872-
// makeGuaranteedNoWrapRegion will produce an exact NSW range since we are
873-
// passing a single element range.
872+
// makeGuaranteedNoWrapRegion will produce an exact NSW range.
874873
auto NSWRange = ConstantRange::makeGuaranteedNoWrapRegion(BinaryOperator::Add,
875874
ConstantRange(Other),
876875
OverflowingBinaryOperator::NoSignedWrap);

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,76 @@ TEST(ConstantRange, MakeGuaranteedNoWrapRegion) {
11511151
ConstantRange(APInt::getMinValue(32) + 1, APInt::getSignedMinValue(32)));
11521152
}
11531153

1154+
template<typename Fn>
1155+
void TestNoWrapRegionExhaustive(Instruction::BinaryOps BinOp,
1156+
unsigned NoWrapKind, Fn OverflowFn) {
1157+
// When using 4 bits this test needs ~3s on a debug build.
1158+
unsigned Bits = 3;
1159+
EnumerateTwoConstantRanges(Bits,
1160+
[&](const ConstantRange &CR1, const ConstantRange &CR2) {
1161+
if (CR2.isEmptySet())
1162+
return;
1163+
1164+
ConstantRange NoWrap =
1165+
ConstantRange::makeGuaranteedNoWrapRegion(BinOp, CR2, NoWrapKind);
1166+
ForeachNumInConstantRange(CR1, [&](const APInt &N1) {
1167+
bool NoOverflow = true;
1168+
ForeachNumInConstantRange(CR2, [&](const APInt &N2) {
1169+
if (OverflowFn(N1, N2))
1170+
NoOverflow = false;
1171+
});
1172+
EXPECT_EQ(NoOverflow, NoWrap.contains(N1));
1173+
});
1174+
});
1175+
}
1176+
1177+
// Show that makeGuaranteedNoWrapRegion is precise if only one of
1178+
// NoUnsignedWrap or NoSignedWrap is used.
1179+
TEST(ConstantRange, NoWrapRegionExhaustive) {
1180+
TestNoWrapRegionExhaustive(
1181+
Instruction::Add, OverflowingBinaryOperator::NoUnsignedWrap,
1182+
[](const APInt &N1, const APInt &N2) {
1183+
bool Overflow;
1184+
(void) N1.uadd_ov(N2, Overflow);
1185+
return Overflow;
1186+
});
1187+
TestNoWrapRegionExhaustive(
1188+
Instruction::Add, OverflowingBinaryOperator::NoSignedWrap,
1189+
[](const APInt &N1, const APInt &N2) {
1190+
bool Overflow;
1191+
(void) N1.sadd_ov(N2, Overflow);
1192+
return Overflow;
1193+
});
1194+
TestNoWrapRegionExhaustive(
1195+
Instruction::Sub, OverflowingBinaryOperator::NoUnsignedWrap,
1196+
[](const APInt &N1, const APInt &N2) {
1197+
bool Overflow;
1198+
(void) N1.usub_ov(N2, Overflow);
1199+
return Overflow;
1200+
});
1201+
TestNoWrapRegionExhaustive(
1202+
Instruction::Sub, OverflowingBinaryOperator::NoSignedWrap,
1203+
[](const APInt &N1, const APInt &N2) {
1204+
bool Overflow;
1205+
(void) N1.ssub_ov(N2, Overflow);
1206+
return Overflow;
1207+
});
1208+
TestNoWrapRegionExhaustive(
1209+
Instruction::Mul, OverflowingBinaryOperator::NoUnsignedWrap,
1210+
[](const APInt &N1, const APInt &N2) {
1211+
bool Overflow;
1212+
(void) N1.umul_ov(N2, Overflow);
1213+
return Overflow;
1214+
});
1215+
TestNoWrapRegionExhaustive(
1216+
Instruction::Mul, OverflowingBinaryOperator::NoSignedWrap,
1217+
[](const APInt &N1, const APInt &N2) {
1218+
bool Overflow;
1219+
(void) N1.smul_ov(N2, Overflow);
1220+
return Overflow;
1221+
});
1222+
}
1223+
11541224
TEST(ConstantRange, GetEquivalentICmp) {
11551225
APInt RHS;
11561226
CmpInst::Predicate Pred;

0 commit comments

Comments
 (0)