1//===--- InterpFrame.cpp - Call Frame implementation for the 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 "InterpFrame.h"
10#include "Boolean.h"
11#include "Char.h"
12#include "Function.h"
13#include "InterpStack.h"
14#include "InterpState.h"
15#include "MemberPointer.h"
16#include "Pointer.h"
17#include "PrimType.h"
18#include "Program.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/AST/DeclCXX.h"
21#include "clang/AST/ExprCXX.h"
22
23using namespace clang;
24using namespace clang::interp;
25
26InterpFrame::InterpFrame(InterpState &S)
27 : Caller(nullptr), S(S), Func(nullptr), RetPC(CodePtr()), Args(nullptr),
28 ArgSize(0), Depth(0) {}
29
30InterpFrame::InterpFrame(InterpState &S, const Function *Func,
31 InterpFrame *Caller, CodePtr RetPC, unsigned ArgSize)
32 : Caller(Caller), S(S), Func(Func), RetPC(RetPC),
33 Args(static_cast<char *>(S.Stk.top())), ArgSize(ArgSize),
34 Depth(Caller ? Caller->Depth + 1 : 0) {
35#ifndef NDEBUG
36 FrameOffset = S.Stk.size();
37#endif
38
39 if (!Func)
40 return;
41
42 FuncFlags |= Func->hasRVO() * HasRVOFlag;
43 FuncFlags |= Func->hasThisPointer() * HasThisFlag;
44
45 // Initialize argument blocks.
46 for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I)
47 new (argBlock(Index: I)) Block(S.EvalID, Func->getParamDescriptor(Index: I).Desc);
48
49 if (Func->getFrameSize() == 0)
50 return;
51
52 for (auto &Scope : Func->scopes()) {
53 for (auto &Local : Scope.locals()) {
54 new (localBlock(Offset: Local.Offset))
55 Block(S.EvalID, Local.Desc, Block::InlineDescMD);
56 // Note that we are NOT calling invokeCtor() here, since that is done
57 // via the InitScope op.
58 new (localInlineDesc(Offset: Local.Offset)) InlineDescriptor(Local.Desc);
59 }
60 }
61}
62
63InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
64 unsigned VarArgSize)
65 : InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {
66 // As per our calling convention, the this pointer is
67 // part of the ArgSize.
68 // If the function has RVO, the RVO pointer is first.
69 // If the fuction has a This pointer, that one is next.
70 // Then follow the actual arguments (but those are handled
71 // in getParamPointer()).
72}
73
74InterpFrame::~InterpFrame() {
75 if (!Func)
76 return;
77
78 // De-initialize all argument blocks.
79 for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I)
80 S.deallocate(B: argBlock(Index: I));
81
82 // When destroying the InterpFrame, call the Dtor for all block
83 // that haven't been destroyed via a destroy() op yet.
84 // This happens when the execution is interruped midway-through.
85 destroyScopes();
86}
87
88void InterpFrame::destroyScopes() {
89 if (!Func || Func->getFrameSize() == 0)
90 return;
91 for (auto &Scope : Func->scopes()) {
92 for (auto &Local : Scope.locals()) {
93 S.deallocate(B: localBlock(Offset: Local.Offset));
94 }
95 }
96}
97
98void InterpFrame::initScope(unsigned Idx) {
99 if (!Func)
100 return;
101
102 for (auto &Local : Func->getScope(Idx).locals()) {
103 assert(!localBlock(Local.Offset)->isInitialized());
104 localBlock(Offset: Local.Offset)->invokeCtor();
105 }
106}
107
108void InterpFrame::enableLocal(unsigned Idx) {
109 assert(Func);
110
111 // FIXME: This is a little dirty, but to avoid adding a flag to
112 // InlineDescriptor that's only ever useful on the toplevel of local
113 // variables, we reuse the IsActive flag for the enabled state. We should
114 // probably use a different struct than InlineDescriptor for the block-level
115 // inline descriptor of local varaibles.
116 localInlineDesc(Offset: Idx)->IsActive = true;
117}
118
119void InterpFrame::destroy(unsigned Idx) {
120 for (auto &Local : Func->getScope(Idx).locals_reverse()) {
121 S.deallocate(B: localBlock(Offset: Local.Offset));
122 }
123}
124
125template <typename T>
126static void print(llvm::raw_ostream &OS, const T &V, const Context &Ctx,
127 QualType Ty) {
128 if constexpr (std::is_same_v<Pointer, T>) {
129 if (Ty->isPointerOrReferenceType())
130 V.toAPValue(Ctx.getASTContext()).printPretty(OS, Ctx.getASTContext(), Ty);
131 else {
132 if (std::optional<APValue> RValue = V.toRValue(Ctx, Ty))
133 RValue->printPretty(OS, Ctx: Ctx.getASTContext(), Ty);
134 else
135 OS << "...";
136 }
137 } else {
138 V.toAPValue(Ctx.getASTContext()).printPretty(OS, Ctx.getASTContext(), Ty);
139 }
140}
141
142static bool shouldSkipInBacktrace(const Function *F) {
143 if (F->isLambdaStaticInvoker())
144 return true;
145
146 const FunctionDecl *FD = F->getDecl();
147 if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
148 FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
149 return true;
150
151 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FD);
152 MD && MD->getParent()->isAnonymousStructOrUnion())
153 return true;
154
155 return false;
156}
157
158void InterpFrame::describe(llvm::raw_ostream &OS) const {
159 assert(Func);
160 // For lambda static invokers, we would just print __invoke().
161 if (shouldSkipInBacktrace(F: Func))
162 return;
163
164 const ASTContext &ASTCtx = S.getASTContext();
165 const Expr *CallExpr = Caller->getExpr(PC: getRetOpPC());
166 const FunctionDecl *F = getCallee();
167 auto PrintingPolicy = ASTCtx.getPrintingPolicy();
168 PrintingPolicy.SuppressLambdaBody = true;
169
170 bool IsMemberCall = false;
171 bool ExplicitInstanceParam = false;
172 if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: F)) {
173 IsMemberCall = !isa<CXXConstructorDecl>(Val: MD) && !MD->isStatic();
174 ExplicitInstanceParam = MD->isExplicitObjectMemberFunction();
175 }
176
177 if (Func->hasThisPointer() && IsMemberCall) {
178 if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(Val: CallExpr)) {
179 const Expr *Object = MCE->getImplicitObjectArgument();
180 Object->printPretty(OS, /*Helper=*/nullptr,
181 Policy: PrintingPolicy,
182 /*Indentation=*/0);
183 if (Object->getType()->isPointerType())
184 OS << "->";
185 else
186 OS << '.';
187 } else if (const auto *OCE =
188 dyn_cast_if_present<CXXOperatorCallExpr>(Val: CallExpr)) {
189 OCE->getArg(Arg: 0)->printPretty(OS, /*Helper=*/nullptr,
190 Policy: PrintingPolicy,
191 /*Indentation=*/0);
192 OS << '.';
193 } else if (const auto *M = dyn_cast<CXXMethodDecl>(Val: F)) {
194 print(OS, V: getThis(), Ctx: S.getContext(),
195 Ty: ASTCtx.getLValueReferenceType(
196 T: ASTCtx.getCanonicalTagType(TD: M->getParent())));
197 OS << '.';
198 }
199 }
200
201 F->getNameForDiagnostic(OS, Policy: PrintingPolicy, /*Qualified=*/false);
202 OS << '(';
203 unsigned Off = 0;
204 unsigned ParamIndex = ExplicitInstanceParam;
205 Off += Func->hasRVO() ? primSize(Type: PT_Ptr) : 0;
206 Off += Func->hasThisPointer() ? primSize(Type: PT_Ptr) : 0;
207 llvm::ListSeparator Comma;
208 for (const ParmVarDecl *Param :
209 F->parameters().slice(N: ExplicitInstanceParam)) {
210 OS << Comma;
211 PrimType PrimT = Func->getParamDescriptor(Index: ParamIndex).T;
212 TYPE_SWITCH(PrimT,
213 print(OS, stackRef<T>(Off), S.getContext(), Param->getType()));
214 Off += align(Size: primSize(Type: PrimT));
215 ++ParamIndex;
216 }
217 OS << ')';
218}
219
220SourceRange InterpFrame::getCallRange() const {
221 if (!Caller->Func) {
222 if (SourceRange NullRange = S.getSource(PC: {}).getRange(); NullRange.isValid())
223 return NullRange;
224
225 return S.EvalLocation;
226 }
227
228 // Move up to the frame that has a valid location for the caller.
229 for (const InterpFrame *C = this; C; C = C->Caller) {
230 if (!C->RetPC)
231 continue;
232 SourceRange CallRange =
233 C->Caller->Func->getSource(PC: C->getRetOpPC() - sizeof(uintptr_t))
234 .getRange();
235 if (CallRange.isValid())
236 return CallRange;
237 }
238 return S.EvalLocation;
239}
240
241const FunctionDecl *InterpFrame::getCallee() const {
242 if (!Func)
243 return nullptr;
244 return Func->getDecl();
245}
246
247Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
248 assert(Offset < Func->getFrameSize() && "Invalid local offset.");
249 return Pointer(localBlock(Offset));
250}
251
252Block *InterpFrame::getLocalBlock(unsigned Offset) const {
253 return localBlock(Offset);
254}
255
256Pointer InterpFrame::getParamPointer(unsigned Index) {
257 assert(!isBottomFrame());
258
259 Block *B = argBlock(Index);
260
261 // Copy the initial value.
262 if (!B->isInitialized()) {
263 unsigned ByteOffset = Func->getParamDescriptor(Index).Offset;
264 assert(B->getDescriptor()->isPrimitive());
265 B->invokeCtor();
266 TYPE_SWITCH(B->getDescriptor()->getPrimType(),
267 new (B->data()) T(stackRef<T>(ByteOffset)));
268 assert(B->isInitialized());
269 }
270
271 return Pointer(B);
272}
273
274static bool funcHasUsableBody(const Function *F) {
275 assert(F);
276
277 if (F->isConstructor() || F->isDestructor())
278 return true;
279
280 return !F->getDecl()->isImplicit();
281}
282
283SourceInfo InterpFrame::getSource(CodePtr PC) const {
284 if (!Func)
285 return S.getSource(PC);
286
287 // Implicitly created functions don't have any code we could point at,
288 // so return the call site.
289 if (Func && !funcHasUsableBody(F: Func) && Caller)
290 return Caller->getSource(PC: getRetOpPC());
291
292 // Similarly, if the resulting source location is invalid anyway,
293 // point to the caller instead.
294 SourceInfo Result = Func->getSource(PC);
295 if (Result.getLoc().isInvalid() && Caller)
296 return Caller->getSource(PC: getRetOpPC());
297
298 return Result;
299}
300
301bool InterpFrame::isStdFunction() const {
302 if (!Func)
303 return false;
304 for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
305 if (DC->isStdNamespace())
306 return true;
307
308 return false;
309}
310