Add an `EvaluationResult` class. This contains the result either as a `Pointer` or as a `APValue`. This way, we can inspect the result of the evaluation and diagnose problems with it (e.g. uninitialized fields in global initializers or pointers pointing to things they shouldn't point to).
203 lines
5.1 KiB
C++
203 lines
5.1 KiB
C++
//===--- EvalEmitter.cpp - Instruction emitter for the 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "EvalEmitter.h"
|
|
#include "ByteCodeGenError.h"
|
|
#include "Context.h"
|
|
#include "IntegralAP.h"
|
|
#include "Interp.h"
|
|
#include "Opcode.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
|
|
using namespace clang;
|
|
using namespace clang::interp;
|
|
|
|
EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
|
|
InterpStack &Stk, APValue &Result)
|
|
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
|
|
// Create a dummy frame for the interpreter which does not have locals.
|
|
S.Current =
|
|
new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
|
|
}
|
|
|
|
EvalEmitter::~EvalEmitter() {
|
|
for (auto &[K, V] : Locals) {
|
|
Block *B = reinterpret_cast<Block *>(V.get());
|
|
if (B->isInitialized())
|
|
B->invokeDtor();
|
|
}
|
|
}
|
|
|
|
EvaluationResult EvalEmitter::interpretExpr(const Expr *E) {
|
|
EvalResult.setSource(E);
|
|
|
|
if (!this->visitExpr(E))
|
|
EvalResult.setInvalid();
|
|
|
|
return std::move(this->EvalResult);
|
|
}
|
|
|
|
EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD) {
|
|
EvalResult.setSource(VD);
|
|
|
|
if (!this->visitDecl(VD))
|
|
EvalResult.setInvalid();
|
|
|
|
return std::move(this->EvalResult);
|
|
}
|
|
|
|
void EvalEmitter::emitLabel(LabelTy Label) {
|
|
CurrentLabel = Label;
|
|
}
|
|
|
|
EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; }
|
|
|
|
Scope::Local EvalEmitter::createLocal(Descriptor *D) {
|
|
// Allocate memory for a local.
|
|
auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize());
|
|
auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
|
|
B->invokeCtor();
|
|
|
|
// Initialize local variable inline descriptor.
|
|
InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
|
|
Desc.Desc = D;
|
|
Desc.Offset = sizeof(InlineDescriptor);
|
|
Desc.IsActive = true;
|
|
Desc.IsBase = false;
|
|
Desc.IsFieldMutable = false;
|
|
Desc.IsConst = false;
|
|
Desc.IsInitialized = false;
|
|
|
|
// Register the local.
|
|
unsigned Off = Locals.size();
|
|
Locals.insert({Off, std::move(Memory)});
|
|
return {Off, D};
|
|
}
|
|
|
|
bool EvalEmitter::jumpTrue(const LabelTy &Label) {
|
|
if (isActive()) {
|
|
if (S.Stk.pop<bool>())
|
|
ActiveLabel = Label;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EvalEmitter::jumpFalse(const LabelTy &Label) {
|
|
if (isActive()) {
|
|
if (!S.Stk.pop<bool>())
|
|
ActiveLabel = Label;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EvalEmitter::jump(const LabelTy &Label) {
|
|
if (isActive())
|
|
CurrentLabel = ActiveLabel = Label;
|
|
return true;
|
|
}
|
|
|
|
bool EvalEmitter::fallthrough(const LabelTy &Label) {
|
|
if (isActive())
|
|
ActiveLabel = Label;
|
|
CurrentLabel = Label;
|
|
return true;
|
|
}
|
|
|
|
template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
using T = typename PrimConv<OpType>::T;
|
|
EvalResult.setValue(S.Stk.pop<T>().toAPValue());
|
|
return true;
|
|
}
|
|
|
|
template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
EvalResult.setPointer(S.Stk.pop<Pointer>());
|
|
return true;
|
|
}
|
|
template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
|
|
return true;
|
|
}
|
|
|
|
bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
|
|
EvalResult.setValid();
|
|
return true;
|
|
}
|
|
|
|
bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
|
|
const auto &Ptr = S.Stk.pop<Pointer>();
|
|
if (std::optional<APValue> APV = Ptr.toRValue(S.getCtx())) {
|
|
EvalResult.setValue(*APV);
|
|
return true;
|
|
}
|
|
|
|
EvalResult.setInvalid();
|
|
return false;
|
|
}
|
|
|
|
bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
|
|
Block *B = getLocal(I);
|
|
S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
|
|
return true;
|
|
}
|
|
|
|
template <PrimType OpType>
|
|
bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
|
|
using T = typename PrimConv<OpType>::T;
|
|
|
|
Block *B = getLocal(I);
|
|
S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
|
|
return true;
|
|
}
|
|
|
|
template <PrimType OpType>
|
|
bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
|
|
using T = typename PrimConv<OpType>::T;
|
|
|
|
Block *B = getLocal(I);
|
|
*reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
|
|
InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
|
|
Desc.IsInitialized = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) {
|
|
if (!isActive())
|
|
return true;
|
|
|
|
for (auto &Local : Descriptors[I]) {
|
|
Block *B = getLocal(Local.Offset);
|
|
S.deallocate(B);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Opcode evaluators
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define GET_EVAL_IMPL
|
|
#include "Opcodes.inc"
|
|
#undef GET_EVAL_IMPL
|