Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Interpreter/InterpreterValuePrinter.cpp
213766 views
1
//===--- InterpreterValuePrinter.cpp - Value printing utils -----*- 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
// This file implements routines for in-process value printing in clang-repl.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "IncrementalParser.h"
14
#include "InterpreterUtils.h"
15
#include "clang/AST/ASTContext.h"
16
#include "clang/AST/PrettyPrinter.h"
17
#include "clang/AST/Type.h"
18
#include "clang/Frontend/CompilerInstance.h"
19
#include "clang/Interpreter/Interpreter.h"
20
#include "clang/Interpreter/Value.h"
21
#include "clang/Sema/Lookup.h"
22
#include "clang/Sema/Sema.h"
23
24
#include "llvm/Support/Error.h"
25
#include "llvm/Support/raw_ostream.h"
26
27
#include <cassert>
28
29
#include <cstdarg>
30
31
namespace clang {
32
33
llvm::Expected<llvm::orc::ExecutorAddr>
34
Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {
35
assert(CXXRD && "Cannot compile a destructor for a nullptr");
36
if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())
37
return Dtor->getSecond();
38
39
if (CXXRD->hasIrrelevantDestructor())
40
return llvm::orc::ExecutorAddr{};
41
42
CXXDestructorDecl *DtorRD =
43
getCompilerInstance()->getSema().LookupDestructor(CXXRD);
44
45
llvm::StringRef Name =
46
getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));
47
auto AddrOrErr = getSymbolAddress(Name);
48
if (!AddrOrErr)
49
return AddrOrErr.takeError();
50
51
Dtors[CXXRD] = *AddrOrErr;
52
return AddrOrErr;
53
}
54
55
enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };
56
57
class InterfaceKindVisitor
58
: public TypeVisitor<InterfaceKindVisitor, InterfaceKind> {
59
60
Sema &S;
61
Expr *E;
62
llvm::SmallVectorImpl<Expr *> &Args;
63
64
public:
65
InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)
66
: S(S), E(E), Args(Args) {}
67
68
InterfaceKind computeInterfaceKind(QualType Ty) {
69
return Visit(Ty.getTypePtr());
70
}
71
72
InterfaceKind VisitRecordType(const RecordType *Ty) {
73
return InterfaceKind::WithAlloc;
74
}
75
76
InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {
77
return InterfaceKind::WithAlloc;
78
}
79
80
InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {
81
return InterfaceKind::CopyArray;
82
}
83
84
InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {
85
HandlePtrType(Ty);
86
return InterfaceKind::NoAlloc;
87
}
88
89
InterfaceKind VisitPointerType(const PointerType *Ty) {
90
HandlePtrType(Ty);
91
return InterfaceKind::NoAlloc;
92
}
93
94
InterfaceKind VisitReferenceType(const ReferenceType *Ty) {
95
ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);
96
assert(!AddrOfE.isInvalid() && "Can not create unary expression");
97
Args.push_back(AddrOfE.get());
98
return InterfaceKind::NoAlloc;
99
}
100
101
InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {
102
if (Ty->isNullPtrType())
103
Args.push_back(E);
104
else if (Ty->isFloatingType())
105
Args.push_back(E);
106
else if (Ty->isIntegralOrEnumerationType())
107
HandleIntegralOrEnumType(Ty);
108
else if (Ty->isVoidType()) {
109
// Do we need to still run `E`?
110
}
111
112
return InterfaceKind::NoAlloc;
113
}
114
115
InterfaceKind VisitEnumType(const EnumType *Ty) {
116
HandleIntegralOrEnumType(Ty);
117
return InterfaceKind::NoAlloc;
118
}
119
120
private:
121
// Force cast these types to the uint that fits the register size. That way we
122
// reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.
123
void HandleIntegralOrEnumType(const Type *Ty) {
124
ASTContext &Ctx = S.getASTContext();
125
uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);
126
QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);
127
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);
128
ExprResult CastedExpr =
129
S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
130
assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");
131
Args.push_back(CastedExpr.get());
132
}
133
134
void HandlePtrType(const Type *Ty) {
135
ASTContext &Ctx = S.getASTContext();
136
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);
137
ExprResult CastedExpr =
138
S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);
139
assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");
140
Args.push_back(CastedExpr.get());
141
}
142
};
143
144
// This synthesizes a call expression to a speciall
145
// function that is responsible for generating the Value.
146
// In general, we transform:
147
// clang-repl> x
148
// To:
149
// // 1. If x is a built-in type like int, float.
150
// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);
151
// // 2. If x is a struct, and a lvalue.
152
// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,
153
// &x);
154
// // 3. If x is a struct, but a rvalue.
155
// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,
156
// xQualType)) (x);
157
llvm::Expected<Expr *> Interpreter::ExtractValueFromExpr(Expr *E) {
158
Sema &S = getCompilerInstance()->getSema();
159
ASTContext &Ctx = S.getASTContext();
160
161
// Find the value printing builtins.
162
if (!ValuePrintingInfo[0]) {
163
assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; }));
164
165
auto LookupInterface = [&](Expr *&Interface,
166
llvm::StringRef Name) -> llvm::Error {
167
LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),
168
Sema::LookupOrdinaryName,
169
RedeclarationKind::ForVisibleRedeclaration);
170
S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());
171
if (R.empty())
172
return llvm::make_error<llvm::StringError>(
173
Name + " not found!", llvm::inconvertibleErrorCode());
174
175
CXXScopeSpec CSS;
176
Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();
177
return llvm::Error::success();
178
};
179
static constexpr llvm::StringRef Builtin[] = {
180
"__clang_Interpreter_SetValueNoAlloc",
181
"__clang_Interpreter_SetValueWithAlloc",
182
"__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};
183
if (llvm::Error Err =
184
LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))
185
return std::move(Err);
186
187
if (Ctx.getLangOpts().CPlusPlus) {
188
if (llvm::Error Err =
189
LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))
190
return std::move(Err);
191
if (llvm::Error Err =
192
LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))
193
return std::move(Err);
194
if (llvm::Error Err =
195
LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))
196
return std::move(Err);
197
}
198
}
199
200
llvm::SmallVector<Expr *, 4> AdjustedArgs;
201
// Create parameter `ThisInterp`.
202
AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this));
203
204
// Create parameter `OutVal`.
205
AdjustedArgs.push_back(
206
CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue));
207
208
// Build `__clang_Interpreter_SetValue*` call.
209
210
// Get rid of ExprWithCleanups.
211
if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
212
E = EWC->getSubExpr();
213
214
QualType Ty = E->getType();
215
QualType DesugaredTy = Ty.getDesugaredType(Ctx);
216
217
// For lvalue struct, we treat it as a reference.
218
if (DesugaredTy->isRecordType() && E->isLValue()) {
219
DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);
220
Ty = Ctx.getLValueReferenceType(Ty);
221
}
222
223
Expr *TypeArg =
224
CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
225
// The QualType parameter `OpaqueType`, represented as `void*`.
226
AdjustedArgs.push_back(TypeArg);
227
228
// We push the last parameter based on the type of the Expr. Note we need
229
// special care for rvalue struct.
230
InterfaceKindVisitor V(S, E, AdjustedArgs);
231
Scope *Scope = nullptr;
232
ExprResult SetValueE;
233
InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);
234
switch (Kind) {
235
case InterfaceKind::WithAlloc:
236
LLVM_FALLTHROUGH;
237
case InterfaceKind::CopyArray: {
238
// __clang_Interpreter_SetValueWithAlloc.
239
ExprResult AllocCall =
240
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],
241
E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
242
assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");
243
244
TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());
245
246
// Force CodeGen to emit destructor.
247
if (auto *RD = Ty->getAsCXXRecordDecl()) {
248
auto *Dtor = S.LookupDestructor(RD);
249
Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
250
getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
251
DeclGroupRef(Dtor));
252
}
253
254
// __clang_Interpreter_SetValueCopyArr.
255
if (Kind == InterfaceKind::CopyArray) {
256
const auto *ConstantArrTy =
257
cast<ConstantArrayType>(DesugaredTy.getTypePtr());
258
size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
259
Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
260
Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
261
SetValueE =
262
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],
263
SourceLocation(), Args, SourceLocation());
264
}
265
Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};
266
ExprResult CXXNewCall = S.BuildCXXNew(
267
E->getSourceRange(),
268
/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
269
/*PlacementRParen=*/SourceLocation(),
270
/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
271
E->getSourceRange(), E);
272
273
assert(!CXXNewCall.isInvalid() &&
274
"Can't create runtime placement new call!");
275
276
SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),
277
/*DiscardedValue=*/false);
278
break;
279
}
280
// __clang_Interpreter_SetValueNoAlloc.
281
case InterfaceKind::NoAlloc: {
282
SetValueE =
283
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],
284
E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
285
break;
286
}
287
default:
288
llvm_unreachable("Unhandled InterfaceKind");
289
}
290
291
// It could fail, like printing an array type in C. (not supported)
292
if (SetValueE.isInvalid())
293
return E;
294
295
return SetValueE.get();
296
}
297
298
} // namespace clang
299
300
using namespace clang;
301
302
// Temporary rvalue struct that need special care.
303
REPL_EXTERNAL_VISIBILITY void *
304
__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
305
void *OpaqueType) {
306
Value &VRef = *(Value *)OutVal;
307
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
308
return VRef.getPtr();
309
}
310
311
extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
312
void *This, void *OutVal, void *OpaqueType, ...) {
313
Value &VRef = *(Value *)OutVal;
314
Interpreter *I = static_cast<Interpreter *>(This);
315
VRef = Value(I, OpaqueType);
316
if (VRef.isVoid())
317
return;
318
319
va_list args;
320
va_start(args, /*last named param*/ OpaqueType);
321
322
QualType QT = VRef.getType();
323
if (VRef.getKind() == Value::K_PtrOrObj) {
324
VRef.setPtr(va_arg(args, void *));
325
} else {
326
if (const auto *ET = QT->getAs<EnumType>())
327
QT = ET->getDecl()->getIntegerType();
328
switch (QT->castAs<BuiltinType>()->getKind()) {
329
default:
330
llvm_unreachable("unknown type kind!");
331
break;
332
// Types shorter than int are resolved as int, else va_arg has UB.
333
case BuiltinType::Bool:
334
VRef.setBool(va_arg(args, int));
335
break;
336
case BuiltinType::Char_S:
337
VRef.setChar_S(va_arg(args, int));
338
break;
339
case BuiltinType::SChar:
340
VRef.setSChar(va_arg(args, int));
341
break;
342
case BuiltinType::Char_U:
343
VRef.setChar_U(va_arg(args, unsigned));
344
break;
345
case BuiltinType::UChar:
346
VRef.setUChar(va_arg(args, unsigned));
347
break;
348
case BuiltinType::Short:
349
VRef.setShort(va_arg(args, int));
350
break;
351
case BuiltinType::UShort:
352
VRef.setUShort(va_arg(args, unsigned));
353
break;
354
case BuiltinType::Int:
355
VRef.setInt(va_arg(args, int));
356
break;
357
case BuiltinType::UInt:
358
VRef.setUInt(va_arg(args, unsigned));
359
break;
360
case BuiltinType::Long:
361
VRef.setLong(va_arg(args, long));
362
break;
363
case BuiltinType::ULong:
364
VRef.setULong(va_arg(args, unsigned long));
365
break;
366
case BuiltinType::LongLong:
367
VRef.setLongLong(va_arg(args, long long));
368
break;
369
case BuiltinType::ULongLong:
370
VRef.setULongLong(va_arg(args, unsigned long long));
371
break;
372
// Types shorter than double are resolved as double, else va_arg has UB.
373
case BuiltinType::Float:
374
VRef.setFloat(va_arg(args, double));
375
break;
376
case BuiltinType::Double:
377
VRef.setDouble(va_arg(args, double));
378
break;
379
case BuiltinType::LongDouble:
380
VRef.setLongDouble(va_arg(args, long double));
381
break;
382
// See REPL_BUILTIN_TYPES.
383
}
384
}
385
va_end(args);
386
}
387
388
// A trampoline to work around the fact that operator placement new cannot
389
// really be forward declared due to libc++ and libstdc++ declaration mismatch.
390
// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same
391
// definition in the interpreter runtime. We should move it in a runtime header
392
// which gets included by the interpreter and here.
393
struct __clang_Interpreter_NewTag {};
394
REPL_EXTERNAL_VISIBILITY void *
395
operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {
396
// Just forward to the standard operator placement new.
397
return operator new(__sz, __p);
398
}
399
400