diff --git a/include/llvm/ADT/APInt.h b/include/llvm/ADT/APInt.h index c697402341f..022cfc985e1 100644 --- a/include/llvm/ADT/APInt.h +++ b/include/llvm/ADT/APInt.h @@ -198,6 +198,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); @@ -898,7 +901,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); // undefined + else + VAL = SExtVAL >> ShiftAmt; + clearUnusedBits(); + return; + } + ashrSlowCase(ShiftAmt); + } /// \brief Logical right-shift function. /// @@ -940,7 +962,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/include/llvm/ADT/APSInt.h b/include/llvm/ADT/APSInt.h index 650d276fdb5..dabbf3314bd 100644 --- a/include/llvm/ADT/APSInt.h +++ b/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/lib/Support/APInt.cpp b/lib/Support/APInt.cpp index 9bb364af279..c72e3bf9872 100644 --- a/lib/Support/APInt.cpp +++ b/lib/Support/APInt.cpp @@ -1029,89 +1029,42 @@ 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) - return APInt(BitWidth, 0); // undefined - return APInt(BitWidth, SignExtend64(VAL, BitWidth) >> shiftAmt); - } + bool Negative = isNegative(); - // 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); - } + // WordShift is the inter-part shift; BitShift is is intra-part shift. + unsigned Words = getNumWords(); + unsigned WordShift = std::min(ShiftAmt / APINT_BITS_PER_WORD, Words); + unsigned BitShift = ShiftAmt % APINT_BITS_PER_WORD; - // 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 + unsigned WordsToMove = Words - WordShift; + // Fastpath for moving by whole words. + if (BitShift == 0) { + std::memmove(pVal, pVal + WordShift, WordsToMove * APINT_WORD_SIZE); } 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)); - } - - // 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); + for (unsigned i = 0; i != WordsToMove; ++i) { + pVal[i] = pVal[i + WordShift] >> BitShift; + if (i + 1 != WordsToMove) + pVal[i] |= pVal[i + WordShift + 1] << (APINT_BITS_PER_WORD - BitShift); + else if (Negative) + pVal[i] |= WORD_MAX << (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/unittests/ADT/APIntTest.cpp b/unittests/ADT/APIntTest.cpp index 5d3afe9a159..6b889bf2dfd 100644 --- a/unittests/ADT/APIntTest.cpp +++ b/unittests/ADT/APIntTest.cpp @@ -288,7 +288,7 @@ TEST(APIntTest, i1) { EXPECT_EQ(zero, one.shl(1)); EXPECT_EQ(one, one.shl(0)); EXPECT_EQ(zero, one.lshr(1)); - EXPECT_EQ(zero, one.ashr(1)); + EXPECT_EQ(one, one.ashr(1)); // Rotates. EXPECT_EQ(one, one.rotl(0)); @@ -2024,6 +2024,24 @@ TEST(APIntTest, LogicalRightShift) { EXPECT_EQ(0, neg_one.lshr(128)); } +TEST(APIntTest, ArithmeticRightShift) { + // 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));