clang 24.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) {}
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#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(I)) Block(S.EvalID, Func->getParamDescriptor(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(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(Local.Offset)) InlineDescriptor(Local.Desc);
57 }
58 }
59}
60
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
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, 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.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>(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 ASTContext &ASTCtx = S.getASTContext();
163 const Expr *CallExpr = Caller->getExpr(getRetOpPC());
164 const FunctionDecl *F = getCallee();
165 auto PrintingPolicy = ASTCtx.getPrintingPolicy();
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,
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,
189 /*Indentation=*/0);
190 OS << '.';
191 } else if (const auto *M = dyn_cast<CXXMethodDecl>(F)) {
192 print(OS, getThis(), S.getContext(),
194 ASTCtx.getCanonicalTagType(M->getParent())));
195 OS << '.';
196 }
197 }
198
199 F->getNameForDiagnostic(OS, PrintingPolicy, /*Qualified=*/false);
200 OS << '(';
201 unsigned Off = 0;
202 unsigned ParamIndex = ExplicitInstanceParam;
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 PrimType PrimT = Func->getParamDescriptor(ParamIndex).T;
210 TYPE_SWITCH(PrimT,
211 print(OS, stackRef<T>(Off), S.getContext(), Param->getType()));
212 Off += align(primSize(PrimT));
213 ++ParamIndex;
214 }
215 OS << ')';
216}
217
219 if (!Caller->Func) {
220 if (SourceRange NullRange = S.getRange({}); 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 C->Caller->Func->getSource(C->getRetOpPC() - sizeof(uintptr_t))
231 .getRange();
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 if (!Func)
282 return S.getSource(PC);
283
284 // Implicitly created functions don't have any code we could point at,
285 // so return the call site.
286 if (Func && !funcHasUsableBody(Func) && Caller)
287 return Caller->getSource(getRetOpPC());
288
289 // Similarly, if the resulting source location is invalid anyway,
290 // point to the caller instead.
291 SourceInfo Result = Func->getSource(PC);
292 if (Result.getLoc().isInvalid() && Caller)
293 return Caller->getSource(getRetOpPC());
294
295 return Result;
296}
297
299 if (!Func)
300 return S.getExpr(PC);
301
302 if (!funcHasUsableBody(Func) && Caller)
303 return Caller->getExpr(getRetOpPC());
304
305 return Func->getSource(PC).asExpr();
306}
307
309 if (!Func)
310 return S.getLocation(PC);
311 if (!funcHasUsableBody(Func) && Caller)
312 return Caller->getLocation(getRetOpPC());
313
314 return Func->getSource(PC).getLoc();
315}
316
318 if (!Func)
319 return S.getRange(PC);
320
321 if (!funcHasUsableBody(Func) && Caller)
322 return Caller->getRange(getRetOpPC());
323
324 return Func->getSource(PC).getRange();
325}
326
328 if (!Func)
329 return false;
330 for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
331 if (DC->isStdNamespace())
332 return true;
333
334 return false;
335}
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 bool shouldSkipInBacktrace(const Function *F)
static void print(llvm::raw_ostream &OS, const T &V, const Context &Ctx, QualType Ty)
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:223
QualType getLValueReferenceType(QualType T, bool SpelledAsLValue=true) const
Return the uniqued reference to the type for an lvalue reference to the specified type.
const clang::PrintingPolicy & getPrintingPolicy() const
Definition ASTContext.h:876
CanQualType getCanonicalTagType(const TagDecl *TD) const
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2954
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition DeclBase.h:1466
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2126
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:2058
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2904
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:3112
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:1819
A (possibly-)qualified type.
Definition TypeBase.h:938
Encodes a location in the source.
A trivial tuple used to represent a source range.
bool isPointerOrReferenceType() const
Definition TypeBase.h:8745
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:31
Holds all information required to evaluate constexpr code in a module.
Definition Context.h:47
ASTContext & getASTContext() const
Returns the AST context.
Definition Context.h:107
Bytecode function.
Definition Function.h:99
CodePtr getRetOpPC() const
Returns the return address of the opcode in the caller frame.
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.
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
unsigned getArgSize() 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:43
A pointer to a memory block, live or dead.
Definition Pointer.h:405
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:77
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
Top level wrappers for InstallAPI frontend operations.
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:906
const FunctionProtoType * T
@ Off
Never emit colors regardless of the output stream.
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:272
PrimType getPrimType() const
Definition Descriptor.h:240