Timm Baeder 32fc625a3f
Reapply "Reapply "[clang][bytecode] Allocate IntegralAP and Floating … (#145014)
…types usi… (#144676)"

This reverts commit 68471d29eed2c49f9b439e505b3f24d387d54f97.

IntegralAP contains a union:
  union {
    uint64_t *Memory = nullptr;
    uint64_t Val;
  };

On 64bit systems, both Memory and Val have the same size. However, on 32
bit system, Val is 64bit and Memory only 32bit. Which means the default
initializer for Memory will only zero half of Val. We fixed this by
zero-initializing Val explicitly in the IntegralAP(unsigned BitWidth)
constructor.


See also the discussion in
https://github.com/llvm/llvm-project/pull/144246
2025-06-20 18:06:01 +02:00

264 lines
8.6 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 T,
const Type *SourceTy = nullptr,
Descriptor::MetadataSize MDSize = std::nullopt,
bool IsConst = false, bool IsTemporary = false,
bool IsMutable = false,
bool IsVolatile = false) {
return allocateDescriptor(D, SourceTy, T, MDSize, IsConst, IsTemporary,
IsMutable, IsVolatile);
}
/// 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, bool IsVolatile = false,
const Expr *Init = nullptr);
void *Allocate(size_t Size, unsigned Align = 8) const {
return Allocator.Allocate(Size, Align);
}
template <typename T> T *Allocate(size_t Num = 1) const {
return static_cast<T *>(Allocate(Num * sizeof(T), alignof(T)));
}
void Deallocate(void *Ptr) const {}
/// 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.
mutable 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
inline void *operator new(size_t Bytes, const clang::interp::Program &C,
size_t Alignment = 8) {
return C.Allocate(Bytes, Alignment);
}
inline void operator delete(void *Ptr, const clang::interp::Program &C,
size_t) {
C.Deallocate(Ptr);
}
inline void *operator new[](size_t Bytes, const clang::interp::Program &C,
size_t Alignment = 8) {
return C.Allocate(Bytes, Alignment);
}
#endif