aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/AST/ByteCode
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/AST/ByteCode')
-rw-r--r--clang/lib/AST/ByteCode/Compiler.cpp7
-rw-r--r--clang/lib/AST/ByteCode/DynamicAllocator.cpp56
-rw-r--r--clang/lib/AST/ByteCode/DynamicAllocator.h4
-rw-r--r--clang/lib/AST/ByteCode/Interp.cpp4
-rw-r--r--clang/lib/AST/ByteCode/InterpBlock.cpp5
-rw-r--r--clang/lib/AST/ByteCode/InterpState.cpp9
6 files changed, 44 insertions, 41 deletions
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index cc99efa..f656687 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2063,12 +2063,9 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
const FunctionDecl *FuncDecl,
bool Activate) {
assert(VarScope->getKind() == ScopeKind::Call);
- bool HasNonNullAttr = false;
llvm::BitVector NonNullArgs;
- if (FuncDecl && FuncDecl->hasAttr<NonNullAttr>()) {
- HasNonNullAttr = true;
+ if (FuncDecl && FuncDecl->hasAttr<NonNullAttr>())
NonNullArgs = collectNonNullArgs(FuncDecl, Args);
- }
unsigned ArgIndex = 0;
for (const Expr *Arg : Args) {
@@ -2094,7 +2091,7 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args,
return false;
}
- if (HasNonNullAttr && NonNullArgs[ArgIndex]) {
+ if (!NonNullArgs.empty() && NonNullArgs[ArgIndex]) {
PrimType ArgT = classify(Arg).value_or(PT_Ptr);
if (ArgT == PT_Ptr) {
if (!this->emitCheckNonNullArg(ArgT, Arg))
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index bbef941..f38d585 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -13,25 +13,6 @@
using namespace clang;
using namespace clang::interp;
-// FIXME: There is a peculiar problem with the way we track pointers
-// to blocks and the way we allocate dynamic memory.
-//
-// When we have code like this:
-// while (true) {
-// char *buffer = new char[1024];
-// delete[] buffer;
-// }
-//
-// We have a local variable 'buffer' pointing to the heap allocated memory.
-// When deallocating the memory via delete[], that local variable still
-// points to the memory, which means we will create a DeadBlock for it and move
-// it over to that block, essentially duplicating the allocation. Moving
-// the data is also slow.
-//
-// However, when we actually try to access the allocation after it has been
-// freed, we need the block to still exist (alive or dead) so we can tell
-// that it's a dynamic allocation.
-
DynamicAllocator::~DynamicAllocator() { cleanup(); }
void DynamicAllocator::cleanup() {
@@ -42,8 +23,11 @@ void DynamicAllocator::cleanup() {
for (auto &Iter : AllocationSites) {
auto &AllocSite = Iter.second;
for (auto &Alloc : AllocSite.Allocations) {
- Block *B = reinterpret_cast<Block *>(Alloc.Memory.get());
+ Block *B = Alloc.block();
+ assert(!B->IsDead);
+ assert(B->isInitialized());
B->invokeDtor();
+
if (B->hasPointers()) {
while (B->Pointers) {
Pointer *Next = B->Pointers->asBlockPointer().Next;
@@ -89,6 +73,12 @@ Block *DynamicAllocator::allocate(const Descriptor *D, unsigned EvalID,
assert(D);
assert(D->asExpr());
+ // Garbage collection. Remove all dead allocations that don't have pointers to
+ // them anymore.
+ llvm::erase_if(DeadAllocations, [](Allocation &Alloc) -> bool {
+ return !Alloc.block()->hasPointers();
+ });
+
auto Memory =
std::make_unique<std::byte[]>(sizeof(Block) + D->getAllocSize());
auto *B = new (Memory.get()) Block(EvalID, D, /*isStatic=*/false);
@@ -132,18 +122,34 @@ bool DynamicAllocator::deallocate(const Expr *Source,
// Find the Block to delete.
auto AllocIt = llvm::find_if(Site.Allocations, [&](const Allocation &A) {
- const Block *B = reinterpret_cast<const Block *>(A.Memory.get());
- return BlockToDelete == B;
+ return BlockToDelete == A.block();
});
assert(AllocIt != Site.Allocations.end());
- Block *B = reinterpret_cast<Block *>(AllocIt->Memory.get());
+ Block *B = AllocIt->block();
+ assert(B->isInitialized());
+ assert(!B->IsDead);
B->invokeDtor();
- S.deallocate(B);
- Site.Allocations.erase(AllocIt);
+ // Almost all our dynamic allocations have a pointer pointing to them
+ // when we deallocate them, since otherwise we can't call delete() at all.
+ // This means that we would usually need to create DeadBlocks for all of them.
+ // To work around that, we instead mark them as dead without moving the data
+ // over to a DeadBlock and simply keep the block in a separate DeadAllocations
+ // list.
+ if (B->hasPointers()) {
+ B->IsDead = true;
+ DeadAllocations.push_back(std::move(*AllocIt));
+ Site.Allocations.erase(AllocIt);
+
+ if (Site.size() == 0)
+ AllocationSites.erase(It);
+ return true;
+ }
+ // Get rid of the allocation altogether.
+ Site.Allocations.erase(AllocIt);
if (Site.empty())
AllocationSites.erase(It);
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.h b/clang/lib/AST/ByteCode/DynamicAllocator.h
index cba5e34..31d0e58 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.h
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.h
@@ -43,6 +43,7 @@ private:
std::unique_ptr<std::byte[]> Memory;
Allocation(std::unique_ptr<std::byte[]> Memory)
: Memory(std::move(Memory)) {}
+ Block *block() const { return reinterpret_cast<Block *>(Memory.get()); }
};
struct AllocationSite {
@@ -99,6 +100,9 @@ public:
private:
llvm::DenseMap<const Expr *, AllocationSite> AllocationSites;
+ // Allocations that have already been deallocated but had pointers
+ // to them.
+ llvm::SmallVector<Allocation> DeadAllocations;
using PoolAllocTy = llvm::BumpPtrAllocator;
PoolAllocTy DescAllocator;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index bc14bd3d..b5c044c 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -518,7 +518,7 @@ bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK) {
- if (!Ptr.isOnePastEnd())
+ if (!Ptr.isOnePastEnd() && !Ptr.isZeroSizeArray())
return true;
if (S.getLangOpts().CPlusPlus) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
@@ -829,8 +829,6 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
- if (!CheckRange(S, OpPC, Ptr, AK_Read))
- return false;
if (!CheckActive(S, OpPC, Ptr, AK_Read))
return false;
if (!CheckLifetime(S, OpPC, Ptr.getLifetime(), AK_Read))
diff --git a/clang/lib/AST/ByteCode/InterpBlock.cpp b/clang/lib/AST/ByteCode/InterpBlock.cpp
index 963b54e..8b7f6a7 100644
--- a/clang/lib/AST/ByteCode/InterpBlock.cpp
+++ b/clang/lib/AST/ByteCode/InterpBlock.cpp
@@ -64,7 +64,7 @@ void Block::removePointer(Pointer *P) {
}
void Block::cleanup() {
- if (Pointers == nullptr && IsDead)
+ if (Pointers == nullptr && !IsDynamic && IsDead)
(reinterpret_cast<DeadBlock *>(this + 1) - 1)->free();
}
@@ -133,8 +133,7 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
}
void DeadBlock::free() {
- if (B.IsInitialized)
- B.invokeDtor();
+ assert(!B.isInitialized());
if (Prev)
Prev->Next = Next;
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index 49c9b54..32f940c 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -76,6 +76,7 @@ bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) {
void InterpState::deallocate(Block *B) {
assert(B);
+ assert(!B->isDynamic());
// The block might have a pointer saved in a field in its data
// that points to the block itself. We call the dtor first,
// which will destroy all the data but leave InlineDescriptors
@@ -84,6 +85,7 @@ void InterpState::deallocate(Block *B) {
if (B->IsInitialized)
B->invokeDtor();
+ assert(!B->isInitialized());
if (B->hasPointers()) {
size_t Size = B->getSize();
// Allocate a new block, transferring over pointers.
@@ -92,11 +94,8 @@ void InterpState::deallocate(Block *B) {
auto *D = new (Memory) DeadBlock(DeadBlocks, B);
// Since the block doesn't hold any actual data anymore, we can just
// memcpy() everything over.
- std::memcpy(D->rawData(), B->rawData(), B->getSize());
- D->B.IsInitialized = B->IsInitialized;
-
- // We moved the contents over to the DeadBlock.
- B->IsInitialized = false;
+ std::memcpy(D->rawData(), B->rawData(), Size);
+ D->B.IsInitialized = false;
}
}