blob: 20239c139d5ef4a8adb4974eeb43bcd50e1bdb91 [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_INTERPRETER_H_
#define RUNTIME_VM_INTERPRETER_H_
#include "vm/globals.h"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/compiler/method_recognizer.h"
#include "vm/constants_kbc.h"
#include "vm/object.h"
#include "vm/tagged_pointer.h"
namespace dart {
class Array;
class Code;
class InterpreterSetjmpBuffer;
class Isolate;
class ObjectPointerVisitor;
class Thread;
class LookupCache : public ValueObject {
public:
LookupCache() {
ASSERT(Utils::IsPowerOfTwo(sizeof(Entry)));
ASSERT(Utils::IsPowerOfTwo(sizeof(kNumEntries)));
Clear();
}
void Clear();
bool Lookup(intptr_t receiver_cid,
StringPtr function_name,
ArrayPtr arguments_descriptor,
FunctionPtr* target) const;
void Insert(intptr_t receiver_cid,
StringPtr function_name,
ArrayPtr arguments_descriptor,
FunctionPtr target);
private:
struct Entry {
intptr_t receiver_cid;
StringPtr function_name;
ArrayPtr arguments_descriptor;
FunctionPtr target;
};
static const intptr_t kNumEntries = 1024;
static const intptr_t kTableMask = kNumEntries - 1;
Entry entries_[kNumEntries];
};
class Interpreter {
public:
static const uword kInterpreterStackUnderflowSize = 0x80;
// The entry frame pc marker must be non-zero (a valid exception handler pc).
static const word kEntryFramePcMarker = -1;
Interpreter();
~Interpreter();
// The currently executing Interpreter instance, which is associated to the
// current isolate
static Interpreter* Current();
// Low address (KBC stack grows up).
uword stack_base() const { return stack_base_; }
// Limit for StackOverflowError.
uword overflow_stack_limit() const { return overflow_stack_limit_; }
// High address (KBC stack grows up).
uword stack_limit() const { return stack_limit_; }
// Returns true if the interpreter's stack contains the given frame.
// TODO(regis): We should rely on a new thread vm_tag to identify an
// interpreter frame and not need this HasFrame() method.
bool HasFrame(uword frame) const {
return frame >= stack_base() && frame < stack_limit();
}
// Identify an entry frame by looking at its pc marker value.
static bool IsEntryFrameMarker(const KBCInstr* pc) {
return reinterpret_cast<word>(pc) == kEntryFramePcMarker;
}
ObjectPtr Call(const Function& function,
const Array& arguments_descriptor,
const Array& arguments,
Thread* thread);
ObjectPtr Call(FunctionPtr function,
ArrayPtr argdesc,
intptr_t argc,
ObjectPtr const* argv,
ArrayPtr args_array,
Thread* thread);
ObjectPtr Resume(Thread* thread,
uword resumed_frame_fp,
uword resumed_frame_sp,
ObjectPtr value,
ObjectPtr exception,
ObjectPtr stack_trace);
BytecodePtr GetSuspendedLocation(const SuspendState& suspend_state,
uword* pc_offset);
void JumpToFrame(uword pc, uword sp, uword fp, Thread* thread);
uword get_sp() const { return reinterpret_cast<uword>(fp_); } // Yes, fp_.
uword get_fp() const { return reinterpret_cast<uword>(fp_); }
uword get_pc() const { return reinterpret_cast<uword>(pc_); }
void Unexit(Thread* thread);
void VisitObjectPointers(ObjectPointerVisitor* visitor);
void ClearLookupCache() { lookup_cache_.Clear(); }
#ifndef PRODUCT
void set_is_debugging(bool value) { is_debugging_ = value; }
bool is_debugging() const { return is_debugging_; }
#endif // !PRODUCT
private:
enum {
kKBCFunctionSlotInSuspendedFrame,
kKBCPcOffsetSlotInSuspendedFrame,
kKBCSuspendedFrameFixedSlots
};
uintptr_t* stack_;
uword stack_base_;
uword overflow_stack_limit_;
uword stack_limit_;
ObjectPtr* volatile fp_;
const KBCInstr* volatile pc_;
DEBUG_ONLY(uint64_t icount_;)
InterpreterSetjmpBuffer* last_setjmp_buffer_;
ObjectPoolPtr pp_; // Pool Pointer.
ArrayPtr argdesc_; // Arguments Descriptor: used to pass information between
// call instruction and the function entry.
SubtypeTestCachePtr subtype_test_cache_;
ObjectPtr special_[KernelBytecode::kSpecialIndexCount];
LookupCache lookup_cache_;
void Exit(Thread* thread,
ObjectPtr* base,
ObjectPtr* exit_frame,
const KBCInstr* pc);
bool Invoke(Thread* thread,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool InvokeCompiled(Thread* thread,
FunctionPtr function,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool InvokeBytecode(Thread* thread,
FunctionPtr function,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool InstanceCall(Thread* thread,
StringPtr target_name,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool CopyParameters(Thread* thread,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP,
const intptr_t num_fixed_params,
const intptr_t num_opt_pos_params,
const intptr_t num_opt_named_params,
const intptr_t num_reserved_locals);
bool AssertAssignable(Thread* thread,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* call_top,
ObjectPtr* args,
SubtypeTestCachePtr cache);
template <bool is_getter>
bool AssertAssignableField(Thread* thread,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP,
InstancePtr instance,
FieldPtr field,
InstancePtr value);
bool AllocateMint(Thread* thread,
int64_t value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateDouble(Thread* thread,
double value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateFloat32x4(Thread* thread,
simd128_value_t value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateFloat64x2(Thread* thread,
simd128_value_t value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateArray(Thread* thread,
TypeArgumentsPtr type_args,
ObjectPtr length,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateRecord(Thread* thread,
RecordShape shape,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateContext(Thread* thread,
intptr_t num_variables,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateClosure(Thread* thread,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
void SetupEntryFrame(Thread* thread);
ObjectPtr Run(Thread* thread, ObjectPtr* sp, bool rethrow_exception);
#if defined(DEBUG)
// Returns true if tracing of executed instructions is enabled.
bool IsTracingExecution() const;
// Prints bytecode instruction at given pc for instruction tracing.
void TraceInstruction(const KBCInstr* pc) const;
bool IsWritingTraceFile() const;
void FlushTraceBuffer();
void WriteInstructionToTrace(const KBCInstr* pc);
void* trace_file_;
uint64_t trace_file_bytes_written_;
static const intptr_t kTraceBufferSizeInBytes = 10 * KB;
static const intptr_t kTraceBufferInstrs =
kTraceBufferSizeInBytes / sizeof(KBCInstr);
KBCInstr* trace_buffer_;
intptr_t trace_buffer_idx_;
#endif // defined(DEBUG)
// Longjmp support for exceptions.
InterpreterSetjmpBuffer* last_setjmp_buffer() { return last_setjmp_buffer_; }
void set_last_setjmp_buffer(InterpreterSetjmpBuffer* buffer) {
last_setjmp_buffer_ = buffer;
}
#ifndef PRODUCT
bool is_debugging_ = false;
#endif // !PRODUCT
friend class InterpreterSetjmpBuffer;
DISALLOW_COPY_AND_ASSIGN(Interpreter);
};
} // namespace dart
#endif // defined(DART_DYNAMIC_MODULES)
#endif // RUNTIME_VM_INTERPRETER_H_