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

Reply via email to