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