Path: blob/main/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h
35292 views
//===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Defines the classes responsible for pointer tracking.9//10//===----------------------------------------------------------------------===//1112#ifndef LLVM_CLANG_AST_INTERP_POINTER_H13#define LLVM_CLANG_AST_INTERP_POINTER_H1415#include "Descriptor.h"16#include "InterpBlock.h"17#include "clang/AST/ComparisonCategories.h"18#include "clang/AST/Decl.h"19#include "clang/AST/DeclCXX.h"20#include "clang/AST/Expr.h"21#include "llvm/Support/raw_ostream.h"2223namespace clang {24namespace interp {25class Block;26class DeadBlock;27class Pointer;28class Context;29template <unsigned A, bool B> class Integral;30enum PrimType : unsigned;3132class Pointer;33inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);3435struct BlockPointer {36/// The block the pointer is pointing to.37Block *Pointee;38/// Start of the current subfield.39unsigned Base;40};4142struct IntPointer {43const Descriptor *Desc;44uint64_t Value;45};4647enum class Storage { Block, Int };4849/// A pointer to a memory block, live or dead.50///51/// This object can be allocated into interpreter stack frames. If pointing to52/// a live block, it is a link in the chain of pointers pointing to the block.53///54/// In the simplest form, a Pointer has a Block* (the pointee) and both Base55/// and Offset are 0, which means it will point to raw data.56///57/// The Base field is used to access metadata about the data. For primitive58/// arrays, the Base is followed by an InitMap. In a variety of cases, the59/// Base is preceded by an InlineDescriptor, which is used to track the60/// initialization state, among other things.61///62/// The Offset field is used to access the actual data. In other words, the63/// data the pointer decribes can be found at64/// Pointee->rawData() + Pointer.Offset.65///66///67/// Pointee Offset68/// │ │69/// │ │70/// ▼ ▼71/// ┌───────┬────────────┬─────────┬────────────────────────────┐72/// │ Block │ InlineDesc │ InitMap │ Actual Data │73/// └───────┴────────────┴─────────┴────────────────────────────┘74/// ▲75/// │76/// │77/// Base78class Pointer {79private:80static constexpr unsigned PastEndMark = ~0u;81static constexpr unsigned RootPtrMark = ~0u;8283public:84Pointer() {85StorageKind = Storage::Int;86PointeeStorage.Int.Value = 0;87PointeeStorage.Int.Desc = nullptr;88}89Pointer(Block *B);90Pointer(Block *B, uint64_t BaseAndOffset);91Pointer(const Pointer &P);92Pointer(Pointer &&P);93Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0)94: Offset(Offset), StorageKind(Storage::Int) {95PointeeStorage.Int.Value = Address;96PointeeStorage.Int.Desc = Desc;97}98~Pointer();99100void operator=(const Pointer &P);101void operator=(Pointer &&P);102103/// Equality operators are just for tests.104bool operator==(const Pointer &P) const {105if (P.StorageKind != StorageKind)106return false;107if (isIntegralPointer())108return P.asIntPointer().Value == asIntPointer().Value &&109Offset == P.Offset;110111assert(isBlockPointer());112return P.asBlockPointer().Pointee == asBlockPointer().Pointee &&113P.asBlockPointer().Base == asBlockPointer().Base &&114Offset == P.Offset;115}116117bool operator!=(const Pointer &P) const { return !(P == *this); }118119/// Converts the pointer to an APValue.120APValue toAPValue(const ASTContext &ASTCtx) const;121122/// Converts the pointer to a string usable in diagnostics.123std::string toDiagnosticString(const ASTContext &Ctx) const;124125uint64_t getIntegerRepresentation() const {126if (isIntegralPointer())127return asIntPointer().Value + (Offset * elemSize());128return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset;129}130131/// Converts the pointer to an APValue that is an rvalue.132std::optional<APValue> toRValue(const Context &Ctx,133QualType ResultType) const;134135/// Offsets a pointer inside an array.136[[nodiscard]] Pointer atIndex(uint64_t Idx) const {137if (isIntegralPointer())138return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx);139140if (asBlockPointer().Base == RootPtrMark)141return Pointer(asBlockPointer().Pointee, RootPtrMark,142getDeclDesc()->getSize());143uint64_t Off = Idx * elemSize();144if (getFieldDesc()->ElemDesc)145Off += sizeof(InlineDescriptor);146else147Off += sizeof(InitMapPtr);148return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,149asBlockPointer().Base + Off);150}151152/// Creates a pointer to a field.153[[nodiscard]] Pointer atField(unsigned Off) const {154unsigned Field = Offset + Off;155if (isIntegralPointer())156return Pointer(asIntPointer().Value + Field, asIntPointer().Desc);157return Pointer(asBlockPointer().Pointee, Field, Field);158}159160/// Subtract the given offset from the current Base and Offset161/// of the pointer.162[[nodiscard]] Pointer atFieldSub(unsigned Off) const {163assert(Offset >= Off);164unsigned O = Offset - Off;165return Pointer(asBlockPointer().Pointee, O, O);166}167168/// Restricts the scope of an array element pointer.169[[nodiscard]] Pointer narrow() const {170if (!isBlockPointer())171return *this;172assert(isBlockPointer());173// Null pointers cannot be narrowed.174if (isZero() || isUnknownSizeArray())175return *this;176177// Pointer to an array of base types - enter block.178if (asBlockPointer().Base == RootPtrMark)179return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor),180Offset == 0 ? Offset : PastEndMark);181182// Pointer is one past end - magic offset marks that.183if (isOnePastEnd())184return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,185PastEndMark);186187// Primitive arrays are a bit special since they do not have inline188// descriptors. If Offset != Base, then the pointer already points to189// an element and there is nothing to do. Otherwise, the pointer is190// adjusted to the first element of the array.191if (inPrimitiveArray()) {192if (Offset != asBlockPointer().Base)193return *this;194return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,195Offset + sizeof(InitMapPtr));196}197198// Pointer is to a field or array element - enter it.199if (Offset != asBlockPointer().Base)200return Pointer(asBlockPointer().Pointee, Offset, Offset);201202// Enter the first element of an array.203if (!getFieldDesc()->isArray())204return *this;205206const unsigned NewBase = asBlockPointer().Base + sizeof(InlineDescriptor);207return Pointer(asBlockPointer().Pointee, NewBase, NewBase);208}209210/// Expands a pointer to the containing array, undoing narrowing.211[[nodiscard]] Pointer expand() const {212assert(isBlockPointer());213Block *Pointee = asBlockPointer().Pointee;214215if (isElementPastEnd()) {216// Revert to an outer one-past-end pointer.217unsigned Adjust;218if (inPrimitiveArray())219Adjust = sizeof(InitMapPtr);220else221Adjust = sizeof(InlineDescriptor);222return Pointer(Pointee, asBlockPointer().Base,223asBlockPointer().Base + getSize() + Adjust);224}225226// Do not step out of array elements.227if (asBlockPointer().Base != Offset)228return *this;229230// If at base, point to an array of base types.231if (isRoot())232return Pointer(Pointee, RootPtrMark, 0);233234// Step into the containing array, if inside one.235unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset;236const Descriptor *Desc =237(Next == Pointee->getDescriptor()->getMetadataSize())238? getDeclDesc()239: getDescriptor(Next)->Desc;240if (!Desc->IsArray)241return *this;242return Pointer(Pointee, Next, Offset);243}244245/// Checks if the pointer is null.246bool isZero() const {247if (isBlockPointer())248return asBlockPointer().Pointee == nullptr;249assert(isIntegralPointer());250return asIntPointer().Value == 0 && Offset == 0;251}252/// Checks if the pointer is live.253bool isLive() const {254if (isIntegralPointer())255return true;256return asBlockPointer().Pointee && !asBlockPointer().Pointee->IsDead;257}258/// Checks if the item is a field in an object.259bool isField() const {260if (isIntegralPointer())261return false;262263return !isRoot() && getFieldDesc()->asDecl();264}265266/// Accessor for information about the declaration site.267const Descriptor *getDeclDesc() const {268if (isIntegralPointer())269return asIntPointer().Desc;270271assert(isBlockPointer());272assert(asBlockPointer().Pointee);273return asBlockPointer().Pointee->Desc;274}275SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }276277/// Returns the expression or declaration the pointer has been created for.278DeclTy getSource() const {279if (isBlockPointer())280return getDeclDesc()->getSource();281282assert(isIntegralPointer());283return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy();284}285286/// Returns a pointer to the object of which this pointer is a field.287[[nodiscard]] Pointer getBase() const {288if (asBlockPointer().Base == RootPtrMark) {289assert(Offset == PastEndMark && "cannot get base of a block");290return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);291}292unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset;293return Pointer(asBlockPointer().Pointee, NewBase, NewBase);294}295/// Returns the parent array.296[[nodiscard]] Pointer getArray() const {297if (asBlockPointer().Base == RootPtrMark) {298assert(Offset != 0 && Offset != PastEndMark && "not an array element");299return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);300}301assert(Offset != asBlockPointer().Base && "not an array element");302return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,303asBlockPointer().Base);304}305306/// Accessors for information about the innermost field.307const Descriptor *getFieldDesc() const {308if (isIntegralPointer())309return asIntPointer().Desc;310311if (isRoot())312return getDeclDesc();313return getInlineDesc()->Desc;314}315316/// Returns the type of the innermost field.317QualType getType() const {318if (inPrimitiveArray() && Offset != asBlockPointer().Base) {319// Unfortunately, complex and vector types are not array types in clang,320// but they are for us.321if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())322return AT->getElementType();323if (const auto *CT = getFieldDesc()->getType()->getAs<ComplexType>())324return CT->getElementType();325if (const auto *CT = getFieldDesc()->getType()->getAs<VectorType>())326return CT->getElementType();327}328return getFieldDesc()->getType();329}330331[[nodiscard]] Pointer getDeclPtr() const {332return Pointer(asBlockPointer().Pointee);333}334335/// Returns the element size of the innermost field.336size_t elemSize() const {337if (isIntegralPointer()) {338if (!asIntPointer().Desc)339return 1;340return asIntPointer().Desc->getElemSize();341}342343if (asBlockPointer().Base == RootPtrMark)344return getDeclDesc()->getSize();345return getFieldDesc()->getElemSize();346}347/// Returns the total size of the innermost field.348size_t getSize() const {349assert(isBlockPointer());350return getFieldDesc()->getSize();351}352353/// Returns the offset into an array.354unsigned getOffset() const {355assert(Offset != PastEndMark && "invalid offset");356if (asBlockPointer().Base == RootPtrMark)357return Offset;358359unsigned Adjust = 0;360if (Offset != asBlockPointer().Base) {361if (getFieldDesc()->ElemDesc)362Adjust = sizeof(InlineDescriptor);363else364Adjust = sizeof(InitMapPtr);365}366return Offset - asBlockPointer().Base - Adjust;367}368369/// Whether this array refers to an array, but not370/// to the first element.371bool isArrayRoot() const {372return inArray() && Offset == asBlockPointer().Base;373}374375/// Checks if the innermost field is an array.376bool inArray() const {377if (isBlockPointer())378return getFieldDesc()->IsArray;379return false;380}381/// Checks if the structure is a primitive array.382bool inPrimitiveArray() const {383if (isBlockPointer())384return getFieldDesc()->isPrimitiveArray();385return false;386}387/// Checks if the structure is an array of unknown size.388bool isUnknownSizeArray() const {389if (!isBlockPointer())390return false;391return getFieldDesc()->isUnknownSizeArray();392}393/// Checks if the pointer points to an array.394bool isArrayElement() const {395if (isBlockPointer())396return inArray() && asBlockPointer().Base != Offset;397return false;398}399/// Pointer points directly to a block.400bool isRoot() const {401if (isZero() || isIntegralPointer())402return true;403return (asBlockPointer().Base ==404asBlockPointer().Pointee->getDescriptor()->getMetadataSize() ||405asBlockPointer().Base == 0);406}407/// If this pointer has an InlineDescriptor we can use to initialize.408bool canBeInitialized() const {409if (!isBlockPointer())410return false;411412return asBlockPointer().Pointee && asBlockPointer().Base > 0;413}414415[[nodiscard]] const BlockPointer &asBlockPointer() const {416assert(isBlockPointer());417return PointeeStorage.BS;418}419[[nodiscard]] const IntPointer &asIntPointer() const {420assert(isIntegralPointer());421return PointeeStorage.Int;422}423bool isBlockPointer() const { return StorageKind == Storage::Block; }424bool isIntegralPointer() const { return StorageKind == Storage::Int; }425426/// Returns the record descriptor of a class.427const Record *getRecord() const { return getFieldDesc()->ElemRecord; }428/// Returns the element record type, if this is a non-primive array.429const Record *getElemRecord() const {430const Descriptor *ElemDesc = getFieldDesc()->ElemDesc;431return ElemDesc ? ElemDesc->ElemRecord : nullptr;432}433/// Returns the field information.434const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }435436/// Checks if the object is a union.437bool isUnion() const;438439/// Checks if the storage is extern.440bool isExtern() const {441if (isBlockPointer())442return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern();443return false;444}445/// Checks if the storage is static.446bool isStatic() const {447if (isIntegralPointer())448return true;449assert(asBlockPointer().Pointee);450return asBlockPointer().Pointee->isStatic();451}452/// Checks if the storage is temporary.453bool isTemporary() const {454if (isBlockPointer()) {455assert(asBlockPointer().Pointee);456return asBlockPointer().Pointee->isTemporary();457}458return false;459}460/// Checks if the storage is a static temporary.461bool isStaticTemporary() const { return isStatic() && isTemporary(); }462463/// Checks if the field is mutable.464bool isMutable() const {465if (!isBlockPointer())466return false;467return !isRoot() && getInlineDesc()->IsFieldMutable;468}469470bool isWeak() const {471if (isIntegralPointer())472return false;473474assert(isBlockPointer());475if (const ValueDecl *VD = getDeclDesc()->asValueDecl())476return VD->isWeak();477return false;478}479/// Checks if an object was initialized.480bool isInitialized() const;481/// Checks if the object is active.482bool isActive() const {483if (!isBlockPointer())484return true;485return isRoot() || getInlineDesc()->IsActive;486}487/// Checks if a structure is a base class.488bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }489bool isVirtualBaseClass() const {490return isField() && getInlineDesc()->IsVirtualBase;491}492/// Checks if the pointer points to a dummy value.493bool isDummy() const {494if (!isBlockPointer())495return false;496497if (!asBlockPointer().Pointee)498return false;499500return getDeclDesc()->isDummy();501}502503/// Checks if an object or a subfield is mutable.504bool isConst() const {505if (isIntegralPointer())506return true;507return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;508}509510/// Returns the declaration ID.511std::optional<unsigned> getDeclID() const {512if (isBlockPointer()) {513assert(asBlockPointer().Pointee);514return asBlockPointer().Pointee->getDeclID();515}516return std::nullopt;517}518519/// Returns the byte offset from the start.520unsigned getByteOffset() const {521if (isIntegralPointer())522return asIntPointer().Value + Offset;523if (isOnePastEnd())524return PastEndMark;525return Offset;526}527528/// Returns the number of elements.529unsigned getNumElems() const {530if (isIntegralPointer())531return ~unsigned(0);532return getSize() / elemSize();533}534535const Block *block() const { return asBlockPointer().Pointee; }536537/// Returns the index into an array.538int64_t getIndex() const {539if (!isBlockPointer())540return 0;541542if (isZero())543return 0;544545// narrow()ed element in a composite array.546if (asBlockPointer().Base > sizeof(InlineDescriptor) &&547asBlockPointer().Base == Offset)548return 0;549550if (auto ElemSize = elemSize())551return getOffset() / ElemSize;552return 0;553}554555/// Checks if the index is one past end.556bool isOnePastEnd() const {557if (isIntegralPointer())558return false;559560if (!asBlockPointer().Pointee)561return false;562563if (isUnknownSizeArray())564return false;565566return isElementPastEnd() || isPastEnd() ||567(getSize() == getOffset() && !isZeroSizeArray());568}569570/// Checks if the pointer points past the end of the object.571bool isPastEnd() const {572if (isIntegralPointer())573return false;574575return !isZero() && Offset > PointeeStorage.BS.Pointee->getSize();576}577578/// Checks if the pointer is an out-of-bounds element pointer.579bool isElementPastEnd() const { return Offset == PastEndMark; }580581/// Checks if the pointer is pointing to a zero-size array.582bool isZeroSizeArray() const { return getFieldDesc()->isZeroSizeArray(); }583584/// Dereferences the pointer, if it's live.585template <typename T> T &deref() const {586assert(isLive() && "Invalid pointer");587assert(isBlockPointer());588assert(asBlockPointer().Pointee);589assert(isDereferencable());590assert(Offset + sizeof(T) <=591asBlockPointer().Pointee->getDescriptor()->getAllocSize());592593if (isArrayRoot())594return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() +595asBlockPointer().Base + sizeof(InitMapPtr));596597return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset);598}599600/// Dereferences a primitive element.601template <typename T> T &elem(unsigned I) const {602assert(I < getNumElems());603assert(isBlockPointer());604assert(asBlockPointer().Pointee);605return reinterpret_cast<T *>(asBlockPointer().Pointee->data() +606sizeof(InitMapPtr))[I];607}608609/// Whether this block can be read from at all. This is only true for610/// block pointers that point to a valid location inside that block.611bool isDereferencable() const {612if (!isBlockPointer())613return false;614if (isPastEnd())615return false;616617return true;618}619620/// Initializes a field.621void initialize() const;622/// Activats a field.623void activate() const;624/// Deactivates an entire strurcutre.625void deactivate() const;626627/// Compare two pointers.628ComparisonCategoryResult compare(const Pointer &Other) const {629if (!hasSameBase(*this, Other))630return ComparisonCategoryResult::Unordered;631632if (Offset < Other.Offset)633return ComparisonCategoryResult::Less;634else if (Offset > Other.Offset)635return ComparisonCategoryResult::Greater;636637return ComparisonCategoryResult::Equal;638}639640/// Checks if two pointers are comparable.641static bool hasSameBase(const Pointer &A, const Pointer &B);642/// Checks if two pointers can be subtracted.643static bool hasSameArray(const Pointer &A, const Pointer &B);644645/// Prints the pointer.646void print(llvm::raw_ostream &OS) const;647648private:649friend class Block;650friend class DeadBlock;651friend class MemberPointer;652friend class InterpState;653friend struct InitMap;654friend class DynamicAllocator;655656Pointer(Block *Pointee, unsigned Base, uint64_t Offset);657658/// Returns the embedded descriptor preceding a field.659InlineDescriptor *getInlineDesc() const {660assert(asBlockPointer().Base != sizeof(GlobalInlineDescriptor));661assert(asBlockPointer().Base <= asBlockPointer().Pointee->getSize());662return getDescriptor(asBlockPointer().Base);663}664665/// Returns a descriptor at a given offset.666InlineDescriptor *getDescriptor(unsigned Offset) const {667assert(Offset != 0 && "Not a nested pointer");668assert(isBlockPointer());669assert(!isZero());670return reinterpret_cast<InlineDescriptor *>(671asBlockPointer().Pointee->rawData() + Offset) -6721;673}674675/// Returns a reference to the InitMapPtr which stores the initialization map.676InitMapPtr &getInitMap() const {677assert(isBlockPointer());678assert(!isZero());679return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() +680asBlockPointer().Base);681}682683/// Offset into the storage.684uint64_t Offset = 0;685686/// Previous link in the pointer chain.687Pointer *Prev = nullptr;688/// Next link in the pointer chain.689Pointer *Next = nullptr;690691union {692BlockPointer BS;693IntPointer Int;694} PointeeStorage;695Storage StorageKind = Storage::Int;696};697698inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {699P.print(OS);700return OS;701}702703} // namespace interp704} // namespace clang705706#endif707708709