-
Notifications
You must be signed in to change notification settings - Fork 12k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[APInt] Add default-disabled assertion to APInt constructor #106524
Conversation
If the uint64_t constructor is used, assert that the value is actually a signed or unsigned N-bit integer depending on whether the isSigned flag is set. Provide an implicitTrunc flag to restore the previous behavior, where the argument is silently truncated instead. In this commit, implicitTrunc is enabled by default, which means that the new assertions are disabled and no actual change in behavior occurs. The plan is to flip the default once all places violating the assertion have been fixed. See llvm#80309 for the scope of the necessary changes. The primary motivation for this change is to avoid incorrectly specified isSigned flags. A recurring problem we have is that people write something like `APInt(BW, -1)` and this works perfectly fine -- until the code path is hit with `BW > 64`. Most of our i128 specific miscompilations are caused by variants of this issue. The cost of the change is that we have to specify the correct isSigned flag (and make sure there are no excess bits) for uses where BW is always <= 64 as well.
@llvm/pr-subscribers-llvm-adt @llvm/pr-subscribers-llvm-support Author: Nikita Popov (nikic) ChangesIf the uint64_t constructor is used, assert that the value is actually a signed or unsigned N-bit integer depending on whether the isSigned flag is set. Provide an implicitTrunc flag to restore the previous behavior, where the argument is silently truncated instead. In this commit, implicitTrunc is enabled by default, which means that the new assertions are disabled and no actual change in behavior occurs. The plan is to flip the default once all places violating the assertion have been fixed. See #80309 for the scope of the necessary changes. The primary motivation for this change is to avoid incorrectly specified isSigned flags. A recurring problem we have is that people write something like The cost of the change is that we have to specify the correct isSigned flag (and make sure there are no excess bits) for uses where BW is always <= 64 as well. Full diff: https://github.com/llvm/llvm-project/pull/106524.diff 3 Files Affected:
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 65ba3f15305c78..a42dae8887392d 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -106,11 +106,26 @@ class [[nodiscard]] APInt {
/// \param numBits the bit width of the constructed APInt
/// \param val the initial value of the APInt
/// \param isSigned how to treat signedness of val
- APInt(unsigned numBits, uint64_t val, bool isSigned = false)
+ /// \param implicitTrunc allow implicit truncation of non-zero/sign bits of
+ /// val beyond the range of numBits
+ APInt(unsigned numBits, uint64_t val, bool isSigned = false,
+ bool implicitTrunc = true)
: BitWidth(numBits) {
+ if (!implicitTrunc) {
+ if (BitWidth == 0) {
+ assert(val == 0 && "Value must be zero for 0-bit APInt");
+ } else if (isSigned) {
+ assert(llvm::isIntN(BitWidth, val) &&
+ "Value is not an N-bit signed value");
+ } else {
+ assert(llvm::isUIntN(BitWidth, val) &&
+ "Value is not an N-bit unsigned value");
+ }
+ }
if (isSingleWord()) {
U.VAL = val;
- clearUnusedBits();
+ if (implicitTrunc || isSigned)
+ clearUnusedBits();
} else {
initSlowCase(val, isSigned);
}
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index fe22e9ba04b6f5..78d573966c6c99 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -234,7 +234,8 @@ APInt& APInt::operator-=(uint64_t RHS) {
APInt APInt::operator*(const APInt& RHS) const {
assert(BitWidth == RHS.BitWidth && "Bit widths must be the same");
if (isSingleWord())
- return APInt(BitWidth, U.VAL * RHS.U.VAL);
+ return APInt(BitWidth, U.VAL * RHS.U.VAL, /*isSigned=*/false,
+ /*implicitTrunc=*/true);
APInt Result(getMemory(getNumWords()), getBitWidth());
tcMultiply(Result.U.pVal, U.pVal, RHS.U.pVal, getNumWords());
@@ -455,7 +456,8 @@ APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const {
"Illegal bit extraction");
if (isSingleWord())
- return APInt(numBits, U.VAL >> bitPosition);
+ return APInt(numBits, U.VAL >> bitPosition, /*isSigned=*/false,
+ /*implicitTrunc=*/true);
unsigned loBit = whichBit(bitPosition);
unsigned loWord = whichWord(bitPosition);
@@ -463,7 +465,8 @@ APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const {
// Single word result extracting bits from a single word source.
if (loWord == hiWord)
- return APInt(numBits, U.pVal[loWord] >> loBit);
+ return APInt(numBits, U.pVal[loWord] >> loBit, /*isSigned=*/false,
+ /*implicitTrunc=*/true);
// Extracting bits that start on a source word boundary can be done
// as a fast memory copy.
@@ -907,7 +910,8 @@ APInt APInt::trunc(unsigned width) const {
assert(width <= BitWidth && "Invalid APInt Truncate request");
if (width <= APINT_BITS_PER_WORD)
- return APInt(width, getRawData()[0]);
+ return APInt(width, getRawData()[0], /*isSigned=*/false,
+ /*implicitTrunc=*/true);
if (width == BitWidth)
return *this;
@@ -955,7 +959,7 @@ APInt APInt::sext(unsigned Width) const {
assert(Width >= BitWidth && "Invalid APInt SignExtend request");
if (Width <= APINT_BITS_PER_WORD)
- return APInt(Width, SignExtend64(U.VAL, BitWidth));
+ return APInt(Width, SignExtend64(U.VAL, BitWidth), /*isSigned=*/true);
if (Width == BitWidth)
return *this;
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index eb4b847185f53b..fff29d24a05299 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -220,11 +220,12 @@ TEST(APIntTest, i256) {
}
TEST(APIntTest, i1) {
- const APInt neg_two(1, static_cast<uint64_t>(-2), true);
+ const APInt neg_two(1, static_cast<uint64_t>(-2), true,
+ /*implicitTrunc=*/true);
const APInt neg_one(1, static_cast<uint64_t>(-1), true);
const APInt zero(1, 0);
const APInt one(1, 1);
- const APInt two(1, 2);
+ const APInt two(1, 2, false, /*implicitTrunc=*/true);
EXPECT_EQ(0, neg_two.getSExtValue());
EXPECT_EQ(-1, neg_one.getSExtValue());
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This makes unit tests compatible with the assertion added in #106524, by setting the isSigned flag to the correct value or changing how the value is constructed.
This fixes all the places in MLIR that hit the new assertion added in llvm#106524, in preparation for enabling it by default. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, or set the implicitTrunc flag to retain the old behavior. I've left TODOs for the latter case in some places, where I think that it may be worthwhile to stop doing implicit truncation in the future. Note that the assertion is currently still disabled by default, so this patch is mostly NFC.
This makes unit tests compatible with the assertion added in llvm#106524, by setting the isSigned flag to the correct value or changing how the value is constructed.
…unc (#110466) This fixes all the places in MLIR that hit the new assertion added in #106524, in preparation for enabling it by default. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, or set the implicitTrunc flag to retain the old behavior. I've left TODOs for the latter case in some places, where I think that it may be worthwhile to stop doing implicit truncation in the future. Note that the assertion is currently still disabled by default, so this patch is mostly NFC. This is just the MLIR changes split off from #80309.
…unc (llvm#110466) This fixes all the places in MLIR that hit the new assertion added in llvm#106524, in preparation for enabling it by default. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, or set the implicitTrunc flag to retain the old behavior. I've left TODOs for the latter case in some places, where I think that it may be worthwhile to stop doing implicit truncation in the future. Note that the assertion is currently still disabled by default, so this patch is mostly NFC. This is just the MLIR changes split off from llvm#80309.
…CI) (#80309) This fixes all the places that hit the new assertion added in #106524 in tests. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, set the implicitTrunc flag, or perform more calculations inside APInt. Note that the assertion is currently still disabled by default, so this patch is mostly NFC.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true.
…unc (llvm#110466) This fixes all the places in MLIR that hit the new assertion added in llvm#106524, in preparation for enabling it by default. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, or set the implicitTrunc flag to retain the old behavior. I've left TODOs for the latter case in some places, where I think that it may be worthwhile to stop doing implicit truncation in the future. Note that the assertion is currently still disabled by default, so this patch is mostly NFC. This is just the MLIR changes split off from llvm#80309.
…CI) (llvm#80309) This fixes all the places that hit the new assertion added in llvm#106524 in tests. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, set the implicitTrunc flag, or perform more calculations inside APInt. Note that the assertion is currently still disabled by default, so this patch is mostly NFC.
This enables the assertion introduced in #106524, which checks that the value passed to the APInt constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in #80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the APInt constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true.
…unc (llvm#110466) This fixes all the places in MLIR that hit the new assertion added in llvm#106524, in preparation for enabling it by default. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, or set the implicitTrunc flag to retain the old behavior. I've left TODOs for the latter case in some places, where I think that it may be worthwhile to stop doing implicit truncation in the future. Note that the assertion is currently still disabled by default, so this patch is mostly NFC. This is just the MLIR changes split off from llvm#80309.
…CI) (llvm#80309) This fixes all the places that hit the new assertion added in llvm#106524 in tests. That is, cases where the value passed to the APInt constructor is not an N-bit signed/unsigned integer, where N is the bit width and signedness is determined by the isSigned flag. The fixes either set the correct value for isSigned, set the implicitTrunc flag, or perform more calculations inside APInt. Note that the assertion is currently still disabled by default, so this patch is mostly NFC.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the APInt constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true.
…stant. NFC This assert is also present inside the APInt constructor after llvm#106524, but its not yet enabled by default. So we also need to pass false to the implicitTrunc flag.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true. ----- The buildbot failures from the previous attempt should be fixed by a18dd29 and e2074c6.
This enables the assertion introduced in #106524, which checks that the value passed to the constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in #80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true. ----- The buildbot failures from the previous attempt should be fixed by a18dd29 and e2074c6.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true. ----- The buildbot failures from the previous attempt should be fixed by a18dd29 and e2074c6.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true. ----- The buildbot failures from the previous attempt should be fixed by a18dd29 and e2074c6.
This enables the assertion introduced in llvm#106524, which checks that the value passed to the constructor is indeed a valid N-bit signed or unsigned integer. Places that previously violated the assertion were updated in advance, e.g. in llvm#80309. It is possible to opt-out of the check and restore the previous behavior by setting implicitTrunc=true. ----- The buildbot failures from the previous attempt should be fixed by a18dd29 and e2074c6.
If the uint64_t constructor is used, assert that the value is actually a signed or unsigned N-bit integer depending on whether the isSigned flag is set. Provide an implicitTrunc flag to restore the previous behavior, where the argument is silently truncated instead.
In this commit, implicitTrunc is enabled by default, which means that the new assertions are disabled and no actual change in behavior occurs. The plan is to flip the default once all places violating the assertion have been fixed. See #80309 for the scope of the necessary changes.
The primary motivation for this change is to avoid incorrectly specified isSigned flags. A recurring problem we have is that people write something like
APInt(BW, -1)
and this works perfectly fine -- until the code path is hit withBW > 64
. Most of our i128 specific miscompilations are caused by variants of this issue.The cost of the change is that we have to specify the correct isSigned flag (and make sure there are no excess bits) for uses where BW is always <= 64 as well.