1//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "InterpState.h"
10#include "InterpFrame.h"
11#include "InterpStack.h"
12#include "Program.h"
13#include "State.h"
14
15using namespace clang;
16using namespace clang::interp;
17
18InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
19 Context &Ctx, SourceMapper *M)
20 : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this),
21 Current(&BottomFrame) {}
22
23InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
24 Context &Ctx, const Function *Func)
25 : Parent(Parent), M(nullptr), P(P), Stk(Stk), Ctx(Ctx),
26 BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()),
27 Current(&BottomFrame) {}
28
29bool InterpState::inConstantContext() const {
30 if (ConstantContextOverride)
31 return *ConstantContextOverride;
32
33 return Parent.InConstantContext;
34}
35
36InterpState::~InterpState() {
37 while (Current && !Current->isBottomFrame()) {
38 InterpFrame *Next = Current->Caller;
39 delete Current;
40 Current = Next;
41 }
42 BottomFrame.destroyScopes();
43
44 while (DeadBlocks) {
45 DeadBlock *Next = DeadBlocks->Next;
46 std::free(ptr: DeadBlocks);
47 DeadBlocks = Next;
48 }
49}
50
51void InterpState::cleanup() {
52 // As a last resort, make sure all pointers still pointing to a dead block
53 // don't point to it anymore.
54 for (DeadBlock *DB = DeadBlocks; DB; DB = DB->Next) {
55 for (Pointer *P = DB->B.Pointers; P; P = P->Next) {
56 P->PointeeStorage.BS.Pointee = nullptr;
57 }
58 }
59
60 Alloc.cleanup();
61}
62
63Frame *InterpState::getCurrentFrame() {
64 if (Current && Current->Caller)
65 return Current;
66 return Parent.getCurrentFrame();
67}
68
69bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) {
70 QualType Type = E->getType();
71 CCEDiag(E, DiagId: diag::note_constexpr_overflow) << Value << Type;
72 return noteUndefinedBehavior();
73}
74
75void InterpState::deallocate(Block *B) {
76 assert(B);
77 const Descriptor *Desc = B->getDescriptor();
78 assert(Desc);
79
80 if (B->hasPointers()) {
81 size_t Size = B->getSize();
82
83 // Allocate a new block, transferring over pointers.
84 char *Memory =
85 reinterpret_cast<char *>(std::malloc(size: sizeof(DeadBlock) + Size));
86 auto *D = new (Memory) DeadBlock(DeadBlocks, B);
87 std::memset(s: D->B.rawData(), c: 0, n: D->B.getSize());
88
89 // Move data and metadata from the old block to the new (dead)block.
90 if (B->IsInitialized && Desc->MoveFn) {
91 Desc->MoveFn(B, B->data(), D->data(), Desc);
92 if (Desc->getMetadataSize() > 0)
93 std::memcpy(dest: D->rawData(), src: B->rawData(), n: Desc->getMetadataSize());
94 }
95 D->B.IsInitialized = B->IsInitialized;
96
97 // We moved the contents over to the DeadBlock.
98 B->IsInitialized = false;
99 } else if (B->IsInitialized) {
100 B->invokeDtor();
101 }
102}
103
104bool InterpState::maybeDiagnoseDanglingAllocations() {
105 bool NoAllocationsLeft = (Alloc.getNumAllocations() == 0);
106
107 if (!checkingPotentialConstantExpression()) {
108 for (const auto &It : Alloc.allocation_sites()) {
109 assert(It.second.size() > 0);
110
111 const Expr *Source = It.first;
112 CCEDiag(Loc: Source->getExprLoc(), DiagId: diag::note_constexpr_memory_leak)
113 << (It.second.size() - 1) << Source->getSourceRange();
114 }
115 }
116 // Keep evaluating before C++20, since the CXXNewExpr wasn't valid there
117 // in the first place.
118 return NoAllocationsLeft || !getLangOpts().CPlusPlus20;
119}
120
121StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const {
122 for (const InterpFrame *F = Current; F; F = F->Caller) {
123 const Function *Func = F->getFunction();
124 if (!Func)
125 continue;
126 const auto *MD = dyn_cast_if_present<CXXMethodDecl>(Val: Func->getDecl());
127 if (!MD)
128 continue;
129 const IdentifierInfo *FnII = MD->getIdentifier();
130 if (!FnII || !FnII->isStr(Str: Name))
131 continue;
132
133 const auto *CTSD =
134 dyn_cast<ClassTemplateSpecializationDecl>(Val: MD->getParent());
135 if (!CTSD)
136 continue;
137
138 const IdentifierInfo *ClassII = CTSD->getIdentifier();
139 const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
140 if (CTSD->isInStdNamespace() && ClassII && ClassII->isStr(Str: "allocator") &&
141 TAL.size() >= 1 && TAL[0].getKind() == TemplateArgument::Type) {
142 QualType ElemType = TAL[0].getAsType();
143 const auto *NewCall = cast<CallExpr>(Val: F->Caller->getExpr(PC: F->getRetPC()));
144 return {.Call: NewCall, .AllocType: ElemType};
145 }
146 }
147
148 return {};
149}
150