Path: blob/main/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp
35291 views
//===--- ByteCodeEmitter.cpp - Instruction emitter 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 "ByteCodeEmitter.h"9#include "Context.h"10#include "Floating.h"11#include "IntegralAP.h"12#include "Opcode.h"13#include "Program.h"14#include "clang/AST/ASTLambda.h"15#include "clang/AST/Attr.h"16#include "clang/AST/DeclCXX.h"17#include "clang/Basic/Builtins.h"18#include <type_traits>1920using namespace clang;21using namespace clang::interp;2223/// Unevaluated builtins don't get their arguments put on the stack24/// automatically. They instead operate on the AST of their Call25/// Expression.26/// Similar information is available via ASTContext::BuiltinInfo,27/// but that is not correct for our use cases.28static bool isUnevaluatedBuiltin(unsigned BuiltinID) {29return BuiltinID == Builtin::BI__builtin_classify_type ||30BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size;31}3233Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {3435// Manually created functions that haven't been assigned proper36// parameters yet.37if (!FuncDecl->param_empty() && !FuncDecl->param_begin())38return nullptr;3940bool IsLambdaStaticInvoker = false;41if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);42MD && MD->isLambdaStaticInvoker()) {43// For a lambda static invoker, we might have to pick a specialized44// version if the lambda is generic. In that case, the picked function45// will *NOT* be a static invoker anymore. However, it will still46// be a non-static member function, this (usually) requiring an47// instance pointer. We suppress that later in this function.48IsLambdaStaticInvoker = true;4950const CXXRecordDecl *ClosureClass = MD->getParent();51assert(ClosureClass->captures_begin() == ClosureClass->captures_end());52if (ClosureClass->isGenericLambda()) {53const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();54assert(MD->isFunctionTemplateSpecialization() &&55"A generic lambda's static-invoker function must be a "56"template specialization");57const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();58FunctionTemplateDecl *CallOpTemplate =59LambdaCallOp->getDescribedFunctionTemplate();60void *InsertPos = nullptr;61const FunctionDecl *CorrespondingCallOpSpecialization =62CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);63assert(CorrespondingCallOpSpecialization);64FuncDecl = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);65}66}6768// Set up argument indices.69unsigned ParamOffset = 0;70SmallVector<PrimType, 8> ParamTypes;71SmallVector<unsigned, 8> ParamOffsets;72llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;7374// If the return is not a primitive, a pointer to the storage where the75// value is initialized in is passed as the first argument. See 'RVO'76// elsewhere in the code.77QualType Ty = FuncDecl->getReturnType();78bool HasRVO = false;79if (!Ty->isVoidType() && !Ctx.classify(Ty)) {80HasRVO = true;81ParamTypes.push_back(PT_Ptr);82ParamOffsets.push_back(ParamOffset);83ParamOffset += align(primSize(PT_Ptr));84}8586// If the function decl is a member decl, the next parameter is87// the 'this' pointer. This parameter is pop()ed from the88// InterpStack when calling the function.89bool HasThisPointer = false;90if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {91if (!IsLambdaStaticInvoker) {92HasThisPointer = MD->isInstance();93if (MD->isImplicitObjectMemberFunction()) {94ParamTypes.push_back(PT_Ptr);95ParamOffsets.push_back(ParamOffset);96ParamOffset += align(primSize(PT_Ptr));97}98}99100// Set up lambda capture to closure record field mapping.101if (isLambdaCallOperator(MD)) {102// The parent record needs to be complete, we need to know about all103// the lambda captures.104if (!MD->getParent()->isCompleteDefinition())105return nullptr;106107const Record *R = P.getOrCreateRecord(MD->getParent());108llvm::DenseMap<const ValueDecl *, FieldDecl *> LC;109FieldDecl *LTC;110111MD->getParent()->getCaptureFields(LC, LTC);112113for (auto Cap : LC) {114// Static lambdas cannot have any captures. If this one does,115// it has already been diagnosed and we can only ignore it.116if (MD->isStatic())117return nullptr;118119unsigned Offset = R->getField(Cap.second)->Offset;120this->LambdaCaptures[Cap.first] = {121Offset, Cap.second->getType()->isReferenceType()};122}123if (LTC) {124QualType CaptureType = R->getField(LTC)->Decl->getType();125this->LambdaThisCapture = {R->getField(LTC)->Offset,126CaptureType->isReferenceType() ||127CaptureType->isPointerType()};128}129}130}131132// Assign descriptors to all parameters.133// Composite objects are lowered to pointers.134for (const ParmVarDecl *PD : FuncDecl->parameters()) {135std::optional<PrimType> T = Ctx.classify(PD->getType());136PrimType PT = T.value_or(PT_Ptr);137Descriptor *Desc = P.createDescriptor(PD, PT);138ParamDescriptors.insert({ParamOffset, {PT, Desc}});139Params.insert({PD, {ParamOffset, T != std::nullopt}});140ParamOffsets.push_back(ParamOffset);141ParamOffset += align(primSize(PT));142ParamTypes.push_back(PT);143}144145// Create a handle over the emitted code.146Function *Func = P.getFunction(FuncDecl);147if (!Func) {148bool IsUnevaluatedBuiltin = false;149if (unsigned BI = FuncDecl->getBuiltinID())150IsUnevaluatedBuiltin = isUnevaluatedBuiltin(BI);151152Func =153P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),154std::move(ParamDescriptors), std::move(ParamOffsets),155HasThisPointer, HasRVO, IsUnevaluatedBuiltin);156}157158assert(Func);159// For not-yet-defined functions, we only create a Function instance and160// compile their body later.161if (!FuncDecl->isDefined() ||162(FuncDecl->willHaveBody() && !FuncDecl->hasBody())) {163Func->setDefined(false);164return Func;165}166167Func->setDefined(true);168169// Lambda static invokers are a special case that we emit custom code for.170bool IsEligibleForCompilation = false;171if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl))172IsEligibleForCompilation = MD->isLambdaStaticInvoker();173if (!IsEligibleForCompilation)174IsEligibleForCompilation =175FuncDecl->isConstexpr() || FuncDecl->hasAttr<MSConstexprAttr>();176177// Compile the function body.178if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) {179Func->setIsFullyCompiled(true);180return Func;181}182183// Create scopes from descriptors.184llvm::SmallVector<Scope, 2> Scopes;185for (auto &DS : Descriptors) {186Scopes.emplace_back(std::move(DS));187}188189// Set the function's code.190Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),191std::move(Scopes), FuncDecl->hasBody());192Func->setIsFullyCompiled(true);193return Func;194}195196Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {197NextLocalOffset += sizeof(Block);198unsigned Location = NextLocalOffset;199NextLocalOffset += align(D->getAllocSize());200return {Location, D};201}202203void ByteCodeEmitter::emitLabel(LabelTy Label) {204const size_t Target = Code.size();205LabelOffsets.insert({Label, Target});206207if (auto It = LabelRelocs.find(Label);208It != LabelRelocs.end()) {209for (unsigned Reloc : It->second) {210using namespace llvm::support;211212// Rewrite the operand of all jumps to this label.213void *Location = Code.data() + Reloc - align(sizeof(int32_t));214assert(aligned(Location));215const int32_t Offset = Target - static_cast<int64_t>(Reloc);216endian::write<int32_t, llvm::endianness::native>(Location, Offset);217}218LabelRelocs.erase(It);219}220}221222int32_t ByteCodeEmitter::getOffset(LabelTy Label) {223// Compute the PC offset which the jump is relative to.224const int64_t Position =225Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t));226assert(aligned(Position));227228// If target is known, compute jump offset.229if (auto It = LabelOffsets.find(Label);230It != LabelOffsets.end())231return It->second - Position;232233// Otherwise, record relocation and return dummy offset.234LabelRelocs[Label].push_back(Position);235return 0ull;236}237238/// Helper to write bytecode and bail out if 32-bit offsets become invalid.239/// Pointers will be automatically marshalled as 32-bit IDs.240template <typename T>241static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,242bool &Success) {243size_t Size;244245if constexpr (std::is_pointer_v<T>)246Size = sizeof(uint32_t);247else248Size = sizeof(T);249250if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {251Success = false;252return;253}254255// Access must be aligned!256size_t ValPos = align(Code.size());257Size = align(Size);258assert(aligned(ValPos + Size));259Code.resize(ValPos + Size);260261if constexpr (!std::is_pointer_v<T>) {262new (Code.data() + ValPos) T(Val);263} else {264uint32_t ID = P.getOrCreateNativePointer(Val);265new (Code.data() + ValPos) uint32_t(ID);266}267}268269/// Emits a serializable value. These usually (potentially) contain270/// heap-allocated memory and aren't trivially copyable.271template <typename T>272static void emitSerialized(std::vector<std::byte> &Code, const T &Val,273bool &Success) {274size_t Size = Val.bytesToSerialize();275276if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {277Success = false;278return;279}280281// Access must be aligned!282size_t ValPos = align(Code.size());283Size = align(Size);284assert(aligned(ValPos + Size));285Code.resize(ValPos + Size);286287Val.serialize(Code.data() + ValPos);288}289290template <>291void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,292bool &Success) {293emitSerialized(Code, Val, Success);294}295296template <>297void emit(Program &P, std::vector<std::byte> &Code,298const IntegralAP<false> &Val, bool &Success) {299emitSerialized(Code, Val, Success);300}301302template <>303void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val,304bool &Success) {305emitSerialized(Code, Val, Success);306}307308template <typename... Tys>309bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {310bool Success = true;311312// The opcode is followed by arguments. The source info is313// attached to the address after the opcode.314emit(P, Code, Op, Success);315if (SI)316SrcMap.emplace_back(Code.size(), SI);317318(..., emit(P, Code, Args, Success));319return Success;320}321322bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) {323return emitJt(getOffset(Label), SourceInfo{});324}325326bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) {327return emitJf(getOffset(Label), SourceInfo{});328}329330bool ByteCodeEmitter::jump(const LabelTy &Label) {331return emitJmp(getOffset(Label), SourceInfo{});332}333334bool ByteCodeEmitter::fallthrough(const LabelTy &Label) {335emitLabel(Label);336return true;337}338339//===----------------------------------------------------------------------===//340// Opcode emitters341//===----------------------------------------------------------------------===//342343#define GET_LINK_IMPL344#include "Opcodes.inc"345#undef GET_LINK_IMPL346347348