Path: blob/main/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp
35291 views
//===--- InterpFrame.cpp - Call Frame implementation for the VM -*- 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//===----------------------------------------------------------------------===//78#include "InterpFrame.h"9#include "Boolean.h"10#include "Floating.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"2021using namespace clang;22using namespace clang::interp;2324InterpFrame::InterpFrame(InterpState &S, const Function *Func,25InterpFrame *Caller, CodePtr RetPC, unsigned ArgSize)26: Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),27RetPC(RetPC), ArgSize(ArgSize), Args(static_cast<char *>(S.Stk.top())),28FrameOffset(S.Stk.size()) {29if (!Func)30return;3132unsigned FrameSize = Func->getFrameSize();33if (FrameSize == 0)34return;3536Locals = std::make_unique<char[]>(FrameSize);37for (auto &Scope : Func->scopes()) {38for (auto &Local : Scope.locals()) {39Block *B =40new (localBlock(Local.Offset)) Block(S.Ctx.getEvalID(), Local.Desc);41B->invokeCtor();42new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);43}44}45}4647InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC,48unsigned VarArgSize)49: InterpFrame(S, Func, S.Current, RetPC, Func->getArgSize() + VarArgSize) {50// As per our calling convention, the this pointer is51// part of the ArgSize.52// If the function has RVO, the RVO pointer is first.53// If the fuction has a This pointer, that one is next.54// Then follow the actual arguments (but those are handled55// in getParamPointer()).56if (Func->hasRVO())57RVOPtr = stackRef<Pointer>(0);5859if (Func->hasThisPointer()) {60if (Func->hasRVO())61This = stackRef<Pointer>(sizeof(Pointer));62else63This = stackRef<Pointer>(0);64}65}6667InterpFrame::~InterpFrame() {68for (auto &Param : Params)69S.deallocate(reinterpret_cast<Block *>(Param.second.get()));7071// When destroying the InterpFrame, call the Dtor for all block72// that haven't been destroyed via a destroy() op yet.73// This happens when the execution is interruped midway-through.74if (Func) {75for (auto &Scope : Func->scopes()) {76for (auto &Local : Scope.locals()) {77Block *B = localBlock(Local.Offset);78if (B->isInitialized())79B->invokeDtor();80}81}82}83}8485void InterpFrame::destroy(unsigned Idx) {86for (auto &Local : Func->getScope(Idx).locals()) {87S.deallocate(localBlock(Local.Offset));88}89}9091void InterpFrame::popArgs() {92for (PrimType Ty : Func->args_reverse())93TYPE_SWITCH(Ty, S.Stk.discard<T>());94}9596template <typename T>97static void print(llvm::raw_ostream &OS, const T &V, ASTContext &, QualType) {98OS << V;99}100101template <>102void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx,103QualType Ty) {104if (P.isZero()) {105OS << "nullptr";106return;107}108109auto printDesc = [&OS, &Ctx](const Descriptor *Desc) {110if (const auto *D = Desc->asDecl()) {111// Subfields or named values.112if (const auto *VD = dyn_cast<ValueDecl>(D)) {113OS << *VD;114return;115}116// Base classes.117if (isa<RecordDecl>(D))118return;119}120// Temporary expression.121if (const auto *E = Desc->asExpr()) {122E->printPretty(OS, nullptr, Ctx.getPrintingPolicy());123return;124}125llvm_unreachable("Invalid descriptor type");126};127128if (!Ty->isReferenceType())129OS << "&";130llvm::SmallVector<Pointer, 2> Levels;131for (Pointer F = P; !F.isRoot(); ) {132Levels.push_back(F);133F = F.isArrayElement() ? F.getArray().expand() : F.getBase();134}135136// Drop the first pointer since we print it unconditionally anyway.137if (!Levels.empty())138Levels.erase(Levels.begin());139140printDesc(P.getDeclDesc());141for (const auto &It : Levels) {142if (It.inArray()) {143OS << "[" << It.expand().getIndex() << "]";144continue;145}146if (auto Index = It.getIndex()) {147OS << " + " << Index;148continue;149}150OS << ".";151printDesc(It.getFieldDesc());152}153}154155void InterpFrame::describe(llvm::raw_ostream &OS) const {156// We create frames for builtin functions as well, but we can't reliably157// diagnose them. The 'in call to' diagnostics for them add no value to the158// user _and_ it doesn't generally work since the argument types don't always159// match the function prototype. Just ignore them.160// Similarly, for lambda static invokers, we would just print __invoke().161if (const auto *F = getFunction();162F && (F->isBuiltin() || F->isLambdaStaticInvoker()))163return;164165const FunctionDecl *F = getCallee();166if (const auto *M = dyn_cast<CXXMethodDecl>(F);167M && M->isInstance() && !isa<CXXConstructorDecl>(F)) {168print(OS, This, S.getCtx(), S.getCtx().getRecordType(M->getParent()));169OS << "->";170}171172F->getNameForDiagnostic(OS, S.getCtx().getPrintingPolicy(),173/*Qualified=*/false);174OS << '(';175unsigned Off = 0;176177Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;178Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;179180for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) {181QualType Ty = F->getParamDecl(I)->getType();182183PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr);184185TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getCtx(), Ty));186Off += align(primSize(PrimTy));187if (I + 1 != N)188OS << ", ";189}190OS << ")";191}192193Frame *InterpFrame::getCaller() const {194if (Caller->Caller)195return Caller;196return S.getSplitFrame();197}198199SourceRange InterpFrame::getCallRange() const {200if (!Caller->Func) {201if (SourceRange NullRange = S.getRange(nullptr, {}); NullRange.isValid())202return NullRange;203return S.EvalLocation;204}205return S.getRange(Caller->Func, RetPC - sizeof(uintptr_t));206}207208const FunctionDecl *InterpFrame::getCallee() const {209if (!Func)210return nullptr;211return Func->getDecl();212}213214Pointer InterpFrame::getLocalPointer(unsigned Offset) const {215assert(Offset < Func->getFrameSize() && "Invalid local offset.");216return Pointer(localBlock(Offset));217}218219Pointer InterpFrame::getParamPointer(unsigned Off) {220// Return the block if it was created previously.221if (auto Pt = Params.find(Off); Pt != Params.end())222return Pointer(reinterpret_cast<Block *>(Pt->second.get()));223224// Allocate memory to store the parameter and the block metadata.225const auto &Desc = Func->getParamDescriptor(Off);226size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize();227auto Memory = std::make_unique<char[]>(BlockSize);228auto *B = new (Memory.get()) Block(S.Ctx.getEvalID(), Desc.second);229B->invokeCtor();230231// Copy the initial value.232TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off)));233234// Record the param.235Params.insert({Off, std::move(Memory)});236return Pointer(B);237}238239SourceInfo InterpFrame::getSource(CodePtr PC) const {240// Implicitly created functions don't have any code we could point at,241// so return the call site.242if (Func && (!Func->hasBody() || Func->getDecl()->isImplicit()) && Caller)243return Caller->getSource(RetPC);244245return S.getSource(Func, PC);246}247248const Expr *InterpFrame::getExpr(CodePtr PC) const {249if (Func && (!Func->hasBody() || Func->getDecl()->isImplicit()) && Caller)250return Caller->getExpr(RetPC);251252return S.getExpr(Func, PC);253}254255SourceLocation InterpFrame::getLocation(CodePtr PC) const {256if (Func && (!Func->hasBody() || Func->getDecl()->isImplicit()) && Caller)257return Caller->getLocation(RetPC);258259return S.getLocation(Func, PC);260}261262SourceRange InterpFrame::getRange(CodePtr PC) const {263if (Func && (!Func->hasBody() || Func->getDecl()->isImplicit()) && Caller)264return Caller->getRange(RetPC);265266return S.getRange(Func, PC);267}268269270