Path: blob/main/contrib/llvm-project/clang/lib/Interpreter/InterpreterValuePrinter.cpp
213766 views
//===--- InterpreterValuePrinter.cpp - Value printing utils -----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements routines for in-process value printing in clang-repl.9//10//===----------------------------------------------------------------------===//1112#include "IncrementalParser.h"13#include "InterpreterUtils.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/PrettyPrinter.h"16#include "clang/AST/Type.h"17#include "clang/Frontend/CompilerInstance.h"18#include "clang/Interpreter/Interpreter.h"19#include "clang/Interpreter/Value.h"20#include "clang/Sema/Lookup.h"21#include "clang/Sema/Sema.h"2223#include "llvm/Support/Error.h"24#include "llvm/Support/raw_ostream.h"2526#include <cassert>2728#include <cstdarg>2930namespace clang {3132llvm::Expected<llvm::orc::ExecutorAddr>33Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) {34assert(CXXRD && "Cannot compile a destructor for a nullptr");35if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end())36return Dtor->getSecond();3738if (CXXRD->hasIrrelevantDestructor())39return llvm::orc::ExecutorAddr{};4041CXXDestructorDecl *DtorRD =42getCompilerInstance()->getSema().LookupDestructor(CXXRD);4344llvm::StringRef Name =45getCodeGen()->GetMangledName(GlobalDecl(DtorRD, Dtor_Base));46auto AddrOrErr = getSymbolAddress(Name);47if (!AddrOrErr)48return AddrOrErr.takeError();4950Dtors[CXXRD] = *AddrOrErr;51return AddrOrErr;52}5354enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };5556class InterfaceKindVisitor57: public TypeVisitor<InterfaceKindVisitor, InterfaceKind> {5859Sema &S;60Expr *E;61llvm::SmallVectorImpl<Expr *> &Args;6263public:64InterfaceKindVisitor(Sema &S, Expr *E, llvm::SmallVectorImpl<Expr *> &Args)65: S(S), E(E), Args(Args) {}6667InterfaceKind computeInterfaceKind(QualType Ty) {68return Visit(Ty.getTypePtr());69}7071InterfaceKind VisitRecordType(const RecordType *Ty) {72return InterfaceKind::WithAlloc;73}7475InterfaceKind VisitMemberPointerType(const MemberPointerType *Ty) {76return InterfaceKind::WithAlloc;77}7879InterfaceKind VisitConstantArrayType(const ConstantArrayType *Ty) {80return InterfaceKind::CopyArray;81}8283InterfaceKind VisitFunctionProtoType(const FunctionProtoType *Ty) {84HandlePtrType(Ty);85return InterfaceKind::NoAlloc;86}8788InterfaceKind VisitPointerType(const PointerType *Ty) {89HandlePtrType(Ty);90return InterfaceKind::NoAlloc;91}9293InterfaceKind VisitReferenceType(const ReferenceType *Ty) {94ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E);95assert(!AddrOfE.isInvalid() && "Can not create unary expression");96Args.push_back(AddrOfE.get());97return InterfaceKind::NoAlloc;98}99100InterfaceKind VisitBuiltinType(const BuiltinType *Ty) {101if (Ty->isNullPtrType())102Args.push_back(E);103else if (Ty->isFloatingType())104Args.push_back(E);105else if (Ty->isIntegralOrEnumerationType())106HandleIntegralOrEnumType(Ty);107else if (Ty->isVoidType()) {108// Do we need to still run `E`?109}110111return InterfaceKind::NoAlloc;112}113114InterfaceKind VisitEnumType(const EnumType *Ty) {115HandleIntegralOrEnumType(Ty);116return InterfaceKind::NoAlloc;117}118119private:120// Force cast these types to the uint that fits the register size. That way we121// reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`.122void HandleIntegralOrEnumType(const Type *Ty) {123ASTContext &Ctx = S.getASTContext();124uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy);125QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits);126TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy);127ExprResult CastedExpr =128S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);129assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr");130Args.push_back(CastedExpr.get());131}132133void HandlePtrType(const Type *Ty) {134ASTContext &Ctx = S.getASTContext();135TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy);136ExprResult CastedExpr =137S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E);138assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression");139Args.push_back(CastedExpr.get());140}141};142143// This synthesizes a call expression to a speciall144// function that is responsible for generating the Value.145// In general, we transform:146// clang-repl> x147// To:148// // 1. If x is a built-in type like int, float.149// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x);150// // 2. If x is a struct, and a lvalue.151// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType,152// &x);153// // 3. If x is a struct, but a rvalue.154// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue,155// xQualType)) (x);156llvm::Expected<Expr *> Interpreter::ExtractValueFromExpr(Expr *E) {157Sema &S = getCompilerInstance()->getSema();158ASTContext &Ctx = S.getASTContext();159160// Find the value printing builtins.161if (!ValuePrintingInfo[0]) {162assert(llvm::all_of(ValuePrintingInfo, [](Expr *E) { return !E; }));163164auto LookupInterface = [&](Expr *&Interface,165llvm::StringRef Name) -> llvm::Error {166LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(),167Sema::LookupOrdinaryName,168RedeclarationKind::ForVisibleRedeclaration);169S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl());170if (R.empty())171return llvm::make_error<llvm::StringError>(172Name + " not found!", llvm::inconvertibleErrorCode());173174CXXScopeSpec CSS;175Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get();176return llvm::Error::success();177};178static constexpr llvm::StringRef Builtin[] = {179"__clang_Interpreter_SetValueNoAlloc",180"__clang_Interpreter_SetValueWithAlloc",181"__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};182if (llvm::Error Err =183LookupInterface(ValuePrintingInfo[NoAlloc], Builtin[NoAlloc]))184return std::move(Err);185186if (Ctx.getLangOpts().CPlusPlus) {187if (llvm::Error Err =188LookupInterface(ValuePrintingInfo[WithAlloc], Builtin[WithAlloc]))189return std::move(Err);190if (llvm::Error Err =191LookupInterface(ValuePrintingInfo[CopyArray], Builtin[CopyArray]))192return std::move(Err);193if (llvm::Error Err =194LookupInterface(ValuePrintingInfo[NewTag], Builtin[NewTag]))195return std::move(Err);196}197}198199llvm::SmallVector<Expr *, 4> AdjustedArgs;200// Create parameter `ThisInterp`.201AdjustedArgs.push_back(CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this));202203// Create parameter `OutVal`.204AdjustedArgs.push_back(205CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue));206207// Build `__clang_Interpreter_SetValue*` call.208209// Get rid of ExprWithCleanups.210if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))211E = EWC->getSubExpr();212213QualType Ty = E->getType();214QualType DesugaredTy = Ty.getDesugaredType(Ctx);215216// For lvalue struct, we treat it as a reference.217if (DesugaredTy->isRecordType() && E->isLValue()) {218DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);219Ty = Ctx.getLValueReferenceType(Ty);220}221222Expr *TypeArg =223CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());224// The QualType parameter `OpaqueType`, represented as `void*`.225AdjustedArgs.push_back(TypeArg);226227// We push the last parameter based on the type of the Expr. Note we need228// special care for rvalue struct.229InterfaceKindVisitor V(S, E, AdjustedArgs);230Scope *Scope = nullptr;231ExprResult SetValueE;232InterfaceKind Kind = V.computeInterfaceKind(DesugaredTy);233switch (Kind) {234case InterfaceKind::WithAlloc:235LLVM_FALLTHROUGH;236case InterfaceKind::CopyArray: {237// __clang_Interpreter_SetValueWithAlloc.238ExprResult AllocCall =239S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::WithAlloc],240E->getBeginLoc(), AdjustedArgs, E->getEndLoc());241assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");242243TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());244245// Force CodeGen to emit destructor.246if (auto *RD = Ty->getAsCXXRecordDecl()) {247auto *Dtor = S.LookupDestructor(RD);248Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));249getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(250DeclGroupRef(Dtor));251}252253// __clang_Interpreter_SetValueCopyArr.254if (Kind == InterfaceKind::CopyArray) {255const auto *ConstantArrTy =256cast<ConstantArrayType>(DesugaredTy.getTypePtr());257size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);258Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);259Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};260SetValueE =261S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::CopyArray],262SourceLocation(), Args, SourceLocation());263}264Expr *Args[] = {AllocCall.get(), ValuePrintingInfo[InterfaceKind::NewTag]};265ExprResult CXXNewCall = S.BuildCXXNew(266E->getSourceRange(),267/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,268/*PlacementRParen=*/SourceLocation(),269/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,270E->getSourceRange(), E);271272assert(!CXXNewCall.isInvalid() &&273"Can't create runtime placement new call!");274275SetValueE = S.ActOnFinishFullExpr(CXXNewCall.get(),276/*DiscardedValue=*/false);277break;278}279// __clang_Interpreter_SetValueNoAlloc.280case InterfaceKind::NoAlloc: {281SetValueE =282S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],283E->getBeginLoc(), AdjustedArgs, E->getEndLoc());284break;285}286default:287llvm_unreachable("Unhandled InterfaceKind");288}289290// It could fail, like printing an array type in C. (not supported)291if (SetValueE.isInvalid())292return E;293294return SetValueE.get();295}296297} // namespace clang298299using namespace clang;300301// Temporary rvalue struct that need special care.302REPL_EXTERNAL_VISIBILITY void *303__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,304void *OpaqueType) {305Value &VRef = *(Value *)OutVal;306VRef = Value(static_cast<Interpreter *>(This), OpaqueType);307return VRef.getPtr();308}309310extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(311void *This, void *OutVal, void *OpaqueType, ...) {312Value &VRef = *(Value *)OutVal;313Interpreter *I = static_cast<Interpreter *>(This);314VRef = Value(I, OpaqueType);315if (VRef.isVoid())316return;317318va_list args;319va_start(args, /*last named param*/ OpaqueType);320321QualType QT = VRef.getType();322if (VRef.getKind() == Value::K_PtrOrObj) {323VRef.setPtr(va_arg(args, void *));324} else {325if (const auto *ET = QT->getAs<EnumType>())326QT = ET->getDecl()->getIntegerType();327switch (QT->castAs<BuiltinType>()->getKind()) {328default:329llvm_unreachable("unknown type kind!");330break;331// Types shorter than int are resolved as int, else va_arg has UB.332case BuiltinType::Bool:333VRef.setBool(va_arg(args, int));334break;335case BuiltinType::Char_S:336VRef.setChar_S(va_arg(args, int));337break;338case BuiltinType::SChar:339VRef.setSChar(va_arg(args, int));340break;341case BuiltinType::Char_U:342VRef.setChar_U(va_arg(args, unsigned));343break;344case BuiltinType::UChar:345VRef.setUChar(va_arg(args, unsigned));346break;347case BuiltinType::Short:348VRef.setShort(va_arg(args, int));349break;350case BuiltinType::UShort:351VRef.setUShort(va_arg(args, unsigned));352break;353case BuiltinType::Int:354VRef.setInt(va_arg(args, int));355break;356case BuiltinType::UInt:357VRef.setUInt(va_arg(args, unsigned));358break;359case BuiltinType::Long:360VRef.setLong(va_arg(args, long));361break;362case BuiltinType::ULong:363VRef.setULong(va_arg(args, unsigned long));364break;365case BuiltinType::LongLong:366VRef.setLongLong(va_arg(args, long long));367break;368case BuiltinType::ULongLong:369VRef.setULongLong(va_arg(args, unsigned long long));370break;371// Types shorter than double are resolved as double, else va_arg has UB.372case BuiltinType::Float:373VRef.setFloat(va_arg(args, double));374break;375case BuiltinType::Double:376VRef.setDouble(va_arg(args, double));377break;378case BuiltinType::LongDouble:379VRef.setLongDouble(va_arg(args, long double));380break;381// See REPL_BUILTIN_TYPES.382}383}384va_end(args);385}386387// A trampoline to work around the fact that operator placement new cannot388// really be forward declared due to libc++ and libstdc++ declaration mismatch.389// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same390// definition in the interpreter runtime. We should move it in a runtime header391// which gets included by the interpreter and here.392struct __clang_Interpreter_NewTag {};393REPL_EXTERNAL_VISIBILITY void *394operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept {395// Just forward to the standard operator placement new.396return operator new(__sz, __p);397}398399400