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
