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"
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), Func(nullptr), RetPC(CodePtr()), Args(nullptr),
27 ArgSize(0), Depth(0) {}
28
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(I)) Block(S.EvalID, Func->getParamDescriptor(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(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(Local.Offset)) InlineDescriptor(Local.Desc);
56 }
57 }
58}
59
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
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 assert(!localBlock(Local.Offset)->isInitialized());
101 localBlock(Local.Offset)->invokeCtor();
102 }
103}
104
105void InterpFrame::enableLocal(unsigned Idx) {
106 assert(Func);
107
108 // FIXME: This is a little dirty, but to avoid adding a flag to
109 // InlineDescriptor that's only ever useful on the toplevel of local
110 // variables, we reuse the IsActive flag for the enabled state. We should
111 // probably use a different struct than InlineDescriptor for the block-level
112 // inline descriptor of local varaibles.
113 localInlineDesc(Idx)->IsActive = true;
114}
115
116void InterpFrame::destroy(unsigned Idx) {
117 for (auto &Local : Func->getScope(Idx).locals_reverse()) {
118 S.deallocate(localBlock(Local.Offset));
119 }
120}
121
122template <typename T>
123static void print(llvm::raw_ostream &OS, const T &V, const Context &Ctx,
124 QualType Ty) {
125 if constexpr (std::is_same_v<Pointer, T>) {
126 if (Ty->isPointerOrReferenceType())
127 V.toAPValue(Ctx.getASTContext()).printPretty(OS, Ctx.getASTContext(), Ty);
128 else {
129 if (std::optional<APValue> RValue = V.toRValue(Ctx, Ty))
130 RValue->printPretty(OS, Ctx.getASTContext(), Ty);
131 else
132 OS << "...";
133 }
134 } else {
135 V.toAPValue(Ctx.getASTContext()).printPretty(OS, Ctx.getASTContext(), Ty);
136 }
137}
138
139static bool shouldSkipInBacktrace(const Function *F) {
140 if (F->isLambdaStaticInvoker())
141 return true;
142
143 const FunctionDecl *FD = F->getDecl();
144 if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
145 FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
146 return true;
147
148 if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
149 MD && MD->getParent()->isAnonymousStructOrUnion())
150 return true;
151
152 return false;
153}
154
155void InterpFrame::describe(llvm::raw_ostream &OS) const {
156 assert(Func);
157 // For lambda static invokers, we would just print __invoke().
158 if (shouldSkipInBacktrace(Func))
159 return;
160
161 const ASTContext &ASTCtx = S.getASTContext();
162 const Expr *CallExpr = Caller->getExpr(getRetOpPC());
163 const FunctionDecl *F = Func->getDecl();
164 auto PrintingPolicy = ASTCtx.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.getContext(),
193 ASTCtx.getCanonicalTagType(M->getParent())));
194 OS << '.';
195 }
196 }
197
198 F->getNameForDiagnostic(OS, PrintingPolicy, /*Qualified=*/false);
199 OS << '(';
200 unsigned Off = 0;
201 unsigned ParamIndex = ExplicitInstanceParam;
202 Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;
203 Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;
204 llvm::ListSeparator Comma;
205 for (const ParmVarDecl *Param :
206 F->parameters().slice(ExplicitInstanceParam)) {
207 OS << Comma;
208 PrimType PrimT = Func->getParamDescriptor(ParamIndex).T;
209 TYPE_SWITCH(PrimT,
210 print(OS, stackRef<T>(Off), S.getContext(), Param->getType()));
211 Off += align(primSize(PrimT));
212 ++ParamIndex;
213 }
214 OS << ')';
215}
216
218 if (!Caller->Func) {
219 if (SourceRange NullRange = S.getSource({}).getRange(); NullRange.isValid())
220 return NullRange;
221
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()).getRange();
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 if (!Func)
281 return S.getSource(PC);
282
283 // Implicitly created functions don't have any code we could point at,
284 // so return the call site.
285 if (Func && !funcHasUsableBody(Func) && Caller)
286 return Caller->getSource(getRetOpPC());
287
288 // Similarly, if the resulting source location is invalid anyway,
289 // point to the caller instead.
290 SourceInfo Result = Func->getSource(PC);
291 if (Result.getLoc().isInvalid() && Caller)
292 return Caller->getSource(getRetOpPC());
293
294 return Result;
295}
296
298 if (!Func)
299 return false;
300 for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
301 if (DC->isStdNamespace())
302 return true;
303
304 return false;
305}
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:235
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:881
CanQualType getCanonicalTagType(const TagDecl *TD) const
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2987
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:113
Represents a function declaration or definition.
Definition Decl.h:2059
ArrayRef< ParmVarDecl * > parameters() const
Definition Decl.h:2905
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:3113
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition Decl.h:341
Represents a parameter to a function.
Definition Decl.h:1820
A (possibly-)qualified type.
Definition TypeBase.h:938
A trivial tuple used to represent a source range.
bool isPointerOrReferenceType() const
Definition TypeBase.h:8742
A memory block, either on the stack or in the heap.
Definition InterpBlock.h:43
std::byte * data()
Returns a pointer to the stored data.
const Descriptor * getDescriptor() const
Returns the block's descriptor.
Definition InterpBlock.h:77
static constexpr uint8_t InlineDescMD
Definition InterpBlock.h:51
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:98
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:98
CodePtr getRetOpPC() const
Returns the return address of the opcode in the caller frame.
InterpFrame(InterpState &S)
Bottom Frame.
InterpFrame * Caller
The frame of the previous function.
SourceInfo getSource(CodePtr PC) const
Map a location to a source.
void enableLocal(unsigned Idx)
Block * getLocalBlock(unsigned Offset) const
~InterpFrame()
Destroys the frame, killing all live pointers to stack slots.
const Pointer & getThis() const
Returns the 'this' pointer.
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:531
Describes a scope block.
Definition Function.h:35
llvm::iterator_range< LocalVectorTy::const_iterator > locals() const
Definition Function.h:51
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:213
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
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.
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:260
PrimType getPrimType() const
Definition Descriptor.h:231
Inline descriptor embedded in structures and arrays.
Definition Descriptor.h:67