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