From ee0f608ce4d9ba35f360dfec76626ff4613eb103 Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Mon, 18 Apr 2016 23:02:01 +0200 Subject: [PATCH] Dart Byte Code interpreter. This version is Clang/GCC only and does not support Windows because it uses computed goto's. Only unoptimized mode is supported. Architecture is described in constants_dbc.h and stack_frame_dbc.h. R=fschneider@google.com, zra@google.com Review URL: https://codereview.chromium.org/1858283002 . --- .../observatory/tests/service/service.status | 5 + runtime/platform/globals.h | 14 + runtime/tests/vm/vm.status | 86 +- runtime/vm/assembler.h | 2 + runtime/vm/assembler_dbc.cc | 142 ++ runtime/vm/assembler_dbc.h | 197 ++ runtime/vm/assembler_test.cc | 3 + runtime/vm/atomic.h | 6 + runtime/vm/atomic_android.h | 4 +- runtime/vm/atomic_linux.h | 4 +- runtime/vm/atomic_macos.h | 4 +- runtime/vm/atomic_simulator.h | 4 +- runtime/vm/code_generator.cc | 24 +- runtime/vm/code_patcher_dbc.cc | 106 + runtime/vm/constants_dbc.h | 490 +++++ runtime/vm/cpu.h | 2 + runtime/vm/cpu_dbc.cc | 26 + runtime/vm/cpu_dbc.h | 30 + runtime/vm/cpu_test.cc | 2 + runtime/vm/cpuinfo_test.cc | 2 + runtime/vm/dart_entry.cc | 9 +- runtime/vm/dart_entry.h | 14 + runtime/vm/debugger.cc | 34 +- runtime/vm/debugger.h | 7 + runtime/vm/debugger_api_impl_test.cc | 5 + runtime/vm/debugger_dbc.cc | 86 + runtime/vm/deopt_instructions.cc | 5 + runtime/vm/disassembler_dbc.cc | 233 ++ runtime/vm/disassembler_test.cc | 4 +- runtime/vm/flag_list.h | 14 +- runtime/vm/flow_graph_builder.cc | 3 + runtime/vm/flow_graph_compiler.cc | 42 +- runtime/vm/flow_graph_compiler.h | 37 +- runtime/vm/flow_graph_compiler_dbc.cc | 331 +++ runtime/vm/instructions.h | 2 + runtime/vm/instructions_dbc.cc | 177 ++ runtime/vm/instructions_dbc.h | 138 ++ runtime/vm/intermediate_language.cc | 103 +- runtime/vm/intermediate_language_dbc.cc | 704 ++++++ runtime/vm/intrinsifier.cc | 22 + runtime/vm/intrinsifier.h | 6 + runtime/vm/intrinsifier_dbc.cc | 39 + runtime/vm/locations.cc | 4 +- runtime/vm/locations.h | 7 + runtime/vm/method_recognizer.h | 3 +- runtime/vm/native_arguments.h | 25 +- runtime/vm/native_entry.cc | 13 +- runtime/vm/native_entry.h | 3 + runtime/vm/object.cc | 16 +- runtime/vm/object.h | 3 + runtime/vm/object_test.cc | 11 +- runtime/vm/os_android.cc | 4 +- runtime/vm/os_linux.cc | 6 +- runtime/vm/precompiler.cc | 2 + runtime/vm/profiler.cc | 18 +- runtime/vm/raw_object.h | 4 + runtime/vm/raw_object_snapshot.cc | 13 +- runtime/vm/runtime_entry_dbc.cc | 29 + runtime/vm/simulator.h | 2 + runtime/vm/simulator_arm.cc | 3 +- runtime/vm/simulator_arm.h | 4 + runtime/vm/simulator_arm64.cc | 1 - runtime/vm/simulator_arm64.h | 4 + runtime/vm/simulator_dbc.cc | 1903 +++++++++++++++++ runtime/vm/simulator_dbc.h | 182 ++ runtime/vm/simulator_mips.cc | 3 +- runtime/vm/simulator_mips.h | 4 + runtime/vm/stack_frame.cc | 24 + runtime/vm/stack_frame.h | 33 +- runtime/vm/stack_frame_dbc.h | 85 + runtime/vm/stub_code.cc | 25 + runtime/vm/stub_code.h | 9 +- runtime/vm/stub_code_dbc.cc | 66 + runtime/vm/thread.cc | 12 +- runtime/vm/thread.h | 35 +- runtime/vm/unit_test.h | 10 +- runtime/vm/vm_sources.gypi | 15 + tests/co19/co19-runtime.status | 5 + tests/corelib/corelib.status | 5 +- tests/language/language.status | 8 + tests/lib/lib.status | 5 + tests/standalone/standalone.status | 10 + tools/build.py | 12 +- tools/gyp/configurations.gypi | 29 + tools/gyp/configurations_make.gypi | 6 + tools/testing/dart/runtime_configuration.dart | 1 + tools/testing/dart/test_options.dart | 3 +- tools/utils.py | 1 + 88 files changed, 5722 insertions(+), 112 deletions(-) create mode 100644 runtime/vm/assembler_dbc.cc create mode 100644 runtime/vm/assembler_dbc.h create mode 100644 runtime/vm/code_patcher_dbc.cc create mode 100644 runtime/vm/constants_dbc.h create mode 100644 runtime/vm/cpu_dbc.cc create mode 100644 runtime/vm/cpu_dbc.h create mode 100644 runtime/vm/debugger_dbc.cc create mode 100644 runtime/vm/disassembler_dbc.cc create mode 100644 runtime/vm/flow_graph_compiler_dbc.cc create mode 100644 runtime/vm/instructions_dbc.cc create mode 100644 runtime/vm/instructions_dbc.h create mode 100644 runtime/vm/intermediate_language_dbc.cc create mode 100644 runtime/vm/intrinsifier_dbc.cc create mode 100644 runtime/vm/runtime_entry_dbc.cc create mode 100644 runtime/vm/simulator_dbc.cc create mode 100644 runtime/vm/simulator_dbc.h create mode 100644 runtime/vm/stack_frame_dbc.h create mode 100644 runtime/vm/stub_code_dbc.cc diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status index b4d620d400e..d212a410a68 100644 --- a/runtime/observatory/tests/service/service.status +++ b/runtime/observatory/tests/service/service.status @@ -42,3 +42,8 @@ coverage_test: Pass, Slow [ $compiler == dart2analyzer ] evaluate_activation_in_method_class_test: CompileTimeError # Issue 24478 + +[ $arch == simdbc ] +# TODO(vegorov) re-enable when debugger, coverage and profiling is completely +# fixed for SIMDBC. +*: Skip diff --git a/runtime/platform/globals.h b/runtime/platform/globals.h index 7e11bb3163e..40c4b07a47a 100644 --- a/runtime/platform/globals.h +++ b/runtime/platform/globals.h @@ -257,6 +257,15 @@ typedef simd128_value_t fpu_register_t; #error Automatic compiler detection failed. #endif +// DART_NOINLINE tells compiler to never inline a particular function. +#ifdef _MSC_VER +#define DART_NOINLINE __declspec(noinline) +#elif __GNUC__ +#define DART_NOINLINE __attribute__((noinline)) +#else +#error Automatic compiler detection failed. +#endif + // DART_UNUSED inidicates to the compiler that a variable/typedef is expected // to be unused and disables the related warning. #ifdef __GNUC__ @@ -282,6 +291,7 @@ typedef simd128_value_t fpu_register_t; #if !defined(TARGET_ARCH_X64) #if !defined(TARGET_ARCH_IA32) #if !defined(TARGET_ARCH_ARM64) +#if !defined(TARGET_ARCH_DBC) // No target architecture specified pick the one matching the host architecture. #if defined(HOST_ARCH_MIPS) #define TARGET_ARCH_MIPS 1 @@ -301,6 +311,7 @@ typedef simd128_value_t fpu_register_t; #endif #endif #endif +#endif // Verify that host and target architectures match, we cannot // have a 64 bit Dart VM generating 32 bit code or vice-versa. @@ -337,6 +348,9 @@ typedef simd128_value_t fpu_register_t; #define USING_SIMULATOR 1 #endif +#elif defined(TARGET_ARCH_DBC) +#define USING_SIMULATOR 1 + #else #error Unknown architecture. #endif diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index 62cc6da6170..7fba3a487e9 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -33,7 +33,7 @@ cc/Service_Profile: Skip cc/Dart2JSCompilerStats: Skip cc/CorelibCompilerStats: Skip -[ $arch == simarm || $arch == simarmv6 || $arch == simarmv5te || $arch == simarm64 || $arch == simmips ] +[ $arch == simarm || $arch == simarmv6 || $arch == simarmv5te || $arch == simarm64 || $arch == simmips || $arch == simdbc ] cc/Service_Profile: Skip [ $compiler == dart2js ] @@ -97,3 +97,87 @@ dart/optimized_stacktrace_test: SkipByDesign # Requires line numbers cc/IsolateSetCheckedMode: Fail,OK # Expects exact type name. cc/LibraryGetClassNames: Fail,OK # Expects exact type name. cc/StackTraceFormat: Fail,OK # Expects exact type name. + +[ $arch == simdbc ] +# TODO(vegorov) Profiler is completely disabled in SIMDBC builds. +cc/Profiler_AllocationSampleTest: Skip +cc/Profiler_ArrayAllocation: Skip +cc/Profiler_BasicSourcePosition: Skip +cc/Profiler_BasicSourcePositionOptimized: Skip +cc/Profiler_BinaryOperatorSourcePosition: Skip +cc/Profiler_BinaryOperatorSourcePositionOptimized: Skip +cc/Profiler_ChainedSamples: Skip +cc/Profiler_ClosureAllocation: Skip +cc/Profiler_CodeTicks: Skip +cc/Profiler_ContextAllocation: Skip +cc/Profiler_FunctionInline: Skip +cc/Profiler_FunctionTicks: Skip +cc/Profiler_InliningIntervalBoundry: Skip +cc/Profiler_IntrinsicAllocation: Skip +cc/Profiler_SampleBufferIterateTest: Skip +cc/Profiler_SampleBufferWrapTest: Skip +cc/Profiler_SourcePosition: Skip +cc/Profiler_SourcePositionOptimized: Skip +cc/Profiler_StringAllocation: Skip +cc/Profiler_StringInterpolation: Skip +cc/Profiler_ToggleRecordAllocation: Skip +cc/Profiler_TrivialRecordAllocation: Skip +cc/Profiler_TypedArrayAllocation: Skip +cc/Profiler_GetSourceReport: Skip + +# TODO(vegorov) These tests are crashing because ICData objects can't be found +cc/SourceReport_CallSites_PolymorphicCall: Skip +cc/SourceReport_CallSites_SimpleCall: Skip +cc/SourceReport_Coverage_AllFunctions: Skip +cc/SourceReport_Coverage_ForceCompile: Skip +cc/SourceReport_Coverage_NestedFunctions: Skip +cc/SourceReport_Coverage_SimpleCall: Skip +cc/SourceReport_MultipleReports: Skip +cc/Coverage_Empty: Skip +cc/Coverage_FilterFunction: Skip +cc/Coverage_MainWithClass: Skip + +# TODO(vegorov) DisassembleToJSONStream requires +# DecodeLoadObjectFromPoolOrThread which is unimplemented. +cc/Service_Code: Skip +cc/PrintJSON: Skip + +# TODO(vegorov) These tests don't seem to work if FLAG_interpret_irregexp +# is switched on by default because they attempt to call regexp functions +# directly instead of going through JSSyntaxRegExp_ExecuteMatch. +cc/RegExp_ExternalOneByteString: Skip +cc/RegExp_ExternalTwoByteString: Skip +cc/RegExp_OneByteString: Skip +cc/RegExp_TwoByteString: Skip + +# TODO(vegorov) Optimizing compiler is disabled for the SIMDBC +cc/CompileFunctionOnHelperThread: Skip + +# TODO(vegorov) Field guards are disabled for SIMDBC +cc/GuardFieldConstructor2Test: Skip +cc/GuardFieldConstructorTest: Skip +cc/GuardFieldFinalListTest: Skip +cc/GuardFieldFinalVariableLengthListTest: Skip +cc/GuardFieldSimpleTest: Skip + +# TODO(vegorov) Not all bytecodes have appropriate debug breaks. +cc/Debug_BreakpointStubPatching: Skip +cc/Debug_ExprClosureBreakpoint: Skip +cc/Debug_StackTraceDump1: Skip +cc/Debug_StepInto: Skip + +# TODO(vegorov) These parser tests rely on debugger. +cc/Parser_AllocateVariables_CapturedVar: Skip +cc/Parser_AllocateVariables_MiddleChain: Skip + +# TODO(vegorov) Test needs to generate a large enough function to go on to the +# large page. However large enough function overflows bytecode encoding. +cc/FindCodeObject: Skip + +# This test is meaningless for DBC as allocation stubs are not used. +cc/RegenerateAllocStubs: Skip + +# TODO(vegorov) Enable when DBC supports optimizing compiler. +cc/Debug_InspectStack_Optimized: Skip +cc/Debug_InspectStackWithClosure_Optimized: Skip + diff --git a/runtime/vm/assembler.h b/runtime/vm/assembler.h index 65820fe916f..ceaadaa671a 100644 --- a/runtime/vm/assembler.h +++ b/runtime/vm/assembler.h @@ -340,6 +340,8 @@ enum RestorePP { #include "vm/assembler_arm64.h" #elif defined(TARGET_ARCH_MIPS) #include "vm/assembler_mips.h" +#elif defined(TARGET_ARCH_DBC) +#include "vm/assembler_dbc.h" #else #error Unknown architecture. #endif diff --git a/runtime/vm/assembler_dbc.cc b/runtime/vm/assembler_dbc.cc new file mode 100644 index 00000000000..bd3d3fa3853 --- /dev/null +++ b/runtime/vm/assembler_dbc.cc @@ -0,0 +1,142 @@ +// 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 "vm/globals.h" // NOLINT +#if defined(TARGET_ARCH_DBC) + +#include "vm/assembler.h" +#include "vm/cpu.h" +#include "vm/longjump.h" +#include "vm/runtime_entry.h" +#include "vm/simulator.h" +#include "vm/stack_frame.h" +#include "vm/stub_code.h" + +namespace dart { + +DECLARE_FLAG(bool, allow_absolute_addresses); +DECLARE_FLAG(bool, check_code_pointer); +DECLARE_FLAG(bool, inline_alloc); + +void Assembler::InitializeMemoryWithBreakpoints(uword data, intptr_t length) { + const uword end = data + length; + while (data < end) { + *reinterpret_cast(data) = Bytecode::kTrap; + data += sizeof(int32_t); + } +} + +#define DEFINE_EMIT(Name, Signature, Fmt0, Fmt1, Fmt2) \ + void Assembler::Name(PARAMS_##Signature) { \ + Emit(Bytecode::FENCODE_##Signature( \ + Bytecode::k##Name ENCODE_##Signature)); \ + } \ + + +#define PARAMS_0 +#define PARAMS_A_D uintptr_t ra, uintptr_t rd +#define PARAMS_D uintptr_t rd +#define PARAMS_A_B_C uintptr_t ra, uintptr_t rb, uintptr_t rc +#define PARAMS_A uintptr_t ra +#define PARAMS_T intptr_t x +#define PARAMS_A_X uintptr_t ra, intptr_t x +#define PARAMS_X intptr_t x + +#define ENCODE_0 +#define ENCODE_A_D , ra, rd +#define ENCODE_D , 0, rd +#define ENCODE_A_B_C , ra, rb, rc +#define ENCODE_A , ra, 0 +#define ENCODE_T , x +#define ENCODE_A_X , ra, x +#define ENCODE_X , 0, x + +#define FENCODE_0 Encode +#define FENCODE_A_D Encode +#define FENCODE_D Encode +#define FENCODE_A_B_C Encode +#define FENCODE_A Encode +#define FENCODE_T EncodeSigned +#define FENCODE_A_X EncodeSigned +#define FENCODE_X EncodeSigned + + +BYTECODES_LIST(DEFINE_EMIT) + + +#undef DEFINE_EMIT + + +void Assembler::Emit(int32_t value) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + buffer_.Emit(value); +} + + +const char* Assembler::RegisterName(Register reg) { + return Thread::Current()->zone()->PrintToString("R%d", reg); +} + + +static int32_t EncodeJump(int32_t relative_pc) { + return Bytecode::kJump | (relative_pc << 8); +} + + +static int32_t OffsetToPC(int32_t offset) { + return offset >> 2; +} + + +void Assembler::Jump(Label* label) { + if (label->IsBound()) { + Emit(EncodeJump(OffsetToPC(label->Position() - buffer_.Size()))); + } else { + const intptr_t position = buffer_.Size(); + Emit(label->position_); + label->LinkTo(position); + } +} + + +void Assembler::Bind(Label* label) { + ASSERT(!label->IsBound()); + ASSERT(!label->IsBound()); + intptr_t bound_pc = buffer_.Size(); + while (label->IsLinked()) { + const int32_t position = label->Position(); + const int32_t next_position = buffer_.Load(position); + buffer_.Store(position, + EncodeJump(OffsetToPC(bound_pc - position))); + label->position_ = next_position; + } + label->BindTo(bound_pc); +} + + +void Assembler::Stop(const char* message) { + // TODO(vegorov) support passing a message to the bytecode. + Emit(Bytecode::kTrap); +} + + +void Assembler::PushConstant(const Object& obj) { + PushConstant(AddConstant(obj)); +} + + +void Assembler::LoadConstant(uintptr_t ra, const Object& obj) { + LoadConstant(ra, AddConstant(obj)); +} + + +intptr_t Assembler::AddConstant(const Object& obj) { + return object_pool_wrapper().FindObject( + Object::ZoneHandle(obj.raw())); +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/assembler_dbc.h b/runtime/vm/assembler_dbc.h new file mode 100644 index 00000000000..590dd5ca012 --- /dev/null +++ b/runtime/vm/assembler_dbc.h @@ -0,0 +1,197 @@ +// 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. + +#ifndef VM_ASSEMBLER_DBC_H_ +#define VM_ASSEMBLER_DBC_H_ + +#ifndef VM_ASSEMBLER_H_ +#error Do not include assembler_dbc.h directly; use assembler.h instead. +#endif + +#include "platform/assert.h" +#include "platform/utils.h" +#include "vm/constants_dbc.h" +#include "vm/cpu.h" +#include "vm/hash_map.h" +#include "vm/object.h" +#include "vm/simulator.h" + +namespace dart { + + +// Dummy declaration to make things compile. +class Address : public ValueObject { + private: + Address(); +}; + + +class Label : public ValueObject { + public: + Label() : position_(0) { } + + ~Label() { + // Assert if label is being destroyed with unresolved branches pending. + ASSERT(!IsLinked()); + } + + // Returns the position for bound and linked labels. Cannot be used + // for unused labels. + intptr_t Position() const { + ASSERT(!IsUnused()); + return IsBound() ? -position_ - kWordSize : position_ - kWordSize; + } + + bool IsBound() const { return position_ < 0; } + bool IsUnused() const { return position_ == 0; } + bool IsLinked() const { return position_ > 0; } + + private: + intptr_t position_; + + void Reinitialize() { + position_ = 0; + } + + void BindTo(intptr_t position) { + ASSERT(!IsBound()); + position_ = -position - kWordSize; + ASSERT(IsBound()); + } + + void LinkTo(intptr_t position) { + ASSERT(!IsBound()); + position_ = position + kWordSize; + ASSERT(IsLinked()); + } + + friend class Assembler; + DISALLOW_COPY_AND_ASSIGN(Label); +}; + + +class Assembler : public ValueObject { + public: + explicit Assembler(bool use_far_branches = false) + : buffer_(), + comments_() { + } + + ~Assembler() { } + + void Bind(Label* label); + void Jump(Label* label); + + // Misc. functionality + intptr_t CodeSize() const { return buffer_.Size(); } + intptr_t prologue_offset() const { return 0; } + + // Count the fixups that produce a pointer offset, without processing + // the fixups. + intptr_t CountPointerOffsets() const { return 0; } + + const ZoneGrowableArray& GetPointerOffsets() const { + ASSERT(buffer_.pointer_offsets().length() == 0); // No pointers in code. + return buffer_.pointer_offsets(); + } + + ObjectPoolWrapper& object_pool_wrapper() { return object_pool_wrapper_; } + + RawObjectPool* MakeObjectPool() { + return object_pool_wrapper_.MakeObjectPool(); + } + + void FinalizeInstructions(const MemoryRegion& region) { + buffer_.FinalizeInstructions(region); + } + + // Debugging and bringup support. + void Stop(const char* message); + void Unimplemented(const char* message); + void Untested(const char* message); + void Unreachable(const char* message); + + static void InitializeMemoryWithBreakpoints(uword data, intptr_t length); + + void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3); + static bool EmittingComments(); + + const Code::Comments& GetCodeComments() const; + + static const char* RegisterName(Register reg); + + static const char* FpuRegisterName(FpuRegister reg) { + return "?"; + } + + static uword GetBreakInstructionFiller() { + return Bytecode::kTrap; + } + + static bool IsSafe(const Object& value) { return true; } + static bool IsSafeSmi(const Object& value) { return false; } + + // Bytecodes. + +#define DECLARE_EMIT(Name, Signature, Fmt0, Fmt1, Fmt2) \ + void Name(PARAMS_##Signature); + +#define PARAMS_0 +#define PARAMS_A_D uintptr_t ra, uintptr_t rd +#define PARAMS_D uintptr_t rd +#define PARAMS_A_B_C uintptr_t ra, uintptr_t rb, uintptr_t rc +#define PARAMS_A uintptr_t ra +#define PARAMS_X intptr_t x +#define PARAMS_T intptr_t x +#define PARAMS_A_X uintptr_t ra, intptr_t x + + + BYTECODES_LIST(DECLARE_EMIT) + +#undef PARAMS_0 +#undef PARAMS_A_D +#undef PARAMS_D +#undef PARAMS_A_B_C +#undef PARAMS_A +#undef PARAMS_X +#undef PARAMS_T +#undef PARAMS_A_X +#undef DECLARE_EMIT + + void Emit(int32_t value); + + void PushConstant(const Object& obj); + void LoadConstant(uintptr_t ra, const Object& obj); + + intptr_t AddConstant(const Object& obj); + + private: + AssemblerBuffer buffer_; // Contains position independent code. + ObjectPoolWrapper object_pool_wrapper_; + + class CodeComment : public ZoneAllocated { + public: + CodeComment(intptr_t pc_offset, const String& comment) + : pc_offset_(pc_offset), comment_(comment) { } + + intptr_t pc_offset() const { return pc_offset_; } + const String& comment() const { return comment_; } + + private: + intptr_t pc_offset_; + const String& comment_; + + DISALLOW_COPY_AND_ASSIGN(CodeComment); + }; + + GrowableArray comments_; + + DISALLOW_ALLOCATION(); + DISALLOW_COPY_AND_ASSIGN(Assembler); +}; + + +} // namespace dart + +#endif // VM_ASSEMBLER_DBC_H_ diff --git a/runtime/vm/assembler_test.cc b/runtime/vm/assembler_test.cc index fbbd994698f..88ff6676111 100644 --- a/runtime/vm/assembler_test.cc +++ b/runtime/vm/assembler_test.cc @@ -11,6 +11,8 @@ namespace dart { +// TODO(vegorov) assembler part of this test is not implemented. +#if !defined(TARGET_ARCH_DBC) ASSEMBLER_TEST_EXTERN(StoreIntoObject); ASSEMBLER_TEST_RUN(StoreIntoObject, test) { @@ -60,5 +62,6 @@ ASSEMBLER_TEST_RUN(StoreIntoObject, test) { EXPECT(old_array.raw() == grow_new_array.data()); EXPECT(!thread->StoreBufferContains(grow_new_array.raw())); } +#endif } // namespace dart diff --git a/runtime/vm/atomic.h b/runtime/vm/atomic.h index 026762f2c61..c182a8445a9 100644 --- a/runtime/vm/atomic.h +++ b/runtime/vm/atomic.h @@ -65,9 +65,15 @@ class AtomicOperations : public AllStatic { } // namespace dart +#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC) +#define USING_SIMULATOR_ATOMICS +#endif + +#if defined(USING_SIMULATOR_ATOMICS) // We need to use the simulator to ensure that atomic operations are observed // both in C++ and in generated code if the simulator is active. #include "vm/atomic_simulator.h" +#endif #if defined(TARGET_OS_ANDROID) #include "vm/atomic_android.h" diff --git a/runtime/vm/atomic_android.h b/runtime/vm/atomic_android.h index b994343e8f6..689173a2ac6 100644 --- a/runtime/vm/atomic_android.h +++ b/runtime/vm/atomic_android.h @@ -41,7 +41,7 @@ inline void AtomicOperations::DecrementBy(intptr_t* p, intptr_t value) { } -#if !defined(USING_SIMULATOR) +#if !defined(USING_SIMULATOR_ATOMICS) inline uword AtomicOperations::CompareAndSwapWord(uword* ptr, uword old_value, uword new_value) { @@ -54,7 +54,7 @@ inline uint32_t AtomicOperations::CompareAndSwapUint32(uint32_t* ptr, uint32_t new_value) { return __sync_val_compare_and_swap(ptr, old_value, new_value); } -#endif // !defined(USING_SIMULATOR) +#endif // !defined(USING_SIMULATOR_ATOMICS) } // namespace dart diff --git a/runtime/vm/atomic_linux.h b/runtime/vm/atomic_linux.h index 3e6f4adde07..18456cf57d3 100644 --- a/runtime/vm/atomic_linux.h +++ b/runtime/vm/atomic_linux.h @@ -46,7 +46,7 @@ inline void AtomicOperations::DecrementBy(intptr_t* p, intptr_t value) { } -#if !defined(USING_SIMULATOR) +#if !defined(USING_SIMULATOR_ATOMICS) inline uword AtomicOperations::CompareAndSwapWord(uword* ptr, uword old_value, uword new_value) { @@ -59,7 +59,7 @@ inline uint32_t AtomicOperations::CompareAndSwapUint32(uint32_t* ptr, uint32_t new_value) { return __sync_val_compare_and_swap(ptr, old_value, new_value); } -#endif // !defined(USING_SIMULATOR) +#endif // !defined(USING_SIMULATOR_ATOMICS) } // namespace dart diff --git a/runtime/vm/atomic_macos.h b/runtime/vm/atomic_macos.h index ae5da3ada6a..479ca30b353 100644 --- a/runtime/vm/atomic_macos.h +++ b/runtime/vm/atomic_macos.h @@ -41,7 +41,7 @@ inline void AtomicOperations::DecrementBy(intptr_t* p, intptr_t value) { } -#if !defined(USING_SIMULATOR) +#if !defined(USING_SIMULATOR_ATOMICS) inline uword AtomicOperations::CompareAndSwapWord(uword* ptr, uword old_value, uword new_value) { @@ -54,7 +54,7 @@ inline uint32_t AtomicOperations::CompareAndSwapUint32(uint32_t* ptr, uint32_t new_value) { return __sync_val_compare_and_swap(ptr, old_value, new_value); } -#endif // !defined(USING_SIMULATOR) +#endif // !defined(USING_SIMULATOR_ATOMICS) } // namespace dart diff --git a/runtime/vm/atomic_simulator.h b/runtime/vm/atomic_simulator.h index 2b52a227c1f..78e7f88eb01 100644 --- a/runtime/vm/atomic_simulator.h +++ b/runtime/vm/atomic_simulator.h @@ -11,7 +11,7 @@ namespace dart { -#if defined(USING_SIMULATOR) +#if defined(USING_SIMULATOR_ATOMICS) // Forward atomic operations to the simulator if the simulator is active. inline uword AtomicOperations::CompareAndSwapWord(uword* ptr, uword old_value, @@ -25,7 +25,7 @@ inline uint32_t AtomicOperations::CompareAndSwapUint32(uint32_t* ptr, uint32_t new_value) { return Simulator::CompareExchangeUint32(ptr, old_value, new_value); } -#endif // defined(USING_SIMULATOR) +#endif // defined(USING_SIMULATOR_ATOMICS) } // namespace dart diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc index 9511db9a98c..46bbe5e64ab 100644 --- a/runtime/vm/code_generator.cc +++ b/runtime/vm/code_generator.cc @@ -654,6 +654,7 @@ static void CheckResultError(const Object& result) { } +#if !defined(TARGET_ARCH_DBC) // Gets called from debug stub when code reaches a breakpoint // set on a runtime stub call. DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) { @@ -674,6 +675,20 @@ DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) { } arguments.SetReturn(orig_stub); } +#else +// Gets called from the simulator when the breakpoint is reached. +DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) { + if (!FLAG_support_debugger) { + UNREACHABLE(); + return; + } + const Error& error = Error::Handle(isolate->debugger()->SignalBpReached()); + if (!error.IsNull()) { + Exceptions::PropagateError(error); + UNREACHABLE(); + } +} +#endif // !defined(TARGET_ARCH_DBC) DEFINE_RUNTIME_ENTRY(SingleStepHandler, 0) { @@ -956,6 +971,8 @@ DEFINE_RUNTIME_ENTRY(StaticCallMissHandlerTwoArgs, 3) { // Arg2: Arguments descriptor array. // Returns: target function to call. DEFINE_RUNTIME_ENTRY(MegamorphicCacheMissHandler, 3) { +// DBC does not use megamorphic calls right now. +#if !defined(TARGET_ARCH_DBC) const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0)); const Object& ic_data_or_cache = Object::Handle(zone, arguments.ArgAt(1)); const Array& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(2)); @@ -1013,6 +1030,9 @@ DEFINE_RUNTIME_ENTRY(MegamorphicCacheMissHandler, 3) { cache.Insert(class_id, target_function); } arguments.SetReturn(target_function); +#else + UNREACHABLE(); +#endif // !defined(TARGET_ARCH_DBC) } @@ -1192,7 +1212,7 @@ DEFINE_RUNTIME_ENTRY(InvokeClosureNoSuchMethod, 3) { DEFINE_RUNTIME_ENTRY(StackOverflow, 0) { #if defined(USING_SIMULATOR) - uword stack_pos = Simulator::Current()->get_register(SPREG); + uword stack_pos = Simulator::Current()->get_sp(); #else uword stack_pos = Thread::GetCurrentStackPointer(); #endif @@ -1204,7 +1224,7 @@ DEFINE_RUNTIME_ENTRY(StackOverflow, 0) { // If an interrupt happens at the same time as a stack overflow, we // process the stack overflow now and leave the interrupt for next // time. - if (stack_pos < thread->saved_stack_limit()) { + if (IsCalleeFrameOf(thread->saved_stack_limit(), stack_pos)) { // Use the preallocated stack overflow exception to avoid calling // into dart code. const Instance& exception = diff --git a/runtime/vm/code_patcher_dbc.cc b/runtime/vm/code_patcher_dbc.cc new file mode 100644 index 00000000000..e0010abf373 --- /dev/null +++ b/runtime/vm/code_patcher_dbc.cc @@ -0,0 +1,106 @@ +// 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 "vm/globals.h" // Needed here to get TARGET_ARCH_DBC. +#if defined(TARGET_ARCH_DBC) + +#include "vm/code_patcher.h" + +#include "vm/flow_graph_compiler.h" +#include "vm/instructions.h" +#include "vm/object.h" + +namespace dart { + +RawCode* CodePatcher::GetStaticCallTargetAt(uword return_address, + const Code& code) { + ASSERT(code.ContainsInstructionAt(return_address)); + CallPattern call(return_address, code); + return call.TargetCode(); +} + + +void CodePatcher::PatchStaticCallAt(uword return_address, + const Code& code, + const Code& new_target) { + ASSERT(code.ContainsInstructionAt(return_address)); + CallPattern call(return_address, code); + call.SetTargetCode(new_target); +} + + +void CodePatcher::InsertDeoptimizationCallAt(uword start, uword target) { + // The inserted call should not overlap the lazy deopt jump code. + ASSERT(start + CallPattern::DeoptCallPatternLengthInBytes() <= target); + CallPattern::InsertDeoptCallAt(start, target); +} + + +RawCode* CodePatcher::GetInstanceCallAt(uword return_address, + const Code& code, + ICData* ic_data) { + ASSERT(code.ContainsInstructionAt(return_address)); + CallPattern call(return_address, code); + if (ic_data != NULL) { + *ic_data = call.IcData(); + } + return call.TargetCode(); +} + + +intptr_t CodePatcher::InstanceCallSizeInBytes() { + UNREACHABLE(); + return 0; +} + + +RawFunction* CodePatcher::GetUnoptimizedStaticCallAt( + uword return_address, const Code& code, ICData* ic_data_result) { + ASSERT(code.ContainsInstructionAt(return_address)); + CallPattern static_call(return_address, code); + ICData& ic_data = ICData::Handle(); + ic_data ^= static_call.IcData(); + if (ic_data_result != NULL) { + *ic_data_result = ic_data.raw(); + } + return ic_data.GetTargetAt(0); +} + + +void CodePatcher::PatchSwitchableCallAt(uword return_address, + const Code& code, + const ICData& ic_data, + const MegamorphicCache& cache, + const Code& lookup_stub) { + ASSERT(code.ContainsInstructionAt(return_address)); + SwitchableCallPattern call(return_address, code); + ASSERT(call.cache() == ic_data.raw()); + call.SetLookupStub(lookup_stub); + call.SetCache(cache); +} + + +void CodePatcher::PatchNativeCallAt(uword return_address, + const Code& code, + NativeFunction target, + const Code& trampoline) { + ASSERT(code.ContainsInstructionAt(return_address)); + NativeCallPattern call(return_address, code); + call.set_target(trampoline); + call.set_native_function(target); +} + + +RawCode* CodePatcher::GetNativeCallAt(uword return_address, + const Code& code, + NativeFunction* target) { + ASSERT(code.ContainsInstructionAt(return_address)); + NativeCallPattern call(return_address, code); + *target = call.native_function(); + return call.target(); +} + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/constants_dbc.h b/runtime/vm/constants_dbc.h new file mode 100644 index 00000000000..62202432baf --- /dev/null +++ b/runtime/vm/constants_dbc.h @@ -0,0 +1,490 @@ +// 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. + +#ifndef VM_CONSTANTS_DBC_H_ +#define VM_CONSTANTS_DBC_H_ + +#include "platform/globals.h" +#include "platform/assert.h" +#include "platform/utils.h" + + +namespace dart { + +// List of Dart Bytecode instructions. +// +// INTERPRETER STATE +// +// current frame info (see stack_frame_dbc.h for layout) +// v-----^-----v +// ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~ +// ~ | ~ ~ | FP[0] | FP[1] | ~ ~ | SP[-1]| SP[0] | +// ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~ +// ^ ^ +// FP SP +// +// +// The state of execution is captured in few interpreter registers: +// +// FP - base of the current frame +// SP - top of the stack (TOS) for the current frame +// PP - object pool for the currently execution function +// +// Frame info stored below FP additionally contains pointers to the currently +// executing function and code (see stack_frame_dbc.h for more information). +// +// In the unoptimized code most of bytecodes take operands implicitly from +// stack and store results again on the stack. Constant operands are usually +// taken from the object pool by index. +// +// ENCODING +// +// Each instruction is a 32-bit integer with opcode stored in the least +// significant byte. The following operand encodings are used: +// +// 0........8.......16.......24.......32 +// +--------+--------+--------+--------+ +// | opcode |~~~~~~~~~~~~~~~~~~~~~~~~~~| 0: no operands +// +--------+--------+--------+--------+ +// +// +--------+--------+--------+--------+ +// | opcode | A |~~~~~~~~~~~~~~~~~| A: single unsigned 8-bit operand +// +--------+--------+--------+--------+ +// +// +--------+--------+--------+--------+ +// | opcode | A | D | A_D: unsigned 8-bit operand and +// +--------+--------+--------+--------+ unsigned 16-bit operand +// +// +--------+--------+--------+--------+ +// | opcode | A | X | A_X: unsigned 8-bit operand and +// +--------+--------+--------+--------+ signed 16-bit operand +// +// +--------+--------+--------+--------+ +// | opcode |~~~~~~~~| D | D: unsigned 16-bit operand +// +--------+--------+--------+--------+ +// +// +--------+--------+--------+--------+ +// | opcode |~~~~~~~~| X | X: signed 16-bit operand +// +--------+--------+--------+--------+ +// +// +--------+--------+--------+--------+ +// | opcode | A | B | C | A_B_C: 3 unsigned 8-bit operands +// +--------+--------+--------+--------+ +// +// +--------+--------+--------+--------+ +// | opcode | T | T: signed 24-bit operand +// +--------+--------+--------+--------+ +// +// +// INSTRUCTIONS +// +// - Trap +// +// Unreachable instruction. +// +// - Compile +// +// Compile current function and start executing newly produced code +// (used to implement LazyCompileStub); +// +// - Intrinsic id +// +// Execute intrinsic with the given id. If intrinsic returns true then +// return from the current function to the caller passing value produced +// by the intrinsic as a result; +// +// - Drop1; DropR n; Drop n +// +// Drop 1 or n values from the stack, if instruction is DropR push the first +// dropped value to the stack; +// +// - Jump target +// +// Jump to the given target. Target is specified as offset from the PC of the +// jump instruction. +// +// - Return R; ReturnTOS +// +// Return to the caller using either a value from the given register or a +// value from the top-of-stack as a result. +// +// Note: return instruction knows how many arguments to remove from the +// stack because it can look at the call instruction at caller's PC and +// take argument count from it. +// +// - Move rA, rX +// +// FP[rA] <- FP[rX] +// Note: rX is signed so it can be used to address parameters which are +// at negative indices with respect to FP. +// +// - Push rX +// +// Push FP[rX] to the stack. +// +// - LoadConstant rA, D; PushConstant D +// +// Load value at index D from constant pool into FP[rA] or push it onto the +// stack. +// +// - StoreLocal rX; PopLocal rX +// +// Store top of the stack into FP[rX] and pop it if needed. +// +// - StaticCall ArgC, D +// +// Invoke function in SP[0] with arguments SP[-(1+ArgC)], ..., SP[-1] and +// argument descriptor PP[D]. +// +// - InstanceCall ArgC, D; InstanceCall2 ArgC, D; InstanceCall3 ArgC, D +// +// Lookup and invoke method using ICData in PP[D] with arguments +// SP[-(1+ArgC)], ..., SP[-1]. +// +// - NativeCall, NativeBootstrapCall +// +// Invoke native function SP[-1] with argc_tag SP[0]. +// +// - AddTOS; SubTOS; MulTOS; BitOrTOS; BitAndTOS; EqualTOS; LessThanTOS; +// GreaterThanTOS; +// +// Smi fast-path for a corresponding method. Checks if SP[0] and SP[-1] are +// both smis and result of SP[0] SP[-1] is a smi - if this is true +// then pops operands and pushes result on the stack and skips the next +// instruction (which implements a slow path fallback). +// +// - StoreStaticTOS D +// +// Stores TOS into the static field PP[D]. +// +// - PushStatic +// +// Pushes value of the static field PP[D] on to the stack. +// +// - InitStaticTOS +// +// Takes static field from TOS and ensures that it is initialized. +// +// - IfNeStrictTOS; IfEqStrictTOS; IfNeStrictNumTOS; IfEqStrictNumTOS +// +// Skips the next instruction unless the given condition holds. 'Num' +// variants perform number check while non-Num variants just compare +// RawObject pointers. +// +// Used to implement conditional jump: +// +// IfNeStrictTOS +// Jump T ;; jump if not equal +// +// - CreateArrayTOS +// +// Allocate array of length SP[0] with type arguments SP[-1]. +// +// - Allocate D +// +// Allocate object of class PP[D] with no type arguments. +// +// - AllocateT +// +// Allocate object of class SP[0] with type arguments SP[-1]. +// +// - StoreIndexedTOS +// +// Store SP[0] into array SP[-2] at index SP[-1]. No typechecking is done. +// SP[-2] is assumed to be a RawArray, SP[-1] to be a smi. +// +// - StoreField rA, B, rC +// +// Store value FP[rC] into object FP[rA] at offset (in words) B. +// +// - StoreFieldTOS D +// +// Store value SP[0] into object SP[-1] at offset (in words) D. +// +// - LoadField rA, rB, C +// +// Load value at offset (in words) C from object FP[rB] into FP[rA]. +// +// - LoadFieldTOS D +// +// Push value at offset (in words) D from object SP[0]. +// +// - BooleanNegateTOS +// +// SP[0] = !SP[0] +// +// - Throw A +// +// Throw (Rethrow if A != 0) exception. Exception object and stack object +// are taken from TOS. +// +// - Entry A, B, rC +// +// Function prologue for the function with no optional or named arguments: +// A - expected number of positional arguments; +// B - number of local slots to reserve; +// rC - specifies context register to initialize with empty context. +// +// - EntryOpt A, B, C +// +// Function prologue for the function with optional or named arguments: +// A - expected number of positional arguments; +// B - number of optional arguments; +// C - number of named arguments; +// +// Only one of B and C can be not 0. +// +// If B is not 0 then EntryOpt bytecode is followed by B LoadConstant +// bytecodes specifying default values for optional arguments. +// +// If C is not 0 then EntryOpt is followed by 2 * B LoadConstant bytecodes. +// Bytecode at 2 * i specifies name of the i-th named argument and at +// 2 * i + 1 default value. rA part of the LoadConstant bytecode specifies +// the location of the parameter on the stack. Here named arguments are +// sorted alphabetically to enable linear matching similar to how function +// prologues are implemented on other architectures. +// +// Note: Unlike Entry bytecode EntryOpt does not setup the frame for +// local variables this is done by a separate bytecode Frame. +// +// - Frame D +// +// Reserve and initialize with null space for D local variables. +// +// - SetFrame A +// +// Reinitialize SP assuming that current frame has size A. +// Used to drop temporaries from the stack in the exception handler. +// +// - AllocateContext D +// +// Allocate Context object assuming for D context variables. +// +// - CloneContext +// +// Clone context stored in TOS. +// +// - MoveSpecial rA, D +// +// Copy special values from inside interpreter to FP[rA]. Currently only +// used to pass exception object (D = 0) and stack trace object (D = 1) to +// catch handler. +// +// - InstantiateType D +// +// Instantiate type PP[D] with instantiator type arguments SP[0]. +// +// - InstantiateTypeArgumentsTOS D +// +// Instantiate type arguments PP[D] with instantiator SP[0]. +// +// - AssertAssignable D +// +// Assert that SP[-3] is assignable to variable named SP[0] of type +// SP[-1] with type arguments SP[-2] using SubtypeTestCache PP[D]. +// +// - AssertBoolean A +// +// Assert that TOS is a boolean (A = 1) or that TOS is not null (A = 0). +// +// - CheckStack +// +// Compare SP against isolate stack limit and call StackOverflow handler if +// necessary. +// +// - DebugStep, DebugBreak A +// +// Debugger support. DebugBreak is bytecode that can be patched into the +// instruction stream to trigger in place breakpoint. +// +// When patching instance or static call with DebugBreak we set A to +// match patched call's argument count so that Return instructions continue +// to work. +// +// TODO(vegorov) the way we replace calls with DebugBreak does not work +// with our smi fast paths because DebugBreak is simply skipped. +// +// BYTECODE LIST FORMAT +// +// Bytecode list below is specified using the following format: +// +// V(BytecodeName, OperandForm, Op1, Op2, Op3) +// +// - OperandForm specifies operand encoding and should be one of 0, A, T, A_D, +// A_X, X, D (see ENCODING section above). +// +// - Op1, Op2, Op2 specify operand meaning. Possible values: +// +// ___ ignored / non-existent operand +// num immediate operand +// lit constant literal from object pool +// reg register (unsigned FP relative local) +// xeg x-register (signed FP relative local) +// tgt jump target relative to the PC of the current instruction +// +// TODO(vegorov) jump targets should be encoded relative to PC of the next +// instruction because PC is incremeted immediately after fetch +// and before decoding. +// +#define BYTECODES_LIST(V) \ + V(Trap, 0, ___, ___, ___) \ + V(Compile, 0, ___, ___, ___) \ + V(Intrinsic, A, num, ___, ___) \ + V(Drop1, 0, ___, ___, ___) \ + V(DropR, A, num, ___, ___) \ + V(Drop, A, num, ___, ___) \ + V(Jump, T, tgt, ___, ___) \ + V(Return, A, num, ___, ___) \ + V(ReturnTOS, 0, ___, ___, ___) \ + V(Move, A_X, reg, xeg, ___) \ + V(Push, X, xeg, ___, ___) \ + V(LoadConstant, A_D, reg, lit, ___) \ + V(PushConstant, D, lit, ___, ___) \ + V(StoreLocal, X, xeg, ___, ___) \ + V(PopLocal, X, xeg, ___, ___) \ + V(StaticCall, A_D, num, num, ___) \ + V(InstanceCall, A_D, num, num, ___) \ + V(InstanceCall2, A_D, num, num, ___) \ + V(InstanceCall3, A_D, num, num, ___) \ + V(NativeCall, 0, ___, ___, ___) \ + V(NativeBootstrapCall, 0, ___, ___, ___) \ + V(AddTOS, 0, ___, ___, ___) \ + V(SubTOS, 0, ___, ___, ___) \ + V(MulTOS, 0, ___, ___, ___) \ + V(BitOrTOS, 0, ___, ___, ___) \ + V(BitAndTOS, 0, ___, ___, ___) \ + V(EqualTOS, 0, ___, ___, ___) \ + V(LessThanTOS, 0, ___, ___, ___) \ + V(GreaterThanTOS, 0, ___, ___, ___) \ + V(StoreStaticTOS, D, lit, ___, ___) \ + V(PushStatic, D, lit, ___, ___) \ + V(InitStaticTOS, 0, ___, ___, ___) \ + V(IfNeStrictTOS, 0, ___, ___, ___) \ + V(IfEqStrictTOS, 0, ___, ___, ___) \ + V(IfNeStrictNumTOS, 0, ___, ___, ___) \ + V(IfEqStrictNumTOS, 0, ___, ___, ___) \ + V(CreateArrayTOS, 0, ___, ___, ___) \ + V(Allocate, D, lit, ___, ___) \ + V(AllocateT, 0, ___, ___, ___) \ + V(StoreIndexedTOS, 0, ___, ___, ___) \ + V(StoreField, A_B_C, reg, reg, reg) \ + V(StoreFieldTOS, D, num, ___, ___) \ + V(LoadField, A_B_C, reg, reg, reg) \ + V(LoadFieldTOS, D, num, ___, ___) \ + V(BooleanNegateTOS, 0, ___, ___, ___) \ + V(Throw, A, num, ___, ___) \ + V(Entry, A_B_C, num, num, num) \ + V(EntryOpt, A_B_C, num, num, num) \ + V(Frame, D, num, ___, ___) \ + V(SetFrame, A, num, ___, num) \ + V(AllocateContext, D, num, ___, ___) \ + V(CloneContext, 0, ___, ___, ___) \ + V(MoveSpecial, A_D, reg, num, ___) \ + V(InstantiateType, D, lit, ___, ___) \ + V(InstantiateTypeArgumentsTOS, A_D, num, lit, ___) \ + V(AssertAssignable, D, num, lit, ___) \ + V(AssertBoolean, A, num, ___, ___) \ + V(CheckStack, 0, ___, ___, ___) \ + V(DebugStep, 0, ___, ___, ___) \ + V(DebugBreak, A, num, ___, ___) \ + +typedef uint32_t Instr; + +class Bytecode { + public: + enum Opcode { +#define DECLARE_BYTECODE(name, encoding, op1, op2, op3) k##name, +BYTECODES_LIST(DECLARE_BYTECODE) +#undef DECLARE_BYTECODE + }; + + static const intptr_t kOpShift = 0; + static const intptr_t kAShift = 8; + static const intptr_t kAMask = 0xFF; + static const intptr_t kBShift = 16; + static const intptr_t kBMask = 0xFF; + static const intptr_t kCShift = 24; + static const intptr_t kCMask = 0xFF; + static const intptr_t kDShift = 16; + static const intptr_t kDMask = 0xFFFF; + + static Instr Encode(Opcode op, uintptr_t a, uintptr_t b, uintptr_t c) { + ASSERT((a & kAMask) == a); + ASSERT((b & kBMask) == b); + ASSERT((c & kCMask) == c); + return op | (a << kAShift) | (b << kBShift) | (c << kCShift); + } + + static Instr Encode(Opcode op, uintptr_t a, uintptr_t d) { + ASSERT((a & kAMask) == a); + ASSERT((d & kDMask) == d); + return op | (a << kAShift) | (d << kDShift); + } + + static Instr EncodeSigned(Opcode op, uintptr_t a, intptr_t x) { + ASSERT((a & kAMask) == a); + ASSERT((x << kDShift) >> kDShift == x); + return op | (a << kAShift) | (x << kDShift); + } + + static Instr EncodeSigned(Opcode op, intptr_t x) { + ASSERT((x << kAShift) >> kAShift == x); + return op | (x << kAShift); + } + + static Instr Encode(Opcode op) { + return op; + } + + DART_FORCE_INLINE static uint8_t DecodeA(Instr bc) { + return (bc >> kAShift) & kAMask; + } + + DART_FORCE_INLINE static uint16_t DecodeD(Instr bc) { + return (bc >> kDShift) & kDMask; + } + + DART_FORCE_INLINE static Opcode DecodeOpcode(Instr bc) { + return static_cast(bc & 0xFF); + } + + DART_FORCE_INLINE static uint8_t DecodeArgc(Instr call) { +#if defined(DEBUG) + const Opcode op = DecodeOpcode(call); + ASSERT((op == Bytecode::kStaticCall) || + (op == Bytecode::kInstanceCall) || + (op == Bytecode::kInstanceCall2) || + (op == Bytecode::kInstanceCall3) || + (op == Bytecode::kDebugBreak)); +#endif + return (call >> 8) & 0xFF; + } +}; + +// Various dummy declarations to make shared code compile. +// TODO(vegorov) we need to prune away as much dead code as possible instead +// of just making it compile. +typedef int16_t Register; + +const int16_t FPREG = 0; +const int16_t SPREG = 1; +const intptr_t kNumberOfCpuRegisters = 20; +const intptr_t kDartAvailableCpuRegs = 0; +const intptr_t kNoRegister = -1; +const intptr_t kReservedCpuRegisters = 0; +const intptr_t ARGS_DESC_REG = 0; +const intptr_t CODE_REG = 0; +const intptr_t kExceptionObjectReg = 0; +const intptr_t kStackTraceObjectReg = 0; +const intptr_t CTX = 0; + +enum FpuRegister { kNoFpuRegister = -1, kFakeFpuRegister }; +const FpuRegister FpuTMP = kFakeFpuRegister; +const intptr_t kNumberOfFpuRegisters = 1; + +enum Condition { EQ, NE }; + +} // namespace dart + +#endif // VM_CONSTANTS_DBC_H_ diff --git a/runtime/vm/cpu.h b/runtime/vm/cpu.h index 2fffb25d809..3b9348982e0 100644 --- a/runtime/vm/cpu.h +++ b/runtime/vm/cpu.h @@ -33,6 +33,8 @@ class CPU : public AllStatic { #include "vm/cpu_arm64.h" #elif defined(TARGET_ARCH_MIPS) #include "vm/cpu_mips.h" +#elif defined(TARGET_ARCH_DBC) +#include "vm/cpu_dbc.h" #else #error Unknown architecture. #endif diff --git a/runtime/vm/cpu_dbc.cc b/runtime/vm/cpu_dbc.cc new file mode 100644 index 00000000000..8fcff5bb58f --- /dev/null +++ b/runtime/vm/cpu_dbc.cc @@ -0,0 +1,26 @@ +// 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 "vm/globals.h" +#if defined(TARGET_ARCH_DBC) + +#include "vm/cpu.h" + + +namespace dart { + + +void CPU::FlushICache(uword start, uword size) { + // Nothing to do. +} + + +const char* CPU::Id() { + return "dbc"; +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/cpu_dbc.h b/runtime/vm/cpu_dbc.h new file mode 100644 index 00000000000..b437beb36c3 --- /dev/null +++ b/runtime/vm/cpu_dbc.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef VM_CPU_DBC_H_ +#define VM_CPU_DBC_H_ + +#include "vm/allocation.h" +#include "vm/simulator.h" + +namespace dart { + +class HostCPUFeatures: public AllStatic { + public: + static const char* hardware() { return "simdbc"; } +}; + +class TargetCPUFeatures : public AllStatic { + public: + static void InitOnce() {} + static void Cleanup() {} + + static bool double_truncate_round_supported() { + return true; + } +}; + +} // namespace dart + +#endif // VM_CPU_DBC_H_ diff --git a/runtime/vm/cpu_test.cc b/runtime/vm/cpu_test.cc index 6ccdebe38bc..7ffbe6fbac8 100644 --- a/runtime/vm/cpu_test.cc +++ b/runtime/vm/cpu_test.cc @@ -32,6 +32,8 @@ UNIT_TEST_CASE(Id) { #else // defined(HOST_ARCH_MIPS) EXPECT_STREQ("simmips", CPU::Id()); #endif // defined(HOST_ARCH_MIPS) +#elif defined(TARGET_ARCH_DBC) + EXPECT_STREQ("dbc", CPU::Id()); #else #error Architecture was not detected as supported by Dart. #endif diff --git a/runtime/vm/cpuinfo_test.cc b/runtime/vm/cpuinfo_test.cc index 0b6113b75b9..2b587f1a534 100644 --- a/runtime/vm/cpuinfo_test.cc +++ b/runtime/vm/cpuinfo_test.cc @@ -9,11 +9,13 @@ namespace dart { +#if !defined(TARGET_ARCH_DBC) UNIT_TEST_CASE(GetCpuModelTest) { const char* cpumodel = CpuInfo::GetCpuModel(); EXPECT_NE(strlen(cpumodel), 0UL); // caller is responsible for deleting the returned cpumodel string. free(const_cast(cpumodel)); } +#endif } // namespace dart diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc index ea55b49e9f7..643efa1750c 100644 --- a/runtime/vm/dart_entry.cc +++ b/runtime/vm/dart_entry.cc @@ -103,15 +103,22 @@ RawObject* DartEntry::InvokeFunction(const Function& function, } } // Now Call the invoke stub which will invoke the dart function. +#if !defined(TARGET_ARCH_DBC) invokestub entrypoint = reinterpret_cast( StubCode::InvokeDartCode_entry()->EntryPoint()); +#endif const Code& code = Code::Handle(zone, function.CurrentCode()); ASSERT(!code.IsNull()); ASSERT(thread->no_callback_scope_depth() == 0); ScopedIsolateStackLimits stack_limit(thread); SuspendLongJumpScope suspend_long_jump_scope(thread); TransitionToGenerated transition(thread); -#if defined(USING_SIMULATOR) +#if defined(TARGET_ARCH_DBC) + return Simulator::Current()->Call(code, + arguments_descriptor, + arguments, + thread); +#elif defined(USING_SIMULATOR) return bit_copy(Simulator::Current()->Call( reinterpret_cast(entrypoint), reinterpret_cast(&code), diff --git a/runtime/vm/dart_entry.h b/runtime/vm/dart_entry.h index 0f9597f4189..8c530ea6c47 100644 --- a/runtime/vm/dart_entry.h +++ b/runtime/vm/dart_entry.h @@ -86,6 +86,19 @@ class ArgumentsDescriptor : public ValueObject { static RawArray* NewNonCached(intptr_t count, bool canonicalize = true); + // Used by Simulator to parse argument descriptors. + static intptr_t name_index(intptr_t index) { + return kFirstNamedEntryIndex + + (index * kNamedEntrySize) + + kNameOffset; + } + + static intptr_t position_index(intptr_t index) { + return kFirstNamedEntryIndex + + (index * kNamedEntrySize) + + kPositionOffset; + } + const Array& array_; // A cache of VM heap allocated arguments descriptors. @@ -93,6 +106,7 @@ class ArgumentsDescriptor : public ValueObject { friend class SnapshotReader; friend class SnapshotWriter; + friend class Simulator; DISALLOW_COPY_AND_ASSIGN(ArgumentsDescriptor); }; diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index aa665334079..6afad6318c9 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -231,7 +231,9 @@ void Breakpoint::PrintJSON(JSONStream* stream) { void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { visitor->VisitPointer(reinterpret_cast(&code_)); +#if !defined(TARGET_ARCH_DBC) visitor->VisitPointer(reinterpret_cast(&saved_value_)); +#endif } @@ -859,24 +861,25 @@ intptr_t ActivationFrame::NumLocalVariables() { } +DART_FORCE_INLINE static RawObject* GetVariableValue(uword addr) { + return *reinterpret_cast(addr); +} + + RawObject* ActivationFrame::GetParameter(intptr_t index) { intptr_t num_parameters = function().num_fixed_parameters(); ASSERT(0 <= index && index < num_parameters); - intptr_t reverse_index = num_parameters - index; if (function().NumOptionalParameters() > 0) { // If the function has optional parameters, the first positional parameter // can be in a number of places in the caller's frame depending on how many // were actually supplied at the call site, but they are copied to a fixed // place in the callee's frame. - uword var_address = fp() + ((kFirstLocalSlotFromFp - index) * kWordSize); - return reinterpret_cast( - *reinterpret_cast(var_address)); + return GetVariableValue(LocalVarAddress(fp(), + (kFirstLocalSlotFromFp - index))); } else { - uword var_address = fp() + (kParamEndSlotFromFp * kWordSize) - + (reverse_index * kWordSize); - return reinterpret_cast( - *reinterpret_cast(var_address)); + intptr_t reverse_index = num_parameters - index; + return GetVariableValue(ParamAddress(fp(), reverse_index)); } } @@ -889,9 +892,7 @@ RawObject* ActivationFrame::GetClosure() { RawObject* ActivationFrame::GetStackVar(intptr_t slot_index) { if (deopt_frame_.IsNull()) { - uword var_address = fp() + slot_index * kWordSize; - return reinterpret_cast( - *reinterpret_cast(var_address)); + return GetVariableValue(LocalVarAddress(fp(), slot_index)); } else { return deopt_frame_.At(deopt_frame_offset_ + slot_index); } @@ -1171,7 +1172,12 @@ CodeBreakpoint::CodeBreakpoint(const Code& code, bpt_location_(NULL), next_(NULL), breakpoint_kind_(kind), - saved_value_(Code::null()) { +#if !defined(TARGET_ARCH_DBC) + saved_value_(Code::null()) +#else + saved_value_(Bytecode::kTrap) +#endif + { ASSERT(!code.IsNull()); ASSERT(token_pos_.IsReal()); ASSERT(pc_ != 0); @@ -2693,11 +2699,11 @@ RawError* Debugger::DebuggerStepCallback() { if (stepping_fp_ != 0) { // There is an "interesting frame" set. Only pause at appropriate // locations in this frame. - if (stepping_fp_ > frame->fp()) { + if (IsCalleeFrameOf(stepping_fp_, frame->fp())) { // We are i n a callee of the frame we're interested in. // Ignore this stepping break. return Error::null(); - } else if (frame->fp() > stepping_fp_) { + } else if (IsCalleeFrameOf(frame->fp(), stepping_fp_)) { // We returned from the "interesting frame", there can be no more // stepping breaks for it. Pause at the next appropriate location // and let the user set the "interesting" frame again. diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h index 4d00a34100f..ab833391403 100644 --- a/runtime/vm/debugger.h +++ b/runtime/vm/debugger.h @@ -227,7 +227,14 @@ class CodeBreakpoint { CodeBreakpoint* next_; RawPcDescriptors::Kind breakpoint_kind_; +#if !defined(TARGET_ARCH_DBC) RawCode* saved_value_; +#else + // When running on the DBC interpreter we patch bytecode in place with + // DebugBreak. This is an instruction that was replaced. DebugBreak + // will execute it after the breakpoint. + Instr saved_value_; +#endif friend class Debugger; DISALLOW_COPY_AND_ASSIGN(CodeBreakpoint); diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc index 0be2d29e1dc..80311f1169d 100644 --- a/runtime/vm/debugger_api_impl_test.cc +++ b/runtime/vm/debugger_api_impl_test.cc @@ -282,8 +282,13 @@ static void VerifyStackTrace(Dart_StackTrace trace, res = Dart_ActivationFrameGetFramePointer(frame, &frame_pointer); EXPECT_TRUE(res); if (i > 0) { +#if !defined(TARGET_ARCH_DBC) // We expect the stack to grow from high to low addresses. EXPECT_GT(frame_pointer, last_frame_pointer); +#else + // On DBC stack grows upwards from low to high addresses. + EXPECT_LT(frame_pointer, last_frame_pointer); +#endif } last_frame_pointer = frame_pointer; if (i < expected_frames) { diff --git a/runtime/vm/debugger_dbc.cc b/runtime/vm/debugger_dbc.cc new file mode 100644 index 00000000000..5f82cd5368a --- /dev/null +++ b/runtime/vm/debugger_dbc.cc @@ -0,0 +1,86 @@ +// 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 "vm/globals.h" +#if defined(TARGET_ARCH_DBC) + +#include "vm/code_patcher.h" +#include "vm/cpu.h" +#include "vm/debugger.h" +#include "vm/instructions.h" +#include "vm/stub_code.h" + +namespace dart { + +#ifndef PRODUCT + +RawCode* CodeBreakpoint::OrigStubAddress() const { + return reinterpret_cast(static_cast(saved_value_)); +} + + +static Instr* CallInstructionFromReturnAddress(uword pc) { + return reinterpret_cast(pc) - 1; +} + + +void CodeBreakpoint::PatchCode() { + ASSERT(!is_enabled_); + const Code& code = Code::Handle(code_); + const Instructions& instrs = Instructions::Handle(code.instructions()); + { + WritableInstructionsScope writable(instrs.EntryPoint(), instrs.size()); + saved_value_ = *CallInstructionFromReturnAddress(pc_); + switch (breakpoint_kind_) { + case RawPcDescriptors::kIcCall: + case RawPcDescriptors::kUnoptStaticCall: { + // DebugBreak has an A operand matching the call it replaces. + // This ensures that Return instructions continue to work - as they + // look at calls to figure out how many arguments to drop. + *CallInstructionFromReturnAddress(pc_) = + Bytecode::Encode(Bytecode::kDebugBreak, + Bytecode::DecodeArgc(saved_value_), + 0, + 0); + break; + } + + case RawPcDescriptors::kRuntimeCall: { + *CallInstructionFromReturnAddress(pc_) = Bytecode::kDebugBreak; + break; + } + + default: + UNREACHABLE(); + } + } + is_enabled_ = true; +} + + +void CodeBreakpoint::RestoreCode() { + ASSERT(is_enabled_); + const Code& code = Code::Handle(code_); + const Instructions& instrs = Instructions::Handle(code.instructions()); + { + WritableInstructionsScope writable(instrs.EntryPoint(), instrs.size()); + switch (breakpoint_kind_) { + case RawPcDescriptors::kIcCall: + case RawPcDescriptors::kUnoptStaticCall: + case RawPcDescriptors::kRuntimeCall: { + *CallInstructionFromReturnAddress(pc_) = saved_value_; + break; + } + default: + UNREACHABLE(); + } + } + is_enabled_ = false; +} + +#endif // !PRODUCT + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc index ecef27927e7..614b9765b8a 100644 --- a/runtime/vm/deopt_instructions.cc +++ b/runtime/vm/deopt_instructions.cc @@ -660,12 +660,16 @@ class DeoptPcMarkerInstr : public DeoptInstr { Function& function = Function::Handle(deopt_context->zone()); function ^= deopt_context->ObjectAt(object_table_index_); if (function.IsNull()) { + // There are no deoptimization stubs on DBC. +#if !defined(TARGET_ARCH_DBC) *reinterpret_cast(dest_addr) = deopt_context->is_lazy_deopt() ? StubCode::DeoptimizeLazy_entry()->code() : StubCode::Deoptimize_entry()->code(); +#endif return; } +#if !defined(TARGET_ARCH_DBC) // We don't always have the Code object for the frame's corresponding // unoptimized code as it may have been collected. Use a stub as the pc // marker until we can recreate that Code object during deferred @@ -673,6 +677,7 @@ class DeoptPcMarkerInstr : public DeoptInstr { // a pc marker. *reinterpret_cast(dest_addr) = StubCode::FrameAwaitingMaterialization_entry()->code(); +#endif deopt_context->DeferPcMarkerMaterialization(object_table_index_, dest_addr); } diff --git a/runtime/vm/disassembler_dbc.cc b/runtime/vm/disassembler_dbc.cc new file mode 100644 index 00000000000..85d734dfd3b --- /dev/null +++ b/runtime/vm/disassembler_dbc.cc @@ -0,0 +1,233 @@ +// 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 "vm/disassembler.h" + +#include "vm/globals.h" // Needed here to get TARGET_ARCH_DBC. +#if defined(TARGET_ARCH_DBC) + +#include "platform/assert.h" +#include "vm/constants_dbc.h" +#include "vm/cpu.h" + +namespace dart { + +static const char* kOpcodeNames[] = { +#define BYTECODE_NAME(name, encoding, op1, op2, op3) #name, + BYTECODES_LIST(BYTECODE_NAME) +#undef BYTECODE_NAME +}; + +static const size_t kOpcodeCount = + sizeof(kOpcodeNames) / sizeof(kOpcodeNames[0]); + +typedef void (*BytecodeFormatter)(char* buffer, + intptr_t size, + uword pc, + uint32_t bc); +typedef void (*Fmt)(char** buf, intptr_t* size, uword pc, int32_t value); + + +template +void FormatOperand(char** buf, + intptr_t* size, + const char* fmt, + ValueType value) { + intptr_t written = OS::SNPrint(*buf, *size, fmt, value); + if (written < *size) { + *buf += written; + *size += written; + } else { + *size = -1; + } +} + + +static void Fmt___(char** buf, intptr_t* size, uword pc, int32_t value) {} + + +static void Fmttgt(char** buf, intptr_t* size, uword pc, int32_t value) { + FormatOperand(buf, size, "-> %" Px, pc + (value << 2)); +} + + +static void Fmtlit(char** buf, intptr_t* size, uword pc, int32_t value) { + FormatOperand(buf, size, "k%d", value); +} + + +static void Fmtreg(char** buf, intptr_t* size, uword pc, int32_t value) { + FormatOperand(buf, size, "r%d", value); +} + + +static void Fmtxeg(char** buf, intptr_t* size, uword pc, int32_t value) { + FormatOperand(buf, size, "R(%d)", value); +} + + +static void Fmtnum(char** buf, intptr_t* size, uword pc, int32_t value) { + FormatOperand(buf, size, "#%d", value); +} + + +static void Apply(char** buf, + intptr_t* size, + uword pc, + Fmt fmt, + int32_t value, + const char* suffix) { + if (*size <= 0) { + return; + } + + fmt(buf, size, pc, value); + if (*size > 0) { + FormatOperand(buf, size, "%s", suffix); + } +} + + +static void Format0(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) {} + + +static void FormatT(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t x = static_cast(op) >> 8; + Apply(&buf, &size, pc, op1, x, ""); +} + + +static void FormatA(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t a = (op & 0xFF00) >> 8; + Apply(&buf, &size, pc, op1, a, ""); +} + + +static void FormatA_D(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t a = (op & 0xFF00) >> 8; + const int32_t bc = op >> 16; + Apply(&buf, &size, pc, op1, a, ", "); + Apply(&buf, &size, pc, op2, bc, ""); +} + + +static void FormatA_X(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t a = (op & 0xFF00) >> 8; + const int32_t bc = static_cast(op) >> 16; + Apply(&buf, &size, pc, op1, a, ", "); + Apply(&buf, &size, pc, op2, bc, ""); +} + + +static void FormatX(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t bc = static_cast(op) >> 16; + Apply(&buf, &size, pc, op1, bc, ""); +} + + +static void FormatD(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t bc = op >> 16; + Apply(&buf, &size, pc, op1, bc, ""); +} + + +static void FormatA_B_C(char* buf, + intptr_t size, + uword pc, + uint32_t op, + Fmt op1, + Fmt op2, + Fmt op3) { + const int32_t a = (op >> 8) & 0xFF; + const int32_t b = (op >> 16) & 0xFF; + const int32_t c = (op >> 24) & 0xFF; + Apply(&buf, &size, pc, op1, a, ", "); + Apply(&buf, &size, pc, op2, b, ", "); + Apply(&buf, &size, pc, op3, c, ""); +} + + +#define BYTECODE_FORMATTER(name, encoding, op1, op2, op3) \ + static void Format##name(char* buf, intptr_t size, uword pc, uint32_t op) { \ + Format##encoding(buf, size, pc, op, Fmt##op1, Fmt##op2, Fmt##op3); \ + } +BYTECODES_LIST(BYTECODE_FORMATTER) +#undef BYTECODE_FORMATTER + + +static const BytecodeFormatter kFormatters[] = { +#define BYTECODE_FORMATTER(name, encoding, op1, op2, op3) &Format##name, + BYTECODES_LIST(BYTECODE_FORMATTER) +#undef BYTECODE_FORMATTER +}; + + +void Disassembler::DecodeInstruction(char* hex_buffer, + intptr_t hex_size, + char* human_buffer, + intptr_t human_size, + int* out_instr_size, + uword pc) { + const uint32_t instr = *reinterpret_cast(pc); + const uint8_t opcode = instr & 0xFF; + ASSERT(opcode < kOpcodeCount); + size_t name_size = + OS::SNPrint(human_buffer, human_size, "%-10s\t", kOpcodeNames[opcode]); + + human_buffer += name_size; + human_size -= name_size; + kFormatters[opcode](human_buffer, human_size, pc, instr); + + OS::SNPrint(hex_buffer, hex_size, "%08x", instr); + if (out_instr_size) { + *out_instr_size = sizeof(uint32_t); + } +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/disassembler_test.cc b/runtime/vm/disassembler_test.cc index d07e174e138..8df643e3572 100644 --- a/runtime/vm/disassembler_test.cc +++ b/runtime/vm/disassembler_test.cc @@ -9,7 +9,9 @@ namespace dart { -#ifndef PRODUCT +// TODO(vegorov) this test is disabled on DBC because there is no PopRegister +// method on DBC assembler. +#if !defined(PRODUCT) && !defined(TARGET_ARCH_DBC) TEST_CASE(Disassembler) { Assembler assembler; diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h index dc7f4b228bb..84e59fcdc87 100644 --- a/runtime/vm/flag_list.h +++ b/runtime/vm/flag_list.h @@ -5,6 +5,12 @@ #ifndef VM_FLAG_LIST_H_ #define VM_FLAG_LIST_H_ +#if defined(TARGET_ARCH_DBC) +#define USING_DBC 1 +#else +#define USING_DBC 0 +#endif + // List of all flags in the VM. // Flags can be one of three categories: // * P roduct flags: Can be set in any of the deployment modes, including in @@ -70,9 +76,9 @@ P(getter_setter_ratio, int, 13, \ "Ratio of getter/setter usage used for double field unboxing heuristics") \ P(guess_icdata_cid, bool, true, \ "Artificially create type feedback for arithmetic etc. operations") \ -P(ic_range_profiling, bool, true, \ +P(ic_range_profiling, bool, !USING_DBC, \ "Generate special IC stubs collecting range information ") \ -P(interpret_irregexp, bool, false, \ +P(interpret_irregexp, bool, USING_DBC, \ "Use irregexp bytecode interpreter") \ P(lazy_dispatchers, bool, true, \ "Generate dispatchers lazily") \ @@ -119,7 +125,7 @@ R(print_ssa_liveranges, false, bool, false, \ "Print live ranges after allocation.") \ C(print_stop_message, false, false, bool, false, \ "Print stop message.") \ -R(profiler, false, bool, true, \ +R(profiler, false, bool, !USING_DBC, \ "Enable the profiler.") \ P(reorder_basic_blocks, bool, true, \ "Reorder basic blocks") \ @@ -159,7 +165,7 @@ P(truncating_left_shift, bool, true, \ "Optimize left shift to truncate if possible") \ P(use_cha_deopt, bool, true, \ "Use class hierarchy analysis even if it can cause deoptimization.") \ -P(use_field_guards, bool, true, \ +P(use_field_guards, bool, !USING_DBC, \ "Use field guards and track field types") \ C(use_osr, false, true, bool, true, \ "Use OSR") \ diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc index 1f42817473f..a4214773d4e 100644 --- a/runtime/vm/flow_graph_builder.cc +++ b/runtime/vm/flow_graph_builder.cc @@ -3499,6 +3499,8 @@ void EffectGraphVisitor::VisitNativeBodyNode(NativeBodyNode* node) { load->set_is_immutable(kind != MethodRecognizer::kGrowableArrayLength); return ReturnDefinition(load); } +#if !defined(TARGET_ARCH_DBC) + // TODO(vegorov) add bytecode to support this method. case MethodRecognizer::kClassIDgetID: { LocalVariable* value_var = node->scope()->LookupVariable(Symbols::Value(), true); @@ -3506,6 +3508,7 @@ void EffectGraphVisitor::VisitNativeBodyNode(NativeBodyNode* node) { LoadClassIdInstr* load = new(Z) LoadClassIdInstr(value); return ReturnDefinition(load); } +#endif case MethodRecognizer::kGrowableArrayCapacity: { Value* receiver = Bind(BuildLoadThisVar(node->scope(), token_pos)); LoadFieldInstr* data_load = new(Z) LoadFieldInstr( diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc index d28c293506b..e64c11589f8 100644 --- a/runtime/vm/flow_graph_compiler.cc +++ b/runtime/vm/flow_graph_compiler.cc @@ -503,7 +503,7 @@ void FlowGraphCompiler::VisitBlocks() { continue; } -#if defined(DEBUG) +#if defined(DEBUG) && !defined(TARGET_ARCH_DBC) if (!is_optimizing()) { FrameStateClear(); } @@ -557,14 +557,14 @@ void FlowGraphCompiler::VisitBlocks() { EndCodeSourceRange(instr->token_pos()); } -#if defined(DEBUG) +#if defined(DEBUG) && !defined(TARGET_ARCH_DBC) if (!is_optimizing()) { FrameStateUpdateWith(instr); } #endif } -#if defined(DEBUG) +#if defined(DEBUG) && !defined(TARGET_ARCH_DBC) ASSERT(is_optimizing() || FrameStateIsSafeToCall()); #endif } @@ -1147,6 +1147,9 @@ bool FlowGraphCompiler::TryIntrinsify() { } +// DBC is very different from other architectures in how it performs instance +// and static calls because it does not use stubs. +#if !defined(TARGET_ARCH_DBC) void FlowGraphCompiler::GenerateInstanceCall( intptr_t deopt_id, TokenPosition token_pos, @@ -1286,7 +1289,7 @@ void FlowGraphCompiler::GenerateListTypeCheck(Register kClassIdReg, CheckClassIds(kClassIdReg, args, is_instance_lbl, &unknown); assembler()->Bind(&unknown); } - +#endif // !defined(TARGET_ARCH_DBC) void FlowGraphCompiler::EmitComment(Instruction* instr) { if (!FLAG_support_il_printer || !FLAG_support_disassembler) { @@ -1301,6 +1304,8 @@ void FlowGraphCompiler::EmitComment(Instruction* instr) { } +#if !defined(TARGET_ARCH_DBC) +// TODO(vegorov) enable edge-counters on DBC if we consider them beneficial. bool FlowGraphCompiler::NeedsEdgeCounter(TargetEntryInstr* block) { // Only emit an edge counter if there is not goto at the end of the block, // except for the entry block. @@ -1321,18 +1326,17 @@ static Register AllocateFreeRegister(bool* blocked_registers) { UNREACHABLE(); return kNoRegister; } - - -static uword RegMaskBit(Register reg) { - return ((reg) != kNoRegister) ? (1 << (reg)) : 0; -} +#endif void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) { ASSERT(!is_optimizing()); - instr->InitializeLocationSummary(zone(), false); // Not optimizing. + + // No need to allocate registers based on LocationSummary on DBC as in + // unoptimized mode it's a stack based bytecode just like IR itself. +#if !defined(TARGET_ARCH_DBC) LocationSummary* locs = instr->locs(); bool blocked_registers[kNumberOfCpuRegisters]; @@ -1422,6 +1426,12 @@ void FlowGraphCompiler::AllocateRegistersLocally(Instruction* instr) { } locs->set_out(0, result_location); } +#endif // !defined(TARGET_ARCH_DBC) +} + + +static uword RegMaskBit(Register reg) { + return ((reg) != kNoRegister) ? (1 << (reg)) : 0; } @@ -1838,6 +1848,9 @@ NOT_IN_PRODUCT( } +#if !defined(TARGET_ARCH_DBC) +// DBC emits calls very differently from other architectures due to its +// interpreted nature. void FlowGraphCompiler::EmitPolymorphicInstanceCall( const ICData& ic_data, intptr_t argument_count, @@ -1881,9 +1894,12 @@ void FlowGraphCompiler::EmitPolymorphicInstanceCall( } } } +#endif - -#if defined(DEBUG) +#if defined(DEBUG) && !defined(TARGET_ARCH_DBC) +// TODO(vegorov) re-enable frame state tracking on DBC. It is +// currently disabled because it relies on LocationSummaries and +// we don't use them during unoptimized compilation on DBC. void FlowGraphCompiler::FrameStateUpdateWith(Instruction* instr) { ASSERT(!is_optimizing()); @@ -1954,7 +1970,7 @@ void FlowGraphCompiler::FrameStateClear() { ASSERT(!is_optimizing()); frame_state_.TruncateTo(0); } -#endif +#endif // defined(DEBUG) && !defined(TARGET_ARCH_DBC) } // namespace dart diff --git a/runtime/vm/flow_graph_compiler.h b/runtime/vm/flow_graph_compiler.h index 7c1fa0e4116..586dbdb9bf4 100644 --- a/runtime/vm/flow_graph_compiler.h +++ b/runtime/vm/flow_graph_compiler.h @@ -367,6 +367,15 @@ class FlowGraphCompiler : public ValueObject { // Returns 'true' if regular code generation should be skipped. bool TryIntrinsify(); + void GenerateAssertAssignable(TokenPosition token_pos, + intptr_t deopt_id, + const AbstractType& dst_type, + const String& dst_name, + LocationSummary* locs); + + // DBC emits calls very differently from all other architectures due to its + // interpreted nature. +#if !defined(TARGET_ARCH_DBC) void GenerateRuntimeCall(TokenPosition token_pos, intptr_t deopt_id, const RuntimeEntry& entry, @@ -390,12 +399,6 @@ class FlowGraphCompiler : public ValueObject { LocationSummary* locs, const Function& target); - void GenerateAssertAssignable(TokenPosition token_pos, - intptr_t deopt_id, - const AbstractType& dst_type, - const String& dst_name, - LocationSummary* locs); - void GenerateInstanceOf(TokenPosition token_pos, intptr_t deopt_id, const AbstractType& type, @@ -426,12 +429,6 @@ class FlowGraphCompiler : public ValueObject { void GenerateListTypeCheck(Register kClassIdReg, Label* is_instance_lbl); - void EmitComment(Instruction* instr); - - bool NeedsEdgeCounter(TargetEntryInstr* block); - - void EmitEdgeCounter(intptr_t edge_id); - void EmitOptimizedInstanceCall(const StubEntry& stub_entry, const ICData& ic_data, intptr_t argument_count, @@ -489,8 +486,15 @@ class FlowGraphCompiler : public ValueObject { bool needs_number_check, TokenPosition token_pos); + bool NeedsEdgeCounter(TargetEntryInstr* block); + + void EmitEdgeCounter(intptr_t edge_id); +#endif // !defined(TARGET_ARCH_DBC) + void EmitTrySync(Instruction* instr, intptr_t try_index); + void EmitComment(Instruction* instr); + intptr_t StackSize() const; // Returns assembler label associated with the given block entry. @@ -637,6 +641,9 @@ class FlowGraphCompiler : public ValueObject { LocationSummary* locs, const ICData& ic_data); + // DBC handles type tests differently from all other architectures due + // to its interpreted nature. +#if !defined(TARGET_ARCH_DBC) // Type checking helper methods. void CheckClassIds(Register class_id_reg, const GrowableArray& class_ids, @@ -690,6 +697,7 @@ class FlowGraphCompiler : public ValueObject { void GenerateBoolToJump(Register bool_reg, Label* is_true, Label* is_false); void CopyParameters(); +#endif // !defined(TARGET_ARCH_DBC) void GenerateInlinedGetter(intptr_t offset); void GenerateInlinedSetter(intptr_t offset); @@ -722,7 +730,10 @@ class FlowGraphCompiler : public ValueObject { return stackmap_table_builder_; } -#if defined(DEBUG) + // TODO(vegorov) re-enable frame state tracking on DBC. It is + // currently disabled because it relies on LocationSummaries and + // we don't use them during unoptimized compilation on DBC. +#if defined(DEBUG) && !defined(TARGET_ARCH_DBC) void FrameStateUpdateWith(Instruction* instr); void FrameStatePush(Definition* defn); void FrameStatePop(intptr_t count); diff --git a/runtime/vm/flow_graph_compiler_dbc.cc b/runtime/vm/flow_graph_compiler_dbc.cc new file mode 100644 index 00000000000..2f3ff8e6cee --- /dev/null +++ b/runtime/vm/flow_graph_compiler_dbc.cc @@ -0,0 +1,331 @@ +// 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 "vm/globals.h" // Needed here to get TARGET_ARCH_DBC. +#if defined(TARGET_ARCH_DBC) + +#include "vm/flow_graph_compiler.h" + +#include "vm/ast_printer.h" +#include "vm/compiler.h" +#include "vm/cpu.h" +#include "vm/dart_entry.h" +#include "vm/deopt_instructions.h" +#include "vm/il_printer.h" +#include "vm/instructions.h" +#include "vm/locations.h" +#include "vm/object_store.h" +#include "vm/parser.h" +#include "vm/stack_frame.h" +#include "vm/stub_code.h" +#include "vm/symbols.h" +#include "vm/verified_memory.h" + +namespace dart { + +DEFINE_FLAG(bool, trap_on_deoptimization, false, "Trap on deoptimization."); +DEFINE_FLAG(bool, unbox_mints, true, "Optimize 64-bit integer arithmetic."); +DEFINE_FLAG(bool, unbox_doubles, true, "Optimize double arithmetic."); +DECLARE_FLAG(bool, enable_simd_inline); +DECLARE_FLAG(bool, use_megamorphic_stub); +DECLARE_FLAG(charp, optimization_filter); + +void MegamorphicSlowPath::EmitNativeCode(FlowGraphCompiler* compiler) { + UNIMPLEMENTED(); +} + + +FlowGraphCompiler::~FlowGraphCompiler() { + // BlockInfos are zone-allocated, so their destructors are not called. + // Verify the labels explicitly here. + for (int i = 0; i < block_info_.length(); ++i) { + ASSERT(!block_info_[i]->jump_label()->IsLinked()); + } +} + + +bool FlowGraphCompiler::SupportsUnboxedDoubles() { + return false; +} + + +bool FlowGraphCompiler::SupportsUnboxedMints() { + return false; +} + + +bool FlowGraphCompiler::SupportsUnboxedSimd128() { + return false; +} + + +bool FlowGraphCompiler::SupportsSinCos() { + return false; +} + + +bool FlowGraphCompiler::SupportsHardwareDivision() { + return true; +} + + +bool FlowGraphCompiler::CanConvertUnboxedMintToDouble() { + return false; +} + + +void FlowGraphCompiler::EnterIntrinsicMode() { + ASSERT(!intrinsic_mode()); + intrinsic_mode_ = true; +} + + +void FlowGraphCompiler::ExitIntrinsicMode() { + ASSERT(intrinsic_mode()); + intrinsic_mode_ = false; +} + + +RawTypedData* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler, + DeoptInfoBuilder* builder, + const Array& deopt_table) { + UNIMPLEMENTED(); + return TypedData::null(); +} + + +void CompilerDeoptInfoWithStub::GenerateCode(FlowGraphCompiler* compiler, + intptr_t stub_ix) { + UNIMPLEMENTED(); +} + + +#define __ assembler()-> + + +void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, + intptr_t deopt_id, + const AbstractType& dst_type, + const String& dst_name, + LocationSummary* locs) { + ASSERT(!is_optimizing()); + SubtypeTestCache& test_cache = SubtypeTestCache::Handle(); + if (!dst_type.IsVoidType() && dst_type.IsInstantiated()) { + test_cache = SubtypeTestCache::New(); + } + + __ PushConstant(dst_type); + __ PushConstant(dst_name); + __ AssertAssignable(__ AddConstant(test_cache)); + AddCurrentDescriptor(RawPcDescriptors::kOther, deopt_id, token_pos); +} + + +void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) { + if (!is_optimizing()) { + Definition* defn = instr->AsDefinition(); + if ((defn != NULL) && + (defn->tag() != Instruction::kPushArgument) && + (defn->tag() != Instruction::kStoreIndexed) && + (defn->tag() != Instruction::kStoreStaticField) && + (defn->tag() != Instruction::kStoreLocal) && + (defn->tag() != Instruction::kStoreInstanceField) && + (defn->tag() != Instruction::kDropTemps) && + (defn->tag() != Instruction::kPushTemp) && + !defn->HasTemp()) { + __ Drop1(); + } + } +} + + +void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { + __ Move(0, -(1 + kParamEndSlotFromFp)); + __ LoadField(0, 0, offset / kWordSize); + __ Return(0); +} + + +void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { + __ Move(0, -(2 + kParamEndSlotFromFp)); + __ Move(1, -(1 + kParamEndSlotFromFp)); + __ StoreField(0, offset / kWordSize, 1); + __ LoadConstant(0, Object::Handle()); + __ Return(0); +} + + +void FlowGraphCompiler::EmitFrameEntry() { + const Function& function = parsed_function().function(); + const intptr_t num_fixed_params = function.num_fixed_parameters(); + const int num_opt_pos_params = function.NumOptionalPositionalParameters(); + const int num_opt_named_params = function.NumOptionalNamedParameters(); + const int num_params = + num_fixed_params + num_opt_pos_params + num_opt_named_params; + const bool has_optional_params = (num_opt_pos_params != 0) || + (num_opt_named_params != 0); + const int num_locals = parsed_function().num_stack_locals(); + const intptr_t context_index = + -parsed_function().current_context_var()->index() - 1; + + if (has_optional_params) { + __ EntryOpt(num_fixed_params, num_opt_pos_params, num_opt_named_params); + } else { + __ Entry(num_fixed_params, num_locals, context_index); + } + + if (num_opt_named_params != 0) { + LocalScope* scope = parsed_function().node_sequence()->scope(); + + // Start by alphabetically sorting the names of the optional parameters. + LocalVariable** opt_param = + zone()->Alloc(num_opt_named_params); + int* opt_param_position = zone()->Alloc(num_opt_named_params); + for (int pos = num_fixed_params; pos < num_params; pos++) { + LocalVariable* parameter = scope->VariableAt(pos); + const String& opt_param_name = parameter->name(); + int i = pos - num_fixed_params; + while (--i >= 0) { + LocalVariable* param_i = opt_param[i]; + const intptr_t result = opt_param_name.CompareTo(param_i->name()); + ASSERT(result != 0); + if (result > 0) break; + opt_param[i + 1] = opt_param[i]; + opt_param_position[i + 1] = opt_param_position[i]; + } + opt_param[i + 1] = parameter; + opt_param_position[i + 1] = pos; + } + + for (intptr_t i = 0; i < num_opt_named_params; i++) { + const int param_pos = opt_param_position[i]; + const Instance& value = parsed_function().DefaultParameterValueAt( + param_pos - num_fixed_params); + __ LoadConstant(param_pos, opt_param[i]->name()); + __ LoadConstant(param_pos, value); + } + } else if (num_opt_pos_params != 0) { + for (intptr_t i = 0; i < num_opt_pos_params; i++) { + const Object& value = parsed_function().DefaultParameterValueAt(i); + __ LoadConstant(num_fixed_params + i, value); + } + } + + + ASSERT(num_locals > 0); // There is always at least context_var. + if (has_optional_params) { + ASSERT(!is_optimizing()); + __ Frame(num_locals); // Reserve space for locals. + } + + if (function.IsClosureFunction()) { + Register reg = context_index; + Register closure_reg = reg; + LocalScope* scope = parsed_function().node_sequence()->scope(); + LocalVariable* local = scope->VariableAt(0); + if (local->index() > 0) { + __ Move(reg, -local->index()); + } else { + closure_reg = -local->index() - 1; + } + __ LoadField(reg, closure_reg, Closure::context_offset() / kWordSize); + } else if (has_optional_params) { + __ LoadConstant(context_index, + Object::Handle(isolate()->object_store()->empty_context())); + } +} + + +void FlowGraphCompiler::CompileGraph() { + InitCompiler(); + + if (TryIntrinsify()) { + // Skip regular code generation. + return; + } + + EmitFrameEntry(); + VisitBlocks(); +} + + +#undef __ +#define __ compiler_->assembler()-> + + +void ParallelMoveResolver::EmitMove(int index) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::EmitSwap(int index) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::MoveMemoryToMemory(const Address& dst, + const Address& src) { + UNREACHABLE(); +} + + +void ParallelMoveResolver::StoreObject(const Address& dst, const Object& obj) { + UNREACHABLE(); +} + + +// Do not call or implement this function. Instead, use the form below that +// uses an offset from the frame pointer instead of an Address. +void ParallelMoveResolver::Exchange(Register reg, const Address& mem) { + UNREACHABLE(); +} + + +// Do not call or implement this function. Instead, use the form below that +// uses offsets from the frame pointer instead of Addresses. +void ParallelMoveResolver::Exchange(const Address& mem1, const Address& mem2) { + UNREACHABLE(); +} + + +void ParallelMoveResolver::Exchange(Register reg, + Register base_reg, + intptr_t stack_offset) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::Exchange(Register base_reg1, + intptr_t stack_offset1, + Register base_reg2, + intptr_t stack_offset2) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::SpillScratch(Register reg) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::RestoreScratch(Register reg) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::SpillFpuScratch(FpuRegister reg) { + UNIMPLEMENTED(); +} + + +void ParallelMoveResolver::RestoreFpuScratch(FpuRegister reg) { + UNIMPLEMENTED(); +} + + +#undef __ + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/instructions.h b/runtime/vm/instructions.h index a2716729f3f..2274becb986 100644 --- a/runtime/vm/instructions.h +++ b/runtime/vm/instructions.h @@ -17,6 +17,8 @@ #include "vm/instructions_arm64.h" #elif defined(TARGET_ARCH_MIPS) #include "vm/instructions_mips.h" +#elif defined(TARGET_ARCH_DBC) +#include "vm/instructions_dbc.h" #else #error Unknown architecture. #endif diff --git a/runtime/vm/instructions_dbc.cc b/runtime/vm/instructions_dbc.cc new file mode 100644 index 00000000000..b7b796c51f5 --- /dev/null +++ b/runtime/vm/instructions_dbc.cc @@ -0,0 +1,177 @@ +// 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 "vm/globals.h" // Needed here to get TARGET_ARCH_DBC. +#if defined(TARGET_ARCH_DBC) + +#include "vm/assembler.h" +#include "vm/constants_dbc.h" +#include "vm/cpu.h" +#include "vm/instructions.h" +#include "vm/object.h" + +namespace dart { + +CallPattern::CallPattern(uword pc, const Code& code) + : object_pool_(ObjectPool::Handle(code.GetObjectPool())), + end_(pc), + ic_data_load_end_(0), + target_code_pool_index_(-1), + ic_data_(ICData::Handle()) { + UNIMPLEMENTED(); +} + + +int CallPattern::DeoptCallPatternLengthInInstructions() { + UNIMPLEMENTED(); + return 0; +} + +int CallPattern::DeoptCallPatternLengthInBytes() { + UNIMPLEMENTED(); + return 0; +} + + +NativeCallPattern::NativeCallPattern(uword pc, const Code& code) + : object_pool_(ObjectPool::Handle(code.GetObjectPool())), + end_(pc), + native_function_pool_index_(-1), + target_code_pool_index_(-1) { + UNIMPLEMENTED(); +} + + +RawCode* NativeCallPattern::target() const { + return reinterpret_cast( + object_pool_.ObjectAt(target_code_pool_index_)); +} + + +void NativeCallPattern::set_target(const Code& new_target) const { + object_pool_.SetObjectAt(target_code_pool_index_, new_target); + // No need to flush the instruction cache, since the code is not modified. +} + + +NativeFunction NativeCallPattern::native_function() const { + return reinterpret_cast( + object_pool_.RawValueAt(native_function_pool_index_)); +} + + +void NativeCallPattern::set_native_function(NativeFunction func) const { + object_pool_.SetRawValueAt(native_function_pool_index_, + reinterpret_cast(func)); +} + + +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the loaded object in the output parameters 'reg' and 'obj' +// respectively. +uword InstructionPattern::DecodeLoadObject(uword end, + const ObjectPool& object_pool, + Register* reg, + Object* obj) { + UNIMPLEMENTED(); + return 0; +} + + +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the loaded immediate value in the output parameters 'reg' and 'value' +// respectively. +uword InstructionPattern::DecodeLoadWordImmediate(uword end, + Register* reg, + intptr_t* value) { + UNIMPLEMENTED(); + return 0; +} + + +// Decodes a load sequence ending at 'end' (the last instruction of the load +// sequence is the instruction before the one at end). Returns a pointer to +// the first instruction in the sequence. Returns the register being loaded +// and the index in the pool being read from in the output parameters 'reg' +// and 'index' respectively. +uword InstructionPattern::DecodeLoadWordFromPool(uword end, + Register* reg, + intptr_t* index) { + UNIMPLEMENTED(); + return 0; +} + + +bool DecodeLoadObjectFromPoolOrThread(uword pc, + const Code& code, + Object* obj) { + UNIMPLEMENTED(); + return false; +} + + +RawICData* CallPattern::IcData() { + UNIMPLEMENTED(); + return ICData::null(); +} + + +RawCode* CallPattern::TargetCode() const { + return reinterpret_cast( + object_pool_.ObjectAt(target_code_pool_index_)); +} + + +void CallPattern::SetTargetCode(const Code& target_code) const { + object_pool_.SetObjectAt(target_code_pool_index_, target_code); +} + + +void CallPattern::InsertDeoptCallAt(uword pc, uword target_address) { + UNIMPLEMENTED(); +} + + +SwitchableCallPattern::SwitchableCallPattern(uword pc, const Code& code) + : object_pool_(ObjectPool::Handle(code.GetObjectPool())), + cache_pool_index_(-1), + stub_pool_index_(-1) { + UNIMPLEMENTED(); +} + + +RawObject* SwitchableCallPattern::cache() const { + return reinterpret_cast( + object_pool_.ObjectAt(cache_pool_index_)); +} + + +void SwitchableCallPattern::SetCache(const MegamorphicCache& cache) const { + ASSERT(Object::Handle(object_pool_.ObjectAt(cache_pool_index_)).IsICData()); + object_pool_.SetObjectAt(cache_pool_index_, cache); +} + + +void SwitchableCallPattern::SetLookupStub(const Code& lookup_stub) const { + ASSERT(Object::Handle(object_pool_.ObjectAt(stub_pool_index_)).IsCode()); + object_pool_.SetObjectAt(stub_pool_index_, lookup_stub); +} + + +ReturnPattern::ReturnPattern(uword pc) : pc_(pc) { +} + + +bool ReturnPattern::IsValid() const { + UNIMPLEMENTED(); + return false; +} + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/instructions_dbc.h b/runtime/vm/instructions_dbc.h new file mode 100644 index 00000000000..5eb0c6e5bf8 --- /dev/null +++ b/runtime/vm/instructions_dbc.h @@ -0,0 +1,138 @@ +// 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. +// Classes that describe assembly patterns as used by inline caches. + +#ifndef VM_INSTRUCTIONS_DBC_H_ +#define VM_INSTRUCTIONS_DBC_H_ + +#ifndef VM_INSTRUCTIONS_H_ +#error Do not include instructions_dbc.h directly; use instructions.h instead. +#endif + +#include "vm/constants_dbc.h" +#include "vm/native_entry.h" +#include "vm/object.h" + +namespace dart { + +class InstructionPattern : public AllStatic { + public: + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the loaded object in the output parameters 'reg' and + // 'obj' respectively. + static uword DecodeLoadObject(uword end, + const ObjectPool& object_pool, + Register* reg, + Object* obj); + + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the loaded immediate value in the output parameters + // 'reg' and 'value' respectively. + static uword DecodeLoadWordImmediate(uword end, + Register* reg, + intptr_t* value); + + // Decodes a load sequence ending at 'end' (the last instruction of the + // load sequence is the instruction before the one at end). Returns the + // address of the first instruction in the sequence. Returns the register + // being loaded and the index in the pool being read from in the output + // parameters 'reg' and 'index' respectively. + static uword DecodeLoadWordFromPool(uword end, + Register* reg, + intptr_t* index); +}; + + +class CallPattern : public ValueObject { + public: + CallPattern(uword pc, const Code& code); + + RawICData* IcData(); + + RawCode* TargetCode() const; + void SetTargetCode(const Code& code) const; + + // This constant length is only valid for inserted call patterns used for + // lazy deoptimization. Regular call pattern may vary in length. + static int DeoptCallPatternLengthInBytes(); + static int DeoptCallPatternLengthInInstructions(); + + static void InsertDeoptCallAt(uword pc, uword target_address); + + private: + const ObjectPool& object_pool_; + + uword end_; + uword ic_data_load_end_; + + intptr_t target_code_pool_index_; + ICData& ic_data_; + + DISALLOW_COPY_AND_ASSIGN(CallPattern); +}; + + +class NativeCallPattern : public ValueObject { + public: + NativeCallPattern(uword pc, const Code& code); + + RawCode* target() const; + void set_target(const Code& target) const; + + NativeFunction native_function() const; + void set_native_function(NativeFunction target) const; + + private: + const ObjectPool& object_pool_; + + uword end_; + intptr_t native_function_pool_index_; + intptr_t target_code_pool_index_; + + DISALLOW_COPY_AND_ASSIGN(NativeCallPattern); +}; + + +// Instance call that can switch from an IC call to a megamorphic call +class SwitchableCallPattern : public ValueObject { + public: + SwitchableCallPattern(uword pc, const Code& code); + + RawObject* cache() const; + void SetCache(const MegamorphicCache& cache) const; + void SetLookupStub(const Code& stub) const; + + private: + const ObjectPool& object_pool_; + intptr_t cache_pool_index_; + intptr_t stub_pool_index_; + + DISALLOW_COPY_AND_ASSIGN(SwitchableCallPattern); +}; + + +class ReturnPattern : public ValueObject { + public: + explicit ReturnPattern(uword pc); + + static const int kLengthInBytes = 0; + + int pattern_length_in_bytes() const { + UNIMPLEMENTED(); + return kLengthInBytes; + } + + bool IsValid() const; + + private: + const uword pc_; +}; + +} // namespace dart + +#endif // VM_INSTRUCTIONS_DBC_H_ diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc index c68f2776923..927b7475845 100644 --- a/runtime/vm/intermediate_language.cc +++ b/runtime/vm/intermediate_language.cc @@ -33,7 +33,7 @@ DEFINE_FLAG(bool, propagate_ic_data, true, "Propagate IC data from unoptimized to optimized IC calls."); DEFINE_FLAG(bool, two_args_smi_icd, true, "Generate special IC stubs for two args Smi operations"); -DEFINE_FLAG(bool, unbox_numeric_fields, true, +DEFINE_FLAG(bool, unbox_numeric_fields, !USING_DBC, "Support unboxed double and float32x4 fields."); DECLARE_FLAG(bool, eliminate_type_checks); DECLARE_FLAG(bool, support_externalizable_strings); @@ -2766,9 +2766,14 @@ LocationSummary* TargetEntryInstr::MakeLocationSummary(Zone* zone, void TargetEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { __ Bind(compiler->GetJumpLabel(this)); if (!compiler->is_optimizing()) { +#if !defined(TARGET_ARCH_DBC) + // TODO(vegorov) re-enable edge counters on DBC if we consider them + // beneficial for the quality of the optimized bytecode. if (compiler->NeedsEdgeCounter(this)) { compiler->EmitEdgeCounter(preorder_number()); } +#endif + // The deoptimization descriptor points after the edge counter code for // uniformity with ARM and MIPS, where we can reuse pattern matching // code that matches backwards from the end of the pattern. @@ -2968,10 +2973,23 @@ LocationSummary* DropTempsInstr::MakeLocationSummary(Zone* zone, void DropTempsInstr::EmitNativeCode(FlowGraphCompiler* compiler) { +#if defined(TARGET_ARCH_DBC) + // On DBC the action of poping the TOS value and then pushing it + // after all intermediates are poped is folded into a special + // bytecode (DropR). On other architectures this is handled by + // instruction prologue/epilogues. + ASSERT(!compiler->is_optimizing()); + if ((InputCount() != 0) && HasTemp()) { + __ DropR(num_temps()); + } else { + __ Drop(num_temps() + ((InputCount() != 0) ? 1 : 0)); + } +#else ASSERT(!compiler->is_optimizing()); // Assert that register assignment is correct. ASSERT((InputCount() == 0) || (locs()->out(0).reg() == locs()->in(0).reg())); __ Drop(num_temps()); +#endif // defined(TARGET_ARCH_DBC) } @@ -2996,6 +3014,8 @@ LocationSummary* InstanceCallInstr::MakeLocationSummary(Zone* zone, } +// DBC does not use specialized inline cache stubs for smi operations. +#if !defined(TARGET_ARCH_DBC) static const StubEntry* TwoArgsSmiOpInlineCacheEntry(Token::Kind kind) { if (!FLAG_two_args_smi_icd) { return 0; @@ -3007,6 +3027,7 @@ static const StubEntry* TwoArgsSmiOpInlineCacheEntry(Token::Kind kind) { default: return NULL; } } +#endif void InstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { @@ -3023,6 +3044,8 @@ void InstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { } else { call_ic_data = &ICData::ZoneHandle(zone, ic_data()->raw()); } + +#if !defined(TARGET_ARCH_DBC) if (compiler->is_optimizing() && HasICData()) { ASSERT(HasICData()); if (ic_data()->NumberOfUsedChecks() > 0) { @@ -3096,6 +3119,44 @@ void InstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { *call_ic_data); } } +#else + // Emit smi fast path instruction. If fast-path succeeds it skips the next + // instruction otherwise it falls through. + if (function_name().raw() == Symbols::Plus().raw()) { + __ AddTOS(); + } else if (function_name().raw() == Symbols::EqualOperator().raw()) { + __ EqualTOS(); + } else if (function_name().raw() == Symbols::LAngleBracket().raw()) { + __ LessThanTOS(); + } else if (function_name().raw() == Symbols::RAngleBracket().raw()) { + __ GreaterThanTOS(); + } else if (function_name().raw() == Symbols::BitAnd().raw()) { + __ BitAndTOS(); + } else if (function_name().raw() == Symbols::BitOr().raw()) { + __ BitOrTOS(); + } else if (function_name().raw() == Symbols::Star().raw()) { + __ MulTOS(); + } + + const intptr_t call_ic_data_kidx = __ AddConstant(*call_ic_data); + switch (call_ic_data->NumArgsTested()) { + case 1: + __ InstanceCall(ArgumentCount(), call_ic_data_kidx); + break; + case 2: + __ InstanceCall2(ArgumentCount(), call_ic_data_kidx); + break; + case 3: + __ InstanceCall3(ArgumentCount(), call_ic_data_kidx); + break; + default: + UNIMPLEMENTED(); + break; + } + compiler->AddCurrentDescriptor(RawPcDescriptors::kIcCall, + deopt_id(), + token_pos()); +#endif // !defined(TARGET_ARCH_DBC) } @@ -3118,6 +3179,10 @@ bool PolymorphicInstanceCallInstr::HasOnlyDispatcherTargets() const { return true; } + +// DBC does not support optimizing compiler and thus doesn't emit +// PolymorphicInstanceCallInstr. +#if !defined(TARGET_ARCH_DBC) void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { ASSERT(ic_data().NumArgsTested() == 1); if (!with_checks()) { @@ -3141,6 +3206,7 @@ void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { locs(), complete()); } +#endif LocationSummary* StaticCallInstr::MakeLocationSummary(Zone* zone, @@ -3150,6 +3216,7 @@ LocationSummary* StaticCallInstr::MakeLocationSummary(Zone* zone, void StaticCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { +#if !defined(TARGET_ARCH_DBC) const ICData* call_ic_data = NULL; if (!FLAG_propagate_ic_data || !compiler->is_optimizing() || (ic_data() == NULL)) { @@ -3182,6 +3249,20 @@ void StaticCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { argument_names(), locs(), *call_ic_data); +#else + const Array& arguments_descriptor = + (ic_data() == NULL) ? + Array::Handle(ArgumentsDescriptor::New(ArgumentCount(), + argument_names())) : + Array::Handle(ic_data()->arguments_descriptor()); + const intptr_t argdesc_kidx = __ AddConstant(arguments_descriptor); + + __ PushConstant(function()); + __ StaticCall(ArgumentCount(), argdesc_kidx); + compiler->AddCurrentDescriptor(RawPcDescriptors::kUnoptStaticCall, + deopt_id(), + token_pos()); +#endif // !defined(TARGET_ARCH_DBC) } @@ -3191,7 +3272,11 @@ void AssertAssignableInstr::EmitNativeCode(FlowGraphCompiler* compiler) { dst_type(), dst_name(), locs()); + + // DBC does not use LocationSummaries in the same way as other architectures. +#if !defined(TARGET_ARCH_DBC) ASSERT(locs()->in(0).reg() == locs()->out(0).reg()); +#endif } @@ -3302,11 +3387,6 @@ void Environment::DeepCopyToOuter(Zone* zone, Instruction* instr) const { } -static bool BindsToSmiConstant(Value* value) { - return value->BindsToConstant() && value->BoundConstant().IsSmi(); -} - - ComparisonInstr* EqualityCompareInstr::CopyWithNewOperands(Value* new_left, Value* new_right) { return new EqualityCompareInstr(token_pos(), @@ -3374,9 +3454,17 @@ bool TestCidsInstr::AttributesEqual(Instruction* other) const { } +#if !defined(TARGET_ARCH_DBC) +static bool BindsToSmiConstant(Value* value) { + return value->BindsToConstant() && value->BoundConstant().IsSmi(); +} +#endif + + bool IfThenElseInstr::Supports(ComparisonInstr* comparison, Value* v1, Value* v2) { +#if !defined(TARGET_ARCH_DBC) bool is_smi_result = BindsToSmiConstant(v1) && BindsToSmiConstant(v2); if (comparison->IsStrictCompare()) { // Strict comparison with number checks calls a stub and is not supported @@ -3389,6 +3477,9 @@ bool IfThenElseInstr::Supports(ComparisonInstr* comparison, return false; } return is_smi_result; +#else + return false; +#endif // !defined(TARGET_ARCH_DBC) } diff --git a/runtime/vm/intermediate_language_dbc.cc b/runtime/vm/intermediate_language_dbc.cc new file mode 100644 index 00000000000..7f533e697f2 --- /dev/null +++ b/runtime/vm/intermediate_language_dbc.cc @@ -0,0 +1,704 @@ +// 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 "vm/globals.h" // Needed here to get TARGET_ARCH_DBC. +#if defined(TARGET_ARCH_DBC) + +#include "vm/intermediate_language.h" + +#include "vm/cpu.h" +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/flow_graph.h" +#include "vm/flow_graph_compiler.h" +#include "vm/flow_graph_range_analysis.h" +#include "vm/locations.h" +#include "vm/object_store.h" +#include "vm/parser.h" +#include "vm/simulator.h" +#include "vm/stack_frame.h" +#include "vm/stub_code.h" +#include "vm/symbols.h" + +#define __ compiler->assembler()-> + +namespace dart { + +DECLARE_FLAG(bool, allow_absolute_addresses); +DECLARE_FLAG(bool, emit_edge_counters); +DECLARE_FLAG(int, optimization_counter_threshold); + +// List of instructions that are still unimplemented by DBC backend. +#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ + M(Stop) \ + M(IndirectGoto) \ + M(LoadCodeUnits) \ + M(InstanceOf) \ + M(LoadUntagged) \ + M(AllocateUninitializedContext) \ + M(BinaryInt32Op) \ + M(UnarySmiOp) \ + M(UnaryDoubleOp) \ + M(SmiToDouble) \ + M(Int32ToDouble) \ + M(MintToDouble) \ + M(DoubleToInteger) \ + M(DoubleToSmi) \ + M(DoubleToDouble) \ + M(DoubleToFloat) \ + M(FloatToDouble) \ + M(UnboxedConstant) \ + M(CheckEitherNonSmi) \ + M(BinaryDoubleOp) \ + M(MathUnary) \ + M(MathMinMax) \ + M(Box) \ + M(Unbox) \ + M(BoxInt64) \ + M(CaseInsensitiveCompareUC16) \ + M(BinaryMintOp) \ + M(ShiftMintOp) \ + M(UnaryMintOp) \ + M(StringToCharCode) \ + M(StringFromCharCode) \ + M(InvokeMathCFunction) \ + M(MergedMath) \ + M(GuardFieldClass) \ + M(GuardFieldLength) \ + M(IfThenElse) \ + M(BinaryFloat32x4Op) \ + M(Simd32x4Shuffle) \ + M(Simd32x4ShuffleMix) \ + M(Simd32x4GetSignMask) \ + M(Float32x4Constructor) \ + M(Float32x4Zero) \ + M(Float32x4Splat) \ + M(Float32x4Comparison) \ + M(Float32x4MinMax) \ + M(Float32x4Scale) \ + M(Float32x4Sqrt) \ + M(Float32x4ZeroArg) \ + M(Float32x4Clamp) \ + M(Float32x4With) \ + M(Float32x4ToInt32x4) \ + M(Int32x4Constructor) \ + M(Int32x4BoolConstructor) \ + M(Int32x4GetFlag) \ + M(Int32x4Select) \ + M(Int32x4SetFlag) \ + M(Int32x4ToFloat32x4) \ + M(BinaryInt32x4Op) \ + M(TestCids) \ + M(BinaryFloat64x2Op) \ + M(Float64x2Zero) \ + M(Float64x2Constructor) \ + M(Float64x2Splat) \ + M(Float32x4ToFloat64x2) \ + M(Float64x2ToFloat32x4) \ + M(Simd64x2Shuffle) \ + M(Float64x2ZeroArg) \ + M(Float64x2OneArg) \ + M(ExtractNthOutput) \ + M(BinaryUint32Op) \ + M(ShiftUint32Op) \ + M(UnaryUint32Op) \ + M(UnboxedIntConverter) \ + M(GrowRegExpStack) \ + M(BoxInteger32) \ + M(UnboxInteger32) \ + M(CheckedSmiOp) \ + M(CheckArrayBound) \ + M(CheckSmi) \ + M(LoadClassId) \ + M(CheckClassId) \ + M(CheckClass) \ + M(BinarySmiOp) \ + M(TestSmi) \ + M(RelationalOp) \ + M(EqualityCompare) \ + M(LoadIndexed) \ +// Location summaries actually are not used by the unoptimizing DBC compiler +// because we don't allocate any registers. +static LocationSummary* CreateLocationSummary(Zone* zone, + intptr_t num_inputs, + bool has_result) { + const intptr_t kNumTemps = 0; + LocationSummary* locs = new(zone) LocationSummary( + zone, num_inputs, kNumTemps, LocationSummary::kNoCall); + for (intptr_t i = 0; i < num_inputs; i++) { + locs->set_in(i, Location::RequiresRegister()); + } + if (has_result) { + locs->set_out(0, Location::RequiresRegister()); + } + return locs; +} + + +#define DEFINE_MAKE_LOCATION_SUMMARY(Name, In, Out) \ + LocationSummary* Name##Instr::MakeLocationSummary(Zone* zone, bool opt) \ + const { \ + return CreateLocationSummary(zone, In, Out); \ + } \ + +#define EMIT_NATIVE_CODE(Name, In, Out) \ + DEFINE_MAKE_LOCATION_SUMMARY(Name, In, Out); \ + void Name##Instr::EmitNativeCode(FlowGraphCompiler* compiler) \ + +#define DEFINE_UNIMPLEMENTED_MAKE_LOCATION_SUMMARY(Name) \ + LocationSummary* Name##Instr::MakeLocationSummary(Zone* zone, bool opt) \ + const { \ + UNIMPLEMENTED(); \ + return NULL; \ + } \ + +#define DEFINE_UNIMPLEMENTED_EMIT_NATIVE_CODE(Name) \ + void Name##Instr::EmitNativeCode(FlowGraphCompiler* compiler) { \ + UNIMPLEMENTED(); \ + } + +#define DEFINE_UNIMPLEMENTED_EMIT_BRANCH_CODE(Name) \ + void Name##Instr::EmitBranchCode(FlowGraphCompiler*, BranchInstr*) { \ + UNIMPLEMENTED(); \ + } \ + Condition Name##Instr::EmitComparisonCode(FlowGraphCompiler*, \ + BranchLabels) { \ + UNIMPLEMENTED(); \ + return EQ; \ + } + +#define DEFINE_UNIMPLEMENTED(Name) \ + DEFINE_UNIMPLEMENTED_MAKE_LOCATION_SUMMARY(Name) \ + DEFINE_UNIMPLEMENTED_EMIT_NATIVE_CODE(Name) \ + +FOR_EACH_UNIMPLEMENTED_INSTRUCTION(DEFINE_UNIMPLEMENTED) + +#undef DEFINE_UNIMPLEMENTED + +DEFINE_UNIMPLEMENTED_EMIT_BRANCH_CODE(TestCids) +DEFINE_UNIMPLEMENTED_EMIT_BRANCH_CODE(TestSmi) +DEFINE_UNIMPLEMENTED_EMIT_BRANCH_CODE(RelationalOp) +DEFINE_UNIMPLEMENTED_EMIT_BRANCH_CODE(EqualityCompare) + + +DEFINE_MAKE_LOCATION_SUMMARY(AssertAssignable, 2, true); + + +EMIT_NATIVE_CODE(AssertBoolean, 1, true) { + __ AssertBoolean(Isolate::Current()->type_checks() ? 1 : 0); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + deopt_id(), + token_pos()); +} + + +LocationSummary* PolymorphicInstanceCallInstr::MakeLocationSummary(Zone* zone, + bool optimizing) const { + return MakeCallSummary(zone); +} + + +void PolymorphicInstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + UNIMPLEMENTED(); +} + + +EMIT_NATIVE_CODE(CheckStackOverflow, 0, false) { + __ CheckStack(); + compiler->AddCurrentDescriptor(RawPcDescriptors::kRuntimeCall, + Thread::kNoDeoptId, + token_pos()); +} + + +EMIT_NATIVE_CODE(PushArgument, 1, false) { + if (compiler->is_optimizing()) { + __ Push(locs()->in(0).reg()); + } +} + + +EMIT_NATIVE_CODE(LoadLocal, 0, false) { + ASSERT(!compiler->is_optimizing()); + ASSERT(local().index() != 0); + __ Push((local().index() > 0) ? (-local().index()) : (-local().index() - 1)); +} + + +EMIT_NATIVE_CODE(StoreLocal, 0, false) { + ASSERT(!compiler->is_optimizing()); + ASSERT(local().index() != 0); + if (HasTemp()) { + __ StoreLocal( + (local().index() > 0) ? (-local().index()) : (-local().index() - 1)); + } else { + __ PopLocal( + (local().index() > 0) ? (-local().index()) : (-local().index() - 1)); + } +} + + +EMIT_NATIVE_CODE(Constant, 0, true) { + const intptr_t kidx = __ AddConstant(value()); + if (compiler->is_optimizing()) { + __ LoadConstant(locs()->out(0).reg(), kidx); + } else { + __ PushConstant(kidx); + } +} + + +EMIT_NATIVE_CODE(Return, 1, false) { + __ ReturnTOS(); +} + + +EMIT_NATIVE_CODE(StoreStaticField, 1, false) { + const intptr_t kidx = __ AddConstant(field()); + __ StoreStaticTOS(kidx); +} + + +EMIT_NATIVE_CODE(LoadStaticField, 1, true) { + const intptr_t kidx = __ AddConstant(StaticField()); + __ PushStatic(kidx); +} + + +EMIT_NATIVE_CODE(InitStaticField, 0, false) { + ASSERT(!compiler->is_optimizing()); + __ InitStaticTOS(); +} + + +EMIT_NATIVE_CODE(ClosureCall, 0, false) { + intptr_t argument_count = ArgumentCount(); + const Array& arguments_descriptor = + Array::ZoneHandle(ArgumentsDescriptor::New(argument_count, + argument_names())); + const intptr_t argdesc_kidx = + compiler->assembler()->AddConstant(arguments_descriptor); + __ StaticCall(argument_count, argdesc_kidx); + + compiler->RecordSafepoint(locs()); + // Marks either the continuation point in unoptimized code or the + // deoptimization point in optimized code, after call. + const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id()); + if (compiler->is_optimizing()) { + compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos()); + } + // Add deoptimization continuation point after the call and before the + // arguments are removed. + // In optimized code this descriptor is needed for exception handling. + compiler->AddCurrentDescriptor(RawPcDescriptors::kDeopt, + deopt_id_after, + token_pos()); +} + + +static void EmitBranchOnCondition(FlowGraphCompiler* compiler, + Condition true_condition, + BranchLabels labels) { + if (labels.fall_through == labels.false_label) { + // If the next block is the false successor, fall through to it. + __ Jump(labels.true_label); + } else { + // If the next block is not the false successor, branch to it. + __ Jump(labels.false_label); + + // Fall through or jump to the true successor. + if (labels.fall_through != labels.true_label) { + __ Jump(labels.true_label); + } + } +} + + +Condition StrictCompareInstr::EmitComparisonCode(FlowGraphCompiler* compiler, + BranchLabels labels) { + ASSERT((kind() == Token::kNE_STRICT) || + (kind() == Token::kEQ_STRICT)); + const Bytecode::Opcode eq_op = needs_number_check() ? + Bytecode::kIfEqStrictNumTOS : Bytecode::kIfEqStrictTOS; + const Bytecode::Opcode ne_op = needs_number_check() ? + Bytecode::kIfNeStrictNumTOS : Bytecode::kIfNeStrictTOS; + + if (kind() == Token::kEQ_STRICT) { + __ Emit((labels.fall_through == labels.false_label) ? eq_op : ne_op); + } else { + __ Emit((labels.fall_through == labels.false_label) ? ne_op : eq_op); + } + + if (needs_number_check() && token_pos().IsReal()) { + compiler->AddCurrentDescriptor(RawPcDescriptors::kRuntimeCall, + Thread::kNoDeoptId, + token_pos()); + } + return EQ; +} + + +void StrictCompareInstr::EmitBranchCode(FlowGraphCompiler* compiler, + BranchInstr* branch) { + ASSERT((kind() == Token::kEQ_STRICT) || + (kind() == Token::kNE_STRICT)); + + BranchLabels labels = compiler->CreateBranchLabels(branch); + Condition true_condition = EmitComparisonCode(compiler, labels); + EmitBranchOnCondition(compiler, true_condition, labels); +} + + +EMIT_NATIVE_CODE(StrictCompare, 2, true) { + ASSERT((kind() == Token::kEQ_STRICT) || + (kind() == Token::kNE_STRICT)); + + Label is_true, is_false; + BranchLabels labels = { &is_true, &is_false, &is_false }; + Condition true_condition = EmitComparisonCode(compiler, labels); + EmitBranchOnCondition(compiler, true_condition, labels); + Label done; + __ Bind(&is_false); + __ PushConstant(Bool::False()); + __ Jump(&done); + __ Bind(&is_true); + __ PushConstant(Bool::True()); + __ Bind(&done); +} + + +LocationSummary* BranchInstr::MakeLocationSummary(Zone* zone, + bool opt) const { + comparison()->InitializeLocationSummary(zone, opt); + // Branches don't produce a result. + comparison()->locs()->set_out(0, Location::NoLocation()); + return comparison()->locs(); +} + + +void BranchInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + comparison()->EmitBranchCode(compiler, this); +} + + +EMIT_NATIVE_CODE(Goto, 0, false) { + if (HasParallelMove()) { + compiler->parallel_move_resolver()->EmitNativeCode(parallel_move()); + } + // We can fall through if the successor is the next block in the list. + // Otherwise, we need a jump. + if (!compiler->CanFallThroughTo(successor())) { + __ Jump(compiler->GetJumpLabel(successor())); + } +} + + +EMIT_NATIVE_CODE(CreateArray, 2, true) { + __ CreateArrayTOS(); +} + + +EMIT_NATIVE_CODE(StoreIndexed, 3, false) { + ASSERT(class_id() == kArrayCid); + __ StoreIndexedTOS(); +} + + +EMIT_NATIVE_CODE(StringInterpolate, 0, false) { + const intptr_t kArgumentCount = 1; + const Array& arguments_descriptor = Array::Handle( + ArgumentsDescriptor::New(kArgumentCount, Object::null_array())); + __ PushConstant(CallFunction()); + const intptr_t argdesc_kidx = __ AddConstant(arguments_descriptor); + __ StaticCall(kArgumentCount, argdesc_kidx); +} + + +EMIT_NATIVE_CODE(NativeCall, 0, false) { + SetupNative(); + + const intptr_t argc_tag = NativeArguments::ComputeArgcTag(function()); + + ASSERT(!link_lazily()); + const ExternalLabel label(reinterpret_cast(native_c_function())); + const intptr_t target_kidx = + __ object_pool_wrapper().FindImmediate(label.address()); + const intptr_t argc_tag_kidx = + __ object_pool_wrapper().FindImmediate(static_cast(argc_tag)); + __ PushConstant(target_kidx); + __ PushConstant(argc_tag_kidx); + if (is_bootstrap_native()) { + __ NativeBootstrapCall(); + } else { + __ NativeCall(); + } + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + Thread::kNoDeoptId, + token_pos()); +} + + +EMIT_NATIVE_CODE(AllocateObject, 0, true) { + if (ArgumentCount() == 1) { + __ PushConstant(cls()); + __ AllocateT(); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + Thread::kNoDeoptId, + token_pos()); + } else { + const intptr_t kidx = __ AddConstant(cls()); + __ Allocate(kidx); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + Thread::kNoDeoptId, + token_pos()); + } +} + + +EMIT_NATIVE_CODE(StoreInstanceField, 2, false) { + ASSERT(!HasTemp()); + ASSERT(offset_in_bytes() % kWordSize == 0); + if (compiler->is_optimizing()) { + const Register value = locs()->in(1).reg(); + const Register instance = locs()->in(0).reg(); + __ StoreField(instance, offset_in_bytes() / kWordSize, value); + } else { + __ StoreFieldTOS(offset_in_bytes() / kWordSize); + } +} + + +EMIT_NATIVE_CODE(LoadField, 1, true) { + ASSERT(offset_in_bytes() % kWordSize == 0); + __ LoadFieldTOS(offset_in_bytes() / kWordSize); +} + + +EMIT_NATIVE_CODE(BooleanNegate, 1, true) { + __ BooleanNegateTOS(); +} + + +EMIT_NATIVE_CODE(AllocateContext, 0, false) { + __ AllocateContext(num_context_variables()); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + Thread::kNoDeoptId, + token_pos()); +} + + +EMIT_NATIVE_CODE(CloneContext, 0, false) { + __ CloneContext(); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + Thread::kNoDeoptId, + token_pos()); +} + + +EMIT_NATIVE_CODE(CatchBlockEntry, 0, false) { + __ Bind(compiler->GetJumpLabel(this)); + compiler->AddExceptionHandler(catch_try_index(), + try_index(), + compiler->assembler()->CodeSize(), + catch_handler_types_, + needs_stacktrace()); + __ MoveSpecial(-exception_var().index()-1, + Simulator::kExceptionSpecialIndex); + __ MoveSpecial(-stacktrace_var().index()-1, + Simulator::kStacktraceSpecialIndex); + __ SetFrame(compiler->StackSize()); +} + + +EMIT_NATIVE_CODE(Throw, 0, false) { + __ Throw(0); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + deopt_id(), + token_pos()); + __ Trap(); +} + + +EMIT_NATIVE_CODE(ReThrow, 0, false) { + compiler->SetNeedsStacktrace(catch_try_index()); + __ Throw(1); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + deopt_id(), + token_pos()); + __ Trap(); +} + +EMIT_NATIVE_CODE(InstantiateType, 1, true) { + __ InstantiateType(__ AddConstant(type())); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + deopt_id(), + token_pos()); +} + +EMIT_NATIVE_CODE(InstantiateTypeArguments, 1, true) { + __ InstantiateTypeArgumentsTOS( + type_arguments().IsRawInstantiatedRaw(type_arguments().Length()), + __ AddConstant(type_arguments())); + compiler->AddCurrentDescriptor(RawPcDescriptors::kOther, + deopt_id(), + token_pos()); +} + + +void DebugStepCheckInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + __ DebugStep(); + compiler->AddCurrentDescriptor(stub_kind_, Thread::kNoDeoptId, token_pos()); +} + + +void GraphEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) { + if (!compiler->CanFallThroughTo(normal_entry())) { + __ Jump(compiler->GetJumpLabel(normal_entry())); + } +} + + +LocationSummary* Instruction::MakeCallSummary(Zone* zone) { + LocationSummary* result = new(zone) LocationSummary( + zone, 0, 0, LocationSummary::kCall); + result->set_out(0, Location::RequiresRegister()); + return result; +} + + +CompileType BinaryUint32OpInstr::ComputeType() const { + return CompileType::Int(); +} + + +CompileType ShiftUint32OpInstr::ComputeType() const { + return CompileType::Int(); +} + + +CompileType UnaryUint32OpInstr::ComputeType() const { + return CompileType::Int(); +} + + +static const intptr_t kMintShiftCountLimit = 63; + + +bool ShiftMintOpInstr::has_shift_count_check() const { + return !RangeUtils::IsWithin( + right()->definition()->range(), 0, kMintShiftCountLimit); +} + + +CompileType LoadIndexedInstr::ComputeType() const { + switch (class_id_) { + case kArrayCid: + case kImmutableArrayCid: + return CompileType::Dynamic(); + + case kTypedDataFloat32ArrayCid: + case kTypedDataFloat64ArrayCid: + return CompileType::FromCid(kDoubleCid); + case kTypedDataFloat32x4ArrayCid: + return CompileType::FromCid(kFloat32x4Cid); + case kTypedDataInt32x4ArrayCid: + return CompileType::FromCid(kInt32x4Cid); + case kTypedDataFloat64x2ArrayCid: + return CompileType::FromCid(kFloat64x2Cid); + + case kTypedDataInt8ArrayCid: + case kTypedDataUint8ArrayCid: + case kTypedDataUint8ClampedArrayCid: + case kExternalTypedDataUint8ArrayCid: + case kExternalTypedDataUint8ClampedArrayCid: + case kTypedDataInt16ArrayCid: + case kTypedDataUint16ArrayCid: + case kOneByteStringCid: + case kTwoByteStringCid: + return CompileType::FromCid(kSmiCid); + + case kTypedDataInt32ArrayCid: + case kTypedDataUint32ArrayCid: + return CompileType::Int(); + + default: + UNREACHABLE(); + return CompileType::Dynamic(); + } +} + + +Representation LoadIndexedInstr::representation() const { + switch (class_id_) { + case kArrayCid: + case kImmutableArrayCid: + case kTypedDataInt8ArrayCid: + case kTypedDataUint8ArrayCid: + case kTypedDataUint8ClampedArrayCid: + case kExternalTypedDataUint8ArrayCid: + case kExternalTypedDataUint8ClampedArrayCid: + case kTypedDataInt16ArrayCid: + case kTypedDataUint16ArrayCid: + case kOneByteStringCid: + case kTwoByteStringCid: + return kTagged; + case kTypedDataInt32ArrayCid: + return kUnboxedInt32; + case kTypedDataUint32ArrayCid: + return kUnboxedUint32; + case kTypedDataFloat32ArrayCid: + case kTypedDataFloat64ArrayCid: + return kUnboxedDouble; + case kTypedDataInt32x4ArrayCid: + return kUnboxedInt32x4; + case kTypedDataFloat32x4ArrayCid: + return kUnboxedFloat32x4; + case kTypedDataFloat64x2ArrayCid: + return kUnboxedFloat64x2; + default: + UNREACHABLE(); + return kTagged; + } +} + + +Representation StoreIndexedInstr::RequiredInputRepresentation( + intptr_t idx) const { + // Array can be a Dart object or a pointer to external data. + if (idx == 0) return kNoRepresentation; // Flexible input representation. + if (idx == 1) return kTagged; // Index is a smi. + ASSERT(idx == 2); + switch (class_id_) { + case kArrayCid: + case kOneByteStringCid: + case kTypedDataInt8ArrayCid: + case kTypedDataUint8ArrayCid: + case kExternalTypedDataUint8ArrayCid: + case kTypedDataUint8ClampedArrayCid: + case kExternalTypedDataUint8ClampedArrayCid: + case kTypedDataInt16ArrayCid: + case kTypedDataUint16ArrayCid: + return kTagged; + case kTypedDataInt32ArrayCid: + return kUnboxedInt32; + case kTypedDataUint32ArrayCid: + return kUnboxedUint32; + case kTypedDataFloat32ArrayCid: + case kTypedDataFloat64ArrayCid: + return kUnboxedDouble; + case kTypedDataFloat32x4ArrayCid: + return kUnboxedFloat32x4; + case kTypedDataInt32x4ArrayCid: + return kUnboxedInt32x4; + case kTypedDataFloat64x2ArrayCid: + return kUnboxedFloat64x2; + default: + UNREACHABLE(); + return kTagged; + } +} + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/intrinsifier.cc b/runtime/vm/intrinsifier.cc index f42d85e3e66..5615e58a699 100644 --- a/runtime/vm/intrinsifier.cc +++ b/runtime/vm/intrinsifier.cc @@ -119,6 +119,9 @@ void Intrinsifier::InitializeState() { } #endif // defined(DART_NO_SNAPSHOT). + +// DBC does not use graph intrinsics. +#if !defined(TARGET_ARCH_DBC) static void EmitCodeFor(FlowGraphCompiler* compiler, FlowGraph* graph) { // The FlowGraph here is constructed by the intrinsics builder methods, and @@ -154,10 +157,12 @@ static void EmitCodeFor(FlowGraphCompiler* compiler, } compiler->assembler()->Comment("Graph intrinsic end"); } +#endif bool Intrinsifier::GraphIntrinsify(const ParsedFunction& parsed_function, FlowGraphCompiler* compiler) { +#if !defined(TARGET_ARCH_DBC) ZoneGrowableArray* ic_data_array = new ZoneGrowableArray(); FlowGraphBuilder builder(parsed_function, @@ -204,6 +209,9 @@ bool Intrinsifier::GraphIntrinsify(const ParsedFunction& parsed_function, } EmitCodeFor(compiler, graph); return true; +#else + return false; +#endif // !defined(TARGET_ARCH_DBC) } @@ -235,10 +243,22 @@ void Intrinsifier::Intrinsify(const ParsedFunction& parsed_function, default: break; } + + // On DBC all graph intrinsics are handled in the same way as non-graph + // intrinsics. +#if defined(TARGET_ARCH_DBC) + switch (function.recognized_kind()) { + GRAPH_INTRINSICS_LIST(EMIT_CASE) + default: + break; + } +#endif + #undef EMIT_INTRINSIC } +#if !defined(TARGET_ARCH_DBC) static intptr_t CidForRepresentation(Representation rep) { switch (rep) { case kUnboxedDouble: @@ -1138,5 +1158,7 @@ bool Intrinsifier::Build_DoubleRound(FlowGraph* flow_graph) { return BuildInvokeMathCFunction(&builder, MethodRecognizer::kDoubleRound); } +#endif // !defined(TARGET_ARCH_DBC) + } // namespace dart diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h index 1a7e07a178a..54eec6ebae4 100644 --- a/runtime/vm/intrinsifier.h +++ b/runtime/vm/intrinsifier.h @@ -42,15 +42,21 @@ class Intrinsifier : public AllStatic { static void enum_name(Assembler* assembler); ALL_INTRINSICS_LIST(DECLARE_FUNCTION) +#if defined(TARGET_ARCH_DBC) + // On DBC graph intrinsics are handled in the same way as non-graph ones. + GRAPH_INTRINSICS_LIST(DECLARE_FUNCTION) +#endif #undef DECLARE_FUNCTION +#if !defined(TARGET_ARCH_DBC) #define DECLARE_FUNCTION(test_class_name, test_function_name, enum_name, fp) \ static bool Build_##enum_name(FlowGraph* flow_graph); GRAPH_INTRINSICS_LIST(DECLARE_FUNCTION) #undef DECLARE_FUNCTION +#endif }; } // namespace dart diff --git a/runtime/vm/intrinsifier_dbc.cc b/runtime/vm/intrinsifier_dbc.cc new file mode 100644 index 00000000000..21a3ae05101 --- /dev/null +++ b/runtime/vm/intrinsifier_dbc.cc @@ -0,0 +1,39 @@ +// 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 "vm/globals.h" // Needed here to get TARGET_ARCH_DBC. +#if defined(TARGET_ARCH_DBC) + +#include "vm/intrinsifier.h" + +#include "vm/assembler.h" +#include "vm/cpu.h" +#include "vm/dart_entry.h" +#include "vm/flow_graph_compiler.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/regexp_assembler.h" +#include "vm/symbols.h" +#include "vm/simulator.h" + +namespace dart { + +DECLARE_FLAG(bool, interpret_irregexp); + +intptr_t Intrinsifier::ParameterSlotFromSp() { return -1; } + +#define DEFINE_FUNCTION(test_class_name, test_function_name, enum_name, fp) \ + void Intrinsifier::enum_name(Assembler* assembler) { \ + if (Simulator::IsSupportedIntrinsic(Simulator::k##enum_name##Intrinsic)) { \ + assembler->Intrinsic(Simulator::k##enum_name##Intrinsic); \ + } \ + } \ + +ALL_INTRINSICS_LIST(DEFINE_FUNCTION) +GRAPH_INTRINSICS_LIST(DEFINE_FUNCTION) +#undef DEFINE_FUNCTION + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/locations.cc b/runtime/vm/locations.cc index 8a9499eb3c2..5603a1c7a50 100644 --- a/runtime/vm/locations.cc +++ b/runtime/vm/locations.cc @@ -118,6 +118,8 @@ Location Location::AnyOrConstant(Value* value) { } +// DBC does not have an notion of 'address' in its instruction set. +#if !defined(TARGET_ARCH_DBC) Address Location::ToStackSlotAddress() const { const intptr_t index = stack_index(); const Register base = base_reg(); @@ -134,7 +136,7 @@ Address Location::ToStackSlotAddress() const { return Address(base, index * kWordSize); } } - +#endif intptr_t Location::ToStackSlotOffset() const { const intptr_t index = stack_index(); diff --git a/runtime/vm/locations.h b/runtime/vm/locations.h index 963c4b49a7f..fae87a7f306 100644 --- a/runtime/vm/locations.h +++ b/runtime/vm/locations.h @@ -341,8 +341,11 @@ class Location : public ValueObject { return IsStackSlot() || IsDoubleStackSlot() || IsQuadStackSlot(); } + // DBC does not have an notion of 'address' in its instruction set. +#if !defined(TARGET_ARCH_DBC) // Return a memory operand for stack slot locations. Address ToStackSlotAddress() const; +#endif // Returns the offset from the frame pointer for stack slot locations. intptr_t ToStackSlotOffset() const; @@ -650,9 +653,13 @@ class LocationSummary : public ZoneAllocated { void set_out(intptr_t index, Location loc) { ASSERT(index == 0); + // DBC calls are different from call on other architectures so this + // assert doesn't make sense. +#if !defined(TARGET_ARCH_DBC) ASSERT(!always_calls() || (loc.IsMachineRegister() || loc.IsInvalid() || loc.IsPairLocation())); +#endif output_location_ = loc; } diff --git a/runtime/vm/method_recognizer.h b/runtime/vm/method_recognizer.h index 7efa6e77949..fd2c7ff58d4 100644 --- a/runtime/vm/method_recognizer.h +++ b/runtime/vm/method_recognizer.h @@ -256,7 +256,7 @@ namespace dart { V(Int32x4List, ., TypedData_Int32x4Array_factory, 504220232) \ V(Float64x2List, ., TypedData_Float64x2Array_factory, 416019673) \ -#define GRAPH_TYPED_DATA_INTRINSICS_LIST(V) \ +#define GRAPH_TYPED_DATA_INTRINSICS_LIST(V) \ V(Uint8List, [], Uint8ArrayGetIndexed, 513704632) \ V(Uint8List, []=, Uint8ArraySetIndexed, 2123520783) \ V(_ExternalUint8Array, [], ExternalUint8ArrayGetIndexed, 513704632) \ @@ -310,7 +310,6 @@ namespace dart { MATH_LIB_INTRINSIC_LIST(V) \ TYPED_DATA_LIB_INTRINSIC_LIST(V) \ - #define ALL_INTRINSICS_LIST(V) \ ALL_INTRINSICS_NO_INTEGER_LIB_LIST(V) \ CORE_INTEGER_LIB_INTRINSIC_LIST(V) diff --git a/runtime/vm/native_arguments.h b/runtime/vm/native_arguments.h index 88bc4113561..7035a50b53c 100644 --- a/runtime/vm/native_arguments.h +++ b/runtime/vm/native_arguments.h @@ -25,7 +25,10 @@ class Thread; #if defined(TESTING) || defined(DEBUG) -#if defined(USING_SIMULATOR) +#if defined(TARGET_ARCH_DBC) +// C-stack is always aligned on DBC because we don't have any native code. +#define CHECK_STACK_ALIGNMENT +#elif defined(USING_SIMULATOR) #define CHECK_STACK_ALIGNMENT { \ uword current_sp = Simulator::Current()->get_register(SPREG); \ ASSERT(Utils::IsAligned(current_sp, OS::ActivationFrameAlignment())); \ @@ -92,7 +95,13 @@ class NativeArguments { RawObject* ArgAt(int index) const { ASSERT((index >= 0) && (index < ArgCount())); - RawObject** arg_ptr = &((*argv_)[-index]); +#if defined(TARGET_ARCH_DBC) + // On DBC stack is growing upwards, in reverse direction from all other + // architectures. + RawObject** arg_ptr = &(argv_[index]); +#else + RawObject** arg_ptr = &(argv_[-index]); +#endif // Tell MemorySanitizer the RawObject* was initialized (by generated code). MSAN_UNPOISON(arg_ptr, kWordSize); return *arg_ptr; @@ -204,6 +213,16 @@ class NativeArguments { friend class BootstrapNatives; friend class Simulator; +#if defined(TARGET_ARCH_DBC) + // Allow simulator to create NativeArguments on the stack. + NativeArguments(Thread* thread, + int argc_tag, + RawObject** argv, + RawObject** retval) + : thread_(thread), argc_tag_(argc_tag), argv_(argv), retval_(retval) { + } +#endif + // Since this function is passed a RawObject directly, we need to be // exceedingly careful when we use it. If there are any other side // effects in the statement that may cause GC, it could lead to @@ -235,7 +254,7 @@ class NativeArguments { Thread* thread_; // Current thread pointer. intptr_t argc_tag_; // Encodes argument count and invoked native call type. - RawObject*(*argv_)[]; // Pointer to an array of arguments to runtime call. + RawObject** argv_; // Pointer to an array of arguments to runtime call. RawObject** retval_; // Pointer to the return value area. }; diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc index a5c79d3731a..e1e7e45e7f7 100644 --- a/runtime/vm/native_entry.cc +++ b/runtime/vm/native_entry.cc @@ -90,7 +90,8 @@ const uint8_t* NativeEntry::ResolveSymbol(uword pc) { uword NativeEntry::NativeCallWrapperEntry() { uword entry = reinterpret_cast(NativeEntry::NativeCallWrapper); -#if defined(USING_SIMULATOR) +#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC) + // DBC does not use redirections unlike other simulators. entry = Simulator::RedirectExternalReference( entry, Simulator::kNativeCall, NativeEntry::kNumCallWrapperArguments); #endif @@ -177,6 +178,8 @@ void NativeEntry::NativeCallWrapperNoStackCheck(Dart_NativeArguments args, } +// DBC does not support lazy native call linking. +#if !defined(TARGET_ARCH_DBC) static NativeFunction ResolveNativeFunction(Zone* zone, const Function& func, bool* is_bootstrap_native) { @@ -260,9 +263,10 @@ void NativeEntry::LinkNativeCall(Dart_NativeArguments args) { #endif call_through_wrapper = !is_bootstrap_native; - const Code& trampoline = Code::Handle(call_through_wrapper ? - StubCode::CallNativeCFunction_entry()->code() : - StubCode::CallBootstrapCFunction_entry()->code()); + const Code& trampoline = + Code::Handle(call_through_wrapper ? + StubCode::CallNativeCFunction_entry()->code() : + StubCode::CallBootstrapCFunction_entry()->code()); NativeFunction patch_target_function = target_function; #if defined(USING_SIMULATOR) @@ -295,6 +299,7 @@ void NativeEntry::LinkNativeCall(Dart_NativeArguments args) { target_function(arguments); } } +#endif // !defined(TARGET_ARCH_DBC) } // namespace dart diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h index 9557073c974..4be2cc3917d 100644 --- a/runtime/vm/native_entry.h +++ b/runtime/vm/native_entry.h @@ -118,8 +118,11 @@ class NativeEntry : public AllStatic { static void NativeCallWrapper(Dart_NativeArguments args, Dart_NativeFunction func); + // DBC does not support lazy native call linking. +#if !defined(TARGET_ARCH_DBC) static uword LinkNativeCallEntry(); static void LinkNativeCall(Dart_NativeArguments args); +#endif private: static void NativeCallWrapperNoStackCheck(Dart_NativeArguments args, diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index c7ca37337d6..7f293d10fc9 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -13825,6 +13825,7 @@ void Code::DisableDartCode() const { void Code::DisableStubCode() const { +#if !defined(TARGET_ARCH_DBC) ASSERT(Thread::Current()->IsMutatorThread()); ASSERT(IsAllocationStubCode()); ASSERT(!IsDisabled()); @@ -13832,6 +13833,10 @@ void Code::DisableStubCode() const { Code::Handle(StubCode::FixAllocationStubTarget_entry()->code()); ASSERT(new_code.instructions()->IsVMHeapObject()); SetActiveInstructions(new_code.instructions()); +#else + // DBC does not use allocation stubs. + UNIMPLEMENTED(); +#endif // !defined(TARGET_ARCH_DBC) } @@ -14970,7 +14975,9 @@ bool Instance::IsInstanceOf(const AbstractType& other, Zone* zone = Thread::Current()->zone(); const Class& cls = Class::Handle(zone, clazz()); if (cls.IsClosureClass()) { - if (other.IsObjectType() || other.IsDartFunctionType()) { + if (other.IsObjectType() || + other.IsDartFunctionType() || + other.IsDartClosureType()) { return true; } Function& other_signature = Function::Handle(zone); @@ -15757,6 +15764,13 @@ bool AbstractType::IsDartFunctionType() const { } +bool AbstractType::IsDartClosureType() const { + return !IsFunctionType() && + HasResolvedTypeClass() && + (type_class() == Isolate::Current()->object_store()->closure_class()); +} + + bool AbstractType::TypeTest(TypeTestKind test_kind, const AbstractType& other, Error* bound_error, diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 6a2008fe609..84d91bfe036 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -5485,6 +5485,9 @@ class AbstractType : public Instance { // Check if this type represents the Dart 'Function' type. bool IsDartFunctionType() const; + // Check if this type represents the Dart '_Closure' type. + bool IsDartClosureType() const; + // Check the subtype relationship. bool IsSubtypeOf(const AbstractType& other, Error* bound_error, diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc index 62718becd52..aa75ee9feb1 100644 --- a/runtime/vm/object_test.cc +++ b/runtime/vm/object_test.cc @@ -21,10 +21,6 @@ namespace dart { DECLARE_FLAG(bool, write_protect_code); -static RawLibrary* CreateDummyLibrary(const String& library_name) { - return Library::New(library_name); -} - static RawClass* CreateDummyClass(const String& class_name, const Script& script) { @@ -2690,6 +2686,12 @@ VM_TEST_CASE(CheckedHandle) { } +#if !defined(TARGET_ARCH_DBC) +static RawLibrary* CreateDummyLibrary(const String& library_name) { + return Library::New(library_name); +} + + static RawFunction* CreateFunction(const char* name) { Thread* thread = Thread::Current(); const String& class_name = String::Handle(Symbols::New(thread, "ownerClass")); @@ -2966,6 +2968,7 @@ VM_TEST_CASE(PcDescriptorsLargeDeltas) { EXPECT_EQ(false, iter.MoveNext()); } +#endif // !defined(TARGET_ARCH_DBC) static RawClass* CreateTestClass(const char* name) { diff --git a/runtime/vm/os_android.cc b/runtime/vm/os_android.cc index 03540bb99b2..3269b128e76 100644 --- a/runtime/vm/os_android.cc +++ b/runtime/vm/os_android.cc @@ -191,7 +191,7 @@ intptr_t OS::ActivationFrameAlignment() { #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) || \ defined(TARGET_ARCH_ARM64) const int kMinimumAlignment = 16; -#elif defined(TARGET_ARCH_ARM) +#elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_DBC) const int kMinimumAlignment = 8; #else #error Unsupported architecture. @@ -212,6 +212,8 @@ intptr_t OS::PreferredCodeAlignment() { const int kMinimumAlignment = 16; #elif defined(TARGET_ARCH_ARM) const int kMinimumAlignment = 16; +#elif defined(TARGET_ARCH_DBC) + const int kMinimumAlignment = 16; #else #error Unsupported architecture. #endif diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc index edfb890a73f..0fc90c7394a 100644 --- a/runtime/vm/os_linux.cc +++ b/runtime/vm/os_linux.cc @@ -197,7 +197,8 @@ void OS::AlignedFree(void* ptr) { intptr_t OS::ActivationFrameAlignment() { #if defined(TARGET_ARCH_IA32) || \ defined(TARGET_ARCH_X64) || \ - defined(TARGET_ARCH_ARM64) + defined(TARGET_ARCH_ARM64) || \ + defined(TARGET_ARCH_DBC) const int kMinimumAlignment = 16; #elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_MIPS) const int kMinimumAlignment = 8; @@ -217,7 +218,8 @@ intptr_t OS::ActivationFrameAlignment() { intptr_t OS::PreferredCodeAlignment() { #if defined(TARGET_ARCH_IA32) || \ defined(TARGET_ARCH_X64) || \ - defined(TARGET_ARCH_ARM64) + defined(TARGET_ARCH_ARM64) || \ + defined(TARGET_ARCH_DBC) const int kMinimumAlignment = 32; #elif defined(TARGET_ARCH_ARM) || defined(TARGET_ARCH_MIPS) const int kMinimumAlignment = 16; diff --git a/runtime/vm/precompiler.cc b/runtime/vm/precompiler.cc index c1fa12865a2..536092e83db 100644 --- a/runtime/vm/precompiler.cc +++ b/runtime/vm/precompiler.cc @@ -1721,6 +1721,7 @@ void Precompiler::BindStaticCalls() { void Precompiler::SwitchICCalls() { +#if !defined(TARGET_ARCH_DBC) // Now that all functions have been compiled, we can switch to an instance // call sequence that loads the Code object and entry point directly from // the ic data array instead indirectly through a Function in the ic data @@ -1794,6 +1795,7 @@ void Precompiler::SwitchICCalls() { ASSERT(!I->compilation_allowed()); SwitchICCallsVisitor visitor(Z); VisitFunctions(&visitor); +#endif } diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc index e597260ef88..2f1ae378005 100644 --- a/runtime/vm/profiler.cc +++ b/runtime/vm/profiler.cc @@ -290,6 +290,11 @@ bool ReturnAddressLocator::LocateReturnAddress(uword* return_address) { ASSERT(return_address != NULL); return false; } +#elif defined(TARGET_ARCH_DBC) +bool ReturnAddressLocator::LocateReturnAddress(uword* return_address) { + ASSERT(return_address != NULL); + return false; +} #else #error ReturnAddressLocator implementation missing for this architecture. #endif @@ -839,7 +844,7 @@ static Sample* SetupSample(Thread* thread, Sample* sample = sample_buffer->ReserveSample(); sample->Init(isolate, OS::GetCurrentMonotonicMicros(), tid); uword vm_tag = thread->vm_tag(); -#if defined(USING_SIMULATOR) +#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC) // When running in the simulator, the runtime entry function address // (stored as the vm tag) is the address of a redirect function. // Attempt to find the real runtime entry function address and use that. @@ -973,6 +978,11 @@ void Profiler::SampleThreadSingleFrame(Thread* thread, uintptr_t pc) { void Profiler::SampleThread(Thread* thread, const InterruptedThreadState& state) { +#if defined(TARGET_ARCH_DBC) + // TODO(vegorov) implement simulator stack sampling. + return; +#endif + ASSERT(thread != NULL); OSThread* os_thread = thread->os_thread(); ASSERT(os_thread != NULL); @@ -996,13 +1006,15 @@ void Profiler::SampleThread(Thread* thread, uintptr_t sp = 0; uintptr_t fp = state.fp; uintptr_t pc = state.pc; -#if defined(USING_SIMULATOR) +#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC) Simulator* simulator = NULL; #endif if (in_dart_code) { // If we're in Dart code, use the Dart stack pointer. -#if defined(USING_SIMULATOR) +#if defined(TARGET_ARCH_DBC) + UNIMPLEMENTED(); +#elif defined(USING_SIMULATOR) simulator = isolate->simulator(); sp = simulator->get_register(SPREG); fp = simulator->get_register(FPREG); diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 6a5d626c01f..49d74aacbd4 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -231,6 +231,8 @@ CLASS_LIST_TYPED_DATA(V) friend class object; \ friend class RawObject; \ friend class Heap; \ + friend class Simulator; \ + friend class SimulatorHelpers; \ DISALLOW_ALLOCATION(); \ DISALLOW_IMPLICIT_CONSTRUCTORS(Raw##object) @@ -665,6 +667,8 @@ class RawObject { friend class StackFrame; // GetCodeObject assertion. friend class CodeLookupTableBuilder; // profiler friend class NativeEntry; // GetClassId + friend class Simulator; + friend class SimulatorHelpers; DISALLOW_ALLOCATION(); DISALLOW_IMPLICIT_CONSTRUCTORS(RawObject); diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc index 0ca54d8f33c..e22055c1d20 100644 --- a/runtime/vm/raw_object_snapshot.cc +++ b/runtime/vm/raw_object_snapshot.cc @@ -1540,9 +1540,13 @@ RawObjectPool* ObjectPool::ReadFrom(SnapshotReader* reader, break; } case ObjectPool::kNativeEntry: { +#if !defined(TARGET_ARCH_DBC) // Read nothing. Initialize with the lazy link entry. uword new_entry = NativeEntry::LinkNativeCallEntry(); result->SetRawValueAt(i, static_cast(new_entry)); +#else + UNREACHABLE(); // DBC does not support lazy native call linking. +#endif break; } default: @@ -1592,14 +1596,16 @@ void RawObjectPool::WriteTo(SnapshotWriter* writer, Entry& entry = ptr()->data()[i]; switch (entry_type) { case ObjectPool::kTaggedObject: { +#if !defined(TARGET_ARCH_DBC) if (entry.raw_obj_ == StubCode::CallNativeCFunction_entry()->code()) { // Natives can run while precompiling, becoming linked and switching // their stub. Reset to the initial stub used for lazy-linking. writer->WriteObjectImpl( StubCode::CallBootstrapCFunction_entry()->code(), kAsReference); - } else { - writer->WriteObjectImpl(entry.raw_obj_, kAsReference); + break; } +#endif + writer->WriteObjectImpl(entry.raw_obj_, kAsReference); break; } case ObjectPool::kImmediate: { @@ -1608,6 +1614,9 @@ void RawObjectPool::WriteTo(SnapshotWriter* writer, } case ObjectPool::kNativeEntry: { // Write nothing. Will initialize with the lazy link entry. +#if defined(TARGET_ARCH_DBC) + UNREACHABLE(); // DBC does not support lazy native call linking. +#endif break; } default: diff --git a/runtime/vm/runtime_entry_dbc.cc b/runtime/vm/runtime_entry_dbc.cc new file mode 100644 index 00000000000..8e03f1aa6f6 --- /dev/null +++ b/runtime/vm/runtime_entry_dbc.cc @@ -0,0 +1,29 @@ +// 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 "vm/globals.h" +#if defined(TARGET_ARCH_DBC) + +#include "vm/runtime_entry.h" + +#include "vm/assembler.h" +#include "vm/simulator.h" +#include "vm/stub_code.h" + +namespace dart { + + +uword RuntimeEntry::GetEntryPoint() const { + return reinterpret_cast(function()); +} + + +void RuntimeEntry::Call(Assembler* assembler, intptr_t argument_count) const { + UNIMPLEMENTED(); +} + + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/simulator.h b/runtime/vm/simulator.h index 8a859e34f99..b73bd97f378 100644 --- a/runtime/vm/simulator.h +++ b/runtime/vm/simulator.h @@ -17,6 +17,8 @@ #include "vm/simulator_arm64.h" #elif defined(TARGET_ARCH_MIPS) #include "vm/simulator_mips.h" +#elif defined(TARGET_ARCH_DBC) +#include "vm/simulator_dbc.h" #else #error Unknown architecture. #endif // defined(TARGET_ARCH_...) diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc index 1fcb66bc922..f11bab75716 100644 --- a/runtime/vm/simulator_arm.cc +++ b/runtime/vm/simulator_arm.cc @@ -44,7 +44,6 @@ DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX, // The runtime then does a Longjmp on that buffer to return to the simulator. class SimulatorSetjmpBuffer { public: - int Setjmp() { return setjmp(buffer_); } void Longjmp() { // "This" is now the last setjmp buffer. simulator_->set_last_setjmp_buffer(this); @@ -1546,7 +1545,7 @@ void Simulator::SupervisorCall(Instr* instr) { ASSERT(sizeof(NativeArguments) == 4*kWordSize); arguments.thread_ = reinterpret_cast(get_register(R0)); arguments.argc_tag_ = get_register(R1); - arguments.argv_ = reinterpret_cast(get_register(R2)); + arguments.argv_ = reinterpret_cast(get_register(R2)); arguments.retval_ = reinterpret_cast(get_register(R3)); SimulatorRuntimeCall target = reinterpret_cast(external); diff --git a/runtime/vm/simulator_arm.h b/runtime/vm/simulator_arm.h index af3cb098dc9..ce18ac00fe4 100644 --- a/runtime/vm/simulator_arm.h +++ b/runtime/vm/simulator_arm.h @@ -52,6 +52,10 @@ class Simulator { void set_register(Register reg, int32_t value); int32_t get_register(Register reg) const; + int32_t get_sp() const { + return get_register(SPREG); + } + // Special case of set_register and get_register to access the raw PC value. void set_pc(int32_t value); int32_t get_pc() const; diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc index cef6aa241fc..36f5f9cfebd 100644 --- a/runtime/vm/simulator_arm64.cc +++ b/runtime/vm/simulator_arm64.cc @@ -43,7 +43,6 @@ DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX, // The runtime then does a Longjmp on that buffer to return to the simulator. class SimulatorSetjmpBuffer { public: - int Setjmp() { return setjmp(buffer_); } void Longjmp() { // "This" is now the last setjmp buffer. simulator_->set_last_setjmp_buffer(this); diff --git a/runtime/vm/simulator_arm64.h b/runtime/vm/simulator_arm64.h index bf3572ce6bf..78c1812ac72 100644 --- a/runtime/vm/simulator_arm64.h +++ b/runtime/vm/simulator_arm64.h @@ -65,6 +65,10 @@ class Simulator { void get_vregister(VRegister reg, simd_value_t* value) const; void set_vregister(VRegister reg, const simd_value_t& value); + int64_t get_sp() const { + return get_register(SPREG); + } + int64_t get_pc() const; int64_t get_last_pc() const; void set_pc(int64_t pc); diff --git a/runtime/vm/simulator_dbc.cc b/runtime/vm/simulator_dbc.cc new file mode 100644 index 00000000000..780e44f4842 --- /dev/null +++ b/runtime/vm/simulator_dbc.cc @@ -0,0 +1,1903 @@ +// 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 "vm/assembler.h" +#include "vm/compiler.h" +#include "vm/constants_dbc.h" +#include "vm/cpu.h" +#include "vm/dart_entry.h" +#include "vm/debugger.h" +#include "vm/disassembler.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" + +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); + sp_ = sim->sp_; + fp_ = sim->fp_; + } + + ~SimulatorSetjmpBuffer() { + ASSERT(simulator_->last_setjmp_buffer() == this); + simulator_->set_last_setjmp_buffer(link_); + } + + SimulatorSetjmpBuffer* link() const { return link_; } + + uword sp() const { return reinterpret_cast(sp_); } + uword fp() const { return reinterpret_cast(fp_); } + + jmp_buf buffer_; + + private: + RawObject** sp_; + 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 RawCode* FrameCode(RawObject** FP) { + return static_cast(FP[kPcMarkerSlotFromFp]); +} + + +DART_FORCE_INLINE static void SetFrameCode(RawObject** FP, RawCode* code) { + FP[kPcMarkerSlotFromFp] = code; +} + + +DART_FORCE_INLINE static RawObject** FrameArguments(RawObject** FP, + intptr_t argc) { + return FP - (kDartFrameFixedSize + argc); +} + + +class SimulatorHelpers { + public: + DART_FORCE_INLINE static RawSmi* GetClassIdAsSmi(RawObject* obj) { + return Smi::New(obj->IsHeapObject() ? obj->GetClassId() : kSmiCid); + } + + DART_FORCE_INLINE static intptr_t GetClassId(RawObject* obj) { + return obj->IsHeapObject() ? obj->GetClassId() : kSmiCid; + } + + DART_FORCE_INLINE static void IncrementUsageCounter(RawICData* icdata) { + reinterpret_cast(icdata->ptr()->owner_) + ->ptr() + ->usage_counter_++; + } + + 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_); + + case kBigintCid: + return (DLRT_BigintCompare(static_cast(lhs), + static_cast(rhs)) == 0); + } + } + } + + 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)); + } + + static bool ObjectArraySetIndexed(Thread* thread, + RawObject** FP, + RawObject** result) { + if (thread->isolate()->type_checks()) { + return false; + } + + RawObject** args = FrameArguments(FP, 3); + RawSmi* index = static_cast(args[1]); + RawArray* array = static_cast(args[0]); + if (CheckIndex(index, array->ptr()->length_)) { + array->StorePointer(array->ptr()->data() + Smi::Value(index), args[2]); + 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) { + if (thread->isolate()->type_checks()) { + return false; + } + + 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->StorePointer(data->ptr()->data() + Smi::Value(index), args[2]); + 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; + } +}; + + +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); + return function; +} + + +IntrinsicHandler Simulator::intrinsics_[Simulator::kIntrinsicCount]; + + +// Synchronization primitives support. +void Simulator::InitOnce() { + for (intptr_t i = 0; i < kIntrinsicCount; i++) { + intrinsics_[i] = 0; + } + + intrinsics_[kObjectArraySetIndexedIntrinsic] = + SimulatorHelpers::ObjectArraySetIndexed; + intrinsics_[kObjectArrayGetIndexedIntrinsic] = + SimulatorHelpers::ObjectArrayGetIndexed; + intrinsics_[kGrowableArraySetIndexedIntrinsic] = + SimulatorHelpers::GrowableArraySetIndexed; + intrinsics_[kGrowableArrayGetIndexedIntrinsic] = + SimulatorHelpers::GrowableArrayGetIndexed; +} + + +Simulator::Simulator() + : stack_(NULL), + fp_(NULL), + sp_(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::kStackSizeBuffer + + kSimulatorStackUnderflowSize) / + sizeof(uintptr_t)]; + last_setjmp_buffer_ = NULL; + top_exit_frame_info_ = 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; +} + + +// Returns the top of the stack area to enable checking for stack pointer +// validity. +uword Simulator::StackTop() const { + // To be safe in potential stack underflows we leave some buffer above and + // set the stack top. + return StackBase() + + (OSThread::GetSpecifiedStackSize() + OSThread::kStackSizeBuffer); +} + + +// 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 int32_t (*SimulatorLeafRuntimeCall)(int32_t r0, + int32_t r1, + int32_t r2, + int32_t r3); + +// Calls to leaf float Dart runtime functions are based on this interface. +typedef double (*SimulatorLeafFloatRuntimeCall)(double d0, double d1); + +// Calls to native Dart functions are based on this interface. +typedef void (*SimulatorBootstrapNativeCall)(NativeArguments* arguments); +typedef void (*SimulatorNativeCall)(NativeArguments* arguments, uword target); + + +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_ = sp_ = frame + kDartFrameFixedSize; + thread->set_top_exit_frame_info(reinterpret_cast(sp_)); +} + + +#if defined(__has_builtin) +#if __has_builtin(__builtin_smul_overflow) +#define HAS_MUL_OVERFLOW +#endif +#if __has_builtin(__builtin_sadd_overflow) +#define HAS_ADD_OVERFLOW +#endif +#if __has_builtin(__builtin_ssub_overflow) +#define HAS_SUB_OVERFLOW +#endif +#endif + + +DART_FORCE_INLINE static bool SignedAddWithOverflow(int32_t lhs, + int32_t rhs, + intptr_t* out) { + int32_t res = 1; +#if defined(HAS_ADD_OVERFLOW) + res = static_cast(__builtin_sadd_overflow(lhs, rhs, out)); +#elif defined(__i386__) + 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(__arm__) + asm volatile( + "adds %1, %1, %2;\n" + "bvs 1f;\n" + "mov %0, $0;\n" + "str %1, [%3, #0]\n" + "1:" + : "+r"(res), "+r"(lhs) + : "r"(rhs), "r"(out) + : "cc", "r12"); +#else +#error "Unsupported platform" +#endif + return (res != 0); +} + + +DART_FORCE_INLINE static bool SignedSubWithOverflow(int32_t lhs, + int32_t rhs, + intptr_t* out) { + int32_t res = 1; +#if defined(HAS_SUB_OVERFLOW) + res = static_cast(__builtin_ssub_overflow(lhs, rhs, out)); +#elif defined(__i386__) + 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(__arm__) + asm volatile( + "subs %1, %1, %2;\n" + "bvs 1f;\n" + "mov %0, $0;\n" + "str %1, [%3, #0]\n" + "1:" + : "+r"(res), "+r"(lhs) + : "r"(rhs), "r"(out) + : "cc", "r12"); +#else +#error "Unsupported platform" +#endif + return (res != 0); +} + + +DART_FORCE_INLINE static bool SignedMulWithOverflow(int32_t lhs, + int32_t rhs, + intptr_t* out) { + int32_t res = 1; +#if defined(HAS_MUL_OVERFLOW) + res = static_cast(__builtin_smul_overflow(lhs, rhs, out)); +#elif defined(__i386__) + 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(__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"); +#else +#error "Unsupported platform" +#endif + return (res != 0); +} + + +#define LIKELY(cond) __builtin_expect((cond), 1) + + +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) + + +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 void Simulator::Invoke(Thread* thread, + RawObject** call_base, + RawObject** call_top, + RawObjectPool** pp, + 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_->ptr(); + *pc = reinterpret_cast(code->ptr()->entry_point_); + *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; + RawObject** miss_handler_args = top + 1; + for (intptr_t i = 0; i < checked_args; i++) { + miss_handler_args[i] = args[i]; + } + miss_handler_args[checked_args] = icdata; + RuntimeFunction handler = NULL; + switch (checked_args) { + case 1: + handler = DRT_InlineCacheMissHandlerOneArg; + break; + case 2: + handler = DRT_InlineCacheMissHandlerTwoArgs; + break; + case 3: + handler = DRT_InlineCacheMissHandlerThreeArgs; + 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)); +} + + +DART_FORCE_INLINE void Simulator::InstanceCall1(Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** top, + RawArray** argdesc, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** FP, + RawObject*** SP) { + ASSERT(icdata->GetClassId() == kICDataCid); + SimulatorHelpers::IncrementUsageCounter(icdata); + + const intptr_t kCheckedArgs = 1; + RawObject** args = call_base; + RawArray* cache = icdata->ptr()->ic_data_->ptr(); + + RawSmi* receiver_cid = SimulatorHelpers::GetClassIdAsSmi(args[0]); + + bool found = false; + const intptr_t length = Smi::Value(cache->length_); + for (intptr_t i = 0; + i < (length - (kCheckedArgs + 2)); i += (kCheckedArgs + 2)) { + if (cache->data()[i + 0] == receiver_cid) { + top[0] = cache->data()[i + kCheckedArgs]; + found = true; + break; + } + } + + if (!found) { + InlineCacheMiss( + kCheckedArgs, thread, icdata, call_base, top, *pc, *FP, *SP); + } + + *argdesc = icdata->ptr()->args_descriptor_; + Invoke(thread, call_base, top, pp, pc, FP, SP); +} + + +DART_FORCE_INLINE void Simulator::InstanceCall2(Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** top, + RawArray** argdesc, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** FP, + RawObject*** SP) { + ASSERT(icdata->GetClassId() == kICDataCid); + SimulatorHelpers::IncrementUsageCounter(icdata); + + const intptr_t kCheckedArgs = 2; + RawObject** args = call_base; + RawArray* cache = icdata->ptr()->ic_data_->ptr(); + + RawSmi* receiver_cid = SimulatorHelpers::GetClassIdAsSmi(args[0]); + RawSmi* arg0_cid = SimulatorHelpers::GetClassIdAsSmi(args[1]); + + bool found = false; + const intptr_t length = Smi::Value(cache->length_); + for (intptr_t i = 0; + i < (length - (kCheckedArgs + 2)); i += (kCheckedArgs + 2)) { + if ((cache->data()[i + 0] == receiver_cid) && + (cache->data()[i + 1] == arg0_cid)) { + top[0] = cache->data()[i + kCheckedArgs]; + found = true; + break; + } + } + + if (!found) { + InlineCacheMiss( + kCheckedArgs, thread, icdata, call_base, top, *pc, *FP, *SP); + } + + *argdesc = icdata->ptr()->args_descriptor_; + Invoke(thread, call_base, top, pp, pc, FP, SP); +} + + +DART_FORCE_INLINE void Simulator::InstanceCall3(Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** top, + RawArray** argdesc, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** FP, + RawObject*** SP) { + ASSERT(icdata->GetClassId() == kICDataCid); + SimulatorHelpers::IncrementUsageCounter(icdata); + + const intptr_t kCheckedArgs = 3; + RawObject** args = call_base; + RawArray* cache = icdata->ptr()->ic_data_->ptr(); + + RawSmi* receiver_cid = SimulatorHelpers::GetClassIdAsSmi(args[0]); + RawSmi* arg0_cid = SimulatorHelpers::GetClassIdAsSmi(args[1]); + RawSmi* arg1_cid = SimulatorHelpers::GetClassIdAsSmi(args[2]); + + bool found = false; + const intptr_t length = Smi::Value(cache->length_); + for (intptr_t i = 0; + i < (length - (kCheckedArgs + 2)); i += (kCheckedArgs + 2)) { + if ((cache->data()[i + 0] == receiver_cid) && + (cache->data()[i + 1] == arg0_cid) && + (cache->data()[i + 2] == arg1_cid)) { + top[0] = cache->data()[i + kCheckedArgs]; + found = true; + break; + } + } + + if (!found) { + InlineCacheMiss( + kCheckedArgs, thread, icdata, call_base, top, *pc, *FP, *SP); + } + + *argdesc = icdata->ptr()->args_descriptor_; + Invoke(thread, call_base, top, pp, pc, FP, SP); +} + + +// Note: functions below are marked DART_NOINLINE to recover performance on +// ARM where inlining these functions into the interpreter loop seemed to cause +// some code quality issues. +static DART_NOINLINE bool InvokeRuntime( + Thread* thread, + 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::kDartTagId); + return true; + } else { + return false; + } +} + + +static DART_NOINLINE bool InvokeNative( + Thread* thread, + Simulator* sim, + SimulatorBootstrapNativeCall f, + NativeArguments* args) { + SimulatorSetjmpBuffer buffer(sim); + if (!setjmp(buffer.buffer_)) { + thread->set_vm_tag(reinterpret_cast(f)); + f(args); + thread->set_vm_tag(VMTag::kDartTagId); + return true; + } else { + return false; + } +} + + +static DART_NOINLINE bool InvokeNativeWrapper( + Thread* thread, + Simulator* sim, + Dart_NativeFunction f, + NativeArguments* args) { + SimulatorSetjmpBuffer buffer(sim); + if (!setjmp(buffer.buffer_)) { + thread->set_vm_tag(reinterpret_cast(f)); + NativeEntry::NativeCallWrapper(reinterpret_cast(args), + f); + thread->set_vm_tag(VMTag::kDartTagId); + return true; + } else { + return false; + } +} + +// Note: all macro helpers are intended to be used only inside Simulator::Call. + +// Decode opcode and A part of the given value and dispatch to the +// corresponding bytecode handler. +#define DISPATCH_OP(val) \ + do { \ + op = (val); \ + rA = ((op >> 8) & 0xFF); \ + goto* dispatch[op & 0xFF]; \ + } while (0) + +// Fetch next operation from PC, increment program counter and dispatch. +#define DISPATCH() DISPATCH_OP(*pc++) + +// Define entry point that handles bytecode Name with the given operand format. +#define BYTECODE(Name, Operands) \ + BYTECODE_HEADER(Name, DECLARE_##Operands, DECODE_##Operands) + +#define BYTECODE_HEADER(Name, Declare, Decode) \ + Declare; \ + bc##Name : Decode \ + +// Helpers to decode common instruction formats. Used in conjunction with +// BYTECODE() macro. +#define DECLARE_A_B_C uint16_t rB, rC; USE(rB); USE(rC) +#define DECODE_A_B_C \ + rB = ((op >> Bytecode::kBShift) & Bytecode::kBMask); \ + rC = ((op >> Bytecode::kCShift) & Bytecode::kCMask); + +#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 >> Bytecode::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) >> Bytecode::kDShift); + +// 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 (LIKELY(AreBothSmis(lhs, rhs) && !Func(lhs, rhs, slot))) { \ + /* Fast path succeeded. Skip the generic call that follows. */ \ + pc++; \ + /* We dropped 2 arguments and push result */ \ + SP--; \ + } \ + } + +// 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_ = sp_ = reinterpret_cast(fp_[0]); \ + thread->set_top_exit_frame_info(reinterpret_cast(sp_)); \ + thread->set_top_resource(top_resource); \ + thread->set_vm_tag(vm_tag); \ + return special_[kExceptionSpecialIndex]; \ + } \ + pp = FrameCode(FP)->ptr()->object_pool_->ptr(); \ + goto DispatchAfterException; \ + } 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; \ + } \ + +#define INVOKE_NATIVE(Func, Args) \ + if (!InvokeNative(thread, this, Func, &Args)) { \ + HANDLE_EXCEPTION; \ + } \ + +#define INVOKE_NATIVE_WRAPPER(Func, Args) \ + if (!InvokeNativeWrapper(thread, this, Func, &Args)) { \ + HANDLE_EXCEPTION; \ + } \ + +#define LOAD_CONSTANT(index) (pp->data()[(index)].raw_obj_) + +RawObject* Simulator::Call(const Code& code, + const Array& arguments_descriptor, + const Array& arguments, + Thread* thread) { + // Dispatch used to interpret bytecode. Contains addresses of + // labels of bytecode handlers. Handlers themselves are defined below. + static const void* dispatch[] = { +#define TARGET(name, fmt, fmta, fmtb, fmtc) &&bc##name, + BYTECODES_LIST(TARGET) +#undef TARGET + }; + + // Interpreter state (see constants_dbc.h for high-level overview). + uint32_t* pc; // Program Counter: points to the next op to execute. + RawObjectPool* pp; // Pool Pointer. + RawObject** FP; // Frame Pointer. + RawObject** SP; // Stack Pointer. + + RawArray* argdesc; // Arguments Descriptor: used to pass information between + // call instruction and the function entry. + + uint32_t op; // Currently executing op. + uint16_t rA; // A component of the currently executing op. + + if (sp_ == NULL) { + fp_ = sp_ = reinterpret_cast(stack_); + } + + // Save current VM tag and mark thread as executing Dart code. + const uword vm_tag = thread->vm_tag(); + thread->set_vm_tag(VMTag::kDartTagId); + + // 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 | | + // | callee 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()); + + // 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_); + pp = code.object_pool()->ptr(); + + // 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(); + RawObject* empty_context = thread->isolate()->object_store()->empty_context(); + +#if defined(DEBUG) + Function& function_h = Function::Handle(); +#endif + + // Enter the dispatch loop. + DISPATCH(); + + // Bytecode handlers (see constants_dbc.h for bytecode descriptions). + { + BYTECODE(Entry, A_B_C); + const uint8_t num_fixed_params = rA; + const uint16_t num_locals = rB; + const uint16_t context_reg = rC; + + // Decode arguments descriptor. + const intptr_t pos_count = Smi::Value(*reinterpret_cast( + reinterpret_cast(argdesc->ptr()) + + Array::element_offset(ArgumentsDescriptor::kPositionalCountIndex))); + + // Check that we got the right number of positional parameters. + if (pos_count != num_fixed_params) { + // Mismatch can only occur if current function is a closure. + goto ClosureNoSuchMethod; + } + + // Initialize locals with null and set current context variable to + // empty context. + { + RawObject** L = FP; + for (intptr_t i = 0; i < num_locals; i++) { + L[i] = null_value; + } + L[context_reg] = empty_context; + SP = FP + num_locals - 1; + } + + DISPATCH(); + } + + { + BYTECODE(EntryOpt, A_B_C); + const uint16_t num_fixed_params = rA; + const uint16_t num_opt_pos_params = rB; + const uint16_t num_opt_named_params = rC; + const intptr_t min_num_pos_args = num_fixed_params; + const intptr_t max_num_pos_args = num_fixed_params + num_opt_pos_params; + + // Decode arguments descriptor. + const intptr_t arg_count = Smi::Value(*reinterpret_cast( + reinterpret_cast(argdesc->ptr()) + + Array::element_offset(ArgumentsDescriptor::kCountIndex))); + const intptr_t pos_count = Smi::Value(*reinterpret_cast( + reinterpret_cast(argdesc->ptr()) + + Array::element_offset(ArgumentsDescriptor::kPositionalCountIndex))); + const intptr_t named_count = (arg_count - pos_count); + + // Check that got the right number of positional parameters. + if ((min_num_pos_args > pos_count) || (pos_count > max_num_pos_args)) { + goto ClosureNoSuchMethod; + } + + // Copy all passed position arguments. + RawObject** first_arg = FrameArguments(FP, arg_count); + memmove(FP, first_arg, pos_count * kWordSize); + + if (num_opt_named_params != 0) { + // This is a function with named parameters. + // Walk the list of named parameters and their + // default values encoded as pairs of LoadConstant instructions that + // follows the entry point and find matching values via arguments + // descriptor. + RawObject** argdesc_data = argdesc->ptr()->data(); + + intptr_t i = named_count - 1; // argument position + intptr_t j = num_opt_named_params - 1; // parameter position + while ((j >= 0) && (i >= 0)) { + // Fetch formal parameter information: name, default value, target slot. + const uint32_t load_name = pc[2 * j]; + const uint32_t load_value = pc[2 * j + 1]; + ASSERT(Bytecode::DecodeOpcode(load_name) == Bytecode::kLoadConstant); + ASSERT(Bytecode::DecodeOpcode(load_value) == Bytecode::kLoadConstant); + const uint8_t reg = Bytecode::DecodeA(load_name); + ASSERT(reg == Bytecode::DecodeA(load_value)); + + RawString* name = static_cast( + LOAD_CONSTANT(Bytecode::DecodeD(load_name))); + if (name == argdesc_data[ArgumentsDescriptor::name_index(i)]) { + // Parameter was passed. Fetch passed value. + const intptr_t arg_index = Smi::Value(static_cast( + argdesc_data[ArgumentsDescriptor::position_index(i)])); + FP[reg] = first_arg[arg_index]; + i--; // Consume passed argument. + } else { + // Parameter was not passed. Fetch default value. + FP[reg] = LOAD_CONSTANT(Bytecode::DecodeD(load_value)); + } + j--; // Next formal parameter. + } + + // If we have unprocessed formal parameters then initialize them all + // using default values. + while (j >= 0) { + const uint32_t load_name = pc[2 * j]; + const uint32_t load_value = pc[2 * j + 1]; + ASSERT(Bytecode::DecodeOpcode(load_name) == Bytecode::kLoadConstant); + ASSERT(Bytecode::DecodeOpcode(load_value) == Bytecode::kLoadConstant); + const uint8_t reg = Bytecode::DecodeA(load_name); + ASSERT(reg == Bytecode::DecodeA(load_value)); + + FP[reg] = LOAD_CONSTANT(Bytecode::DecodeD(load_value)); + j--; + } + + // If we have unprocessed passed arguments that means we have mismatch + // between formal parameters and concrete arguments. This can only + // occur if the current function is a closure. + if (i != -1) { + goto ClosureNoSuchMethod; + } + + // Skip LoadConstant-s encoding information about named parameters. + pc += num_opt_named_params * 2; + + // SP points past copied arguments. + SP = FP + num_fixed_params + num_opt_named_params - 1; + } else { + ASSERT(num_opt_pos_params != 0); + if (named_count != 0) { + // Function can't have both named and optional positional parameters. + // This kind of mismatch can only occur if the current function + // is a closure. + goto ClosureNoSuchMethod; + } + + // Process the list of default values encoded as a sequence of + // LoadConstant instructions after EntryOpt bytecode. + // Execute only those that correspond to parameters the were not passed. + for (intptr_t i = pos_count - num_fixed_params; + i < num_opt_pos_params; + i++) { + const uint32_t load_value = pc[i]; + ASSERT(Bytecode::DecodeOpcode(load_value) == Bytecode::kLoadConstant); +#if defined(DEBUG) + const uint8_t reg = Bytecode::DecodeA(load_value); + ASSERT((num_fixed_params + i) == reg); +#endif + FP[num_fixed_params + i] = LOAD_CONSTANT(Bytecode::DecodeD(load_value)); + } + + // Skip LoadConstant-s encoding default values for optional positional + // parameters. + pc += num_opt_pos_params; + + // SP points past the last copied parameter. + SP = FP + max_num_pos_args - 1; + } + + DISPATCH(); + } + + { + BYTECODE(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] = FrameFunction(FP); + FP[1] = 0; + Exit(thread, FP, FP + 2, pc); + NativeArguments args(thread, 1, FP, FP + 1); + INVOKE_RUNTIME(DRT_CompileFunction, args); + { + // Function should be compiled now, dispatch to its entry point. + RawCode* code = FrameFunction(FP)->ptr()->code_; + SetFrameCode(FP, code); + pp = code->ptr()->object_pool_->ptr(); + pc = reinterpret_cast(code->ptr()->entry_point_); + } + 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(DebugStep, A); + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + DISPATCH(); + } + + { + BYTECODE(DebugBreak, A); + { + 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); + } + DISPATCH(); + } + + { + BYTECODE(InstantiateType, A_D); + RawObject* type = LOAD_CONSTANT(rD); + SP[1] = type; + SP[2] = SP[0]; + SP[0] = null_value; + Exit(thread, FP, SP + 3, pc); + { + NativeArguments args(thread, 2, SP + 1, SP); + INVOKE_RUNTIME(DRT_InstantiateType, args); + } + DISPATCH(); + } + + { + BYTECODE(InstantiateTypeArgumentsTOS, A_D); + RawTypeArguments* type_arguments = + static_cast(LOAD_CONSTANT(rD)); + + RawObject* instantiator = SP[0]; + // If the instantiator is 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) { + // First lookup in the cache. + RawArray* instantiations = type_arguments->ptr()->instantiations_; + for (intptr_t i = 0; + instantiations->ptr()->data()[i] != NULL; // kNoInstantiator + i += 2) { + if (instantiations->ptr()->data()[i] == instantiator) { + // Found in the cache. + SP[0] = instantiations->ptr()->data()[i + 1]; + goto InstantiateTypeArgumentsTOSDone; + } + } + + // Cache lookup failed, call runtime. + SP[1] = type_arguments; + SP[2] = instantiator; + + Exit(thread, FP, SP + 3, pc); + NativeArguments args(thread, 2, SP + 1, SP); + INVOKE_RUNTIME(DRT_InstantiateTypeArguments, args); + } + + InstantiateTypeArgumentsTOSDone: + 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(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(StaticCall, A_D); + + // Check if single stepping. + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + + // Invoke target function. + { + const uint16_t argc = rA; + RawObject** call_base = SP - argc; + RawObject** call_top = SP; // *SP contains function + argdesc = static_cast(LOAD_CONSTANT(rD)); + Invoke(thread, call_base, call_top, &pp, &pc, &FP, &SP); + } + + DISPATCH(); + } + + { + BYTECODE(InstanceCall, A_D); + + // Check if single stepping. + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + + { + const uint16_t argc = rA; + const uint16_t kidx = rD; + + RawObject** call_base = SP - argc + 1; + RawObject** call_top = SP + 1; + InstanceCall1(thread, + static_cast(LOAD_CONSTANT(kidx)), + call_base, call_top, &argdesc, &pp, &pc, &FP, &SP); + } + + DISPATCH(); + } + + { + BYTECODE(InstanceCall2, A_D); + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + + { + const uint16_t argc = rA; + const uint16_t kidx = rD; + + RawObject** call_base = SP - argc + 1; + RawObject** call_top = SP + 1; + InstanceCall2(thread, + static_cast(LOAD_CONSTANT(kidx)), + call_base, call_top, &argdesc, &pp, &pc, &FP, &SP); + } + + DISPATCH(); + } + + { + BYTECODE(InstanceCall3, A_D); + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + + { + const uint16_t argc = rA; + const uint16_t kidx = rD; + + RawObject** call_base = SP - argc + 1; + RawObject** call_top = SP + 1; + InstanceCall3(thread, + static_cast(LOAD_CONSTANT(kidx)), + call_base, call_top, &argdesc, &pp, &pc, &FP, &SP); + } + + DISPATCH(); + } + + { + BYTECODE(NativeBootstrapCall, 0); + RawFunction* function = FrameFunction(FP); + RawObject** incoming_args = + (function->ptr()->num_optional_parameters_ == 0) + ? FrameArguments(FP, function->ptr()->num_fixed_parameters_) + : FP; + + SimulatorBootstrapNativeCall native_target = + reinterpret_cast(SP[-1]); + intptr_t argc_tag = reinterpret_cast(SP[-0]); + SP[-0] = 0; // Note: argc_tag is not smi-tagged. + SP[-1] = null_value; + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, argc_tag, incoming_args, SP - 1); + INVOKE_NATIVE(native_target, args); + SP -= 1; + DISPATCH(); + } + + { + BYTECODE(NativeCall, 0); + RawFunction* function = FrameFunction(FP); + RawObject** incoming_args = + (function->ptr()->num_optional_parameters_ == 0) + ? FrameArguments(FP, function->ptr()->num_fixed_parameters_) + : FP; + + Dart_NativeFunction native_target = + reinterpret_cast(SP[-1]); + intptr_t argc_tag = reinterpret_cast(SP[-0]); + SP[-0] = 0; // argc_tag is not smi tagged! + SP[-1] = null_value; + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, argc_tag, incoming_args, SP - 1); + INVOKE_NATIVE_WRAPPER(native_target, args); + SP -= 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(); + } + + // 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); + + // 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_ = sp_ = + reinterpret_cast(FrameArguments(FP, argc + 1)[0]); + thread->set_top_exit_frame_info(reinterpret_cast(sp_)); + 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 = Bytecode::DecodeArgc(pc[-1]); + + // Restore SP, FP and PP. Push result and dispatch. + SP = FrameArguments(FP, argc); + FP = SavedCallerFP(FP); + pp = FrameCode(FP)->ptr()->object_pool_->ptr(); + *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); + 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 = reinterpret_cast(FP[value_reg]); + + instance->StorePointer( + reinterpret_cast(instance->ptr()) + offset_in_words, + value); + DISPATCH(); + } + + { + BYTECODE(StoreFieldTOS, A_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); + + 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(LoadFieldTOS, A_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, A); + 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(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(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(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(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(AssertAssignable, A_D); // Stack: instance, type args, type, name + RawObject** args = SP - 3; + if (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]); + + const intptr_t cid = SimulatorHelpers::GetClassId(instance); + + RawTypeArguments* instance_type_arguments = + static_cast(null_value); + RawObject* instance_cid_or_function; + if (cid == kClosureCid) { + RawClosure* closure = static_cast(instance); + instance_type_arguments = closure->ptr()->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_]; + } + } + + 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)) { + if (true_value == entries[SubtypeTestCache::kTestResult]) { + goto AssertAssignableOk; + } else { + break; + } + } + } + } + + AssertAssignableCallRuntime: + SP[1] = args[0]; // instance + SP[2] = args[2]; // type + SP[3] = args[1]; // type args + SP[4] = args[3]; // name + SP[5] = cache; + Exit(thread, FP, SP + 6, pc); + NativeArguments args(thread, 5, SP + 1, SP - 3); + INVOKE_RUNTIME(DRT_TypeCheck, args); + } + + AssertAssignableOk: + SP -= 3; + 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(IfEqStrictTOS, A_D); + SP -= 2; + if (SP[1] != SP[2]) { + pc++; + } + DISPATCH(); + } + + { + BYTECODE(IfNeStrictTOS, A_D); + SP -= 2; + if (SP[1] == SP[2]) { + pc++; + } + DISPATCH(); + } + + { + BYTECODE(IfEqStrictNumTOS, A_D); + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + + SP -= 2; + if (!SimulatorHelpers::IsStrictEqualWithNumberCheck(SP[1], SP[2])) { + pc++; + } + DISPATCH(); + } + + { + BYTECODE(IfNeStrictNumTOS, A_D); + if (thread->isolate()->single_step()) { + Exit(thread, FP, SP + 1, pc); + NativeArguments args(thread, 0, NULL, NULL); + INVOKE_RUNTIME(DRT_SingleStepHandler, args); + } + + SP -= 2; + if (SimulatorHelpers::IsStrictEqualWithNumberCheck(SP[1], SP[2])) { + pc++; + } + DISPATCH(); + } + + { + BYTECODE(Jump, 0); + const int32_t target = static_cast(op) >> 8; + pc += (target - 1); + DISPATCH(); + } + + { + BYTECODE(StoreIndexedTOS, 0); + SP -= 3; + RawArray* array = static_cast(SP[1]); + RawSmi* index = static_cast(SP[2]); + RawObject* value = SP[3]; + ASSERT(array->GetClassId() == kArrayCid); + ASSERT(!index->IsHeapObject()); + array->StorePointer(array->ptr()->data() + Smi::Value(index), value); + DISPATCH(); + } + + { + BYTECODE(Trap, 0); + UNIMPLEMENTED(); + DISPATCH(); + } + + // Helper used to handle noSuchMethod on closures. + { + ClosureNoSuchMethod: +#if defined(DEBUG) + function_h ^= FrameFunction(FP); + ASSERT(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 + ? Bytecode::DecodeArgc(pc[-1]) + : (reinterpret_cast(pc) >> 2); + + SP = FrameArguments(FP, 0); + RawObject** args = SP - argc; + FP = SavedCallerFP(FP); + if (has_dart_caller) { + pp = FrameCode(FP)->ptr()->object_pool_->ptr(); + } + + *++SP = null_value; + *++SP = args[0]; // Closure object. + *++SP = argdesc; + *++SP = null_value; // Array of arguments (will be filled). + + // Allocate array of arguments. + { + SP[1] = Smi::New(argc); // length + SP[2] = null_value; // type + Exit(thread, FP, SP + 3, pc); + NativeArguments native_args(thread, 2, SP + 1, SP); + INVOKE_RUNTIME(DRT_AllocateArray, native_args); + + // 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, 3, SP - 2, SP - 3); + INVOKE_RUNTIME(DRT_InvokeClosureNoSuchMethod, native_args); + UNREACHABLE(); + } + + DISPATCH(); + } + + // Single dispatch point used by exception handling macros. + { + DispatchAfterException: + DISPATCH(); + } + + UNREACHABLE(); + return 0; +} + +void Simulator::Longjmp(uword pc, + uword sp, + uword fp, + RawObject* raw_exception, + RawObject* raw_stacktrace, + Thread* thread) { + // Walk over all setjmp buffers (simulated --> C++ transitions) + // and try to find the setjmp associated with the simulated stack 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); + + // Set the tag. + thread->set_vm_tag(VMTag::kDartTagId); + // Clear top exit frame. + thread->set_top_exit_frame_info(0); + + ASSERT(raw_exception != Object::null()); + sp_ = reinterpret_cast(sp); + fp_ = reinterpret_cast(fp); + pc_ = pc; + special_[kExceptionSpecialIndex] = raw_exception; + special_[kStacktraceSpecialIndex] = raw_stacktrace; + buf->Longjmp(); + UNREACHABLE(); +} + +} // namespace dart + + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/simulator_dbc.h b/runtime/vm/simulator_dbc.h new file mode 100644 index 00000000000..0e69674bc83 --- /dev/null +++ b/runtime/vm/simulator_dbc.h @@ -0,0 +1,182 @@ +// 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. + +#ifndef VM_SIMULATOR_DBC_H_ +#define VM_SIMULATOR_DBC_H_ + +#ifndef VM_SIMULATOR_H_ +#error Do not include simulator_dbc.h directly; use simulator.h. +#endif + +#include "vm/constants_dbc.h" +#include "vm/method_recognizer.h" + +namespace dart { + +class Isolate; +class RawObject; +class SimulatorSetjmpBuffer; +class Thread; +class Code; +class Array; +class RawICData; +class RawArray; +class RawObjectPool; +class RawFunction; + +// Simulator intrinsic handler. It is invoked on entry to the intrinsified +// function via Intrinsic bytecode before the frame is setup. +// If the handler returns true then Intrinsic bytecode works as a return +// instruction returning the value in result. Otherwise interpreter proceeds to +// execute the body of the function. +typedef bool (*IntrinsicHandler)(Thread* thread, + RawObject** FP, + RawObject** result); + + +class Simulator { + public: + static const uword kSimulatorStackUnderflowSize = 64; + + Simulator(); + ~Simulator(); + + // The currently executing Simulator instance, which is associated to the + // current isolate + static Simulator* Current(); + + // Accessors to the internal simulator stack base and top. + uword StackBase() const { return reinterpret_cast(stack_); } + uword StackTop() const; + + // The isolate's top_exit_frame_info refers to a Dart frame in the simulator + // stack. The simulator's top_exit_frame_info refers to a C++ frame in the + // native stack. + uword top_exit_frame_info() const { return top_exit_frame_info_; } + void set_top_exit_frame_info(uword value) { top_exit_frame_info_ = value; } + + // Call on program start. + static void InitOnce(); + + RawObject* Call(const Code& code, + const Array& arguments_descriptor, + const Array& arguments, + Thread* thread); + + void Longjmp(uword pc, + uword sp, + uword fp, + RawObject* raw_exception, + RawObject* raw_stacktrace, + Thread* thread); + + uword get_sp() const { + return reinterpret_cast(sp_); + } + + enum IntrinsicId { +#define V(test_class_name, test_function_name, enum_name, fp) \ + k##enum_name##Intrinsic, + ALL_INTRINSICS_LIST(V) + GRAPH_INTRINSICS_LIST(V) +#undef V + kIntrinsicCount, + }; + + static bool IsSupportedIntrinsic(IntrinsicId id) { + return intrinsics_[id] != NULL; + } + + enum SpecialIndex { + kExceptionSpecialIndex, + kStacktraceSpecialIndex, + kSpecialIndexCount + }; + + private: + uintptr_t* stack_; + + RawObject** fp_; + RawObject** sp_; + uword pc_; + + SimulatorSetjmpBuffer* last_setjmp_buffer_; + uword top_exit_frame_info_; + + RawObject* special_[kSpecialIndexCount]; + + static IntrinsicHandler intrinsics_[kIntrinsicCount]; + + void Exit(Thread* thread, + RawObject** base, + RawObject** exit_frame, + uint32_t* pc); + + void CallRuntime(Thread* thread, + RawObject** base, + RawObject** exit_frame, + uint32_t* pc, + intptr_t argc_tag, + RawObject** args, + RawObject** result, + uword target); + + void Invoke(Thread* thread, + RawObject** call_base, + RawObject** call_top, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** B, + RawObject*** SP); + + void InlineCacheMiss(int checked_args, + Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** top, + uint32_t* pc, + RawObject** B, RawObject** SP); + + void InstanceCall1(Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** call_top, + RawArray** argdesc, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** B, RawObject*** SP); + + void InstanceCall2(Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** call_top, + RawArray** argdesc, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** B, RawObject*** SP); + + void InstanceCall3(Thread* thread, + RawICData* icdata, + RawObject** call_base, + RawObject** call_top, + RawArray** argdesc, + RawObjectPool** pp, + uint32_t** pc, + RawObject*** B, RawObject*** SP); + + // Longjmp support for exceptions. + SimulatorSetjmpBuffer* last_setjmp_buffer() { + return last_setjmp_buffer_; + } + void set_last_setjmp_buffer(SimulatorSetjmpBuffer* buffer) { + last_setjmp_buffer_ = buffer; + } + + friend class SimulatorSetjmpBuffer; + DISALLOW_COPY_AND_ASSIGN(Simulator); +}; + +} // namespace dart + +#endif // VM_SIMULATOR_DBC_H_ diff --git a/runtime/vm/simulator_mips.cc b/runtime/vm/simulator_mips.cc index 244b331d0ed..d23c755067c 100644 --- a/runtime/vm/simulator_mips.cc +++ b/runtime/vm/simulator_mips.cc @@ -43,7 +43,6 @@ DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX, // The runtime then does a Longjmp on that buffer to return to the simulator. class SimulatorSetjmpBuffer { public: - int Setjmp() { return setjmp(buffer_); } void Longjmp() { // "This" is now the last setjmp buffer. simulator_->set_last_setjmp_buffer(this); @@ -1257,7 +1256,7 @@ void Simulator::DoBreak(Instr *instr) { ASSERT(sizeof(NativeArguments) == 4*kWordSize); arguments.thread_ = reinterpret_cast(get_register(A0)); arguments.argc_tag_ = get_register(A1); - arguments.argv_ = reinterpret_cast(get_register(A2)); + arguments.argv_ = reinterpret_cast(get_register(A2)); arguments.retval_ = reinterpret_cast(get_register(A3)); SimulatorRuntimeCall target = reinterpret_cast(external); diff --git a/runtime/vm/simulator_mips.h b/runtime/vm/simulator_mips.h index 8d10c3f2d7e..700b1f5e2ba 100644 --- a/runtime/vm/simulator_mips.h +++ b/runtime/vm/simulator_mips.h @@ -58,6 +58,10 @@ class Simulator { int64_t get_dregister_bits(DRegister freg) const; double get_dregister(DRegister freg) const; + int32_t get_sp() const { + return get_register(SPREG); + } + // Accessor for the pc. void set_pc(int32_t value) { pc_ = value; } int32_t get_pc() const { return pc_; } diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc index ebfe69ef7a3..aac196b171d 100644 --- a/runtime/vm/stack_frame.cc +++ b/runtime/vm/stack_frame.cc @@ -69,6 +69,7 @@ void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { +#if !defined(TARGET_ARCH_DBC) ASSERT(thread() == Thread::Current()); // Visit objects between SP and (FP - callee_save_area). ASSERT(visitor != NULL); @@ -76,10 +77,17 @@ void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { RawObject** last = reinterpret_cast( fp() + (kExitLinkSlotFromEntryFp - 1) * kWordSize); visitor->VisitPointers(first, last); +#else + // On DBC stack is growing upwards which implies fp() <= sp(). + RawObject** first = reinterpret_cast(fp()); + RawObject** last = reinterpret_cast(sp()); + visitor->VisitPointers(first, last); +#endif } void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { +#if !defined(TARGET_ARCH_DBC) // NOTE: This code runs while GC is in progress and runs within // a NoHandleScope block. Hence it is not ok to use regular Zone or // Scope handles. We use direct stack handles, the raw pointers in @@ -159,6 +167,13 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { RawObject** last = reinterpret_cast( fp() + (kFirstObjectSlotFromFp * kWordSize)); visitor->VisitPointers(first, last); +#else + // On DBC stack grows upwards: fp() <= sp(). + RawObject** first = reinterpret_cast( + fp() + (kFirstObjectSlotFromFp * kWordSize)); + RawObject** last = reinterpret_cast(sp()); + visitor->VisitPointers(first, last); +#endif // !defined(TARGET_ARCH_DBC) } @@ -319,6 +334,7 @@ StackFrameIterator::StackFrameIterator(uword last_fp, bool validate, } +#if !defined(TARGET_ARCH_DBC) StackFrameIterator::StackFrameIterator(uword fp, uword sp, uword pc, bool validate, Thread* thread) : validate_(validate), @@ -333,6 +349,7 @@ StackFrameIterator::StackFrameIterator(uword fp, uword sp, uword pc, frames_.sp_ = sp; frames_.pc_ = pc; } +#endif StackFrame* StackFrameIterator::NextFrame() { @@ -353,6 +370,7 @@ StackFrame* StackFrameIterator::NextFrame() { return NULL; } UnpoisonStack(frames_.fp_); +#if !defined(TARGET_ARCH_DBC) if (frames_.pc_ == 0) { // Iteration starts from an exit frame given by its fp. current_frame_ = NextExitFrame(); @@ -364,6 +382,12 @@ StackFrame* StackFrameIterator::NextFrame() { // Iteration starts from a Dart or stub frame given by its fp, sp, and pc. current_frame_ = frames_.NextFrame(validate_); } +#else + // Iteration starts from an exit frame given by its fp. This is the only + // mode supported on DBC. + ASSERT(frames_.pc_ == 0); + current_frame_ = NextExitFrame(); +#endif // !defined(TARGET_ARCH_DBC) return current_frame_; } ASSERT((validate_ == kDontValidateFrames) || current_frame_->IsValid()); diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h index 2d1b12c2f2e..dac3e5fb714 100644 --- a/runtime/vm/stack_frame.h +++ b/runtime/vm/stack_frame.h @@ -19,6 +19,8 @@ #include "vm/stack_frame_arm64.h" #elif defined(TARGET_ARCH_MIPS) #include "vm/stack_frame_mips.h" +#elif defined(TARGET_ARCH_DBC) +#include "vm/stack_frame_dbc.h" #else #error Unknown architecture. #endif @@ -101,10 +103,12 @@ class StackFrame : public ValueObject { uword GetCallerSp() const { return fp() + (kCallerSpSlotFromFp * kWordSize); } + uword GetCallerFp() const { return *(reinterpret_cast( - fp() + (kSavedCallerFpSlotFromFp * kWordSize))); + fp() + (kSavedCallerFpSlotFromFp * kWordSize))); } + uword GetCallerPc() const { return *(reinterpret_cast( fp() + (kSavedCallerPcSlotFromFp * kWordSize))); @@ -188,10 +192,12 @@ class StackFrameIterator : public ValueObject { StackFrameIterator(uword last_fp, bool validate, Thread* thread = Thread::Current()); +#if !defined(TARGET_ARCH_DBC) // Iterator for iterating over all frames from the current frame (given by its // fp, sp, and pc) to the first EntryFrame. StackFrameIterator(uword fp, uword sp, uword pc, bool validate, Thread* thread = Thread::Current()); +#endif // Checks if a next frame exists. bool HasNextFrame() const { return frames_.fp_ != 0; } @@ -272,6 +278,8 @@ class DartFrameIterator : public ValueObject { DartFrameIterator(uword last_fp, Thread* thread = Thread::Current()) : frames_(last_fp, StackFrameIterator::kDontValidateFrames, thread) { } + +#if !defined(TARGET_ARCH_DBC) DartFrameIterator(uword fp, uword sp, uword pc, @@ -279,6 +287,8 @@ class DartFrameIterator : public ValueObject { : frames_(fp, sp, pc, StackFrameIterator::kDontValidateFrames, thread) { } +#endif + // Get next dart frame. StackFrame* NextFrame() { StackFrame* frame = frames_.NextFrame(); @@ -336,6 +346,27 @@ class InlinedFunctionsIterator : public ValueObject { DISALLOW_COPY_AND_ASSIGN(InlinedFunctionsIterator); }; +#if !defined(TARGET_ARCH_DBC) +DART_FORCE_INLINE static uword LocalVarAddress(uword fp, intptr_t index) { + return fp + (index * kWordSize); +} + + +DART_FORCE_INLINE static uword ParamAddress(uword fp, intptr_t reverse_index) { + return fp + (kParamEndSlotFromFp * kWordSize) + (reverse_index * kWordSize); +} + + +DART_FORCE_INLINE static bool IsCalleeFrameOf(uword fp, uword other_fp) { + return other_fp < fp; +} + +// Value for stack limit that is used to cause an interrupt. +// Note that on DBC stack is growing upwards so interrupt limit is 0 unlike +// on all other architectures. +static const uword kInterruptStackLimit = ~static_cast(0); +#endif + } // namespace dart #endif // VM_STACK_FRAME_H_ diff --git a/runtime/vm/stack_frame_dbc.h b/runtime/vm/stack_frame_dbc.h new file mode 100644 index 00000000000..a59a417ed88 --- /dev/null +++ b/runtime/vm/stack_frame_dbc.h @@ -0,0 +1,85 @@ +// 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. + +#ifndef VM_STACK_FRAME_DBC_H_ +#define VM_STACK_FRAME_DBC_H_ + +namespace dart { + +/* DBC Frame Layout + +IMPORTANT: On DBC stack is growing upwards which is different from all other +architectures. This enables effecient addressing for locals via unsigned index. + + | | <- TOS +Callee frame | ... | + | saved FP | (FP of current frame) + | saved PC | (PC of current frame) + | code object | + | function object | + +--------------------+ +Current frame | ... T| <- SP of current frame + | ... T| + | first local T| <- FP of current frame + | caller's FP *| + | caller's PC *| + | code object T| (current frame's code object) + | function object T| (current frame's function object) + +--------------------+ +Caller frame | last parameter | <- SP of caller frame + | ... | + + T against a slot indicates it needs to be traversed during GC. + * against a slot indicates that it can be traversed during GC + because it will look like a smi to the visitor. +*/ + +static const int kDartFrameFixedSize = 4; // Function, Code, PC, FP +static const int kSavedPcSlotFromSp = 3; + +static const int kFirstObjectSlotFromFp = -4; // Used by GC to traverse stack. + +static const int kSavedCallerFpSlotFromFp = -1; +static const int kSavedCallerPpSlotFromFp = kSavedCallerFpSlotFromFp; +static const int kSavedCallerPcSlotFromFp = -2; +static const int kCallerSpSlotFromFp = -kDartFrameFixedSize-1; +static const int kPcMarkerSlotFromFp = -3; +static const int kFunctionSlotFromFp = -4; + +// Note: These constants don't match actual DBC behavior. This is done because +// setting kFirstLocalSlotFromFp to 0 breaks assumptions spread across the code. +// Instead for the purposes of local variable allocation we pretend that DBC +// behaves as other architectures (stack growing downwards) and later fix +// these indices during code generation in the backend. +static const int kParamEndSlotFromFp = 4; // One slot past last parameter. +static const int kFirstLocalSlotFromFp = -1; + + +DART_FORCE_INLINE static uword LocalVarAddress(uword fp, intptr_t index) { + ASSERT(index != 0); + if (index > 0) { + return fp - index * kWordSize; + } else { + return fp - (index + 1) * kWordSize; + } +} + +DART_FORCE_INLINE static uword ParamAddress(uword fp, intptr_t reverse_index) { + return fp - (kDartFrameFixedSize + reverse_index) * kWordSize; +} + +DART_FORCE_INLINE static bool IsCalleeFrameOf(uword fp, uword other_fp) { + return other_fp > fp; +} + +static const int kExitLinkSlotFromEntryFp = 0; + +// Value for stack limit that is used to cause an interrupt. +// Note that on DBC stack is growing upwards so interrupt limit is 0 unlike +// on all other architectures. +static const uword kInterruptStackLimit = 0; + +} // namespace dart + +#endif // VM_STACK_FRAME_DBC_H_ diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc index 6738a259e01..04ca4f49258 100644 --- a/runtime/vm/stub_code.cc +++ b/runtime/vm/stub_code.cc @@ -85,30 +85,47 @@ void StubCode::VisitObjectPointers(ObjectPointerVisitor* visitor) { bool StubCode::HasBeenInitialized() { +#if !defined(TARGET_ARCH_DBC) // Use JumpToExceptionHandler and InvokeDart as canaries. const StubEntry* entry_1 = StubCode::JumpToExceptionHandler_entry(); const StubEntry* entry_2 = StubCode::InvokeDartCode_entry(); return (entry_1 != NULL) && (entry_2 != NULL); +#else + return true; +#endif } bool StubCode::InInvocationStub(uword pc) { +#if !defined(TARGET_ARCH_DBC) ASSERT(HasBeenInitialized()); uword entry = StubCode::InvokeDartCode_entry()->EntryPoint(); uword size = StubCode::InvokeDartCodeSize(); return (pc >= entry) && (pc < (entry + size)); +#else + // On DBC we use a special marker PC to signify entry frame because there is + // no such thing as invocation stub. + return (pc & 2) != 0; +#endif } bool StubCode::InJumpToExceptionHandlerStub(uword pc) { +#if !defined(TARGET_ARCH_DBC) ASSERT(HasBeenInitialized()); uword entry = StubCode::JumpToExceptionHandler_entry()->EntryPoint(); uword size = StubCode::JumpToExceptionHandlerSize(); return (pc >= entry) && (pc < (entry + size)); +#else + // This stub does not exist on DBC. + return false; +#endif } RawCode* StubCode::GetAllocationStubForClass(const Class& cls) { + // These stubs are not used by DBC. +#if !defined(TARGET_ARCH_DBC) Thread* thread = Thread::Current(); Zone* zone = thread->zone(); const Error& error = Error::Handle(zone, cls.EnsureIsFinalized(thread)); @@ -169,11 +186,16 @@ RawCode* StubCode::GetAllocationStubForClass(const Class& cls) { } } return stub.raw(); +#endif + UNIMPLEMENTED(); + return Code::null(); } const StubEntry* StubCode::UnoptimizedStaticCallEntry( intptr_t num_args_tested) { + // These stubs are not used by DBC. +#if !defined(TARGET_ARCH_DBC) switch (num_args_tested) { case 0: return ZeroArgsUnoptimizedStaticCall_entry(); @@ -185,6 +207,9 @@ const StubEntry* StubCode::UnoptimizedStaticCallEntry( UNIMPLEMENTED(); return NULL; } +#else + return NULL; +#endif } diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h index fed306ba637..aa20321ec8f 100644 --- a/runtime/vm/stub_code.h +++ b/runtime/vm/stub_code.h @@ -18,9 +18,9 @@ class RawCode; class SnapshotReader; class SnapshotWriter; - // List of stubs created in the VM isolate, these stubs are shared by different // isolates running in this dart process. +#if !defined(TARGET_ARCH_DBC) #define VM_STUB_CODE_LIST(V) \ V(GetStackPointer) \ V(JumpToExceptionHandler) \ @@ -65,6 +65,13 @@ class SnapshotWriter; V(CallClosureNoSuchMethod) \ V(FrameAwaitingMaterialization) \ +#else +#define VM_STUB_CODE_LIST(V) \ + V(LazyCompile) \ + V(FixCallersTarget) \ + +#endif // !defined(TARGET_ARCH_DBC) + // Is it permitted for the stubs above to refer to Object::null(), which is // allocated in the VM isolate and shared across all isolates. // However, in cases where a simple GC-safe placeholder is needed on the stack, diff --git a/runtime/vm/stub_code_dbc.cc b/runtime/vm/stub_code_dbc.cc new file mode 100644 index 00000000000..0042f2da5c9 --- /dev/null +++ b/runtime/vm/stub_code_dbc.cc @@ -0,0 +1,66 @@ +// 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 "vm/globals.h" +#if defined(TARGET_ARCH_DBC) + +#include "vm/assembler.h" +#include "vm/code_generator.h" +#include "vm/cpu.h" +#include "vm/compiler.h" +#include "vm/dart_entry.h" +#include "vm/flow_graph_compiler.h" +#include "vm/heap.h" +#include "vm/instructions.h" +#include "vm/object_store.h" +#include "vm/stack_frame.h" +#include "vm/stub_code.h" +#include "vm/tags.h" + +#define __ assembler-> + +namespace dart { + +DEFINE_FLAG(bool, inline_alloc, true, "Inline allocation of objects."); +DEFINE_FLAG(bool, use_slow_path, false, + "Set to true for debugging & verifying the slow paths."); +DECLARE_FLAG(bool, trace_optimized_ic_calls); +DECLARE_FLAG(int, optimization_counter_threshold); +DECLARE_FLAG(bool, support_debugger); +DECLARE_FLAG(bool, lazy_dispatchers); + +void StubCode::GenerateLazyCompileStub(Assembler* assembler) { + __ Compile(); +} + + +// TODO(vegorov) Don't generate this stub. +void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) { + __ Trap(); +} + + +// TODO(vegorov) Don't generate these stubs. +void StubCode::GenerateAllocationStubForClass(Assembler* assembler, + const Class& cls) { + __ Trap(); +} + + +// TODO(vegorov) Don't generate this stub. +void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) { + __ Trap(); +} + + +// Print the stop message. +DEFINE_LEAF_RUNTIME_ENTRY(void, PrintStopMessage, 1, const char* message) { + OS::Print("Stop message: %s\n", message); +} +END_LEAF_RUNTIME_ENTRY + + +} // namespace dart + +#endif // defined TARGET_ARCH_DBC diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc index 5775145f604..3c7efeeebeb 100644 --- a/runtime/vm/thread.cc +++ b/runtime/vm/thread.cc @@ -318,6 +318,7 @@ void Thread::PrepareForGC() { void Thread::SetStackLimitFromStackBase(uword stack_base) { // Set stack limit. +#if !defined(TARGET_ARCH_DBC) #if defined(USING_SIMULATOR) // Ignore passed-in native stack top and use Simulator stack top. Simulator* sim = Simulator::Current(); // May allocate a simulator. @@ -326,6 +327,9 @@ void Thread::SetStackLimitFromStackBase(uword stack_base) { // The overflow area is accounted for by the simulator. #endif SetStackLimit(stack_base - OSThread::GetSpecifiedStackSize()); +#else + SetStackLimit(Simulator::Current()->StackTop()); +#endif // !defined(TARGET_ARCH_DBC) } @@ -348,11 +352,15 @@ void Thread::ClearStackLimit() { /* static */ uword Thread::GetCurrentStackPointer() { +#if !defined(TARGET_ARCH_DBC) // Since AddressSanitizer's detect_stack_use_after_return instruments the // C++ code to give out fake stack addresses, we call a stub in that case. ASSERT(StubCode::GetStackPointer_entry() != NULL); uword (*func)() = reinterpret_cast( StubCode::GetStackPointer_entry()->EntryPoint()); +#else + uword (*func)() = NULL; +#endif // But for performance (and to support simulators), we normally use a local. #if defined(__has_feature) #if __has_feature(address_sanitizer) @@ -390,7 +398,7 @@ void Thread::ScheduleInterruptsLocked(uword interrupt_bits) { } if (stack_limit_ == saved_stack_limit_) { - stack_limit_ = (~static_cast(0)) & ~kInterruptsMask; + stack_limit_ = kInterruptStackLimit & ~kInterruptsMask; } stack_limit_ |= interrupt_bits; } @@ -437,7 +445,7 @@ void Thread::RestoreOOBMessageInterrupts() { deferred_interrupts_mask_ = 0; if (deferred_interrupts_ != 0) { if (stack_limit_ == saved_stack_limit_) { - stack_limit_ = (~static_cast(0)) & ~kInterruptsMask; + stack_limit_ = kInterruptStackLimit & ~kInterruptsMask; } stack_limit_ |= deferred_interrupts_; deferred_interrupts_ = 0; diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h index 8e126f5337f..4a21cc56f28 100644 --- a/runtime/vm/thread.h +++ b/runtime/vm/thread.h @@ -70,11 +70,10 @@ class Zone; V(TypeParameter) \ -// List of VM-global objects/addresses cached in each Thread object. -#define CACHED_VM_OBJECTS_LIST(V) \ - V(RawObject*, object_null_, Object::null(), NULL) \ - V(RawBool*, bool_true_, Object::bool_true().raw(), NULL) \ - V(RawBool*, bool_false_, Object::bool_false().raw(), NULL) \ +#if defined(TARGET_ARCH_DBC) +#define CACHED_VM_STUBS_LIST(V) +#else +#define CACHED_VM_STUBS_LIST(V) \ V(RawCode*, update_store_buffer_code_, \ StubCode::UpdateStoreBuffer_entry()->code(), NULL) \ V(RawCode*, fix_callers_target_code_, \ @@ -86,11 +85,28 @@ class Zone; V(RawCode*, call_to_runtime_stub_, \ StubCode::CallToRuntime_entry()->code(), NULL) \ -#define CACHED_ADDRESSES_LIST(V) \ +#endif + +// List of VM-global objects/addresses cached in each Thread object. +#define CACHED_VM_OBJECTS_LIST(V) \ + V(RawObject*, object_null_, Object::null(), NULL) \ + V(RawBool*, bool_true_, Object::bool_true().raw(), NULL) \ + V(RawBool*, bool_false_, Object::bool_false().raw(), NULL) \ + CACHED_VM_STUBS_LIST(V) \ + +#if defined(TARGET_ARCH_DBC) +#define CACHED_VM_STUBS_ADDRESSES_LIST(V) +#else +#define CACHED_VM_STUBS_ADDRESSES_LIST(V) \ V(uword, update_store_buffer_entry_point_, \ StubCode::UpdateStoreBuffer_entry()->EntryPoint(), 0) \ V(uword, call_to_runtime_entry_point_, \ StubCode::CallToRuntime_entry()->EntryPoint(), 0) \ + +#endif + +#define CACHED_ADDRESSES_LIST(V) \ + CACHED_VM_STUBS_ADDRESSES_LIST(V) \ V(uword, native_call_wrapper_entry_point_, \ NativeEntry::NativeCallWrapperEntry(), 0) \ V(RawString**, predefined_symbols_address_, \ @@ -175,6 +191,13 @@ class Thread : public BaseThread { // The true stack limit for this isolate. uword saved_stack_limit() const { return saved_stack_limit_; } +#if defined(TARGET_ARCH_DBC) + // Access to the current stack limit for DBC interpreter. + uword stack_limit() const { + return stack_limit_; + } +#endif + // Stack overflow flags enum { kOsrRequest = 0x1, // Current stack overflow caused by OSR request. diff --git a/runtime/vm/unit_test.h b/runtime/vm/unit_test.h index defaea68c6c..c7e3186e856 100644 --- a/runtime/vm/unit_test.h +++ b/runtime/vm/unit_test.h @@ -157,10 +157,12 @@ #if defined(TARGET_ARCH_ARM) || \ defined(TARGET_ARCH_MIPS) || \ - defined(TARGET_ARCH_ARM64) + defined(TARGET_ARCH_ARM64) || \ + defined(TARGET_ARCH_DBC) #if defined(HOST_ARCH_ARM) || \ defined(HOST_ARCH_MIPS) || \ - defined(HOST_ARCH_ARM64) + defined(HOST_ARCH_ARM64) || \ + !defined(TARGET_ARCH_DBC) // Running on actual ARM or MIPS hardware, execute code natively. #define EXECUTE_TEST_CODE_INT32(name, entry) reinterpret_cast(entry)() #define EXECUTE_TEST_CODE_INT64(name, entry) reinterpret_cast(entry)() @@ -374,7 +376,7 @@ class AssemblerTest { // using the ABI calling convention. // ResultType is the return type of the assembler test function. // ArgNType is the type of the Nth argument. -#if defined(USING_SIMULATOR) +#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC) #if defined(ARCH_IS_64_BIT) // TODO(fschneider): Make InvokeWithCodeAndThread<> more general and work on @@ -454,7 +456,7 @@ class AssemblerTest { typedef ResultType (*FunctionType) (Arg1Type, Arg2Type, Arg3Type); return reinterpret_cast(entry())(arg1, arg2, arg3); } -#endif // USING_SIMULATOR +#endif // defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC) // Assemble test and set code_. void Assemble(); diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi index f73d16de86f..fa053a3b512 100644 --- a/runtime/vm/vm_sources.gypi +++ b/runtime/vm/vm_sources.gypi @@ -19,6 +19,8 @@ 'assembler_arm64.cc', 'assembler_arm64.h', 'assembler_arm64_test.cc', + 'assembler_dbc.cc', + 'assembler_dbc.h', 'assembler_ia32.cc', 'assembler_ia32.h', 'assembler_ia32_test.cc', @@ -89,6 +91,7 @@ 'code_patcher_arm_test.cc', 'code_patcher_arm64.cc', 'code_patcher_arm64_test.cc', + 'code_patcher_dbc.cc', 'code_patcher_ia32.cc', 'code_patcher_ia32_test.cc', 'code_patcher_mips.cc', @@ -110,6 +113,7 @@ 'cpu.h', 'cpu_arm.cc', 'cpu_arm64.cc', + 'cpu_dbc.cc', 'cpu_ia32.cc', 'cpu_mips.cc', 'cpu_test.cc', @@ -140,6 +144,7 @@ 'debugger_api_impl_test.cc', 'debugger_arm.cc', 'debugger_arm64.cc', + 'debugger_dbc.cc', 'debugger_ia32.cc', 'debugger_mips.cc', 'debugger_x64.cc', @@ -151,6 +156,7 @@ 'disassembler.h', 'disassembler_arm.cc', 'disassembler_arm64.cc', + 'disassembler_dbc.cc', 'disassembler_ia32.cc', 'disassembler_mips.cc', 'disassembler_test.cc', @@ -177,6 +183,7 @@ 'flow_graph_compiler.h', 'flow_graph_compiler_arm.cc', 'flow_graph_compiler_arm64.cc', + 'flow_graph_compiler_dbc.cc', 'flow_graph_compiler_ia32.cc', 'flow_graph_compiler_mips.cc', 'flow_graph_compiler_x64.cc', @@ -218,6 +225,8 @@ 'instructions_arm64.cc', 'instructions_arm64.h', 'instructions_arm64_test.cc', + 'instructions_dbc.cc', + 'instructions_dbc.h', 'instructions_ia32.cc', 'instructions_ia32.h', 'instructions_ia32_test.cc', @@ -231,6 +240,7 @@ 'intermediate_language.h', 'intermediate_language_arm.cc', 'intermediate_language_arm64.cc', + 'intermediate_language_dbc.cc', 'intermediate_language_ia32.cc', 'intermediate_language_mips.cc', 'intermediate_language_test.cc', @@ -239,6 +249,7 @@ 'intrinsifier.h', 'intrinsifier_arm.cc', 'intrinsifier_arm64.cc', + 'intrinsifier_dbc.cc', 'intrinsifier_ia32.cc', 'intrinsifier_mips.cc', 'intrinsifier_x64.cc', @@ -377,6 +388,7 @@ 'runtime_entry_list.h', 'runtime_entry_arm.cc', 'runtime_entry_arm64.cc', + 'runtime_entry_dbc.cc', 'runtime_entry_ia32.cc', 'runtime_entry_mips.cc', 'runtime_entry.cc', @@ -410,6 +422,8 @@ 'simulator_arm.h', 'simulator_arm64.cc', 'simulator_arm64.h', + 'simulator_dbc.cc', + 'simulator_dbc.h', 'simulator_mips.cc', 'simulator_mips.h', 'snapshot.cc', @@ -436,6 +450,7 @@ 'stub_code_arm_test.cc', 'stub_code_arm64.cc', 'stub_code_arm64_test.cc', + 'stub_code_dbc.cc', 'stub_code_ia32.cc', 'stub_code_ia32_test.cc', 'stub_code_mips.cc', diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status index 57ac699d3fa..0b78b116e5d 100644 --- a/tests/co19/co19-runtime.status +++ b/tests/co19/co19-runtime.status @@ -163,3 +163,8 @@ LibTest/typed_data/Uint32List/runtimeType_A01_t01: Fail,OK # Expects exact type LibTest/typed_data/Uint64List/runtimeType_A01_t01: Fail,OK # Expects exact type name. LibTest/typed_data/Uint8ClampedList/runtimeType_A01_t01: Fail,OK # Expects exact type name. LibTest/typed_data/Uint8List/runtimeType_A01_t01: Fail,OK # Expects exact type name. + +[ $arch == simdbc && $mode == debug ] +# TODO(vegorov) These tests are very slow on unoptimized SIMDBC +LibTest/collection/ListMixin/ListMixin_class_A01_t02: Timeout +LibTest/collection/ListBase/ListBase_class_A01_t02: Timeout diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status index fe92613e7d6..d957e7f7b1c 100644 --- a/tests/corelib/corelib.status +++ b/tests/corelib/corelib.status @@ -207,4 +207,7 @@ int_parse_radix_test: Pass, Timeout # --no_intrinsify data_resource_test: Skip # Resolve URI not supported yet in product mode. package_resource_test: Skip # Resolve URI not supported yet in product mode. file_resource_test: Skip # Resolve URI not supported yet in product mode. -http_resource_test: Skip # Resolve URI not supported yet in product mode. \ No newline at end of file +http_resource_test: Skip # Resolve URI not supported yet in product mode. + +[ $arch == simdbc ] +regexp/stack-overflow_test: RuntimeError, OK # Smaller limit with irregex interpreter diff --git a/tests/language/language.status b/tests/language/language.status index 428e0d1bddd..e62cb7a5ec0 100644 --- a/tests/language/language.status +++ b/tests/language/language.status @@ -211,3 +211,11 @@ library_env_test/has_no_mirror_support: RuntimeError, OK [ $noopt || $compiler == precompiler || $mode == product ] # The following tests are supposed to fail. library_env_test/has_mirror_support: RuntimeError, OK + +[ $arch == simdbc ] +# TODO(vegorov) StopInstr is unimplemented. +vm/debug_break_enabled_vm_test/none: Skip + +# TODO(vegorov) Encoding limitation: StoreField bytecode only supports 256 +# fields in an object. +large_class_declaration_test: Skip diff --git a/tests/lib/lib.status b/tests/lib/lib.status index 9271009b889..7edcb91fa46 100644 --- a/tests/lib/lib.status +++ b/tests/lib/lib.status @@ -362,3 +362,8 @@ mirrors/*: SkipByDesign [ $noopt || $compiler == precompiler ] convert/chunked_conversion_utf88_test: Pass, Timeout convert/utf85_test: Pass, Timeout + +[ $arch == simdbc ] +# TODO(vegorov) LoadField bytecode supports only up to 256 fields. Need a long +# version. +mirrors/accessor_cache_overflow_test: Skip diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status index 83c44afb686..d8ee964f29d 100644 --- a/tests/standalone/standalone.status +++ b/tests/standalone/standalone.status @@ -285,3 +285,13 @@ package/scenarios/invalid/non_existent_packages_file_test: Skip package/scenarios/empty_packages_file/empty_packages_file_noimports_test: Skip package/scenarios/packages_option_only/packages_option_only_noimports_test: Skip package/scenarios/packages_option_only/packages_option_only_test: Skip + +[ $arch == simdbc ] +# TODO(vegorov) SIMDBC interpreter doesn't support coverage yet. +full_coverage_test: Skip + +# SIMDBC interpreter doesn't support lazy linking of natives. +link_natives_lazily_test: SkipByDesign + +# SIMDBC interpreter doesn't support --no_lazy_dispatchers +no_lazy_dispatchers_test: SkipByDesign diff --git a/tools/build.py b/tools/build.py index 3ec70d2257a..e15b699f5a6 100755 --- a/tools/build.py +++ b/tools/build.py @@ -56,7 +56,7 @@ def BuildOptions(): result.add_option("-a", "--arch", help='Target architectures (comma-separated).', metavar='[all,ia32,x64,simarm,arm,simarmv6,armv6,simarmv5te,armv5te,' - 'simmips,mips,simarm64,arm64,]', + 'simmips,mips,simarm64,arm64,simdbc,]', default=utils.GuessArchitecture()) result.add_option("--os", help='Target OSs (comma-separated).', @@ -102,7 +102,8 @@ def ProcessOptions(options, args): return False for arch in options.arch: archs = ['ia32', 'x64', 'simarm', 'arm', 'simarmv6', 'armv6', - 'simarmv5te', 'armv5te', 'simmips', 'mips', 'simarm64', 'arm64',] + 'simarmv5te', 'armv5te', 'simmips', 'mips', 'simarm64', 'arm64', + 'simdbc',] if not arch in archs: print "Unknown arch %s" % arch return False @@ -119,7 +120,8 @@ def ProcessOptions(options, args): print ("Cross-compilation to %s is not supported on host os %s." % (os_name, HOST_OS)) return False - if not arch in ['ia32', 'x64', 'arm', 'armv6', 'armv5te', 'arm64', 'mips']: + if not arch in ['ia32', 'x64', 'arm', 'armv6', 'armv5te', 'arm64', 'mips', + 'simdbc',]: print ("Cross-compilation to %s is not supported for architecture %s." % (os_name, arch)) return False @@ -137,7 +139,7 @@ def GetToolchainPrefix(target_os, arch, options): if target_os == 'android': android_toolchain = GetAndroidToolchainDir(HOST_OS, arch) - if arch == 'arm': + if arch == 'arm' or arch == 'simdbc': return os.path.join(android_toolchain, 'arm-linux-androideabi') if arch == 'arm64': return os.path.join(android_toolchain, 'aarch64-linux-android') @@ -197,7 +199,7 @@ def GetAndroidToolchainDir(host_os, target_arch): global THIRD_PARTY_ROOT if host_os not in ['linux']: raise Exception('Unsupported host os %s' % host_os) - if target_arch not in ['ia32', 'x64', 'arm', 'arm64']: + if target_arch not in ['ia32', 'x64', 'arm', 'arm64', 'simdbc']: raise Exception('Unsupported target architecture %s' % target_arch) # Set up path to the Android NDK. diff --git a/tools/gyp/configurations.gypi b/tools/gyp/configurations.gypi index 6a4b796618b..0957d818935 100644 --- a/tools/gyp/configurations.gypi +++ b/tools/gyp/configurations.gypi @@ -26,6 +26,7 @@ ['"<(target_arch)"=="simarm64"', { 'dart_target_arch': 'SIMARM64', }], ['"<(target_arch)"=="mips"', { 'dart_target_arch': 'MIPS', }], ['"<(target_arch)"=="simmips"', { 'dart_target_arch': 'SIMMIPS', }], + ['"<(target_arch)"=="simdbc"', { 'dart_target_arch': 'SIMDBC', }], [ 'OS=="linux"', { 'dart_target_os': 'Linux', } ], [ 'OS=="mac"', { 'dart_target_os': 'Macos', } ], [ 'OS=="win"', { 'dart_target_os': 'Win', } ], @@ -132,6 +133,14 @@ ], }, + 'Dart_simdbc_Base': { + 'abstract': 1, + 'defines': [ + 'TARGET_ARCH_DBC', + 'USING_SIMULATOR', + ] + }, + 'Dart_Debug': { 'abstract': 1, }, @@ -356,6 +365,26 @@ ], }, + 'DebugSIMDBC': { + 'inherit_from': [ + 'Dart_Base', 'Dart_simdbc_Base', 'Dart_Debug', + 'Dart_<(dart_target_os)_Base', + 'Dart_<(dart_target_os)_simdbc_Base', + 'Dart_<(dart_target_os)_Debug', + ], + 'defines': [ + 'DEBUG', + ], + }, + + 'ReleaseSIMDBC': { + 'inherit_from': [ + 'Dart_Base', 'Dart_simdbc_Base', 'Dart_Release', + 'Dart_<(dart_target_os)_Base', + 'Dart_<(dart_target_os)_simdbc_Base', + 'Dart_<(dart_target_os)_Release', + ], + }, # ARM and MIPS hardware configurations are only for Linux and Android. 'DebugXARM': { diff --git a/tools/gyp/configurations_make.gypi b/tools/gyp/configurations_make.gypi index 98d28fa5328..c86ffc291ef 100644 --- a/tools/gyp/configurations_make.gypi +++ b/tools/gyp/configurations_make.gypi @@ -113,6 +113,12 @@ ], }, + 'Dart_Linux_simdbc_Base': { + 'abstract': 1, + 'cflags': [ '-O3', '-m32', '-msse2', '-mfpmath=sse' ], + 'ldflags': [ '-m32', ], + }, + # ARM cross-build 'Dart_Linux_xarm_Base': { 'abstract': 1, diff --git a/tools/testing/dart/runtime_configuration.dart b/tools/testing/dart/runtime_configuration.dart index 8766322b3b4..4edacc3acd7 100644 --- a/tools/testing/dart/runtime_configuration.dart +++ b/tools/testing/dart/runtime_configuration.dart @@ -171,6 +171,7 @@ class DartVmRuntimeConfiguration extends RuntimeConfiguration { case 'simmips': case 'mips': case 'simarm64': + case 'simdbc': multiplier *= 4; break; } diff --git a/tools/testing/dart/test_options.dart b/tools/testing/dart/test_options.dart index 9cbd3c2b0be..b085f9498e2 100644 --- a/tools/testing/dart/test_options.dart +++ b/tools/testing/dart/test_options.dart @@ -148,7 +148,8 @@ class TestOptionsParser { 'simarmv6', 'simarmv5te', 'simarm64', - 'simmips' + 'simmips', + 'simdbc' ], 'x64'), new _TestOptionSpecification( diff --git a/tools/utils.py b/tools/utils.py index 6965a1c4949..dc862116373 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -240,6 +240,7 @@ ARCH_FAMILY = { 'simarmv5te': 'ia32', 'simmips': 'ia32', 'simarm64': 'ia32', + 'simdbc': 'ia32', } ARCH_GUESS = GuessArchitecture()