[deoptimizer] Remove deoptimized code list

The deoptimized code list is inserted into when walking a native context
to find Code objects marked for deoptimization, and is then only used
for two purposes:

  1. Looking up lazy deoptimizing code objects by PC, and
  2. Counting deoptimizing code that's not marked for deoptimization.

Point 1 is slow, as it is a linked list traversal, and is made slightly
slower by the CodeT refactoring which adds another layer of indirection
to the list. The existing Isolate::FindCodeObject approach is faster,
and is already used in the deoptimizer for Code objects not found in the
list, in particular all eager deopts.

The careful reader will notice that point 2 results in a count that's
always zero, since the count excludes exactly those code objects which
are added to the list (ones marked for deopt). Indeed, all uses (which
were all in tests) were verying only that it is equal to zero.

So, we can remove this deoptimized code list entirely.

Change-Id: I352e77b1df83260a30464dbac7f268484211b2e3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4030582
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84325}
This commit is contained in:
Leszek Swirski 2022-11-17 10:43:30 +01:00 committed by V8 LUCI CQ
parent 7f0edaad07
commit 8fa1da43af
8 changed files with 8 additions and 91 deletions

View File

@ -175,22 +175,6 @@ class FrameWriter {
unsigned top_offset_; unsigned top_offset_;
}; };
Code Deoptimizer::FindDeoptimizingCode(Address addr) {
if (function_.IsHeapObject()) {
// Search all deoptimizing code in the native context of the function.
Isolate* isolate = isolate_;
NativeContext native_context = function_.native_context();
Object element = native_context.DeoptimizedCodeListHead();
while (!element.IsUndefined(isolate)) {
CodeT code = CodeT::cast(element);
CHECK(CodeKindCanDeoptimize(code.kind()));
if (code.contains(isolate, addr)) return FromCodeT(code);
element = code.next_code_link();
}
}
return Code();
}
// We rely on this function not causing a GC. It is called from generated code // We rely on this function not causing a GC. It is called from generated code
// without having a real stack frame in place. // without having a real stack frame in place.
Deoptimizer* Deoptimizer::New(Address raw_function, DeoptimizeKind kind, Deoptimizer* Deoptimizer::New(Address raw_function, DeoptimizeKind kind,
@ -299,8 +283,8 @@ class ActivationsFinder : public ThreadVisitor {
}; };
} // namespace } // namespace
// Move marked code from the optimized code list to the deoptimized code list, // Move marked code out of the optimized code list, and replace pc on the stack
// and replace pc on the stack for codes marked for deoptimization. // for codes marked for deoptimization.
// static // static
void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) { void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
DisallowGarbageCollection no_gc; DisallowGarbageCollection no_gc;
@ -346,8 +330,8 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
// deoptimization and have not been found in stack frames. // deoptimization and have not been found in stack frames.
std::set<CodeT> codes; std::set<CodeT> codes;
// Move marked code from the optimized code list to the deoptimized code list. // Move marked code out of the optimized code list. Walk over all optimized
// Walk over all optimized code objects in this native context. // code objects in this native context.
CodeT prev; CodeT prev;
Object element = native_context.OptimizedCodeListHead(); Object element = native_context.OptimizedCodeListHead();
while (!element.IsUndefined(isolate)) { while (!element.IsUndefined(isolate)) {
@ -368,10 +352,7 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(NativeContext native_context) {
// There was no previous node, the next node is the new head. // There was no previous node, the next node is the new head.
native_context.SetOptimizedCodeListHead(next); native_context.SetOptimizedCodeListHead(next);
} }
code.set_next_code_link(ReadOnlyRoots(isolate).undefined_value());
// Move the code to the _deoptimized_ code list.
code.set_next_code_link(native_context.DeoptimizedCodeListHead());
native_context.SetDeoptimizedCodeListHead(code);
} else { } else {
// Not marked; preserve this element. // Not marked; preserve this element.
prev = code; prev = code;
@ -563,8 +544,6 @@ Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction function,
} }
Code Deoptimizer::FindOptimizedCode() { Code Deoptimizer::FindOptimizedCode() {
Code compiled_code = FindDeoptimizingCode(from_);
if (!compiled_code.is_null()) return compiled_code;
CodeLookupResult lookup_result = isolate_->FindCodeObject(from_); CodeLookupResult lookup_result = isolate_->FindCodeObject(from_);
return lookup_result.code(); return lookup_result.code();
} }
@ -627,26 +606,6 @@ bool Deoptimizer::IsDeoptimizationEntry(Isolate* isolate, Address addr,
UNREACHABLE(); UNREACHABLE();
} }
int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) {
int length = 0;
// Count all entries in the deoptimizing code list of every context.
Object context = isolate->heap()->native_contexts_list();
while (!context.IsUndefined(isolate)) {
NativeContext native_context = NativeContext::cast(context);
Object element = native_context.DeoptimizedCodeListHead();
while (!element.IsUndefined(isolate)) {
CodeT code = CodeT::cast(element);
DCHECK(CodeKindCanDeoptimize(code.kind()));
if (!code.marked_for_deoptimization()) {
length++;
}
element = code.next_code_link();
}
context = Context::cast(context).next_context_link();
}
return length;
}
namespace { namespace {
int LookupCatchHandler(Isolate* isolate, TranslatedFrame* translated_frame, int LookupCatchHandler(Isolate* isolate, TranslatedFrame* translated_frame,

View File

@ -120,8 +120,6 @@ class Deoptimizer : public Malloced {
return offsetof(Deoptimizer, caller_frame_top_); return offsetof(Deoptimizer, caller_frame_top_);
} }
V8_EXPORT_PRIVATE static int GetDeoptimizedCodeCount(Isolate* isolate);
Isolate* isolate() const { return isolate_; } Isolate* isolate() const { return isolate_; }
static constexpr int kMaxNumberOfEntries = 16384; static constexpr int kMaxNumberOfEntries = 16384;

View File

@ -139,8 +139,6 @@ struct WeakListVisitor<Context> {
// Code objects are always allocated in Code space, we do not have to // Code objects are always allocated in Code space, we do not have to
// visit them during scavenges. // visit them during scavenges.
DoWeakList<CodeT>(heap, context, retainer, Context::OPTIMIZED_CODE_LIST); DoWeakList<CodeT>(heap, context, retainer, Context::OPTIMIZED_CODE_LIST);
DoWeakList<CodeT>(heap, context, retainer,
Context::DEOPTIMIZED_CODE_LIST);
} }
} }
@ -163,7 +161,6 @@ struct WeakListVisitor<Context> {
static void VisitPhantomObject(Heap* heap, Context context) { static void VisitPhantomObject(Heap* heap, Context context) {
ClearWeakList<CodeT>(heap, context.get(Context::OPTIMIZED_CODE_LIST)); ClearWeakList<CodeT>(heap, context.get(Context::OPTIMIZED_CODE_LIST));
ClearWeakList<CodeT>(heap, context.get(Context::DEOPTIMIZED_CODE_LIST));
} }
}; };

View File

@ -311,14 +311,6 @@ Object NativeContext::OptimizedCodeListHead() {
return get(OPTIMIZED_CODE_LIST); return get(OPTIMIZED_CODE_LIST);
} }
void NativeContext::SetDeoptimizedCodeListHead(Object head) {
set(DEOPTIMIZED_CODE_LIST, head, UPDATE_WRITE_BARRIER, kReleaseStore);
}
Object NativeContext::DeoptimizedCodeListHead() {
return get(DEOPTIMIZED_CODE_LIST);
}
OBJECT_CONSTRUCTORS_IMPL(NativeContext, Context) OBJECT_CONSTRUCTORS_IMPL(NativeContext, Context)
} // namespace internal } // namespace internal

View File

@ -549,9 +549,8 @@ class Context : public TorqueGeneratedContext<Context, HeapObject> {
// Properties from here are treated as weak references by the full GC. // Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references. // Scavenge treats them as strong references.
OPTIMIZED_CODE_LIST, // Weak. OPTIMIZED_CODE_LIST, // Weak.
DEOPTIMIZED_CODE_LIST, // Weak. NEXT_CONTEXT_LINK, // Weak.
NEXT_CONTEXT_LINK, // Weak.
// Total number of slots. // Total number of slots.
NATIVE_CONTEXT_SLOTS, NATIVE_CONTEXT_SLOTS,
@ -785,8 +784,6 @@ class NativeContext : public Context {
V8_EXPORT_PRIVATE void AddOptimizedCode(CodeT code); V8_EXPORT_PRIVATE void AddOptimizedCode(CodeT code);
inline void SetOptimizedCodeListHead(Object head); inline void SetOptimizedCodeListHead(Object head);
inline Object OptimizedCodeListHead(); inline Object OptimizedCodeListHead();
inline void SetDeoptimizedCodeListHead(Object head);
inline Object DeoptimizedCodeListHead();
void ResetErrorsThrown(); void ResetErrorsThrown();
void IncrementErrorsThrown(); void IncrementErrorsThrown();

View File

@ -1398,14 +1398,10 @@ void V8HeapExplorer::ExtractContextReferences(HeapEntry* entry,
context.get(Context::OPTIMIZED_CODE_LIST), context.get(Context::OPTIMIZED_CODE_LIST),
Context::OffsetOfElementAt(Context::OPTIMIZED_CODE_LIST), Context::OffsetOfElementAt(Context::OPTIMIZED_CODE_LIST),
HeapEntry::kCustomWeakPointer); HeapEntry::kCustomWeakPointer);
SetWeakReference(entry, "deoptimized_code_list",
context.get(Context::DEOPTIMIZED_CODE_LIST),
Context::OffsetOfElementAt(Context::DEOPTIMIZED_CODE_LIST),
HeapEntry::kCustomWeakPointer);
static_assert(Context::OPTIMIZED_CODE_LIST == Context::FIRST_WEAK_SLOT); static_assert(Context::OPTIMIZED_CODE_LIST == Context::FIRST_WEAK_SLOT);
static_assert(Context::NEXT_CONTEXT_LINK + 1 == static_assert(Context::NEXT_CONTEXT_LINK + 1 ==
Context::NATIVE_CONTEXT_SLOTS); Context::NATIVE_CONTEXT_SLOTS);
static_assert(Context::FIRST_WEAK_SLOT + 3 == static_assert(Context::FIRST_WEAK_SLOT + 2 ==
Context::NATIVE_CONTEXT_SLOTS); Context::NATIVE_CONTEXT_SLOTS);
} }
} }

View File

@ -27,7 +27,6 @@ class V8_NODISCARD SanitizeNativeContextScope final {
const DisallowGarbageCollection& no_gc) const DisallowGarbageCollection& no_gc)
: native_context_(native_context), : native_context_(native_context),
optimized_code_list_(native_context.OptimizedCodeListHead()), optimized_code_list_(native_context.OptimizedCodeListHead()),
deoptimized_code_list_(native_context.DeoptimizedCodeListHead()),
no_gc_(no_gc) { no_gc_(no_gc) {
#ifdef DEBUG #ifdef DEBUG
if (!allow_active_isolate_for_testing) { if (!allow_active_isolate_for_testing) {
@ -39,7 +38,6 @@ class V8_NODISCARD SanitizeNativeContextScope final {
DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero()); DCHECK(microtask_queue->DebugMicrotasksScopeDepthIsZero());
// Code lists. // Code lists.
DCHECK(optimized_code_list_.IsUndefined(isolate)); DCHECK(optimized_code_list_.IsUndefined(isolate));
DCHECK(deoptimized_code_list_.IsUndefined(isolate));
} }
#endif #endif
microtask_queue_external_pointer_ = microtask_queue_external_pointer_ =
@ -48,13 +46,11 @@ class V8_NODISCARD SanitizeNativeContextScope final {
.GetAndClearContentForSerialization(no_gc); .GetAndClearContentForSerialization(no_gc);
Object undefined = ReadOnlyRoots(isolate).undefined_value(); Object undefined = ReadOnlyRoots(isolate).undefined_value();
native_context.SetOptimizedCodeListHead(undefined); native_context.SetOptimizedCodeListHead(undefined);
native_context.SetDeoptimizedCodeListHead(undefined);
} }
~SanitizeNativeContextScope() { ~SanitizeNativeContextScope() {
// Restore saved fields. // Restore saved fields.
native_context_.SetOptimizedCodeListHead(optimized_code_list_); native_context_.SetOptimizedCodeListHead(optimized_code_list_);
native_context_.SetDeoptimizedCodeListHead(deoptimized_code_list_);
native_context_ native_context_
.RawExternalPointerField(NativeContext::kMicrotaskQueueOffset) .RawExternalPointerField(NativeContext::kMicrotaskQueueOffset)
.RestoreContentAfterSerialization(microtask_queue_external_pointer_, .RestoreContentAfterSerialization(microtask_queue_external_pointer_,
@ -65,7 +61,6 @@ class V8_NODISCARD SanitizeNativeContextScope final {
NativeContext native_context_; NativeContext native_context_;
ExternalPointerSlot::RawContent microtask_queue_external_pointer_; ExternalPointerSlot::RawContent microtask_queue_external_pointer_;
const Object optimized_code_list_; const Object optimized_code_list_;
const Object deoptimized_code_list_;
const DisallowGarbageCollection& no_gc_; const DisallowGarbageCollection& no_gc_;
}; };

View File

@ -141,7 +141,6 @@ TEST_F(DeoptimizationTest, DeoptimizeSimple) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode()); CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
// Test lazy deoptimization of a simple function. Call the function after the // Test lazy deoptimization of a simple function. Call the function after the
// deoptimization while it is still activated further down the stack. // deoptimization while it is still activated further down the stack.
@ -157,7 +156,6 @@ TEST_F(DeoptimizationTest, DeoptimizeSimple) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode()); CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationTest, DeoptimizeSimpleWithArguments) { TEST_F(DeoptimizationTest, DeoptimizeSimpleWithArguments) {
@ -178,7 +176,6 @@ TEST_F(DeoptimizationTest, DeoptimizeSimpleWithArguments) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode()); CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
// Test lazy deoptimization of a simple function with some arguments. Call the // Test lazy deoptimization of a simple function with some arguments. Call the
// function after the deoptimization while it is still activated further down // function after the deoptimization while it is still activated further down
@ -195,7 +192,6 @@ TEST_F(DeoptimizationTest, DeoptimizeSimpleWithArguments) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode()); CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationTest, DeoptimizeSimpleNested) { TEST_F(DeoptimizationTest, DeoptimizeSimpleNested) {
@ -218,7 +214,6 @@ TEST_F(DeoptimizationTest, DeoptimizeSimpleNested) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(6, "result", context()); CheckJsInt32(6, "result", context());
CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode()); CHECK(!GetJSFunction("f")->HasAttachedOptimizedCode());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
} }
@ -241,7 +236,6 @@ TEST_F(DeoptimizationTest, DeoptimizeRecursive) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(11, "calls", context()); CheckJsInt32(11, "calls", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast( v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(
context()->Global()->Get(context(), NewString("f")).ToLocalChecked()); context()->Global()->Get(context(), NewString("f")).ToLocalChecked());
@ -272,7 +266,6 @@ TEST_F(DeoptimizationTest, DeoptimizeMultiple) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(14, "result", context()); CheckJsInt32(14, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationTest, DeoptimizeConstructor) { TEST_F(DeoptimizationTest, DeoptimizeConstructor) {
@ -296,7 +289,6 @@ TEST_F(DeoptimizationTest, DeoptimizeConstructor) {
->Get(context(), NewString("result")) ->Get(context(), NewString("result"))
.ToLocalChecked() .ToLocalChecked()
->IsTrue()); ->IsTrue());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
{ {
AlwaysOptimizeAllowNativesSyntaxNoInlining options; AlwaysOptimizeAllowNativesSyntaxNoInlining options;
@ -313,7 +305,6 @@ TEST_F(DeoptimizationTest, DeoptimizeConstructor) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(3, "result", context()); CheckJsInt32(3, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationTest, DeoptimizeConstructorMultiple) { TEST_F(DeoptimizationTest, DeoptimizeConstructorMultiple) {
@ -341,7 +332,6 @@ TEST_F(DeoptimizationTest, DeoptimizeConstructorMultiple) {
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(14, "result", context()); CheckJsInt32(14, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
class DeoptimizationDisableConcurrentRecompilationTest class DeoptimizationDisableConcurrentRecompilationTest
@ -439,7 +429,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
CHECK(result->IsString()); CHECK(result->IsString());
v8::String::Utf8Value utf8(isolate(), result); v8::String::Utf8Value utf8(isolate(), result);
CHECK_EQ(0, strcmp("a+an X", *utf8)); CHECK_EQ(0, strcmp("a+an X", *utf8));
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
@ -451,7 +440,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(15, "result", context()); CheckJsInt32(15, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
@ -463,7 +451,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(-1, "result", context()); CheckJsInt32(-1, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
@ -476,7 +463,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(56, "result", context()); CheckJsInt32(56, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
@ -488,7 +474,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(0, "result", context()); CheckJsInt32(0, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
@ -500,7 +485,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest,
CheckJsInt32(1, "count", context()); CheckJsInt32(1, "count", context());
CheckJsInt32(7, "result", context()); CheckJsInt32(7, "result", context());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, DeoptimizeCompare) { TEST_F(DeoptimizationDisableConcurrentRecompilationTest, DeoptimizeCompare) {
@ -550,7 +534,6 @@ TEST_F(DeoptimizationDisableConcurrentRecompilationTest, DeoptimizeCompare) {
->Get(context(), NewString("result")) ->Get(context(), NewString("result"))
.ToLocalChecked() .ToLocalChecked()
->IsTrue()); ->IsTrue());
CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate()));
} }
TEST_F(DeoptimizationDisableConcurrentRecompilationTest, TEST_F(DeoptimizationDisableConcurrentRecompilationTest,