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 .
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<int32_t*>(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<int32_t>(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<int32_t>(position);
|
||||
buffer_.Store<int32_t>(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
|
||||
@@ -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<intptr_t>& 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<CodeComment*> comments_;
|
||||
|
||||
DISALLOW_ALLOCATION();
|
||||
DISALLOW_COPY_AND_ASSIGN(Assembler);
|
||||
};
|
||||
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // VM_ASSEMBLER_DBC_H_
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
@@ -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] <op> 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<Opcode>(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_
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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<char*>(cpumodel));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
||||
|
||||
@@ -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<invokestub>(
|
||||
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<RawObject*, int64_t>(Simulator::Current()->Call(
|
||||
reinterpret_cast<intptr_t>(entrypoint),
|
||||
reinterpret_cast<intptr_t>(&code),
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
+20
-14
@@ -231,7 +231,9 @@ void Breakpoint::PrintJSON(JSONStream* stream) {
|
||||
|
||||
void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
||||
visitor->VisitPointer(reinterpret_cast<RawObject**>(&code_));
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
visitor->VisitPointer(reinterpret_cast<RawObject**>(&saved_value_));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -859,24 +861,25 @@ intptr_t ActivationFrame::NumLocalVariables() {
|
||||
}
|
||||
|
||||
|
||||
DART_FORCE_INLINE static RawObject* GetVariableValue(uword addr) {
|
||||
return *reinterpret_cast<RawObject**>(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<RawObject*>(
|
||||
*reinterpret_cast<uword*>(var_address));
|
||||
return GetVariableValue(LocalVarAddress(fp(),
|
||||
(kFirstLocalSlotFromFp - index)));
|
||||
} else {
|
||||
uword var_address = fp() + (kParamEndSlotFromFp * kWordSize)
|
||||
+ (reverse_index * kWordSize);
|
||||
return reinterpret_cast<RawObject*>(
|
||||
*reinterpret_cast<uword*>(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<RawObject*>(
|
||||
*reinterpret_cast<uword*>(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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<RawCode*>(static_cast<uintptr_t>(saved_value_));
|
||||
}
|
||||
|
||||
|
||||
static Instr* CallInstructionFromReturnAddress(uword pc) {
|
||||
return reinterpret_cast<Instr*>(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
|
||||
@@ -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<RawObject**>(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<RawObject**>(dest_addr) =
|
||||
StubCode::FrameAwaitingMaterialization_entry()->code();
|
||||
#endif
|
||||
deopt_context->DeferPcMarkerMaterialization(object_table_index_, dest_addr);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <typename ValueType>
|
||||
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<int32_t>(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<int32_t>(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<int32_t>(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<uint32_t*>(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
|
||||
@@ -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;
|
||||
|
||||
+10
-4
@@ -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") \
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<intptr_t>& 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);
|
||||
|
||||
@@ -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<LocalVariable*>(num_opt_named_params);
|
||||
int* opt_param_position = zone()->Alloc<int>(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
|
||||
@@ -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
|
||||
|
||||
@@ -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<RawCode*>(
|
||||
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<NativeFunction>(
|
||||
object_pool_.RawValueAt(native_function_pool_index_));
|
||||
}
|
||||
|
||||
|
||||
void NativeCallPattern::set_native_function(NativeFunction func) const {
|
||||
object_pool_.SetRawValueAt(native_function_pool_index_,
|
||||
reinterpret_cast<uword>(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<RawCode*>(
|
||||
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<RawCode*>(
|
||||
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
|
||||
@@ -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_
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<uword>(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<uword>(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
|
||||
@@ -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<const ICData*>* ic_data_array =
|
||||
new ZoneGrowableArray<const ICData*>();
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
};
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ const uint8_t* NativeEntry::ResolveSymbol(uword pc) {
|
||||
|
||||
uword NativeEntry::NativeCallWrapperEntry() {
|
||||
uword entry = reinterpret_cast<uword>(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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+15
-1
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
+15
-3
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<intptr_t>(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:
|
||||
|
||||
@@ -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<uword>(function());
|
||||
}
|
||||
|
||||
|
||||
void RuntimeEntry::Call(Assembler* assembler, intptr_t argument_count) const {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // defined TARGET_ARCH_DBC
|
||||
@@ -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_...)
|
||||
|
||||
@@ -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<Thread*>(get_register(R0));
|
||||
arguments.argc_tag_ = get_register(R1);
|
||||
arguments.argv_ = reinterpret_cast<RawObject*(*)[]>(get_register(R2));
|
||||
arguments.argv_ = reinterpret_cast<RawObject**>(get_register(R2));
|
||||
arguments.retval_ = reinterpret_cast<RawObject**>(get_register(R3));
|
||||
SimulatorRuntimeCall target =
|
||||
reinterpret_cast<SimulatorRuntimeCall>(external);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<uword>(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<uword>(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_
|
||||
@@ -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<Thread*>(get_register(A0));
|
||||
arguments.argc_tag_ = get_register(A1);
|
||||
arguments.argv_ = reinterpret_cast<RawObject*(*)[]>(get_register(A2));
|
||||
arguments.argv_ = reinterpret_cast<RawObject**>(get_register(A2));
|
||||
arguments.retval_ = reinterpret_cast<RawObject**>(get_register(A3));
|
||||
SimulatorRuntimeCall target =
|
||||
reinterpret_cast<SimulatorRuntimeCall>(external);
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
@@ -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<RawObject**>(
|
||||
fp() + (kExitLinkSlotFromEntryFp - 1) * kWordSize);
|
||||
visitor->VisitPointers(first, last);
|
||||
#else
|
||||
// On DBC stack is growing upwards which implies fp() <= sp().
|
||||
RawObject** first = reinterpret_cast<RawObject**>(fp());
|
||||
RawObject** last = reinterpret_cast<RawObject**>(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<RawObject**>(
|
||||
fp() + (kFirstObjectSlotFromFp * kWordSize));
|
||||
visitor->VisitPointers(first, last);
|
||||
#else
|
||||
// On DBC stack grows upwards: fp() <= sp().
|
||||
RawObject** first = reinterpret_cast<RawObject**>(
|
||||
fp() + (kFirstObjectSlotFromFp * kWordSize));
|
||||
RawObject** last = reinterpret_cast<RawObject**>(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());
|
||||
|
||||
@@ -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<uword*>(
|
||||
fp() + (kSavedCallerFpSlotFromFp * kWordSize)));
|
||||
fp() + (kSavedCallerFpSlotFromFp * kWordSize)));
|
||||
}
|
||||
|
||||
uword GetCallerPc() const {
|
||||
return *(reinterpret_cast<uword*>(
|
||||
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<uword>(0);
|
||||
#endif
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // VM_STACK_FRAME_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_
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
+10
-2
@@ -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<uword (*)()>(
|
||||
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<uword>(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<uword>(0)) & ~kInterruptsMask;
|
||||
stack_limit_ = kInterruptStackLimit & ~kInterruptsMask;
|
||||
}
|
||||
stack_limit_ |= deferred_interrupts_;
|
||||
deferred_interrupts_ = 0;
|
||||
|
||||
+29
-6
@@ -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.
|
||||
|
||||
@@ -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<name>(entry)()
|
||||
#define EXECUTE_TEST_CODE_INT64(name, entry) reinterpret_cast<name>(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<FunctionType>(entry())(arg1, arg2, arg3);
|
||||
}
|
||||
#endif // USING_SIMULATOR
|
||||
#endif // defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC)
|
||||
|
||||
// Assemble test and set code_.
|
||||
void Assemble();
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+7
-5
@@ -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.
|
||||
|
||||
@@ -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': {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -171,6 +171,7 @@ class DartVmRuntimeConfiguration extends RuntimeConfiguration {
|
||||
case 'simmips':
|
||||
case 'mips':
|
||||
case 'simarm64':
|
||||
case 'simdbc':
|
||||
multiplier *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,8 @@ class TestOptionsParser {
|
||||
'simarmv6',
|
||||
'simarmv5te',
|
||||
'simarm64',
|
||||
'simmips'
|
||||
'simmips',
|
||||
'simdbc'
|
||||
],
|
||||
'x64'),
|
||||
new _TestOptionSpecification(
|
||||
|
||||
@@ -240,6 +240,7 @@ ARCH_FAMILY = {
|
||||
'simarmv5te': 'ia32',
|
||||
'simmips': 'ia32',
|
||||
'simarm64': 'ia32',
|
||||
'simdbc': 'ia32',
|
||||
}
|
||||
|
||||
ARCH_GUESS = GuessArchitecture()
|
||||
|
||||
Reference in New Issue
Block a user