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); ---------------- tbaederr wrote: I can't, this is just the interface of the builtin. https://github.com/llvm/llvm-project/pull/213017 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
