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


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

This adds a new pointer type to represent something we don't have data for, but 
can still refer to.
They are similar to LValue APValues: it's a base (currently always a `VarDecl`) 
and a "lvalue designator" path.

For the first argument of `__builtin_object_size` and 
`__builtin_dynamic_object_size`, when we see an unknown variable, we emit an 
opaque pointer for the variable instead of a dummy pointer. Also if any load in 
that argument fails, we don't abort the evaluation but instead push an opaque 
pointer of the appropriate type on the stack.



This fixes the review complaint from 
https://github.com/llvm/llvm-project/pull/196548, namely that we didn't have a 
way to just point to an opaque type if a load fails.



Cases that still don't work:

 1) The stuff in `test/Sema/builtin-object-size-cxx14.cpp` that use 
non-constexpr functions in the first argument of `__builtin_object_size` 
(everything in the `InvalidBase` namespace). This behavior matches GCC.
 2) From `test/CodeGen/object-size.c`, the sample
     ```c
     void test26(void) {
        struct { int v[10]; } t[10];

        // CHECK: store i32 312
        gi = OBJECT_SIZE_BUILTIN(&amp;t[1].v[12], 1);
    }
   results in 0, not 312. I don't know why it would result in 312. For modes 1 
and 3, we should consider the closest surrounding   variable, wich only has 10 
elements, so at index 12, we have 0 bytes we can read from. GCC also returns 0.

 3) From `test/CodeGen/object-size.c`, functions `test7` through `test17`, 
because they modify variables in the first argument of `__builtin_object_size`.
 4) This line from `test/CodeGen/pass-object-size.c`:
      ```c
       gi = NoViableOverloadObjectSize1(&amp;t[1]);
      ```




This might seem overkill for now, but I plan on using opaque pointers as 
replacement for the current "dummy pointer" mechanism as well.

---

Patch is 66.58 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/213017.diff


22 Files Affected:

- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+12-3) 
- (modified) clang/lib/AST/ByteCode/Context.cpp (+7-9) 
- (modified) clang/lib/AST/ByteCode/Context.h (+1-1) 
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+127) 
- (modified) clang/lib/AST/ByteCode/Interp.h (+79-36) 
- (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+20-212) 
- (added) clang/lib/AST/ByteCode/InterpBuiltinObjectSize.cpp (+527) 
- (modified) clang/lib/AST/ByteCode/InterpHelpers.h (+2-1) 
- (modified) clang/lib/AST/ByteCode/InterpState.h (+4) 
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+6) 
- (modified) clang/lib/AST/ByteCode/Pointer.cpp (+53) 
- (modified) clang/lib/AST/ByteCode/Pointer.h (+94-1) 
- (modified) clang/lib/AST/ByteCode/Program.cpp (+4-2) 
- (modified) clang/lib/AST/ByteCode/Record.h (+1) 
- (modified) clang/lib/AST/CMakeLists.txt (+1) 
- (modified) clang/lib/AST/ExprConstant.cpp (+4-1) 
- (added) clang/test/AST/ByteCode/builtin-object-size-codegen-cxx23.cpp (+15) 
- (modified) clang/test/AST/ByteCode/builtin-object-size-codegen.c (+64) 
- (modified) clang/test/AST/ByteCode/builtin-object-size-codegen.cpp (+125) 
- (modified) clang/test/AST/ByteCode/enable_if.c (+2-2) 
- (modified) clang/test/CodeGen/object-size.c (+3-89) 
- (modified) clang/test/CodeGen/pass-object-size.c (+2-3) 


``````````diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index 479b5251b9f57..1cb6768d6c67c 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -453,8 +453,12 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *E) {
 
   switch (E->getCastKind()) {
   case CK_LValueToRValue: {
-    if (ToLValue && E->getType()->isPointerType())
-      return this->delegate(SubExpr);
+    if (ToLValue && E->getType()->isPointerType()) {
+      assert(!DiscardResult);
+      if (!this->visit(SubExpr))
+        return false;
+      return this->emitLoadPopL(E);
+    }
 
     if (SubExpr->getType().isVolatileQualified())
       return this->emitInvalidCast(CastKind::Volatile, /*Fatal=*/true, E);
@@ -8706,8 +8710,13 @@ bool Compiler<Emitter>::emitDestructionPop(const 
Descriptor *Desc,
 template <class Emitter>
 bool Compiler<Emitter>::emitDummyPtr(const DeclTy &D, const Expr *E, bool CU) {
   assert(!DiscardResult && "Should've been checked before");
-  unsigned DummyID = P.getOrCreateDummy(D, CU);
 
+  if (ToLValue) {
+    if (auto *VD = dyn_cast_if_present<ValueDecl>(D.dyn_cast<const Decl *>()))
+      return this->emitGetOpaquePtr(VD, E);
+  }
+
+  unsigned DummyID = P.getOrCreateDummy(D, CU);
   if (!this->emitGetPtrGlobal(DummyID, E))
     return false;
   if (E->getType()->isVoidType())
diff --git a/clang/lib/AST/ByteCode/Context.cpp 
b/clang/lib/AST/ByteCode/Context.cpp
index b913d2a9f539c..6e3b93e2107b5 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -366,26 +366,24 @@ std::optional<uint64_t> Context::evaluateStrlen(State 
&Parent, const Expr *E) {
   return Result;
 }
 
-std::optional<uint64_t>
-Context::tryEvaluateObjectSize(State &Parent, const Expr *E, unsigned Kind) {
+std::optional<uint64_t> Context::tryEvaluateObjectSize(State &Parent,
+                                                       const Expr *E,
+                                                       unsigned Kind,
+                                                       bool IsDynamic) {
   assert(Stk.empty());
   Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
 
   std::optional<uint64_t> Result;
-
   auto PtrRes = C.interpretAsLValuePointer(E, [&](InterpState &S, CodePtr OpPC,
                                                   const Pointer &Ptr) {
-    const Descriptor *DeclDesc = Ptr.getDeclDesc();
-    if (!DeclDesc)
-      return false;
-
-    QualType T = DeclDesc->getType().getNonReferenceType();
+    QualType T = Ptr.getType().getNonReferenceType();
     if (T->isIncompleteType() || T->isFunctionType() ||
         !T->isConstantSizeType())
       return false;
 
     Pointer P = Ptr;
-    if (auto ObjectSize = evaluateBuiltinObjectSize(getASTContext(), Kind, P)) 
{
+    if (auto ObjectSize =
+            evaluateBuiltinObjectSize(getASTContext(), Kind, P, E, IsDynamic)) 
{
       Result = *ObjectSize;
       return true;
     }
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 47821a3e3a7f3..77566d35ad6e0 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -95,7 +95,7 @@ class Context final {
   /// bytes belonging to the same storage (stack, heap allocation,
   /// global variable) are considered.
   std::optional<uint64_t> tryEvaluateObjectSize(State &Parent, const Expr *E,
-                                                unsigned Kind);
+                                                unsigned Kind, bool IsDynamic);
 
   std::optional<bool> evaluateWithSubstitution(State &Parent,
                                                const FunctionDecl *Callee,
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index 23bdc5ea28c03..795f63690bfcf 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1612,6 +1612,29 @@ static bool getField(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
     return false;
   }
 
+  if (Ptr.isOpaquePointer()) {
+    const OpaquePointer &OP = Ptr.asOpaquePointer();
+    const Record *R =
+        S.getContext().getRecord(OP.getFieldType()->getAsRecordDecl());
+    if (!R)
+      return false;
+
+    const Record::Field *F = R->findField(Off);
+    if (!F)
+      return false;
+
+    PointerPathEntry *NewPath = S.allocPointerPath(OP.PathLength + 1);
+    if (OP.Path)
+      std::memcpy(NewPath, Ptr.asOpaquePointer().Path,
+                  sizeof(PointerPathEntry) * OP.PathLength);
+
+    NewPath[OP.PathLength] = PointerPathEntry::field(F->Decl);
+
+    S.Stk.push<Pointer>(OpaqueTag{}, OP.Base, F->Decl->getType().getTypePtr(),
+                        NewPath, OP.PathLength + 1);
+    return true;
+  }
+
   if (!Ptr.isBlockPointer()) {
     // If we're trying to get the field of a TypeId pointer, try to produce a
     // proper diagnostic.
@@ -1646,6 +1669,31 @@ static bool getBase(InterpState &S, CodePtr OpPC, const 
Pointer &Ptr,
   if (!NullOK && !CheckNull(S, OpPC, Ptr, CSK_Base))
     return false;
 
+  if (Ptr.isOpaquePointer()) {
+    const OpaquePointer &OP = Ptr.asOpaquePointer();
+    const Record *R =
+        S.getContext().getRecord(OP.getFieldType()->getAsRecordDecl());
+    assert(R);
+
+    const Record::Base *B = R->findBase(Off);
+    if (!B)
+      return false;
+
+    unsigned NewPathLength = OP.PathLength + 1;
+    PointerPathEntry *NewPath = S.allocPointerPath(NewPathLength);
+    if (OP.Path)
+      std::memcpy(NewPath, OP.Path,
+                  sizeof(PointerPathEntry) * (NewPathLength - 1));
+
+    NewPath[NewPathLength - 1] =
+        PointerPathEntry::base(cast<CXXRecordDecl>(B->Decl));
+    S.Stk.push<Pointer>(
+        OpaqueTag{}, OP.Base,
+        S.getASTContext().getCanonicalTagType(B->Decl).getTypePtr(), NewPath,
+        NewPathLength);
+    return true;
+  }
+
   if (!Ptr.isBlockPointer()) {
     if (!Ptr.isIntegralPointer())
       return false;
@@ -1906,6 +1954,7 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function 
*Func,
   S.Current = FrameBefore;
   return false;
 }
+
 bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
           uint32_t VarArgSize) {
 
@@ -3219,6 +3268,84 @@ bool CastFloatingIntegralAPS(InterpState &S, CodePtr 
OpPC, uint32_t BitWidth,
   return floatAPCast<true>(S, OpPC, F, BitWidth, FPOI);
 }
 
+bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const OpaquePointer &OP,
+                        uint64_t Offset) {
+  QualType ArrTy;
+  if (OP.PathLength > 0) {
+    if (OP.path().back().Kind == PointerPathEntry::Array) {
+      ArrTy = OP.getSurroundingArray(S.getASTContext());
+    } else {
+      ArrTy = OP.getFieldType();
+    }
+  } else {
+    ArrTy = OP.getObjectType();
+  }
+
+  QualType ElemType;
+  bool PastEnd = true;
+  if (ArrTy->isArrayType()) {
+    const auto *AT = ArrTy->getAsArrayTypeUnsafe();
+    ElemType = AT->getElementType();
+    if (const auto *CAT = dyn_cast<ConstantArrayType>(AT))
+      PastEnd = Offset >= CAT->getZExtSize();
+  } else if (ArrTy->isRecordType()) {
+    ElemType = ArrTy;
+  } else {
+    ElemType = ArrTy;
+  }
+  if (ElemType.isNull())
+    return false;
+
+  unsigned NewPathLength = OP.PathLength + 1;
+  PointerPathEntry *NewPath = S.allocPointerPath(NewPathLength);
+  if (OP.Path)
+    std::memcpy(NewPath, OP.Path,
+                sizeof(PointerPathEntry) * (NewPathLength - 1));
+
+  NewPath[NewPathLength - 1] = PointerPathEntry::array(Offset);
+  S.Stk.push<Pointer>(OpaqueTag{}, OP.Base, ElemType.getTypePtr(), NewPath,
+                      NewPathLength, PastEnd);
+  return true;
+}
+
+bool addSubOffsetOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                        uint64_t Offset, bool Add) {
+  assert(Ptr.isOpaquePointer());
+  const OpaquePointer &OP = Ptr.asOpaquePointer();
+
+  QualType ArrTy = OP.getFieldType();
+  QualType ElemTy;
+  if (ArrTy->isArrayType())
+    ElemTy = ArrTy->getAsArrayTypeUnsafe()->getElementType();
+  else
+    ElemTy = ArrTy;
+
+  if (Offset != 0) {
+    if (isa<IncompleteArrayType>(ArrTy)) {
+      const SourceInfo &E = S.Current->getSource(OpPC);
+      S.FFDiag(E, diag::note_constexpr_unsized_array_indexed);
+      return false;
+    }
+
+    S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+        << Offset << /*non-array*/ true << 0;
+  }
+
+  unsigned ElemSize =
+      S.getASTContext().getTypeSizeInChars(ElemTy).getQuantity();
+  unsigned NewOffset;
+  if (Add)
+    NewOffset = Ptr.getByteOffset() + (ElemSize * Offset);
+  else
+    NewOffset = Ptr.getByteOffset() - (ElemSize * Offset);
+
+  bool PastEnd = NewOffset > 0;
+
+  S.Stk.push<Pointer>(OpaqueTag{}, OP.Base, OP.FieldType, OP.Path,
+                      OP.PathLength, PastEnd, NewOffset);
+  return true;
+}
+
 // FIXME: Would be nice to generate this instead of hardcoding it here.
 constexpr bool OpReturns(Opcode Op) {
   return Op == OP_RetVoid || Op == OP_RetValue || Op == OP_NoRet ||
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 405f4a29ec982..bc22c896db655 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2252,6 +2252,37 @@ bool LoadPop(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+/// Like LoadPop above, but if any of the checks fail, we
+/// turn the pointer into an opaque pointer of appropriate type.
+inline bool LoadPopL(InterpState &S, CodePtr OpPC) {
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
+  auto P = S.getEvalStatus().Diag;
+  S.getEvalStatus().Diag = nullptr;
+
+  bool Failed = false;
+  if (!CheckLoad(S, OpPC, Ptr))
+    Failed = true;
+  if (!Ptr.isBlockPointer())
+    Failed = true;
+  if (!Ptr.canDeref(PT_Ptr))
+    Failed = true;
+  S.getEvalStatus().Diag = P;
+
+  if (Failed) {
+    if (Ptr.isOpaquePointer()) {
+      S.Stk.push<Pointer>(OpaqueTag{}, Ptr.asOpaquePointer().Base,
+                          Ptr.getType()->getPointeeType().getTypePtr());
+    } else {
+      S.Stk.push<Pointer>(OpaqueTag{}, Ptr.getDeclDesc()->asValueDecl(),
+                          Ptr.getType()->getPointeeType().getTypePtr());
+    }
+
+  } else {
+    S.Stk.push<Pointer>(Ptr.deref<Pointer>());
+  }
+  return true;
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool Store(InterpState &S, CodePtr OpPC) {
   const T &Value = S.Stk.pop<T>();
@@ -2646,11 +2677,17 @@ std::optional<Pointer> OffsetHelper(InterpState &S, 
CodePtr OpPC,
   return Ptr.atIndex(static_cast<uint64_t>(Result));
 }
 
+bool addSubOffsetOpaque(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                        uint64_t Offset, bool Add);
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool AddOffset(InterpState &S, CodePtr OpPC) {
   const T &Offset = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.pop<Pointer>().expand();
 
+  if (Ptr.isOpaquePointer())
+    return addSubOffsetOpaque(S, OpPC, Ptr, static_cast<uint64_t>(Offset),
+                              /*Add=*/true);
+
   if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Add>(
           S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) {
     S.Stk.push<Pointer>(Result->narrow());
@@ -2664,6 +2701,10 @@ bool SubOffset(InterpState &S, CodePtr OpPC) {
   const T &Offset = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.pop<Pointer>().expand();
 
+  if (Ptr.isOpaquePointer())
+    return addSubOffsetOpaque(S, OpPC, Ptr, static_cast<uint64_t>(Offset),
+                              /*Add=*/false);
+
   if (std::optional<Pointer> Result = OffsetHelper<T, ArithOp::Sub>(
           S, OpPC, Offset, Ptr, /*IsPointerArith=*/true)) {
     S.Stk.push<Pointer>(Result->narrow());
@@ -2672,6 +2713,11 @@ bool SubOffset(InterpState &S, CodePtr OpPC) {
   return false;
 }
 
+inline bool GetOpaquePtr(InterpState &S, const ValueDecl *VD) {
+  S.Stk.push<Pointer>(OpaqueTag{}, VD);
+  return true;
+}
+
 template <ArithOp Op>
 static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC,
                                    const Pointer &Ptr) {
@@ -3431,18 +3477,16 @@ inline bool ExpandPtr(InterpState &S) {
   return true;
 }
 
-// 1) Pops an integral value from the stack
-// 2) Peeks a pointer
-// 3) Pushes a new pointer that's a narrowed array
-//   element of the peeked pointer with the value
-//   from 1) added as offset.
-//
-// This leaves the original pointer on the stack and pushes a new one
-// with the offset applied and narrowed.
-template <PrimType Name, class T = typename PrimConv<Name>::T>
-inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
-  const T &Offset = S.Stk.pop<T>();
-  const Pointer &Ptr = S.Stk.peek<Pointer>();
+bool arrayElemPtrOpaque(InterpState &S, CodePtr OpPC, const OpaquePointer &OP,
+                        uint64_t Offset);
+
+// Implementation for ArrayElemPtr and ArrayElemPtrPop ops.
+template <typename T>
+inline bool arrayElemPtr(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+                         const T &Offset) {
+  if (Ptr.isOpaquePointer())
+    return arrayElemPtrOpaque(S, OpPC, Ptr.asOpaquePointer(),
+                              static_cast<int64_t>(Offset));
 
   if (!Ptr.isZero() && !Offset.isZero()) {
     if (!CheckArray(S, OpPC, Ptr))
@@ -3466,38 +3510,31 @@ inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
     S.Stk.push<Pointer>(Result->narrow());
     return true;
   }
-
   return false;
 }
 
+// 1) Pops an integral value from the stack
+// 2) Peeks a pointer
+// 3) Pushes a new pointer that's a narrowed array
+//   element of the peeked pointer with the value
+//   from 1) added as offset.
+//
+// This leaves the original pointer on the stack and pushes a new one
+// with the offset applied and narrowed.
 template <PrimType Name, class T = typename PrimConv<Name>::T>
-inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
+inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
   const T &Offset = S.Stk.pop<T>();
-  const Pointer &Ptr = S.Stk.pop<Pointer>();
-
-  if (!Ptr.isZero() && !Offset.isZero()) {
-    if (!CheckArray(S, OpPC, Ptr))
-      return false;
-  }
+  const Pointer &Ptr = S.Stk.peek<Pointer>();
 
-  if (Offset.isZero()) {
-    if (const Descriptor *Desc = Ptr.getFieldDesc();
-        Desc && Desc->isArray() && Ptr.getIndex() == 0) {
-      S.Stk.push<Pointer>(Ptr.atIndex(0).narrow());
-      return true;
-    }
-    S.Stk.push<Pointer>(Ptr.narrow());
-    return true;
-  }
+  return arrayElemPtr<T>(S, OpPC, Ptr, Offset);
+}
 
-  assert(!Offset.isZero());
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
+  const T &Offset = S.Stk.pop<T>();
+  const Pointer &Ptr = S.Stk.pop<Pointer>();
 
-  if (std::optional<Pointer> Result =
-          OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr)) {
-    S.Stk.push<Pointer>(Result->narrow());
-    return true;
-  }
-  return false;
+  return arrayElemPtr<T>(S, OpPC, Ptr, Offset);
 }
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
@@ -3569,6 +3606,12 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
       return false;
   }
 
+  if (Ptr.isOpaquePointer()) {
+    // llvm::errs()<< "ArrayDecay of Opaque pointer\n";
+    S.Stk.push<Pointer>(Ptr);
+    return true;
+  }
+
   if (Ptr.isRoot() || !Ptr.isUnknownSizeArray()) {
     S.Stk.push<Pointer>(Ptr.atIndex(0).narrow());
     return true;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index a539fb26abc08..7ba47a99cbb6e 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2297,218 +2297,9 @@ static bool interp__builtin_memchr(InterpState &S, 
CodePtr OpPC,
   return true;
 }
 
-static std::optional<unsigned> computeFullDescSize(const ASTContext &ASTCtx,
-                                                   const Descriptor *Desc) {
-  if (Desc->isPrimitive() || Desc->isArray())
-    return ASTCtx.getTypeSizeInChars(Desc->getType()).getQuantity();
-
-  if (Desc->isRecord()) {
-    // Can't use Descriptor::getType() as that may return a pointer type. Look
-    // at the decl directly.
-    return ASTCtx
-        .getTypeSizeInChars(
-            ASTCtx.getCanonicalTagType(Desc->ElemRecord->getDecl()))
-        .getQuantity();
-  }
-
-  return std::nullopt;
-}
-
-/// Compute the byte offset of \p Ptr in the full declaration.
-static unsigned computePointerOffset(const ASTContext &ASTCtx,
-                                     const Pointer &Ptr) {
-  unsigned Result = 0;
-
-  Pointer P = Ptr;
-  while (P.isField() || P.isArrayElement()) {
-    P = P.expand();
-    const Descriptor *D = P.getFieldDesc();
-
-    if (P.isArrayElement()) {
-      unsigned ElemSize =
-          ASTCtx.getTypeSizeInChars(D->getElemQualType()).getQuantity();
-      if (P.isOnePastEnd())
-        Result += ElemSize * P.getNumElems();
-      else
-        Result += ElemSize * P.getIndex();
-      P = P.expand().getArray();
-    } else if (P.isBaseClass()) {
-      const auto *RD = cast<CXXRecordDecl>(D->asDecl());
-      bool IsVirtual = Ptr.isVirtualBaseClass();
-      P = P.getBase();
-      const Record *BaseRecord = P.getRecord();
-
-      const ASTRecordLayout &Layout =
-          
ASTCtx.getASTRecordLayout(cast<CXXRecordDecl>(BaseRecord->getDecl()));
-      if (IsVirtual)
-        Result += Layout.getVBaseClassOffset(RD).getQuantity();
-      else
-        Result += Layout.getBaseClassOffset(RD).getQuantity();
-    } else if (P.isField()) {
-      const FieldDecl *FD = P.getField();
-      const ASTRecordLayout &Layout =
-          ASTCtx.getASTRecordLayout(FD->getParent());
-      unsigned FieldIndex = FD->getFieldIndex();
-      uint64_t FieldOffset =
-          ASTCtx.toCharUnitsFromBits(Layout.getFieldOffset(FieldIndex))
-              .getQuantity();
-      Result += FieldOffset;
-      P = P.getBase();
-    } else
-      llvm_unreachable("Unhandled descriptor type");
-  }
-
-  return Result;
-}
-
-/// 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);
-}
-
-UnsignedOrNone evaluateBuiltinObjectSize(const ASTContext &ASTCtx,
-                                         unsigned Kind, Pointer &Ptr) {
-  if (Ptr.isZero() || !Ptr.isBlockPointer())
-    return std::nullopt;
-
-  if (Ptr.isDummy() && Ptr.getType()->isPointerType())
-    return std::nullopt;
-
-  bool InvalidBase = false;
-
-  if (Ptr.isDummy()) {
-    if (const VarDecl *VD = Ptr.getDeclDesc()->asVarDecl();
-        VD && VD->getType()->isPointerType())
-      InvalidBase = true;
-  }
-
-  // According to the GCC documentation, we want the size of the subobject
-  // denoted by the pointer. But that's not quite right -- what we actually
-  // want is the size of the immediately-enclosing array, if there is one.
-  if (Ptr.isArrayElement())
-    Ptr = Ptr.expand();
-
-  bool DetermineForCompleteObject = Ptr.getFieldDesc() == Ptr.getDeclDesc();
-  const Descriptor *DeclDesc = Ptr.getDeclDesc...
[truncated]

``````````

</details>


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