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 "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"
20#include "clang/AST/DeclCXX.h"
21#include "clang/AST/ExprCXX.h"
22
23using namespace clang;
24using namespace clang::interp;
25
27 : Caller(nullptr), S(S), Depth(0), Func(nullptr), RetPC(CodePtr()),
28 ArgSize(0), Args(nullptr), FrameOffset(0) {}
29
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 FrameOffset(S.Stk.size()) {
35
36 if (!Func)
37 return;
38 // Initialize argument blocks.
39 for (unsigned I = 0, N = Func->getNumWrittenParams(); I != N; ++I)
40 new (argBlock(I)) Block(S.EvalID, Func->getParamDescriptor(I).Desc);
41
42 if (Func->getFrameSize() == 0)
43 return;
44
45 for (auto &Scope : Func->scopes()) {
46 for (auto &Local : Scope.locals()) {
47 new (localBlock(Local.Offset)) Block(S.EvalID, Local.Desc);
48 // Note that we are NOT calling invokeCtor() here, since that is done
49 // via the InitScope op.
50 new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
51 }
52 }
53}
54
56 unsigned VarArgSize)
57 : InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {
58 // As per our calling convention, the this pointer is
59 // part of the ArgSize.
60 // If the function has RVO, the RVO pointer is first.
61 // If the fuction has a This pointer, that one is next.
62 // Then follow the actual arguments (but those are handled
63 // in getParamPointer()).
64 if (Func->hasRVO()) {
65 // RVO pointer offset is always 0.
66 }
67
68 if (Func->hasThisPointer())
69 ThisPointerOffset = Func->hasRVO() ? sizeof(Pointer) : 0;
70}
71
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(argBlock(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.
84}
85
87 if (!Func || Func->getFrameSize() == 0)
88 return;
89 for (auto &Scope : Func->scopes()) {
90 for (auto &Local : Scope.locals()) {
91 S.deallocate(localBlock(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(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(Idx)->IsActive = true;
115}
116
117void InterpFrame::destroy(unsigned Idx) {
118 for (auto &Local : Func->getScope(Idx).locals_reverse()) {
119 S.deallocate(localBlock(Local.Offset));
120 }
121}
122
123template <typename T>
124static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx,
125 QualType Ty) {
126 if constexpr (std::is_same_v<Pointer, T>) {
127 if (Ty->isPointerOrReferenceType())
128 V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
129 else {
130 if (std::optional<APValue> RValue = V.toRValue(ASTCtx, Ty))
131 RValue->printPretty(OS, ASTCtx, Ty);
132 else
133 OS << "...";
134 }
135 } else {
136 V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, 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>(FD);
150 MD && MD->getParent()->isAnonymousStructOrUnion())
151 return true;
152
153 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
154 Ctor && Ctor->isDefaulted() && Ctor->isTrivial() &&
155 Ctor->isCopyOrMoveConstructor() && Ctor->inits().empty())
156 return true;
157
158 return false;
159}
160
161void InterpFrame::describe(llvm::raw_ostream &OS) const {
162 assert(Func);
163 // For lambda static invokers, we would just print __invoke().
164 if (shouldSkipInBacktrace(Func))
165 return;
166
167 const Expr *CallExpr = Caller->getExpr(getRetPC());
168 const FunctionDecl *F = getCallee();
169 auto PrintingPolicy = S.getASTContext().getPrintingPolicy();
171
172 bool IsMemberCall = false;
173 bool ExplicitInstanceParam = false;
174 if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) {
175 IsMemberCall = !isa<CXXConstructorDecl>(MD) && !MD->isStatic();
176 ExplicitInstanceParam = MD->isExplicitObjectMemberFunction();
177 }
178
179 if (Func->hasThisPointer() && IsMemberCall) {
180 if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(CallExpr)) {
181 const Expr *Object = MCE->getImplicitObjectArgument();
182 Object->printPretty(OS, /*Helper=*/nullptr,
184 /*Indentation=*/0);
185 if (Object->getType()->isPointerType())
186 OS << "->";
187 else
188 OS << ".";
189 } else if (const auto *OCE =
190 dyn_cast_if_present<CXXOperatorCallExpr>(CallExpr)) {
191 OCE->getArg(0)->printPretty(OS, /*Helper=*/nullptr,
193 /*Indentation=*/0);
194 OS << ".";
195 } else if (const auto *M = dyn_cast<CXXMethodDecl>(F)) {
196 print(OS, getThis(), S.getASTContext(),
197 S.getASTContext().getLValueReferenceType(
198 S.getASTContext().getCanonicalTagType(M->getParent())));
199 OS << ".";
200 }
201 }
202
204 /*Qualified=*/false);
205 OS << '(';
206 unsigned Off = 0;
207
208 Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;
209 Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;
210 llvm::ListSeparator Comma;
211 for (const ParmVarDecl *Param :
212 F->parameters().slice(ExplicitInstanceParam)) {
213 OS << Comma;
214 QualType Ty = Param->getType();
215 PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr);
216
217 TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getASTContext(), Ty));
218 Off += align(primSize(PrimTy));
219 }
220 OS << ")";
221}
222
224 if (!Caller->Func) {
225 if (SourceRange NullRange = S.getRange(nullptr, {}); NullRange.isValid())
226 return NullRange;
227 return S.EvalLocation;
228 }
229
230 // Move up to the frame that has a valid location for the caller.
231 for (const InterpFrame *C = this; C; C = C->Caller) {
232 if (!C->RetPC)
233 continue;
234 SourceRange CallRange =
235 S.getRange(C->Caller->Func, C->RetPC - sizeof(uintptr_t));
236 if (CallRange.isValid())
237 return CallRange;
238 }
239 return S.EvalLocation;
240}
241
243 if (!Func)
244 return nullptr;
245 return Func->getDecl();
246}
247
248Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
249 assert(Offset < Func->getFrameSize() && "Invalid local offset.");
250 return Pointer(localBlock(Offset));
251}
252
253Block *InterpFrame::getLocalBlock(unsigned Offset) const {
254 return localBlock(Offset);
255}
256
258 assert(!isBottomFrame());
259
260 Block *B = argBlock(Index);
261
262 // Copy the initial value.
263 if (!B->isInitialized()) {
264 unsigned ByteOffset = Func->getParamDescriptor(Index).Offset;
265 assert(B->getDescriptor()->isPrimitive());
266 B->invokeCtor();
268 new (B->data()) T(stackRef<T>(ByteOffset)));
269 assert(B->isInitialized());
270 }
271
272 return Pointer(B);
273}
274
275static bool funcHasUsableBody(const Function *F) {
276 assert(F);
277
278 if (F->isConstructor() || F->isDestructor())
279 return true;
280
281 return !F->getDecl()->isImplicit();
282}
283
285 // Implicitly created functions don't have any code we could point at,
286 // so return the call site.
287 if (Func && !funcHasUsableBody(Func) && Caller)
288 return Caller->getSource(RetPC);
289
290 // Similarly, if the resulting source location is invalid anyway,
291 // point to the caller instead.
292 SourceInfo Result = S.getSource(Func, PC);
293 if (Result.getLoc().isInvalid() && Caller)
294 return Caller->getSource(RetPC);
295 return Result;
296}
297
299 if (Func && !funcHasUsableBody(Func) && Caller)
300 return Caller->getExpr(RetPC);
301
302 return S.getExpr(Func, PC);
303}
304
306 if (Func && !funcHasUsableBody(Func) && Caller)
307 return Caller->getLocation(RetPC);
308
309 return S.getLocation(Func, PC);
310}
311
313 if (Func && !funcHasUsableBody(Func) && Caller)
314 return Caller->getRange(RetPC);
315
316 return S.getRange(Func, PC);
317}
318
320 if (!Func)
321 return false;
322 for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
323 if (DC->isStdNamespace())
324 return true;
325
326 return false;
327}
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:223
llvm::json::Object Object
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:1462
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2122
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:3127
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:201
PrimType
Enumeration of the primitive types of the VM.
Definition PrimType.h:34
size_t primSize(PrimType Type)
Returns the size of a primitive type in bytes.
Definition PrimType.cpp:24
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...
Describes how types, statements, expressions, and declarations should be printed.
unsigned SuppressLambdaBody
Whether to suppress printing the body of a lambda.
bool isPrimitive() const
Checks if the descriptor is of a primitive.
Definition Descriptor.h:273
PrimType getPrimType() const
Definition Descriptor.h:241