https://github.com/tbaederr created 
https://github.com/llvm/llvm-project/pull/225741

We sometimes create intermediate pointers, which always insert themselves into 
the pointer list of a block. Which is slow. 

This adds a `CheckLoad()` overload taking a `PtrView`, which we can use in 
those cases.


>From b8a0bb591348c124dec54489bc3c6295d9f924de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Wed, 23 Sep 2026 10:12:54 +0200
Subject: [PATCH] LoadPop PtrView

---
 clang/lib/AST/ByteCode/Interp.cpp      | 117 +++++++++++++++++++------
 clang/lib/AST/ByteCode/Interp.h        |  23 +++--
 clang/lib/AST/ByteCode/InterpHelpers.h |   2 +
 clang/lib/AST/ByteCode/Pointer.h       |   3 +-
 4 files changed, 103 insertions(+), 42 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 7afed0e05c886..b0c4c9b66c308 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -370,16 +370,16 @@ bool CheckBCPResult(InterpState &S, const Pointer &Ptr) {
   return false;
 }
 
-bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
-                 AccessKinds AK, bool WillActivate) {
+static bool CheckActive(InterpState &S, CodePtr OpPC, PtrView Ptr,
+                        AccessKinds AK, bool WillActivate = false) {
   if (Ptr.isActive())
     return true;
 
   assert(Ptr.inUnion());
 
   // Find the outermost union.
-  PtrView U = Ptr.view().getBase();
-  PtrView C = Ptr.view();
+  PtrView U = Ptr.getBase();
+  PtrView C = Ptr;
   while (!U.isRoot() && !U.isActive()) {
     // A little arbitrary, but this is what the current interpreter does.
     // See the AnonymousUnion test in test/AST/ByteCode/unions.cpp.
@@ -413,7 +413,7 @@ bool CheckActive(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   // non-trivial default constructor.
   if (WillActivate) {
     bool Fails = false;
-    PtrView It = Ptr.view();
+    PtrView It = Ptr;
     while (!It.isRoot() && !It.isActive()) {
       if (const Record *R = It.getRecord(); R && R->isUnion()) {
         if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(R->getDecl());
@@ -452,6 +452,13 @@ bool CheckActive(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   return false;
 }
 
+static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                        AccessKinds AK, bool WillActivate = false) {
+  if (!Ptr.isBlockPointer())
+    return true;
+  return CheckActive(S, OpPC, Ptr.view(), AK, WillActivate);
+}
+
 static bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   if (!Ptr.isExtern())
     return true;
@@ -585,6 +592,18 @@ static bool CheckConstant(InterpState &S, CodePtr OpPC, 
const Pointer &Ptr,
   return CheckConstant(S, OpPC, Ptr.getDeclDesc(), AK);
 }
 
+static bool CheckConstant(InterpState &S, CodePtr OpPC, PtrView Ptr,
+                          AccessKinds AK = AK_Read) {
+  if (S.checkingConstantDestruction(Ptr.getDeclDesc()->asVarDecl()))
+    return CheckConstant(S, OpPC, Ptr.getDeclDesc(), AK);
+
+  if (!Ptr.block()->isStatic())
+    return true;
+  if (!Ptr.block()->getDeclID())
+    return true;
+  return CheckConstant(S, OpPC, Ptr.getDeclDesc(), AK);
+}
+
 bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                CheckSubobjectKind CSK) {
   if (!Ptr.isZero())
@@ -834,6 +853,14 @@ bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, 
bool Extern,
   return false;
 }
 
+static bool diagnoseUninitialized(InterpState &S, CodePtr OpPC, PtrView Ptr,
+                                  AccessKinds AK) {
+  assert(Ptr.isLive());
+  assert(!Ptr.isInitialized());
+  return diagnoseUninitialized(S, OpPC, Ptr.isExtern(), Ptr.block(),
+                               Ptr.getLifetime(), AK);
+}
+
 static bool CheckLifetime(InterpState &S, CodePtr OpPC, Lifetime LT,
                           const Block *B, AccessKinds AK) {
   if (LT == Lifetime::Started)
@@ -928,25 +955,32 @@ bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const 
Block *B) {
   return true;
 }
 
-bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
-               AccessKinds AK) {
+bool CheckLoad(InterpState &S, CodePtr OpPC, PtrView Ptr, AccessKinds AK) {
   if (Ptr.isZero()) {
-    const auto &Src = S.Current->getSource(OpPC);
+    SourceInfo Loc = S.Current->getSource(OpPC);
 
     if (Ptr.isField())
-      S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
+      S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK_Field;
     else
-      S.FFDiag(Src, diag::note_constexpr_access_null) << AK;
+      S.FFDiag(Loc, diag::note_constexpr_access_null) << AK;
     return false;
   }
-  // Block and string pointers are the only ones we can actually read from.
-  if (!Ptr.isReadablePointerType())
-    return CheckDummy(S, OpPC, Ptr, AK);
 
-  if (Ptr.isBlockPointer() && !Ptr.block()->isAccessible()) {
-    if (!CheckLive(S, OpPC, Ptr, AK))
+  if (!Ptr.block()->isAccessible()) {
+    if (!Ptr.isLive()) {
+      if (Ptr.block()->isDynamic()) {
+        S.FFDiag(S.Current->getSource(OpPC),
+                 diag::note_constexpr_access_deleted_object)
+            << AK;
+      } else if (!S.checkingPotentialConstantExpression()) {
+        S.FFDiag(S.Current->getSource(OpPC), 
diag::note_constexpr_access_uninit)
+            << AK << /*uninitialized=*/false << S.Current->getRange(OpPC);
+        noteValueLocation(S, Ptr.block());
+      }
+
       return false;
-    if (!CheckExtern(S, OpPC, Ptr))
+    }
+    if (!CheckExtern(S, OpPC, Ptr.block()))
       return false;
     return CheckWeak(S, OpPC, Ptr.block());
   }
@@ -960,21 +994,19 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   if (!Ptr.isInitialized())
     return diagnoseUninitialized(S, OpPC, Ptr, AK);
 
-  if (Ptr.isBlockPointer()) {
-    if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), Ptr.block(), AK))
-      return false;
-    if (!CheckTemporary(S, OpPC, Ptr.block(), AK))
-      return false;
+  if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), Ptr.block(), AK))
+    return false;
+  if (!CheckTemporary(S, OpPC, Ptr.block(), AK))
+    return false;
 
-    if (!CheckMutable(S, OpPC, Ptr.view(), AK))
-      return false;
-    if (!CheckVolatile(S, OpPC, Ptr.view(), AK))
-      return false;
-  }
-  if (isConstexprUnknown(Ptr))
+  if (!CheckMutable(S, OpPC, Ptr, AK))
+    return false;
+  if (!CheckVolatile(S, OpPC, Ptr, AK))
+    return false;
+  if (isConstexprUnknown(Ptr.block()))
     return false;
 
-  if (Ptr.isBlockPointer() && !Ptr.isArrayRoot()) {
+  if (!Ptr.isArrayRoot()) {
     // According to GCC info page:
     //
     // 6.28 Compound Literals
@@ -1004,6 +1036,35 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   return true;
 }
 
+bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+               AccessKinds AK) {
+  if (Ptr.isBlockPointer())
+    return CheckLoad(S, OpPC, Ptr.view(), AK);
+
+  if (Ptr.isZero()) {
+    SourceInfo Loc = S.Current->getSource(OpPC);
+    if (Ptr.isField())
+      S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK_Field;
+    else
+      S.FFDiag(Loc, diag::note_constexpr_access_null) << AK;
+    return false;
+  }
+
+  // Block and string pointers are the only ones we can actually read from.
+  if (!Ptr.isReadablePointerType())
+    return CheckDummy(S, OpPC, Ptr, AK);
+
+  assert(Ptr.isStringPointer());
+
+  if (!CheckConstant(S, OpPC, Ptr, AK))
+    return false;
+  if (!CheckRange(S, OpPC, Ptr, AK))
+    return false;
+  if (!Ptr.isInitialized())
+    return diagnoseUninitialized(S, OpPC, Ptr, AK);
+  return true;
+}
+
 /// This is not used by any of the opcodes directly. It's used by
 /// EvalEmitter to do the final lvalue-to-rvalue conversion.
 bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 2eab69431c94c..aec352bc08b32 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -107,9 +107,6 @@ bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr 
OpPC);
 bool CheckDeleteSource(InterpState &S, CodePtr OpPC, const Expr *Source,
                        const Pointer &Ptr);
 
-bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
-                 AccessKinds AK, bool WillActivate = false);
-
 /// Sets the given integral value to the pointer, which is of
 /// a std::{weak,partial,strong}_ordering type.
 bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
@@ -1687,10 +1684,10 @@ bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) 
{
   if (!Obj.getFieldDesc()->isRecord() && !Obj.isUnknownSizeArray())
     return false;
 
-  const Pointer &Field = Obj.atField(I);
-  if (!CheckLoad(S, OpPC, Field))
+  PtrView FieldPtr = Obj.view().atField(I);
+  if (!CheckLoad(S, OpPC, FieldPtr))
     return false;
-  S.Stk.push<T>(Field.deref<T>());
+  S.Stk.push<T>(FieldPtr.deref<T>());
   return true;
 }
 
@@ -1713,10 +1710,10 @@ bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t 
I) {
   if (!Obj.getFieldDesc()->isRecord() && !Obj.isUnknownSizeArray())
     return false;
 
-  const Pointer &Field = Obj.atField(I);
-  if (!CheckLoad(S, OpPC, Field))
+  PtrView FieldPtr = Obj.view().atField(I);
+  if (!CheckLoad(S, OpPC, FieldPtr))
     return false;
-  S.Stk.push<T>(Field.deref<T>());
+  S.Stk.push<T>(FieldPtr.deref<T>());
   return true;
 }
 
@@ -1731,10 +1728,10 @@ bool GetThisField(InterpState &S, CodePtr OpPC, 
uint32_t I) {
   if (!This.isBlockPointer())
     return false;
 
-  const Pointer &Field = This.atField(I);
-  if (!CheckLoad(S, OpPC, Field))
+  PtrView FieldPtr = This.view().atField(I);
+  if (!CheckLoad(S, OpPC, FieldPtr))
     return false;
-  S.Stk.push<T>(Field.deref<T>());
+  S.Stk.push<T>(FieldPtr.deref<T>());
   return true;
 }
 
@@ -3598,7 +3595,7 @@ inline bool CopyArray(InterpState &S, CodePtr OpPC, 
uint32_t SrcIndex,
     return false;
 
   for (uint32_t I = 0; I != Size; ++I) {
-    const Pointer &SP = SrcPtr.atIndex(SrcIndex + I);
+    PtrView SP = SrcPtr.view().atIndex(SrcIndex + I);
 
     if (!CheckLoad(S, OpPC, SP))
       return false;
diff --git a/clang/lib/AST/ByteCode/InterpHelpers.h 
b/clang/lib/AST/ByteCode/InterpHelpers.h
index 77d06598c8e2e..38a69b72e0c12 100644
--- a/clang/lib/AST/ByteCode/InterpHelpers.h
+++ b/clang/lib/AST/ByteCode/InterpHelpers.h
@@ -76,6 +76,8 @@ inline bool CheckMutable(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
 /// Checks if a value can be loaded from a block.
 bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                AccessKinds AK = AK_Read);
+bool CheckLoad(InterpState &S, CodePtr OpPC, PtrView Ptr,
+               AccessKinds AK = AK_Read);
 
 /// Diagnose mismatched new[]/delete or new/delete[] pairs.
 bool CheckNewDeleteForms(InterpState &S, CodePtr OpPC,
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index be59580577d14..35a0645ff34c6 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -49,6 +49,7 @@ struct PtrView {
   bool isMutable() const {
     return !isRoot() && getInlineDesc()->IsFieldMutable;
   }
+  bool isExtern() const { return Pointee && Pointee->isExtern(); }
   bool isVolatile() const {
     return isRoot() ? getDeclDesc()->IsVolatile : getInlineDesc()->IsVolatile;
   }
@@ -874,7 +875,7 @@ class Pointer {
   /// Checks if the storage is extern.
   bool isExtern() const {
     if (isBlockPointer())
-      return BS.Pointee && BS.Pointee->isExtern();
+      return view().isExtern();
     return false;
   }
   /// Checks if the storage is static.

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to