[clang] [clang][bytecode] Add opaque pointers to support type-only pointers (PR #213017)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sat Aug 8 04:08:30 PDT 2026
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/213017 at github.com>
================
@@ -0,0 +1,508 @@
+//===------------- InterpBuiltinObjectSize.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
+//
+//===----------------------------------------------------------------------===//
+
+// Implementation of the frontend part of the __builtin_object_size and
+// __builtin_dynamic_object_size builtins.
+
+#include "InterpHelpers.h"
+#include "Pointer.h"
+#include "Record.h"
+#include "clang/AST/RecordLayout.h"
+
+using namespace clang;
+using namespace clang::interp;
+
+enum : uint8_t {
+ Regular = 1 << 0,
+ IgnoreBaseCasts = 1 << 1,
+ SurroundingArray = 1 << 2,
+};
+
+// Helper to check if a RecordDecl can be passed to
+// ASTContext::getRecordLayout().
+static bool validRecordDecl(const RecordDecl *D) {
+ D = D->getDefinition();
+ return D && !D->isInvalidDecl() && D->isCompleteDefinition();
+}
+
+// Same but for types.
+static bool validType(QualType T) {
+ if (const RecordDecl *RD = T->getAsRecordDecl())
+ return validRecordDecl(RD);
+ return true;
+}
+
+static QualType computeFieldType(const ASTContext &ASTCtx,
+ const OpaquePointer &OP,
+ unsigned TypeModifier = 0) {
+ QualType CurType = OP.getObjectType();
+
+ unsigned Drop = 0;
+ if (TypeModifier & IgnoreBaseCasts && OP.PathLength != 0 &&
+ OP.path().back().Kind == PointerPathEntry::Base)
+ Drop = 1;
+
+ if (TypeModifier & SurroundingArray && OP.PathLength != 0 &&
+ OP.path().back().Kind == PointerPathEntry::Array)
+ Drop = 1;
+
+ for (const PointerPathEntry &Entry : OP.path().drop_back(Drop)) {
+ switch (Entry.Kind) {
+ case PointerPathEntry::Base:
+ CurType = ASTCtx.getCanonicalTagType(Entry.RD.getPointer());
+ break;
+ case PointerPathEntry::Field:
+ CurType = Entry.FD->getType();
+ break;
+ case PointerPathEntry::Array:
+ if (!CurType->isArrayType())
+ continue;
+ CurType = CurType->getAsArrayTypeUnsafe()->getElementType();
+ }
+ }
+
+ return CurType;
+}
+
+static std::optional<unsigned> computeFullDescSize(const ASTContext &ASTCtx,
+ const Descriptor *Desc) {
+ if (Desc->isPrimitive() || Desc->isArray()) {
+ QualType T = Desc->getType();
+ if (!validType(T))
+ return std::nullopt;
+ return ASTCtx.getTypeSizeInChars(T).getQuantity();
+ }
+
+ if (Desc->isRecord()) {
+ // Can't use Descriptor::getType() as that may return a pointer type. Look
+ // at the decl directly.
+
+ const RecordDecl *RD = Desc->ElemRecord->getDecl();
+ if (!validRecordDecl(RD))
+ return std::nullopt;
+
+ return ASTCtx.getTypeSizeInChars(ASTCtx.getCanonicalTagType(RD))
+ .getQuantity();
+ }
+
+ return std::nullopt;
+}
+
+/// Compute the byte offset of \p Ptr in the full declaration.
+static unsigned computePointerOffset(const ASTContext &ASTCtx,
+ const Pointer &Ptr) {
+ if (auto p = Ptr.computeLayoutOffset(ASTCtx))
+ return *p;
+ return 0;
+}
+
+/// Does Ptr point to the last subobject?
+static bool pointsToLastObject(const Pointer &Ptr) {
+ Pointer P = Ptr;
+ while (!P.isRoot()) {
+
+ if (P.isArrayElement()) {
+ P = P.expand().getArray();
+ continue;
+ }
+ if (P.isBaseClass()) {
+ if (P.getRecord()->getNumFields() > 0)
+ return false;
+ P = P.getBase();
+ continue;
+ }
+
+ Pointer Base = P.getBase();
+ if (const Record *R = Base.getRecord()) {
+ assert(P.getField());
+ if (P.getField()->getFieldIndex() != R->getNumFields() - 1)
+ return false;
+ }
+ P = Base;
+ }
+
+ return true;
+}
+
+/// Does Ptr point to the last object AND to a flexible array member?
+static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const Pointer &Ptr,
+ bool InvalidBase) {
+ auto isFlexibleArrayMember = [&](const Descriptor *FieldDesc) {
+ using FAMKind = LangOptions::StrictFlexArraysLevelKind;
+ FAMKind StrictFlexArraysLevel =
+ Ctx.getLangOpts().getStrictFlexArraysLevel();
+
+ if (StrictFlexArraysLevel == FAMKind::Default)
+ return true;
+
+ unsigned NumElems = FieldDesc->getNumElems();
+ if (NumElems == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly)
+ return true;
+
+ if (NumElems == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete)
+ return true;
+ return false;
+ };
+
+ const Descriptor *FieldDesc = Ptr.getFieldDesc();
+ if (!FieldDesc->isArray())
+ return false;
+
+ return InvalidBase && pointsToLastObject(Ptr) &&
+ isFlexibleArrayMember(FieldDesc);
+}
+
+static bool isUserWritingOffTheEnd(const ASTContext &ASTCtx,
+ const OpaquePointer &OP) {
+ if (OP.PathLength == 0)
+ return false;
+
+ QualType CurType = OP.getObjectType();
+ for (unsigned I = 0; I != OP.PathLength; ++I) {
+ const PointerPathEntry &Entry = OP.Path[I];
+ switch (Entry.Kind) {
+ case PointerPathEntry::Base:
+ return false;
+ case PointerPathEntry::Field: {
+ const FieldDecl *FD = OP.Path[I].FD;
+ if (!FD->getParent()->isUnion() &&
+ FD->getFieldIndex() != FD->getParent()->getNumFields() - 1)
+ return false;
+ CurType = FD->getType();
+ } break;
+ case PointerPathEntry::Array: {
+ if (I == OP.PathLength - 1)
+ break;
+
+ if (!CurType->isArrayType())
+ break;
+
+ unsigned Index = OP.Path[I].Index;
+ const ArrayType *AT = CurType->getAsArrayTypeUnsafe();
+ assert(AT);
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ if (Index != CAT->getLimitedSize() - 1)
+ return false;
+ CurType = CAT->getElementType();
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+
+ // We're pointing to the last field in the full object.
+ // CurType is now the most derived type.
+ if (!CurType->isArrayType())
+ return false;
+
+ if (isa<IncompleteArrayType>(CurType))
+ return true;
+
+ const auto *CAT = dyn_cast<ConstantArrayType>(CurType);
+ if (!CAT)
+ return false;
+
+ using FAMKind = LangOptions::StrictFlexArraysLevelKind;
+ FAMKind StrictFlexArraysLevel =
+ ASTCtx.getLangOpts().getStrictFlexArraysLevel();
+
+ if (StrictFlexArraysLevel == FAMKind::Default)
+ return true;
+
+ unsigned Size = CAT->getZExtSize();
+ if (Size == 0 && StrictFlexArraysLevel != FAMKind::IncompleteOnly)
+ return true;
+
+ if (Size == 1 && StrictFlexArraysLevel == FAMKind::OneZeroOrIncomplete)
+ return true;
+ return false;
+}
+
+/// Determine the offset of the given pointer. Depending on \c
+/// UseClosestSurroundingVariable, the offset is either relative to the full
+/// object or to the closest surrounding field or array.
+static std::optional<uint64_t>
+computeOpaquePtrOffset(const ASTContext &ASTCtx, const Pointer &Ptr,
+ bool UseClosestSurroundingVariable,
+ bool &OffsetIsNegative) {
+ const OpaquePointer &OP = Ptr.asOpaquePointer();
+
+ uint64_t Offset = 0;
+ std::optional<uint64_t> SurroundingArrayOffset;
+ QualType CurType = OP.getObjectType();
+ for (const PointerPathEntry &Entry : OP.path()) {
+ switch (Entry.Kind) {
+ case PointerPathEntry::Base: {
+ const RecordDecl *RD = CurType->getAsRecordDecl();
+ if (!validRecordDecl(RD))
+ return std::nullopt;
+
+ const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD);
+ Offset += Layout.getBaseClassOffset(Entry.RD.getPointer()).getQuantity();
+
+ CurType = ASTCtx.getCanonicalTagType(Entry.RD.getPointer());
+ } break;
----------------
tbaederr wrote:
yep
https://github.com/llvm/llvm-project/pull/213017
More information about the cfe-commits
mailing list