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 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(Func))
160 return;
161
162 const Expr *CallExpr = Caller->getExpr(getRetPC());
163 const FunctionDecl *F = getCallee();
164 auto PrintingPolicy = S.getASTContext().getPrintingPolicy();
166
167 bool IsMemberCall = false;
168 bool ExplicitInstanceParam = false;
169 if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) {
170 IsMemberCall = !isa<CXXConstructorDecl>(MD) && !MD->isStatic();
171 ExplicitInstanceParam = MD->isExplicitObjectMemberFunction();
172 }
173
174 if (Func->hasThisPointer() && IsMemberCall) {
175 if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(CallExpr)) {
176 const Expr *Object = MCE->getImplicitObjectArgument();
177 Object->printPretty(OS, /*Helper=*/nullptr,
179 /*Indentation=*/0);
180 if (Object->getType()->isPointerType())
181 OS << "->";
182 else
183 OS << ".";
184 } else if (const auto *OCE =
185 dyn_cast_if_present<CXXOperatorCallExpr>(CallExpr)) {
186 OCE->getArg(0)->printPretty(OS, /*Helper=*/nullptr,
188 /*Indentation=*/0);
189 OS << ".";
190 } else if (const auto *M = dyn_cast<CXXMethodDecl>(F)) {
191 print(OS, getThis(), S.getASTContext(),
192 S.getASTContext().getLValueReferenceType(
193 S.getASTContext().getCanonicalTagType(M->getParent())));
194 OS << ".";
195 }
196 }
197
199 /*Qualified=*/false);
200 OS << '(';
201 unsigned Off = 0;
202
203 Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;
204 Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;
205 llvm::ListSeparator Comma;
206 for (const ParmVarDecl *Param :
207 F->parameters().slice(ExplicitInstanceParam)) {
208 OS << Comma;
209 QualType Ty = Param->getType();
210 PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr);
211
212 TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getASTContext(), Ty));
213 Off += align(primSize(PrimTy));
214 }
215 OS << ")";
216}
217
219 if (!Caller->Func) {
220 if (SourceRange NullRange = S.getRange(nullptr, {}); 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(C->Caller->Func, C->RetPC - sizeof(uintptr_t));
231 if (CallRange.isValid())
232 return CallRange;
233 }
234 return S.EvalLocation;
235}
236
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
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();
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
280 // Implicitly created functions don't have any code we could point at,
281 // so return the call site.
282 if (Func && !funcHasUsableBody(Func) && Caller)
283 return Caller->getSource(RetPC);
284
285 // Similarly, if the resulting source location is invalid anyway,
286 // point to the caller instead.
287 SourceInfo Result = S.getSource(Func, PC);
288 if (Result.getLoc().isInvalid() && Caller)
289 return Caller->getSource(RetPC);
290 return Result;
291}
292
294 if (Func && !funcHasUsableBody(Func) && Caller)
295 return Caller->getExpr(RetPC);
296
297 return S.getExpr(Func, PC);
298}
299
301 if (Func && !funcHasUsableBody(Func) && Caller)
302 return Caller->getLocation(RetPC);
303
304 return S.getLocation(Func, PC);
305}
306
308 if (Func && !funcHasUsableBody(Func) && Caller)
309 return Caller->getRange(RetPC);
310
311 return S.getRange(Func, PC);
312}
313
315 if (!Func)
316 return false;
317 for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
318 if (DC->isStdNamespace())
319 return true;
320
321 return false;
322}
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)
llvm::json::Object Object
#define TYPE_SWITCH(Expr, B)
Definition PrimType.h:223
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:227
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:2018
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2792
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:3120
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:1808
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:8677
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:274
PrimType getPrimType() const
Definition Descriptor.h:242