Timm =?utf-8?q?Bäder?= <[email protected]>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/[email protected]>


================
@@ -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;
+
+    case PointerPathEntry::Field: {
+      const FieldDecl *FD = Entry.FD;
+      const RecordDecl *RD = FD->getParent();
+      if (!validRecordDecl(RD))
+        return std::nullopt;
+
+      const ASTRecordLayout &Layout = ASTCtx.getASTRecordLayout(RD);
+      Offset +=
+          
ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FD->getFieldIndex()))
+              .getQuantity();
+
+      CurType = FD->getType();
+    } break;
+    case PointerPathEntry::Array: {
+      int64_t Index = Entry.Index;
+      if (Index < 0)
+        OffsetIsNegative = true;
+      SurroundingArrayOffset = Offset;
+      if (!CurType->isArrayType()) {
+        Offset += Index * ASTCtx.getTypeSizeInChars(CurType).getQuantity();
+        continue;
+      }
+      const ArrayType *AT = CurType->getAsArrayTypeUnsafe();
+      assert(AT);
+      QualType ElemTy = AT->getElementType();
+      if (!validType(ElemTy))
+        return std::nullopt;
+      Offset += Index * ASTCtx.getTypeSizeInChars(ElemTy).getQuantity();
+      CurType = AT->getElementType();
+    }
+    }
+  }
+
+  if (UseClosestSurroundingVariable && SurroundingArrayOffset)
+    return Offset - *SurroundingArrayOffset;
+
+  QualType Ty = CurType.getNonReferenceType();
+
+  if (UseClosestSurroundingVariable &&
+      (Ty->isIncompleteType() || Ty->isFunctionType()))
+    return std::nullopt;
+
+  if (OP.PathLength == 1 && OP.path().back().Kind == PointerPathEntry::Field &&
+      isa<IncompleteArrayType>(CurType)) {
+    return Offset;
+  }
+
+  if (UseClosestSurroundingVariable)
+    return 0;
+
+  return Offset;
+}
+
+/// Check if the given pointer points to the complete object, i.e. either to 
the
+/// very beginning or after the end (into the flexible array member) of the
+/// object.
+static bool pointsToCompleteObject(const ASTContext &ASTCtx,
+                                   const Pointer &Ptr) {
+  const OpaquePointer &OP = Ptr.asOpaquePointer();
+  if (OP.PathLength == 0)
+    return true;
+
+  QualType FieldType = computeFieldType(ASTCtx, OP);
+  return isa<IncompleteArrayType>(FieldType);
+}
+
+static std::optional<unsigned>
+computeOpaqueSize(const ASTContext &ASTCtx, const Pointer &Ptr,
+                  bool UseClosestSurroundingVariable) {
+  const OpaquePointer &OP = Ptr.asOpaquePointer();
+
+  CharUnits TypeSize;
+  // NOTE: Clang does not consider base casts. GCC does.
+  if (UseClosestSurroundingVariable) {
+    QualType FieldTy =
+        computeFieldType(ASTCtx, OP, SurroundingArray | IgnoreBaseCasts);
+    if (!validType(FieldTy))
+      return std::nullopt;
+    TypeSize = ASTCtx.getTypeSizeInChars(FieldTy);
+  } else {
+    QualType ObjectTy = OP.getObjectType();
+    if (!validType(ObjectTy))
+      return std::nullopt;
+    TypeSize = ASTCtx.getTypeSizeInChars(ObjectTy);
+  }
+
+  // Check if we need to add the flexible array member size.
+  const VarDecl *Base = dyn_cast<VarDecl>(OP.Base);
+  if (!Base || !Base->getType()->isRecordType())
+    return TypeSize.getQuantity();
+
+  if (!Base->hasInit())
+    return TypeSize.getQuantity();
+
+  CharUnits FlexibleArraySize = Base->getFlexibleArrayInitChars(ASTCtx);
+  return (TypeSize + FlexibleArraySize).getQuantity();
+}
+
+namespace clang {
+namespace interp {
+
+/// Evaluate __builtin_object_size or __builtin_dynamic_object_size for the
+/// given pointer and Kind.
+///
+/// When computing the final result, the most important variable is
+/// UseClosestSurroundingVariable. If it is true, we will use the field the
+/// pointer points to, or the parent array of the element.
+/// UseClosestSurroundingVariable is true for Kind 1 and 3.
+UnsignedOrNone evaluateBuiltinObjectSize(const ASTContext &ASTCtx,
+                                         unsigned Kind, Pointer &Ptr,
+                                         const Expr *E, bool IsDynamic) {
+  if (Ptr.isZero())
+    return std::nullopt;
+
+  bool InvalidBase = false;
+  if (Ptr.isOpaquePointer()) {
+    bool UseClosestSurroundingVariable = (Kind == 1) || (Kind == 3);
----------------
ojhunt wrote:

yup - just ignore any other comments I have related to this misery API :D

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

Reply via email to