Commit cfb10028 authored by Mythri A's avatar Mythri A Committed by Commit Bot

[compiler] Cache OSR optimized code

With lazy feedback allocation, for functions that get OSRed we may
not have feedback for the initial part of the functions since feedback
vectors might be allocated after the function started executing. Hence
we would not be able to optimize the function on the next call. This
means we may have to OSR twice before we actually optimize function.
This cl introduces OSR cache, so we could reuse the optimized code. One
side effect of this cl is that the OSRed code won't be function context
specialized anymore.

Bug: chromium:987523
Change-Id: Ic1e2abca85ccfa0a66a0fa83f7247392cc1e7cb2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1796329
Commit-Queue: Mythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64014}
parent 8f823778
...@@ -2648,6 +2648,9 @@ v8_source_set("v8_base_without_compiler") { ...@@ -2648,6 +2648,9 @@ v8_source_set("v8_base_without_compiler") {
"src/objects/ordered-hash-table-inl.h", "src/objects/ordered-hash-table-inl.h",
"src/objects/ordered-hash-table.cc", "src/objects/ordered-hash-table.cc",
"src/objects/ordered-hash-table.h", "src/objects/ordered-hash-table.h",
"src/objects/osr-optimized-code-cache-inl.h",
"src/objects/osr-optimized-code-cache.cc",
"src/objects/osr-optimized-code-cache.h",
"src/objects/promise-inl.h", "src/objects/promise-inl.h",
"src/objects/promise.h", "src/objects/promise.h",
"src/objects/property-array-inl.h", "src/objects/property-array-inl.h",
......
...@@ -666,21 +666,25 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache( ...@@ -666,21 +666,25 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache(
function->GetIsolate(), function->GetIsolate(),
RuntimeCallCounterId::kCompileGetFromOptimizedCodeMap); RuntimeCallCounterId::kCompileGetFromOptimizedCodeMap);
Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate()); Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate());
Isolate* isolate = function->GetIsolate();
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
if (osr_offset.IsNone()) { Code code;
if (function->has_feedback_vector()) { if (osr_offset.IsNone() && function->has_feedback_vector()) {
FeedbackVector feedback_vector = function->feedback_vector(); FeedbackVector feedback_vector = function->feedback_vector();
feedback_vector.EvictOptimizedCodeMarkedForDeoptimization( feedback_vector.EvictOptimizedCodeMarkedForDeoptimization(
function->shared(), "GetCodeFromOptimizedCodeCache"); function->shared(), "GetCodeFromOptimizedCodeCache");
Code code = feedback_vector.optimized_code(); code = feedback_vector.optimized_code();
} else if (!osr_offset.IsNone()) {
code = function->context()
.native_context()
.GetOSROptimizedCodeCache()
.GetOptimizedCode(shared, osr_offset, isolate);
}
if (!code.is_null()) { if (!code.is_null()) {
// Caching of optimized code enabled and optimized code found. // Caching of optimized code enabled and optimized code found.
DCHECK(!code.marked_for_deoptimization()); DCHECK(!code.marked_for_deoptimization());
DCHECK(function->shared().is_compiled()); DCHECK(function->shared().is_compiled());
return Handle<Code>(code, feedback_vector.GetIsolate()); return Handle<Code>(code, isolate);
}
}
} }
return MaybeHandle<Code>(); return MaybeHandle<Code>();
} }
...@@ -711,12 +715,15 @@ void InsertCodeIntoOptimizedCodeCache( ...@@ -711,12 +715,15 @@ void InsertCodeIntoOptimizedCodeCache(
// Cache optimized context-specific code. // Cache optimized context-specific code.
Handle<JSFunction> function = compilation_info->closure(); Handle<JSFunction> function = compilation_info->closure();
Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate()); Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate());
Handle<Context> native_context(function->context().native_context(), Handle<NativeContext> native_context(function->context().native_context(),
function->GetIsolate()); function->GetIsolate());
if (compilation_info->osr_offset().IsNone()) { if (compilation_info->osr_offset().IsNone()) {
Handle<FeedbackVector> vector = Handle<FeedbackVector> vector =
handle(function->feedback_vector(), function->GetIsolate()); handle(function->feedback_vector(), function->GetIsolate());
FeedbackVector::SetOptimizedCode(vector, code); FeedbackVector::SetOptimizedCode(vector, code);
} else {
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
compilation_info->osr_offset());
} }
} }
......
...@@ -1028,8 +1028,14 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl( ...@@ -1028,8 +1028,14 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
compilation_info()->MarkAsAllocationFoldingEnabled(); compilation_info()->MarkAsAllocationFoldingEnabled();
} }
// Determine whether to specialize the code for the function's context.
// We can't do this in the case of OSR, because we want to cache the
// generated code on the native context keyed on SharedFunctionInfo.
// TODO(mythria): Check if it is better to key the OSR cache on JSFunction and
// allow context specialization for OSR code.
if (compilation_info()->closure()->raw_feedback_cell().map() == if (compilation_info()->closure()->raw_feedback_cell().map() ==
ReadOnlyRoots(isolate).one_closure_cell_map()) { ReadOnlyRoots(isolate).one_closure_cell_map() &&
!compilation_info()->is_osr()) {
compilation_info()->MarkAsFunctionContextSpecializing(); compilation_info()->MarkAsFunctionContextSpecializing();
data_.ChooseSpecializationContext(); data_.ChooseSpecializationContext();
} }
......
...@@ -357,6 +357,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) { ...@@ -357,6 +357,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
for (Code code : codes) { for (Code code : codes) {
isolate->heap()->InvalidateCodeDeoptimizationData(code); isolate->heap()->InvalidateCodeDeoptimizationData(code);
} }
native_context.GetOSROptimizedCodeCache().EvictMarkedCode(
native_context.GetIsolate());
} }
void Deoptimizer::DeoptimizeAll(Isolate* isolate) { void Deoptimizer::DeoptimizeAll(Isolate* isolate) {
...@@ -375,6 +378,7 @@ void Deoptimizer::DeoptimizeAll(Isolate* isolate) { ...@@ -375,6 +378,7 @@ void Deoptimizer::DeoptimizeAll(Isolate* isolate) {
while (!context.IsUndefined(isolate)) { while (!context.IsUndefined(isolate)) {
NativeContext native_context = NativeContext::cast(context); NativeContext native_context = NativeContext::cast(context);
MarkAllCodeForContext(native_context); MarkAllCodeForContext(native_context);
OSROptimizedCodeCache::Clear(native_context);
DeoptimizeMarkedCodeForContext(native_context); DeoptimizeMarkedCodeForContext(native_context);
context = native_context.next_context_link(); context = native_context.next_context_link();
} }
...@@ -432,6 +436,13 @@ void Deoptimizer::DeoptimizeFunction(JSFunction function, Code code) { ...@@ -432,6 +436,13 @@ void Deoptimizer::DeoptimizeFunction(JSFunction function, Code code) {
code.set_deopt_already_counted(true); code.set_deopt_already_counted(true);
} }
DeoptimizeMarkedCodeForContext(function.context().native_context()); DeoptimizeMarkedCodeForContext(function.context().native_context());
// TODO(mythria): Ideally EvictMarkCode should compact the cache without
// having to explicitly call this. We don't do this currently because
// compacting causes GC and DeoptimizeMarkedCodeForContext uses raw
// pointers. Update DeoptimizeMarkedCodeForContext to use handles and remove
// this call from here.
OSROptimizedCodeCache::Compact(
Handle<NativeContext>(function.context().native_context(), isolate));
} }
} }
......
...@@ -1445,6 +1445,7 @@ Handle<NativeContext> Factory::NewNativeContext() { ...@@ -1445,6 +1445,7 @@ Handle<NativeContext> Factory::NewNativeContext() {
context->set_math_random_index(Smi::zero()); context->set_math_random_index(Smi::zero());
context->set_serialized_objects(*empty_fixed_array()); context->set_serialized_objects(*empty_fixed_array());
context->set_microtask_queue(nullptr); context->set_microtask_queue(nullptr);
context->set_osr_code_cache(*empty_weak_fixed_array());
return context; return context;
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/objects/js-objects-inl.h" #include "src/objects/js-objects-inl.h"
#include "src/objects/map-inl.h" #include "src/objects/map-inl.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/objects/osr-optimized-code-cache-inl.h"
#include "src/objects/regexp-match-info.h" #include "src/objects/regexp-match-info.h"
#include "src/objects/scope-info.h" #include "src/objects/scope-info.h"
#include "src/objects/shared-function-info.h" #include "src/objects/shared-function-info.h"
...@@ -280,6 +281,10 @@ void NativeContext::set_microtask_queue(MicrotaskQueue* microtask_queue) { ...@@ -280,6 +281,10 @@ void NativeContext::set_microtask_queue(MicrotaskQueue* microtask_queue) {
reinterpret_cast<Address>(microtask_queue)); reinterpret_cast<Address>(microtask_queue));
} }
OSROptimizedCodeCache NativeContext::GetOSROptimizedCodeCache() {
return OSROptimizedCodeCache::cast(osr_code_cache());
}
OBJECT_CONSTRUCTORS_IMPL(NativeContext, Context) OBJECT_CONSTRUCTORS_IMPL(NativeContext, Context)
} // namespace internal } // namespace internal
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/objects/fixed-array.h" #include "src/objects/fixed-array.h"
#include "src/objects/function-kind.h" #include "src/objects/function-kind.h"
#include "src/objects/osr-optimized-code-cache.h"
#include "torque-generated/field-offsets-tq.h" #include "torque-generated/field-offsets-tq.h"
// Has to be the last include (doesn't have include guards): // Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h" #include "src/objects/object-macros.h"
...@@ -355,6 +356,7 @@ enum ContextLookupFlags { ...@@ -355,6 +356,7 @@ enum ContextLookupFlags {
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \ V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
V(WEAKMAP_GET_INDEX, JSFunction, weakmap_get) \ V(WEAKMAP_GET_INDEX, JSFunction, weakmap_get) \
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add) \ V(WEAKSET_ADD_INDEX, JSFunction, weakset_add) \
V(OSR_CODE_CACHE_INDEX, WeakFixedArray, osr_code_cache) \
NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V)
// A table of all script contexts. Every loaded top-level script with top-level // A table of all script contexts. Every loaded top-level script with top-level
...@@ -718,6 +720,8 @@ class NativeContext : public Context { ...@@ -718,6 +720,8 @@ class NativeContext : public Context {
void SetDeoptimizedCodeListHead(Object head); void SetDeoptimizedCodeListHead(Object head);
Object DeoptimizedCodeListHead(); Object DeoptimizedCodeListHead();
inline OSROptimizedCodeCache GetOSROptimizedCodeCache();
void ResetErrorsThrown(); void ResetErrorsThrown();
void IncrementErrorsThrown(); void IncrementErrorsThrown();
int GetErrorsThrown(); int GetErrorsThrown();
......
...@@ -41,6 +41,7 @@ class HeapNumber; ...@@ -41,6 +41,7 @@ class HeapNumber;
class ObjectHashTable; class ObjectHashTable;
class ObjectTemplateInfo; class ObjectTemplateInfo;
class ObjectVisitor; class ObjectVisitor;
class OSROptimizedCodeCache;
class PreparseData; class PreparseData;
class PropertyArray; class PropertyArray;
class PropertyCell; class PropertyCell;
...@@ -186,6 +187,7 @@ class ZoneForwardList; ...@@ -186,6 +187,7 @@ class ZoneForwardList;
V(OrderedHashMap) \ V(OrderedHashMap) \
V(OrderedHashSet) \ V(OrderedHashSet) \
V(OrderedNameDictionary) \ V(OrderedNameDictionary) \
V(OSROptimizedCodeCache) \
V(PreparseData) \ V(PreparseData) \
V(PromiseReactionJobTask) \ V(PromiseReactionJobTask) \
V(PropertyArray) \ V(PropertyArray) \
......
...@@ -350,6 +350,13 @@ DEF_GETTER(HeapObject, IsDependentCode, bool) { ...@@ -350,6 +350,13 @@ DEF_GETTER(HeapObject, IsDependentCode, bool) {
return true; return true;
} }
DEF_GETTER(HeapObject, IsOSROptimizedCodeCache, bool) {
if (!IsWeakFixedArray(isolate)) return false;
// There's actually no way to see the difference between a weak fixed array
// and a osr optimized code cache.
return true;
}
DEF_GETTER(HeapObject, IsAbstractCode, bool) { DEF_GETTER(HeapObject, IsAbstractCode, bool) {
return IsBytecodeArray(isolate) || IsCode(isolate); return IsBytecodeArray(isolate) || IsCode(isolate);
} }
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_OSR_OPTIMIZED_CODE_CACHE_INL_H_
#define V8_OBJECTS_OSR_OPTIMIZED_CODE_CACHE_INL_H_
#include "src/objects/osr-optimized-code-cache.h"
#include "src/objects/fixed-array-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
OBJECT_CONSTRUCTORS_IMPL(OSROptimizedCodeCache, WeakFixedArray)
CAST_ACCESSOR(OSROptimizedCodeCache)
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_OSR_OPTIMIZED_CODE_CACHE_INL_H_
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/execution/isolate-inl.h"
#include "src/objects/code.h"
#include "src/objects/maybe-object.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/osr-optimized-code-cache.h"
namespace v8 {
namespace internal {
const int OSROptimizedCodeCache::kInitialLength;
const int OSROptimizedCodeCache::kMaxLength;
void OSROptimizedCodeCache::AddOptimizedCode(
Handle<NativeContext> native_context, Handle<SharedFunctionInfo> shared,
Handle<Code> code, BailoutId osr_offset) {
DCHECK(!osr_offset.IsNone());
DCHECK_EQ(code->kind(), Code::OPTIMIZED_FUNCTION);
STATIC_ASSERT(kEntryLength == 3);
Isolate* isolate = native_context->GetIsolate();
DCHECK(!isolate->serializer_enabled());
Handle<OSROptimizedCodeCache> osr_cache(
native_context->GetOSROptimizedCodeCache(), isolate);
DCHECK_EQ(osr_cache->FindEntry(shared, osr_offset), -1);
int entry = -1;
for (int index = 0; index < osr_cache->length(); index += kEntryLength) {
if (osr_cache->Get(index + kSharedOffset)->IsCleared() ||
osr_cache->Get(index + kCachedCodeOffset)->IsCleared()) {
entry = index;
break;
}
}
if (entry == -1 && osr_cache->length() + kEntryLength <= kMaxLength) {
entry = GrowOSRCache(native_context, &osr_cache);
} else if (entry == -1) {
// We reached max capacity and cannot grow further. Reuse an existing entry.
// TODO(mythria): We could use better mechanisms (like lru) to replace
// existing entries. Though we don't expect this to be a common case, so
// for now choosing to replace the first entry.
entry = 0;
}
osr_cache->InitializeEntry(entry, *shared, *code, osr_offset);
}
void OSROptimizedCodeCache::Clear(NativeContext native_context) {
native_context.set_osr_code_cache(
*native_context.GetIsolate()->factory()->empty_weak_fixed_array());
}
void OSROptimizedCodeCache::Compact(Handle<NativeContext> native_context) {
Handle<OSROptimizedCodeCache> osr_cache(
native_context->GetOSROptimizedCodeCache(), native_context->GetIsolate());
Isolate* isolate = native_context->GetIsolate();
// Re-adjust the cache so all the valid entries are on one side. This will
// enable us to compress the cache if needed.
int curr_valid_index = 0;
for (int curr_index = 0; curr_index < osr_cache->length();
curr_index += kEntryLength) {
if (osr_cache->Get(curr_index + kSharedOffset)->IsCleared() ||
osr_cache->Get(curr_index + kCachedCodeOffset)->IsCleared()) {
continue;
}
if (curr_valid_index != curr_index) {
osr_cache->MoveEntry(curr_index, curr_valid_index, isolate);
}
curr_valid_index += kEntryLength;
}
if (!NeedsTrimming(curr_valid_index, osr_cache->length())) return;
Handle<OSROptimizedCodeCache> new_osr_cache =
Handle<OSROptimizedCodeCache>::cast(isolate->factory()->NewWeakFixedArray(
CapacityForLength(curr_valid_index), AllocationType::kOld));
DCHECK_LT(new_osr_cache->length(), osr_cache->length());
{
DisallowHeapAllocation no_gc;
new_osr_cache->CopyElements(native_context->GetIsolate(), 0, *osr_cache, 0,
new_osr_cache->length(),
new_osr_cache->GetWriteBarrierMode(no_gc));
}
native_context->set_osr_code_cache(*new_osr_cache);
}
Code OSROptimizedCodeCache::GetOptimizedCode(Handle<SharedFunctionInfo> shared,
BailoutId osr_offset,
Isolate* isolate) {
DisallowHeapAllocation no_gc;
int index = FindEntry(shared, osr_offset);
if (index == -1) return Code();
Code code = GetCodeFromEntry(index);
if (code.is_null()) {
ClearEntry(index, isolate);
return code;
}
DCHECK(code.is_optimized_code() && !code.marked_for_deoptimization());
return code;
}
void OSROptimizedCodeCache::EvictMarkedCode(Isolate* isolate) {
// This is called from DeoptimizeMarkedCodeForContext that uses raw pointers
// and hence the DisallowHeapAllocation scope here.
DisallowHeapAllocation no_gc;
for (int index = 0; index < length(); index += kEntryLength) {
MaybeObject code_entry = Get(index + kCachedCodeOffset);
HeapObject heap_object;
if (!code_entry->GetHeapObject(&heap_object)) continue;
DCHECK(heap_object.IsCode());
DCHECK(Code::cast(heap_object).is_optimized_code());
if (!Code::cast(heap_object).marked_for_deoptimization()) continue;
ClearEntry(index, isolate);
}
}
int OSROptimizedCodeCache::GrowOSRCache(
Handle<NativeContext> native_context,
Handle<OSROptimizedCodeCache>* osr_cache) {
Isolate* isolate = native_context->GetIsolate();
int old_length = (*osr_cache)->length();
int grow_by = CapacityForLength(old_length) - old_length;
DCHECK_GT(grow_by, kEntryLength);
*osr_cache = Handle<OSROptimizedCodeCache>::cast(
isolate->factory()->CopyWeakFixedArrayAndGrow(*osr_cache, grow_by));
for (int i = old_length; i < (*osr_cache)->length(); i++) {
(*osr_cache)->Set(i, HeapObjectReference::ClearedValue(isolate));
}
native_context->set_osr_code_cache(**osr_cache);
return old_length;
}
Code OSROptimizedCodeCache::GetCodeFromEntry(int index) {
DCHECK_LE(index + OSRCodeCacheConstants::kEntryLength, length());
DCHECK_EQ(index % kEntryLength, 0);
HeapObject code_entry;
Get(index + OSRCodeCacheConstants::kCachedCodeOffset)
->GetHeapObject(&code_entry);
return code_entry.is_null() ? Code() : Code::cast(code_entry);
}
SharedFunctionInfo OSROptimizedCodeCache::GetSFIFromEntry(int index) {
DCHECK_LE(index + OSRCodeCacheConstants::kEntryLength, length());
DCHECK_EQ(index % kEntryLength, 0);
HeapObject sfi_entry;
Get(index + OSRCodeCacheConstants::kSharedOffset)->GetHeapObject(&sfi_entry);
return sfi_entry.is_null() ? SharedFunctionInfo()
: SharedFunctionInfo::cast(sfi_entry);
}
BailoutId OSROptimizedCodeCache::GetBailoutIdFromEntry(int index) {
DCHECK_LE(index + OSRCodeCacheConstants::kEntryLength, length());
DCHECK_EQ(index % kEntryLength, 0);
Smi osr_offset_entry;
Get(index + kOsrIdOffset)->ToSmi(&osr_offset_entry);
return BailoutId(osr_offset_entry.value());
}
int OSROptimizedCodeCache::FindEntry(Handle<SharedFunctionInfo> shared,
BailoutId osr_offset) {
DisallowHeapAllocation no_gc;
DCHECK(!osr_offset.IsNone());
for (int index = 0; index < length(); index += kEntryLength) {
if (GetSFIFromEntry(index) != *shared) continue;
if (GetBailoutIdFromEntry(index) != osr_offset) continue;
return index;
}
return -1;
}
void OSROptimizedCodeCache::ClearEntry(int index, Isolate* isolate) {
Set(index + OSRCodeCacheConstants::kSharedOffset,
HeapObjectReference::ClearedValue(isolate));
Set(index + OSRCodeCacheConstants::kCachedCodeOffset,
HeapObjectReference::ClearedValue(isolate));
Set(index + OSRCodeCacheConstants::kOsrIdOffset,
HeapObjectReference::ClearedValue(isolate));
}
void OSROptimizedCodeCache::InitializeEntry(int entry,
SharedFunctionInfo shared,
Code code, BailoutId osr_offset) {
Set(entry + OSRCodeCacheConstants::kSharedOffset,
HeapObjectReference::Weak(shared));
Set(entry + OSRCodeCacheConstants::kCachedCodeOffset,
HeapObjectReference::Weak(code));
Set(entry + OSRCodeCacheConstants::kOsrIdOffset,
MaybeObject::FromSmi(Smi::FromInt(osr_offset.ToInt())));
}
void OSROptimizedCodeCache::MoveEntry(int src, int dst, Isolate* isolate) {
Set(dst + OSRCodeCacheConstants::kSharedOffset,
Get(src + OSRCodeCacheConstants::kSharedOffset));
Set(dst + OSRCodeCacheConstants::kCachedCodeOffset,
Get(src + OSRCodeCacheConstants::kCachedCodeOffset));
Set(dst + OSRCodeCacheConstants::kOsrIdOffset, Get(src + kOsrIdOffset));
ClearEntry(src, isolate);
}
int OSROptimizedCodeCache::CapacityForLength(int curr_length) {
// TODO(mythria): This is a randomly chosen heuristic and is not based on any
// data. We may have to tune this later.
if (curr_length == 0) return kInitialLength;
if (curr_length * 2 > kMaxLength) return kMaxLength;
return curr_length * 2;
}
bool OSROptimizedCodeCache::NeedsTrimming(int num_valid_entries,
int curr_length) {
return curr_length > kInitialLength && curr_length > num_valid_entries * 3;
}
} // namespace internal
} // namespace v8
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_OBJECTS_OSR_OPTIMIZED_CODE_CACHE_H_
#define V8_OBJECTS_OSR_OPTIMIZED_CODE_CACHE_H_
#include "src/objects/fixed-array.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
class V8_EXPORT OSROptimizedCodeCache : public WeakFixedArray {
public:
DECL_CAST(OSROptimizedCodeCache)
enum OSRCodeCacheConstants {
kSharedOffset,
kCachedCodeOffset,
kOsrIdOffset,
kEntryLength
};
static const int kInitialLength = OSRCodeCacheConstants::kEntryLength * 4;
static const int kMaxLength = OSRCodeCacheConstants::kEntryLength * 1024;
// Caches the optimized code |code| corresponding to the shared function
// |shared| and bailout id |osr_offset| in the OSROptimized code cache.
// If the OSR code cache wasn't created before it creates a code cache with
// kOSRCodeCacheInitialLength entries.
static void AddOptimizedCode(Handle<NativeContext> context,
Handle<SharedFunctionInfo> shared,
Handle<Code> code, BailoutId osr_offset);
// Reduces the size of the OSR code cache if the number of valid entries are
// less than the current capacity of the cache.
static void Compact(Handle<NativeContext> context);
// Sets the OSR optimized code cache to an empty array.
static void Clear(NativeContext context);
// Returns the code corresponding to the shared function |shared| and
// BailoutId |offset| if an entry exists in the cache. Returns an empty
// object otherwise.
Code GetOptimizedCode(Handle<SharedFunctionInfo> shared, BailoutId osr_offset,
Isolate* isolate);
// Remove all code objects marked for deoptimization from OSR code cache.
void EvictMarkedCode(Isolate* isolate);
private:
// Functions that implement heuristics on when to grow / shrink the cache.
static int CapacityForLength(int curr_capacity);
static bool NeedsTrimming(int num_valid_entries, int curr_capacity);
static int GrowOSRCache(Handle<NativeContext> native_context,
Handle<OSROptimizedCodeCache>* osr_cache);
// Helper functions to get individual items from an entry in the cache.
Code GetCodeFromEntry(int index);
SharedFunctionInfo GetSFIFromEntry(int index);
BailoutId GetBailoutIdFromEntry(int index);
inline int FindEntry(Handle<SharedFunctionInfo> shared, BailoutId osr_offset);
inline void ClearEntry(int src, Isolate* isolate);
inline void InitializeEntry(int entry, SharedFunctionInfo shared, Code code,
BailoutId osr_offset);
inline void MoveEntry(int src, int dst, Isolate* isolate);
OBJECT_CONSTRUCTORS(OSROptimizedCodeCache, WeakFixedArray);
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_OSR_OPTIMIZED_CODE_CACHE_H_
...@@ -157,6 +157,9 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { ...@@ -157,6 +157,9 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
TimerEventScope<TimerEventDeoptimizeCode> timer(isolate); TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
TRACE_EVENT0("v8", "V8.DeoptimizeCode"); TRACE_EVENT0("v8", "V8.DeoptimizeCode");
Handle<JSFunction> function = deoptimizer->function(); Handle<JSFunction> function = deoptimizer->function();
// For OSR the optimized code isn't installed on the function, so get the
// code object from deoptimizer.
Handle<Code> optimized_code = deoptimizer->compiled_code();
DeoptimizeKind type = deoptimizer->deopt_kind(); DeoptimizeKind type = deoptimizer->deopt_kind();
// TODO(turbofan): We currently need the native context to materialize // TODO(turbofan): We currently need the native context to materialize
...@@ -174,7 +177,7 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { ...@@ -174,7 +177,7 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
// Invalidate the underlying optimized code on non-lazy deopts. // Invalidate the underlying optimized code on non-lazy deopts.
if (type != DeoptimizeKind::kLazy) { if (type != DeoptimizeKind::kLazy) {
Deoptimizer::DeoptimizeFunction(*function); Deoptimizer::DeoptimizeFunction(*function, *optimized_code);
} }
return ReadOnlyRoots(isolate).undefined_value(); return ReadOnlyRoots(isolate).undefined_value();
......
...@@ -190,6 +190,7 @@ v8_source_set("unittests_sources") { ...@@ -190,6 +190,7 @@ v8_source_set("unittests_sources") {
"numbers/conversions-unittest.cc", "numbers/conversions-unittest.cc",
"objects/backing-store-unittest.cc", "objects/backing-store-unittest.cc",
"objects/object-unittest.cc", "objects/object-unittest.cc",
"objects/osr-optimized-code-cache-unittest.cc",
"objects/value-serializer-unittest.cc", "objects/value-serializer-unittest.cc",
"parser/ast-value-unittest.cc", "parser/ast-value-unittest.cc",
"parser/preparser-unittest.cc", "parser/preparser-unittest.cc",
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment