llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h

332 lines
10 KiB
C++

//===--- ByteCodeExprGen.h - Code generator for expressions -----*- 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 the constexpr bytecode compiler.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H
#define LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H
#include "ByteCodeEmitter.h"
#include "EvalEmitter.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/StmtVisitor.h"
#include "llvm/ADT/Optional.h"
namespace clang {
class QualType;
namespace interp {
class Function;
class State;
template <class Emitter> class LocalScope;
template <class Emitter> class RecordScope;
template <class Emitter> class VariableScope;
template <class Emitter> class DeclScope;
template <class Emitter> class OptionScope;
/// Compilation context for expressions.
template <class Emitter>
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
public Emitter {
protected:
// Emitters for opcodes of various arities.
using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &);
using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &);
using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType,
const SourceInfo &);
// Aliases for types defined in the emitter.
using LabelTy = typename Emitter::LabelTy;
using AddrTy = typename Emitter::AddrTy;
// Reference to a function generating the pointer of an initialized object.s
using InitFnRef = std::function<bool()>;
/// Current compilation context.
Context &Ctx;
/// Program to link to.
Program &P;
public:
/// Initializes the compiler and the backend emitter.
template <typename... Tys>
ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args)
: Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}
// Expression visitors - result returned on stack.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
bool VisitParenExpr(const ParenExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
protected:
bool visitExpr(const Expr *E) override;
bool visitDecl(const VarDecl *VD) override;
protected:
/// Emits scope cleanup instructions.
void emitCleanup();
/// Returns a record type from a record or pointer type.
const RecordType *getRecordTy(QualType Ty);
/// Returns a record from a record or pointer type.
Record *getRecord(QualType Ty);
Record *getRecord(const RecordDecl *RD);
/// Returns the size int bits of an integer.
unsigned getIntWidth(QualType Ty) {
auto &ASTContext = Ctx.getASTContext();
return ASTContext.getIntWidth(Ty);
}
/// Returns the value of CHAR_BIT.
unsigned getCharBit() const {
auto &ASTContext = Ctx.getASTContext();
return ASTContext.getTargetInfo().getCharWidth();
}
/// Classifies a type.
llvm::Optional<PrimType> classify(const Expr *E) const {
return E->isGLValue() ? PT_Ptr : classify(E->getType());
}
llvm::Optional<PrimType> classify(QualType Ty) const {
return Ctx.classify(Ty);
}
/// Checks if a pointer needs adjustment.
bool needsAdjust(QualType Ty) const {
return true;
}
/// Classifies a known primitive type
PrimType classifyPrim(QualType Ty) const {
if (auto T = classify(Ty)) {
return *T;
}
llvm_unreachable("not a primitive type");
}
/// Evaluates an expression for side effects and discards the result.
bool discard(const Expr *E);
/// Evaluates an expression and places result on stack.
bool visit(const Expr *E);
/// Compiles an initializer for a local.
bool visitInitializer(const Expr *E, InitFnRef GenPtr);
/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
/// Visits an initializer for a local.
bool visitLocalInitializer(const Expr *Init, unsigned I) {
return visitInitializer(Init, [this, I, Init] {
return this->emitGetPtrLocal(I, Init);
});
}
/// Visits an initializer for a global.
bool visitGlobalInitializer(const Expr *Init, unsigned I) {
return visitInitializer(Init, [this, I, Init] {
return this->emitGetPtrGlobal(I, Init);
});
}
/// Visits a delegated initializer.
bool visitThisInitializer(const Expr *I) {
return visitInitializer(I, [this, I] { return this->emitThis(I); });
}
/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable,
bool IsExtended = false);
/// Allocates a space storing a local given its type.
llvm::Optional<unsigned> allocateLocal(DeclTy &&Decl,
bool IsExtended = false);
private:
friend class VariableScope<Emitter>;
friend class LocalScope<Emitter>;
friend class RecordScope<Emitter>;
friend class DeclScope<Emitter>;
friend class OptionScope<Emitter>;
/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, const Expr *E);
enum class DerefKind {
/// Value is read and pushed to stack.
Read,
/// Direct method generates a value which is written. Returns pointer.
Write,
/// Direct method receives the value, pushes mutated value. Returns pointer.
ReadWrite,
};
/// Method to directly load a value. If the value can be fetched directly,
/// the direct handler is called. Otherwise, a pointer is left on the stack
/// and the indirect handler is expected to operate on that.
bool dereference(const Expr *LV, DerefKind AK,
llvm::function_ref<bool(PrimType)> Direct,
llvm::function_ref<bool(PrimType)> Indirect);
bool dereferenceParam(const Expr *LV, PrimType T, const ParmVarDecl *PD,
DerefKind AK,
llvm::function_ref<bool(PrimType)> Direct,
llvm::function_ref<bool(PrimType)> Indirect);
bool dereferenceVar(const Expr *LV, PrimType T, const VarDecl *PD,
DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
llvm::function_ref<bool(PrimType)> Indirect);
/// Emits an APInt constant.
bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value,
const Expr *E);
/// Emits an integer constant.
template <typename T> bool emitConst(const Expr *E, T Value) {
QualType Ty = E->getType();
unsigned NumBits = getIntWidth(Ty);
APInt WrappedValue(NumBits, Value, std::is_signed<T>::value);
return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E);
}
/// Returns a pointer to a variable declaration.
bool getPtrVarDecl(const VarDecl *VD, const Expr *E);
/// Returns the index of a global.
llvm::Optional<unsigned> getGlobalIdx(const VarDecl *VD);
/// Emits the initialized pointer.
bool emitInitFn() {
assert(InitFn && "missing initializer");
return (*InitFn)();
}
protected:
/// Variable to storage mapping.
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
/// OpaqueValueExpr to location mapping.
llvm::DenseMap<const OpaqueValueExpr *, unsigned> OpaqueExprs;
/// Current scope.
VariableScope<Emitter> *VarScope = nullptr;
/// Current argument index.
llvm::Optional<uint64_t> ArrayIndex;
/// Flag indicating if return value is to be discarded.
bool DiscardResult = false;
/// Expression being initialized.
llvm::Optional<InitFnRef> InitFn = {};
};
extern template class ByteCodeExprGen<ByteCodeEmitter>;
extern template class ByteCodeExprGen<EvalEmitter>;
/// Scope chain managing the variable lifetimes.
template <class Emitter> class VariableScope {
public:
virtual ~VariableScope() { Ctx->VarScope = this->Parent; }
void add(const Scope::Local &Local, bool IsExtended) {
if (IsExtended)
this->addExtended(Local);
else
this->addLocal(Local);
}
virtual void addLocal(const Scope::Local &Local) {
if (this->Parent)
this->Parent->addLocal(Local);
}
virtual void addExtended(const Scope::Local &Local) {
if (this->Parent)
this->Parent->addExtended(Local);
}
virtual void emitDestruction() {}
VariableScope *getParent() { return Parent; }
protected:
VariableScope(ByteCodeExprGen<Emitter> *Ctx)
: Ctx(Ctx), Parent(Ctx->VarScope) {
Ctx->VarScope = this;
}
/// ByteCodeExprGen instance.
ByteCodeExprGen<Emitter> *Ctx;
/// Link to the parent scope.
VariableScope *Parent;
};
/// Scope for local variables.
///
/// When the scope is destroyed, instructions are emitted to tear down
/// all variables declared in this scope.
template <class Emitter> class LocalScope : public VariableScope<Emitter> {
public:
LocalScope(ByteCodeExprGen<Emitter> *Ctx) : VariableScope<Emitter>(Ctx) {}
~LocalScope() override { this->emitDestruction(); }
void addLocal(const Scope::Local &Local) override {
if (!Idx.hasValue()) {
Idx = this->Ctx->Descriptors.size();
this->Ctx->Descriptors.emplace_back();
}
this->Ctx->Descriptors[*Idx].emplace_back(Local);
}
void emitDestruction() override {
if (!Idx.hasValue())
return;
this->Ctx->emitDestroy(*Idx, SourceInfo{});
}
protected:
/// Index of the scope in the chain.
Optional<unsigned> Idx;
};
/// Scope for storage declared in a compound statement.
template <class Emitter> class BlockScope final : public LocalScope<Emitter> {
public:
BlockScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {}
void addExtended(const Scope::Local &Local) override {
llvm_unreachable("Cannot create temporaries in full scopes");
}
};
/// Expression scope which tracks potentially lifetime extended
/// temporaries which are hoisted to the parent scope on exit.
template <class Emitter> class ExprScope final : public LocalScope<Emitter> {
public:
ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {}
void addExtended(const Scope::Local &Local) override {
this->Parent->addLocal(Local);
}
};
} // namespace interp
} // namespace clang
#endif