Author: Jan Schultke Date: 2026-05-29T16:56:01Z New Revision: 2520a9bcd0911b598e99a34420d6359c3dc61277
URL: https://github.com/llvm/llvm-project/commit/2520a9bcd0911b598e99a34420d6359c3dc61277 DIFF: https://github.com/llvm/llvm-project/commit/2520a9bcd0911b598e99a34420d6359c3dc61277.diff LOG: [APInt] Add `APIntOps::compressBits` and `APIntOps::expandBits` (#200114) These are necessary to implement portable intrinsics for bit_compress/pext/bext and bit_expand/pdep/bdep. See - https://github.com/llvm/llvm-project/issues/172857 - https://github.com/llvm/llvm-project/pull/168527 (I basically modeled my PR after this one) Added: Modified: clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ExprConstant.cpp llvm/include/llvm/ADT/APInt.h llvm/lib/Support/APInt.cpp llvm/unittests/ADT/APIntTest.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 15d9b8554b425..ee3813a9287be 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -5097,33 +5097,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case clang::X86::BI__builtin_ia32_pdep_si: case clang::X86::BI__builtin_ia32_pdep_di: - return interp__builtin_elementwise_int_binop( - S, OpPC, Call, [](const APSInt &Val, const APSInt &Mask) { - unsigned BitWidth = Val.getBitWidth(); - APInt Result = APInt::getZero(BitWidth); - - for (unsigned I = 0, P = 0; I != BitWidth; ++I) { - if (Mask[I]) - Result.setBitVal(I, Val[P++]); - } - - return Result; - }); + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::expandBits); case clang::X86::BI__builtin_ia32_pext_si: case clang::X86::BI__builtin_ia32_pext_di: - return interp__builtin_elementwise_int_binop( - S, OpPC, Call, [](const APSInt &Val, const APSInt &Mask) { - unsigned BitWidth = Val.getBitWidth(); - APInt Result = APInt::getZero(BitWidth); - - for (unsigned I = 0, P = 0; I != BitWidth; ++I) { - if (Mask[I]) - Result.setBitVal(P++, Val[I]); - } - - return Result; - }); + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::compressBits); case clang::X86::BI__builtin_ia32_addcarryx_u32: case clang::X86::BI__builtin_ia32_addcarryx_u64: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 293e2f61a3b03..808c9b4f89ed9 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17880,13 +17880,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, if (!EvaluateInteger(E->getArg(0), Val, Info) || !EvaluateInteger(E->getArg(1), Msk, Info)) return false; - - unsigned BitWidth = Val.getBitWidth(); - APInt Result = APInt::getZero(BitWidth); - for (unsigned I = 0, P = 0; I != BitWidth; ++I) - if (Msk[I]) - Result.setBitVal(I, Val[P++]); - return Success(Result, E); + return Success(llvm::APIntOps::expandBits(Val, Msk), E); } case clang::X86::BI__builtin_ia32_pext_si: @@ -17895,13 +17889,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, if (!EvaluateInteger(E->getArg(0), Val, Info) || !EvaluateInteger(E->getArg(1), Msk, Info)) return false; - - unsigned BitWidth = Val.getBitWidth(); - APInt Result = APInt::getZero(BitWidth); - for (unsigned I = 0, P = 0; I != BitWidth; ++I) - if (Msk[I]) - Result.setBitVal(P++, Val[I]); - return Success(Result, E); + return Success(llvm::APIntOps::compressBits(Val, Msk), E); } case X86::BI__builtin_ia32_ptestz128: case X86::BI__builtin_ia32_ptestz256: diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h index 859cbd5c07147..e8d806cf78578 100644 --- a/llvm/include/llvm/ADT/APInt.h +++ b/llvm/include/llvm/ADT/APInt.h @@ -2489,6 +2489,26 @@ LLVM_ABI APInt clmulr(const APInt &LHS, const APInt &RHS); /// clmulh(a, b) = clmulr(a, b) >> 1 LLVM_ABI APInt clmulh(const APInt &LHS, const APInt &RHS); +/// Perform a "compress" operation, also known as pext or bext. +/// +/// Selects the bits from /p Val at the positions where /p Mask has a 1-bit, +/// and packs them contiguously into the least significant bits of the result. +/// +/// Examples: +/// (1) compressBits(i8 0b1010'1010, i8 0b1100'1100) = 0b0000'1010 +/// (2) compressBits(i8 0b1111'1111, i8 0b1010'1010) = 0b0000'1111 +LLVM_ABI APInt compressBits(const APInt &Val, const APInt &Mask); + +/// Perform an "expand" operation, also known as pdep or bdep. +/// +/// Places the least significant bits of /p Val at the positions where /p Mask +/// has a 1-bit, and zeros the remaining bits. +/// +/// Examples: +/// (1) expandBits(i8 0b0000'1010, i8 0b1100'1100) = 0b1000'1000 +/// (2) expandBits(i8 0b0000'1111, i8 0b1010'1010) = 0b1010'1010 +LLVM_ABI APInt expandBits(const APInt &Val, const APInt &Mask); + } // 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 df3616abd7dcf..86b2eff7ada61 100644 --- a/llvm/lib/Support/APInt.cpp +++ b/llvm/lib/Support/APInt.cpp @@ -3259,3 +3259,23 @@ APInt llvm::APIntOps::clmulh(const APInt &LHS, const APInt &RHS) { assert(LHS.getBitWidth() == RHS.getBitWidth()); return clmulr(LHS, RHS).lshr(1); } + +APInt llvm::APIntOps::compressBits(const APInt &Val, const APInt &Mask) { + unsigned BW = Val.getBitWidth(); + assert(BW == Mask.getBitWidth() && "Operand mismatch"); + APInt Result = APInt::getZero(BW); + for (unsigned I = 0, P = 0; I != BW; ++I) + if (Mask[I]) + Result.setBitVal(P++, Val[I]); + return Result; +} + +APInt llvm::APIntOps::expandBits(const APInt &Val, const APInt &Mask) { + unsigned BW = Val.getBitWidth(); + assert(BW == Mask.getBitWidth() && "Operand mismatch"); + APInt Result = APInt::getZero(BW); + for (unsigned I = 0, P = 0; I != BW; ++I) + if (Mask[I]) + Result.setBitVal(I, Val[P++]); + return Result; +} diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp index 3c0446867b14b..a6cb34376d1cb 100644 --- a/llvm/unittests/ADT/APIntTest.cpp +++ b/llvm/unittests/ADT/APIntTest.cpp @@ -4017,4 +4017,56 @@ TEST(APIntTest, sqrt) { EXPECT_EQ(APInt::getMaxValue(256).sqrt(), APInt(256, "340282366920938463463374607431768211456", 10)); } + +TEST(APIntTest, compressBits) { + EXPECT_EQ(APIntOps::compressBits(APInt(8, 0), APInt(8, 0xAAU)).getZExtValue(), + 0U); + EXPECT_EQ( + APIntOps::compressBits(APInt(8, 0x55U), APInt(8, 0xAAU)).getZExtValue(), + 0U); + EXPECT_EQ( + APIntOps::compressBits(APInt(8, 0xAAU), APInt(8, 0xAAU)).getZExtValue(), + 15U); + EXPECT_EQ( + APIntOps::compressBits(APInt(8, 0xFFU), APInt(8, 0xAAU)).getZExtValue(), + 15U); + EXPECT_EQ(APIntOps::compressBits(APInt(8, 0xFFU), APInt(8, 0)).getZExtValue(), + 0U); + EXPECT_EQ( + APIntOps::compressBits(APInt(4, 0xFU), APInt(4, 0xAU)).getZExtValue(), + 3U); + EXPECT_EQ( + APIntOps::compressBits(APInt(4, 0xAU), APInt(4, 0xAU)).getZExtValue(), + 3U); + EXPECT_EQ( + APIntOps::compressBits(APInt(4, 0x5U), APInt(4, 0xAU)).getZExtValue(), + 0U); +} + +TEST(APIntTest, expandBits) { + EXPECT_EQ(APIntOps::expandBits(APInt(8, 0), APInt(8, 0xAAU)).getZExtValue(), + 0U); + EXPECT_EQ(APIntOps::expandBits(APInt(8, 15U), APInt(8, 0xAAU)).getZExtValue(), + 0xAAU); + EXPECT_EQ(APIntOps::expandBits(APInt(8, 0xFFU), APInt(8, 0)).getZExtValue(), + 0U); + EXPECT_EQ(APIntOps::expandBits(APInt(4, 3U), APInt(4, 0xAU)).getZExtValue(), + 0xAU); + EXPECT_EQ(APIntOps::expandBits(APInt(4, 1U), APInt(4, 0xAU)).getZExtValue(), + 2U); + APInt X(8, 0b10110100U); + APInt M(8, 0b11001110U); + EXPECT_EQ(APIntOps::expandBits(APIntOps::compressBits(X, M), M), X & M); +} + +TEST(APIntTest, compressExpandBitsExhaustive) { + for (unsigned V = 0; V < 256; ++V) { + for (unsigned Mask = 0; Mask < 256; ++Mask) { + APInt Val(8, V), APMask(8, Mask); + APInt Compressed = APIntOps::compressBits(Val, APMask); + APInt RoundTrip = APIntOps::expandBits(Compressed, APMask); + EXPECT_EQ(RoundTrip, Val & APMask); + } + } +} } // end anonymous namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
