clang 23.0.0git
InterpFrame.cpp
Go to the documentation of this file.
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 "Function.h"
12#include "InterpStack.h"
13#include "InterpState.h"
14#include "MemberPointer.h"
15#include "Pointer.h"
16#include "PrimType.h"
17#include "Program.h"
19#include "clang/AST/DeclCXX.h"
20#include "clang/AST/ExprCXX.h"
21
22using namespace clang;
23using namespace clang::interp;
24
26 : Caller(nullptr), S(S), Depth(0), Func(nullptr), RetPC(CodePtr()),
27 ArgSize(0), Args(nullptr), FrameOffset(0) {}
28
30 InterpFrame *Caller, CodePtr RetPC, unsigned ArgSize)
31 : Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),
32 RetPC(RetPC), ArgSize(ArgSize), Args(static_cast<char *>(S.Stk.top())),
33 FrameOffset(S.Stk.size()) {
34
35 if (!Func)
36 return;
37 // Initialize argument blocks.
38 for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I)
39 new (argBlock(I)) Block(S.EvalID, Func->getParamDescriptor(I).Desc);
40
41 if (Func->getFrameSize() == 0)
42 return;
43
44 for (auto &Scope : Func->scopes()) {
45 for (auto &Local : Scope.locals()) {
46 new (localBlock(Local.Offset)) Block(S.EvalID, Local.Desc);
47 // Note that we are NOT calling invokeCtor() here, since that is done
48 // via the InitScope op.
49 new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
50 }
51 }
52}
53
55 unsigned VarArgSize)
56 : InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {
57 // As per our calling convention, the this pointer is
58 // part of the ArgSize.
59 // If the function has RVO, the RVO pointer is first.
60 // If the fuction has a This pointer, that one is next.
61 // Then follow the actual arguments (but those are handled
62 // in getParamPointer()).
63 if (Func->hasRVO()) {
64 // RVO pointer offset is always 0.
65 }
66
67 if (Func->hasThisPointer())
68 ThisPointerOffset = Func->hasRVO() ? sizeof(Pointer) : 0;
69}
70
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(argBlock(I));
78
79 // When destroying the InterpFrame, call the Dtor for all block
80 // that haven't been destroyed via a destroy() op yet.
81 // This happens when the execution is interruped midway-through.
83}
84
86 if (!Func || Func->getFrameSize() == 0)
87 return;
88 for (auto &Scope : Func->scopes()) {
89 for (auto &Local : Scope.locals()) {
90 S.deallocate(localBlock(Local.Offset));
91 }
92 }
93}
94
95void InterpFrame::initScope(unsigned Idx) {
96 if (!Func)
97 return;
98
99 for (auto &Local : Func->getScope(Idx).locals()) {
100 localBlock(Local.Offset)->invokeCtor();
101 }
102}
103
104void InterpFrame::enableLocal(unsigned Idx) {
105 assert(Func);
106
107 // FIXME: This is a little dirty, but to avoid adding a flag to
108 // InlineDescriptor that's only ever useful on the toplevel of local
109 // variables, we reuse the IsActive flag for the enabled state. We should
110 // probably use a different struct than InlineDescriptor for the block-level
111 // inline descriptor of local varaibles.
112 localInlineDesc(Idx)->IsActive = true;
113}
114
115void InterpFrame::destroy(unsigned Idx) {
116 for (auto &Local : Func->getScope(Idx).locals_reverse()) {
117 S.deallocate(localBlock(Local.Offset));
118 }
119}
120
121template <typename T>
122static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx,
123 QualType Ty) {
124 if constexpr (std::is_same_v<Pointer, T>) {
125 if (Ty->isPointerOrReferenceType())
126 V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
127 else {
128 if (std::optional<APValue> RValue = V.toRValue(ASTCtx, Ty))
129 RValue->printPretty(OS, ASTCtx, Ty);
130 else
131 OS << "...";
132 }
133 } else {
134 V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
135 }
136}
137
138static bool shouldSkipInBacktrace(const Function *F) {
139 if (F->isLambdaStaticInvoker())
140 return true;
141
142 const FunctionDecl *FD = F->getDecl();
143 if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
144 FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
145 return true;
146
147 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
148 MD && MD->getParent()->isAnonymousStructOrUnion())
149 return true;
150
151 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
152 Ctor && Ctor->isDefaulted() && Ctor->isTrivial() &&
153 Ctor->isCopyOrMoveConstructor() && Ctor->inits().empty())
154 return true;
155
156 return false;
157}
158
159void InterpFrame::describe(llvm::raw_ostream &OS) const {
160 assert(Func);
161 // For lambda static invokers, we would just print __invoke().
162 if (shouldSkipInBacktrace(Func))
163 return;
164
165 const Expr *CallExpr = Caller->getExpr(getRetPC());
166 const FunctionDecl *F = getCallee();
167
168 bool IsMemberCall = false;
169 bool ExplicitInstanceParam = false;
170 if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) {
171 IsMemberCall = !isa<CXXConstructorDecl>(MD) && !MD->isStatic();
172 ExplicitInstanceParam = MD->isExplicitObjectMemberFunction();
173 }
174
175 if (Func->hasThisPointer() && IsMemberCall) {
176 if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(CallExpr)) {
177 const Expr *Object = MCE->getImplicitObjectArgument();
178 Object->printPretty(OS, /*Helper=*/nullptr,
179 S.getASTContext().getPrintingPolicy(),
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>(CallExpr)) {
187 OCE->getArg(0)->printPretty(OS, /*Helper=*/nullptr,
188 S.getASTContext().getPrintingPolicy(),
189 /*Indentation=*/0);
190 OS << ".";
191 } else if (const auto *M = dyn_cast<CXXMethodDecl>(F)) {
192 print(OS, getThis(), S.getASTContext(),
193 S.getASTContext().getLValueReferenceType(
194 S.getASTContext().getCanonicalTagType(M->getParent())));
195 OS << ".";
196 }
197 }
198
199 F->getNameForDiagnostic(OS, S.getASTContext().getPrintingPolicy(),
200 /*Qualified=*/false);
201 OS << '(';
202 unsigned Off = 0;
203
204 Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;
205 Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;
206 llvm::ListSeparator Comma;
207 for (const ParmVarDecl *Param :
208 F->parameters().slice(ExplicitInstanceParam)) {
209 OS << Comma;
210 QualType Ty = Param->getType();
211 PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr);
212
213 TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getASTContext(), Ty));
214 Off += align(primSize(PrimTy));
215 }
216 OS << ")";
217}
218
220 if (!Caller->Func) {
221 if (SourceRange NullRange = S.getRange(nullptr, {}); NullRange.isValid())
222 return NullRange;
223 return S.EvalLocation;
224 }
225
226 // Move up to the frame that has a valid location for the caller.
227 for (const InterpFrame *C = this; C; C = C->Caller) {
228 if (!C->RetPC)
229 continue;
230 SourceRange CallRange =
231 S.getRange(C->Caller->Func, C->RetPC - sizeof(uintptr_t));
232 if (CallRange.isValid())
233 return CallRange;
234 }
235 return S.EvalLocation;
236}
237
239 if (!Func)
240 return nullptr;
241 return Func->getDecl();
242}
243
244Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
245 assert(Offset < Func->getFrameSize() && "Invalid local offset.");
246 return Pointer(localBlock(Offset));
247}
248
249Block *InterpFrame::getLocalBlock(unsigned Offset) const {
250 return localBlock(Offset);
251}
252
254 assert(!isBottomFrame());
255
256 Block *B = argBlock(Index);
257
258 // Copy the initial value.
259 if (!B->isInitialized()) {
260 unsigned ByteOffset = Func->getParamDescriptor(Index).Offset;
261 assert(B->getDescriptor()->isPrimitive());
262 B->invokeCtor();
264 new (B->data()) T(stackRef<T>(ByteOffset)));
265 assert(B->isInitialized());
266 }
267
268 return Pointer(B);
269}
270
271static bool funcHasUsableBody(const Function *F) {
272 assert(F);
273
274 if (F->isConstructor() || F->isDestructor())
275 return true;
276
277 return !F->getDecl()->isImplicit();
278}
279
281 // Implicitly created functions don't have any code we could point at,
282 // so return the call site.
283 if (Func && !funcHasUsableBody(Func) && Caller)
284 return Caller->getSource(RetPC);
285
286 // Similarly, if the resulting source location is invalid anyway,
287 // point to the caller instead.
288 SourceInfo Result = S.getSource(Func, PC);
289 if (Result.getLoc().isInvalid() && Caller)
290 return Caller->getSource(RetPC);
291 return Result;
292}
293
295 if (Func && !funcHasUsableBody(Func) && Caller)
296 return Caller->getExpr(RetPC);
297
298 return S.getExpr(Func, PC);
299}
300
302 if (Func && !funcHasUsableBody(Func) && Caller)
303 return Caller->getLocation(RetPC);
304
305 return S.getLocation(Func, PC);
306}
307
309 if (Func && !funcHasUsableBody(Func) && Caller)
310 return Caller->getRange(RetPC);
311
312 return S.getRange(Func, PC);
313}
314
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}
Defines the clang::ASTContext interface.
#define V(N, I)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx, QualType Ty)
static bool shouldSkipInBacktrace(const Function *F)
static bool funcHasUsableBody(const Function *F)
#define TYPE_SWITCH(Expr, B)
Definition PrimType.h:213
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:226
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2946
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1449
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2109
OverloadedOperatorKind getCXXOverloadedOperator() const
If this name is the name of an overloadable operator in C++ (e.g., operator+), retrieve the kind of o...
This represents one expression.
Definition Expr.h:112
Represents a function declaration or definition.
Definition Decl.h:2015
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2789
void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, bool Qualified) const override
Appends a human-readable name for this declaration into the given stream.
Definition Decl.cpp:3126
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:340
Represents a parameter to a function.
Definition Decl.h:1805
A (possibly-)qualified type.
Definition TypeBase.h:937
Encodes a location in the source.
A trivial tuple used to represent a source range.
bool isPointerOrReferenceType() const
Definition TypeBase.h:8672
A memory block, either on the stack or in the heap.
Definition InterpBlock.h:44
std::byte * data()
Returns a pointer to the stored data.
const Descriptor * getDescriptor() const
Returns the block's descriptor.
Definition InterpBlock.h:73
void invokeCtor()
Invokes the constructor.
bool isInitialized() const
Returns whether the data of this block has been initialized via invoking the Ctor func.
Definition InterpBlock.h:92
Pointer into the code segment.
Definition Source.h:30
Bytecode function.
Definition Function.h:99
InterpFrame(InterpState &S)
Bottom Frame.
const Expr * getExpr(CodePtr PC) const
InterpFrame * Caller
The frame of the previous function.
Definition InterpFrame.h:30
SourceInfo getSource(CodePtr PC) const
Map a location to a source.
CodePtr getRetPC() const
Returns the return address of the frame.
void enableLocal(unsigned Idx)
Block * getLocalBlock(unsigned Offset) const
SourceLocation getLocation(CodePtr PC) const
~InterpFrame()
Destroys the frame, killing all live pointers to stack slots.
const Pointer & getThis() const
Returns the 'this' pointer.
SourceRange getRange(CodePtr PC) const
Pointer getLocalPointer(unsigned Offset) const
Returns a pointer to a local variables.
void destroy(unsigned Idx)
Invokes the destructors for a scope.
Pointer getParamPointer(unsigned Offset)
Returns a pointer to an argument - lazily creates a block.
const FunctionDecl * getCallee() const override
Returns the caller.
void initScope(unsigned Idx)
SourceRange getCallRange() const override
Returns the location of the call to the frame.
void describe(llvm::raw_ostream &OS) const override
Describes the frame with arguments for diagnostic purposes.
Interpreter context.
Definition InterpState.h:36
A pointer to a memory block, live or dead.
Definition Pointer.h:97
Describes a scope block.
Definition Function.h:36
llvm::iterator_range< LocalVectorTy::const_iterator > locals() const
Definition Function.h:52
Describes the statement/declaration an opcode was generated from.
Definition Source.h:74
constexpr size_t align(size_t Size)
Aligns a size to the pointer alignment.
Definition PrimType.h:191
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:33
size_t primSize(PrimType Type)
Returns the size of a primitive type in bytes.
Definition PrimType.cpp:23
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Result
The result type of a method or function.
Definition TypeBase.h:905
for(const auto &A :T->param_types())
__UINTPTR_TYPE__ uintptr_t
An unsigned integer type with the property that any valid pointer to void can be converted to this ty...
bool isPrimitive() const
Checks if the descriptor is of a primitive.
Definition Descriptor.h:267
PrimType getPrimType() const
Definition Descriptor.h:240