Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/AST/ByteCode/InterpFrame.cpp
213799 views
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"
18
#include "clang/AST/ASTContext.h"
19
#include "clang/AST/DeclCXX.h"
20
#include "clang/AST/ExprCXX.h"
21
22
using namespace clang;
23
using namespace clang::interp;
24
25
InterpFrame::InterpFrame(InterpState &S)
26
: Caller(nullptr), S(S), Depth(0), Func(nullptr), RetPC(CodePtr()),
27
ArgSize(0), Args(nullptr), FrameOffset(0), IsBottom(true) {}
28
29
InterpFrame::InterpFrame(InterpState &S, const Function *Func,
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()), IsBottom(!Caller) {
34
if (!Func)
35
return;
36
37
unsigned FrameSize = Func->getFrameSize();
38
if (FrameSize == 0)
39
return;
40
41
Locals = std::make_unique<char[]>(FrameSize);
42
for (auto &Scope : Func->scopes()) {
43
for (auto &Local : Scope.locals()) {
44
new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);
45
// Note that we are NOT calling invokeCtor() here, since that is done
46
// via the InitScope op.
47
new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
48
}
49
}
50
}
51
52
InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,
53
unsigned VarArgSize)
54
: InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {
55
// As per our calling convention, the this pointer is
56
// part of the ArgSize.
57
// If the function has RVO, the RVO pointer is first.
58
// If the fuction has a This pointer, that one is next.
59
// Then follow the actual arguments (but those are handled
60
// in getParamPointer()).
61
if (Func->hasRVO())
62
RVOPtr = stackRef<Pointer>(0);
63
64
if (Func->hasThisPointer()) {
65
if (Func->hasRVO())
66
This = stackRef<Pointer>(sizeof(Pointer));
67
else
68
This = stackRef<Pointer>(0);
69
}
70
}
71
72
InterpFrame::~InterpFrame() {
73
for (auto &Param : Params)
74
S.deallocate(reinterpret_cast<Block *>(Param.second.get()));
75
76
// When destroying the InterpFrame, call the Dtor for all block
77
// that haven't been destroyed via a destroy() op yet.
78
// This happens when the execution is interruped midway-through.
79
destroyScopes();
80
}
81
82
void InterpFrame::destroyScopes() {
83
if (!Func)
84
return;
85
for (auto &Scope : Func->scopes()) {
86
for (auto &Local : Scope.locals()) {
87
S.deallocate(localBlock(Local.Offset));
88
}
89
}
90
}
91
92
void InterpFrame::initScope(unsigned Idx) {
93
if (!Func)
94
return;
95
for (auto &Local : Func->getScope(Idx).locals()) {
96
localBlock(Local.Offset)->invokeCtor();
97
}
98
}
99
100
void InterpFrame::destroy(unsigned Idx) {
101
for (auto &Local : Func->getScope(Idx).locals_reverse()) {
102
S.deallocate(localBlock(Local.Offset));
103
}
104
}
105
106
template <typename T>
107
static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx,
108
QualType Ty) {
109
if constexpr (std::is_same_v<Pointer, T>) {
110
if (Ty->isPointerOrReferenceType())
111
V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
112
else {
113
if (std::optional<APValue> RValue = V.toRValue(ASTCtx, Ty))
114
RValue->printPretty(OS, ASTCtx, Ty);
115
else
116
OS << "...";
117
}
118
} else {
119
V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
120
}
121
}
122
123
static bool shouldSkipInBacktrace(const Function *F) {
124
if (F->isLambdaStaticInvoker())
125
return true;
126
127
const FunctionDecl *FD = F->getDecl();
128
if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
129
FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
130
return true;
131
return false;
132
}
133
134
void InterpFrame::describe(llvm::raw_ostream &OS) const {
135
// For lambda static invokers, we would just print __invoke().
136
if (const auto *F = getFunction(); F && shouldSkipInBacktrace(F))
137
return;
138
139
const Expr *CallExpr = Caller->getExpr(getRetPC());
140
const FunctionDecl *F = getCallee();
141
bool IsMemberCall = isa<CXXMethodDecl>(F) && !isa<CXXConstructorDecl>(F) &&
142
cast<CXXMethodDecl>(F)->isImplicitObjectMemberFunction();
143
if (Func->hasThisPointer() && IsMemberCall) {
144
if (const auto *MCE = dyn_cast_if_present<CXXMemberCallExpr>(CallExpr)) {
145
const Expr *Object = MCE->getImplicitObjectArgument();
146
Object->printPretty(OS, /*Helper=*/nullptr,
147
S.getASTContext().getPrintingPolicy(),
148
/*Indentation=*/0);
149
if (Object->getType()->isPointerType())
150
OS << "->";
151
else
152
OS << ".";
153
} else if (const auto *OCE =
154
dyn_cast_if_present<CXXOperatorCallExpr>(CallExpr)) {
155
OCE->getArg(0)->printPretty(OS, /*Helper=*/nullptr,
156
S.getASTContext().getPrintingPolicy(),
157
/*Indentation=*/0);
158
OS << ".";
159
} else if (const auto *M = dyn_cast<CXXMethodDecl>(F)) {
160
print(OS, This, S.getASTContext(),
161
S.getASTContext().getLValueReferenceType(
162
S.getASTContext().getRecordType(M->getParent())));
163
OS << ".";
164
}
165
}
166
167
F->getNameForDiagnostic(OS, S.getASTContext().getPrintingPolicy(),
168
/*Qualified=*/false);
169
OS << '(';
170
unsigned Off = 0;
171
172
Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;
173
Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;
174
175
for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) {
176
QualType Ty = F->getParamDecl(I)->getType();
177
178
PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr);
179
180
TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getASTContext(), Ty));
181
Off += align(primSize(PrimTy));
182
if (I + 1 != N)
183
OS << ", ";
184
}
185
OS << ")";
186
}
187
188
Frame *InterpFrame::getCaller() const {
189
if (Caller->Caller)
190
return Caller;
191
return S.getSplitFrame();
192
}
193
194
SourceRange InterpFrame::getCallRange() const {
195
if (!Caller->Func) {
196
if (SourceRange NullRange = S.getRange(nullptr, {}); NullRange.isValid())
197
return NullRange;
198
return S.EvalLocation;
199
}
200
return S.getRange(Caller->Func, RetPC - sizeof(uintptr_t));
201
}
202
203
const FunctionDecl *InterpFrame::getCallee() const {
204
if (!Func)
205
return nullptr;
206
return Func->getDecl();
207
}
208
209
Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
210
assert(Offset < Func->getFrameSize() && "Invalid local offset.");
211
return Pointer(localBlock(Offset));
212
}
213
214
Pointer InterpFrame::getParamPointer(unsigned Off) {
215
// Return the block if it was created previously.
216
if (auto Pt = Params.find(Off); Pt != Params.end())
217
return Pointer(reinterpret_cast<Block *>(Pt->second.get()));
218
219
// Allocate memory to store the parameter and the block metadata.
220
const auto &Desc = Func->getParamDescriptor(Off);
221
size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();
222
auto Memory = std::make_unique<char[]>(BlockSize);
223
auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), Desc.second);
224
B->invokeCtor();
225
226
// Copy the initial value.
227
TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off)));
228
229
// Record the param.
230
Params.insert({Off, std::move(Memory)});
231
return Pointer(B);
232
}
233
234
static bool funcHasUsableBody(const Function *F) {
235
assert(F);
236
237
if (F->isConstructor() || F->isDestructor())
238
return true;
239
240
return !F->getDecl()->isImplicit();
241
}
242
243
SourceInfo InterpFrame::getSource(CodePtr PC) const {
244
// Implicitly created functions don't have any code we could point at,
245
// so return the call site.
246
if (Func && !funcHasUsableBody(Func) && Caller)
247
return Caller->getSource(RetPC);
248
249
// Similarly, if the resulting source location is invalid anyway,
250
// point to the caller instead.
251
SourceInfo Result = S.getSource(Func, PC);
252
if (Result.getLoc().isInvalid() && Caller)
253
return Caller->getSource(RetPC);
254
return Result;
255
}
256
257
const Expr *InterpFrame::getExpr(CodePtr PC) const {
258
if (Func && !funcHasUsableBody(Func) && Caller)
259
return Caller->getExpr(RetPC);
260
261
return S.getExpr(Func, PC);
262
}
263
264
SourceLocation InterpFrame::getLocation(CodePtr PC) const {
265
if (Func && !funcHasUsableBody(Func) && Caller)
266
return Caller->getLocation(RetPC);
267
268
return S.getLocation(Func, PC);
269
}
270
271
SourceRange InterpFrame::getRange(CodePtr PC) const {
272
if (Func && !funcHasUsableBody(Func) && Caller)
273
return Caller->getRange(RetPC);
274
275
return S.getRange(Func, PC);
276
}
277
278
bool InterpFrame::isStdFunction() const {
279
if (!Func)
280
return false;
281
for (const DeclContext *DC = Func->getDecl(); DC; DC = DC->getParent())
282
if (DC->isStdNamespace())
283
return true;
284
285
return false;
286
}
287
288