// Copyright (c) 2016, 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 // NOLINT #include #include "vm/globals.h" #if defined(TARGET_ARCH_DBC) #if !defined(USING_SIMULATOR) #error "DBC is a simulated architecture" #endif #include "vm/simulator.h" #include "lib/ffi.h" #include "platform/assert.h" #include "vm/compiler/assembler/assembler.h" #include "vm/compiler/assembler/disassembler.h" #include "vm/compiler/backend/il.h" #include "vm/compiler/backend/locations.h" #include "vm/compiler/ffi.h" #include "vm/compiler/ffi_dbc_trampoline.h" #include "vm/compiler/jit/compiler.h" #include "vm/constants_dbc.h" #include "vm/cpu.h" #include "vm/dart_entry.h" #include "vm/debugger.h" #include "vm/heap/safepoint.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.h" #include "vm/symbols.h" namespace dart { DEFINE_FLAG(uint64_t, trace_sim_after, ULLONG_MAX, "Trace simulator execution after instruction count reached."); DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX, "Instruction address or instruction count to stop simulator at."); // SimulatorSetjmpBuffer are linked together, and the last created one // is referenced by the Simulator. When an exception is thrown, the exception // runtime looks at where to jump and finds the corresponding // SimulatorSetjmpBuffer based on the stack pointer of the exception handler. // The runtime then does a Longjmp on that buffer to return to the simulator. class SimulatorSetjmpBuffer { public: void Longjmp() { // "This" is now the last setjmp buffer. simulator_->set_last_setjmp_buffer(this); longjmp(buffer_, 1); } explicit SimulatorSetjmpBuffer(Simulator* sim) { simulator_ = sim; link_ = sim->last_setjmp_buffer(); sim->set_last_setjmp_buffer(this); fp_ = sim->fp_; } ~SimulatorSetjmpBuffer() { ASSERT(simulator_->last_setjmp_buffer() == this); simulator_->set_last_setjmp_buffer(link_); } SimulatorSetjmpBuffer* link() const { return link_; } uword fp() const { return reinterpret_cast(fp_); } jmp_buf buffer_; private: RawObject** fp_; Simulator* simulator_; SimulatorSetjmpBuffer* link_; friend class Simulator; DISALLOW_ALLOCATION(); DISALLOW_COPY_AND_ASSIGN(SimulatorSetjmpBuffer); }; DART_FORCE_INLINE static RawObject** SavedCallerFP(RawObject** FP) { return reinterpret_cast(FP[kSavedCallerFpSlotFromFp]); } DART_FORCE_INLINE static RawObject** FrameArguments(RawObject** FP, intptr_t argc) { return FP - (kDartFrameFixedSize + argc); } #define RAW_CAST(Type, val) (SimulatorHelpers::CastTo##Type(val)) class SimulatorHelpers { 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(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(kSmiCid)); } DART_FORCE_INLINE static intptr_t GetClassId(RawObject* obj) { return obj->IsHeapObject() ? obj->GetClassId() : static_cast(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(entries[offset + count_offset]); const intptr_t raw_smi_new = raw_smi_old + Smi::RawValue(1); *reinterpret_cast(&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( static_cast(lhs)->ptr()->value_) == bit_cast( static_cast(rhs)->ptr()->value_)); case kMintCid: return (static_cast(lhs)->ptr()->value_ == static_cast(rhs)->ptr()->value_); } } } return false; } template 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(index) >= 0) && (reinterpret_cast(index) < reinterpret_cast(length)); } DART_FORCE_INLINE static intptr_t ArgDescTypeArgsLen(RawArray* argdesc) { return Smi::Value(*reinterpret_cast( reinterpret_cast(argdesc->ptr()) + Array::element_offset(ArgumentsDescriptor::kTypeArgsLenIndex))); } DART_FORCE_INLINE static intptr_t ArgDescArgCount(RawArray* argdesc) { return Smi::Value(*reinterpret_cast( reinterpret_cast(argdesc->ptr()) + Array::element_offset(ArgumentsDescriptor::kCountIndex))); } DART_FORCE_INLINE static intptr_t ArgDescPosCount(RawArray* argdesc) { return Smi::Value(*reinterpret_cast( reinterpret_cast(argdesc->ptr()) + Array::element_offset(ArgumentsDescriptor::kPositionalCountIndex))); } static bool ObjectArraySetIndexed(Thread* thread, RawObject** FP, RawObject** result) { return ObjectArraySetIndexedUnchecked(thread, FP, result); } static bool ObjectArraySetIndexedUnchecked(Thread* thread, RawObject** FP, RawObject** result) { RawObject** args = FrameArguments(FP, 3); RawSmi* index = static_cast(args[1]); RawArray* array = static_cast(args[0]); if (CheckIndex(index, array->ptr()->length_)) { array->StoreArrayPointer(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(args[1]); RawArray* array = static_cast(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 GrowableArraySetIndexedUnchecked(thread, FP, result); } static bool GrowableArraySetIndexedUnchecked(Thread* thread, RawObject** FP, RawObject** result) { RawObject** args = FrameArguments(FP, 3); RawSmi* index = static_cast(args[1]); RawGrowableObjectArray* array = static_cast(args[0]); if (CheckIndex(index, array->ptr()->length_)) { RawArray* data = array->ptr()->data_; data->StoreArrayPointer(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(args[1]); RawGrowableObjectArray* array = static_cast(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(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(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()->declaration_type_; if (typ == Object::null()) { return false; } *result = static_cast(typ); return true; } static bool GetDoubleOperands(RawObject** args, double* d1, double* d2) { RawObject* obj2 = args[1]; if (!obj2->IsHeapObject()) { *d2 = static_cast(reinterpret_cast(obj2) >> kSmiTagSize); } else if (obj2->GetClassId() == kDoubleCid) { RawDouble* obj2d = static_cast(obj2); *d2 = obj2d->ptr()->value_; } else { return false; } RawDouble* obj1 = static_cast(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(start + Double::tags_offset()) = tags; *reinterpret_cast(start + Double::value_offset()) = value; return reinterpret_cast(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(args[0])); *result = Object::null(); return true; } DART_FORCE_INLINE static RawCode* FrameCode(RawObject** FP) { ASSERT(GetClassId(FP[kPcMarkerSlotFromFp]) == kCodeCid); return static_cast(FP[kPcMarkerSlotFromFp]); } DART_FORCE_INLINE static void SetFrameCode(RawObject** FP, RawCode* code) { ASSERT(GetClassId(code) == kCodeCid); FP[kPcMarkerSlotFromFp] = code; } DART_FORCE_INLINE static uint8_t* GetTypedData(RawObject* obj, RawObject* index) { ASSERT(RawObject::IsTypedDataClassId(obj->GetClassId())); RawTypedData* array = reinterpret_cast(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(FP[kSavedCallerPcSlotFromFp]); } DART_FORCE_INLINE static RawFunction* FrameFunction(RawObject** FP) { RawFunction* function = static_cast(FP[kFunctionSlotFromFp]); ASSERT(SimulatorHelpers::GetClassId(function) == kFunctionCid || SimulatorHelpers::GetClassId(function) == kNullCid); return function; } IntrinsicHandler Simulator::intrinsics_[Simulator::kIntrinsicCount]; // Synchronization primitives support. void Simulator::Init() { for (intptr_t i = 0; i < kIntrinsicCount; i++) { intrinsics_[i] = 0; } intrinsics_[kObjectArraySetIndexedIntrinsic] = SimulatorHelpers::ObjectArraySetIndexed; intrinsics_[kObjectArraySetIndexedUncheckedIntrinsic] = SimulatorHelpers::ObjectArraySetIndexedUnchecked; intrinsics_[kObjectArrayGetIndexedIntrinsic] = SimulatorHelpers::ObjectArrayGetIndexed; intrinsics_[kGrowableArraySetIndexedIntrinsic] = SimulatorHelpers::GrowableArraySetIndexed; intrinsics_[kGrowableArraySetIndexedUncheckedIntrinsic] = SimulatorHelpers::GrowableArraySetIndexedUnchecked; intrinsics_[kGrowableArrayGetIndexedIntrinsic] = SimulatorHelpers::GrowableArrayGetIndexed; intrinsics_[kObjectEqualsIntrinsic] = SimulatorHelpers::ObjectEquals; intrinsics_[kObjectRuntimeTypeIntrinsic] = SimulatorHelpers::ObjectRuntimeType; intrinsics_[kDouble_getIsNaNIntrinsic] = SimulatorHelpers::Double_getIsNan; intrinsics_[kDouble_getIsInfiniteIntrinsic] = SimulatorHelpers::Double_getIsInfinite; intrinsics_[kDouble_addIntrinsic] = SimulatorHelpers::Double_add; intrinsics_[kDouble_mulIntrinsic] = SimulatorHelpers::Double_mul; intrinsics_[kDouble_subIntrinsic] = SimulatorHelpers::Double_sub; intrinsics_[kDouble_divIntrinsic] = SimulatorHelpers::Double_div; intrinsics_[kDouble_greaterThanIntrinsic] = SimulatorHelpers::Double_greaterThan; intrinsics_[kDouble_greaterEqualThanIntrinsic] = SimulatorHelpers::Double_greaterEqualThan; intrinsics_[kDouble_lessThanIntrinsic] = SimulatorHelpers::Double_lessThan; intrinsics_[kDouble_equalIntrinsic] = SimulatorHelpers::Double_equal; intrinsics_[kDouble_lessEqualThanIntrinsic] = SimulatorHelpers::Double_lessEqualThan; intrinsics_[kClearAsyncThreadStackTraceIntrinsic] = SimulatorHelpers::ClearAsyncThreadStack; intrinsics_[kSetAsyncThreadStackTraceIntrinsic] = SimulatorHelpers::SetAsyncThreadStackTrace; } Simulator::Simulator() : stack_(NULL), fp_(NULL), pp_(NULL), argdesc_(NULL) { // Setup simulator 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::kStackSizeBufferMax + kSimulatorStackUnderflowSize) / sizeof(uintptr_t)]; // Low address. stack_base_ = reinterpret_cast(stack_) + kSimulatorStackUnderflowSize; // Limit for StackOverflowError. overflow_stack_limit_ = stack_base_ + OSThread::GetSpecifiedStackSize(); // High address. stack_limit_ = overflow_stack_limit_ + OSThread::kStackSizeBufferMax; last_setjmp_buffer_ = NULL; DEBUG_ONLY(icount_ = 0); } Simulator::~Simulator() { delete[] stack_; Isolate* isolate = Isolate::Current(); if (isolate != NULL) { isolate->set_simulator(NULL); } } // Get the active Simulator for the current isolate. Simulator* Simulator::Current() { Simulator* simulator = Isolate::Current()->simulator(); if (simulator == NULL) { simulator = new Simulator(); Isolate::Current()->set_simulator(simulator); } return simulator; } #if defined(DEBUG) // Returns true if tracing of executed instructions is enabled. DART_FORCE_INLINE bool Simulator::IsTracingExecution() const { return icount_ > FLAG_trace_sim_after; } // Prints bytecode instruction at given pc for instruction tracing. DART_NOINLINE void Simulator::TraceInstruction(uint32_t* pc) const { THR_Print("%" Pu64 " ", icount_); if (FLAG_support_disassembler) { Disassembler::Disassemble(reinterpret_cast(pc), reinterpret_cast(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 (*SimulatorRuntimeCall)(NativeArguments arguments); // Calls to leaf Dart runtime functions are based on this interface. typedef intptr_t (*SimulatorLeafRuntimeCall)(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 (*SimulatorLeafFloatRuntimeCall)(double d0, double d1); // Set up an exit frame for the garbage collector. void Simulator::Exit(Thread* thread, RawObject** base, RawObject** frame, uint32_t* pc) { frame[0] = Function::null(); frame[1] = Code::null(); frame[2] = reinterpret_cast(pc); frame[3] = reinterpret_cast(base); fp_ = frame + kDartFrameFixedSize; thread->set_top_exit_frame_info(reinterpret_cast(fp_)); } // 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(intptr_t lhs, intptr_t rhs, intptr_t* out) { intptr_t res = 1; #if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) 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_ARM) || defined(HOST_ARCH_ARM64) 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"); #else #error "Unsupported platform" #endif return (res != 0); } DART_FORCE_INLINE static bool SignedSubWithOverflow(intptr_t lhs, intptr_t rhs, intptr_t* out) { intptr_t res = 1; #if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) 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_ARM) || defined(HOST_ARCH_ARM64) 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"); #else #error "Unsupported platform" #endif return (res != 0); } DART_FORCE_INLINE static bool SignedMulWithOverflow(intptr_t lhs, intptr_t rhs, intptr_t* out) { intptr_t res = 1; #if defined(HOST_ARCH_IA32) || defined(HOST_ARCH_X64) 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_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( "mul %1, %2, %3\n" "smulh %2, %2, %3\n" "cmp %2, %1, ASR #63;\n" "bne 1f;\n" "mov %0, #0;\n" "str %1, [%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 Simulator::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(target)(native_args); } DART_FORCE_INLINE static void EnterSyntheticFrame(RawObject*** FP, RawObject*** SP, uint32_t* pc) { RawObject** fp = *SP + kDartFrameFixedSize; fp[kPcMarkerSlotFromFp] = 0; fp[kSavedCallerPcSlotFromFp] = reinterpret_cast(pc); fp[kSavedCallerFpSlotFromFp] = reinterpret_cast(*FP); *FP = fp; *SP = fp - 1; } DART_FORCE_INLINE static void LeaveSyntheticFrame(RawObject*** FP, RawObject*** SP) { RawObject** fp = *FP; *FP = reinterpret_cast(fp[kSavedCallerFpSlotFromFp]); *SP = fp - kDartFrameFixedSize; } DART_FORCE_INLINE void Simulator::Invoke(Thread* thread, RawObject** call_base, RawObject** call_top, uint32_t** pc, RawObject*** FP, RawObject*** SP) { RawObject** callee_fp = call_top + kDartFrameFixedSize; RawFunction* function = FrameFunction(callee_fp); RawCode* code = function->ptr()->code_; callee_fp[kPcMarkerSlotFromFp] = code; callee_fp[kSavedCallerPcSlotFromFp] = reinterpret_cast(*pc); callee_fp[kSavedCallerFpSlotFromFp] = reinterpret_cast(*FP); pp_ = code->ptr()->object_pool_; *pc = reinterpret_cast(code->ptr()->entry_point_); pc_ = reinterpret_cast(*pc); // For the profiler. *FP = callee_fp; *SP = *FP - 1; } void Simulator::InlineCacheMiss(int checked_args, Thread* thread, RawICData* icdata, RawObject** args, RawObject** top, uint32_t* pc, RawObject** FP, RawObject** SP) { RawObject** result = top; top[0] = 0; // Clean up result slot. // Save arguments descriptor as it may be clobbered by running Dart code // during the call to miss handler (class finalization). top[1] = argdesc_; RawObject** miss_handler_args = top + 2; 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(handler)); argdesc_ = Array::RawCast(top[1]); } DART_FORCE_INLINE void Simulator::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()->entries_->ptr(); const intptr_t type_args_len = SimulatorHelpers::ArgDescTypeArgsLen(icdata->ptr()->args_descriptor_); const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0; RawSmi* receiver_cid = SimulatorHelpers::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 + ICData::TargetIndexFor(kCheckedArgs)]; found = true; break; } } argdesc_ = icdata->ptr()->args_descriptor_; if (found) { if (!optimized) { SimulatorHelpers::IncrementICUsageCount(cache->data(), i, kCheckedArgs); } } else { InlineCacheMiss(kCheckedArgs, thread, icdata, call_base + receiver_idx, top, *pc, *FP, *SP); } Invoke(thread, call_base, top, pc, FP, SP); } DART_FORCE_INLINE void Simulator::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()->entries_->ptr(); const intptr_t type_args_len = SimulatorHelpers::ArgDescTypeArgsLen(icdata->ptr()->args_descriptor_); const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0; RawSmi* receiver_cid = SimulatorHelpers::GetClassIdAsSmi(args[receiver_idx]); RawSmi* arg0_cid = SimulatorHelpers::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 + ICData::TargetIndexFor(kCheckedArgs)]; found = true; break; } } argdesc_ = icdata->ptr()->args_descriptor_; if (found) { if (!optimized) { SimulatorHelpers::IncrementICUsageCount(cache->data(), i, kCheckedArgs); } } else { InlineCacheMiss(kCheckedArgs, thread, icdata, call_base + receiver_idx, top, *pc, *FP, *SP); } Invoke(thread, call_base, top, pc, FP, SP); } DART_FORCE_INLINE void Simulator::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[kFunctionSlotFromFp] = Object::null(); FP[kPcMarkerSlotFromFp] = code; *pc = reinterpret_cast(code->ptr()->entry_point_); pc_ = reinterpret_cast(pc); // For the profiler. pp_ = code->ptr()->object_pool_; argdesc_ = args_desc; } // 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. DART_NOINLINE static bool InvokeRuntime(Thread* thread, Simulator* sim, RuntimeFunction drt, const NativeArguments& args) { SimulatorSetjmpBuffer buffer(sim); if (!setjmp(buffer.buffer_)) { thread->set_vm_tag(reinterpret_cast(drt)); drt(args); thread->set_vm_tag(VMTag::kDartCompiledTagId); thread->set_top_exit_frame_info(0); return true; } else { return false; } } DART_NOINLINE static bool InvokeNative(Thread* thread, Simulator* sim, NativeFunctionWrapper wrapper, Dart_NativeFunction function, Dart_NativeArguments args) { SimulatorSetjmpBuffer buffer(sim); if (!setjmp(buffer.buffer_)) { thread->set_vm_tag(reinterpret_cast(function)); wrapper(args, function); thread->set_vm_tag(VMTag::kDartCompiledTagId); thread->set_top_exit_frame_info(0); return true; } else { return false; } } // Note: all macro helpers are intended to be used only inside Simulator::Call. // Counts and prints executed bytecode instructions (in DEBUG mode). #if defined(DEBUG) #define TRACE_INSTRUCTION \ icount_++; \ if (IsTracingExecution()) { \ TraceInstruction(pc - 1); \ } #else #define TRACE_INSTRUCTION #endif // defined(DEBUG) // Decode opcode and A part of the given value and dispatch to the // corresponding bytecode handler. #ifdef DART_HAS_COMPUTED_GOTO #define DISPATCH_OP(val) \ do { \ op = (val); \ rA = ((op >> 8) & 0xFF); \ TRACE_INSTRUCTION \ goto* dispatch[op & 0xFF]; \ } while (0) #else #define DISPATCH_OP(val) \ do { \ op = (val); \ rA = ((op >> 8) & 0xFF); \ TRACE_INSTRUCTION \ goto SwitchDispatch; \ } while (0) #endif // 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 >> SimulatorBytecode::kBShift) & SimulatorBytecode::kBMask); \ rC = ((op >> SimulatorBytecode::kCShift) & SimulatorBytecode::kCMask); #define DECLARE_A_B_Y \ uint16_t rB; \ int8_t rY; \ USE(rB); \ USE(rY) #define DECODE_A_B_Y \ rB = ((op >> SimulatorBytecode::kBShift) & SimulatorBytecode::kBMask); \ rY = ((op >> SimulatorBytecode::kYShift) & SimulatorBytecode::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 >> SimulatorBytecode::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(op) >> SimulatorBytecode::kDShift); #define SMI_FASTPATH_ICDATA_INC \ do { \ ASSERT(SimulatorBytecode::IsCallOpcode(*pc)); \ const uint16_t kidx = SimulatorBytecode::DecodeD(*pc); \ const RawICData* icdata = RAW_CAST(ICData, LOAD_CONSTANT(kidx)); \ RawObject** entries = icdata->ptr()->entries_->ptr()->data(); \ SimulatorHelpers::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(SP[-1]); \ const intptr_t rhs = reinterpret_cast(SP[-0]); \ ResultT* slot = reinterpret_cast(SP - 1); \ if (NOT_IN_PRODUCT(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(FP[rB]); \ const intptr_t rhs = reinterpret_cast(FP[rC]); \ ResultT* slot = reinterpret_cast(&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(FP[rB]); \ const intptr_t rhs = reinterpret_cast(FP[rC]); \ ResultT* slot = reinterpret_cast(&FP[rA]); \ Func(lhs, rhs, slot); \ } // Exception handling helper. Gets handler FP and PC from the Simulator where // they were stored by Simulator::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 Simulator::Call. #define HANDLE_EXCEPTION \ do { \ FP = reinterpret_cast(fp_); \ pc = reinterpret_cast(pc_); \ if ((reinterpret_cast(pc) & 2) != 0) { /* Entry frame? */ \ fp_ = reinterpret_cast(fp_[0]); \ thread->set_top_exit_frame_info(reinterpret_cast(fp_)); \ thread->set_top_resource(top_resource); \ thread->set_vm_tag(vm_tag); \ return special_[kExceptionSpecialIndex]; \ } \ pp_ = SimulatorHelpers::FrameCode(FP)->ptr()->object_pool_; \ goto DispatchAfterException; \ } while (0) #define HANDLE_RETURN \ do { \ pp_ = SimulatorHelpers::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 Simulator::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(*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(*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(*pc); // For the profiler. // Check if it is a fake PC marking the entry frame. ASSERT((reinterpret_cast(*pc) & 2) == 0); // 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_ = SimulatorHelpers::FrameCode(*FP)->ptr()->object_pool_; return true; } RawObject* Simulator::Call(const Code& code, const Array& arguments_descriptor, const Array& arguments, Thread* thread) { // Interpreter state (see constants_dbc.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. if (fp_ == NULL) { fp_ = reinterpret_cast(stack_base_); } // Save current VM tag and mark thread as executing Dart code. const uword vm_tag = thread->vm_tag(); thread->set_vm_tag(VMTag::kDartCompiledTagId); // 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_ > | | saved top_exit_frame_info // | arg 0 | -+ // ~~~~~~~~~~~~~~~ | // > incoming arguments // ~~~~~~~~~~~~~~~ | // | arg 1 | -+ // | function | -+ // | code | | // | caller PC | ---> special fake PC marking an entry frame // SP > | fp_ | | // FP > | ........... | > normal Dart frame (see stack_frame_dbc.h) // | // v // FP = fp_ + 1 + arguments.Length() + kDartFrameFixedSize; SP = FP - 1; // Save outer top_exit_frame_info. fp_[0] = reinterpret_cast(thread->top_exit_frame_info()); thread->set_top_exit_frame_info(0); // Copy arguments and setup the Dart frame. const intptr_t argc = arguments.Length(); for (intptr_t i = 0; i < argc; i++) { fp_[1 + i] = arguments.At(i); } FP[kFunctionSlotFromFp] = code.function(); FP[kPcMarkerSlotFromFp] = code.raw(); FP[kSavedCallerPcSlotFromFp] = reinterpret_cast((argc << 2) | 2); FP[kSavedCallerFpSlotFromFp] = reinterpret_cast(fp_); // Load argument descriptor. argdesc_ = arguments_descriptor.raw(); // Ready to start executing bytecode. Load entry point and corresponding // object pool. pc = reinterpret_cast(code.raw()->ptr()->entry_point_); pc_ = reinterpret_cast(pc); // For the profiler. pp_ = code.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 #ifdef DART_HAS_COMPUTED_GOTO static const void* dispatch[] = { #define TARGET(name, fmt, fmta, fmtb, fmtc) &&bc##name, BYTECODES_LIST(TARGET) #undef TARGET }; DISPATCH(); // Enter the dispatch loop. #else DISPATCH(); // Enter the dispatch loop. SwitchDispatch: switch (op & 0xFF) { #define TARGET(name, fmt, fmta, fmtb, fmtc) \ case SimulatorBytecode::k##name: \ goto bc##name; BYTECODES_LIST(TARGET) #undef TARGET default: FATAL1("Undefined opcode: %d\n", op); } #endif // Bytecode handlers (see constants_dbc.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(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; 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_; SimulatorHelpers::SetFrameCode(FP, code); pp_ = code->ptr()->object_pool_; pc = reinterpret_cast(code->ptr()->entry_point_); pc_ = reinterpret_cast(pc); // For the profiler. argdesc_ = static_cast(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_; // 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 Code& stub = StubCode::OptimizeFunction(); FP[kPcMarkerSlotFromFp] = stub.raw(); pc = reinterpret_cast(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(FP[1]); RawCode* code = function->ptr()->code_; SimulatorHelpers::SetFrameCode(FP, code); // Restore args descriptor which came in. argdesc_ = Array::RawCast(FP[2]); pp_ = code->ptr()->object_pool_; pc = reinterpret_cast(function->ptr()->entry_point_); pc_ = reinterpret_cast(pc); // For the profiler. } } DISPATCH(); } { BYTECODE(CheckStack, A); { if (reinterpret_cast(SP) >= thread->stack_limit()) { Exit(thread, FP, SP + 1, pc); NativeArguments args(thread, 0, NULL, NULL); INVOKE_RUNTIME(DRT_StackOverflow, 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 = SimulatorHelpers::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 = SimulatorHelpers::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); #ifdef PRODUCT FATAL("No debugging in PRODUCT mode"); #else if (thread->isolate()->single_step()) { Exit(thread, FP, SP + 1, pc); NativeArguments args(thread, 0, NULL, NULL); INVOKE_RUNTIME(DRT_SingleStepHandler, args); } #endif // !PRODUCT DISPATCH(); } { BYTECODE(DebugBreak, A); #ifdef PRODUCT FATAL("No debugging in PRODUCT mode"); #else { const uint32_t original_bc = static_cast(reinterpret_cast( thread->isolate()->debugger()->GetPatchedStubAddress( reinterpret_cast(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); } #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(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(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); #ifndef PRODUCT // 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); } #endif // !PRODUCT // 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()->entries_->ptr()->data(); SimulatorHelpers::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(LOAD_CONSTANT(rD)); Invoke(thread, call_base, call_top, &pc, &FP, &SP); } 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(LOAD_CONSTANT(rD)); Invoke(thread, call_base, call_top, &pc, &FP, &SP); DISPATCH(); } { BYTECODE(InstanceCall1, A_D); #ifndef PRODUCT // 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); } #endif // !PRODUCT { 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)); SimulatorHelpers::IncrementUsageCounter( RAW_CAST(Function, icdata->ptr()->owner_)); InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP, false /* optimized */); } DISPATCH(); } { BYTECODE(InstanceCall2, A_D); #ifndef PRODUCT if (thread->isolate()->single_step()) { Exit(thread, FP, SP + 1, pc); NativeArguments args(thread, 0, NULL, NULL); INVOKE_RUNTIME(DRT_SingleStepHandler, args); } #endif // !PRODUCT { 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)); SimulatorHelpers::IncrementUsageCounter( RAW_CAST(Function, icdata->ptr()->owner_)); InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP, false /* optimized */); } 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)); SimulatorHelpers::IncrementUsageCounter(FrameFunction(FP)); InstanceCall1(thread, icdata, call_base, call_top, &pc, &FP, &SP, true /* optimized */); } 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)); SimulatorHelpers::IncrementUsageCounter(FrameFunction(FP)); InstanceCall2(thread, icdata, call_base, call_top, &pc, &FP, &SP, true /* optimized */); } 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 = SimulatorHelpers::GetClassId(args[0]); for (intptr_t i = 0; i < 2 * cids_length; i += 2) { const intptr_t icdata_cid = SimulatorBytecode::DecodeD(*(pc + i)); if (receiver_cid == icdata_cid) { RawFunction* target = RAW_CAST( Function, LOAD_CONSTANT(SimulatorBytecode::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 = SimulatorHelpers::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 = SimulatorBytecode::DecodeD(*(pc + i)); const uintptr_t cids = SimulatorBytecode::DecodeD(*(pc + i + 1)); if (receiver_cid - cid_start < cids) { RawFunction* target = RAW_CAST( Function, LOAD_CONSTANT(SimulatorBytecode::DecodeD(*(pc + i + 2)))); *++SP = target; pc++; break; } } pc += 3 * cids_length; DISPATCH(); } { BYTECODE(NativeCall, A_B_C); NativeFunctionWrapper trampoline = reinterpret_cast(LOAD_CONSTANT(rA)); Dart_NativeFunction function = reinterpret_cast(LOAD_CONSTANT(rB)); intptr_t argc_tag = reinterpret_cast(LOAD_CONSTANT(rC)); 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, function, reinterpret_cast(&args)); *(SP - num_arguments) = *return_slot; SP -= num_arguments; DISPATCH(); } { BYTECODE(FfiCall, __D); { const auto& sig_desc = compiler::ffi::FfiSignatureDescriptor( TypedData::Cast(Object::Handle(LOAD_CONSTANT(rD)))); // The arguments to this ffi call are on the stack at FP. // kFirstLocalSlotFromFp is 0 in DBC, but the const is -1. uint64_t* arg_values = reinterpret_cast(FP); uint64_t* marshalled_args_data = FfiMarshalledArguments::New(sig_desc, arg_values); const auto& marshalled_args = FfiMarshalledArguments(marshalled_args_data); Exit(thread, FP, SP, pc); { TransitionGeneratedToNative transition(thread); FfiTrampolineCall(marshalled_args_data); } const Representation result_rep = sig_desc.ResultRepresentation(); intptr_t result; if (result_rep == kUnboxedDouble || result_rep == kUnboxedFloat) { result = marshalled_args.DoubleResult(); } else { result = marshalled_args.IntResult(); } FP[0] = reinterpret_cast(result); } 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(FP[rD]); intptr_t* out = reinterpret_cast(&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(FP[rD]); *reinterpret_cast(&FP[rA]) = ~value & (~kSmiTagMask); DISPATCH(); } { BYTECODE(Div, A_B_C); const intptr_t rhs = reinterpret_cast(FP[rC]); if (rhs != 0) { const intptr_t lhs = reinterpret_cast(FP[rB]); const intptr_t res = (lhs >> kSmiTagSize) / (rhs >> kSmiTagSize); #if defined(ARCH_IS_64_BIT) const intptr_t untaggable = 0x4000000000000000LL; #else const intptr_t untaggable = 0x40000000L; #endif // defined(ARCH_IS_64_BIT) if (res != untaggable) { *reinterpret_cast(&FP[rA]) = res << kSmiTagSize; pc++; } } DISPATCH(); } { BYTECODE(Mod, A_B_C); const intptr_t rhs = reinterpret_cast(FP[rC]); if (rhs != 0) { const intptr_t lhs = reinterpret_cast(FP[rB]); const intptr_t res = ((lhs >> kSmiTagSize) % (rhs >> kSmiTagSize)) << kSmiTagSize; *reinterpret_cast(&FP[rA]) = (res < 0) ? ((rhs < 0) ? (res - rhs) : (res + rhs)) : res; pc++; } DISPATCH(); } { BYTECODE(Shl, A_B_C); const intptr_t rhs = reinterpret_cast(FP[rC]) >> kSmiTagSize; if (static_cast(rhs) < kBitsPerWord) { const intptr_t lhs = reinterpret_cast(FP[rB]); const intptr_t res = lhs << rhs; if (lhs == (res >> rhs)) { *reinterpret_cast(&FP[rA]) = res; pc++; } } DISPATCH(); } { BYTECODE(Shr, A_B_C); const intptr_t rhs = reinterpret_cast(FP[rC]) >> kSmiTagSize; if (rhs >= 0) { const intptr_t shift_amount = (rhs >= kBitsPerWord) ? (kBitsPerWord - 1) : rhs; const intptr_t lhs = reinterpret_cast(FP[rB]) >> kSmiTagSize; *reinterpret_cast(&FP[rA]) = (lhs >> shift_amount) << kSmiTagSize; pc++; } DISPATCH(); } { BYTECODE(ShlImm, A_B_C); const uint8_t shift = rC; const intptr_t lhs = reinterpret_cast(FP[rB]); FP[rA] = reinterpret_cast(lhs << shift); DISPATCH(); } { BYTECODE(Min, A_B_C); const intptr_t lhs = reinterpret_cast(FP[rB]); const intptr_t rhs = reinterpret_cast(FP[rC]); FP[rA] = reinterpret_cast((lhs < rhs) ? lhs : rhs); DISPATCH(); } { BYTECODE(Max, A_B_C); const intptr_t lhs = reinterpret_cast(FP[rB]); const intptr_t rhs = reinterpret_cast(FP[rC]); FP[rA] = reinterpret_cast((lhs > rhs) ? lhs : rhs); DISPATCH(); } { BYTECODE(UnboxInt32, A_B_C); const intptr_t box_cid = SimulatorHelpers::GetClassId(FP[rB]); const bool may_truncate = rC == 1; if (box_cid == kSmiCid) { const intptr_t value = reinterpret_cast(FP[rB]) >> kSmiTagSize; const int32_t value32 = static_cast(value); if (may_truncate || (value == static_cast(value32))) { FP[rA] = reinterpret_cast(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(value); if (may_truncate || (value == static_cast(value32))) { FP[rA] = reinterpret_cast(value); pc++; } } DISPATCH(); } { BYTECODE(UnboxedWidthExtender, A_B_C); auto rep = static_cast(rC); const intptr_t value_32_or_64_bit = reinterpret_cast(FP[rB]); int32_t value = value_32_or_64_bit & kMaxUint32; // Prevent overflow. switch (rep) { case kSmallUnboxedInt8: // Sign extend the top 24 bits from the sign bit. value <<= 24; value >>= 24; break; case kSmallUnboxedUint8: value &= 0x000000FF; // Throw away upper bits. break; case kSmallUnboxedInt16: // Sign extend the top 16 bits from the sign bit. value <<= 16; value >>= 16; break; case kSmallUnboxedUint16: value &= 0x0000FFFF; // Throw away upper bits. break; default: UNREACHABLE(); } FP[rA] = reinterpret_cast(value); DISPATCH(); } #if defined(ARCH_IS_64_BIT) { BYTECODE(WriteIntoDouble, A_D); const double value = bit_cast(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(box->ptr()->value_); DISPATCH(); } { BYTECODE(CheckedUnboxDouble, A_D); const intptr_t box_cid = SimulatorHelpers::GetClassId(FP[rD]); if (box_cid == kSmiCid) { const intptr_t value = reinterpret_cast(FP[rD]) >> kSmiTagSize; const double result = static_cast(value); FP[rA] = bit_cast(result); pc++; } else if (box_cid == kDoubleCid) { const RawDouble* box = RAW_CAST(Double, FP[rD]); FP[rA] = bit_cast(box->ptr()->value_); pc++; } DISPATCH(); } { BYTECODE(DoubleToSmi, A_D); const double value = bit_cast(FP[rD]); if (!isnan(value)) { const intptr_t result = static_cast(value); if ((result <= Smi::kMaxValue) && (result >= Smi::kMinValue)) { FP[rA] = reinterpret_cast(result << kSmiTagSize); pc++; } } DISPATCH(); } { BYTECODE(SmiToDouble, A_D); const intptr_t value = reinterpret_cast(FP[rD]) >> kSmiTagSize; const double result = static_cast(value); FP[rA] = bit_cast(result); DISPATCH(); } { BYTECODE(DAdd, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); FP[rA] = bit_cast(lhs + rhs); DISPATCH(); } { BYTECODE(DSub, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); FP[rA] = bit_cast(lhs - rhs); DISPATCH(); } { BYTECODE(DMul, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); FP[rA] = bit_cast(lhs * rhs); DISPATCH(); } { BYTECODE(DDiv, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); const double result = lhs / rhs; FP[rA] = bit_cast(result); DISPATCH(); } { BYTECODE(DNeg, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(-value); DISPATCH(); } { BYTECODE(DSqrt, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(sqrt(value)); DISPATCH(); } { BYTECODE(DSin, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(sin(value)); DISPATCH(); } { BYTECODE(DCos, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(cos(value)); DISPATCH(); } { BYTECODE(DPow, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); const double result = pow(lhs, rhs); FP[rA] = bit_cast(result); DISPATCH(); } { BYTECODE(DMod, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); const double result = DartModulo(lhs, rhs); FP[rA] = bit_cast(result); DISPATCH(); } { BYTECODE(DMin, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); FP[rA] = bit_cast(fmin(lhs, rhs)); DISPATCH(); } { BYTECODE(DMax, A_B_C); const double lhs = bit_cast(FP[rB]); const double rhs = bit_cast(FP[rC]); FP[rA] = bit_cast(fmax(lhs, rhs)); DISPATCH(); } { BYTECODE(DTruncate, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(trunc(value)); DISPATCH(); } { BYTECODE(DFloor, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(floor(value)); DISPATCH(); } { BYTECODE(DCeil, A_D); const double value = bit_cast(FP[rD]); FP[rA] = bit_cast(ceil(value)); DISPATCH(); } { BYTECODE(DoubleToFloat, A_D); const double value = bit_cast(FP[rD]); const float valuef = static_cast(value); *reinterpret_cast(&FP[rA]) = valuef; DISPATCH(); } { BYTECODE(FloatToDouble, A_D); const float valuef = *reinterpret_cast(&FP[rD]); const double value = static_cast(valuef); FP[rA] = bit_cast(value); DISPATCH(); } { BYTECODE(DoubleIsNaN, A); const double v = bit_cast(FP[rA]); if (!isnan(v)) { pc++; } DISPATCH(); } { BYTECODE(DoubleIsInfinite, A); const double v = bit_cast(FP[rA]); if (!isinf(v)) { pc++; } DISPATCH(); } { BYTECODE(LoadIndexedFloat32, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rB], FP[rC]); const uint32_t value = *reinterpret_cast(data); const uint64_t value64 = value; FP[rA] = reinterpret_cast(value64); DISPATCH(); } { BYTECODE(LoadIndexed4Float32, A_B_C); ASSERT(RawObject::IsTypedDataClassId(FP[rB]->GetClassId())); RawTypedData* array = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); const uint32_t value = reinterpret_cast(array->ptr()->data())[Smi::Value(index)]; const uint64_t value64 = value; // sign extend to clear high bits. FP[rA] = reinterpret_cast(value64); DISPATCH(); } { BYTECODE(LoadIndexedFloat64, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rB], FP[rC]); *reinterpret_cast(&FP[rA]) = *reinterpret_cast(data); DISPATCH(); } { BYTECODE(LoadIndexed8Float64, A_B_C); ASSERT(RawObject::IsTypedDataClassId(FP[rB]->GetClassId())); RawTypedData* array = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); const int64_t value = reinterpret_cast(array->ptr()->data())[Smi::Value(index)]; FP[rA] = reinterpret_cast(value); DISPATCH(); } { BYTECODE(StoreIndexedFloat32, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rA], FP[rB]); const float value = *reinterpret_cast(&FP[rC]); *reinterpret_cast(data) = value; DISPATCH(); } { BYTECODE(StoreIndexed4Float32, A_B_C); ASSERT(RawObject::IsTypedDataClassId(FP[rA]->GetClassId())); RawTypedData* array = reinterpret_cast(FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); const float value = *reinterpret_cast(&FP[rC]); reinterpret_cast(array->ptr()->data())[Smi::Value(index)] = value; DISPATCH(); } { BYTECODE(StoreIndexedFloat64, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rA], FP[rB]); *reinterpret_cast(data) = reinterpret_cast(FP[rC]); DISPATCH(); } { BYTECODE(StoreIndexed8Float64, A_B_C); ASSERT(RawObject::IsTypedDataClassId(FP[rA]->GetClassId())); RawTypedData* array = reinterpret_cast(FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); const int64_t value = reinterpret_cast(FP[rC]); reinterpret_cast(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(FP[rD]); const int32_t value32 = static_cast(value); FP[rA] = Smi::New(static_cast(value32)); DISPATCH(); } { BYTECODE(BoxUint32, A_D); // Casts to zero out high 32 bits. const uintptr_t value = reinterpret_cast(FP[rD]); const uint32_t value32 = static_cast(value); FP[rA] = Smi::New(static_cast(value32)); DISPATCH(); } { BYTECODE(UnboxInt64, A_D); const intptr_t box_cid = SimulatorHelpers::GetClassId(FP[rD]); if (box_cid == kSmiCid) { const int64_t value = Smi::Value(RAW_CAST(Smi, FP[rD])); FP[rA] = reinterpret_cast(value); } else if (box_cid == kMintCid) { RawMint* mint = RAW_CAST(Mint, FP[rD]); const int64_t value = mint->ptr()->value_; FP[rA] = reinterpret_cast(value); } DISPATCH(); } { BYTECODE(BoxInt64, A_D); const int64_t value = reinterpret_cast(FP[rD]); if (Smi::IsValid(value)) { FP[rA] = Smi::New(static_cast(value)); } else { // If the value does not fit into a Smi the following instruction is // skipped. (The following instruction should be a jump to a label after // the slow path allocating a Mint box and writing into the Mint box.) pc++; } DISPATCH(); } { BYTECODE(WriteIntoMint, A_D); const int64_t value = bit_cast(FP[rD]); RawMint* box = RAW_CAST(Mint, FP[rA]); box->ptr()->value_ = value; 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(); } { BYTECODE(UnboxInt64, A_D); UNREACHABLE(); DISPATCH(); } { BYTECODE(BoxInt64, A_D); UNREACHABLE(); DISPATCH(); } { BYTECODE(WriteIntoMint, A_D); UNREACHABLE(); DISPATCH(); } #endif // defined(ARCH_IS_64_BIT) // Return and return like instructions (Instrinsic). { 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(pc); // For the profiler. // Check if it is a fake PC marking the entry frame. if ((reinterpret_cast(pc) & 2) != 0) { const intptr_t argc = reinterpret_cast(pc) >> 2; fp_ = reinterpret_cast(FrameArguments(FP, argc + 1)[0]); thread->set_top_exit_frame_info(reinterpret_cast(fp_)); thread->set_top_resource(top_resource); thread->set_vm_tag(vm_tag); return result; } // Look at the caller to determine how many arguments to pop. const uint8_t argc = SimulatorBytecode::DecodeArgc(pc[-1]); // Restore SP, FP and PP. Push result and dispatch. SP = FrameArguments(FP, argc); FP = SavedCallerFP(FP); pp_ = SimulatorHelpers::FrameCode(FP)->ptr()->object_pool_; *SP = result; DISPATCH(); } { BYTECODE(StoreStaticTOS, A_D); RawField* field = reinterpret_cast(LOAD_CONSTANT(rD)); RawInstance* value = static_cast(*SP--); field->StorePointer(&field->ptr()->value_.static_value_, value, thread); DISPATCH(); } { BYTECODE(PushStatic, A_D); RawField* field = reinterpret_cast(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(FP[rA]); RawObject* value = FP[value_reg]; instance->StorePointer( reinterpret_cast(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 = SimulatorBytecode::DecodeD(*pc++); RawInstance* instance = reinterpret_cast(FP[rA]); RawObject* value = FP[rD]; instance->StorePointer( reinterpret_cast(instance->ptr()) + offset_in_words, value, thread); DISPATCH(); } { BYTECODE(StoreUntagged, A_B_C); const uint16_t offset_in_words = rB; const uint16_t value_reg = rC; RawInstance* instance = reinterpret_cast(FP[rA]); word value = reinterpret_cast(FP[value_reg]); reinterpret_cast(instance->ptr())[offset_in_words] = value; DISPATCH(); } { BYTECODE(StoreFieldTOS, __D); const uint16_t offset_in_words = rD; RawInstance* instance = reinterpret_cast(SP[-1]); RawObject* value = reinterpret_cast(SP[0]); SP -= 2; // Drop instance and value. instance->StorePointer( reinterpret_cast(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(FP[instance_reg]); FP[rA] = reinterpret_cast(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 = SimulatorBytecode::DecodeD(*pc++); const uint16_t instance_reg = rD; RawInstance* instance = reinterpret_cast(FP[instance_reg]); FP[rA] = reinterpret_cast(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(FP[instance_reg]); FP[rA] = reinterpret_cast(instance)[offset_in_words]; DISPATCH(); } { BYTECODE(LoadFieldTOS, __D); const uint16_t offset_in_words = rD; RawInstance* instance = static_cast(SP[0]); SP[0] = reinterpret_cast(instance->ptr())[offset_in_words]; DISPATCH(); } { BYTECODE(InitStaticTOS, 0); RawField* field = static_cast(*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(start + Array::tags_offset()) = tags; *reinterpret_cast(start + Context::num_variables_offset()) = num_context_variables; FP[rA] = reinterpret_cast(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(start + Instance::tags_offset()) = tags; for (intptr_t current_offset = sizeof(RawInstance); current_offset < instance_size; current_offset += kWordSize) { *reinterpret_cast(start + current_offset) = null_value; } FP[rA] = reinterpret_cast(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 = SimulatorBytecode::DecodeD(*pc); // Writes both the tags and the initial identity hash on 64 bit platforms. tags = RawObject::NewBit::update(true, tags); *reinterpret_cast(start + Instance::tags_offset()) = tags; for (intptr_t current_offset = sizeof(RawInstance); current_offset < instance_size; current_offset += kWordSize) { *reinterpret_cast(start + current_offset) = null_value; } *reinterpret_cast(start + type_args_offset) = type_args; FP[rA] = reinterpret_cast(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(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(start + Instance::tags_offset()) = tags; *reinterpret_cast(start + Array::length_offset()) = FP[rB]; *reinterpret_cast( start + Array::type_arguments_offset()) = FP[rC]; RawObject** data = reinterpret_cast(start + Array::data_offset()); for (intptr_t i = 0; i < length; i++) { data[i] = null_value; } FP[rA] = reinterpret_cast(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(SP[-4]); RawTypeArguments* instantiator_type_arguments = static_cast(SP[-3]); RawTypeArguments* function_type_arguments = static_cast(SP[-2]); RawAbstractType* type = static_cast(SP[-1]); RawSubtypeTestCache* cache = static_cast(SP[0]); if (cache != null_value) { const intptr_t cid = SimulatorHelpers::GetClassId(instance); RawTypeArguments* instance_type_arguments = static_cast(null_value); RawObject* instance_cid_or_function; RawTypeArguments* parent_function_type_arguments; RawTypeArguments* delayed_function_type_arguments; if (cid == kClosureCid) { RawClosure* closure = static_cast(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( instance->ptr())[instance_class->ptr() ->type_arguments_field_offset_in_words_]; } parent_function_type_arguments = static_cast(null_value); delayed_function_type_arguments = static_cast(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(NullError, 0); Exit(thread, FP, SP, pc); NativeArguments native_args(thread, 0, SP, SP); INVOKE_RUNTIME(DRT_NullError, native_args); UNREACHABLE(); } { 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(args[0]) & kSmiTagMask) == kSmiTag); const bool smi_ok = is_smi && may_be_smi; if (!smi_ok && (args[0] != null_value)) { RawSubtypeTestCache* cache = static_cast(LOAD_CONSTANT(rD)); if (cache != null_value) { RawInstance* instance = static_cast(args[0]); RawTypeArguments* instantiator_type_arguments = static_cast(args[1]); RawTypeArguments* function_type_arguments = static_cast(args[2]); const intptr_t cid = SimulatorHelpers::GetClassId(instance); RawTypeArguments* instance_type_arguments = static_cast(null_value); RawObject* instance_cid_or_function; RawTypeArguments* parent_function_type_arguments; RawTypeArguments* delayed_function_type_arguments; if (cid == kClosureCid) { RawClosure* closure = static_cast(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( instance->ptr())[instance_class->ptr() ->type_arguments_field_offset_in_words_]; } parent_function_type_arguments = static_cast(null_value); delayed_function_type_arguments = static_cast(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 (true_value == entries[SubtypeTestCache::kTestResult]) { goto AssertAssignableOk; } else { break; } } } } AssertAssignableCallRuntime: SP[1] = args[0]; // instance SP[2] = args[3]; // type SP[3] = args[1]; // instantiator type args SP[4] = args[2]; // function type args SP[5] = args[4]; // name SP[6] = cache; SP[7] = Smi::New(kTypeCheckFromInline); Exit(thread, FP, SP + 8, pc); NativeArguments native_args(thread, 7, SP + 1, SP - 4); INVOKE_RUNTIME(DRT_TypeCheck, native_args); } AssertAssignableOk: SP -= 4; 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(RAW_CAST(Smi, FP[rA])); intptr_t right = reinterpret_cast(RAW_CAST(Smi, FP[rD])); if ((left & right) != 0) { pc++; } DISPATCH(); } { BYTECODE(TestCids, A_D); const intptr_t cid = SimulatorHelpers::GetClassId(FP[rA]); const intptr_t num_cases = rD; for (intptr_t i = 0; i < num_cases; i++) { ASSERT(SimulatorBytecode::DecodeOpcode(pc[i]) == SimulatorBytecode::kNop); intptr_t test_target = SimulatorBytecode::DecodeA(pc[i]); intptr_t test_cid = SimulatorBytecode::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(FP[rA]); if ((obj & kSmiTagMask) == kSmiTag) { pc++; } DISPATCH(); } { BYTECODE(CheckEitherNonSmi, A_D); const intptr_t obj1 = reinterpret_cast(FP[rA]); const intptr_t obj2 = reinterpret_cast(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(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(FP[rA]) >> kSmiTagSize; const uintptr_t cid_start = rD; const uintptr_t cid_range = SimulatorBytecode::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(FP[rA]); const bool is_smi = ((raw_value & kSmiTagMask) == kSmiTag); const intptr_t cid_min = SimulatorBytecode::DecodeD(*pc); const intptr_t cid_mask = Smi::Value( RAW_CAST(Smi, LOAD_CONSTANT(SimulatorBytecode::DecodeD(*(pc + 1))))); if (LIKELY(!is_smi)) { const intptr_t cid_max = Utils::HighestBit(cid_mask) + cid_min; const intptr_t cid = SimulatorHelpers::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(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 = SimulatorHelpers::GetClassId(FP[rA]); for (intptr_t i = 0; i < cids_length; i++) { const intptr_t desired_cid = SimulatorBytecode::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(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 = SimulatorHelpers::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 = SimulatorBytecode::DecodeD(*(pc + i)); const uintptr_t cids = SimulatorBytecode::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); #ifndef PRODUCT if (thread->isolate()->single_step()) { Exit(thread, FP, SP + 1, pc); NativeArguments args(thread, 0, NULL, NULL); INVOKE_RUNTIME(DRT_SingleStepHandler, args); } #endif // !PRODUCT SP -= 2; if (!SimulatorHelpers::IsStrictEqualWithNumberCheck(SP[1], SP[2])) { pc++; } DISPATCH(); } { BYTECODE(IfNeStrictNumTOS, 0); #ifndef PRODUCT if (thread->isolate()->single_step()) { Exit(thread, FP, SP + 1, pc); NativeArguments args(thread, 0, NULL, NULL); INVOKE_RUNTIME(DRT_SingleStepHandler, args); } #endif // !PRODUCT SP -= 2; if (SimulatorHelpers::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(FP[rA]); const intptr_t rhs = reinterpret_cast(FP[rD]); if (lhs > rhs) { pc++; } DISPATCH(); } { BYTECODE(IfLt, A_D); const intptr_t lhs = reinterpret_cast(FP[rA]); const intptr_t rhs = reinterpret_cast(FP[rD]); if (lhs >= rhs) { pc++; } DISPATCH(); } { BYTECODE(IfGe, A_D); const intptr_t lhs = reinterpret_cast(FP[rA]); const intptr_t rhs = reinterpret_cast(FP[rD]); if (lhs < rhs) { pc++; } DISPATCH(); } { BYTECODE(IfGt, A_D); const intptr_t lhs = reinterpret_cast(FP[rA]); const intptr_t rhs = reinterpret_cast(FP[rD]); if (lhs <= rhs) { pc++; } DISPATCH(); } { BYTECODE(IfULe, A_D); const uintptr_t lhs = reinterpret_cast(FP[rA]); const uintptr_t rhs = reinterpret_cast(FP[rD]); if (lhs > rhs) { pc++; } DISPATCH(); } { BYTECODE(IfULt, A_D); const uintptr_t lhs = reinterpret_cast(FP[rA]); const uintptr_t rhs = reinterpret_cast(FP[rD]); if (lhs >= rhs) { pc++; } DISPATCH(); } { BYTECODE(IfUGe, A_D); const uintptr_t lhs = reinterpret_cast(FP[rA]); const uintptr_t rhs = reinterpret_cast(FP[rD]); if (lhs < rhs) { pc++; } DISPATCH(); } { BYTECODE(IfUGt, A_D); const uintptr_t lhs = reinterpret_cast(FP[rA]); const uintptr_t rhs = reinterpret_cast(FP[rD]); if (lhs <= rhs) { pc++; } DISPATCH(); } #if defined(ARCH_IS_64_BIT) { BYTECODE(IfDEq, A_D); const double lhs = bit_cast(FP[rA]); const double rhs = bit_cast(FP[rD]); pc += (lhs == rhs) ? 0 : 1; DISPATCH(); } { BYTECODE(IfDNe, A_D); const double lhs = bit_cast(FP[rA]); const double rhs = bit_cast(FP[rD]); pc += (lhs != rhs) ? 0 : 1; DISPATCH(); } { BYTECODE(IfDLe, A_D); const double lhs = bit_cast(FP[rA]); const double rhs = bit_cast(FP[rD]); pc += (lhs <= rhs) ? 0 : 1; DISPATCH(); } { BYTECODE(IfDLt, A_D); const double lhs = bit_cast(FP[rA]); const double rhs = bit_cast(FP[rD]); pc += (lhs < rhs) ? 0 : 1; DISPATCH(); } { BYTECODE(IfDGe, A_D); const double lhs = bit_cast(FP[rA]); const double rhs = bit_cast(FP[rD]); pc += (lhs >= rhs) ? 0 : 1; DISPATCH(); } { BYTECODE(IfDGt, A_D); const double lhs = bit_cast(FP[rA]); const double rhs = bit_cast(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 (!SimulatorHelpers::IsStrictEqualWithNumberCheck(lhs, rhs)) { pc++; } DISPATCH(); } { BYTECODE(IfNeStrictNum, A_D); RawObject* lhs = FP[rA]; RawObject* rhs = FP[rD]; if (SimulatorHelpers::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(IfEqNullTOS, 0); if (SP[0] != null_value) { pc++; } DISPATCH(); } { BYTECODE(IfNeNullTOS, 0); if (SP[0] == null_value) { pc++; } DISPATCH(); } { BYTECODE(Jump, 0); const int32_t target = static_cast(op) >> 8; pc += (target - 1); DISPATCH(); } { BYTECODE(LoadClassId, A_D); const uint16_t object_reg = rD; RawObject* obj = static_cast(FP[object_reg]); FP[rA] = SimulatorHelpers::GetClassIdAsSmi(obj); DISPATCH(); } { BYTECODE(LoadClassIdTOS, 0); RawObject* obj = static_cast(SP[0]); SP[0] = SimulatorHelpers::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(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); array->StoreArrayPointer(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(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); array->StoreArrayPointer(array->ptr()->data() + Smi::Value(index), value, thread); DISPATCH(); } { BYTECODE(StoreIndexedUint8, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rA], FP[rB]); *data = static_cast(reinterpret_cast(FP[rC])); DISPATCH(); } { BYTECODE(StoreIndexedUntaggedUint8, A_B_C); uint8_t* array = reinterpret_cast(FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); array[Smi::Value(index)] = static_cast(reinterpret_cast(FP[rC])); DISPATCH(); } { BYTECODE(StoreIndexedUntaggedUint32, A_B_C); uint8_t* array = reinterpret_cast(FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); const uint32_t value = *reinterpret_cast(&FP[rC]); *reinterpret_cast(array + Smi::Value(index)) = value; DISPATCH(); } { BYTECODE(StoreIndexedUntaggedFloat32, A_B_C); uint8_t* array = reinterpret_cast(FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); const float value = *reinterpret_cast(&FP[rC]); *reinterpret_cast(array + Smi::Value(index)) = value; DISPATCH(); } { BYTECODE(StoreIndexedUntaggedFloat64, A_B_C); uint8_t* array = reinterpret_cast(FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); const double value = *reinterpret_cast(&FP[rC]); *reinterpret_cast(array + Smi::Value(index)) = value; DISPATCH(); } { BYTECODE(StoreIndexedOneByteString, A_B_C); RawOneByteString* array = RAW_CAST(OneByteString, FP[rA]); RawSmi* index = RAW_CAST(Smi, FP[rB]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); array->ptr()->data()[Smi::Value(index)] = static_cast(reinterpret_cast(FP[rC])); DISPATCH(); } { BYTECODE(StoreIndexedUint32, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rA], FP[rB]); const uintptr_t value = reinterpret_cast(FP[rC]); *reinterpret_cast(data) = static_cast(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 -= 2; 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(SimulatorHelpers::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(obj); RawSmi* index = RAW_CAST(Smi, FP[rC]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); FP[rA] = array->ptr()->data()[Smi::Value(index)]; DISPATCH(); } { BYTECODE(LoadIndexedUint8, A_B_C); uint8_t* data = SimulatorHelpers::GetTypedData(FP[rB], FP[rC]); FP[rA] = reinterpret_cast(static_cast(*data)); DISPATCH(); } { BYTECODE(LoadIndexedInt8, A_B_C); int8_t* data = reinterpret_cast( SimulatorHelpers::GetTypedData(FP[rB], FP[rC])); FP[rA] = reinterpret_cast(static_cast(*data)); DISPATCH(); } { BYTECODE(LoadIndexedUint32, A_B_C); const uint8_t* data = SimulatorHelpers::GetTypedData(FP[rB], FP[rC]); const uint32_t value = *reinterpret_cast(data); *reinterpret_cast(&FP[rA]) = value; DISPATCH(); } { BYTECODE(LoadIndexedInt32, A_B_C); const uint8_t* data = SimulatorHelpers::GetTypedData(FP[rB], FP[rC]); const int32_t value = *reinterpret_cast(data); *reinterpret_cast(&FP[rA]) = value; DISPATCH(); } { BYTECODE(LoadIndexedUntaggedInt8, A_B_C); int8_t* data = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); FP[rA] = reinterpret_cast( static_cast(data[Smi::Value(index)])); DISPATCH(); } { BYTECODE(LoadIndexedUntaggedUint8, A_B_C); uint8_t* data = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); FP[rA] = reinterpret_cast( static_cast(data[Smi::Value(index)])); DISPATCH(); } { BYTECODE(LoadIndexedUntaggedInt32, A_B_C); uint8_t* data = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); const int32_t value = *reinterpret_cast(data + Smi::Value(index)); *reinterpret_cast(&FP[rA]) = value; DISPATCH(); } { BYTECODE(LoadIndexedUntaggedUint32, A_B_C); uint8_t* data = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); const uint32_t value = *reinterpret_cast(data + Smi::Value(index)); *reinterpret_cast(&FP[rA]) = value; DISPATCH(); } { BYTECODE(LoadIndexedUntaggedFloat32, A_B_C); uint8_t* data = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); const float value = *reinterpret_cast(data + Smi::Value(index)); *reinterpret_cast(&FP[rA]) = value; DISPATCH(); } { BYTECODE(LoadIndexedUntaggedFloat64, A_B_C); uint8_t* data = reinterpret_cast(FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); const double value = *reinterpret_cast(data + Smi::Value(index)); *reinterpret_cast(&FP[rA]) = value; DISPATCH(); } { BYTECODE(LoadIndexedOneByteString, A_B_C); RawOneByteString* array = RAW_CAST(OneByteString, FP[rB]); RawSmi* index = RAW_CAST(Smi, FP[rC]); ASSERT(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); FP[rA] = reinterpret_cast( static_cast(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(SimulatorHelpers::CheckIndex(index, array->ptr()->length_)); FP[rA] = reinterpret_cast( static_cast(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(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 = (reinterpret_cast(pc) & 2) == 0; const intptr_t argc = has_dart_caller ? SimulatorBytecode::DecodeArgc(pc[-1]) : (reinterpret_cast(pc) >> 2); const bool has_function_type_args = has_dart_caller && SimulatorHelpers::ArgDescTypeArgsLen(argdesc_) > 0; SP = FrameArguments(FP, 0); RawObject** args = SP - argc; FP = SavedCallerFP(FP); if (has_dart_caller) { pp_ = SimulatorHelpers::FrameCode(FP)->ptr()->object_pool_; } RawClosure* closure = Closure::RawCast(args[has_function_type_args ? 1 : 0]); *++SP = null_value; *++SP = closure; *++SP = closure->ptr()->function_; *++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(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, 4, SP - 3, SP - 4); INVOKE_RUNTIME(DRT_NoSuchMethodFromPrologue, native_args); UNREACHABLE(); } DISPATCH(); } // Single dispatch point used by exception handling macros. { DispatchAfterException: DISPATCH(); } UNREACHABLE(); return 0; } void Simulator::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. SimulatorSetjmpBuffer* 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); fp_ = reinterpret_cast(fp); if (pc == StubCode::RunExceptionHandler().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_[kExceptionSpecialIndex] = raw_exception; special_[kStackTraceSpecialIndex] = raw_stacktrace; pc_ = thread->resume_pc(); } else { pc_ = pc; } // Set the tag. thread->set_vm_tag(VMTag::kDartCompiledTagId); // Clear top exit frame. thread->set_top_exit_frame_info(0); buf->Longjmp(); UNREACHABLE(); } void Simulator::VisitObjectPointers(ObjectPointerVisitor* visitor) { visitor->VisitPointer(reinterpret_cast(&pp_)); visitor->VisitPointer(reinterpret_cast(&argdesc_)); } } // namespace dart #endif // defined TARGET_ARCH_DBC