Files
clang-p2996/clang/lib/AST/Interp/Pointer.cpp
Timm Bäder 865094746e [clang][Interp] Track initialization state of local variables
Use an InlineDescriptor per local variable to track whether locals
have been initialized or not. This way we can support uninitialized
local variables in constexpr functions.

Differential Revision: https://reviews.llvm.org/D135750
2023-01-18 16:55:02 +01:00

210 lines
5.6 KiB
C++

//===--- Pointer.cpp - Types for the constexpr VM ---------------*- 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
//
//===----------------------------------------------------------------------===//
#include "Pointer.h"
#include "Function.h"
#include "InterpBlock.h"
#include "PrimType.h"
using namespace clang;
using namespace clang::interp;
Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
: Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
Pointer::Pointer(Pointer &&P)
: Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
if (Pointee)
Pointee->movePointer(&P, this);
}
Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
: Pointee(Pointee), Base(Base), Offset(Offset) {
assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
if (Pointee)
Pointee->addPointer(this);
}
Pointer::~Pointer() {
if (Pointee) {
Pointee->removePointer(this);
Pointee->cleanup();
}
}
void Pointer::operator=(const Pointer &P) {
Block *Old = Pointee;
if (Pointee)
Pointee->removePointer(this);
Offset = P.Offset;
Base = P.Base;
Pointee = P.Pointee;
if (Pointee)
Pointee->addPointer(this);
if (Old)
Old->cleanup();
}
void Pointer::operator=(Pointer &&P) {
Block *Old = Pointee;
if (Pointee)
Pointee->removePointer(this);
Offset = P.Offset;
Base = P.Base;
Pointee = P.Pointee;
if (Pointee)
Pointee->movePointer(&P, this);
if (Old)
Old->cleanup();
}
APValue Pointer::toAPValue() const {
APValue::LValueBase Base;
llvm::SmallVector<APValue::LValuePathEntry, 5> Path;
CharUnits Offset;
bool IsNullPtr;
bool IsOnePastEnd;
if (isZero()) {
Base = static_cast<const Expr *>(nullptr);
IsNullPtr = true;
IsOnePastEnd = false;
Offset = CharUnits::Zero();
} else {
// Build the lvalue base from the block.
Descriptor *Desc = getDeclDesc();
if (auto *VD = Desc->asValueDecl())
Base = VD;
else if (auto *E = Desc->asExpr())
Base = E;
else
llvm_unreachable("Invalid allocation type");
// Not a null pointer.
IsNullPtr = false;
if (isUnknownSizeArray()) {
IsOnePastEnd = false;
Offset = CharUnits::Zero();
} else {
// TODO: compute the offset into the object.
Offset = CharUnits::Zero();
// Build the path into the object.
Pointer Ptr = *this;
while (Ptr.isField() || Ptr.isArrayElement()) {
if (Ptr.isArrayElement()) {
Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
Ptr = Ptr.getArray();
} else {
// TODO: figure out if base is virtual
bool IsVirtual = false;
// Create a path entry for the field.
Descriptor *Desc = Ptr.getFieldDesc();
if (auto *BaseOrMember = Desc->asDecl()) {
Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
Ptr = Ptr.getBase();
continue;
}
llvm_unreachable("Invalid field type");
}
}
IsOnePastEnd = isOnePastEnd();
}
}
// We assemble the LValuePath starting from the innermost pointer to the
// outermost one. SO in a.b.c, the first element in Path will refer to
// the field 'c', while later code expects it to refer to 'a'.
// Just invert the order of the elements.
std::reverse(Path.begin(), Path.end());
return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
}
bool Pointer::isInitialized() const {
assert(Pointee && "Cannot check if null pointer was initialized");
Descriptor *Desc = getFieldDesc();
assert(Desc);
if (Desc->isPrimitiveArray()) {
if (isStatic() && Base == 0)
return true;
// Primitive array field are stored in a bitset.
InitMap *Map = getInitMap();
if (!Map)
return false;
if (Map == (InitMap *)-1)
return true;
return Map->isInitialized(getIndex());
} else {
// Field has its bit in an inline descriptor.
return Base == 0 || getInlineDesc()->IsInitialized;
}
}
void Pointer::initialize() const {
assert(Pointee && "Cannot initialize null pointer");
Descriptor *Desc = getFieldDesc();
assert(Desc);
if (Desc->isArray()) {
if (Desc->isPrimitiveArray()) {
// Primitive global arrays don't have an initmap.
if (isStatic() && Base == 0)
return;
// Primitive array initializer.
InitMap *&Map = getInitMap();
if (Map == (InitMap *)-1)
return;
if (Map == nullptr)
Map = InitMap::allocate(Desc->getNumElems());
if (Map->initialize(getIndex())) {
free(Map);
Map = (InitMap *)-1;
}
}
} else {
// Field has its bit in an inline descriptor.
assert(Base != 0 && "Only composite fields can be initialised");
getInlineDesc()->IsInitialized = true;
}
}
void Pointer::activate() const {
// Field has its bit in an inline descriptor.
assert(Base != 0 && "Only composite fields can be initialised");
getInlineDesc()->IsActive = true;
}
void Pointer::deactivate() const {
// TODO: this only appears in constructors, so nothing to deactivate.
}
bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
return A.Pointee == B.Pointee;
}
bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
}