diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 4d60ad4459ad..e68dc8bd14d9 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -2410,6 +2410,35 @@ LLVM_ABI std::optional GetMostSignificantDifferentBit(const APInt &A, /// A.getBitwidth() or NewBitWidth must be a whole multiples of the other. LLVM_ABI APInt ScaleBitMask(const APInt &A, unsigned NewBitWidth, bool MatchAllBits = false); + +/// Perform a funnel shift left. +/// +/// Concatenate Hi and Lo (Hi is the most significant bits of the wide value), +/// the combined value is shifted left by Shift (modulo the bit width of the +/// original arguments), and the most significant bits are extracted to produce +/// a result that is the same size as the original arguments. +/// +/// Examples: +/// (1) fshl(i8 255, i8 0, i8 15) = 128 (0b10000000) +/// (2) fshl(i8 15, i8 15, i8 11) = 120 (0b01111000) +/// (3) fshl(i8 0, i8 255, i8 8) = 0 (0b00000000) +/// (4) fshl(i8 255, i8 0, i8 15) = fshl(i8 255, i8 0, i8 7) // 15 % 8 +LLVM_ABI APInt fshl(const APInt &Hi, const APInt &Lo, const APInt &Shift); + +/// Perform a funnel shift right. +/// +/// Concatenate Hi and Lo (Hi is the most significant bits of the wide value), +/// the combined value is shifted right by Shift (modulo the bit width of the +/// original arguments), and the least significant bits are extracted to produce +/// a result that is the same size as the original arguments. +/// +/// Examples: +/// (1) fshr(i8 255, i8 0, i8 15) = 254 (0b11111110) +/// (2) fshr(i8 15, i8 15, i8 11) = 225 (0b11100001) +/// (3) fshr(i8 0, i8 255, i8 8) = 255 (0b11111111) +/// (4) fshr(i8 255, i8 0, i8 9) = fshr(i8 255, i8 0, i8 1) // 9 % 8 +LLVM_ABI APInt fshr(const APInt &Hi, const APInt &Lo, const APInt &Shift); + } // namespace APIntOps // See friend declaration above. This additional declaration is required in diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp index 0c0e1d042e75..f6fd5f9ddd63 100644 --- a/llvm/lib/Support/APInt.cpp +++ b/llvm/lib/Support/APInt.cpp @@ -3169,3 +3169,21 @@ APInt APIntOps::pow(const APInt &X, int64_t N) { } return Acc; } + +APInt llvm::APIntOps::fshl(const APInt &Hi, const APInt &Lo, + const APInt &Shift) { + assert(Hi.getBitWidth() == Lo.getBitWidth()); + unsigned ShiftAmt = rotateModulo(Hi.getBitWidth(), Shift); + if (ShiftAmt == 0) + return Hi; + return Hi.shl(ShiftAmt) | Lo.lshr(Hi.getBitWidth() - ShiftAmt); +} + +APInt llvm::APIntOps::fshr(const APInt &Hi, const APInt &Lo, + const APInt &Shift) { + assert(Hi.getBitWidth() == Lo.getBitWidth()); + unsigned ShiftAmt = rotateModulo(Hi.getBitWidth(), Shift); + if (ShiftAmt == 0) + return Lo; + return Hi.shl(Hi.getBitWidth() - ShiftAmt) | Lo.lshr(ShiftAmt); +} diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index acc6a09acbf4..5b010729ae3d 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -3744,4 +3744,80 @@ TEST(APIntTest, TryExt) { ASSERT_EQ(42, APInt(128, -1).trySExtValue().value_or(42)); } +TEST(APIntTest, Fshl) { + EXPECT_EQ( + APIntOps::fshl(APInt(8, 0), APInt(8, 255), APInt(8, 8)).getZExtValue(), + 0); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 255), APInt(8, 0), APInt(8, 8)).getZExtValue(), + 255); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 255), APInt(8, 0), APInt(8, 15)).getZExtValue(), + 128); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 15), APInt(8, 15), APInt(8, 11)).getZExtValue(), + 120); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 3)).getZExtValue(), 16); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 1)).getZExtValue(), + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 9)).getZExtValue()); + EXPECT_EQ( + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 7)).getZExtValue(), + APIntOps::fshl(APInt(8, 2), APInt(8, 1), APInt(8, 15)).getZExtValue()); + EXPECT_EQ(APIntOps::fshl(APInt(32, 0, /*isSigned*/ true), + APInt(32, 2147483647, /*isSigned*/ true), + APInt(32, 32, /*isSigned*/ true)) + .getSExtValue(), + 0); + EXPECT_EQ(APIntOps::fshl(APInt(64, 1, /*isSigned*/ true), + APInt(64, 2, /*isSigned*/ true), + APInt(64, 3, /*isSigned*/ true)) + .getSExtValue(), + 8); + EXPECT_EQ(APIntOps::fshl(APInt(16, -2, /*isSigned*/ true), + APInt(16, -1, /*isSigned*/ true), + APInt(16, 3, /*isSigned*/ true)) + .getSExtValue(), + -9); +} + +TEST(APIntTest, Fshr) { + EXPECT_EQ( + APIntOps::fshr(APInt(8, 0), APInt(8, 255), APInt(8, 8)).getZExtValue(), + 255); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 255), APInt(8, 0), APInt(8, 8)).getZExtValue(), + 0); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 255), APInt(8, 0), APInt(8, 15)).getZExtValue(), + 254); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 15), APInt(8, 15), APInt(8, 11)).getZExtValue(), + 225); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 3)).getZExtValue(), 32); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 1)).getZExtValue(), + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 9)).getZExtValue()); + EXPECT_EQ( + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 7)).getZExtValue(), + APIntOps::fshr(APInt(8, 1), APInt(8, 2), APInt(8, 15)).getZExtValue()); + EXPECT_EQ(APIntOps::fshr(APInt(64, 0, /*isSigned*/ true), + APInt(64, 9223372036854775807, /*isSigned*/ true), + APInt(64, 64, /*isSigned*/ true)) + .getSExtValue(), + 9223372036854775807); + EXPECT_EQ(APIntOps::fshr(APInt(64, 1, /*isSigned*/ true), + APInt(64, 2, /*isSigned*/ true), + APInt(64, 3, /*isSigned*/ true)) + .getSExtValue(), + 2305843009213693952); + EXPECT_EQ(APIntOps::fshr(APInt(16, -2, /*isSigned*/ true), + APInt(16, -1, /*isSigned*/ true), + APInt(16, 3, /*isSigned*/ true)) + .getSExtValue(), + -8193); +} + } // end anonymous namespace