012b7050e5
Change-Id: I6256f0337d2ab84722d32d4e6af97b966c619162 Reviewed-on: https://dart-review.googlesource.com/75987 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Régis Crelier <regis@google.com>
4913 lines
159 KiB
C++
4913 lines
159 KiB
C++
// Copyright (c) 2018, 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.
|
|
|
|
#include <setjmp.h> // NOLINT
|
|
#include <stdlib.h>
|
|
|
|
#include "vm/globals.h"
|
|
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_OS_WINDOWS)
|
|
|
|
#include "vm/interpreter.h"
|
|
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/assembler/disassembler_kbc.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/cpu.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/debugger.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/native_arguments.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/stack_frame_kbc.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(uint64_t,
|
|
trace_interpreter_after,
|
|
ULLONG_MAX,
|
|
"Trace interpreter execution after instruction count reached.");
|
|
|
|
#define LIKELY(cond) __builtin_expect((cond), 1)
|
|
#define UNLIKELY(cond) __builtin_expect((cond), 0)
|
|
|
|
// InterpreterSetjmpBuffer are linked together, and the last created one
|
|
// is referenced by the Interpreter. When an exception is thrown, the exception
|
|
// runtime looks at where to jump and finds the corresponding
|
|
// InterpreterSetjmpBuffer based on the stack pointer of the exception handler.
|
|
// The runtime then does a Longjmp on that buffer to return to the interpreter.
|
|
class InterpreterSetjmpBuffer {
|
|
public:
|
|
void Longjmp() {
|
|
// "This" is now the last setjmp buffer.
|
|
interpreter_->set_last_setjmp_buffer(this);
|
|
longjmp(buffer_, 1);
|
|
}
|
|
|
|
explicit InterpreterSetjmpBuffer(Interpreter* interpreter) {
|
|
interpreter_ = interpreter;
|
|
link_ = interpreter->last_setjmp_buffer();
|
|
interpreter->set_last_setjmp_buffer(this);
|
|
fp_ = interpreter->fp_;
|
|
}
|
|
|
|
~InterpreterSetjmpBuffer() {
|
|
ASSERT(interpreter_->last_setjmp_buffer() == this);
|
|
interpreter_->set_last_setjmp_buffer(link_);
|
|
}
|
|
|
|
InterpreterSetjmpBuffer* link() const { return link_; }
|
|
|
|
uword fp() const { return reinterpret_cast<uword>(fp_); }
|
|
|
|
jmp_buf buffer_;
|
|
|
|
private:
|
|
RawObject** fp_;
|
|
Interpreter* interpreter_;
|
|
InterpreterSetjmpBuffer* link_;
|
|
|
|
friend class Interpreter;
|
|
|
|
DISALLOW_ALLOCATION();
|
|
DISALLOW_COPY_AND_ASSIGN(InterpreterSetjmpBuffer);
|
|
};
|
|
|
|
DART_FORCE_INLINE static RawObject** SavedCallerFP(RawObject** FP) {
|
|
return reinterpret_cast<RawObject**>(FP[kKBCSavedCallerFpSlotFromFp]);
|
|
}
|
|
|
|
DART_FORCE_INLINE static RawObject** FrameArguments(RawObject** FP,
|
|
intptr_t argc) {
|
|
return FP - (kKBCDartFrameFixedSize + argc);
|
|
}
|
|
|
|
#define RAW_CAST(Type, val) (InterpreterHelpers::CastTo##Type(val))
|
|
|
|
class InterpreterHelpers {
|
|
public:
|
|
#define DEFINE_CASTS(Type) \
|
|
DART_FORCE_INLINE static Raw##Type* CastTo##Type(RawObject* obj) { \
|
|
ASSERT((k##Type##Cid == kSmiCid) ? !obj->IsHeapObject() \
|
|
: obj->Is##Type()); \
|
|
return reinterpret_cast<Raw##Type*>(obj); \
|
|
}
|
|
CLASS_LIST(DEFINE_CASTS)
|
|
#undef DEFINE_CASTS
|
|
|
|
DART_FORCE_INLINE static RawSmi* GetClassIdAsSmi(RawObject* obj) {
|
|
return Smi::New(obj->IsHeapObject() ? obj->GetClassId()
|
|
: static_cast<intptr_t>(kSmiCid));
|
|
}
|
|
|
|
DART_FORCE_INLINE static intptr_t GetClassId(RawObject* obj) {
|
|
return obj->IsHeapObject() ? obj->GetClassId()
|
|
: static_cast<intptr_t>(kSmiCid);
|
|
}
|
|
|
|
DART_FORCE_INLINE static void IncrementUsageCounter(RawFunction* f) {
|
|
f->ptr()->usage_counter_++;
|
|
}
|
|
|
|
DART_FORCE_INLINE static void IncrementICUsageCount(RawObject** entries,
|
|
intptr_t offset,
|
|
intptr_t args_tested) {
|
|
const intptr_t count_offset = ICData::CountIndexFor(args_tested);
|
|
const intptr_t raw_smi_old =
|
|
reinterpret_cast<intptr_t>(entries[offset + count_offset]);
|
|
const intptr_t raw_smi_new = raw_smi_old + Smi::RawValue(1);
|
|
*reinterpret_cast<intptr_t*>(&entries[offset + count_offset]) = raw_smi_new;
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool IsStrictEqualWithNumberCheck(RawObject* lhs,
|
|
RawObject* rhs) {
|
|
if (lhs == rhs) {
|
|
return true;
|
|
}
|
|
|
|
if (lhs->IsHeapObject() && rhs->IsHeapObject()) {
|
|
const intptr_t lhs_cid = lhs->GetClassId();
|
|
const intptr_t rhs_cid = rhs->GetClassId();
|
|
if (lhs_cid == rhs_cid) {
|
|
switch (lhs_cid) {
|
|
case kDoubleCid:
|
|
return (bit_cast<uint64_t, double>(
|
|
static_cast<RawDouble*>(lhs)->ptr()->value_) ==
|
|
bit_cast<uint64_t, double>(
|
|
static_cast<RawDouble*>(rhs)->ptr()->value_));
|
|
|
|
case kMintCid:
|
|
return (static_cast<RawMint*>(lhs)->ptr()->value_ ==
|
|
static_cast<RawMint*>(rhs)->ptr()->value_);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
DART_FORCE_INLINE static T* Untag(T* tagged) {
|
|
return tagged->ptr();
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool CheckIndex(RawSmi* index, RawSmi* length) {
|
|
return !index->IsHeapObject() && (reinterpret_cast<intptr_t>(index) >= 0) &&
|
|
(reinterpret_cast<intptr_t>(index) <
|
|
reinterpret_cast<intptr_t>(length));
|
|
}
|
|
|
|
DART_FORCE_INLINE static intptr_t ArgDescTypeArgsLen(RawArray* argdesc) {
|
|
return Smi::Value(*reinterpret_cast<RawSmi**>(
|
|
reinterpret_cast<uword>(argdesc->ptr()) +
|
|
Array::element_offset(ArgumentsDescriptor::kTypeArgsLenIndex)));
|
|
}
|
|
|
|
DART_FORCE_INLINE static intptr_t ArgDescArgCount(RawArray* argdesc) {
|
|
return Smi::Value(*reinterpret_cast<RawSmi**>(
|
|
reinterpret_cast<uword>(argdesc->ptr()) +
|
|
Array::element_offset(ArgumentsDescriptor::kCountIndex)));
|
|
}
|
|
|
|
DART_FORCE_INLINE static intptr_t ArgDescPosCount(RawArray* argdesc) {
|
|
return Smi::Value(*reinterpret_cast<RawSmi**>(
|
|
reinterpret_cast<uword>(argdesc->ptr()) +
|
|
Array::element_offset(ArgumentsDescriptor::kPositionalCountIndex)));
|
|
}
|
|
|
|
static bool ObjectArraySetIndexed(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
return !thread->isolate()->type_checks() &&
|
|
ObjectArraySetIndexedUnchecked(thread, FP, result);
|
|
}
|
|
|
|
static bool ObjectArraySetIndexedUnchecked(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 3);
|
|
RawSmi* index = static_cast<RawSmi*>(args[1]);
|
|
RawArray* array = static_cast<RawArray*>(args[0]);
|
|
if (CheckIndex(index, array->ptr()->length_)) {
|
|
array->StorePointer(array->ptr()->data() + Smi::Value(index), args[2],
|
|
thread);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool ObjectArrayGetIndexed(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 2);
|
|
RawSmi* index = static_cast<RawSmi*>(args[1]);
|
|
RawArray* array = static_cast<RawArray*>(args[0]);
|
|
if (CheckIndex(index, array->ptr()->length_)) {
|
|
*result = array->ptr()->data()[Smi::Value(index)];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool GrowableArraySetIndexed(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
return !thread->isolate()->type_checks() &&
|
|
GrowableArraySetIndexedUnchecked(thread, FP, result);
|
|
}
|
|
|
|
static bool GrowableArraySetIndexedUnchecked(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 3);
|
|
RawSmi* index = static_cast<RawSmi*>(args[1]);
|
|
RawGrowableObjectArray* array =
|
|
static_cast<RawGrowableObjectArray*>(args[0]);
|
|
if (CheckIndex(index, array->ptr()->length_)) {
|
|
RawArray* data = array->ptr()->data_;
|
|
data->StorePointer(data->ptr()->data() + Smi::Value(index), args[2],
|
|
thread);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool GrowableArrayGetIndexed(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 2);
|
|
RawSmi* index = static_cast<RawSmi*>(args[1]);
|
|
RawGrowableObjectArray* array =
|
|
static_cast<RawGrowableObjectArray*>(args[0]);
|
|
if (CheckIndex(index, array->ptr()->length_)) {
|
|
*result = array->ptr()->data_->ptr()->data()[Smi::Value(index)];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool Double_getIsNan(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 1);
|
|
RawDouble* d = static_cast<RawDouble*>(args[0]);
|
|
*result =
|
|
isnan(d->ptr()->value_) ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool Double_getIsInfinite(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 1);
|
|
RawDouble* d = static_cast<RawDouble*>(args[0]);
|
|
*result =
|
|
isinf(d->ptr()->value_) ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool ObjectEquals(Thread* thread, RawObject** FP, RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 2);
|
|
*result = args[0] == args[1] ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool ObjectRuntimeType(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 1);
|
|
const intptr_t cid = GetClassId(args[0]);
|
|
if (cid == kClosureCid) {
|
|
return false;
|
|
}
|
|
if (cid < kNumPredefinedCids) {
|
|
if (cid == kDoubleCid) {
|
|
*result = thread->isolate()->object_store()->double_type();
|
|
return true;
|
|
} else if (RawObject::IsStringClassId(cid)) {
|
|
*result = thread->isolate()->object_store()->string_type();
|
|
return true;
|
|
} else if (RawObject::IsIntegerClassId(cid)) {
|
|
*result = thread->isolate()->object_store()->int_type();
|
|
return true;
|
|
}
|
|
}
|
|
RawClass* cls = thread->isolate()->class_table()->At(cid);
|
|
if (cls->ptr()->num_type_arguments_ != 0) {
|
|
return false;
|
|
}
|
|
RawType* typ = cls->ptr()->canonical_type_;
|
|
if (typ == Object::null()) {
|
|
return false;
|
|
}
|
|
*result = static_cast<RawObject*>(typ);
|
|
return true;
|
|
}
|
|
|
|
static bool GetDoubleOperands(RawObject** args, double* d1, double* d2) {
|
|
RawObject* obj2 = args[1];
|
|
if (!obj2->IsHeapObject()) {
|
|
*d2 =
|
|
static_cast<double>(reinterpret_cast<intptr_t>(obj2) >> kSmiTagSize);
|
|
} else if (obj2->GetClassId() == kDoubleCid) {
|
|
RawDouble* obj2d = static_cast<RawDouble*>(obj2);
|
|
*d2 = obj2d->ptr()->value_;
|
|
} else {
|
|
return false;
|
|
}
|
|
RawDouble* obj1 = static_cast<RawDouble*>(args[0]);
|
|
*d1 = obj1->ptr()->value_;
|
|
return true;
|
|
}
|
|
|
|
static RawObject* AllocateDouble(Thread* thread, double value) {
|
|
const intptr_t instance_size = Double::InstanceSize();
|
|
const uword start =
|
|
thread->heap()->new_space()->TryAllocateInTLAB(thread, instance_size);
|
|
if (LIKELY(start != 0)) {
|
|
uword tags = 0;
|
|
tags = RawObject::ClassIdTag::update(kDoubleCid, tags);
|
|
tags = RawObject::SizeTag::update(instance_size, tags);
|
|
tags = RawObject::NewBit::update(true, tags);
|
|
// Also writes zero in the hash_ field.
|
|
*reinterpret_cast<uword*>(start + Double::tags_offset()) = tags;
|
|
*reinterpret_cast<double*>(start + Double::value_offset()) = value;
|
|
return reinterpret_cast<RawObject*>(start + kHeapObjectTag);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool Double_add(Thread* thread, RawObject** FP, RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
RawObject* new_double = AllocateDouble(thread, d1 + d2);
|
|
if (new_double != NULL) {
|
|
*result = new_double;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool Double_mul(Thread* thread, RawObject** FP, RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
RawObject* new_double = AllocateDouble(thread, d1 * d2);
|
|
if (new_double != NULL) {
|
|
*result = new_double;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool Double_sub(Thread* thread, RawObject** FP, RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
RawObject* new_double = AllocateDouble(thread, d1 - d2);
|
|
if (new_double != NULL) {
|
|
*result = new_double;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool Double_div(Thread* thread, RawObject** FP, RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
RawObject* new_double = AllocateDouble(thread, d1 / d2);
|
|
if (new_double != NULL) {
|
|
*result = new_double;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool Double_greaterThan(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
*result = d1 > d2 ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool Double_greaterEqualThan(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
*result = d1 >= d2 ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool Double_lessThan(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
*result = d1 < d2 ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool Double_equal(Thread* thread, RawObject** FP, RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
*result = d1 == d2 ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool Double_lessEqualThan(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
double d1, d2;
|
|
if (!GetDoubleOperands(FrameArguments(FP, 2), &d1, &d2)) {
|
|
return false;
|
|
}
|
|
*result = d1 <= d2 ? Bool::True().raw() : Bool::False().raw();
|
|
return true;
|
|
}
|
|
|
|
static bool ClearAsyncThreadStack(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
thread->clear_async_stack_trace();
|
|
*result = Object::null();
|
|
return true;
|
|
}
|
|
|
|
static bool SetAsyncThreadStackTrace(Thread* thread,
|
|
RawObject** FP,
|
|
RawObject** result) {
|
|
RawObject** args = FrameArguments(FP, 1);
|
|
thread->set_raw_async_stack_trace(
|
|
reinterpret_cast<RawStackTrace*>(args[0]));
|
|
*result = Object::null();
|
|
return true;
|
|
}
|
|
|
|
DART_FORCE_INLINE static RawCode* FrameCode(RawObject** FP) {
|
|
ASSERT(GetClassId(FP[kKBCPcMarkerSlotFromFp]) == kCodeCid);
|
|
return static_cast<RawCode*>(FP[kKBCPcMarkerSlotFromFp]);
|
|
}
|
|
|
|
DART_FORCE_INLINE static void SetFrameCode(RawObject** FP, RawCode* code) {
|
|
ASSERT(GetClassId(code) == kCodeCid);
|
|
FP[kKBCPcMarkerSlotFromFp] = code;
|
|
}
|
|
|
|
DART_FORCE_INLINE static uint8_t* GetTypedData(RawObject* obj,
|
|
RawObject* index) {
|
|
ASSERT(RawObject::IsTypedDataClassId(obj->GetClassId()));
|
|
RawTypedData* array = reinterpret_cast<RawTypedData*>(obj);
|
|
const intptr_t byte_offset = Smi::Value(RAW_CAST(Smi, index));
|
|
ASSERT(byte_offset >= 0);
|
|
return array->ptr()->data() + byte_offset;
|
|
}
|
|
};
|
|
|
|
DART_FORCE_INLINE static uint32_t* SavedCallerPC(RawObject** FP) {
|
|
return reinterpret_cast<uint32_t*>(FP[kKBCSavedCallerPcSlotFromFp]);
|
|
}
|
|
|
|
DART_FORCE_INLINE static RawFunction* FrameFunction(RawObject** FP) {
|
|
RawFunction* function = static_cast<RawFunction*>(FP[kKBCFunctionSlotFromFp]);
|
|
ASSERT(InterpreterHelpers::GetClassId(function) == kFunctionCid ||
|
|
InterpreterHelpers::GetClassId(function) == kNullCid);
|
|
return function;
|
|
}
|
|
|
|
IntrinsicHandler Interpreter::intrinsics_[Interpreter::kIntrinsicCount];
|
|
|
|
// Synchronization primitives support.
|
|
void Interpreter::InitOnce() {
|
|
for (intptr_t i = 0; i < kIntrinsicCount; i++) {
|
|
intrinsics_[i] = 0;
|
|
}
|
|
|
|
intrinsics_[kObjectArraySetIndexedIntrinsic] =
|
|
InterpreterHelpers::ObjectArraySetIndexed;
|
|
intrinsics_[kObjectArraySetIndexedUncheckedIntrinsic] =
|
|
InterpreterHelpers::ObjectArraySetIndexedUnchecked;
|
|
intrinsics_[kObjectArrayGetIndexedIntrinsic] =
|
|
InterpreterHelpers::ObjectArrayGetIndexed;
|
|
intrinsics_[kGrowableArraySetIndexedIntrinsic] =
|
|
InterpreterHelpers::GrowableArraySetIndexed;
|
|
intrinsics_[kGrowableArraySetIndexedUncheckedIntrinsic] =
|
|
InterpreterHelpers::GrowableArraySetIndexedUnchecked;
|
|
intrinsics_[kGrowableArrayGetIndexedIntrinsic] =
|
|
InterpreterHelpers::GrowableArrayGetIndexed;
|
|
intrinsics_[kObjectEqualsIntrinsic] = InterpreterHelpers::ObjectEquals;
|
|
intrinsics_[kObjectRuntimeTypeIntrinsic] =
|
|
InterpreterHelpers::ObjectRuntimeType;
|
|
|
|
intrinsics_[kDouble_getIsNaNIntrinsic] = InterpreterHelpers::Double_getIsNan;
|
|
intrinsics_[kDouble_getIsInfiniteIntrinsic] =
|
|
InterpreterHelpers::Double_getIsInfinite;
|
|
intrinsics_[kDouble_addIntrinsic] = InterpreterHelpers::Double_add;
|
|
intrinsics_[kDouble_mulIntrinsic] = InterpreterHelpers::Double_mul;
|
|
intrinsics_[kDouble_subIntrinsic] = InterpreterHelpers::Double_sub;
|
|
intrinsics_[kDouble_divIntrinsic] = InterpreterHelpers::Double_div;
|
|
intrinsics_[kDouble_greaterThanIntrinsic] =
|
|
InterpreterHelpers::Double_greaterThan;
|
|
intrinsics_[kDouble_greaterEqualThanIntrinsic] =
|
|
InterpreterHelpers::Double_greaterEqualThan;
|
|
intrinsics_[kDouble_lessThanIntrinsic] = InterpreterHelpers::Double_lessThan;
|
|
intrinsics_[kDouble_equalIntrinsic] = InterpreterHelpers::Double_equal;
|
|
intrinsics_[kDouble_lessEqualThanIntrinsic] =
|
|
InterpreterHelpers::Double_lessEqualThan;
|
|
intrinsics_[kClearAsyncThreadStackTraceIntrinsic] =
|
|
InterpreterHelpers::ClearAsyncThreadStack;
|
|
intrinsics_[kSetAsyncThreadStackTraceIntrinsic] =
|
|
InterpreterHelpers::SetAsyncThreadStackTrace;
|
|
}
|
|
|
|
Interpreter::Interpreter()
|
|
: stack_(NULL), fp_(NULL), pp_(NULL), argdesc_(NULL) {
|
|
// Setup interpreter support first. Some of this information is needed to
|
|
// setup the architecture state.
|
|
// We allocate the stack here, the size is computed as the sum of
|
|
// the size specified by the user and the buffer space needed for
|
|
// handling stack overflow exceptions. To be safe in potential
|
|
// stack underflows we also add some underflow buffer space.
|
|
stack_ = new uintptr_t[(OSThread::GetSpecifiedStackSize() +
|
|
OSThread::kStackSizeBuffer +
|
|
kInterpreterStackUnderflowSize) /
|
|
sizeof(uintptr_t)];
|
|
// Low address.
|
|
stack_base_ =
|
|
reinterpret_cast<uword>(stack_) + kInterpreterStackUnderflowSize;
|
|
// High address.
|
|
stack_limit_ = stack_base_ + OSThread::GetSpecifiedStackSize();
|
|
|
|
last_setjmp_buffer_ = NULL;
|
|
|
|
DEBUG_ONLY(icount_ = 1); // So that tracing after 0 traces first bytecode.
|
|
}
|
|
|
|
Interpreter::~Interpreter() {
|
|
delete[] stack_;
|
|
Isolate* isolate = Isolate::Current();
|
|
if (isolate != NULL) {
|
|
isolate->set_interpreter(NULL);
|
|
}
|
|
}
|
|
|
|
// Get the active Interpreter for the current isolate.
|
|
Interpreter* Interpreter::Current() {
|
|
Interpreter* interpreter = Isolate::Current()->interpreter();
|
|
if (interpreter == NULL) {
|
|
interpreter = new Interpreter();
|
|
Isolate::Current()->set_interpreter(interpreter);
|
|
}
|
|
return interpreter;
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
// Returns true if tracing of executed instructions is enabled.
|
|
// May be called on entry, when icount_ has not been incremented yet.
|
|
DART_FORCE_INLINE bool Interpreter::IsTracingExecution() const {
|
|
return icount_ > FLAG_trace_interpreter_after;
|
|
}
|
|
|
|
// Prints bytecode instruction at given pc for instruction tracing.
|
|
DART_NOINLINE void Interpreter::TraceInstruction(uint32_t* pc) const {
|
|
THR_Print("%" Pu64 " ", icount_);
|
|
if (FLAG_support_disassembler) {
|
|
KernelBytecodeDisassembler::Disassemble(reinterpret_cast<uword>(pc),
|
|
reinterpret_cast<uword>(pc + 1));
|
|
} else {
|
|
THR_Print("Disassembler not supported in this mode.\n");
|
|
}
|
|
}
|
|
#endif // defined(DEBUG)
|
|
|
|
// Calls into the Dart runtime are based on this interface.
|
|
typedef void (*InterpreterRuntimeCall)(NativeArguments arguments);
|
|
|
|
// Calls to leaf Dart runtime functions are based on this interface.
|
|
typedef intptr_t (*InterpreterLeafRuntimeCall)(intptr_t r0,
|
|
intptr_t r1,
|
|
intptr_t r2,
|
|
intptr_t r3);
|
|
|
|
// Calls to leaf float Dart runtime functions are based on this interface.
|
|
typedef double (*InterpreterLeafFloatRuntimeCall)(double d0, double d1);
|
|
|
|
void Interpreter::Exit(Thread* thread,
|
|
RawObject** base,
|
|
RawObject** frame,
|
|
uint32_t* pc) {
|
|
frame[0] = Function::null();
|
|
frame[1] = Code::null();
|
|
frame[2] = reinterpret_cast<RawObject*>(pc);
|
|
frame[3] = reinterpret_cast<RawObject*>(base);
|
|
fp_ = frame + kKBCDartFrameFixedSize;
|
|
thread->set_top_exit_frame_info(reinterpret_cast<uword>(fp_));
|
|
#if defined(DEBUG)
|
|
if (IsTracingExecution()) {
|
|
THR_Print("%" Pu64 " ", icount_);
|
|
THR_Print("Exiting interpreter 0x%" Px " at fp_ 0x%" Px "\n",
|
|
reinterpret_cast<uword>(this), reinterpret_cast<uword>(fp_));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// TODO(vegorov): Investigate advantages of using
|
|
// __builtin_s{add,sub,mul}_overflow() intrinsics here and below.
|
|
// Note that they may clobber the output location even when there is overflow:
|
|
// https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
|
|
DART_FORCE_INLINE static bool SignedAddWithOverflow(int32_t lhs,
|
|
int32_t rhs,
|
|
intptr_t* out) {
|
|
intptr_t res = 1;
|
|
#if defined(HOST_ARCH_IA32)
|
|
asm volatile(
|
|
"add %2, %1\n"
|
|
"jo 1f;\n"
|
|
"xor %0, %0\n"
|
|
"mov %1, 0(%3)\n"
|
|
"1: "
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_X64)
|
|
int64_t tmp;
|
|
asm volatile(
|
|
"addl %[rhs], %[lhs]\n"
|
|
"jo 1f;\n"
|
|
"xor %[res], %[res]\n"
|
|
"movslq %[lhs], %[tmp]\n"
|
|
"mov %[tmp], 0(%[out])\n"
|
|
"1: "
|
|
: [res] "+r"(res), [lhs] "+r"(lhs), [tmp] "=&r"(tmp)
|
|
: [rhs] "r"(rhs), [out] "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_ARM)
|
|
asm volatile(
|
|
"adds %1, %1, %2;\n"
|
|
"bvs 1f;\n"
|
|
"mov %0, #0;\n"
|
|
"str %1, [%3, #0]\n"
|
|
"1:"
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_ARM64)
|
|
asm volatile(
|
|
"adds %w1, %w1, %w2;\n"
|
|
"bvs 1f;\n"
|
|
"sxtw %x1, %w1;\n"
|
|
"mov %0, #0;\n"
|
|
"str %x1, [%3, #0]\n"
|
|
"1:"
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
return (res != 0);
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool SignedSubWithOverflow(int32_t lhs,
|
|
int32_t rhs,
|
|
intptr_t* out) {
|
|
intptr_t res = 1;
|
|
#if defined(HOST_ARCH_IA32)
|
|
asm volatile(
|
|
"sub %2, %1\n"
|
|
"jo 1f;\n"
|
|
"xor %0, %0\n"
|
|
"mov %1, 0(%3)\n"
|
|
"1: "
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_X64)
|
|
int64_t tmp;
|
|
asm volatile(
|
|
"subl %[rhs], %[lhs]\n"
|
|
"jo 1f;\n"
|
|
"xor %[res], %[res]\n"
|
|
"movslq %[lhs], %[tmp]\n"
|
|
"mov %[tmp], 0(%[out])\n"
|
|
"1: "
|
|
: [res] "+r"(res), [lhs] "+r"(lhs), [tmp] "=&r"(tmp)
|
|
: [rhs] "r"(rhs), [out] "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_ARM)
|
|
asm volatile(
|
|
"subs %1, %1, %2;\n"
|
|
"bvs 1f;\n"
|
|
"mov %0, #0;\n"
|
|
"str %1, [%3, #0]\n"
|
|
"1:"
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_ARM64)
|
|
asm volatile(
|
|
"subs %w1, %w1, %w2;\n"
|
|
"bvs 1f;\n"
|
|
"sxtw %x1, %w1;\n"
|
|
"mov %0, #0;\n"
|
|
"str %x1, [%3, #0]\n"
|
|
"1:"
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
return (res != 0);
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool SignedMulWithOverflow(int32_t lhs,
|
|
int32_t rhs,
|
|
intptr_t* out) {
|
|
intptr_t res = 1;
|
|
#if defined(HOST_ARCH_IA32)
|
|
asm volatile(
|
|
"imul %2, %1\n"
|
|
"jo 1f;\n"
|
|
"xor %0, %0\n"
|
|
"mov %1, 0(%3)\n"
|
|
"1: "
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_X64)
|
|
int64_t tmp;
|
|
asm volatile(
|
|
"imull %[rhs], %[lhs]\n"
|
|
"jo 1f;\n"
|
|
"xor %[res], %[res]\n"
|
|
"movslq %[lhs], %[tmp]\n"
|
|
"mov %[tmp], 0(%[out])\n"
|
|
"1: "
|
|
: [res] "+r"(res), [lhs] "+r"(lhs), [tmp] "=&r"(tmp)
|
|
: [rhs] "r"(rhs), [out] "r"(out)
|
|
: "cc");
|
|
#elif defined(HOST_ARCH_ARM)
|
|
asm volatile(
|
|
"smull %1, ip, %1, %2;\n"
|
|
"cmp ip, %1, ASR #31;\n"
|
|
"bne 1f;\n"
|
|
"mov %0, $0;\n"
|
|
"str %1, [%3, #0]\n"
|
|
"1:"
|
|
: "+r"(res), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc", "r12");
|
|
#elif defined(HOST_ARCH_ARM64)
|
|
int64_t prod_lo = 0;
|
|
asm volatile(
|
|
"smull %x1, %w2, %w3\n"
|
|
"asr %x2, %x1, #63\n"
|
|
"cmp %x2, %x1, ASR #31;\n"
|
|
"bne 1f;\n"
|
|
"mov %0, #0;\n"
|
|
"str %x1, [%4, #0]\n"
|
|
"1:"
|
|
: "=r"(res), "+r"(prod_lo), "+r"(lhs)
|
|
: "r"(rhs), "r"(out)
|
|
: "cc");
|
|
#else
|
|
#error "Unsupported platform"
|
|
#endif
|
|
return (res != 0);
|
|
}
|
|
|
|
DART_FORCE_INLINE static bool AreBothSmis(intptr_t a, intptr_t b) {
|
|
return ((a | b) & kHeapObjectTag) == 0;
|
|
}
|
|
|
|
#define SMI_MUL(lhs, rhs, pres) SignedMulWithOverflow((lhs), (rhs) >> 1, pres)
|
|
#define SMI_COND(cond, lhs, rhs, pres) \
|
|
((*(pres) = ((lhs cond rhs) ? true_value : false_value)), false)
|
|
#define SMI_EQ(lhs, rhs, pres) SMI_COND(==, lhs, rhs, pres)
|
|
#define SMI_LT(lhs, rhs, pres) SMI_COND(<, lhs, rhs, pres)
|
|
#define SMI_GT(lhs, rhs, pres) SMI_COND(>, lhs, rhs, pres)
|
|
#define SMI_BITOR(lhs, rhs, pres) ((*(pres) = (lhs | rhs)), false)
|
|
#define SMI_BITAND(lhs, rhs, pres) ((*(pres) = ((lhs) & (rhs))), false)
|
|
#define SMI_BITXOR(lhs, rhs, pres) ((*(pres) = ((lhs) ^ (rhs))), false)
|
|
|
|
void Interpreter::CallRuntime(Thread* thread,
|
|
RawObject** base,
|
|
RawObject** exit_frame,
|
|
uint32_t* pc,
|
|
intptr_t argc_tag,
|
|
RawObject** args,
|
|
RawObject** result,
|
|
uword target) {
|
|
Exit(thread, base, exit_frame, pc);
|
|
NativeArguments native_args(thread, argc_tag, args, result);
|
|
reinterpret_cast<RuntimeFunction>(target)(native_args);
|
|
}
|
|
|
|
DART_FORCE_INLINE static void EnterSyntheticFrame(RawObject*** FP,
|
|
RawObject*** SP,
|
|
uint32_t* pc) {
|
|
RawObject** fp = *SP + kKBCDartFrameFixedSize;
|
|
fp[kKBCPcMarkerSlotFromFp] = 0;
|
|
fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(pc);
|
|
fp[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(*FP);
|
|
*FP = fp;
|
|
*SP = fp - 1;
|
|
}
|
|
|
|
DART_FORCE_INLINE static void LeaveSyntheticFrame(RawObject*** FP,
|
|
RawObject*** SP) {
|
|
RawObject** fp = *FP;
|
|
*FP = reinterpret_cast<RawObject**>(fp[kKBCSavedCallerFpSlotFromFp]);
|
|
*SP = fp - kKBCDartFrameFixedSize;
|
|
}
|
|
|
|
// Note: functions below are marked DART_NOINLINE to recover performance on
|
|
// ARM where inlining these functions into the interpreter loop seemed to cause
|
|
// some code quality issues.
|
|
static DART_NOINLINE bool InvokeRuntime(Thread* thread,
|
|
Interpreter* interpreter,
|
|
RuntimeFunction drt,
|
|
const NativeArguments& args) {
|
|
InterpreterSetjmpBuffer buffer(interpreter);
|
|
if (!setjmp(buffer.buffer_)) {
|
|
thread->set_vm_tag(reinterpret_cast<uword>(drt));
|
|
drt(args);
|
|
thread->set_vm_tag(VMTag::kDartTagId);
|
|
thread->set_top_exit_frame_info(0);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static DART_NOINLINE bool InvokeNative(Thread* thread,
|
|
Interpreter* interpreter,
|
|
NativeFunctionWrapper wrapper,
|
|
Dart_NativeFunction function,
|
|
Dart_NativeArguments args) {
|
|
InterpreterSetjmpBuffer buffer(interpreter);
|
|
if (!setjmp(buffer.buffer_)) {
|
|
thread->set_vm_tag(reinterpret_cast<uword>(function));
|
|
wrapper(args, function);
|
|
thread->set_vm_tag(VMTag::kDartTagId);
|
|
thread->set_top_exit_frame_info(0);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
DART_NOINLINE bool Interpreter::InvokeCompiled(Thread* thread,
|
|
RawFunction* function,
|
|
RawObject** call_base,
|
|
RawObject** call_top,
|
|
uint32_t** pc,
|
|
RawObject*** FP,
|
|
RawObject*** SP) {
|
|
#if defined(USING_SIMULATOR) || defined(TARGET_ARCH_DBC)
|
|
// TODO(regis): Revisit.
|
|
UNIMPLEMENTED();
|
|
#endif
|
|
ASSERT(Function::HasCode(function));
|
|
RawCode* volatile code = function->ptr()->code_;
|
|
ASSERT(code != StubCode::LazyCompile_entry()->code());
|
|
// TODO(regis): Once we share the same stack, try to invoke directly.
|
|
#if defined(DEBUG)
|
|
if (IsTracingExecution()) {
|
|
THR_Print("%" Pu64 " ", icount_);
|
|
THR_Print("invoking compiled %s\n", Function::Handle(function).ToCString());
|
|
}
|
|
#endif
|
|
// On success, returns a RawInstance. On failure, a RawError.
|
|
typedef RawObject* (*invokestub)(RawCode * code, RawArray * argdesc,
|
|
RawObject * *arg0, Thread * thread);
|
|
invokestub volatile entrypoint = reinterpret_cast<invokestub>(
|
|
StubCode::InvokeDartCodeFromBytecode_entry()->EntryPoint());
|
|
RawObject* volatile result;
|
|
Exit(thread, *FP, call_top + 1, *pc);
|
|
{
|
|
InterpreterSetjmpBuffer buffer(this);
|
|
if (!setjmp(buffer.buffer_)) {
|
|
thread->set_vm_tag(reinterpret_cast<uword>(entrypoint));
|
|
result = entrypoint(code, argdesc_, call_base, thread);
|
|
thread->set_vm_tag(VMTag::kDartTagId);
|
|
thread->set_top_exit_frame_info(0);
|
|
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
// Pop args and push result.
|
|
*SP = call_base;
|
|
**SP = result;
|
|
pp_ = InterpreterHelpers::FrameCode(*FP)->ptr()->object_pool_;
|
|
|
|
// If the result is an error (not a Dart instance), it must either be rethrown
|
|
// (in the case of an unhandled exception) or it must be returned to the
|
|
// caller of the interpreter to be propagated.
|
|
if (result->IsHeapObject()) {
|
|
const intptr_t result_cid = result->GetClassId();
|
|
if (result_cid == kUnhandledExceptionCid) {
|
|
(*SP)[0] = UnhandledException::RawCast(result)->ptr()->exception_;
|
|
(*SP)[1] = UnhandledException::RawCast(result)->ptr()->stacktrace_;
|
|
(*SP)[2] = 0; // Space for result.
|
|
Exit(thread, *FP, *SP + 3, *pc);
|
|
NativeArguments args(thread, 2, *SP, *SP + 2);
|
|
if (!InvokeRuntime(thread, this, DRT_ReThrow, args)) {
|
|
return false;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
if (RawObject::IsErrorClassId(result_cid)) {
|
|
// Unwind to entry frame.
|
|
fp_ = *FP;
|
|
pc_ = reinterpret_cast<uword>(SavedCallerPC(fp_));
|
|
while (!IsEntryFrameMarker(pc_)) {
|
|
fp_ = SavedCallerFP(fp_);
|
|
pc_ = reinterpret_cast<uword>(SavedCallerPC(fp_));
|
|
}
|
|
// Pop entry frame.
|
|
fp_ = SavedCallerFP(fp_);
|
|
special_[KernelBytecode::kExceptionSpecialIndex] = result;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DART_NOINLINE bool Interpreter::ProcessInvocation(bool* invoked,
|
|
Thread* thread,
|
|
RawFunction* function,
|
|
RawObject** call_base,
|
|
RawObject** call_top,
|
|
uint32_t** pc,
|
|
RawObject*** FP,
|
|
RawObject*** SP) {
|
|
ASSERT(!Function::HasCode(function) && !Function::HasBytecode(function));
|
|
// If the function is an implicit getter or setter, process its invocation
|
|
// here without code or bytecode.
|
|
RawFunction::Kind kind = Function::kind(function);
|
|
switch (kind) {
|
|
case RawFunction::kImplicitGetter: {
|
|
// Field object is cached in function's data_.
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(*call_base);
|
|
RawField* field = reinterpret_cast<RawField*>(function->ptr()->data_);
|
|
intptr_t offset_in_words = Smi::Value(field->ptr()->value_.offset_);
|
|
*SP = call_base;
|
|
**SP = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
*invoked = true;
|
|
return true;
|
|
}
|
|
case RawFunction::kImplicitSetter: {
|
|
// Field object is cached in function's data_.
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(*call_base);
|
|
RawField* field = reinterpret_cast<RawField*>(function->ptr()->data_);
|
|
intptr_t offset_in_words = Smi::Value(field->ptr()->value_.offset_);
|
|
RawAbstractType* field_type = field->ptr()->type_;
|
|
classid_t cid;
|
|
if (field_type->GetClassId() == kTypeCid) {
|
|
cid = Smi::Value(reinterpret_cast<RawSmi*>(
|
|
Type::RawCast(field_type)->ptr()->type_class_id_));
|
|
} else {
|
|
cid = kIllegalCid; // Not really illegal, but not a Type to skip.
|
|
}
|
|
// Perform type test of value if field type is not one of dynamic, object,
|
|
// or void, and if the value is not null.
|
|
RawObject* null_value = Object::null();
|
|
RawObject* value = *(call_base + 1);
|
|
if (cid != kDynamicCid && cid != kInstanceCid && cid != kVoidCid &&
|
|
value != null_value) {
|
|
RawSubtypeTestCache* cache = field->ptr()->type_test_cache_;
|
|
if (cache->GetClassId() != kSubtypeTestCacheCid) {
|
|
// Allocate new cache.
|
|
call_top[1] = null_value; // Result.
|
|
Exit(thread, *FP, call_top + 2, *pc);
|
|
NativeArguments native_args(thread, 0, call_top + 1, call_top + 1);
|
|
if (!InvokeRuntime(thread, this, DRT_AllocateSubtypeTestCache,
|
|
native_args)) {
|
|
*invoked = true;
|
|
return false;
|
|
}
|
|
cache = reinterpret_cast<RawSubtypeTestCache*>(call_top[1]);
|
|
field->ptr()->type_test_cache_ = cache;
|
|
}
|
|
// Push arguments of type test.
|
|
// Type checked value is at call_base + 1.
|
|
// Provide type arguments of instance as instantiator.
|
|
RawClass* instance_class = thread->isolate()->class_table()->At(
|
|
InterpreterHelpers::GetClassId(instance));
|
|
call_base[2] =
|
|
instance_class->ptr()->num_type_arguments_ > 0
|
|
? reinterpret_cast<RawObject**>(
|
|
instance
|
|
->ptr())[instance_class->ptr()
|
|
->type_arguments_field_offset_in_words_]
|
|
: null_value;
|
|
call_base[3] = null_value; // Implicit setters cannot be generic.
|
|
call_base[4] = field_type;
|
|
call_base[5] = field->ptr()->name_;
|
|
if (!AssertAssignable(thread, *pc, *FP, call_base + 5, call_base + 1,
|
|
cache)) {
|
|
*invoked = true;
|
|
return false;
|
|
}
|
|
}
|
|
if (thread->isolate()->use_field_guards()) {
|
|
// Check value cid according to field.guarded_cid().
|
|
// The interpreter should never see a cloned field.
|
|
ASSERT(field->ptr()->owner_->GetClassId() != kFieldCid);
|
|
const classid_t field_guarded_cid = field->ptr()->guarded_cid_;
|
|
const classid_t field_nullability_cid = field->ptr()->is_nullable_;
|
|
const classid_t value_cid = InterpreterHelpers::GetClassId(value);
|
|
if (value_cid != field_guarded_cid &&
|
|
value_cid != field_nullability_cid) {
|
|
if (Smi::Value(field->ptr()->guarded_list_length_) <
|
|
Field::kUnknownFixedLength &&
|
|
field_guarded_cid == kIllegalCid) {
|
|
field->ptr()->guarded_cid_ = value_cid;
|
|
field->ptr()->is_nullable_ = value_cid;
|
|
} else if (field_guarded_cid != kDynamicCid) {
|
|
call_top[1] = 0; // Unused result of runtime call.
|
|
call_top[2] = field;
|
|
call_top[3] = value;
|
|
Exit(thread, *FP, call_top + 4, *pc);
|
|
NativeArguments native_args(thread, 2, call_top + 2, call_top + 1);
|
|
if (!InvokeRuntime(thread, this, DRT_UpdateFieldCid, native_args)) {
|
|
*invoked = true;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
instance->StorePointer(
|
|
reinterpret_cast<RawObject**>(instance->ptr()) + offset_in_words,
|
|
value, thread);
|
|
*SP = call_base;
|
|
**SP = null_value;
|
|
*invoked = true;
|
|
return true;
|
|
}
|
|
case RawFunction::kImplicitStaticFinalGetter: {
|
|
// Field object is cached in function's data_.
|
|
RawField* field = reinterpret_cast<RawField*>(function->ptr()->data_);
|
|
RawInstance* value = field->ptr()->value_.static_value_;
|
|
if (value == Object::sentinel().raw() ||
|
|
value == Object::transition_sentinel().raw()) {
|
|
call_top[1] = 0; // Unused result of invoking the initializer.
|
|
call_top[2] = field;
|
|
Exit(thread, *FP, call_top + 3, *pc);
|
|
NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
|
|
if (!InvokeRuntime(thread, this, DRT_InitStaticField, native_args)) {
|
|
*invoked = true;
|
|
return false;
|
|
}
|
|
pp_ = InterpreterHelpers::FrameCode(*FP)->ptr()->object_pool_;
|
|
// The field is initialized by the runtime call, but not returned.
|
|
value = field->ptr()->value_.static_value_;
|
|
}
|
|
// Field was initialized. Return its value.
|
|
*SP = call_base;
|
|
**SP = value;
|
|
*invoked = true;
|
|
return true;
|
|
}
|
|
case RawFunction::kMethodExtractor: {
|
|
ASSERT(InterpreterHelpers::ArgDescTypeArgsLen(argdesc_) == 0);
|
|
call_top[1] = 0; // Result of runtime call.
|
|
call_top[2] = *call_base; // Receiver.
|
|
call_top[3] = function->ptr()->data_; // Method.
|
|
Exit(thread, *FP, call_top + 4, *pc);
|
|
NativeArguments native_args(thread, 2, call_top + 2, call_top + 1);
|
|
if (!InvokeRuntime(thread, this, DRT_ExtractMethod, native_args)) {
|
|
return false;
|
|
}
|
|
*SP = call_base;
|
|
**SP = call_top[1];
|
|
*invoked = true;
|
|
return true;
|
|
}
|
|
case RawFunction::kInvokeFieldDispatcher: {
|
|
const intptr_t type_args_len =
|
|
InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
|
|
const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
|
|
RawObject* receiver = call_base[receiver_idx];
|
|
RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
|
|
ASSERT(function == FrameFunction(callee_fp));
|
|
RawFunction* call_function = Function::null();
|
|
if (function->ptr()->name_ == Symbols::Call().raw()) {
|
|
RawObject* owner = function->ptr()->owner_;
|
|
if (owner->GetClassId() == kPatchClassCid) {
|
|
owner = PatchClass::RawCast(owner)->ptr()->patched_class_;
|
|
}
|
|
if (owner == thread->isolate()->object_store()->closure_class()) {
|
|
// Closure call.
|
|
call_function = Closure::RawCast(receiver)->ptr()->function_;
|
|
}
|
|
}
|
|
if (call_function == Function::null()) {
|
|
// Invoke field getter on receiver.
|
|
call_top[1] = 0; // Result of runtime call.
|
|
call_top[2] = receiver; // Receiver.
|
|
call_top[3] = function->ptr()->name_; // Field name.
|
|
Exit(thread, *FP, call_top + 4, *pc);
|
|
NativeArguments native_args(thread, 2, call_top + 2, call_top + 1);
|
|
if (!InvokeRuntime(thread, this, DRT_GetFieldForDispatch,
|
|
native_args)) {
|
|
return false;
|
|
}
|
|
// If the field value is a closure, no need to resolve 'call' function.
|
|
// Otherwise, call runtime to resolve 'call' function.
|
|
if (InterpreterHelpers::GetClassId(call_top[1]) == kClosureCid) {
|
|
// Closure call.
|
|
call_function = Closure::RawCast(call_top[1])->ptr()->function_;
|
|
} else {
|
|
// Resolve and invoke the 'call' function.
|
|
call_top[2] = 0; // Result of runtime call.
|
|
Exit(thread, *FP, call_top + 3, *pc);
|
|
NativeArguments native_args(thread, 1, call_top + 1, call_top + 2);
|
|
if (!InvokeRuntime(thread, this, DRT_ResolveCallFunction,
|
|
native_args)) {
|
|
return false;
|
|
}
|
|
call_function = Function::RawCast(call_top[2]);
|
|
if (call_function == Function::null()) {
|
|
// 'Call' could not be resolved. TODO(regis): Can this happen?
|
|
// Fall back to jitting the field dispatcher function.
|
|
break;
|
|
}
|
|
}
|
|
// Replace receiver with field value, keep all other arguments, and
|
|
// invoke 'call' function.
|
|
call_base[receiver_idx] = call_top[1];
|
|
}
|
|
ASSERT(call_function != Function::null());
|
|
// Patch field dispatcher in callee frame with call function.
|
|
callee_fp[kKBCFunctionSlotFromFp] = call_function;
|
|
// Do not compile function if it has code or bytecode.
|
|
if (Function::HasCode(call_function)) {
|
|
*invoked = true;
|
|
return InvokeCompiled(thread, call_function, call_base, call_top, pc,
|
|
FP, SP);
|
|
}
|
|
if (Function::HasBytecode(call_function)) {
|
|
*invoked = false;
|
|
return true;
|
|
}
|
|
function = call_function;
|
|
break; // Compile and invoke the function.
|
|
}
|
|
case RawFunction::kNoSuchMethodDispatcher:
|
|
// TODO(regis): Implement. For now, use jitted version.
|
|
break;
|
|
case RawFunction::kDynamicInvocationForwarder:
|
|
// TODO(regis): Implement. For now, use jitted version.
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// Compile the function to either generate code or load bytecode.
|
|
call_top[1] = 0; // Code result.
|
|
call_top[2] = function;
|
|
Exit(thread, *FP, call_top + 3, *pc);
|
|
NativeArguments native_args(thread, 1, call_top + 2, call_top + 1);
|
|
if (!InvokeRuntime(thread, this, DRT_CompileFunction, native_args)) {
|
|
return false;
|
|
}
|
|
if (Function::HasCode(function)) {
|
|
*invoked = true;
|
|
return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
|
|
}
|
|
ASSERT(Function::HasBytecode(function));
|
|
// Bytecode was loaded in the above compilation step.
|
|
// The caller will dispatch to the function's bytecode.
|
|
*invoked = false;
|
|
ASSERT(thread->vm_tag() == VMTag::kDartTagId);
|
|
ASSERT(thread->top_exit_frame_info() == 0);
|
|
return true;
|
|
}
|
|
|
|
DART_FORCE_INLINE bool Interpreter::Invoke(Thread* thread,
|
|
RawObject** call_base,
|
|
RawObject** call_top,
|
|
uint32_t** pc,
|
|
RawObject*** FP,
|
|
RawObject*** SP) {
|
|
RawObject** callee_fp = call_top + kKBCDartFrameFixedSize;
|
|
|
|
RawFunction* function = FrameFunction(callee_fp);
|
|
if (Function::HasCode(function)) {
|
|
return InvokeCompiled(thread, function, call_base, call_top, pc, FP, SP);
|
|
}
|
|
if (!Function::HasBytecode(function)) {
|
|
bool invoked = false;
|
|
bool result = ProcessInvocation(&invoked, thread, function, call_base,
|
|
call_top, pc, FP, SP);
|
|
if (invoked || !result) {
|
|
return result;
|
|
}
|
|
function = FrameFunction(callee_fp); // Function may have been patched.
|
|
ASSERT(Function::HasBytecode(function));
|
|
}
|
|
#if defined(DEBUG)
|
|
if (IsTracingExecution()) {
|
|
THR_Print("%" Pu64 " ", icount_);
|
|
THR_Print("invoking %s\n", Function::Handle(function).ToCString());
|
|
}
|
|
#endif
|
|
RawCode* bytecode = function->ptr()->bytecode_;
|
|
callee_fp[kKBCPcMarkerSlotFromFp] = bytecode;
|
|
callee_fp[kKBCSavedCallerPcSlotFromFp] = reinterpret_cast<RawObject*>(*pc);
|
|
callee_fp[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(*FP);
|
|
pp_ = bytecode->ptr()->object_pool_;
|
|
*pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
|
|
pc_ = reinterpret_cast<uword>(*pc); // For the profiler.
|
|
*FP = callee_fp;
|
|
*SP = *FP - 1;
|
|
return true;
|
|
}
|
|
|
|
void Interpreter::InlineCacheMiss(int checked_args,
|
|
Thread* thread,
|
|
RawICData* icdata,
|
|
RawObject** args,
|
|
RawObject** top,
|
|
uint32_t* pc,
|
|
RawObject** FP,
|
|
RawObject** SP) {
|
|
RawObject** result = top;
|
|
RawObject** miss_handler_args = top + 1;
|
|
for (intptr_t i = 0; i < checked_args; i++) {
|
|
miss_handler_args[i] = args[i];
|
|
}
|
|
miss_handler_args[checked_args] = icdata;
|
|
RuntimeFunction handler = NULL;
|
|
switch (checked_args) {
|
|
case 1:
|
|
handler = DRT_InlineCacheMissHandlerOneArg;
|
|
break;
|
|
case 2:
|
|
handler = DRT_InlineCacheMissHandlerTwoArgs;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
|
|
// Handler arguments: arguments to check and an ICData object.
|
|
const intptr_t miss_handler_argc = checked_args + 1;
|
|
RawObject** exit_frame = miss_handler_args + miss_handler_argc;
|
|
CallRuntime(thread, FP, exit_frame, pc, miss_handler_argc, miss_handler_args,
|
|
result, reinterpret_cast<uword>(handler));
|
|
}
|
|
|
|
DART_FORCE_INLINE bool Interpreter::InstanceCall1(Thread* thread,
|
|
RawICData* icdata,
|
|
RawObject** call_base,
|
|
RawObject** top,
|
|
uint32_t** pc,
|
|
RawObject*** FP,
|
|
RawObject*** SP,
|
|
bool optimized) {
|
|
ASSERT(icdata->GetClassId() == kICDataCid);
|
|
|
|
const intptr_t kCheckedArgs = 1;
|
|
RawObject** args = call_base;
|
|
RawArray* cache = icdata->ptr()->ic_data_->ptr();
|
|
|
|
const intptr_t type_args_len =
|
|
InterpreterHelpers::ArgDescTypeArgsLen(icdata->ptr()->args_descriptor_);
|
|
const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
|
|
RawSmi* receiver_cid =
|
|
InterpreterHelpers::GetClassIdAsSmi(args[receiver_idx]);
|
|
|
|
bool found = false;
|
|
const intptr_t length = Smi::Value(cache->length_);
|
|
intptr_t i;
|
|
for (i = 0; i < (length - (kCheckedArgs + 2)); i += (kCheckedArgs + 2)) {
|
|
if (cache->data()[i + 0] == receiver_cid) {
|
|
top[0] = cache->data()[i + kCheckedArgs];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argdesc_ = icdata->ptr()->args_descriptor_;
|
|
|
|
if (found) {
|
|
if (!optimized) {
|
|
InterpreterHelpers::IncrementICUsageCount(cache->data(), i, kCheckedArgs);
|
|
}
|
|
} else {
|
|
InlineCacheMiss(kCheckedArgs, thread, icdata, call_base + receiver_idx, top,
|
|
*pc, *FP, *SP);
|
|
}
|
|
|
|
return Invoke(thread, call_base, top, pc, FP, SP);
|
|
}
|
|
|
|
DART_FORCE_INLINE bool Interpreter::InstanceCall2(Thread* thread,
|
|
RawICData* icdata,
|
|
RawObject** call_base,
|
|
RawObject** top,
|
|
uint32_t** pc,
|
|
RawObject*** FP,
|
|
RawObject*** SP,
|
|
bool optimized) {
|
|
ASSERT(icdata->GetClassId() == kICDataCid);
|
|
|
|
const intptr_t kCheckedArgs = 2;
|
|
RawObject** args = call_base;
|
|
RawArray* cache = icdata->ptr()->ic_data_->ptr();
|
|
|
|
const intptr_t type_args_len =
|
|
InterpreterHelpers::ArgDescTypeArgsLen(icdata->ptr()->args_descriptor_);
|
|
const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
|
|
RawSmi* receiver_cid =
|
|
InterpreterHelpers::GetClassIdAsSmi(args[receiver_idx]);
|
|
RawSmi* arg0_cid =
|
|
InterpreterHelpers::GetClassIdAsSmi(args[receiver_idx + 1]);
|
|
|
|
bool found = false;
|
|
const intptr_t length = Smi::Value(cache->length_);
|
|
intptr_t i;
|
|
for (i = 0; i < (length - (kCheckedArgs + 2)); i += (kCheckedArgs + 2)) {
|
|
if ((cache->data()[i + 0] == receiver_cid) &&
|
|
(cache->data()[i + 1] == arg0_cid)) {
|
|
top[0] = cache->data()[i + kCheckedArgs];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
argdesc_ = icdata->ptr()->args_descriptor_;
|
|
|
|
if (found) {
|
|
if (!optimized) {
|
|
InterpreterHelpers::IncrementICUsageCount(cache->data(), i, kCheckedArgs);
|
|
}
|
|
} else {
|
|
InlineCacheMiss(kCheckedArgs, thread, icdata, call_base + receiver_idx, top,
|
|
*pc, *FP, *SP);
|
|
}
|
|
|
|
return Invoke(thread, call_base, top, pc, FP, SP);
|
|
}
|
|
|
|
DART_FORCE_INLINE void Interpreter::PrepareForTailCall(
|
|
RawCode* code,
|
|
RawImmutableArray* args_desc,
|
|
RawObject** FP,
|
|
RawObject*** SP,
|
|
uint32_t** pc) {
|
|
// Drop all stack locals.
|
|
*SP = FP - 1;
|
|
|
|
// Replace the callee with the new [code].
|
|
FP[kKBCFunctionSlotFromFp] = Object::null();
|
|
FP[kKBCPcMarkerSlotFromFp] = code;
|
|
*pc = reinterpret_cast<uint32_t*>(code->ptr()->entry_point_);
|
|
pc_ = reinterpret_cast<uword>(pc); // For the profiler.
|
|
pp_ = code->ptr()->object_pool_;
|
|
argdesc_ = args_desc;
|
|
}
|
|
|
|
// Note:
|
|
// All macro helpers are intended to be used only inside Interpreter::Call.
|
|
|
|
// Counts and prints executed bytecode instructions (in DEBUG mode).
|
|
#if defined(DEBUG)
|
|
#define TRACE_INSTRUCTION \
|
|
if (IsTracingExecution()) { \
|
|
TraceInstruction(pc - 1); \
|
|
} \
|
|
icount_++;
|
|
#else
|
|
#define TRACE_INSTRUCTION
|
|
#endif // defined(DEBUG)
|
|
|
|
// Decode opcode and A part of the given value and dispatch to the
|
|
// corresponding bytecode handler.
|
|
#define DISPATCH_OP(val) \
|
|
do { \
|
|
op = (val); \
|
|
rA = ((op >> 8) & 0xFF); \
|
|
TRACE_INSTRUCTION \
|
|
goto* dispatch[op & 0xFF]; \
|
|
} while (0)
|
|
|
|
// Fetch next operation from PC, increment program counter and dispatch.
|
|
#define DISPATCH() DISPATCH_OP(*pc++)
|
|
|
|
// Define entry point that handles bytecode Name with the given operand format.
|
|
#define BYTECODE(Name, Operands) \
|
|
BYTECODE_HEADER(Name, DECLARE_##Operands, DECODE_##Operands)
|
|
|
|
#define BYTECODE_HEADER(Name, Declare, Decode) \
|
|
Declare; \
|
|
bc##Name : Decode
|
|
|
|
// Helpers to decode common instruction formats. Used in conjunction with
|
|
// BYTECODE() macro.
|
|
#define DECLARE_A_B_C \
|
|
uint16_t rB, rC; \
|
|
USE(rB); \
|
|
USE(rC)
|
|
#define DECODE_A_B_C \
|
|
rB = ((op >> KernelBytecode::kBShift) & KernelBytecode::kBMask); \
|
|
rC = ((op >> KernelBytecode::kCShift) & KernelBytecode::kCMask);
|
|
|
|
#define DECLARE_A_B_Y \
|
|
uint16_t rB; \
|
|
int8_t rY; \
|
|
USE(rB); \
|
|
USE(rY)
|
|
#define DECODE_A_B_Y \
|
|
rB = ((op >> KernelBytecode::kBShift) & KernelBytecode::kBMask); \
|
|
rY = ((op >> KernelBytecode::kYShift) & KernelBytecode::kYMask);
|
|
|
|
#define DECLARE_0
|
|
#define DECODE_0
|
|
|
|
#define DECLARE_A
|
|
#define DECODE_A
|
|
|
|
#define DECLARE___D \
|
|
uint32_t rD; \
|
|
USE(rD)
|
|
#define DECODE___D rD = (op >> KernelBytecode::kDShift);
|
|
|
|
#define DECLARE_A_D DECLARE___D
|
|
#define DECODE_A_D DECODE___D
|
|
|
|
#define DECLARE_A_X \
|
|
int32_t rD; \
|
|
USE(rD)
|
|
#define DECODE_A_X rD = (static_cast<int32_t>(op) >> KernelBytecode::kDShift);
|
|
|
|
#define SMI_FASTPATH_ICDATA_INC \
|
|
do { \
|
|
ASSERT(KernelBytecode::IsCallOpcode(*pc)); \
|
|
const uint16_t kidx = KernelBytecode::DecodeD(*pc); \
|
|
const RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx)); \
|
|
RawObject** entries = icdata->ptr()->ic_data_->ptr()->data(); \
|
|
InterpreterHelpers::IncrementICUsageCount(entries, 0, 2); \
|
|
} while (0);
|
|
|
|
// Declare bytecode handler for a smi operation (e.g. AddTOS) with the
|
|
// given result type and the given behavior specified as a function
|
|
// that takes left and right operands and result slot and returns
|
|
// true if fast-path succeeds.
|
|
#define SMI_FASTPATH_TOS(ResultT, Func) \
|
|
{ \
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(SP[-1]); \
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(SP[-0]); \
|
|
ResultT* slot = reinterpret_cast<ResultT*>(SP - 1); \
|
|
if (LIKELY(!thread->isolate()->single_step()) && \
|
|
LIKELY(AreBothSmis(lhs, rhs) && !Func(lhs, rhs, slot))) { \
|
|
SMI_FASTPATH_ICDATA_INC; \
|
|
/* Fast path succeeded. Skip the generic call that follows. */ \
|
|
pc++; \
|
|
/* We dropped 2 arguments and push result */ \
|
|
SP--; \
|
|
} \
|
|
}
|
|
|
|
// Skip the next instruction if there is no overflow.
|
|
#define SMI_OP_CHECK(ResultT, Func) \
|
|
{ \
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]); \
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]); \
|
|
ResultT* slot = reinterpret_cast<ResultT*>(&FP[rA]); \
|
|
if (LIKELY(!Func(lhs, rhs, slot))) { \
|
|
/* Success. Skip the instruction that follows. */ \
|
|
pc++; \
|
|
} \
|
|
}
|
|
|
|
// Do not check for overflow.
|
|
#define SMI_OP_NOCHECK(ResultT, Func) \
|
|
{ \
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]); \
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]); \
|
|
ResultT* slot = reinterpret_cast<ResultT*>(&FP[rA]); \
|
|
Func(lhs, rhs, slot); \
|
|
}
|
|
|
|
// Exception handling helper. Gets handler FP and PC from the Interpreter where
|
|
// they were stored by Interpreter::Longjmp and proceeds to execute the handler.
|
|
// Corner case: handler PC can be a fake marker that marks entry frame, which
|
|
// means exception was not handled in the Dart code. In this case we return
|
|
// caught exception from Interpreter::Call.
|
|
#if defined(DEBUG)
|
|
|
|
#define HANDLE_EXCEPTION \
|
|
do { \
|
|
FP = reinterpret_cast<RawObject**>(fp_); \
|
|
pc = reinterpret_cast<uint32_t*>(pc_); \
|
|
if (IsEntryFrameMarker(reinterpret_cast<uword>(pc))) { \
|
|
pp_ = reinterpret_cast<RawObjectPool*>(fp_[kKBCSavedPpSlotFromEntryFp]); \
|
|
argdesc_ = \
|
|
reinterpret_cast<RawArray*>(fp_[kKBCSavedArgDescSlotFromEntryFp]); \
|
|
uword exit_fp = \
|
|
reinterpret_cast<uword>(fp_[kKBCExitLinkSlotFromEntryFp]); \
|
|
thread->set_top_exit_frame_info(exit_fp); \
|
|
thread->set_top_resource(top_resource); \
|
|
thread->set_vm_tag(vm_tag); \
|
|
if (IsTracingExecution()) { \
|
|
THR_Print("%" Pu64 " ", icount_); \
|
|
THR_Print("Returning exception from interpreter 0x%" Px \
|
|
" at fp_ 0x%" Px " exit 0x%" Px "\n", \
|
|
reinterpret_cast<uword>(this), reinterpret_cast<uword>(fp_), \
|
|
exit_fp); \
|
|
} \
|
|
ASSERT(reinterpret_cast<uword>(fp_) < stack_limit()); \
|
|
return special_[KernelBytecode::kExceptionSpecialIndex]; \
|
|
} \
|
|
goto DispatchAfterException; \
|
|
} while (0)
|
|
|
|
#else // !defined(DEBUG)
|
|
|
|
#define HANDLE_EXCEPTION \
|
|
do { \
|
|
FP = reinterpret_cast<RawObject**>(fp_); \
|
|
pc = reinterpret_cast<uint32_t*>(pc_); \
|
|
if (IsEntryFrameMarker(reinterpret_cast<uword>(pc))) { \
|
|
pp_ = reinterpret_cast<RawObjectPool*>(fp_[kKBCSavedPpSlotFromEntryFp]); \
|
|
argdesc_ = \
|
|
reinterpret_cast<RawArray*>(fp_[kKBCSavedArgDescSlotFromEntryFp]); \
|
|
uword exit_fp = \
|
|
reinterpret_cast<uword>(fp_[kKBCExitLinkSlotFromEntryFp]); \
|
|
thread->set_top_exit_frame_info(exit_fp); \
|
|
thread->set_top_resource(top_resource); \
|
|
thread->set_vm_tag(vm_tag); \
|
|
return special_[KernelBytecode::kExceptionSpecialIndex]; \
|
|
} \
|
|
goto DispatchAfterException; \
|
|
} while (0)
|
|
|
|
#endif // !defined(DEBUG)
|
|
|
|
#define HANDLE_RETURN \
|
|
do { \
|
|
pp_ = InterpreterHelpers::FrameCode(FP)->ptr()->object_pool_; \
|
|
} while (0)
|
|
|
|
// Runtime call helpers: handle invocation and potential exception after return.
|
|
#define INVOKE_RUNTIME(Func, Args) \
|
|
if (!InvokeRuntime(thread, this, Func, Args)) { \
|
|
HANDLE_EXCEPTION; \
|
|
} else { \
|
|
HANDLE_RETURN; \
|
|
}
|
|
|
|
#define INVOKE_NATIVE(Wrapper, Func, Args) \
|
|
if (!InvokeNative(thread, this, Wrapper, Func, Args)) { \
|
|
HANDLE_EXCEPTION; \
|
|
} else { \
|
|
HANDLE_RETURN; \
|
|
}
|
|
|
|
#define LOAD_CONSTANT(index) (pp_->ptr()->data()[(index)].raw_obj_)
|
|
|
|
// Returns true if deoptimization succeeds.
|
|
DART_FORCE_INLINE bool Interpreter::Deoptimize(Thread* thread,
|
|
uint32_t** pc,
|
|
RawObject*** FP,
|
|
RawObject*** SP,
|
|
bool is_lazy) {
|
|
// Note: frame translation will take care of preserving result at the
|
|
// top of the stack. See CompilerDeoptInfo::CreateDeoptInfo.
|
|
|
|
// Make sure we preserve SP[0] when entering synthetic frame below.
|
|
(*SP)++;
|
|
|
|
// Leaf runtime function DeoptimizeCopyFrame expects a Dart frame.
|
|
// The code in this frame may not cause GC.
|
|
// DeoptimizeCopyFrame and DeoptimizeFillFrame are leaf runtime calls.
|
|
EnterSyntheticFrame(FP, SP, *pc - (is_lazy ? 1 : 0));
|
|
const intptr_t frame_size_in_bytes =
|
|
DLRT_DeoptimizeCopyFrame(reinterpret_cast<uword>(*FP), is_lazy ? 1 : 0);
|
|
LeaveSyntheticFrame(FP, SP);
|
|
|
|
*SP = *FP + (frame_size_in_bytes / kWordSize);
|
|
EnterSyntheticFrame(FP, SP, *pc - (is_lazy ? 1 : 0));
|
|
DLRT_DeoptimizeFillFrame(reinterpret_cast<uword>(*FP));
|
|
|
|
// We are now inside a valid frame.
|
|
{
|
|
*++(*SP) = 0; // Space for the result: number of materialization args.
|
|
Exit(thread, *FP, *SP + 1, /*pc=*/0);
|
|
NativeArguments native_args(thread, 0, *SP, *SP);
|
|
if (!InvokeRuntime(thread, this, DRT_DeoptimizeMaterialize, native_args)) {
|
|
return false;
|
|
}
|
|
}
|
|
const intptr_t materialization_arg_count =
|
|
Smi::Value(RAW_CAST(Smi, *(*SP)--)) / kWordSize;
|
|
|
|
// Restore caller PC.
|
|
*pc = SavedCallerPC(*FP);
|
|
pc_ = reinterpret_cast<uword>(*pc); // For the profiler.
|
|
|
|
// Check if it is a fake PC marking the entry frame.
|
|
ASSERT(!IsEntryFrameMarker(reinterpret_cast<uword>(*pc)));
|
|
|
|
// Restore SP, FP and PP.
|
|
// Unoptimized frame SP is one below FrameArguments(...) because
|
|
// FrameArguments(...) returns a pointer to the first argument.
|
|
*SP = FrameArguments(*FP, materialization_arg_count) - 1;
|
|
*FP = SavedCallerFP(*FP);
|
|
|
|
// Restore pp.
|
|
pp_ = InterpreterHelpers::FrameCode(*FP)->ptr()->object_pool_;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Interpreter::AssertAssignable(Thread* thread,
|
|
uint32_t* pc,
|
|
RawObject** FP,
|
|
RawObject** call_top,
|
|
RawObject** args,
|
|
RawSubtypeTestCache* cache) {
|
|
RawObject* null_value = Object::null();
|
|
if (cache != null_value) {
|
|
RawInstance* instance = static_cast<RawInstance*>(args[0]);
|
|
RawTypeArguments* instantiator_type_arguments =
|
|
static_cast<RawTypeArguments*>(args[1]);
|
|
RawTypeArguments* function_type_arguments =
|
|
static_cast<RawTypeArguments*>(args[2]);
|
|
|
|
const intptr_t cid = InterpreterHelpers::GetClassId(instance);
|
|
|
|
RawTypeArguments* instance_type_arguments =
|
|
static_cast<RawTypeArguments*>(null_value);
|
|
RawObject* instance_cid_or_function;
|
|
|
|
RawTypeArguments* parent_function_type_arguments;
|
|
RawTypeArguments* delayed_function_type_arguments;
|
|
if (cid == kClosureCid) {
|
|
RawClosure* closure = static_cast<RawClosure*>(instance);
|
|
instance_type_arguments = closure->ptr()->instantiator_type_arguments_;
|
|
parent_function_type_arguments = closure->ptr()->function_type_arguments_;
|
|
delayed_function_type_arguments = closure->ptr()->delayed_type_arguments_;
|
|
instance_cid_or_function = closure->ptr()->function_;
|
|
} else {
|
|
instance_cid_or_function = Smi::New(cid);
|
|
|
|
RawClass* instance_class = thread->isolate()->class_table()->At(cid);
|
|
if (instance_class->ptr()->num_type_arguments_ < 0) {
|
|
goto AssertAssignableCallRuntime;
|
|
} else if (instance_class->ptr()->num_type_arguments_ > 0) {
|
|
instance_type_arguments = reinterpret_cast<RawTypeArguments**>(
|
|
instance->ptr())[instance_class->ptr()
|
|
->type_arguments_field_offset_in_words_];
|
|
}
|
|
parent_function_type_arguments =
|
|
static_cast<RawTypeArguments*>(null_value);
|
|
delayed_function_type_arguments =
|
|
static_cast<RawTypeArguments*>(null_value);
|
|
}
|
|
|
|
for (RawObject** entries = cache->ptr()->cache_->ptr()->data();
|
|
entries[0] != null_value;
|
|
entries += SubtypeTestCache::kTestEntryLength) {
|
|
if ((entries[SubtypeTestCache::kInstanceClassIdOrFunction] ==
|
|
instance_cid_or_function) &&
|
|
(entries[SubtypeTestCache::kInstanceTypeArguments] ==
|
|
instance_type_arguments) &&
|
|
(entries[SubtypeTestCache::kInstantiatorTypeArguments] ==
|
|
instantiator_type_arguments) &&
|
|
(entries[SubtypeTestCache::kFunctionTypeArguments] ==
|
|
function_type_arguments) &&
|
|
(entries[SubtypeTestCache::kInstanceParentFunctionTypeArguments] ==
|
|
parent_function_type_arguments) &&
|
|
(entries[SubtypeTestCache::kInstanceDelayedFunctionTypeArguments] ==
|
|
delayed_function_type_arguments)) {
|
|
if (Bool::True().raw() == entries[SubtypeTestCache::kTestResult]) {
|
|
return true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
AssertAssignableCallRuntime:
|
|
// TODO(regis): Modify AssertAssignable bytecode to expect arguments in same
|
|
// order as the TypeCheck runtime call, so this copying can be avoided.
|
|
call_top[1] = 0; // Unused result.
|
|
call_top[2] = args[0]; // Instance.
|
|
call_top[3] = args[3]; // Type.
|
|
call_top[4] = args[1]; // Instantiator type args.
|
|
call_top[5] = args[2]; // Function type args.
|
|
call_top[6] = args[4]; // Name.
|
|
call_top[7] = cache;
|
|
call_top[8] = Smi::New(kTypeCheckFromInline);
|
|
Exit(thread, FP, call_top + 9, pc);
|
|
NativeArguments native_args(thread, 7, call_top + 2, call_top + 1);
|
|
return InvokeRuntime(thread, this, DRT_TypeCheck, native_args);
|
|
}
|
|
|
|
RawObject* Interpreter::Call(const Function& function,
|
|
const Array& arguments_descriptor,
|
|
const Array& arguments,
|
|
Thread* thread) {
|
|
return Call(function.raw(), arguments_descriptor.raw(), arguments.Length(),
|
|
arguments.raw_ptr()->data(), thread);
|
|
}
|
|
|
|
RawObject* Interpreter::Call(RawFunction* function,
|
|
RawArray* argdesc,
|
|
intptr_t argc,
|
|
RawObject* const* argv,
|
|
Thread* thread) {
|
|
// Dispatch used to interpret bytecode. Contains addresses of
|
|
// labels of bytecode handlers. Handlers themselves are defined below.
|
|
static const void* dispatch[] = {
|
|
#define TARGET(name, fmt, fmta, fmtb, fmtc) &&bc##name,
|
|
KERNEL_BYTECODES_LIST(TARGET)
|
|
#undef TARGET
|
|
};
|
|
|
|
// Interpreter state (see constants_kbc.h for high-level overview).
|
|
uint32_t* pc; // Program Counter: points to the next op to execute.
|
|
RawObject** FP; // Frame Pointer.
|
|
RawObject** SP; // Stack Pointer.
|
|
|
|
uint32_t op; // Currently executing op.
|
|
uint16_t rA; // A component of the currently executing op.
|
|
|
|
bool reentering = fp_ != NULL;
|
|
if (!reentering) {
|
|
fp_ = reinterpret_cast<RawObject**>(stack_base_);
|
|
}
|
|
#if defined(DEBUG)
|
|
if (IsTracingExecution()) {
|
|
THR_Print("%" Pu64 " ", icount_);
|
|
THR_Print("%s interpreter 0x%" Px " at fp_ 0x%" Px " exit 0x%" Px " %s\n",
|
|
reentering ? "Re-entering" : "Entering",
|
|
reinterpret_cast<uword>(this), reinterpret_cast<uword>(fp_),
|
|
thread->top_exit_frame_info(),
|
|
Function::Handle(function).ToCString());
|
|
}
|
|
#endif
|
|
|
|
// Save current VM tag and mark thread as executing Dart code.
|
|
const uword vm_tag = thread->vm_tag();
|
|
thread->set_vm_tag(VMTag::kDartTagId); // TODO(regis): kDartBytecodeTagId?
|
|
|
|
// Save current top stack resource and reset the list.
|
|
StackResource* top_resource = thread->top_resource();
|
|
thread->set_top_resource(NULL);
|
|
|
|
// Setup entry frame:
|
|
//
|
|
// ^
|
|
// | previous Dart frames
|
|
// |
|
|
// | ........... | -+
|
|
// fp_ > | exit fp_ | saved top_exit_frame_info
|
|
// | argdesc_ | saved argdesc_ (for reentering interpreter)
|
|
// | pp_ | saved pp_ (for reentering interpreter)
|
|
// | arg 0 | -+
|
|
// | arg 1 | |
|
|
// ... |
|
|
// > incoming arguments
|
|
// |
|
|
// | arg argc-1 | -+
|
|
// | function | -+
|
|
// | code | |
|
|
// | caller PC | ---> special fake PC marking an entry frame
|
|
// SP > | fp_ | |
|
|
// FP > | ........... | > normal Dart frame (see stack_frame_kbc.h)
|
|
// |
|
|
// v
|
|
//
|
|
// A negative argc indicates reverse memory order of arguments.
|
|
const intptr_t arg_count = argc < 0 ? -argc : argc;
|
|
FP = fp_ + kKBCEntrySavedSlots + arg_count + kKBCDartFrameFixedSize;
|
|
SP = FP - 1;
|
|
|
|
// Save outer top_exit_frame_info, current argdesc, and current pp.
|
|
fp_[kKBCExitLinkSlotFromEntryFp] =
|
|
reinterpret_cast<RawObject*>(thread->top_exit_frame_info());
|
|
thread->set_top_exit_frame_info(0);
|
|
fp_[kKBCSavedArgDescSlotFromEntryFp] = reinterpret_cast<RawObject*>(argdesc_);
|
|
fp_[kKBCSavedPpSlotFromEntryFp] = reinterpret_cast<RawObject*>(pp_);
|
|
|
|
// Copy arguments and setup the Dart frame.
|
|
for (intptr_t i = 0; i < arg_count; i++) {
|
|
fp_[kKBCEntrySavedSlots + i] = argv[argc < 0 ? -i : i];
|
|
}
|
|
|
|
RawCode* bytecode = function->ptr()->bytecode_;
|
|
FP[kKBCFunctionSlotFromFp] = function;
|
|
FP[kKBCPcMarkerSlotFromFp] = bytecode;
|
|
FP[kKBCSavedCallerPcSlotFromFp] =
|
|
reinterpret_cast<RawObject*>((arg_count << 2) | 2);
|
|
FP[kKBCSavedCallerFpSlotFromFp] = reinterpret_cast<RawObject*>(fp_);
|
|
|
|
// Load argument descriptor.
|
|
argdesc_ = argdesc;
|
|
|
|
// Ready to start executing bytecode. Load entry point and corresponding
|
|
// object pool.
|
|
pc = reinterpret_cast<uint32_t*>(bytecode->ptr()->entry_point_);
|
|
pc_ = reinterpret_cast<uword>(pc); // For the profiler.
|
|
pp_ = bytecode->ptr()->object_pool_;
|
|
|
|
// Cache some frequently used values in the frame.
|
|
RawBool* true_value = Bool::True().raw();
|
|
RawBool* false_value = Bool::False().raw();
|
|
RawObject* null_value = Object::null();
|
|
|
|
#if defined(DEBUG)
|
|
Function& function_h = Function::Handle();
|
|
#endif
|
|
|
|
// Enter the dispatch loop.
|
|
DISPATCH();
|
|
|
|
// KernelBytecode handlers (see constants_kbc.h for bytecode descriptions).
|
|
{
|
|
BYTECODE(Entry, A_D);
|
|
const uint16_t num_locals = rD;
|
|
|
|
// Initialize locals with null & set SP.
|
|
for (intptr_t i = 0; i < num_locals; i++) {
|
|
FP[i] = null_value;
|
|
}
|
|
SP = FP + num_locals - 1;
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(EntryFixed, A_D);
|
|
const uint16_t num_fixed_params = rA;
|
|
const uint16_t num_locals = rD;
|
|
|
|
const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
|
|
const intptr_t pos_count = InterpreterHelpers::ArgDescPosCount(argdesc_);
|
|
if ((arg_count != num_fixed_params) || (pos_count != num_fixed_params)) {
|
|
goto ClosureNoSuchMethod;
|
|
}
|
|
|
|
// Initialize locals with null & set SP.
|
|
for (intptr_t i = 0; i < num_locals; i++) {
|
|
FP[i] = null_value;
|
|
}
|
|
SP = FP + num_locals - 1;
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(EntryOptional, A_B_C);
|
|
const uint16_t num_fixed_params = rA;
|
|
const uint16_t num_opt_pos_params = rB;
|
|
const uint16_t num_opt_named_params = rC;
|
|
const intptr_t min_num_pos_args = num_fixed_params;
|
|
const intptr_t max_num_pos_args = num_fixed_params + num_opt_pos_params;
|
|
|
|
// Decode arguments descriptor.
|
|
const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
|
|
const intptr_t pos_count = InterpreterHelpers::ArgDescPosCount(argdesc_);
|
|
const intptr_t named_count = (arg_count - pos_count);
|
|
|
|
// Check that got the right number of positional parameters.
|
|
if ((min_num_pos_args > pos_count) || (pos_count > max_num_pos_args)) {
|
|
goto ClosureNoSuchMethod;
|
|
}
|
|
|
|
// Copy all passed position arguments.
|
|
RawObject** first_arg = FrameArguments(FP, arg_count);
|
|
memmove(FP, first_arg, pos_count * kWordSize);
|
|
|
|
if (num_opt_named_params != 0) {
|
|
// This is a function with named parameters.
|
|
// Walk the list of named parameters and their
|
|
// default values encoded as pairs of LoadConstant instructions that
|
|
// follows the entry point and find matching values via arguments
|
|
// descriptor.
|
|
RawObject** argdesc_data = argdesc_->ptr()->data();
|
|
|
|
intptr_t i = named_count - 1; // argument position
|
|
intptr_t j = num_opt_named_params - 1; // parameter position
|
|
while ((j >= 0) && (i >= 0)) {
|
|
// Fetch formal parameter information: name, default value, target slot.
|
|
const uint32_t load_name = pc[2 * j];
|
|
const uint32_t load_value = pc[2 * j + 1];
|
|
ASSERT(KernelBytecode::DecodeOpcode(load_name) ==
|
|
KernelBytecode::kLoadConstant);
|
|
ASSERT(KernelBytecode::DecodeOpcode(load_value) ==
|
|
KernelBytecode::kLoadConstant);
|
|
const uint8_t reg = KernelBytecode::DecodeA(load_name);
|
|
ASSERT(reg == KernelBytecode::DecodeA(load_value));
|
|
|
|
RawString* name = static_cast<RawString*>(
|
|
LOAD_CONSTANT(KernelBytecode::DecodeD(load_name)));
|
|
if (name == argdesc_data[ArgumentsDescriptor::name_index(i)]) {
|
|
// Parameter was passed. Fetch passed value.
|
|
const intptr_t arg_index = Smi::Value(static_cast<RawSmi*>(
|
|
argdesc_data[ArgumentsDescriptor::position_index(i)]));
|
|
FP[reg] = first_arg[arg_index];
|
|
i--; // Consume passed argument.
|
|
} else {
|
|
// Parameter was not passed. Fetch default value.
|
|
FP[reg] = LOAD_CONSTANT(KernelBytecode::DecodeD(load_value));
|
|
}
|
|
j--; // Next formal parameter.
|
|
}
|
|
|
|
// If we have unprocessed formal parameters then initialize them all
|
|
// using default values.
|
|
while (j >= 0) {
|
|
const uint32_t load_name = pc[2 * j];
|
|
const uint32_t load_value = pc[2 * j + 1];
|
|
ASSERT(KernelBytecode::DecodeOpcode(load_name) ==
|
|
KernelBytecode::kLoadConstant);
|
|
ASSERT(KernelBytecode::DecodeOpcode(load_value) ==
|
|
KernelBytecode::kLoadConstant);
|
|
const uint8_t reg = KernelBytecode::DecodeA(load_name);
|
|
ASSERT(reg == KernelBytecode::DecodeA(load_value));
|
|
|
|
FP[reg] = LOAD_CONSTANT(KernelBytecode::DecodeD(load_value));
|
|
j--;
|
|
}
|
|
|
|
// If we have unprocessed passed arguments that means we have mismatch
|
|
// between formal parameters and concrete arguments. This can only
|
|
// occur if the current function is a closure.
|
|
if (i != -1) {
|
|
goto ClosureNoSuchMethod;
|
|
}
|
|
|
|
// Skip LoadConstant-s encoding information about named parameters.
|
|
pc += num_opt_named_params * 2;
|
|
|
|
// SP points past copied arguments.
|
|
SP = FP + num_fixed_params + num_opt_named_params - 1;
|
|
} else {
|
|
ASSERT(num_opt_pos_params != 0);
|
|
if (named_count != 0) {
|
|
// Function can't have both named and optional positional parameters.
|
|
// This kind of mismatch can only occur if the current function
|
|
// is a closure.
|
|
goto ClosureNoSuchMethod;
|
|
}
|
|
|
|
// Process the list of default values encoded as a sequence of
|
|
// LoadConstant instructions after EntryOpt bytecode.
|
|
// Execute only those that correspond to parameters that were not passed.
|
|
for (intptr_t i = pos_count - num_fixed_params; i < num_opt_pos_params;
|
|
i++) {
|
|
const uint32_t load_value = pc[i];
|
|
ASSERT(KernelBytecode::DecodeOpcode(load_value) ==
|
|
KernelBytecode::kLoadConstant);
|
|
#if defined(DEBUG)
|
|
const uint8_t reg = KernelBytecode::DecodeA(load_value);
|
|
ASSERT((num_fixed_params + i) == reg);
|
|
#endif
|
|
FP[num_fixed_params + i] =
|
|
LOAD_CONSTANT(KernelBytecode::DecodeD(load_value));
|
|
}
|
|
|
|
// Skip LoadConstant-s encoding default values for optional positional
|
|
// parameters.
|
|
pc += num_opt_pos_params;
|
|
|
|
// SP points past the last copied parameter.
|
|
SP = FP + max_num_pos_args - 1;
|
|
}
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(EntryOptimized, A_D);
|
|
const uint16_t num_registers = rD;
|
|
|
|
// Reserve space for registers used by the optimized code.
|
|
SP = FP + num_registers - 1;
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Frame, A_D);
|
|
// Initialize locals with null and increment SP.
|
|
const uint16_t num_locals = rD;
|
|
for (intptr_t i = 1; i <= num_locals; i++) {
|
|
SP[i] = null_value;
|
|
}
|
|
SP += num_locals;
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(SetFrame, A);
|
|
SP = FP + rA - 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Compile, 0);
|
|
FP[0] = argdesc_;
|
|
FP[1] = FrameFunction(FP);
|
|
FP[2] = 0;
|
|
|
|
UNIMPLEMENTED(); // TODO(regis): Revisit.
|
|
|
|
Exit(thread, FP, FP + 3, pc);
|
|
NativeArguments args(thread, 1, FP + 1, FP + 2);
|
|
INVOKE_RUNTIME(DRT_CompileFunction, args);
|
|
{
|
|
// Function should be compiled now, dispatch to its entry point.
|
|
RawCode* code = FrameFunction(FP)->ptr()->code_;
|
|
InterpreterHelpers::SetFrameCode(FP, code);
|
|
pp_ = code->ptr()->object_pool_;
|
|
pc = reinterpret_cast<uint32_t*>(code->ptr()->entry_point_);
|
|
pc_ = reinterpret_cast<uword>(pc); // For the profiler.
|
|
argdesc_ = static_cast<RawArray*>(FP[0]);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(HotCheck, A_D);
|
|
const uint8_t increment = rA;
|
|
const uint16_t threshold = rD;
|
|
RawFunction* f = FrameFunction(FP);
|
|
int32_t counter = f->ptr()->usage_counter_;
|
|
// Note: we don't increment usage counter in the prologue of optimized
|
|
// functions.
|
|
if (increment) {
|
|
counter += increment;
|
|
f->ptr()->usage_counter_ = counter;
|
|
}
|
|
if (UNLIKELY(counter >= threshold)) {
|
|
FP[0] = f;
|
|
FP[1] = 0;
|
|
|
|
// Save the args desriptor which came in.
|
|
FP[2] = argdesc_;
|
|
|
|
UNIMPLEMENTED(); // TODO(regis): Revisit.
|
|
|
|
// Make the DRT_OptimizeInvokedFunction see a stub as its caller for
|
|
// consistency with the other architectures, and to avoid needing to
|
|
// generate a stackmap for the HotCheck pc.
|
|
const StubEntry* stub = StubCode::OptimizeFunction_entry();
|
|
FP[kKBCPcMarkerSlotFromFp] = stub->code();
|
|
pc = reinterpret_cast<uint32_t*>(stub->EntryPoint());
|
|
|
|
Exit(thread, FP, FP + 3, pc);
|
|
NativeArguments args(thread, 1, /*argv=*/FP, /*retval=*/FP + 1);
|
|
INVOKE_RUNTIME(DRT_OptimizeInvokedFunction, args);
|
|
{
|
|
// DRT_OptimizeInvokedFunction returns the code object to execute.
|
|
ASSERT(FP[1]->GetClassId() == kFunctionCid);
|
|
RawFunction* function = static_cast<RawFunction*>(FP[1]);
|
|
RawCode* code = function->ptr()->code_;
|
|
InterpreterHelpers::SetFrameCode(FP, code);
|
|
|
|
// Restore args descriptor which came in.
|
|
argdesc_ = Array::RawCast(FP[2]);
|
|
|
|
pp_ = code->ptr()->object_pool_;
|
|
pc = reinterpret_cast<uint32_t*>(function->ptr()->entry_point_);
|
|
pc_ = reinterpret_cast<uword>(pc); // For the profiler.
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckStack, A);
|
|
{
|
|
// Check the interpreter's own stack limit for actual interpreter's stack
|
|
// overflows, and also the thread's stack limit for scheduled interrupts.
|
|
if (reinterpret_cast<uword>(SP) >= stack_limit() ||
|
|
thread->HasScheduledInterrupts()) {
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_StackOverflow, args);
|
|
}
|
|
}
|
|
RawFunction* function = FrameFunction(FP);
|
|
int32_t counter = ++(function->ptr()->usage_counter_);
|
|
if (UNLIKELY(FLAG_compilation_counter_threshold >= 0 &&
|
|
counter >= FLAG_compilation_counter_threshold &&
|
|
!Function::HasCode(function))) {
|
|
SP[1] = 0; // Unused code result.
|
|
SP[2] = function;
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments native_args(thread, 1, SP + 2, SP + 1);
|
|
INVOKE_RUNTIME(DRT_OptimizeInvokedFunction, native_args);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckStackAlwaysExit, A);
|
|
{
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_StackOverflow, args);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckFunctionTypeArgs, A_D);
|
|
const uint16_t declared_type_args_len = rA;
|
|
const uint16_t first_stack_local_index = rD;
|
|
|
|
// Decode arguments descriptor's type args len.
|
|
const intptr_t type_args_len =
|
|
InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
|
|
if ((type_args_len != declared_type_args_len) && (type_args_len != 0)) {
|
|
goto ClosureNoSuchMethod;
|
|
}
|
|
if (type_args_len > 0) {
|
|
// Decode arguments descriptor's argument count (excluding type args).
|
|
const intptr_t arg_count = InterpreterHelpers::ArgDescArgCount(argdesc_);
|
|
// Copy passed-in type args to first local slot.
|
|
FP[first_stack_local_index] = *FrameArguments(FP, arg_count + 1);
|
|
} else if (declared_type_args_len > 0) {
|
|
FP[first_stack_local_index] = Object::null();
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DebugStep, A);
|
|
if (thread->isolate()->single_step()) {
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DebugBreak, A);
|
|
#if !defined(PRODUCT)
|
|
{
|
|
const uint32_t original_bc =
|
|
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
|
|
thread->isolate()->debugger()->GetPatchedStubAddress(
|
|
reinterpret_cast<uword>(pc))));
|
|
|
|
SP[1] = null_value;
|
|
Exit(thread, FP, SP + 2, pc);
|
|
NativeArguments args(thread, 0, NULL, SP + 1);
|
|
INVOKE_RUNTIME(DRT_BreakpointRuntimeHandler, args)
|
|
DISPATCH_OP(original_bc);
|
|
}
|
|
#else
|
|
// There should be no debug breaks in product mode.
|
|
UNREACHABLE();
|
|
#endif
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InstantiateType, A_D);
|
|
// Stack: instantiator type args, function type args
|
|
RawObject* type = LOAD_CONSTANT(rD);
|
|
SP[1] = type;
|
|
SP[2] = SP[-1];
|
|
SP[3] = SP[0];
|
|
Exit(thread, FP, SP + 4, pc);
|
|
{
|
|
NativeArguments args(thread, 3, SP + 1, SP - 1);
|
|
INVOKE_RUNTIME(DRT_InstantiateType, args);
|
|
}
|
|
SP -= 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InstantiateTypeArgumentsTOS, A_D);
|
|
// Stack: instantiator type args, function type args
|
|
RawTypeArguments* type_arguments =
|
|
static_cast<RawTypeArguments*>(LOAD_CONSTANT(rD));
|
|
|
|
RawObject* instantiator_type_args = SP[-1];
|
|
RawObject* function_type_args = SP[0];
|
|
// If both instantiators are null and if the type argument vector
|
|
// instantiated from null becomes a vector of dynamic, then use null as
|
|
// the type arguments.
|
|
if ((rA == 0) || (null_value != instantiator_type_args) ||
|
|
(null_value != function_type_args)) {
|
|
// First lookup in the cache.
|
|
RawArray* instantiations = type_arguments->ptr()->instantiations_;
|
|
for (intptr_t i = 0;
|
|
instantiations->ptr()->data()[i] != NULL; // kNoInstantiator
|
|
i += 3) { // kInstantiationSizeInWords
|
|
if ((instantiations->ptr()->data()[i] == instantiator_type_args) &&
|
|
(instantiations->ptr()->data()[i + 1] == function_type_args)) {
|
|
// Found in the cache.
|
|
SP[-1] = instantiations->ptr()->data()[i + 2];
|
|
goto InstantiateTypeArgumentsTOSDone;
|
|
}
|
|
}
|
|
|
|
// Cache lookup failed, call runtime.
|
|
SP[1] = type_arguments;
|
|
SP[2] = instantiator_type_args;
|
|
SP[3] = function_type_args;
|
|
|
|
Exit(thread, FP, SP + 4, pc);
|
|
NativeArguments args(thread, 3, SP + 1, SP - 1);
|
|
INVOKE_RUNTIME(DRT_InstantiateTypeArguments, args);
|
|
}
|
|
|
|
InstantiateTypeArgumentsTOSDone:
|
|
SP -= 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Throw, A);
|
|
{
|
|
SP[1] = 0; // Space for result.
|
|
Exit(thread, FP, SP + 2, pc);
|
|
if (rA == 0) { // Throw
|
|
NativeArguments args(thread, 1, SP, SP + 1);
|
|
INVOKE_RUNTIME(DRT_Throw, args);
|
|
} else { // ReThrow
|
|
NativeArguments args(thread, 2, SP - 1, SP + 1);
|
|
INVOKE_RUNTIME(DRT_ReThrow, args);
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Drop1, 0);
|
|
SP--;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Drop, 0);
|
|
SP -= rA;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DropR, 0);
|
|
RawObject* result = SP[0];
|
|
SP -= rA;
|
|
SP[0] = result;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadConstant, A_D);
|
|
FP[rA] = LOAD_CONSTANT(rD);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushConstant, __D);
|
|
*++SP = LOAD_CONSTANT(rD);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushNull, 0);
|
|
*++SP = Object::null();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushTrue, 0);
|
|
*++SP = Object::bool_true().raw();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushFalse, 0);
|
|
*++SP = Object::bool_false().raw();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushInt, A_X);
|
|
*++SP = Smi::New(rD);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Push, A_X);
|
|
*++SP = FP[rD];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Move, A_X);
|
|
FP[rA] = FP[rD];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Swap, A_X);
|
|
RawObject* tmp = FP[rD];
|
|
FP[rD] = FP[rA];
|
|
FP[rA] = tmp;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreLocal, A_X);
|
|
FP[rD] = *SP;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PopLocal, A_X);
|
|
FP[rD] = *SP--;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(MoveSpecial, A_D);
|
|
FP[rA] = special_[rD];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BooleanNegateTOS, 0);
|
|
SP[0] = (SP[0] == true_value) ? false_value : true_value;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BooleanNegate, A_D);
|
|
FP[rA] = (FP[rD] == true_value) ? false_value : true_value;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IndirectStaticCall, A_D);
|
|
|
|
// Check if single stepping.
|
|
if (thread->isolate()->single_step()) {
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
|
}
|
|
|
|
// Invoke target function.
|
|
{
|
|
const uint16_t argc = rA;
|
|
// Look up the function in the ICData.
|
|
RawObject* ic_data_obj = SP[0];
|
|
RawICData* ic_data = RAW_CAST(ICData, ic_data_obj);
|
|
RawObject** data = ic_data->ptr()->ic_data_->ptr()->data();
|
|
InterpreterHelpers::IncrementICUsageCount(data, 0, 0);
|
|
SP[0] = data[ICData::TargetIndexFor(ic_data->ptr()->state_bits_ & 0x3)];
|
|
RawObject** call_base = SP - argc;
|
|
RawObject** call_top = SP; // *SP contains function
|
|
argdesc_ = static_cast<RawArray*>(LOAD_CONSTANT(rD));
|
|
if (!Invoke(thread, call_base, call_top, &pc, &FP, &SP)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StaticCall, A_D);
|
|
const uint16_t argc = rA;
|
|
RawObject** call_base = SP - argc;
|
|
RawObject** call_top = SP; // *SP contains function
|
|
argdesc_ = static_cast<RawArray*>(LOAD_CONSTANT(rD));
|
|
if (!Invoke(thread, call_base, call_top, &pc, &FP, &SP)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InstanceCall, A_D);
|
|
|
|
// Check if single stepping.
|
|
if (thread->isolate()->single_step()) {
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
|
}
|
|
|
|
{
|
|
const uint16_t argc = rA;
|
|
const uint16_t kidx = rD;
|
|
|
|
RawObject** call_base = SP - argc + 1;
|
|
RawObject** call_top = SP + 1;
|
|
|
|
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
|
|
InterpreterHelpers::IncrementUsageCounter(
|
|
RAW_CAST(Function, icdata->ptr()->owner_));
|
|
if (ICData::NumArgsTestedBits::decode(icdata->ptr()->state_bits_) == 1) {
|
|
if (!InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
|
|
false /* optimized */)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
} else {
|
|
ASSERT(ICData::NumArgsTestedBits::decode(icdata->ptr()->state_bits_) ==
|
|
2);
|
|
if (!InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
|
|
false /* optimized */)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
}
|
|
}
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InstanceCall1Opt, A_D);
|
|
|
|
{
|
|
const uint16_t argc = rA;
|
|
const uint16_t kidx = rD;
|
|
|
|
RawObject** call_base = SP - argc + 1;
|
|
RawObject** call_top = SP + 1;
|
|
|
|
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
|
|
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
|
|
if (!InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP,
|
|
true /* optimized */)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InstanceCall2Opt, A_D);
|
|
|
|
{
|
|
const uint16_t argc = rA;
|
|
const uint16_t kidx = rD;
|
|
|
|
RawObject** call_base = SP - argc + 1;
|
|
RawObject** call_top = SP + 1;
|
|
|
|
RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx));
|
|
InterpreterHelpers::IncrementUsageCounter(FrameFunction(FP));
|
|
if (!InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP,
|
|
true /* optimized */)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushPolymorphicInstanceCall, A_D);
|
|
const uint8_t argc = rA;
|
|
const intptr_t cids_length = rD;
|
|
RawObject** args = SP - argc + 1;
|
|
const intptr_t receiver_cid = InterpreterHelpers::GetClassId(args[0]);
|
|
for (intptr_t i = 0; i < 2 * cids_length; i += 2) {
|
|
const intptr_t icdata_cid = KernelBytecode::DecodeD(*(pc + i));
|
|
if (receiver_cid == icdata_cid) {
|
|
RawFunction* target = RAW_CAST(
|
|
Function, LOAD_CONSTANT(KernelBytecode::DecodeD(*(pc + i + 1))));
|
|
*++SP = target;
|
|
pc++;
|
|
break;
|
|
}
|
|
}
|
|
pc += 2 * cids_length;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushPolymorphicInstanceCallByRange, A_D);
|
|
const uint8_t argc = rA;
|
|
const intptr_t cids_length = rD;
|
|
RawObject** args = SP - argc + 1;
|
|
const intptr_t receiver_cid = InterpreterHelpers::GetClassId(args[0]);
|
|
for (intptr_t i = 0; i < 3 * cids_length; i += 3) {
|
|
// Note unsigned types to get an unsigned range compare.
|
|
const uintptr_t cid_start = KernelBytecode::DecodeD(*(pc + i));
|
|
const uintptr_t cids = KernelBytecode::DecodeD(*(pc + i + 1));
|
|
if (receiver_cid - cid_start < cids) {
|
|
RawFunction* target = RAW_CAST(
|
|
Function, LOAD_CONSTANT(KernelBytecode::DecodeD(*(pc + i + 2))));
|
|
*++SP = target;
|
|
pc++;
|
|
break;
|
|
}
|
|
}
|
|
pc += 3 * cids_length;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(NativeCall, __D);
|
|
RawTypedData* data = static_cast<RawTypedData*>(LOAD_CONSTANT(rD));
|
|
MethodRecognizer::Kind kind = NativeEntryData::GetKind(data);
|
|
switch (kind) {
|
|
case MethodRecognizer::kObjectEquals: {
|
|
SP[-1] = SP[-1] == SP[0] ? Bool::True().raw() : Bool::False().raw();
|
|
SP--;
|
|
} break;
|
|
case MethodRecognizer::kStringBaseLength:
|
|
case MethodRecognizer::kStringBaseIsEmpty: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[String::length_offset() / kWordSize];
|
|
if (kind == MethodRecognizer::kStringBaseIsEmpty) {
|
|
SP[0] =
|
|
SP[0] == Smi::New(0) ? Bool::True().raw() : Bool::False().raw();
|
|
}
|
|
} break;
|
|
case MethodRecognizer::kGrowableArrayLength: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[GrowableObjectArray::length_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kObjectArrayLength:
|
|
case MethodRecognizer::kImmutableArrayLength: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[Array::length_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kTypedDataLength: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[TypedData::length_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kClassIDgetID: {
|
|
SP[0] = InterpreterHelpers::GetClassIdAsSmi(SP[0]);
|
|
} break;
|
|
case MethodRecognizer::kGrowableArrayCapacity: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
instance = reinterpret_cast<RawInstance**>(
|
|
instance->ptr())[GrowableObjectArray::data_offset() / kWordSize];
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[Array::length_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kListFactory: {
|
|
// factory List<E>([int length]) {
|
|
// return (:arg_desc.positional_count == 2) ? new _List<E>(length)
|
|
// : new _GrowableList<E>(0);
|
|
// }
|
|
if (InterpreterHelpers::ArgDescPosCount(argdesc_) == 2) {
|
|
SP[1] = SP[0]; // length
|
|
SP[2] = SP[-1]; // type
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments native_args(thread, 2, SP + 1, SP - 1);
|
|
INVOKE_RUNTIME(DRT_AllocateArray, native_args);
|
|
SP -= 1; // Result is in SP - 1.
|
|
} else {
|
|
ASSERT(InterpreterHelpers::ArgDescPosCount(argdesc_) == 1);
|
|
// SP[-1] is type.
|
|
// The native wrapper pushed null as the optional length argument.
|
|
ASSERT(SP[0] == null_value);
|
|
SP[0] = Smi::New(0); // Patch null length with zero length.
|
|
SP[1] = thread->isolate()->object_store()->growable_list_factory();
|
|
// Change the ArgumentsDescriptor of the call with a new cached one.
|
|
argdesc_ = ArgumentsDescriptor::New(
|
|
0, KernelBytecode::kNativeCallToGrowableListArgc);
|
|
// Note the special handling of the return of this call in DecodeArgc.
|
|
if (!Invoke(thread, SP - 1, SP + 1, &pc, &FP, &SP)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
}
|
|
} break;
|
|
case MethodRecognizer::kObjectArrayAllocate: {
|
|
SP[1] = SP[0]; // length
|
|
SP[2] = SP[-1]; // type
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments native_args(thread, 2, SP + 1, SP - 1);
|
|
INVOKE_RUNTIME(DRT_AllocateArray, native_args);
|
|
SP -= 1; // Result is in SP - 1.
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_getIndex: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::index_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_setIndex: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
|
|
instance->StorePointer(reinterpret_cast<RawObject**>(instance->ptr()) +
|
|
LinkedHashMap::index_offset() / kWordSize,
|
|
SP[0]);
|
|
*--SP = null_value;
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_getData: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::data_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_setData: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
|
|
instance->StorePointer(reinterpret_cast<RawObject**>(instance->ptr()) +
|
|
LinkedHashMap::data_offset() / kWordSize,
|
|
SP[0]);
|
|
*--SP = null_value;
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_getHashMask: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::hash_mask_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_setHashMask: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
|
|
reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::hash_mask_offset() / kWordSize] =
|
|
SP[0];
|
|
*--SP = null_value;
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_getUsedData: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::used_data_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_setUsedData: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
|
|
reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::used_data_offset() / kWordSize] =
|
|
SP[0];
|
|
*--SP = null_value;
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_getDeletedKeys: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::deleted_keys_offset() / kWordSize];
|
|
} break;
|
|
case MethodRecognizer::kLinkedHashMap_setDeletedKeys: {
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
|
|
reinterpret_cast<RawObject**>(
|
|
instance->ptr())[LinkedHashMap::deleted_keys_offset() / kWordSize] =
|
|
SP[0];
|
|
*--SP = null_value;
|
|
} break;
|
|
default: {
|
|
NativeFunctionWrapper trampoline = NativeEntryData::GetTrampoline(data);
|
|
NativeFunction function = NativeEntryData::GetNativeFunction(data);
|
|
intptr_t argc_tag = NativeEntryData::GetArgcTag(data);
|
|
const intptr_t num_arguments =
|
|
NativeArguments::ArgcBits::decode(argc_tag);
|
|
|
|
*++SP = null_value; // Result slot.
|
|
|
|
RawObject** incoming_args = SP - num_arguments;
|
|
RawObject** return_slot = SP;
|
|
Exit(thread, FP, SP, pc);
|
|
NativeArguments args(thread, argc_tag, incoming_args, return_slot);
|
|
INVOKE_NATIVE(trampoline,
|
|
reinterpret_cast<Dart_NativeFunction>(function),
|
|
reinterpret_cast<Dart_NativeArguments>(&args));
|
|
|
|
*(SP - num_arguments) = *return_slot;
|
|
SP -= num_arguments;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(OneByteStringFromCharCode, A_X);
|
|
const intptr_t char_code = Smi::Value(RAW_CAST(Smi, FP[rD]));
|
|
ASSERT(char_code >= 0);
|
|
ASSERT(char_code <= 255);
|
|
RawString** strings = Symbols::PredefinedAddress();
|
|
const intptr_t index = char_code + Symbols::kNullCharCodeSymbolOffset;
|
|
FP[rA] = strings[index];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StringToCharCode, A_X);
|
|
RawOneByteString* str = RAW_CAST(OneByteString, FP[rD]);
|
|
if (str->ptr()->length_ == Smi::New(1)) {
|
|
FP[rA] = Smi::New(str->ptr()->data()[0]);
|
|
} else {
|
|
FP[rA] = Smi::New(-1);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AddTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(intptr_t, SignedAddWithOverflow);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(SubTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(intptr_t, SignedSubWithOverflow);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(MulTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(intptr_t, SMI_MUL);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(BitOrTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(intptr_t, SMI_BITOR);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(BitAndTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(intptr_t, SMI_BITAND);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(EqualTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(RawObject*, SMI_EQ);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(LessThanTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(RawObject*, SMI_LT);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(GreaterThanTOS, A_B_C);
|
|
SMI_FASTPATH_TOS(RawObject*, SMI_GT);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(SmiAddTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
SP--;
|
|
SP[0] = Smi::New(Smi::Value(left) + Smi::Value(right));
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(SmiSubTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
SP--;
|
|
SP[0] = Smi::New(Smi::Value(left) - Smi::Value(right));
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(SmiMulTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
SP--;
|
|
SP[0] = Smi::New(Smi::Value(left) * Smi::Value(right));
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(SmiBitAndTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
SP--;
|
|
SP[0] = Smi::New(Smi::Value(left) & Smi::Value(right));
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(Add, A_B_C);
|
|
SMI_OP_CHECK(intptr_t, SignedAddWithOverflow);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(Sub, A_B_C);
|
|
SMI_OP_CHECK(intptr_t, SignedSubWithOverflow);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(Mul, A_B_C);
|
|
SMI_OP_CHECK(intptr_t, SMI_MUL);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(Neg, A_D);
|
|
const intptr_t value = reinterpret_cast<intptr_t>(FP[rD]);
|
|
intptr_t* out = reinterpret_cast<intptr_t*>(&FP[rA]);
|
|
if (LIKELY(!SignedSubWithOverflow(0, value, out))) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(BitOr, A_B_C);
|
|
SMI_OP_NOCHECK(intptr_t, SMI_BITOR);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(BitAnd, A_B_C);
|
|
SMI_OP_NOCHECK(intptr_t, SMI_BITAND);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(BitXor, A_B_C);
|
|
SMI_OP_NOCHECK(intptr_t, SMI_BITXOR);
|
|
DISPATCH();
|
|
}
|
|
{
|
|
BYTECODE(BitNot, A_D);
|
|
const intptr_t value = reinterpret_cast<intptr_t>(FP[rD]);
|
|
*reinterpret_cast<intptr_t*>(&FP[rA]) = ~value & (~kSmiTagMask);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Div, A_B_C);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]);
|
|
if (rhs != 0) {
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]);
|
|
const intptr_t res = (lhs >> kSmiTagSize) / (rhs >> kSmiTagSize);
|
|
const intptr_t untaggable = 0x40000000L;
|
|
if (res != untaggable) {
|
|
*reinterpret_cast<intptr_t*>(&FP[rA]) = res << kSmiTagSize;
|
|
pc++;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Mod, A_B_C);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]);
|
|
if (rhs != 0) {
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]);
|
|
const intptr_t res = ((lhs >> kSmiTagSize) % (rhs >> kSmiTagSize))
|
|
<< kSmiTagSize;
|
|
*reinterpret_cast<intptr_t*>(&FP[rA]) =
|
|
(res < 0) ? ((rhs < 0) ? (res - rhs) : (res + rhs)) : res;
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Shl, A_B_C);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]) >> kSmiTagSize;
|
|
const int kBitsPerInt32 = 32;
|
|
if (static_cast<uintptr_t>(rhs) < kBitsPerInt32) {
|
|
const int32_t lhs = reinterpret_cast<intptr_t>(FP[rB]);
|
|
const int32_t res = lhs << rhs;
|
|
if (lhs == (res >> rhs)) {
|
|
*reinterpret_cast<intptr_t*>(&FP[rA]) = static_cast<intptr_t>(res);
|
|
pc++;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Shr, A_B_C);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]) >> kSmiTagSize;
|
|
if (rhs >= 0) {
|
|
const intptr_t shift_amount = (rhs >= 32) ? (32 - 1) : rhs;
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]) >> kSmiTagSize;
|
|
*reinterpret_cast<intptr_t*>(&FP[rA]) = (lhs >> shift_amount)
|
|
<< kSmiTagSize;
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(ShlImm, A_B_C);
|
|
const uint8_t shift = rC;
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]);
|
|
FP[rA] = reinterpret_cast<RawObject*>(lhs << shift);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Min, A_B_C);
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]);
|
|
FP[rA] = reinterpret_cast<RawObject*>((lhs < rhs) ? lhs : rhs);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Max, A_B_C);
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rB]);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rC]);
|
|
FP[rA] = reinterpret_cast<RawObject*>((lhs > rhs) ? lhs : rhs);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(UnboxInt32, A_B_C);
|
|
const intptr_t box_cid = InterpreterHelpers::GetClassId(FP[rB]);
|
|
const bool may_truncate = rC == 1;
|
|
if (box_cid == kSmiCid) {
|
|
const intptr_t value = reinterpret_cast<intptr_t>(FP[rB]) >> kSmiTagSize;
|
|
const int32_t value32 = static_cast<int32_t>(value);
|
|
if (may_truncate || (value == static_cast<intptr_t>(value32))) {
|
|
FP[rA] = reinterpret_cast<RawObject*>(value);
|
|
pc++;
|
|
}
|
|
} else if (box_cid == kMintCid) {
|
|
RawMint* mint = RAW_CAST(Mint, FP[rB]);
|
|
const int64_t value = mint->ptr()->value_;
|
|
const int32_t value32 = static_cast<int32_t>(value);
|
|
if (may_truncate || (value == static_cast<int64_t>(value32))) {
|
|
FP[rA] = reinterpret_cast<RawObject*>(value);
|
|
pc++;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
#if defined(ARCH_IS_64_BIT)
|
|
{
|
|
BYTECODE(WriteIntoDouble, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
RawDouble* box = RAW_CAST(Double, FP[rA]);
|
|
box->ptr()->value_ = value;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(UnboxDouble, A_D);
|
|
const RawDouble* box = RAW_CAST(Double, FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(box->ptr()->value_);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckedUnboxDouble, A_D);
|
|
const intptr_t box_cid = InterpreterHelpers::GetClassId(FP[rD]);
|
|
if (box_cid == kSmiCid) {
|
|
const intptr_t value = reinterpret_cast<intptr_t>(FP[rD]) >> kSmiTagSize;
|
|
const double result = static_cast<double>(value);
|
|
FP[rA] = bit_cast<RawObject*, double>(result);
|
|
pc++;
|
|
} else if (box_cid == kDoubleCid) {
|
|
const RawDouble* box = RAW_CAST(Double, FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(box->ptr()->value_);
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleToSmi, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
if (!isnan(value)) {
|
|
const intptr_t result = static_cast<intptr_t>(value);
|
|
if ((result <= Smi::kMaxValue) && (result >= Smi::kMinValue)) {
|
|
FP[rA] = reinterpret_cast<RawObject*>(result << kSmiTagSize);
|
|
pc++;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(SmiToDouble, A_D);
|
|
const intptr_t value = reinterpret_cast<intptr_t>(FP[rD]) >> kSmiTagSize;
|
|
const double result = static_cast<double>(value);
|
|
FP[rA] = bit_cast<RawObject*, double>(result);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DAdd, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
FP[rA] = bit_cast<RawObject*, double>(lhs + rhs);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DSub, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
FP[rA] = bit_cast<RawObject*, double>(lhs - rhs);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMul, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
FP[rA] = bit_cast<RawObject*, double>(lhs * rhs);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DDiv, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
const double result = lhs / rhs;
|
|
FP[rA] = bit_cast<RawObject*, double>(result);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DNeg, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(-value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DSqrt, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(sqrt(value));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DSin, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(sin(value));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DCos, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(cos(value));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DPow, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
const double result = pow(lhs, rhs);
|
|
FP[rA] = bit_cast<RawObject*, double>(result);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMod, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
const double result = DartModulo(lhs, rhs);
|
|
FP[rA] = bit_cast<RawObject*, double>(result);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMin, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
FP[rA] = bit_cast<RawObject*, double>(fmin(lhs, rhs));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMax, A_B_C);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rB]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rC]);
|
|
FP[rA] = bit_cast<RawObject*, double>(fmax(lhs, rhs));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DTruncate, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(trunc(value));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DFloor, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(floor(value));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DCeil, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
FP[rA] = bit_cast<RawObject*, double>(ceil(value));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleToFloat, A_D);
|
|
const double value = bit_cast<double, RawObject*>(FP[rD]);
|
|
const float valuef = static_cast<float>(value);
|
|
*reinterpret_cast<float*>(&FP[rA]) = valuef;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(FloatToDouble, A_D);
|
|
const float valuef = *reinterpret_cast<float*>(&FP[rD]);
|
|
const double value = static_cast<double>(valuef);
|
|
FP[rA] = bit_cast<RawObject*, double>(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleIsNaN, A);
|
|
const double v = bit_cast<double, RawObject*>(FP[rA]);
|
|
if (!isnan(v)) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleIsInfinite, A);
|
|
const double v = bit_cast<double, RawObject*>(FP[rA]);
|
|
if (!isinf(v)) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedFloat32, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rB], FP[rC]);
|
|
const uint32_t value = *reinterpret_cast<uint32_t*>(data);
|
|
const uint64_t value64 = value;
|
|
FP[rA] = reinterpret_cast<RawObject*>(value64);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexed4Float32, A_B_C);
|
|
ASSERT(RawObject::IsTypedDataClassId(FP[rB]->GetClassId()));
|
|
RawTypedData* array = reinterpret_cast<RawTypedData*>(FP[rB]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
const uint32_t value =
|
|
reinterpret_cast<uint32_t*>(array->ptr()->data())[Smi::Value(index)];
|
|
const uint64_t value64 = value; // sign extend to clear high bits.
|
|
FP[rA] = reinterpret_cast<RawObject*>(value64);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedFloat64, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rB], FP[rC]);
|
|
*reinterpret_cast<uint64_t*>(&FP[rA]) = *reinterpret_cast<uint64_t*>(data);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexed8Float64, A_B_C);
|
|
ASSERT(RawObject::IsTypedDataClassId(FP[rB]->GetClassId()));
|
|
RawTypedData* array = reinterpret_cast<RawTypedData*>(FP[rB]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
const int64_t value =
|
|
reinterpret_cast<int64_t*>(array->ptr()->data())[Smi::Value(index)];
|
|
FP[rA] = reinterpret_cast<RawObject*>(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedFloat32, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rA], FP[rB]);
|
|
const uint64_t value = reinterpret_cast<uint64_t>(FP[rC]);
|
|
const uint32_t value32 = value;
|
|
*reinterpret_cast<uint32_t*>(data) = value32;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexed4Float32, A_B_C);
|
|
ASSERT(RawObject::IsTypedDataClassId(FP[rA]->GetClassId()));
|
|
RawTypedData* array = reinterpret_cast<RawTypedData*>(FP[rA]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
const uint64_t value = reinterpret_cast<uint64_t>(FP[rC]);
|
|
const uint32_t value32 = value;
|
|
reinterpret_cast<uint32_t*>(array->ptr()->data())[Smi::Value(index)] =
|
|
value32;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedFloat64, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rA], FP[rB]);
|
|
*reinterpret_cast<uint64_t*>(data) = reinterpret_cast<uint64_t>(FP[rC]);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexed8Float64, A_B_C);
|
|
ASSERT(RawObject::IsTypedDataClassId(FP[rA]->GetClassId()));
|
|
RawTypedData* array = reinterpret_cast<RawTypedData*>(FP[rA]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
const int64_t value = reinterpret_cast<int64_t>(FP[rC]);
|
|
reinterpret_cast<int64_t*>(array->ptr()->data())[Smi::Value(index)] = value;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BoxInt32, A_D);
|
|
// Casts sign-extend high 32 bits from low 32 bits.
|
|
const intptr_t value = reinterpret_cast<intptr_t>(FP[rD]);
|
|
const int32_t value32 = static_cast<int32_t>(value);
|
|
FP[rA] = Smi::New(static_cast<intptr_t>(value32));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BoxUint32, A_D);
|
|
// Casts to zero out high 32 bits.
|
|
const uintptr_t value = reinterpret_cast<uintptr_t>(FP[rD]);
|
|
const uint32_t value32 = static_cast<uint32_t>(value);
|
|
FP[rA] = Smi::New(static_cast<intptr_t>(value32));
|
|
DISPATCH();
|
|
}
|
|
#else // defined(ARCH_IS_64_BIT)
|
|
{
|
|
BYTECODE(WriteIntoDouble, A_D);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(UnboxDouble, A_D);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckedUnboxDouble, A_D);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleToSmi, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(SmiToDouble, A_D);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DAdd, A_B_C);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DSub, A_B_C);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMul, A_B_C);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DDiv, A_B_C);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DNeg, A_D);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DSqrt, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DSin, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DCos, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DPow, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMod, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMin, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DMax, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DTruncate, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DFloor, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DCeil, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleToFloat, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(FloatToDouble, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleIsNaN, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DoubleIsInfinite, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedFloat32, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexed4Float32, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedFloat64, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexed8Float64, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedFloat32, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexed4Float32, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedFloat64, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexed8Float64, A_B_C);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BoxInt32, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BoxUint32, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
#endif // defined(ARCH_IS_64_BIT)
|
|
|
|
// Return and return like instructions (Intrinsic).
|
|
{
|
|
RawObject* result; // result to return to the caller.
|
|
|
|
BYTECODE(Intrinsic, A);
|
|
// Try invoking intrinsic handler. If it succeeds (returns true)
|
|
// then just return the value it returned to the caller.
|
|
result = null_value;
|
|
if (!intrinsics_[rA](thread, FP, &result)) {
|
|
DISPATCH();
|
|
}
|
|
goto ReturnImpl;
|
|
|
|
BYTECODE(Return, A);
|
|
result = FP[rA];
|
|
goto ReturnImpl;
|
|
|
|
BYTECODE(ReturnTOS, 0);
|
|
result = *SP;
|
|
// Fall through to the ReturnImpl.
|
|
|
|
ReturnImpl:
|
|
// Restore caller PC.
|
|
pc = SavedCallerPC(FP);
|
|
pc_ = reinterpret_cast<uword>(pc); // For the profiler.
|
|
|
|
// Check if it is a fake PC marking the entry frame.
|
|
if (IsEntryFrameMarker(reinterpret_cast<uword>(pc))) {
|
|
// Pop entry frame.
|
|
fp_ = SavedCallerFP(FP);
|
|
// Restore exit frame info saved in entry frame.
|
|
pp_ = reinterpret_cast<RawObjectPool*>(fp_[kKBCSavedPpSlotFromEntryFp]);
|
|
argdesc_ =
|
|
reinterpret_cast<RawArray*>(fp_[kKBCSavedArgDescSlotFromEntryFp]);
|
|
uword exit_fp = reinterpret_cast<uword>(fp_[kKBCExitLinkSlotFromEntryFp]);
|
|
thread->set_top_exit_frame_info(exit_fp);
|
|
thread->set_top_resource(top_resource);
|
|
thread->set_vm_tag(vm_tag);
|
|
#if defined(DEBUG)
|
|
if (IsTracingExecution()) {
|
|
THR_Print("%" Pu64 " ", icount_);
|
|
THR_Print("Returning from interpreter 0x%" Px " at fp_ 0x%" Px
|
|
" exit 0x%" Px "\n",
|
|
reinterpret_cast<uword>(this), reinterpret_cast<uword>(fp_),
|
|
exit_fp);
|
|
}
|
|
ASSERT(reinterpret_cast<uword>(fp_) < stack_limit());
|
|
const intptr_t argc = reinterpret_cast<uword>(pc) >> 2;
|
|
ASSERT(fp_ == FrameArguments(FP, argc + kKBCEntrySavedSlots));
|
|
// Exception propagation should have been done.
|
|
ASSERT(!result->IsHeapObject() ||
|
|
result->GetClassId() != kUnhandledExceptionCid);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
// Look at the caller to determine how many arguments to pop.
|
|
const uint8_t argc = KernelBytecode::DecodeArgc(pc[-1]);
|
|
|
|
// Restore SP, FP and PP. Push result and dispatch.
|
|
SP = FrameArguments(FP, argc);
|
|
FP = SavedCallerFP(FP);
|
|
pp_ = InterpreterHelpers::FrameCode(FP)->ptr()->object_pool_;
|
|
*SP = result;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreStaticTOS, A_D);
|
|
RawField* field = reinterpret_cast<RawField*>(LOAD_CONSTANT(rD));
|
|
RawInstance* value = static_cast<RawInstance*>(*SP--);
|
|
field->StorePointer(&field->ptr()->value_.static_value_, value, thread);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(PushStatic, A_D);
|
|
RawField* field = reinterpret_cast<RawField*>(LOAD_CONSTANT(rD));
|
|
// Note: field is also on the stack, hence no increment.
|
|
*SP = field->ptr()->value_.static_value_;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreField, A_B_C);
|
|
const uint16_t offset_in_words = rB;
|
|
const uint16_t value_reg = rC;
|
|
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(FP[rA]);
|
|
RawObject* value = FP[value_reg];
|
|
|
|
// TODO(regis): Implement cid guard.
|
|
ASSERT(!thread->isolate()->use_field_guards());
|
|
|
|
instance->StorePointer(
|
|
reinterpret_cast<RawObject**>(instance->ptr()) + offset_in_words, value,
|
|
thread);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreFieldExt, A_D);
|
|
// The offset is stored in the following nop-instruction which is skipped.
|
|
const uint16_t offset_in_words = KernelBytecode::DecodeD(*pc++);
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(FP[rA]);
|
|
RawObject* value = FP[rD];
|
|
|
|
UNREACHABLE(); // TODO(regis): unused, remove.
|
|
|
|
instance->StorePointer(
|
|
reinterpret_cast<RawObject**>(instance->ptr()) + offset_in_words, value,
|
|
thread);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreFieldTOS, __D);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Smi::Value(RAW_CAST(Smi, LOAD_CONSTANT(rD))));
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(SP[-1]);
|
|
RawObject* value = reinterpret_cast<RawObject*>(SP[0]);
|
|
SP -= 2; // Drop instance and value.
|
|
|
|
// TODO(regis): Implement cid guard.
|
|
ASSERT(!thread->isolate()->use_field_guards());
|
|
|
|
instance->StorePointer(
|
|
reinterpret_cast<RawObject**>(instance->ptr()) + offset_in_words, value,
|
|
thread);
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreContextParent, 0);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Context::parent_offset() / kWordSize);
|
|
RawContext* instance = reinterpret_cast<RawContext*>(SP[-1]);
|
|
RawContext* value = reinterpret_cast<RawContext*>(SP[0]);
|
|
SP -= 2; // Drop instance and value.
|
|
|
|
instance->StorePointer(
|
|
reinterpret_cast<RawContext**>(instance->ptr()) + offset_in_words,
|
|
value, thread);
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreContextVar, __D);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Context::variable_offset(rD) / kWordSize);
|
|
RawContext* instance = reinterpret_cast<RawContext*>(SP[-1]);
|
|
RawObject* value = reinterpret_cast<RawContext*>(SP[0]);
|
|
SP -= 2; // Drop instance and value.
|
|
|
|
instance->StorePointer(
|
|
reinterpret_cast<RawObject**>(instance->ptr()) + offset_in_words, value,
|
|
thread);
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadField, A_B_C);
|
|
const uint16_t instance_reg = rB;
|
|
const uint16_t offset_in_words = rC;
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(FP[instance_reg]);
|
|
FP[rA] = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadFieldExt, A_D);
|
|
// The offset is stored in the following nop-instruction which is skipped.
|
|
const uint16_t offset_in_words = KernelBytecode::DecodeD(*pc++);
|
|
const uint16_t instance_reg = rD;
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(FP[instance_reg]);
|
|
FP[rA] = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadUntagged, A_B_C);
|
|
const uint16_t instance_reg = rB;
|
|
const uint16_t offset_in_words = rC;
|
|
RawInstance* instance = reinterpret_cast<RawInstance*>(FP[instance_reg]);
|
|
FP[rA] = reinterpret_cast<RawObject**>(instance)[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadFieldTOS, __D);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Smi::Value(RAW_CAST(Smi, LOAD_CONSTANT(rD))));
|
|
RawInstance* instance = static_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadTypeArgumentsField, __D);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Smi::Value(RAW_CAST(Smi, LOAD_CONSTANT(rD))));
|
|
RawInstance* instance = static_cast<RawInstance*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadContextParent, 0);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Context::parent_offset() / kWordSize);
|
|
RawContext* instance = static_cast<RawContext*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadContextVar, __D);
|
|
const uword offset_in_words =
|
|
static_cast<uword>(Context::variable_offset(rD) / kWordSize);
|
|
RawContext* instance = static_cast<RawContext*>(SP[0]);
|
|
SP[0] = reinterpret_cast<RawObject**>(instance->ptr())[offset_in_words];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InitStaticTOS, 0);
|
|
UNREACHABLE(); // Not used. TODO(regis): Remove this bytecode.
|
|
RawField* field = static_cast<RawField*>(*SP--);
|
|
RawObject* value = field->ptr()->value_.static_value_;
|
|
if ((value == Object::sentinel().raw()) ||
|
|
(value == Object::transition_sentinel().raw())) {
|
|
// Note: SP[1] already contains the field object.
|
|
SP[2] = 0;
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments args(thread, 1, SP + 1, SP + 2);
|
|
INVOKE_RUNTIME(DRT_InitStaticField, args);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
// TODO(vegorov) allocation bytecodes can benefit from the new-space
|
|
// allocation fast-path that does not transition into the runtime system.
|
|
{
|
|
BYTECODE(AllocateUninitializedContext, A_D);
|
|
const uint16_t num_context_variables = rD;
|
|
const intptr_t instance_size = Context::InstanceSize(num_context_variables);
|
|
const uword start =
|
|
thread->heap()->new_space()->TryAllocateInTLAB(thread, instance_size);
|
|
if (LIKELY(start != 0)) {
|
|
uint32_t tags = 0;
|
|
tags = RawObject::ClassIdTag::update(kContextCid, tags);
|
|
tags = RawObject::SizeTag::update(instance_size, tags);
|
|
tags = RawObject::NewBit::update(true, tags);
|
|
// Also writes 0 in the hash_ field of the header.
|
|
*reinterpret_cast<uword*>(start + Array::tags_offset()) = tags;
|
|
*reinterpret_cast<uword*>(start + Context::num_variables_offset()) =
|
|
num_context_variables;
|
|
FP[rA] = reinterpret_cast<RawObject*>(start + kHeapObjectTag);
|
|
pc += 2;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AllocateContext, A_D);
|
|
const uint16_t num_context_variables = rD;
|
|
{
|
|
*++SP = 0;
|
|
SP[1] = Smi::New(num_context_variables);
|
|
Exit(thread, FP, SP + 2, pc);
|
|
NativeArguments args(thread, 1, SP + 1, SP);
|
|
INVOKE_RUNTIME(DRT_AllocateContext, args);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CloneContext, A);
|
|
{
|
|
SP[1] = SP[0]; // Context to clone.
|
|
Exit(thread, FP, SP + 2, pc);
|
|
NativeArguments args(thread, 1, SP + 1, SP);
|
|
INVOKE_RUNTIME(DRT_CloneContext, args);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AllocateOpt, A_D);
|
|
uint32_t tags = Smi::Value(RAW_CAST(Smi, LOAD_CONSTANT(rD)));
|
|
const intptr_t instance_size = RawObject::SizeTag::decode(tags);
|
|
const uword start =
|
|
thread->heap()->new_space()->TryAllocateInTLAB(thread, instance_size);
|
|
if (LIKELY(start != 0)) {
|
|
// Writes both the tags and the initial identity hash on 64 bit platforms.
|
|
tags = RawObject::NewBit::update(true, tags);
|
|
*reinterpret_cast<uword*>(start + Instance::tags_offset()) = tags;
|
|
for (intptr_t current_offset = sizeof(RawInstance);
|
|
current_offset < instance_size; current_offset += kWordSize) {
|
|
*reinterpret_cast<RawObject**>(start + current_offset) = null_value;
|
|
}
|
|
FP[rA] = reinterpret_cast<RawObject*>(start + kHeapObjectTag);
|
|
pc += 2;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Allocate, A_D);
|
|
SP[1] = 0; // Space for the result.
|
|
SP[2] = LOAD_CONSTANT(rD); // Class object.
|
|
SP[3] = null_value; // Type arguments.
|
|
Exit(thread, FP, SP + 4, pc);
|
|
NativeArguments args(thread, 2, SP + 2, SP + 1);
|
|
INVOKE_RUNTIME(DRT_AllocateObject, args);
|
|
SP++; // Result is in SP[1].
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AllocateTOpt, A_D);
|
|
uint32_t tags = Smi::Value(RAW_CAST(Smi, LOAD_CONSTANT(rD)));
|
|
const intptr_t instance_size = RawObject::SizeTag::decode(tags);
|
|
const uword start =
|
|
thread->heap()->new_space()->TryAllocateInTLAB(thread, instance_size);
|
|
if (LIKELY(start != 0)) {
|
|
RawObject* type_args = SP[0];
|
|
const intptr_t type_args_offset = KernelBytecode::DecodeD(*pc);
|
|
// Writes both the tags and the initial identity hash on 64 bit platforms.
|
|
tags = RawObject::NewBit::update(true, tags);
|
|
*reinterpret_cast<uword*>(start + Instance::tags_offset()) = tags;
|
|
for (intptr_t current_offset = sizeof(RawInstance);
|
|
current_offset < instance_size; current_offset += kWordSize) {
|
|
*reinterpret_cast<RawObject**>(start + current_offset) = null_value;
|
|
}
|
|
*reinterpret_cast<RawObject**>(start + type_args_offset) = type_args;
|
|
FP[rA] = reinterpret_cast<RawObject*>(start + kHeapObjectTag);
|
|
SP -= 1; // Consume the type arguments on the stack.
|
|
pc += 4;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AllocateT, 0);
|
|
SP[1] = SP[-0]; // Class object.
|
|
SP[2] = SP[-1]; // Type arguments
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments args(thread, 2, SP + 1, SP - 1);
|
|
INVOKE_RUNTIME(DRT_AllocateObject, args);
|
|
SP -= 1; // Result is in SP - 1.
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CreateArrayOpt, A_B_C);
|
|
if (LIKELY(!FP[rB]->IsHeapObject())) {
|
|
const intptr_t length = Smi::Value(RAW_CAST(Smi, FP[rB]));
|
|
if (LIKELY(static_cast<uintptr_t>(length) <= Array::kMaxElements)) {
|
|
const intptr_t fixed_size_plus_alignment_padding =
|
|
sizeof(RawArray) + kObjectAlignment - 1;
|
|
const intptr_t instance_size =
|
|
(fixed_size_plus_alignment_padding + length * kWordSize) &
|
|
~(kObjectAlignment - 1);
|
|
const uword start = thread->heap()->new_space()->TryAllocateInTLAB(
|
|
thread, instance_size);
|
|
if (LIKELY(start != 0)) {
|
|
const intptr_t cid = kArrayCid;
|
|
uword tags = 0;
|
|
if (LIKELY(instance_size <= RawObject::SizeTag::kMaxSizeTag)) {
|
|
tags = RawObject::SizeTag::update(instance_size, tags);
|
|
}
|
|
tags = RawObject::ClassIdTag::update(cid, tags);
|
|
tags = RawObject::NewBit::update(true, tags);
|
|
// Writes both the tags and the initial identity hash on 64 bit
|
|
// platforms.
|
|
*reinterpret_cast<uword*>(start + Instance::tags_offset()) = tags;
|
|
*reinterpret_cast<RawObject**>(start + Array::length_offset()) =
|
|
FP[rB];
|
|
*reinterpret_cast<RawObject**>(
|
|
start + Array::type_arguments_offset()) = FP[rC];
|
|
RawObject** data =
|
|
reinterpret_cast<RawObject**>(start + Array::data_offset());
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
data[i] = null_value;
|
|
}
|
|
FP[rA] = reinterpret_cast<RawObject*>(start + kHeapObjectTag);
|
|
pc += 4;
|
|
}
|
|
}
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CreateArrayTOS, 0);
|
|
SP[1] = SP[-0]; // Length.
|
|
SP[2] = SP[-1]; // Type.
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments args(thread, 2, SP + 1, SP - 1);
|
|
INVOKE_RUNTIME(DRT_AllocateArray, args);
|
|
SP -= 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(InstanceOf, 0);
|
|
// Stack: instance, instantiator type args, function type args, type, cache
|
|
RawInstance* instance = static_cast<RawInstance*>(SP[-4]);
|
|
RawTypeArguments* instantiator_type_arguments =
|
|
static_cast<RawTypeArguments*>(SP[-3]);
|
|
RawTypeArguments* function_type_arguments =
|
|
static_cast<RawTypeArguments*>(SP[-2]);
|
|
RawAbstractType* type = static_cast<RawAbstractType*>(SP[-1]);
|
|
RawSubtypeTestCache* cache = static_cast<RawSubtypeTestCache*>(SP[0]);
|
|
|
|
if (cache != null_value) {
|
|
const intptr_t cid = InterpreterHelpers::GetClassId(instance);
|
|
|
|
RawTypeArguments* instance_type_arguments =
|
|
static_cast<RawTypeArguments*>(null_value);
|
|
RawObject* instance_cid_or_function;
|
|
RawTypeArguments* parent_function_type_arguments;
|
|
RawTypeArguments* delayed_function_type_arguments;
|
|
if (cid == kClosureCid) {
|
|
RawClosure* closure = static_cast<RawClosure*>(instance);
|
|
instance_type_arguments = closure->ptr()->instantiator_type_arguments_;
|
|
parent_function_type_arguments =
|
|
closure->ptr()->function_type_arguments_;
|
|
delayed_function_type_arguments =
|
|
closure->ptr()->delayed_type_arguments_;
|
|
instance_cid_or_function = closure->ptr()->function_;
|
|
} else {
|
|
instance_cid_or_function = Smi::New(cid);
|
|
|
|
RawClass* instance_class = thread->isolate()->class_table()->At(cid);
|
|
if (instance_class->ptr()->num_type_arguments_ < 0) {
|
|
goto InstanceOfCallRuntime;
|
|
} else if (instance_class->ptr()->num_type_arguments_ > 0) {
|
|
instance_type_arguments = reinterpret_cast<RawTypeArguments**>(
|
|
instance->ptr())[instance_class->ptr()
|
|
->type_arguments_field_offset_in_words_];
|
|
}
|
|
parent_function_type_arguments =
|
|
static_cast<RawTypeArguments*>(null_value);
|
|
delayed_function_type_arguments =
|
|
static_cast<RawTypeArguments*>(null_value);
|
|
}
|
|
|
|
for (RawObject** entries = cache->ptr()->cache_->ptr()->data();
|
|
entries[0] != null_value;
|
|
entries += SubtypeTestCache::kTestEntryLength) {
|
|
if ((entries[SubtypeTestCache::kInstanceClassIdOrFunction] ==
|
|
instance_cid_or_function) &&
|
|
(entries[SubtypeTestCache::kInstanceTypeArguments] ==
|
|
instance_type_arguments) &&
|
|
(entries[SubtypeTestCache::kInstantiatorTypeArguments] ==
|
|
instantiator_type_arguments) &&
|
|
(entries[SubtypeTestCache::kFunctionTypeArguments] ==
|
|
function_type_arguments) &&
|
|
(entries[SubtypeTestCache::kInstanceParentFunctionTypeArguments] ==
|
|
parent_function_type_arguments) &&
|
|
(entries[SubtypeTestCache::kInstanceDelayedFunctionTypeArguments] ==
|
|
delayed_function_type_arguments)) {
|
|
SP[-4] = entries[SubtypeTestCache::kTestResult];
|
|
goto InstanceOfOk;
|
|
}
|
|
}
|
|
}
|
|
|
|
// clang-format off
|
|
InstanceOfCallRuntime:
|
|
{
|
|
SP[1] = instance;
|
|
SP[2] = type;
|
|
SP[3] = instantiator_type_arguments;
|
|
SP[4] = function_type_arguments;
|
|
SP[5] = cache;
|
|
Exit(thread, FP, SP + 6, pc);
|
|
NativeArguments native_args(thread, 5, SP + 1, SP - 4);
|
|
INVOKE_RUNTIME(DRT_Instanceof, native_args);
|
|
}
|
|
// clang-format on
|
|
|
|
InstanceOfOk:
|
|
SP -= 4;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(BadTypeError, 0);
|
|
// Stack: instance, instantiator type args, function type args, type, name
|
|
RawObject** args = SP - 4;
|
|
if (args[0] != null_value) {
|
|
SP[1] = args[0]; // instance.
|
|
SP[2] = args[4]; // name.
|
|
SP[3] = args[3]; // type.
|
|
Exit(thread, FP, SP + 4, pc);
|
|
NativeArguments native_args(thread, 3, SP + 1, SP - 4);
|
|
INVOKE_RUNTIME(DRT_BadTypeError, native_args);
|
|
UNREACHABLE();
|
|
}
|
|
SP -= 4;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AssertAssignable, A_D);
|
|
// Stack: instance, instantiator type args, function type args, type, name
|
|
RawObject** args = SP - 4;
|
|
const bool may_be_smi = (rA == 1);
|
|
const bool is_smi =
|
|
((reinterpret_cast<intptr_t>(args[0]) & kSmiTagMask) == kSmiTag);
|
|
const bool smi_ok = is_smi && may_be_smi;
|
|
if (!smi_ok && (args[0] != null_value)) {
|
|
RawSubtypeTestCache* cache =
|
|
static_cast<RawSubtypeTestCache*>(LOAD_CONSTANT(rD));
|
|
|
|
if (!AssertAssignable(thread, pc, FP, SP, args, cache)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
}
|
|
|
|
SP -= 4; // Instance remains on stack.
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AssertSubtype, A);
|
|
RawObject** args = SP - 4;
|
|
|
|
// TODO(kustermann): Implement fast case for common arguments.
|
|
|
|
// The arguments on the stack look like:
|
|
// args[0] instantiator type args
|
|
// args[1] function type args
|
|
// args[2] sub_type
|
|
// args[3] super_type
|
|
// args[4] name
|
|
|
|
// This is unused, since the negative case throws an exception.
|
|
SP++;
|
|
RawObject** result_slot = SP;
|
|
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments native_args(thread, 5, args, result_slot);
|
|
INVOKE_RUNTIME(DRT_SubtypeCheck, native_args);
|
|
|
|
// Result slot not used anymore.
|
|
SP--;
|
|
|
|
// Drop all arguments.
|
|
SP -= 5;
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(AssertBoolean, A);
|
|
RawObject* value = SP[0];
|
|
if (rA) { // Should we perform type check?
|
|
if ((value == true_value) || (value == false_value)) {
|
|
goto AssertBooleanOk;
|
|
}
|
|
} else if (value != null_value) {
|
|
goto AssertBooleanOk;
|
|
}
|
|
|
|
// Assertion failed.
|
|
{
|
|
SP[1] = SP[0]; // instance
|
|
Exit(thread, FP, SP + 2, pc);
|
|
NativeArguments args(thread, 1, SP + 1, SP);
|
|
INVOKE_RUNTIME(DRT_NonBoolTypeError, args);
|
|
}
|
|
|
|
AssertBooleanOk:
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(TestSmi, A_D);
|
|
intptr_t left = reinterpret_cast<intptr_t>(RAW_CAST(Smi, FP[rA]));
|
|
intptr_t right = reinterpret_cast<intptr_t>(RAW_CAST(Smi, FP[rD]));
|
|
if ((left & right) != 0) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(TestCids, A_D);
|
|
const intptr_t cid = InterpreterHelpers::GetClassId(FP[rA]);
|
|
const intptr_t num_cases = rD;
|
|
for (intptr_t i = 0; i < num_cases; i++) {
|
|
ASSERT(KernelBytecode::DecodeOpcode(pc[i]) == KernelBytecode::kNop);
|
|
intptr_t test_target = KernelBytecode::DecodeA(pc[i]);
|
|
intptr_t test_cid = KernelBytecode::DecodeD(pc[i]);
|
|
if (cid == test_cid) {
|
|
if (test_target != 0) {
|
|
pc += 1; // Match true.
|
|
} else {
|
|
pc += 2; // Match false.
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
pc += num_cases;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckSmi, 0);
|
|
intptr_t obj = reinterpret_cast<intptr_t>(FP[rA]);
|
|
if ((obj & kSmiTagMask) == kSmiTag) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckEitherNonSmi, A_D);
|
|
const intptr_t obj1 = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const intptr_t obj2 = reinterpret_cast<intptr_t>(FP[rD]);
|
|
const intptr_t tag = (obj1 | obj2) & kSmiTagMask;
|
|
if (tag != kSmiTag) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckClassId, A_D);
|
|
const intptr_t actual_cid =
|
|
reinterpret_cast<intptr_t>(FP[rA]) >> kSmiTagSize;
|
|
const intptr_t desired_cid = rD;
|
|
pc += (actual_cid == desired_cid) ? 1 : 0;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckClassIdRange, A_D);
|
|
const intptr_t actual_cid =
|
|
reinterpret_cast<intptr_t>(FP[rA]) >> kSmiTagSize;
|
|
const uintptr_t cid_start = rD;
|
|
const uintptr_t cid_range = KernelBytecode::DecodeD(*pc);
|
|
// Unsigned comparison. Skip either just the nop or both the nop and the
|
|
// following instruction.
|
|
pc += (actual_cid - cid_start <= cid_range) ? 2 : 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckBitTest, A_D);
|
|
const intptr_t raw_value = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const bool is_smi = ((raw_value & kSmiTagMask) == kSmiTag);
|
|
const intptr_t cid_min = KernelBytecode::DecodeD(*pc);
|
|
const intptr_t cid_mask = Smi::Value(
|
|
RAW_CAST(Smi, LOAD_CONSTANT(KernelBytecode::DecodeD(*(pc + 1)))));
|
|
if (LIKELY(!is_smi)) {
|
|
const intptr_t cid_max = Utils::HighestBit(cid_mask) + cid_min;
|
|
const intptr_t cid = InterpreterHelpers::GetClassId(FP[rA]);
|
|
// The cid is in-bounds, and the bit is set in the mask.
|
|
if ((cid >= cid_min) && (cid <= cid_max) &&
|
|
((cid_mask & (1 << (cid - cid_min))) != 0)) {
|
|
pc += 3;
|
|
} else {
|
|
pc += 2;
|
|
}
|
|
} else {
|
|
const bool may_be_smi = (rD == 1);
|
|
pc += (may_be_smi ? 3 : 2);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckCids, A_B_C);
|
|
const intptr_t raw_value = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const bool is_smi = ((raw_value & kSmiTagMask) == kSmiTag);
|
|
const bool may_be_smi = (rB == 1);
|
|
const intptr_t cids_length = rC;
|
|
if (LIKELY(!is_smi)) {
|
|
const intptr_t cid = InterpreterHelpers::GetClassId(FP[rA]);
|
|
for (intptr_t i = 0; i < cids_length; i++) {
|
|
const intptr_t desired_cid = KernelBytecode::DecodeD(*(pc + i));
|
|
if (cid == desired_cid) {
|
|
pc++;
|
|
break;
|
|
}
|
|
}
|
|
pc += cids_length;
|
|
} else {
|
|
pc += cids_length;
|
|
pc += (may_be_smi ? 1 : 0);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(CheckCidsByRange, A_B_C);
|
|
const intptr_t raw_value = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const bool is_smi = ((raw_value & kSmiTagMask) == kSmiTag);
|
|
const bool may_be_smi = (rB == 1);
|
|
const intptr_t cids_length = rC;
|
|
if (LIKELY(!is_smi)) {
|
|
const intptr_t cid = InterpreterHelpers::GetClassId(FP[rA]);
|
|
for (intptr_t i = 0; i < cids_length; i += 2) {
|
|
// Note unsigned type to get unsigned range check below.
|
|
const uintptr_t cid_start = KernelBytecode::DecodeD(*(pc + i));
|
|
const uintptr_t cids = KernelBytecode::DecodeD(*(pc + i + 1));
|
|
if (cid - cid_start < cids) {
|
|
pc++;
|
|
break;
|
|
}
|
|
}
|
|
pc += cids_length;
|
|
} else {
|
|
pc += cids_length;
|
|
pc += (may_be_smi ? 1 : 0);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfEqStrictTOS, 0);
|
|
SP -= 2;
|
|
if (SP[1] != SP[2]) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfNeStrictTOS, 0);
|
|
SP -= 2;
|
|
if (SP[1] == SP[2]) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfEqStrictNumTOS, 0);
|
|
if (thread->isolate()->single_step()) {
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
|
}
|
|
|
|
SP -= 2;
|
|
if (!InterpreterHelpers::IsStrictEqualWithNumberCheck(SP[1], SP[2])) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfNeStrictNumTOS, 0);
|
|
if (thread->isolate()->single_step()) {
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_SingleStepHandler, args);
|
|
}
|
|
|
|
SP -= 2;
|
|
if (InterpreterHelpers::IsStrictEqualWithNumberCheck(SP[1], SP[2])) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfSmiLtTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
if (!(Smi::Value(left) < Smi::Value(right))) {
|
|
pc++;
|
|
}
|
|
SP -= 2;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfSmiLeTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
if (!(Smi::Value(left) <= Smi::Value(right))) {
|
|
pc++;
|
|
}
|
|
SP -= 2;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfSmiGeTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
if (!(Smi::Value(left) >= Smi::Value(right))) {
|
|
pc++;
|
|
}
|
|
SP -= 2;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfSmiGtTOS, 0);
|
|
RawSmi* left = Smi::RawCast(SP[-1]);
|
|
RawSmi* right = Smi::RawCast(SP[-0]);
|
|
if (!(Smi::Value(left) > Smi::Value(right))) {
|
|
pc++;
|
|
}
|
|
SP -= 2;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfEqStrict, A_D);
|
|
RawObject* lhs = FP[rA];
|
|
RawObject* rhs = FP[rD];
|
|
if (lhs != rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfNeStrict, A_D);
|
|
RawObject* lhs = FP[rA];
|
|
RawObject* rhs = FP[rD];
|
|
if (lhs == rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfLe, A_D);
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rD]);
|
|
if (lhs > rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfLt, A_D);
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rD]);
|
|
if (lhs >= rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfGe, A_D);
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rD]);
|
|
if (lhs < rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfGt, A_D);
|
|
const intptr_t lhs = reinterpret_cast<intptr_t>(FP[rA]);
|
|
const intptr_t rhs = reinterpret_cast<intptr_t>(FP[rD]);
|
|
if (lhs <= rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfULe, A_D);
|
|
const uintptr_t lhs = reinterpret_cast<uintptr_t>(FP[rA]);
|
|
const uintptr_t rhs = reinterpret_cast<uintptr_t>(FP[rD]);
|
|
if (lhs > rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfULt, A_D);
|
|
const uintptr_t lhs = reinterpret_cast<uintptr_t>(FP[rA]);
|
|
const uintptr_t rhs = reinterpret_cast<uintptr_t>(FP[rD]);
|
|
if (lhs >= rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfUGe, A_D);
|
|
const uintptr_t lhs = reinterpret_cast<uintptr_t>(FP[rA]);
|
|
const uintptr_t rhs = reinterpret_cast<uintptr_t>(FP[rD]);
|
|
if (lhs < rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfUGt, A_D);
|
|
const uintptr_t lhs = reinterpret_cast<uintptr_t>(FP[rA]);
|
|
const uintptr_t rhs = reinterpret_cast<uintptr_t>(FP[rD]);
|
|
if (lhs <= rhs) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
#if defined(ARCH_IS_64_BIT)
|
|
{
|
|
BYTECODE(IfDEq, A_D);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rA]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rD]);
|
|
pc += (lhs == rhs) ? 0 : 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDNe, A_D);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rA]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rD]);
|
|
pc += (lhs != rhs) ? 0 : 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDLe, A_D);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rA]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rD]);
|
|
pc += (lhs <= rhs) ? 0 : 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDLt, A_D);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rA]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rD]);
|
|
pc += (lhs < rhs) ? 0 : 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDGe, A_D);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rA]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rD]);
|
|
pc += (lhs >= rhs) ? 0 : 1;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDGt, A_D);
|
|
const double lhs = bit_cast<double, RawObject*>(FP[rA]);
|
|
const double rhs = bit_cast<double, RawObject*>(FP[rD]);
|
|
pc += (lhs > rhs) ? 0 : 1;
|
|
DISPATCH();
|
|
}
|
|
#else // defined(ARCH_IS_64_BIT)
|
|
{
|
|
BYTECODE(IfDEq, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDNe, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDLe, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDLt, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDGe, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfDGt, A_D);
|
|
UNREACHABLE();
|
|
DISPATCH();
|
|
}
|
|
#endif // defined(ARCH_IS_64_BIT)
|
|
|
|
{
|
|
BYTECODE(IfEqStrictNum, A_D);
|
|
RawObject* lhs = FP[rA];
|
|
RawObject* rhs = FP[rD];
|
|
if (!InterpreterHelpers::IsStrictEqualWithNumberCheck(lhs, rhs)) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfNeStrictNum, A_D);
|
|
RawObject* lhs = FP[rA];
|
|
RawObject* rhs = FP[rD];
|
|
if (InterpreterHelpers::IsStrictEqualWithNumberCheck(lhs, rhs)) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfEqNull, A);
|
|
if (FP[rA] != null_value) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(IfNeNull, A_D);
|
|
if (FP[rA] == null_value) {
|
|
pc++;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Jump, 0);
|
|
const int32_t target = static_cast<int32_t>(op) >> 8;
|
|
pc += (target - 1);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(JumpIfNoAsserts, 0);
|
|
if (!thread->isolate()->asserts()) {
|
|
const int32_t target = static_cast<int32_t>(op) >> 8;
|
|
pc += (target - 1);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(JumpIfNotZeroTypeArgs, 0);
|
|
if (InterpreterHelpers::ArgDescTypeArgsLen(argdesc_) != 0) {
|
|
const int32_t target = static_cast<int32_t>(op) >> 8;
|
|
pc += (target - 1);
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadClassId, A_D);
|
|
const uint16_t object_reg = rD;
|
|
RawObject* obj = static_cast<RawObject*>(FP[object_reg]);
|
|
FP[rA] = InterpreterHelpers::GetClassIdAsSmi(obj);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadClassIdTOS, 0);
|
|
RawObject* obj = static_cast<RawObject*>(SP[0]);
|
|
SP[0] = InterpreterHelpers::GetClassIdAsSmi(obj);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedTOS, 0);
|
|
SP -= 3;
|
|
RawArray* array = RAW_CAST(Array, SP[1]);
|
|
RawSmi* index = RAW_CAST(Smi, SP[2]);
|
|
RawObject* value = SP[3];
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
array->StorePointer(array->ptr()->data() + Smi::Value(index), value,
|
|
thread);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexed, A_B_C);
|
|
RawArray* array = RAW_CAST(Array, FP[rA]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
RawObject* value = FP[rC];
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
array->StorePointer(array->ptr()->data() + Smi::Value(index), value,
|
|
thread);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedUint8, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rA], FP[rB]);
|
|
*data = Smi::Value(RAW_CAST(Smi, FP[rC]));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedExternalUint8, A_B_C);
|
|
uint8_t* array = reinterpret_cast<uint8_t*>(FP[rA]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
RawSmi* value = RAW_CAST(Smi, FP[rC]);
|
|
array[Smi::Value(index)] = Smi::Value(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedOneByteString, A_B_C);
|
|
RawOneByteString* array = RAW_CAST(OneByteString, FP[rA]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
RawSmi* value = RAW_CAST(Smi, FP[rC]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
array->ptr()->data()[Smi::Value(index)] = Smi::Value(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreIndexedUint32, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rA], FP[rB]);
|
|
const uintptr_t value = reinterpret_cast<uintptr_t>(FP[rC]);
|
|
*reinterpret_cast<uint32_t*>(data) = static_cast<uint32_t>(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(TailCall, 0);
|
|
RawCode* code = RAW_CAST(Code, SP[-0]);
|
|
RawImmutableArray* args_desc = RAW_CAST(ImmutableArray, SP[-1]);
|
|
PrepareForTailCall(code, args_desc, FP, &SP, &pc);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(TailCallOpt, A_D);
|
|
RawImmutableArray* args_desc = RAW_CAST(ImmutableArray, FP[rA]);
|
|
RawCode* code = RAW_CAST(Code, FP[rD]);
|
|
PrepareForTailCall(code, args_desc, FP, &SP, &pc);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadArgDescriptor, 0);
|
|
SP++;
|
|
SP[0] = argdesc_;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadArgDescriptorOpt, A);
|
|
FP[rA] = argdesc_;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(NoSuchMethod, 0);
|
|
goto ClosureNoSuchMethod;
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadFpRelativeSlot, A_X);
|
|
RawSmi* index = RAW_CAST(Smi, SP[-0]);
|
|
const int16_t offset = rD;
|
|
SP[-0] = FP[-(Smi::Value(index) + offset)];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadFpRelativeSlotOpt, A_B_Y);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
const int8_t offset = rY;
|
|
FP[rA] = FP[-(Smi::Value(index) + offset)];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreFpRelativeSlot, A_X);
|
|
RawSmi* index = RAW_CAST(Smi, SP[-1]);
|
|
const int16_t offset = rD;
|
|
FP[-(Smi::Value(index) + offset) - 0] = SP[-0];
|
|
SP--;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(StoreFpRelativeSlotOpt, A_B_Y);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rB]);
|
|
const int8_t offset = rY;
|
|
FP[-(Smi::Value(index) + offset) - 0] = FP[rA];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedTOS, 0);
|
|
// Currently this instruction is only emitted if it's safe to do.
|
|
ASSERT(!SP[0]->IsHeapObject());
|
|
ASSERT(SP[-1]->IsArray() || SP[-1]->IsImmutableArray());
|
|
|
|
const intptr_t index_scale = rA;
|
|
RawSmi* index = RAW_CAST(Smi, SP[-0]);
|
|
RawArray* array = Array::RawCast(SP[-1]);
|
|
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
SP[-1] = array->ptr()->data()[Smi::Value(index) << index_scale];
|
|
SP--;
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexed, A_B_C);
|
|
RawObject* obj = FP[rB];
|
|
ASSERT(obj->IsArray() || obj->IsImmutableArray());
|
|
RawArray* array = reinterpret_cast<RawArray*>(obj);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
FP[rA] = array->ptr()->data()[Smi::Value(index)];
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedUint8, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rB], FP[rC]);
|
|
FP[rA] = Smi::New(*data);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedInt8, A_B_C);
|
|
uint8_t* data = InterpreterHelpers::GetTypedData(FP[rB], FP[rC]);
|
|
FP[rA] = Smi::New(*reinterpret_cast<int8_t*>(data));
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedUint32, A_B_C);
|
|
const uint8_t* data = InterpreterHelpers::GetTypedData(FP[rB], FP[rC]);
|
|
const uint32_t value = *reinterpret_cast<const uint32_t*>(data);
|
|
FP[rA] = reinterpret_cast<RawObject*>(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedInt32, A_B_C);
|
|
const uint8_t* data = InterpreterHelpers::GetTypedData(FP[rB], FP[rC]);
|
|
const int32_t value = *reinterpret_cast<const int32_t*>(data);
|
|
FP[rA] = reinterpret_cast<RawObject*>(value);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedExternalUint8, A_B_C);
|
|
uint8_t* data = reinterpret_cast<uint8_t*>(FP[rB]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
FP[rA] = Smi::New(data[Smi::Value(index)]);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedExternalInt8, A_B_C);
|
|
int8_t* data = reinterpret_cast<int8_t*>(FP[rB]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
FP[rA] = Smi::New(data[Smi::Value(index)]);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedOneByteString, A_B_C);
|
|
RawOneByteString* array = RAW_CAST(OneByteString, FP[rB]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
FP[rA] = Smi::New(array->ptr()->data()[Smi::Value(index)]);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(LoadIndexedTwoByteString, A_B_C);
|
|
RawTwoByteString* array = RAW_CAST(TwoByteString, FP[rB]);
|
|
RawSmi* index = RAW_CAST(Smi, FP[rC]);
|
|
ASSERT(InterpreterHelpers::CheckIndex(index, array->ptr()->length_));
|
|
FP[rA] = Smi::New(array->ptr()->data()[Smi::Value(index)]);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Deopt, A_D);
|
|
const bool is_lazy = rD == 0;
|
|
if (!Deoptimize(thread, &pc, &FP, &SP, is_lazy)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(DeoptRewind, 0);
|
|
pc = reinterpret_cast<uint32_t*>(thread->resume_pc());
|
|
if (!Deoptimize(thread, &pc, &FP, &SP, false /* eager */)) {
|
|
HANDLE_EXCEPTION;
|
|
}
|
|
{
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments args(thread, 0, NULL, NULL);
|
|
INVOKE_RUNTIME(DRT_RewindPostDeopt, args);
|
|
}
|
|
UNREACHABLE(); // DRT_RewindPostDeopt does not exit normally.
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Nop, 0);
|
|
DISPATCH();
|
|
}
|
|
|
|
{
|
|
BYTECODE(Trap, 0);
|
|
UNIMPLEMENTED();
|
|
DISPATCH();
|
|
}
|
|
|
|
// Helper used to handle noSuchMethod on closures.
|
|
{
|
|
ClosureNoSuchMethod:
|
|
#if defined(DEBUG)
|
|
function_h ^= FrameFunction(FP);
|
|
ASSERT(function_h.IsNull() || function_h.IsClosureFunction());
|
|
#endif
|
|
|
|
// Restore caller context as we are going to throw NoSuchMethod.
|
|
pc = SavedCallerPC(FP);
|
|
|
|
const bool has_dart_caller =
|
|
!IsEntryFrameMarker(reinterpret_cast<uword>(pc));
|
|
const intptr_t argc = has_dart_caller ? KernelBytecode::DecodeArgc(pc[-1])
|
|
: (reinterpret_cast<uword>(pc) >> 2);
|
|
const intptr_t type_args_len =
|
|
InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
|
|
const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
|
|
|
|
SP = FrameArguments(FP, 0);
|
|
RawObject** args = SP - argc;
|
|
FP = SavedCallerFP(FP);
|
|
if (has_dart_caller) {
|
|
pp_ = InterpreterHelpers::FrameCode(FP)->ptr()->object_pool_;
|
|
}
|
|
|
|
*++SP = null_value;
|
|
*++SP = args[receiver_idx]; // Closure object.
|
|
*++SP = argdesc_;
|
|
*++SP = null_value; // Array of arguments (will be filled).
|
|
|
|
// Allocate array of arguments.
|
|
{
|
|
SP[1] = Smi::New(argc); // length
|
|
SP[2] = null_value; // type
|
|
Exit(thread, FP, SP + 3, pc);
|
|
NativeArguments native_args(thread, 2, SP + 1, SP);
|
|
if (!InvokeRuntime(thread, this, DRT_AllocateArray, native_args)) {
|
|
HANDLE_EXCEPTION;
|
|
} else if (has_dart_caller) {
|
|
HANDLE_RETURN;
|
|
}
|
|
|
|
// Copy arguments into the newly allocated array.
|
|
RawArray* array = static_cast<RawArray*>(SP[0]);
|
|
ASSERT(array->GetClassId() == kArrayCid);
|
|
for (intptr_t i = 0; i < argc; i++) {
|
|
array->ptr()->data()[i] = args[i];
|
|
}
|
|
}
|
|
|
|
// Invoke noSuchMethod passing down closure, argument descriptor and
|
|
// array of arguments.
|
|
{
|
|
Exit(thread, FP, SP + 1, pc);
|
|
NativeArguments native_args(thread, 3, SP - 2, SP - 3);
|
|
INVOKE_RUNTIME(DRT_InvokeClosureNoSuchMethod, native_args);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
DISPATCH();
|
|
}
|
|
|
|
// Single dispatch point used by exception handling macros.
|
|
{
|
|
DispatchAfterException:
|
|
pp_ = InterpreterHelpers::FrameCode(FP)->ptr()->object_pool_;
|
|
DISPATCH();
|
|
}
|
|
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
void Interpreter::JumpToFrame(uword pc, uword sp, uword fp, Thread* thread) {
|
|
// Walk over all setjmp buffers (simulated --> C++ transitions)
|
|
// and try to find the setjmp associated with the simulated frame pointer.
|
|
InterpreterSetjmpBuffer* buf = last_setjmp_buffer();
|
|
while ((buf->link() != NULL) && (buf->link()->fp() > fp)) {
|
|
buf = buf->link();
|
|
}
|
|
ASSERT(buf != NULL);
|
|
ASSERT(last_setjmp_buffer() == buf);
|
|
|
|
// The C++ caller has not cleaned up the stack memory of C++ frames.
|
|
// Prepare for unwinding frames by destroying all the stack resources
|
|
// in the previous C++ frames.
|
|
StackResource::Unwind(thread);
|
|
|
|
// Set the tag.
|
|
thread->set_vm_tag(VMTag::kDartTagId);
|
|
// Clear top exit frame.
|
|
thread->set_top_exit_frame_info(0);
|
|
|
|
fp_ = reinterpret_cast<RawObject**>(fp);
|
|
|
|
if (pc == StubCode::RunExceptionHandler_entry()->EntryPoint()) {
|
|
// The RunExceptionHandler stub is a placeholder. We implement
|
|
// its behavior here.
|
|
RawObject* raw_exception = thread->active_exception();
|
|
RawObject* raw_stacktrace = thread->active_stacktrace();
|
|
ASSERT(raw_exception != Object::null());
|
|
thread->set_active_exception(Object::null_object());
|
|
thread->set_active_stacktrace(Object::null_object());
|
|
special_[KernelBytecode::kExceptionSpecialIndex] = raw_exception;
|
|
special_[KernelBytecode::kStackTraceSpecialIndex] = raw_stacktrace;
|
|
pc_ = thread->resume_pc();
|
|
} else {
|
|
pc_ = pc;
|
|
}
|
|
|
|
buf->Longjmp();
|
|
UNREACHABLE();
|
|
}
|
|
|
|
void Interpreter::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&pp_));
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&argdesc_));
|
|
}
|
|
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_OS_WINDOWS)
|