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; ---------------- tbaederr wrote: yep https://github.com/llvm/llvm-project/pull/213017 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
