summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCraig Topper <craig.topper@gmail.com>2017-04-24 17:18:47 +0000
committerCraig Topper <craig.topper@gmail.com>2017-04-24 17:18:47 +0000
commit8b37326ae2363c51ca33362cd49b28b9d624750e (patch)
tree5ca7f1a0c09b0fa553c37dc71ae1dd8cc56f6706
parent5dea6451389b97186760db15c3b1c3fc033d5ad1 (diff)
downloadbcm5719-llvm-8b37326ae2363c51ca33362cd49b28b9d624750e.tar.gz
bcm5719-llvm-8b37326ae2363c51ca33362cd49b28b9d624750e.zip
[APInt] Add ashrInPlace method and rewrite ashr to make a copy and then call ashrInPlace.
This patch adds an in place version of ashr to match lshr and shl which were recently added. I've tried to make this similar to the lshr code with additions to handle the sign extension. I've also tried to do this with less if checks than the current ashr code by sign extending the original result to a word boundary before doing any of the shifting. This removes a lot of the complexity of determining where to fill in sign bits after the shifting. Differential Revision: https://reviews.llvm.org/D32415 llvm-svn: 301198
-rw-r--r--llvm/include/llvm/ADT/APInt.h33
-rw-r--r--llvm/include/llvm/ADT/APSInt.h2
-rw-r--r--llvm/lib/Support/APInt.cpp112
-rw-r--r--llvm/unittests/ADT/APIntTest.cpp36
4 files changed, 104 insertions, 79 deletions
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 4b966faa6c1..d0104c3f0fa 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -193,6 +193,9 @@ private:
/// out-of-line slow case for lshr.
void lshrSlowCase(unsigned ShiftAmt);
+ /// out-of-line slow case for ashr.
+ void ashrSlowCase(unsigned ShiftAmt);
+
/// out-of-line slow case for operator=
void AssignSlowCase(const APInt &RHS);
@@ -893,7 +896,26 @@ public:
/// \brief Arithmetic right-shift function.
///
/// Arithmetic right-shift this APInt by shiftAmt.
- APInt ashr(unsigned shiftAmt) const;
+ APInt ashr(unsigned ShiftAmt) const {
+ APInt R(*this);
+ R.ashrInPlace(ShiftAmt);
+ return R;
+ }
+
+ /// Arithmetic right-shift this APInt by ShiftAmt in place.
+ void ashrInPlace(unsigned ShiftAmt) {
+ assert(ShiftAmt <= BitWidth && "Invalid shift amount");
+ if (isSingleWord()) {
+ int64_t SExtVAL = SignExtend64(VAL, BitWidth);
+ if (ShiftAmt == BitWidth)
+ VAL = SExtVAL >> (APINT_BITS_PER_WORD - 1); // Fill with sign bit.
+ else
+ VAL = SExtVAL >> ShiftAmt;
+ clearUnusedBits();
+ return;
+ }
+ ashrSlowCase(ShiftAmt);
+ }
/// \brief Logical right-shift function.
///
@@ -935,7 +957,14 @@ public:
/// \brief Arithmetic right-shift function.
///
/// Arithmetic right-shift this APInt by shiftAmt.
- APInt ashr(const APInt &shiftAmt) const;
+ APInt ashr(const APInt &ShiftAmt) const {
+ APInt R(*this);
+ R.ashrInPlace(ShiftAmt);
+ return R;
+ }
+
+ /// Arithmetic right-shift this APInt by shiftAmt in place.
+ void ashrInPlace(const APInt &shiftAmt);
/// \brief Logical right-shift function.
///
diff --git a/llvm/include/llvm/ADT/APSInt.h b/llvm/include/llvm/ADT/APSInt.h
index 650d276fdb5..dabbf3314bd 100644
--- a/llvm/include/llvm/ADT/APSInt.h
+++ b/llvm/include/llvm/ADT/APSInt.h
@@ -128,7 +128,7 @@ public:
if (IsUnsigned)
lshrInPlace(Amt);
else
- *this = *this >> Amt;
+ ashrInPlace(Amt);
return *this;
}
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index 8513214d05a..9c668468ff9 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -1026,91 +1026,51 @@ APInt APInt::sextOrSelf(unsigned width) const {
/// Arithmetic right-shift this APInt by shiftAmt.
/// @brief Arithmetic right-shift function.
-APInt APInt::ashr(const APInt &shiftAmt) const {
- return ashr((unsigned)shiftAmt.getLimitedValue(BitWidth));
+void APInt::ashrInPlace(const APInt &shiftAmt) {
+ ashrInPlace((unsigned)shiftAmt.getLimitedValue(BitWidth));
}
/// Arithmetic right-shift this APInt by shiftAmt.
/// @brief Arithmetic right-shift function.
-APInt APInt::ashr(unsigned shiftAmt) const {
- assert(shiftAmt <= BitWidth && "Invalid shift amount");
- // Handle a degenerate case
- if (shiftAmt == 0)
- return *this;
+void APInt::ashrSlowCase(unsigned ShiftAmt) {
+ // Don't bother performing a no-op shift.
+ if (!ShiftAmt)
+ return;
- // Handle single word shifts with built-in ashr
- if (isSingleWord()) {
- if (shiftAmt == BitWidth)
- // Undefined
- return APInt(BitWidth,
- SignExtend64(VAL, BitWidth) >> (APINT_BITS_PER_WORD - 1));
- return APInt(BitWidth, SignExtend64(VAL, BitWidth) >> shiftAmt);
- }
-
- // If all the bits were shifted out, the result is, technically, undefined.
- // We return -1 if it was negative, 0 otherwise. We check this early to avoid
- // issues in the algorithm below.
- if (shiftAmt == BitWidth) {
- if (isNegative())
- return APInt(BitWidth, WORD_MAX, true);
- else
- return APInt(BitWidth, 0);
- }
-
- // Create some space for the result.
- uint64_t * val = new uint64_t[getNumWords()];
-
- // Compute some values needed by the following shift algorithms
- unsigned wordShift = shiftAmt % APINT_BITS_PER_WORD; // bits to shift per word
- unsigned offset = shiftAmt / APINT_BITS_PER_WORD; // word offset for shift
- unsigned breakWord = getNumWords() - 1 - offset; // last word affected
- unsigned bitsInWord = whichBit(BitWidth); // how many bits in last word?
- if (bitsInWord == 0)
- bitsInWord = APINT_BITS_PER_WORD;
-
- // If we are shifting whole words, just move whole words
- if (wordShift == 0) {
- // Move the words containing significant bits
- for (unsigned i = 0; i <= breakWord; ++i)
- val[i] = pVal[i+offset]; // move whole word
-
- // Adjust the top significant word for sign bit fill, if negative
- if (isNegative())
- if (bitsInWord < APINT_BITS_PER_WORD)
- val[breakWord] |= WORD_MAX << bitsInWord; // set high bits
- } else {
- // Shift the low order words
- for (unsigned i = 0; i < breakWord; ++i) {
- // This combines the shifted corresponding word with the low bits from
- // the next word (shifted into this word's high bits).
- val[i] = (pVal[i+offset] >> wordShift) |
- (pVal[i+offset+1] << (APINT_BITS_PER_WORD - wordShift));
- }
+ // Save the original sign bit for later.
+ bool Negative = isNegative();
+
+ // WordShift is the inter-part shift; BitShift is is intra-part shift.
+ unsigned WordShift = ShiftAmt / APINT_BITS_PER_WORD;
+ unsigned BitShift = ShiftAmt % APINT_BITS_PER_WORD;
+
+ unsigned WordsToMove = getNumWords() - WordShift;
+ if (WordsToMove != 0) {
+ // Sign extend the last word to fill in the unused bits.
+ pVal[getNumWords() - 1] = SignExtend64(
+ pVal[getNumWords() - 1], ((BitWidth - 1) % APINT_BITS_PER_WORD) + 1);
- // Shift the break word. In this case there are no bits from the next word
- // to include in this word.
- val[breakWord] = pVal[breakWord+offset] >> wordShift;
-
- // Deal with sign extension in the break word, and possibly the word before
- // it.
- if (isNegative()) {
- if (wordShift > bitsInWord) {
- if (breakWord > 0)
- val[breakWord-1] |=
- WORD_MAX << (APINT_BITS_PER_WORD - (wordShift - bitsInWord));
- val[breakWord] |= WORD_MAX;
- } else
- val[breakWord] |= WORD_MAX << (bitsInWord - wordShift);
+ // Fastpath for moving by whole words.
+ if (BitShift == 0) {
+ std::memmove(pVal, pVal + WordShift, WordsToMove * APINT_WORD_SIZE);
+ } else {
+ // Move the words containing significant bits.
+ for (unsigned i = 0; i != WordsToMove - 1; ++i)
+ pVal[i] = (pVal[i + WordShift] >> BitShift) |
+ (pVal[i + WordShift + 1] << (APINT_BITS_PER_WORD - BitShift));
+
+ // Handle the last word which has no high bits to copy.
+ pVal[WordsToMove - 1] = pVal[WordShift + WordsToMove - 1] >> BitShift;
+ // Sign extend one more time.
+ pVal[WordsToMove - 1] =
+ SignExtend64(pVal[WordsToMove - 1], APINT_BITS_PER_WORD - BitShift);
}
}
- // Remaining words are 0 or -1, just assign them.
- uint64_t fillValue = (isNegative() ? WORD_MAX : 0);
- for (unsigned i = breakWord+1; i < getNumWords(); ++i)
- val[i] = fillValue;
- APInt Result(val, BitWidth);
- Result.clearUnusedBits();
- return Result;
+ // Fill in the remainder based on the original sign.
+ std::memset(pVal + WordsToMove, Negative ? -1 : 0,
+ WordShift * APINT_WORD_SIZE);
+ clearUnusedBits();
}
/// Logical right-shift this APInt by shiftAmt.
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index e0a53a56455..2235f271658 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -2024,6 +2024,42 @@ TEST(APIntTest, LogicalRightShift) {
EXPECT_EQ(0, neg_one.lshr(128));
}
+TEST(APIntTest, ArithmeticRightShift) {
+ APInt i72(APInt::getHighBitsSet(72, 1));
+ i72.ashrInPlace(46);
+ EXPECT_EQ(47U, i72.countLeadingOnes());
+ EXPECT_EQ(25U, i72.countTrailingZeros());
+ EXPECT_EQ(47U, i72.countPopulation());
+
+ i72 = APInt::getHighBitsSet(72, 1);
+ i72.ashrInPlace(64);
+ EXPECT_EQ(65U, i72.countLeadingOnes());
+ EXPECT_EQ(7U, i72.countTrailingZeros());
+ EXPECT_EQ(65U, i72.countPopulation());
+
+ APInt i128(APInt::getHighBitsSet(128, 1));
+ i128.ashrInPlace(64);
+ EXPECT_EQ(65U, i128.countLeadingOnes());
+ EXPECT_EQ(63U, i128.countTrailingZeros());
+ EXPECT_EQ(65U, i128.countPopulation());
+
+ // Ensure we handle large shifts of multi-word.
+ const APInt signmin32(APInt::getSignedMinValue(32));
+ EXPECT_TRUE(signmin32.ashr(32).isAllOnesValue());
+
+ // Ensure we handle large shifts of multi-word.
+ const APInt umax32(APInt::getSignedMaxValue(32));
+ EXPECT_EQ(0, umax32.ashr(32));
+
+ // Ensure we handle large shifts of multi-word.
+ const APInt signmin128(APInt::getSignedMinValue(128));
+ EXPECT_TRUE(signmin128.ashr(128).isAllOnesValue());
+
+ // Ensure we handle large shifts of multi-word.
+ const APInt umax128(APInt::getSignedMaxValue(128));
+ EXPECT_EQ(0, umax128.ashr(128));
+}
+
TEST(APIntTest, LeftShift) {
APInt i256(APInt::getLowBitsSet(256, 2));
OpenPOWER on IntegriCloud