mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 17:56:06 +00:00
239 lines
7.7 KiB
C++
239 lines
7.7 KiB
C++
//===--- Program.h - Bytecode for the constexpr VM --------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines a program which organises and links multiple bytecode functions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_CLANG_AST_INTERP_PROGRAM_H
|
|
#define LLVM_CLANG_AST_INTERP_PROGRAM_H
|
|
|
|
#include "Function.h"
|
|
#include "Pointer.h"
|
|
#include "PrimType.h"
|
|
#include "Record.h"
|
|
#include "Source.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/PointerUnion.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Allocator.h"
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
namespace clang {
|
|
class RecordDecl;
|
|
class Expr;
|
|
class FunctionDecl;
|
|
class StringLiteral;
|
|
class VarDecl;
|
|
|
|
namespace interp {
|
|
class Context;
|
|
|
|
/// The program contains and links the bytecode for all functions.
|
|
class Program final {
|
|
public:
|
|
Program(Context &Ctx) : Ctx(Ctx) {}
|
|
|
|
~Program() {
|
|
// Manually destroy all the blocks. They are almost all harmless,
|
|
// but primitive arrays might have an InitMap* heap allocated and
|
|
// that needs to be freed.
|
|
for (Global *G : Globals)
|
|
if (Block *B = G->block(); B->isInitialized())
|
|
B->invokeDtor();
|
|
|
|
// Records might actually allocate memory themselves, but they
|
|
// are allocated using a BumpPtrAllocator. Call their desctructors
|
|
// here manually so they are properly freeing their resources.
|
|
for (auto RecordPair : Records) {
|
|
if (Record *R = RecordPair.second)
|
|
R->~Record();
|
|
}
|
|
}
|
|
|
|
/// Marshals a native pointer to an ID for embedding in bytecode.
|
|
unsigned getOrCreateNativePointer(const void *Ptr);
|
|
|
|
/// Returns the value of a marshalled native pointer.
|
|
const void *getNativePointer(unsigned Idx);
|
|
|
|
/// Emits a string literal among global data.
|
|
unsigned createGlobalString(const StringLiteral *S,
|
|
const Expr *Base = nullptr);
|
|
|
|
/// Returns a pointer to a global.
|
|
Pointer getPtrGlobal(unsigned Idx) const;
|
|
|
|
/// Returns the value of a global.
|
|
Block *getGlobal(unsigned Idx) {
|
|
assert(Idx < Globals.size());
|
|
return Globals[Idx]->block();
|
|
}
|
|
|
|
/// Finds a global's index.
|
|
std::optional<unsigned> getGlobal(const ValueDecl *VD);
|
|
std::optional<unsigned> getGlobal(const Expr *E);
|
|
|
|
/// Returns or creates a global an creates an index to it.
|
|
std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD,
|
|
const Expr *Init = nullptr);
|
|
|
|
/// Returns or creates a dummy value for unknown declarations.
|
|
unsigned getOrCreateDummy(const DeclTy &D);
|
|
|
|
/// Creates a global and returns its index.
|
|
std::optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *Init);
|
|
|
|
/// Creates a global from a lifetime-extended temporary.
|
|
std::optional<unsigned> createGlobal(const Expr *E);
|
|
|
|
/// Creates a new function from a code range.
|
|
template <typename... Ts>
|
|
Function *createFunction(const FunctionDecl *Def, Ts &&...Args) {
|
|
Def = Def->getCanonicalDecl();
|
|
auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...);
|
|
Funcs.insert({Def, std::unique_ptr<Function>(Func)});
|
|
return Func;
|
|
}
|
|
/// Creates an anonymous function.
|
|
template <typename... Ts> Function *createFunction(Ts &&...Args) {
|
|
auto *Func = new Function(*this, std::forward<Ts>(Args)...);
|
|
AnonFuncs.emplace_back(Func);
|
|
return Func;
|
|
}
|
|
|
|
/// Returns a function.
|
|
Function *getFunction(const FunctionDecl *F);
|
|
|
|
/// Returns a record or creates one if it does not exist.
|
|
Record *getOrCreateRecord(const RecordDecl *RD);
|
|
|
|
/// Creates a descriptor for a primitive type.
|
|
Descriptor *createDescriptor(const DeclTy &D, PrimType Type,
|
|
Descriptor::MetadataSize MDSize = std::nullopt,
|
|
bool IsConst = false, bool IsTemporary = false,
|
|
bool IsMutable = false) {
|
|
return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable);
|
|
}
|
|
|
|
/// Creates a descriptor for a composite type.
|
|
Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
|
|
Descriptor::MetadataSize MDSize = std::nullopt,
|
|
bool IsConst = false, bool IsTemporary = false,
|
|
bool IsMutable = false,
|
|
const Expr *Init = nullptr);
|
|
|
|
/// Context to manage declaration lifetimes.
|
|
class DeclScope {
|
|
public:
|
|
DeclScope(Program &P) : P(P), PrevDecl(P.CurrentDeclaration) {
|
|
++P.LastDeclaration;
|
|
P.CurrentDeclaration = P.LastDeclaration;
|
|
}
|
|
~DeclScope() { P.CurrentDeclaration = PrevDecl; }
|
|
|
|
private:
|
|
Program &P;
|
|
unsigned PrevDecl;
|
|
};
|
|
|
|
/// Returns the current declaration ID.
|
|
std::optional<unsigned> getCurrentDecl() const {
|
|
if (CurrentDeclaration == NoDeclaration)
|
|
return std::nullopt;
|
|
return CurrentDeclaration;
|
|
}
|
|
|
|
private:
|
|
friend class DeclScope;
|
|
|
|
std::optional<unsigned> createGlobal(const DeclTy &D, QualType Ty,
|
|
bool IsStatic, bool IsExtern,
|
|
bool IsWeak, const Expr *Init = nullptr);
|
|
|
|
/// Reference to the VM context.
|
|
Context &Ctx;
|
|
/// Mapping from decls to cached bytecode functions.
|
|
llvm::DenseMap<const FunctionDecl *, std::unique_ptr<Function>> Funcs;
|
|
/// List of anonymous functions.
|
|
std::vector<std::unique_ptr<Function>> AnonFuncs;
|
|
|
|
/// Function relocation locations.
|
|
llvm::DenseMap<const FunctionDecl *, std::vector<unsigned>> Relocs;
|
|
|
|
/// Native pointers referenced by bytecode.
|
|
std::vector<const void *> NativePointers;
|
|
/// Cached native pointer indices.
|
|
llvm::DenseMap<const void *, unsigned> NativePointerIndices;
|
|
|
|
/// Custom allocator for global storage.
|
|
using PoolAllocTy = llvm::BumpPtrAllocator;
|
|
|
|
/// Descriptor + storage for a global object.
|
|
///
|
|
/// Global objects never go out of scope, thus they do not track pointers.
|
|
class Global {
|
|
public:
|
|
/// Create a global descriptor for string literals.
|
|
template <typename... Tys>
|
|
Global(Tys... Args) : B(std::forward<Tys>(Args)...) {}
|
|
|
|
/// Allocates the global in the pool, reserving storate for data.
|
|
void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) {
|
|
return Alloc.Allocate(Meta + Data, alignof(void *));
|
|
}
|
|
|
|
/// Return a pointer to the data.
|
|
std::byte *data() { return B.data(); }
|
|
/// Return a pointer to the block.
|
|
Block *block() { return &B; }
|
|
const Block *block() const { return &B; }
|
|
|
|
private:
|
|
/// Required metadata - does not actually track pointers.
|
|
Block B;
|
|
};
|
|
|
|
/// Allocator for globals.
|
|
PoolAllocTy Allocator;
|
|
|
|
/// Global objects.
|
|
std::vector<Global *> Globals;
|
|
/// Cached global indices.
|
|
llvm::DenseMap<const void *, unsigned> GlobalIndices;
|
|
|
|
/// Mapping from decls to record metadata.
|
|
llvm::DenseMap<const RecordDecl *, Record *> Records;
|
|
|
|
/// Dummy parameter to generate pointers from.
|
|
llvm::DenseMap<const void *, unsigned> DummyVariables;
|
|
|
|
/// Creates a new descriptor.
|
|
template <typename... Ts> Descriptor *allocateDescriptor(Ts &&...Args) {
|
|
return new (Allocator) Descriptor(std::forward<Ts>(Args)...);
|
|
}
|
|
|
|
/// No declaration ID.
|
|
static constexpr unsigned NoDeclaration = ~0u;
|
|
/// Last declaration ID.
|
|
unsigned LastDeclaration = 0;
|
|
/// Current declaration ID.
|
|
unsigned CurrentDeclaration = NoDeclaration;
|
|
|
|
public:
|
|
/// Dumps the disassembled bytecode to \c llvm::errs().
|
|
void dump() const;
|
|
void dump(llvm::raw_ostream &OS) const;
|
|
};
|
|
|
|
} // namespace interp
|
|
} // namespace clang
|
|
|
|
#endif
|