This turns the current `Pointer` class into a discriminated union of `BlockPointer` and `IntPointer`. The former is what `Pointer` currently is while the latter is just an integer value and an optional `Descriptor*`. The `Pointer` then has type check functions like `isBlockPointer()`/`isIntegralPointer()`/`asBlockPointer()`/`asIntPointer()`, which can be used to access its data. Right now, the `IntPointer` and `BlockPointer` structs do not have any methods of their own and everything is instead implemented in Pointer (like it was before) and the functions now just either assert for the right type or decide what to do based on it. This also implements bitcasts by decaying the pointer to an integral pointer. `test/AST/Interp/const-eval.c` is a new test testing all kinds of stuff related to this. It still has a few tests `#ifdef`-ed out but that mostly depends on other unimplemented things like `__builtin_constant_p`.
119 lines
2.3 KiB
C++
119 lines
2.3 KiB
C++
//===--- Block.cpp - Allocated blocks for the interpreter -------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines the classes describing allocated blocks.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "InterpBlock.h"
|
|
#include "Pointer.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::interp;
|
|
|
|
void Block::addPointer(Pointer *P) {
|
|
assert(P);
|
|
if (IsStatic) {
|
|
assert(!Pointers);
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
assert(!hasPointer(P));
|
|
#endif
|
|
if (Pointers)
|
|
Pointers->Prev = P;
|
|
P->Next = Pointers;
|
|
P->Prev = nullptr;
|
|
Pointers = P;
|
|
}
|
|
|
|
void Block::removePointer(Pointer *P) {
|
|
assert(P);
|
|
if (IsStatic) {
|
|
assert(!Pointers);
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
assert(hasPointer(P));
|
|
#endif
|
|
|
|
if (Pointers == P)
|
|
Pointers = P->Next;
|
|
|
|
if (P->Prev)
|
|
P->Prev->Next = P->Next;
|
|
if (P->Next)
|
|
P->Next->Prev = P->Prev;
|
|
}
|
|
|
|
void Block::cleanup() {
|
|
if (Pointers == nullptr && IsDead)
|
|
(reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
|
|
}
|
|
|
|
void Block::replacePointer(Pointer *Old, Pointer *New) {
|
|
assert(Old);
|
|
assert(New);
|
|
if (IsStatic) {
|
|
assert(!Pointers);
|
|
return;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
assert(hasPointer(Old));
|
|
#endif
|
|
|
|
removePointer(Old);
|
|
addPointer(New);
|
|
|
|
Old->PointeeStorage.BS.Pointee = nullptr;
|
|
|
|
#ifndef NDEBUG
|
|
assert(!hasPointer(Old));
|
|
assert(hasPointer(New));
|
|
#endif
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
bool Block::hasPointer(const Pointer *P) const {
|
|
for (const Pointer *C = Pointers; C; C = C->Next) {
|
|
if (C == P)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
|
|
: Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) {
|
|
// Add the block to the chain of dead blocks.
|
|
if (Root)
|
|
Root->Prev = this;
|
|
|
|
Next = Root;
|
|
Prev = nullptr;
|
|
Root = this;
|
|
|
|
// Transfer pointers.
|
|
B.Pointers = Blk->Pointers;
|
|
for (Pointer *P = Blk->Pointers; P; P = P->Next)
|
|
P->PointeeStorage.BS.Pointee = &B;
|
|
}
|
|
|
|
void DeadBlock::free() {
|
|
if (Prev)
|
|
Prev->Next = Next;
|
|
if (Next)
|
|
Next->Prev = Prev;
|
|
if (Root == this)
|
|
Root = Next;
|
|
std::free(this);
|
|
}
|