[clang] [clang][bytecode] Avoid heap-allocating init maps for small arrays (PR #171093)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 8 00:35:33 PST 2025
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/171093
If the array is small enough, use the initmap pointer directly for storage instead of heap-allocating it.
>From 8742d36e071ad2c7a78c761658814b87ab47e5b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Sep 2025 07:11:56 +0200
Subject: [PATCH] inline init maps
---
clang/lib/AST/ByteCode/Descriptor.h | 27 +++++++++++++++++++++++++++
clang/lib/AST/ByteCode/Pointer.cpp | 26 ++++++++++++++++----------
2 files changed, 43 insertions(+), 10 deletions(-)
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();
More information about the cfe-commits
mailing list