1a7dea167SDimitry Andric //===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric
9a7dea167SDimitry Andric #include "InterpState.h"
10a7dea167SDimitry Andric #include "InterpFrame.h"
11a7dea167SDimitry Andric #include "InterpStack.h"
12a7dea167SDimitry Andric #include "Program.h"
13a7dea167SDimitry Andric #include "State.h"
14a7dea167SDimitry Andric
15a7dea167SDimitry Andric using namespace clang;
16a7dea167SDimitry Andric using namespace clang::interp;
17a7dea167SDimitry Andric
InterpState(State & Parent,Program & P,InterpStack & Stk,Context & Ctx,SourceMapper * M)18a7dea167SDimitry Andric InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
19a7dea167SDimitry Andric Context &Ctx, SourceMapper *M)
2006c3fb27SDimitry Andric : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr) {}
21a7dea167SDimitry Andric
~InterpState()22a7dea167SDimitry Andric InterpState::~InterpState() {
23a7dea167SDimitry Andric while (Current) {
24a7dea167SDimitry Andric InterpFrame *Next = Current->Caller;
25a7dea167SDimitry Andric delete Current;
26a7dea167SDimitry Andric Current = Next;
27a7dea167SDimitry Andric }
28a7dea167SDimitry Andric
29a7dea167SDimitry Andric while (DeadBlocks) {
30a7dea167SDimitry Andric DeadBlock *Next = DeadBlocks->Next;
3106c3fb27SDimitry Andric std::free(DeadBlocks);
32a7dea167SDimitry Andric DeadBlocks = Next;
33a7dea167SDimitry Andric }
34a7dea167SDimitry Andric }
35a7dea167SDimitry Andric
cleanup()36*0fca6ea1SDimitry Andric void InterpState::cleanup() {
37*0fca6ea1SDimitry Andric // As a last resort, make sure all pointers still pointing to a dead block
38*0fca6ea1SDimitry Andric // don't point to it anymore.
39*0fca6ea1SDimitry Andric for (DeadBlock *DB = DeadBlocks; DB; DB = DB->Next) {
40*0fca6ea1SDimitry Andric for (Pointer *P = DB->B.Pointers; P; P = P->Next) {
41*0fca6ea1SDimitry Andric P->PointeeStorage.BS.Pointee = nullptr;
42*0fca6ea1SDimitry Andric }
43*0fca6ea1SDimitry Andric }
44*0fca6ea1SDimitry Andric
45*0fca6ea1SDimitry Andric Alloc.cleanup();
46*0fca6ea1SDimitry Andric }
47*0fca6ea1SDimitry Andric
getCurrentFrame()48a7dea167SDimitry Andric Frame *InterpState::getCurrentFrame() {
4906c3fb27SDimitry Andric if (Current && Current->Caller)
50a7dea167SDimitry Andric return Current;
51a7dea167SDimitry Andric return Parent.getCurrentFrame();
52a7dea167SDimitry Andric }
53a7dea167SDimitry Andric
reportOverflow(const Expr * E,const llvm::APSInt & Value)54a7dea167SDimitry Andric bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) {
55a7dea167SDimitry Andric QualType Type = E->getType();
56a7dea167SDimitry Andric CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
57a7dea167SDimitry Andric return noteUndefinedBehavior();
58a7dea167SDimitry Andric }
59a7dea167SDimitry Andric
deallocate(Block * B)60a7dea167SDimitry Andric void InterpState::deallocate(Block *B) {
6106c3fb27SDimitry Andric assert(B);
6206c3fb27SDimitry Andric const Descriptor *Desc = B->getDescriptor();
6306c3fb27SDimitry Andric assert(Desc);
6406c3fb27SDimitry Andric
65a7dea167SDimitry Andric if (B->hasPointers()) {
66a7dea167SDimitry Andric size_t Size = B->getSize();
67a7dea167SDimitry Andric
68a7dea167SDimitry Andric // Allocate a new block, transferring over pointers.
6906c3fb27SDimitry Andric char *Memory =
7006c3fb27SDimitry Andric reinterpret_cast<char *>(std::malloc(sizeof(DeadBlock) + Size));
71a7dea167SDimitry Andric auto *D = new (Memory) DeadBlock(DeadBlocks, B);
72*0fca6ea1SDimitry Andric std::memset(D->B.rawData(), 0, D->B.getSize());
73a7dea167SDimitry Andric
745f757f3fSDimitry Andric // Move data and metadata from the old block to the new (dead)block.
75*0fca6ea1SDimitry Andric if (B->IsInitialized && Desc->MoveFn) {
76a7dea167SDimitry Andric Desc->MoveFn(B, B->data(), D->data(), Desc);
775f757f3fSDimitry Andric if (Desc->getMetadataSize() > 0)
785f757f3fSDimitry Andric std::memcpy(D->rawData(), B->rawData(), Desc->getMetadataSize());
795f757f3fSDimitry Andric }
80*0fca6ea1SDimitry Andric D->B.IsInitialized = B->IsInitialized;
815f757f3fSDimitry Andric
825f757f3fSDimitry Andric // We moved the contents over to the DeadBlock.
835f757f3fSDimitry Andric B->IsInitialized = false;
84*0fca6ea1SDimitry Andric } else if (B->IsInitialized) {
855f757f3fSDimitry Andric B->invokeDtor();
86a7dea167SDimitry Andric }
87a7dea167SDimitry Andric }
88*0fca6ea1SDimitry Andric
maybeDiagnoseDanglingAllocations()89*0fca6ea1SDimitry Andric bool InterpState::maybeDiagnoseDanglingAllocations() {
90*0fca6ea1SDimitry Andric bool NoAllocationsLeft = (Alloc.getNumAllocations() == 0);
91*0fca6ea1SDimitry Andric
92*0fca6ea1SDimitry Andric if (!checkingPotentialConstantExpression()) {
93*0fca6ea1SDimitry Andric for (const auto &It : Alloc.allocation_sites()) {
94*0fca6ea1SDimitry Andric assert(It.second.size() > 0);
95*0fca6ea1SDimitry Andric
96*0fca6ea1SDimitry Andric const Expr *Source = It.first;
97*0fca6ea1SDimitry Andric CCEDiag(Source->getExprLoc(), diag::note_constexpr_memory_leak)
98*0fca6ea1SDimitry Andric << (It.second.size() - 1) << Source->getSourceRange();
99*0fca6ea1SDimitry Andric }
100*0fca6ea1SDimitry Andric }
101*0fca6ea1SDimitry Andric return NoAllocationsLeft;
102*0fca6ea1SDimitry Andric }
103