[clang] [clang][bytecode] Avoid heap-allocating init maps for small arrays (PR #171093)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 8 00:36:13 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Timm Baeder (tbaederr)
<details>
<summary>Changes</summary>
If the array is small enough, use the initmap pointer directly for storage instead of heap-allocating it.
---
Full diff: https://github.com/llvm/llvm-project/pull/171093.diff
2 Files Affected:
- (modified) clang/lib/AST/ByteCode/Descriptor.h (+27)
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+16-10)
``````````diff
diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h
index 2807f92335dc2..05be3d403cbae 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -278,6 +278,11 @@ struct Descriptor final {
};
/// Bitfield tracking the initialisation status of elements of primitive arrays.
+///
+/// InitMaps are saved for primitive arrays, so we don't have to have a
+/// per-element InlineDescriptor. When the number of elements in the array
+/// is small enough (see InitMap::isInline()), we use the pointer value
+/// directly instead of allocating an InitMap.
struct alignas(alignof(uint64_t)) InitMap final {
private:
/// Type packing bits.
@@ -289,6 +294,28 @@ struct alignas(alignof(uint64_t)) InitMap final {
/// Initializes the map with no fields set.
explicit InitMap(unsigned N);
+ static bool isInline(size_t N) {
+ return N < (sizeof(uintptr_t) * CHAR_BIT - 1);
+ }
+
+ static bool isInlineElementInitialized(uintptr_t BucketValue, unsigned I) {
+ return BucketValue & (uintptr_t(1) << I);
+ }
+
+ static bool initializeInlineElement(InitMap *&IMPtr, size_t NumElems,
+ unsigned I) {
+ assert(isInline(NumElems));
+ assert(I < NumElems);
+ uintptr_t BucketValue = reinterpret_cast<uintptr_t>(IMPtr);
+ uintptr_t Mask = uintptr_t(1) << I;
+ if (!(BucketValue & Mask)) {
+ BucketValue |= Mask;
+ IMPtr = reinterpret_cast<InitMap *>(BucketValue);
+ return static_cast<unsigned>(llvm::popcount(BucketValue)) == NumElems;
+ }
+ return false;
+ }
+
/// Checks if all elements have been initialized.
static bool allInitialized(const InitMap *IM) {
return reinterpret_cast<uintptr_t>(IM) ==
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 8fa17aed29d9e..d2c579a94129b 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -454,17 +454,10 @@ bool Pointer::isInitialized() const {
const Descriptor *Desc = getFieldDesc();
assert(Desc);
- if (Desc->isPrimitiveArray()) {
- InitMap *&IM = getInitMap();
- if (!IM)
- return false;
+ if (Desc->isPrimitiveArray())
+ return this->isElementInitialized(getIndex());
- if (InitMap::allInitialized(IM))
- return true;
- return IM->isElementInitialized(getIndex());
- }
-
- if (asBlockPointer().Base == 0)
+ if (BS.Base == 0)
return true;
// Field has its bit in an inline descriptor.
return getInlineDesc()->IsInitialized;
@@ -477,6 +470,13 @@ bool Pointer::isElementInitialized(unsigned Index) const {
if (Desc->isPrimitiveArray()) {
assert(getFieldDesc()->isPrimitiveArray());
InitMap *&IM = getInitMap();
+ unsigned NumElems = Desc->getNumElems();
+
+ if (InitMap::isInline(NumElems)) {
+ return InitMap::allInitialized(IM) ||
+ InitMap::isInlineElementInitialized(
+ reinterpret_cast<uintptr_t>(IM), Index);
+ }
if (!IM)
return false;
@@ -528,6 +528,12 @@ void Pointer::initializeElement(InterpState &S, unsigned Index) const {
if (InitMap::allInitialized(IM))
return;
+ unsigned NumElems = getFieldDesc()->getNumElems();
+ if (InitMap::isInline(NumElems)) {
+ if (InitMap::initializeInlineElement(IM, NumElems, Index))
+ InitMap::markAllInitialized(IM);
+ return;
+ }
// nullptr means no initmap yet.
if (!IM) {
unsigned NumElems = getFieldDesc()->getNumElems();
``````````
</details>
https://github.com/llvm/llvm-project/pull/171093
More information about the cfe-commits
mailing list