[llvm] 2520a9b - [APInt] Add `APIntOps::compressBits` and `APIntOps::expandBits` (#200114)
via llvm-commits
llvm-commits at lists.llvm.org
Fri May 29 09:56:08 PDT 2026
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
More information about the llvm-commits
mailing list