Reland "[compiler] Cache OSR optimized code"
This is a reland of cfb100282e
with a fix for failures in lite mode.
Original change's description:
> [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: Ross McIlroy <rmcilroy@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#64014}
Bug: chromium:987523
Change-Id: I9c782242b07b24d15247533ab4ee044334b429ff
TBR: rmcilroy@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1826898
Commit-Queue: Mythri Alle <mythria@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64023}
This commit is contained in:
parent
1e4bb08773
commit
fcff24370b
3
BUILD.gn
3
BUILD.gn
@ -2648,6 +2648,9 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/objects/ordered-hash-table-inl.h",
|
||||
"src/objects/ordered-hash-table.cc",
|
||||
"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.h",
|
||||
"src/objects/property-array-inl.h",
|
||||
|
@ -666,21 +666,25 @@ V8_WARN_UNUSED_RESULT MaybeHandle<Code> GetCodeFromOptimizedCodeCache(
|
||||
function->GetIsolate(),
|
||||
RuntimeCallCounterId::kCompileGetFromOptimizedCodeMap);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (osr_offset.IsNone()) {
|
||||
if (function->has_feedback_vector()) {
|
||||
FeedbackVector feedback_vector = function->feedback_vector();
|
||||
feedback_vector.EvictOptimizedCodeMarkedForDeoptimization(
|
||||
function->shared(), "GetCodeFromOptimizedCodeCache");
|
||||
Code code = feedback_vector.optimized_code();
|
||||
|
||||
if (!code.is_null()) {
|
||||
// Caching of optimized code enabled and optimized code found.
|
||||
DCHECK(!code.marked_for_deoptimization());
|
||||
DCHECK(function->shared().is_compiled());
|
||||
return Handle<Code>(code, feedback_vector.GetIsolate());
|
||||
}
|
||||
}
|
||||
Code code;
|
||||
if (osr_offset.IsNone() && function->has_feedback_vector()) {
|
||||
FeedbackVector feedback_vector = function->feedback_vector();
|
||||
feedback_vector.EvictOptimizedCodeMarkedForDeoptimization(
|
||||
function->shared(), "GetCodeFromOptimizedCodeCache");
|
||||
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()) {
|
||||
// Caching of optimized code enabled and optimized code found.
|
||||
DCHECK(!code.marked_for_deoptimization());
|
||||
DCHECK(function->shared().is_compiled());
|
||||
return Handle<Code>(code, isolate);
|
||||
}
|
||||
return MaybeHandle<Code>();
|
||||
}
|
||||
@ -711,12 +715,15 @@ void InsertCodeIntoOptimizedCodeCache(
|
||||
// Cache optimized context-specific code.
|
||||
Handle<JSFunction> function = compilation_info->closure();
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), function->GetIsolate());
|
||||
Handle<Context> native_context(function->context().native_context(),
|
||||
function->GetIsolate());
|
||||
Handle<NativeContext> native_context(function->context().native_context(),
|
||||
function->GetIsolate());
|
||||
if (compilation_info->osr_offset().IsNone()) {
|
||||
Handle<FeedbackVector> vector =
|
||||
handle(function->feedback_vector(), function->GetIsolate());
|
||||
FeedbackVector::SetOptimizedCode(vector, code);
|
||||
} else {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
compilation_info->osr_offset());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1028,8 +1028,14 @@ PipelineCompilationJob::Status PipelineCompilationJob::PrepareJobImpl(
|
||||
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() ==
|
||||
ReadOnlyRoots(isolate).one_closure_cell_map()) {
|
||||
ReadOnlyRoots(isolate).one_closure_cell_map() &&
|
||||
!compilation_info()->is_osr()) {
|
||||
compilation_info()->MarkAsFunctionContextSpecializing();
|
||||
data_.ChooseSpecializationContext();
|
||||
}
|
||||
|
@ -357,6 +357,9 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
|
||||
for (Code code : codes) {
|
||||
isolate->heap()->InvalidateCodeDeoptimizationData(code);
|
||||
}
|
||||
|
||||
native_context.GetOSROptimizedCodeCache().EvictMarkedCode(
|
||||
native_context.GetIsolate());
|
||||
}
|
||||
|
||||
void Deoptimizer::DeoptimizeAll(Isolate* isolate) {
|
||||
@ -375,6 +378,7 @@ void Deoptimizer::DeoptimizeAll(Isolate* isolate) {
|
||||
while (!context.IsUndefined(isolate)) {
|
||||
NativeContext native_context = NativeContext::cast(context);
|
||||
MarkAllCodeForContext(native_context);
|
||||
OSROptimizedCodeCache::Clear(native_context);
|
||||
DeoptimizeMarkedCodeForContext(native_context);
|
||||
context = native_context.next_context_link();
|
||||
}
|
||||
@ -432,6 +436,13 @@ void Deoptimizer::DeoptimizeFunction(JSFunction function, Code code) {
|
||||
code.set_deopt_already_counted(true);
|
||||
}
|
||||
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() {
|
||||
context->set_math_random_index(Smi::zero());
|
||||
context->set_serialized_objects(*empty_fixed_array());
|
||||
context->set_microtask_queue(nullptr);
|
||||
context->set_osr_code_cache(*empty_weak_fixed_array());
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/objects/js-objects-inl.h"
|
||||
#include "src/objects/map-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/scope-info.h"
|
||||
#include "src/objects/shared-function-info.h"
|
||||
@ -280,6 +281,10 @@ void NativeContext::set_microtask_queue(MicrotaskQueue* microtask_queue) {
|
||||
reinterpret_cast<Address>(microtask_queue));
|
||||
}
|
||||
|
||||
OSROptimizedCodeCache NativeContext::GetOSROptimizedCodeCache() {
|
||||
return OSROptimizedCodeCache::cast(osr_code_cache());
|
||||
}
|
||||
|
||||
OBJECT_CONSTRUCTORS_IMPL(NativeContext, Context)
|
||||
|
||||
} // namespace internal
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "src/objects/fixed-array.h"
|
||||
#include "src/objects/function-kind.h"
|
||||
#include "src/objects/osr-optimized-code-cache.h"
|
||||
#include "torque-generated/field-offsets-tq.h"
|
||||
// Has to be the last include (doesn't have include guards):
|
||||
#include "src/objects/object-macros.h"
|
||||
@ -355,6 +356,7 @@ enum ContextLookupFlags {
|
||||
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
|
||||
V(WEAKMAP_GET_INDEX, JSFunction, weakmap_get) \
|
||||
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add) \
|
||||
V(OSR_CODE_CACHE_INDEX, WeakFixedArray, osr_code_cache) \
|
||||
NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V)
|
||||
|
||||
// A table of all script contexts. Every loaded top-level script with top-level
|
||||
@ -718,6 +720,8 @@ class NativeContext : public Context {
|
||||
void SetDeoptimizedCodeListHead(Object head);
|
||||
Object DeoptimizedCodeListHead();
|
||||
|
||||
inline OSROptimizedCodeCache GetOSROptimizedCodeCache();
|
||||
|
||||
void ResetErrorsThrown();
|
||||
void IncrementErrorsThrown();
|
||||
int GetErrorsThrown();
|
||||
|
@ -41,6 +41,7 @@ class HeapNumber;
|
||||
class ObjectHashTable;
|
||||
class ObjectTemplateInfo;
|
||||
class ObjectVisitor;
|
||||
class OSROptimizedCodeCache;
|
||||
class PreparseData;
|
||||
class PropertyArray;
|
||||
class PropertyCell;
|
||||
@ -186,6 +187,7 @@ class ZoneForwardList;
|
||||
V(OrderedHashMap) \
|
||||
V(OrderedHashSet) \
|
||||
V(OrderedNameDictionary) \
|
||||
V(OSROptimizedCodeCache) \
|
||||
V(PreparseData) \
|
||||
V(PromiseReactionJobTask) \
|
||||
V(PropertyArray) \
|
||||
|
@ -350,6 +350,13 @@ DEF_GETTER(HeapObject, IsDependentCode, bool) {
|
||||
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) {
|
||||
return IsBytecodeArray(isolate) || IsCode(isolate);
|
||||
}
|
||||
|
25
src/objects/osr-optimized-code-cache-inl.h
Normal file
25
src/objects/osr-optimized-code-cache-inl.h
Normal file
@ -0,0 +1,25 @@
|
||||
// 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_
|
223
src/objects/osr-optimized-code-cache.cc
Normal file
223
src/objects/osr-optimized-code-cache.cc
Normal file
@ -0,0 +1,223 @@
|
||||
// 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
|
77
src/objects/osr-optimized-code-cache.h
Normal file
77
src/objects/osr-optimized-code-cache.h
Normal file
@ -0,0 +1,77 @@
|
||||
// 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) {
|
||||
TimerEventScope<TimerEventDeoptimizeCode> timer(isolate);
|
||||
TRACE_EVENT0("v8", "V8.DeoptimizeCode");
|
||||
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();
|
||||
|
||||
// TODO(turbofan): We currently need the native context to materialize
|
||||
@ -174,7 +177,7 @@ RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) {
|
||||
|
||||
// Invalidate the underlying optimized code on non-lazy deopts.
|
||||
if (type != DeoptimizeKind::kLazy) {
|
||||
Deoptimizer::DeoptimizeFunction(*function);
|
||||
Deoptimizer::DeoptimizeFunction(*function, *optimized_code);
|
||||
}
|
||||
|
||||
return ReadOnlyRoots(isolate).undefined_value();
|
||||
|
@ -190,6 +190,7 @@ v8_source_set("unittests_sources") {
|
||||
"numbers/conversions-unittest.cc",
|
||||
"objects/backing-store-unittest.cc",
|
||||
"objects/object-unittest.cc",
|
||||
"objects/osr-optimized-code-cache-unittest.cc",
|
||||
"objects/value-serializer-unittest.cc",
|
||||
"parser/ast-value-unittest.cc",
|
||||
"parser/preparser-unittest.cc",
|
||||
|
412
test/unittests/objects/osr-optimized-code-cache-unittest.cc
Normal file
412
test/unittests/objects/osr-optimized-code-cache-unittest.cc
Normal file
@ -0,0 +1,412 @@
|
||||
// Copyright 2016 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 <cmath>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "src/deoptimizer/deoptimizer.h"
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/objects/objects.h"
|
||||
#include "src/objects/osr-optimized-code-cache.h"
|
||||
#include "test/unittests/test-utils.h"
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
const char* code_template_string =
|
||||
"function f%d() { return 0; };"
|
||||
"%%PrepareFunctionForOptimization(f%d);"
|
||||
"f%d(); f%d();"
|
||||
"%%OptimizeFunctionOnNextCall(f%d);"
|
||||
"f%d(); f%d;";
|
||||
|
||||
void GetSource(i::ScopedVector<char>* source, int index) {
|
||||
i::SNPrintF(*source, code_template_string, index, index, index, index, index,
|
||||
index, index);
|
||||
}
|
||||
|
||||
const int kInitialLength = OSROptimizedCodeCache::kInitialLength;
|
||||
const int kInitialEntries =
|
||||
kInitialLength / OSROptimizedCodeCache::kEntryLength;
|
||||
const int kMaxLength = OSROptimizedCodeCache::kMaxLength;
|
||||
const int kMaxEntries = kMaxLength / OSROptimizedCodeCache::kEntryLength;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(TestWithNativeContext, AddCodeToEmptyCache) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
BailoutId bailout_id(1);
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
bailout_id);
|
||||
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kInitialLength);
|
||||
|
||||
HeapObject sfi_entry;
|
||||
osr_cache->Get(OSROptimizedCodeCache::kSharedOffset)
|
||||
->GetHeapObject(&sfi_entry);
|
||||
EXPECT_EQ(sfi_entry, *shared);
|
||||
HeapObject code_entry;
|
||||
osr_cache->Get(OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->GetHeapObject(&code_entry);
|
||||
EXPECT_EQ(code_entry, *code);
|
||||
Smi osr_offset_entry;
|
||||
osr_cache->Get(OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&osr_offset_entry);
|
||||
EXPECT_EQ(osr_offset_entry.value(), bailout_id.ToInt());
|
||||
}
|
||||
|
||||
TEST_F(TestWithNativeContext, GrowCodeCache) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
|
||||
int bailout_id = 0;
|
||||
for (bailout_id = 0; bailout_id < kInitialEntries; bailout_id++) {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
}
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kInitialLength);
|
||||
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kInitialLength * 2);
|
||||
|
||||
int index = kInitialLength;
|
||||
HeapObject sfi_entry;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->GetHeapObject(&sfi_entry);
|
||||
EXPECT_EQ(sfi_entry, *shared);
|
||||
HeapObject code_entry;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->GetHeapObject(&code_entry);
|
||||
EXPECT_EQ(code_entry, *code);
|
||||
Smi osr_offset_entry;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)
|
||||
->ToSmi(&osr_offset_entry);
|
||||
EXPECT_EQ(osr_offset_entry.value(), bailout_id);
|
||||
}
|
||||
|
||||
TEST_F(TestWithNativeContext, FindCachedEntry) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
|
||||
int bailout_id = 0;
|
||||
for (bailout_id = 0; bailout_id < kInitialEntries; bailout_id++) {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
}
|
||||
|
||||
i::ScopedVector<char> source1(1024);
|
||||
GetSource(&source1, 1);
|
||||
Handle<JSFunction> function1 = RunJS<JSFunction>(source1.begin());
|
||||
Handle<SharedFunctionInfo> shared1(function1->shared(), isolate);
|
||||
Handle<Code> code1(function1->code(), isolate);
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared1, code1,
|
||||
BailoutId(bailout_id));
|
||||
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->GetOptimizedCode(shared, BailoutId(0), isolate), *code);
|
||||
EXPECT_EQ(
|
||||
osr_cache->GetOptimizedCode(shared1, BailoutId(bailout_id), isolate),
|
||||
*code1);
|
||||
|
||||
RunJS("%DeoptimizeFunction(f1)");
|
||||
EXPECT_TRUE(
|
||||
osr_cache->GetOptimizedCode(shared1, BailoutId(bailout_id), isolate)
|
||||
.is_null());
|
||||
|
||||
osr_cache->Set(OSROptimizedCodeCache::kCachedCodeOffset,
|
||||
HeapObjectReference::ClearedValue(isolate));
|
||||
EXPECT_TRUE(
|
||||
osr_cache->GetOptimizedCode(shared, BailoutId(0), isolate).is_null());
|
||||
}
|
||||
|
||||
TEST_F(TestWithNativeContext, MaxCapacityCache) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
|
||||
int bailout_id = 0;
|
||||
// Add max_capacity - 1 entries.
|
||||
for (bailout_id = 0; bailout_id < kMaxEntries - 1; bailout_id++) {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
}
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kMaxLength);
|
||||
|
||||
// Add an entry to reach max capacity.
|
||||
i::ScopedVector<char> source1(1024);
|
||||
GetSource(&source1, 1);
|
||||
Handle<JSFunction> function1 = RunJS<JSFunction>(source1.begin());
|
||||
Handle<SharedFunctionInfo> shared1(function1->shared(), isolate);
|
||||
Handle<Code> code1(function1->code(), isolate);
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared1, code1,
|
||||
BailoutId(bailout_id));
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kMaxLength);
|
||||
|
||||
int index = (kMaxEntries - 1) * OSROptimizedCodeCache::kEntryLength;
|
||||
HeapObject object;
|
||||
Smi smi;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *shared1);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *code1);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi);
|
||||
EXPECT_EQ(smi.value(), bailout_id);
|
||||
|
||||
// Add an entry beyond max capacity.
|
||||
i::ScopedVector<char> source2(1024);
|
||||
GetSource(&source2, 2);
|
||||
Handle<JSFunction> function2 = RunJS<JSFunction>(source2.begin());
|
||||
Handle<SharedFunctionInfo> shared2(function2->shared(), isolate);
|
||||
Handle<Code> code2(function2->code(), isolate);
|
||||
bailout_id++;
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared2, code2,
|
||||
BailoutId(bailout_id));
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kMaxLength);
|
||||
|
||||
index = 0;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *shared2);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *code2);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi);
|
||||
EXPECT_EQ(smi.value(), bailout_id);
|
||||
}
|
||||
|
||||
TEST_F(TestWithNativeContext, ReuseClearedEntry) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
|
||||
int num_entries = kInitialEntries * 2;
|
||||
int expected_length = kInitialLength * 2;
|
||||
int bailout_id = 0;
|
||||
for (bailout_id = 0; bailout_id < num_entries; bailout_id++) {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
}
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), expected_length);
|
||||
|
||||
int clear_index1 = 0;
|
||||
int clear_index2 = (num_entries - 1) * OSROptimizedCodeCache::kEntryLength;
|
||||
osr_cache->Set(clear_index1 + OSROptimizedCodeCache::kSharedOffset,
|
||||
HeapObjectReference::ClearedValue(isolate));
|
||||
osr_cache->Set(clear_index2 + OSROptimizedCodeCache::kCachedCodeOffset,
|
||||
HeapObjectReference::ClearedValue(isolate));
|
||||
|
||||
i::ScopedVector<char> source1(1024);
|
||||
GetSource(&source1, 1);
|
||||
Handle<JSFunction> function1 = RunJS<JSFunction>(source1.begin());
|
||||
Handle<SharedFunctionInfo> shared1(function1->shared(), isolate);
|
||||
Handle<Code> code1(function1->code(), isolate);
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared1, code1,
|
||||
BailoutId(bailout_id));
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), expected_length);
|
||||
|
||||
int index = clear_index1;
|
||||
HeapObject object;
|
||||
Smi smi;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *shared1);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *code1);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi);
|
||||
EXPECT_EQ(smi.value(), bailout_id);
|
||||
|
||||
i::ScopedVector<char> source2(1024);
|
||||
GetSource(&source2, 2);
|
||||
Handle<JSFunction> function2 = RunJS<JSFunction>(source2.begin());
|
||||
Handle<SharedFunctionInfo> shared2(function2->shared(), isolate);
|
||||
Handle<Code> code2(function2->code(), isolate);
|
||||
bailout_id++;
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared2, code2,
|
||||
BailoutId(bailout_id));
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), expected_length);
|
||||
|
||||
index = clear_index2;
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *shared2);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->GetHeapObject(&object);
|
||||
EXPECT_EQ(object, *code2);
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi);
|
||||
EXPECT_EQ(smi.value(), bailout_id);
|
||||
}
|
||||
|
||||
TEST_F(TestWithNativeContext, EvictDeoptedEntriesNoCompact) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
|
||||
i::ScopedVector<char> source1(1024);
|
||||
GetSource(&source1, 1);
|
||||
Handle<JSFunction> deopt_function = RunJS<JSFunction>(source1.begin());
|
||||
Handle<SharedFunctionInfo> deopt_shared(deopt_function->shared(), isolate);
|
||||
Handle<Code> deopt_code(deopt_function->code(), isolate);
|
||||
|
||||
int num_entries = kInitialEntries * 2;
|
||||
int expected_length = kInitialLength * 2;
|
||||
int deopt_id1 = num_entries - 2;
|
||||
int deopt_id2 = 0;
|
||||
int bailout_id = 0;
|
||||
for (bailout_id = 0; bailout_id < num_entries; bailout_id++) {
|
||||
if (bailout_id == deopt_id1 || bailout_id == deopt_id2) {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(
|
||||
native_context, deopt_shared, deopt_code, BailoutId(bailout_id));
|
||||
} else {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
}
|
||||
}
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), expected_length);
|
||||
|
||||
RunJS("%DeoptimizeFunction(f1)");
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), expected_length);
|
||||
|
||||
int index = (num_entries - 2) * OSROptimizedCodeCache::kEntryLength;
|
||||
EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->IsCleared());
|
||||
EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->IsCleared());
|
||||
EXPECT_TRUE(
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->IsCleared());
|
||||
|
||||
index = (num_entries - 1) * OSROptimizedCodeCache::kEntryLength;
|
||||
EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset)
|
||||
->IsCleared());
|
||||
EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset)
|
||||
->IsCleared());
|
||||
EXPECT_TRUE(
|
||||
osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->IsCleared());
|
||||
}
|
||||
|
||||
TEST_F(TestWithNativeContext, EvictDeoptedEntriesCompact) {
|
||||
if (!i::FLAG_opt) return;
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
|
||||
i::ScopedVector<char> source(1024);
|
||||
GetSource(&source, 0);
|
||||
Handle<JSFunction> function = RunJS<JSFunction>(source.begin());
|
||||
Isolate* isolate = function->GetIsolate();
|
||||
Handle<NativeContext> native_context(function->native_context(), isolate);
|
||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
|
||||
Handle<Code> code(function->code(), isolate);
|
||||
|
||||
i::ScopedVector<char> source1(1024);
|
||||
GetSource(&source1, 1);
|
||||
Handle<JSFunction> deopt_function = RunJS<JSFunction>(source1.begin());
|
||||
Handle<SharedFunctionInfo> deopt_shared(deopt_function->shared(), isolate);
|
||||
Handle<Code> deopt_code(deopt_function->code(), isolate);
|
||||
|
||||
int num_entries = kInitialEntries + 1;
|
||||
int expected_length = kInitialLength * 2;
|
||||
int bailout_id = 0;
|
||||
for (bailout_id = 0; bailout_id < num_entries; bailout_id++) {
|
||||
if (bailout_id % 2 == 0) {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(
|
||||
native_context, deopt_shared, deopt_code, BailoutId(bailout_id));
|
||||
} else {
|
||||
OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code,
|
||||
BailoutId(bailout_id));
|
||||
}
|
||||
}
|
||||
Handle<OSROptimizedCodeCache> osr_cache(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), expected_length);
|
||||
|
||||
RunJS("%DeoptimizeFunction(f1)");
|
||||
osr_cache = Handle<OSROptimizedCodeCache>(
|
||||
native_context->GetOSROptimizedCodeCache(), isolate);
|
||||
EXPECT_EQ(osr_cache->length(), kInitialLength);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user