[clang] [clang][bytecode] Refactor `InitMapPtr` (PR #172665)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 17 08:15:07 PST 2025
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/172665
>From 03745b5e9123f2bf89f1cc4cf174e0082d89e7df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 17 Dec 2025 14:05:23 +0100
Subject: [PATCH] [clang][bytecode]
---
clang/lib/AST/ByteCode/Descriptor.cpp | 24 +------
clang/lib/AST/ByteCode/Descriptor.h | 36 +---------
clang/lib/AST/ByteCode/InitMap.cpp | 31 +++++++++
clang/lib/AST/ByteCode/InitMap.h | 97 +++++++++++++++++++++++++++
clang/lib/AST/ByteCode/Pointer.cpp | 42 +++++-------
clang/lib/AST/CMakeLists.txt | 1 +
clang/test/AST/ByteCode/const-eval.c | 2 +-
7 files changed, 151 insertions(+), 82 deletions(-)
create mode 100644 clang/lib/AST/ByteCode/InitMap.cpp
create mode 100644 clang/lib/AST/ByteCode/InitMap.h
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index 0a819599287ee..487e5ae4f6ad0 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -52,7 +52,7 @@ static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) {
template <typename T>
static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool,
const Descriptor *D) {
- new (Ptr) InitMapPtr(std::nullopt);
+ new (Ptr) InitMapPtr();
if constexpr (needsCtor<T>()) {
Ptr += sizeof(InitMapPtr);
@@ -66,8 +66,8 @@ template <typename T>
static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
InitMapPtr &IMP = *reinterpret_cast<InitMapPtr *>(Ptr);
- if (IMP)
- IMP = std::nullopt;
+ if (IMP.hasInitMap())
+ IMP.deleteInitMap();
if constexpr (needsCtor<T>()) {
Ptr += sizeof(InitMapPtr);
@@ -467,21 +467,3 @@ bool Descriptor::hasTrivialDtor() const {
}
bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); }
-
-InitMap::InitMap(unsigned N)
- : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {}
-
-bool InitMap::initializeElement(unsigned I) {
- unsigned Bucket = I / PER_FIELD;
- T Mask = T(1) << (I % PER_FIELD);
- if (!(data()[Bucket] & Mask)) {
- data()[Bucket] |= Mask;
- UninitFields -= 1;
- }
- return UninitFields == 0;
-}
-
-bool InitMap::isElementInitialized(unsigned I) const {
- unsigned Bucket = I / PER_FIELD;
- return data()[Bucket] & (T(1) << (I % PER_FIELD));
-}
diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h
index 90dc2b4aa3111..057f40f8f1f69 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
#define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H
+#include "InitMap.h"
#include "PrimType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
@@ -22,12 +23,10 @@ namespace interp {
class Block;
class Record;
class SourceInfo;
-struct InitMap;
struct Descriptor;
enum PrimType : uint8_t;
using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
-using InitMapPtr = std::optional<std::pair<bool, std::shared_ptr<InitMap>>>;
/// Invoked whenever a block is created. The constructor method fills in the
/// inline descriptors of all fields and array elements. It also initializes
@@ -277,39 +276,6 @@ struct Descriptor final {
void dumpFull(unsigned Offset = 0, unsigned Indent = 0) const;
};
-/// Bitfield tracking the initialisation status of elements of primitive arrays.
-struct InitMap final {
-private:
- /// Type packing bits.
- using T = uint64_t;
- /// Bits stored in a single field.
- static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
-
-public:
- /// Initializes the map with no fields set.
- explicit InitMap(unsigned N);
-
-private:
- friend class Pointer;
-
- /// Returns a pointer to storage.
- T *data() { return Data.get(); }
- const T *data() const { return Data.get(); }
-
- /// Initializes an element. Returns true when object if fully initialized.
- bool initializeElement(unsigned I);
-
- /// Checks if an element was initialized.
- bool isElementInitialized(unsigned I) const;
-
- static constexpr size_t numFields(unsigned N) {
- return (N + PER_FIELD - 1) / PER_FIELD;
- }
- /// Number of fields not initialized.
- unsigned UninitFields;
- std::unique_ptr<T[]> Data;
-};
-
} // namespace interp
} // namespace clang
diff --git a/clang/lib/AST/ByteCode/InitMap.cpp b/clang/lib/AST/ByteCode/InitMap.cpp
new file mode 100644
index 0000000000000..ae841dd6df189
--- /dev/null
+++ b/clang/lib/AST/ByteCode/InitMap.cpp
@@ -0,0 +1,31 @@
+//===----------------------- InitMap.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "InitMap.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+InitMap::InitMap(unsigned N)
+ : UninitFields(N), Data(std::make_unique<T[]>(numFields(N))) {}
+
+bool InitMap::initializeElement(unsigned I) {
+ unsigned Bucket = I / PER_FIELD;
+ T Mask = T(1) << (I % PER_FIELD);
+ if (!(data()[Bucket] & Mask)) {
+ data()[Bucket] |= Mask;
+ UninitFields -= 1;
+ }
+ return UninitFields == 0;
+}
+
+bool InitMap::isElementInitialized(unsigned I) const {
+ if (UninitFields == 0)
+ return true;
+ unsigned Bucket = I / PER_FIELD;
+ return data()[Bucket] & (T(1) << (I % PER_FIELD));
+}
diff --git a/clang/lib/AST/ByteCode/InitMap.h b/clang/lib/AST/ByteCode/InitMap.h
new file mode 100644
index 0000000000000..7a5f744748403
--- /dev/null
+++ b/clang/lib/AST/ByteCode/InitMap.h
@@ -0,0 +1,97 @@
+//===----------------------- InitMap.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_INIT_MAP_H
+#define LLVM_CLANG_AST_INTERP_INIT_MAP_H
+
+#include <cassert>
+#include <climits>
+#include <memory>
+
+namespace clang {
+namespace interp {
+
+/// Bitfield tracking the initialisation status of elements of primitive arrays.
+struct InitMap final {
+private:
+ /// Type packing bits.
+ using T = uint64_t;
+ /// Bits stored in a single field.
+ static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT;
+ /// Number of fields not initialized.
+ unsigned UninitFields;
+ std::unique_ptr<T[]> Data;
+
+public:
+ /// Initializes the map with no fields set.
+ explicit InitMap(unsigned N);
+
+private:
+ friend class Pointer;
+
+ /// Returns a pointer to storage.
+ T *data() { return Data.get(); }
+ const T *data() const { return Data.get(); }
+
+ /// Initializes an element. Returns true when object if fully initialized.
+ bool initializeElement(unsigned I);
+
+ /// Checks if an element was initialized.
+ bool isElementInitialized(unsigned I) const;
+
+ static constexpr size_t numFields(unsigned N) {
+ return ((N + PER_FIELD - 1) / PER_FIELD) * 2;
+ }
+};
+
+/// A pointer-sized struct we use to allocate into data storage.
+/// An InitMapPtr is either backed by an actual InitMap, or it
+/// hold information about the absence of the InitMap.
+struct InitMapPtr {
+ /// V's value before an initmap has been created.
+ static constexpr intptr_t NoInitMapValue = 0;
+ /// V's value after the initmap has been destroyed because
+ /// all its elements have already been initialized.
+ static constexpr intptr_t AllInitializedValue = 1;
+ uintptr_t V = 0;
+
+ explicit InitMapPtr() = default;
+ bool hasInitMap() const {
+ return V != NoInitMapValue && V != AllInitializedValue;
+ }
+ bool allInitialized() const { return V == AllInitializedValue; }
+
+ void setInitMap(InitMap *IM) {
+ assert(IM != nullptr);
+ V = reinterpret_cast<uintptr_t>(IM);
+ assert(hasInitMap());
+ }
+
+ void noteAllInitialized() {
+ if (hasInitMap())
+ delete (operator->)();
+ V = AllInitializedValue;
+ }
+
+ InitMap *operator->() {
+ assert(hasInitMap());
+ return reinterpret_cast<InitMap *>(V);
+ }
+
+ void deleteInitMap() {
+ if (hasInitMap())
+ delete (operator->)();
+ V = NoInitMapValue;
+ };
+};
+static_assert(sizeof(InitMapPtr) == sizeof(void *));
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 90f41bed39440..5fb6ccc92c7bf 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -11,6 +11,7 @@
#include "Context.h"
#include "Floating.h"
#include "Function.h"
+#include "InitMap.h"
#include "Integral.h"
#include "InterpBlock.h"
#include "MemberPointer.h"
@@ -477,14 +478,14 @@ bool Pointer::isElementInitialized(unsigned Index) const {
}
if (Desc->isPrimitiveArray()) {
- InitMapPtr &IM = getInitMap();
- if (!IM)
- return false;
+ InitMapPtr IM = getInitMap();
- if (IM->first)
+ if (IM.allInitialized())
return true;
- return IM->second->isElementInitialized(Index);
+ if (!IM.hasInitMap())
+ return false;
+ return IM->isElementInitialized(Index);
}
return isInitialized();
}
@@ -523,34 +524,25 @@ void Pointer::initializeElement(unsigned Index) const {
assert(Index < getFieldDesc()->getNumElems());
InitMapPtr &IM = getInitMap();
- if (!IM) {
- const Descriptor *Desc = getFieldDesc();
- IM = std::make_pair(false, std::make_shared<InitMap>(Desc->getNumElems()));
- }
-
- assert(IM);
- // All initialized.
- if (IM->first)
+ if (IM.allInitialized())
return;
- if (IM->second->initializeElement(Index)) {
- IM->first = true;
- IM->second.reset();
+ if (!IM.hasInitMap()) {
+ const Descriptor *Desc = getFieldDesc();
+ IM.setInitMap(new InitMap(Desc->getNumElems()));
}
+ assert(IM.hasInitMap());
+
+ if (IM->initializeElement(Index))
+ IM.noteAllInitialized();
}
void Pointer::initializeAllElements() const {
assert(getFieldDesc()->isPrimitiveArray());
assert(isArrayRoot());
- InitMapPtr &IM = getInitMap();
- if (!IM) {
- IM = std::make_pair(true, nullptr);
- } else {
- IM->first = true;
- IM->second.reset();
- }
+ getInitMap().noteAllInitialized();
}
bool Pointer::allElementsInitialized() const {
@@ -566,8 +558,8 @@ bool Pointer::allElementsInitialized() const {
return GD.InitState == GlobalInitState::Initialized;
}
- InitMapPtr &IM = getInitMap();
- return IM && IM->first;
+ InitMapPtr IM = getInitMap();
+ return IM.allInitialized();
}
void Pointer::activate() const {
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index fd50e956bb865..f9a5f4f0e7ecd 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -82,6 +82,7 @@ add_clang_library(clangAST
ByteCode/Floating.cpp
ByteCode/EvaluationResult.cpp
ByteCode/DynamicAllocator.cpp
+ ByteCode/InitMap.cpp
ByteCode/Interp.cpp
ByteCode/InterpBlock.cpp
ByteCode/InterpFrame.cpp
diff --git a/clang/test/AST/ByteCode/const-eval.c b/clang/test/AST/ByteCode/const-eval.c
index d6cf600b378a8..70b2a4dbd86e4 100644
--- a/clang/test/AST/ByteCode/const-eval.c
+++ b/clang/test/AST/ByteCode/const-eval.c
@@ -127,7 +127,7 @@ EVAL_EXPR(43, varfloat && constfloat) // both-error {{not an integer constant ex
EVAL_EXPR(45, ((char*)-1) + 1 == 0 ? 1 : -1)
EVAL_EXPR(46, ((char*)-1) + 1 < (char*) -1 ? 1 : -1)
EVAL_EXPR(47, &x < &x + 1 ? 1 : -1)
-EVAL_EXPR(48, &x != &x - 1 ? 1 : -1)
+EVAL_EXPR(48, &x != &x - 1 ? 1 : -1) // expected-error {{declared as an array with a negative size}}
EVAL_EXPR(49, &x < &x - 100 ? 1 : -1) // ref-error {{not an integer constant expression}}
extern struct Test50S Test50;
More information about the cfe-commits
mailing list