diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index 47d3b8e1ee1f..f3a997971729 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -36,6 +36,7 @@ #include "vm/port.h" #include "vm/profiler.h" #include "vm/reusable_handles.h" +#include "vm/reverse_pc_lookup_cache.h" #include "vm/service.h" #include "vm/service_event.h" #include "vm/service_isolate.h" @@ -946,7 +947,8 @@ Isolate::Isolate(const Dart_IsolateFlags& api_flags) handler_info_cache_(), catch_entry_moves_cache_(), embedder_entry_points_(NULL), - obfuscation_map_(NULL) { + obfuscation_map_(NULL), + reverse_pc_lookup_cache_(nullptr) { FlagsCopyFrom(api_flags); SetErrorsFatal(true); set_compilation_allowed(true); @@ -974,6 +976,9 @@ Isolate::~Isolate() { // RELEASE_ASSERT(reload_context_ == NULL); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) + delete reverse_pc_lookup_cache_; + reverse_pc_lookup_cache_ = nullptr; + delete background_compiler_; background_compiler_ = NULL; diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index 25dc8dc15b97..c07bf001e5f5 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -64,6 +64,7 @@ class RawInteger; class RawFloat32x4; class RawInt32x4; class RawUserTag; +class ReversePcLookupCache; class SafepointHandler; class SampleBuffer; class SendPort; @@ -704,6 +705,17 @@ class Isolate : public BaseIsolate { void set_obfuscation_map(const char** map) { obfuscation_map_ = map; } const char** obfuscation_map() const { return obfuscation_map_; } + // Returns the pc -> code lookup cache object for this isolate. + ReversePcLookupCache* reverse_pc_lookup_cache() const { + return reverse_pc_lookup_cache_; + } + + // Sets the pc -> code lookup cache object for this isolate. + void set_reverse_pc_lookup_cache(ReversePcLookupCache* table) { + ASSERT(reverse_pc_lookup_cache_ == nullptr); + reverse_pc_lookup_cache_ = table; + } + // Isolate-specific flag handling. static void FlagsInitialize(Dart_IsolateFlags* api_flags); void FlagsCopyTo(Dart_IsolateFlags* api_flags) const; @@ -1006,6 +1018,8 @@ class Isolate : public BaseIsolate { Dart_QualifiedFunctionName* embedder_entry_points_; const char** obfuscation_map_; + ReversePcLookupCache* reverse_pc_lookup_cache_; + static Dart_IsolateCreateCallback create_callback_; static Dart_IsolateShutdownCallback shutdown_callback_; static Dart_IsolateCleanupCallback cleanup_callback_; diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 967a2fa52b74..110bf5c2e3d2 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -7947,6 +7947,8 @@ class Array : public Instance { return memcmp(a->ptr()->data(), b->ptr()->data(), kWordSize * length) == 0; } + static RawObject** DataOf(RawArray* array) { return array->ptr()->data(); } + RawObject* At(intptr_t index) const { return *ObjectAddr(index); } void SetAt(intptr_t index, const Object& value) const { // TODO(iposva): Add storing NoSafepointScope. diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h index cd760ded0944..6b9695d6e33c 100644 --- a/runtime/vm/object_store.h +++ b/runtime/vm/object_store.h @@ -124,6 +124,7 @@ class ObjectPointerVisitor; RW(Array, unique_dynamic_targets) \ RW(GrowableObjectArray, megamorphic_cache_table) \ RW(Code, build_method_extractor_code) \ + RW(Array, code_order_table) \ R_(Code, megamorphic_miss_code) \ R_(Function, megamorphic_miss_function) \ RW(Array, obfuscation_map) \ diff --git a/runtime/vm/reverse_pc_lookup_cache.cc b/runtime/vm/reverse_pc_lookup_cache.cc new file mode 100644 index 000000000000..325d98f49861 --- /dev/null +++ b/runtime/vm/reverse_pc_lookup_cache.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/reverse_pc_lookup_cache.h" + +#include "vm/isolate.h" + +namespace dart { + +#if defined(DART_PRECOMPILED_RUNTIME) + +static uword BeginPcFromCode(const RawCode* code) { + auto instr = Code::InstructionsOf(code); + return Instructions::PayloadStart(instr); +} + +static uword EndPcFromCode(const RawCode* code) { + auto instr = Code::InstructionsOf(code); + return Instructions::PayloadStart(instr) + Instructions::Size(instr); +} + +void ReversePcLookupCache::BuildAndAttachToIsolate(Isolate* isolate) { + auto object_store = isolate->object_store(); + auto& array = Array::Handle(object_store->code_order_table()); + if (!array.IsNull()) { + const intptr_t length = array.Length(); + { + NoSafepointScope no_safepoint_scope; + + const uword begin = + BeginPcFromCode(reinterpret_cast(array.At(0))); + const uword end = + EndPcFromCode(reinterpret_cast(array.At(length - 1))); + + auto pc_array = new uint32_t[length]; + for (intptr_t i = 0; i < length; i++) { + const auto end_pc = + EndPcFromCode(reinterpret_cast(array.At(i))); + pc_array[i] = end_pc - begin; + } +#if defined(DEBUG) + for (intptr_t i = 1; i < length; i++) { + ASSERT(pc_array[i - 1] <= pc_array[i]); + } +#endif // defined(DEBUG) + auto cache = + new ReversePcLookupCache(isolate, pc_array, length, begin, end); + isolate->set_reverse_pc_lookup_cache(cache); + } + } +} + +#endif // defined(DART_PRECOMPILED_RUNTIME) + +} // namespace dart diff --git a/runtime/vm/reverse_pc_lookup_cache.h b/runtime/vm/reverse_pc_lookup_cache.h new file mode 100644 index 000000000000..8300b622b2c4 --- /dev/null +++ b/runtime/vm/reverse_pc_lookup_cache.h @@ -0,0 +1,126 @@ +// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#ifndef RUNTIME_VM_REVERSE_PC_LOOKUP_CACHE_H_ +#define RUNTIME_VM_REVERSE_PC_LOOKUP_CACHE_H_ + +#include "vm/allocation.h" +#include "vm/growable_array.h" +#include "vm/object.h" +#include "vm/object_store.h" + +namespace dart { + +class Isolate; + +#if defined(DART_PRECOMPILED_RUNTIME) + +// A cache for looking up a Code object based on pc (currently the cache is +// implemented as a binary-searchable uint32 array) +// +// If an AOT snapshot was created with --use_bare_instructions the isolate's +// object store will contain a `code_order_table` - which is a sorted array +// of [Code] objects. The order is based on addresses of the code's +// instructions in memory. +// +// For a binary search we would need to touch O(log(array-size)) array entries, +// code objects and instruction objects. +// +// To avoid this we make another uint32 array which is initialized from the end +// PCs of the instructions (relative to the start pc of the first instruction +// object). +// +// We have the following invariants: +// +// BeginPcFromCode(code_array[0]) <= pc_array[0] +// pc_array[i] == EndPcFromCode(code_array[i]) +// pc_array[i] <= pc_array[i+1] +// +// The lookup will then do a binary search in pc_array. The index can then be +// used in the `code_order_table` of the object store. +// +// WARNING: This class cannot do memory allocation or handle allocation! +class ReversePcLookupCache { + public: + ReversePcLookupCache(Isolate* isolate, + uint32_t* pc_array, + intptr_t length, + uword first_absolute_pc, + uword last_absolute_pc) + : isolate_(isolate), + pc_array_(pc_array), + length_(length), + first_absolute_pc_(first_absolute_pc), + last_absolute_pc_(last_absolute_pc) {} + ~ReversePcLookupCache() { delete[] pc_array_; } + + // Builds a [ReversePcLookupCache] and attaches it to the isolate (if + // `code_order_table` is non-`null`). + static void BuildAndAttachToIsolate(Isolate* isolate); + + // Returns `true` if the given [pc] contains can be mapped to a [Code] object + // using this cache. + inline bool Contains(uword pc) { + return first_absolute_pc_ <= pc && pc <= last_absolute_pc_; + } + + // Looks up the [Code] object from a given [pc]. + inline RawCode* Lookup(uword pc) { + NoSafepointScope no_safepoint_scope; + + intptr_t left = 0; + intptr_t right = length_ - 1; + + ASSERT(first_absolute_pc_ <= pc && pc < last_absolute_pc_); + uint32_t pc_offset = static_cast(pc - first_absolute_pc_); + + while (left < right) { + intptr_t middle = left + (right - left) / 2; + + uword middle_pc = pc_array_[middle]; + if (middle_pc < pc_offset) { + left = middle + 1; + } else { + right = middle; + } + } + + auto code_array = isolate_->object_store()->code_order_table(); + auto raw_code = reinterpret_cast(Array::DataOf(code_array)[left]); + +#if defined(DEBUG) + ASSERT(raw_code->GetClassIdMayBeSmi() == kCodeCid); + ASSERT(Code::ContainsInstructionAt(raw_code, pc)); +#endif + + return raw_code; + } + + private: + Isolate* isolate_; + uint32_t* pc_array_; + intptr_t length_; + uword first_absolute_pc_; + uword last_absolute_pc_; +}; + +#else // defined(DART_PRECOMPILED_RUNTIME + +class ReversePcLookupCache { + public: + ReversePcLookupCache() {} + ~ReversePcLookupCache() {} + + static void BuildAndAttachToIsolate(Isolate* isolate) {} + + inline bool Contains(uword pc) { return false; } + + inline RawCode* Lookup(uword pc) { UNREACHABLE(); } +}; + +#endif // defined(DART_PRECOMPILED_RUNTIME + +} // namespace dart + +#endif // RUNTIME_VM_REVERSE_PC_LOOKUP_CACHE_H_ diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc index 09a452cd10df..cc94c15a3c85 100644 --- a/runtime/vm/stack_frame.cc +++ b/runtime/vm/stack_frame.cc @@ -15,6 +15,7 @@ #include "vm/parser.h" #include "vm/raw_object.h" #include "vm/reusable_handles.h" +#include "vm/reverse_pc_lookup_cache.h" #include "vm/scopes.h" #include "vm/stub_code.h" #include "vm/visitor.h" @@ -65,16 +66,100 @@ void FrameLayout::Init() { runtime_frame_layout = default_frame_layout; } +Isolate* StackFrame::IsolateOfBareInstructionsFrame() const { + auto isolate = this->isolate(); + + if (isolate->object_store()->code_order_table() != Object::null()) { + auto rct = isolate->reverse_pc_lookup_cache(); + if (rct->Contains(pc())) return isolate; + } + + isolate = Dart::vm_isolate(); + if (isolate->object_store()->code_order_table() != Object::null()) { + auto rct = isolate->reverse_pc_lookup_cache(); + if (rct->Contains(pc())) return isolate; + } + + return nullptr; +} + +bool StackFrame::IsBareInstructionsDartFrame() const { + NoSafepointScope no_safepoint; + + if (auto isolate = IsolateOfBareInstructionsFrame()) { + Code code; + auto rct = isolate->reverse_pc_lookup_cache(); + code = rct->Lookup(pc()); + + // All stub codes have a `null` owner except for the megamorphic miss + // stub. So if it's neither of those, we are know it must be a + // precompiled dart frame. + RawObject* owner = code.owner(); + if (owner != Object::null()) { + if (code.raw() == + Isolate::Current()->object_store()->megamorphic_miss_code()) { + return true; + } + } + } + return false; +} + +bool StackFrame::IsBareInstructionsStubFrame() const { + NoSafepointScope no_safepoint; + + if (auto isolate = IsolateOfBareInstructionsFrame()) { + Code code; + auto rct = isolate->reverse_pc_lookup_cache(); + code = rct->Lookup(pc()); + + // All stub codes have a `null` owner except for the megamorphic miss stub. + // So if it's either of those, we are know it must be a precompiled stub + // frame. + RawObject* owner = code.owner(); + if (owner == Object::null()) { + return true; + } + + if (code.raw() == + Isolate::Current()->object_store()->megamorphic_miss_code()) { + return true; + } + } + return false; +} + +bool StackFrame::IsDartFrame(bool validate) const { + ASSERT(!validate || IsValid()); + + if (IsEntryFrame() || IsExitFrame()) return false; + + // Even though the megamorphic miss stub is a stub, we consider it as a + // dart frame for all practical purposes. + const bool is_megamorphic_miss_stub = Code::ContainsInstructionAt( + thread_->isolate()->object_store()->megamorphic_miss_code(), pc_); + + if (is_megamorphic_miss_stub) return true; + + return !IsStubFrame(); +} + bool StackFrame::IsStubFrame() const { if (is_interpreted()) { return false; } + + if (IsBareInstructionsStubFrame()) { + return true; + } + ASSERT(!(IsEntryFrame() || IsExitFrame())); #if !defined(HOST_OS_WINDOWS) && !defined(HOST_OS_FUCHSIA) // On Windows and Fuchsia, the profiler calls this from a separate thread // where Thread::Current() is NULL, so we cannot create a NoSafepointScope. NoSafepointScope no_safepoint; #endif + RawCode* code = GetCodeObject(); ASSERT(code != Object::null()); const intptr_t cid = code->ptr()->owner_->GetClassId(); @@ -172,21 +257,27 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) { // be able to reuse the handle based code and avoid having to add // helper functions to the raw object interface. NoSafepointScope no_safepoint; - RawObject* pc_marker = *(reinterpret_cast( - fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp - : runtime_frame_layout.code_from_fp) * - kWordSize))); - // May forward raw code. Note we don't just visit the pc marker slot first - // because the visitor's forwarding might not be idempotent. - visitor->VisitPointer(&pc_marker); Code code; - if (pc_marker->IsHeapObject() && (pc_marker->GetClassId() == kCodeCid)) { - code ^= pc_marker; + + if (auto isolate = IsolateOfBareInstructionsFrame()) { + code = isolate->reverse_pc_lookup_cache()->Lookup(pc()); } else { - ASSERT(pc_marker == Object::null() || - (is_interpreted() && (!pc_marker->IsHeapObject() || - (pc_marker->GetClassId() == kBytecodeCid)))); + RawObject* pc_marker = *(reinterpret_cast( + fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp + : runtime_frame_layout.code_from_fp) * + kWordSize))); + // May forward raw code. Note we don't just visit the pc marker slot first + // because the visitor's forwarding might not be idempotent. + visitor->VisitPointer(&pc_marker); + if (pc_marker->IsHeapObject() && (pc_marker->GetClassId() == kCodeCid)) { + code ^= pc_marker; + } else { + ASSERT(pc_marker == Object::null() || + (is_interpreted() && (!pc_marker->IsHeapObject() || + (pc_marker->GetClassId() == kBytecodeCid)))); + } } + if (!code.IsNull()) { // Optimized frames have a stack map. We need to visit the frame based // on the stack map. @@ -328,6 +419,10 @@ RawCode* StackFrame::LookupDartCode() const { // where Thread::Current() is NULL, so we cannot create a NoSafepointScope. NoSafepointScope no_safepoint; #endif + if (auto isolate = IsolateOfBareInstructionsFrame()) { + return isolate->reverse_pc_lookup_cache()->Lookup(pc()); + } + RawCode* code = GetCodeObject(); if ((code != Code::null()) && (code->ptr()->owner_->GetClassId() == kFunctionCid)) { @@ -338,11 +433,15 @@ RawCode* StackFrame::LookupDartCode() const { RawCode* StackFrame::GetCodeObject() const { ASSERT(!is_interpreted()); - RawObject* pc_marker = *(reinterpret_cast( - fp() + runtime_frame_layout.code_from_fp * kWordSize)); - ASSERT((pc_marker == Object::null()) || - (pc_marker->GetClassId() == kCodeCid)); - return reinterpret_cast(pc_marker); + if (auto isolate = IsolateOfBareInstructionsFrame()) { + return isolate->reverse_pc_lookup_cache()->Lookup(pc()); + } else { + RawObject* pc_marker = *(reinterpret_cast( + fp() + runtime_frame_layout.code_from_fp * kWordSize)); + ASSERT((pc_marker == Object::null()) || + (pc_marker->GetClassId() == kCodeCid)); + return reinterpret_cast(pc_marker); + } } RawBytecode* StackFrame::LookupDartBytecode() const { diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h index 85ba367131d6..9b6b67aa15d2 100644 --- a/runtime/vm/stack_frame.h +++ b/runtime/vm/stack_frame.h @@ -152,14 +152,24 @@ class StackFrame : public ValueObject { // Check validity of a frame, used for assertion purposes. virtual bool IsValid() const; + // Returns the isolate containing the bare instructions of the current frame. + // + // If the frame does not belong to a bare instructions snapshot, it will + // return nullptr. + Isolate* IsolateOfBareInstructionsFrame() const; + + // Returns true iff the current frame is a bare instructions dart frame. + bool IsBareInstructionsDartFrame() const; + + // Returns true iff the current frame is a bare instructions stub frame. + bool IsBareInstructionsStubFrame() const; + // Frame type. - virtual bool IsDartFrame(bool validate = true) const { - ASSERT(!validate || IsValid()); - return !(IsEntryFrame() || IsExitFrame() || IsStubFrame()); - } + virtual bool IsDartFrame(bool validate = true) const; virtual bool IsStubFrame() const; virtual bool IsEntryFrame() const { return false; } virtual bool IsExitFrame() const { return false; } + virtual bool is_interpreted() const { return is_interpreted_; } RawFunction* LookupDartFunction() const; @@ -180,7 +190,9 @@ class StackFrame : public ValueObject { // Name of the frame, used for generic frame printing functionality. virtual const char* GetName() const { - return IsStubFrame() ? "stub" : "dart"; + if (IsBareInstructionsStubFrame()) return "bare-stub"; + if (IsStubFrame()) return "stub"; + return IsBareInstructionsDartFrame() ? "bare-dart" : "dart"; } Isolate* isolate() const { return thread_->isolate(); } diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index a9b02fc27d5d..593b76d191c7 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni @@ -239,6 +239,8 @@ vm_sources = [ "resolver.cc", "resolver.h", "reusable_handles.h", + "reverse_pc_lookup_cache.cc", + "reverse_pc_lookup_cache.h", "ring_buffer.h", "runtime_entry.cc", "runtime_entry.h",