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