[heap] Decouple code deoptimization from clearing weak objects.

This patch allows the deoptimizer to keep embedded pointers intact.
Previously, the deoptimizer had to clear embedded pointers because
the mark-compactor relied on the Code::marked_for_deoptimization flag
to indicate whether the embedder pointers were cleared or not.

This patch adds a new flag called Code::embedded_objects_cleared()
and thus can correctly clear dead weak objects in deoptimized code.

Bug: v8:8459
Change-Id: I6eb6ff3aa2182bc41730e0a249965f8d8c0525ce
Reviewed-on: https://chromium-review.googlesource.com/c/1335943
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Hannes Payer <hpayer@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57584}
This commit is contained in:
Ulan Degenbaev 2018-11-14 17:40:09 +01:00 committed by Commit Bot
parent f09bec92c1
commit 6b55356d3a
9 changed files with 82 additions and 26 deletions

View File

@ -332,8 +332,6 @@ void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) {
Object* next = code->next_code_link(); Object* next = code->next_code_link();
if (code->marked_for_deoptimization()) { if (code->marked_for_deoptimization()) {
// Make sure that this object does not point to any garbage.
isolate->heap()->InvalidateCodeEmbeddedObjects(code);
codes.insert(code); codes.insert(code);
if (!prev.is_null()) { if (!prev.is_null()) {

View File

@ -816,12 +816,6 @@ void Heap::ProcessPretenuringFeedback() {
} }
} }
void Heap::InvalidateCodeEmbeddedObjects(Code code) {
MemoryChunk* chunk = MemoryChunk::FromAddress(code->ptr());
CodePageMemoryModificationScope modification_scope(chunk);
code->InvalidateEmbeddedObjects(this);
}
void Heap::InvalidateCodeDeoptimizationData(Code code) { void Heap::InvalidateCodeDeoptimizationData(Code code) {
MemoryChunk* chunk = MemoryChunk::FromAddress(code->ptr()); MemoryChunk* chunk = MemoryChunk::FromAddress(code->ptr());
CodePageMemoryModificationScope modification_scope(chunk); CodePageMemoryModificationScope modification_scope(chunk);

View File

@ -869,10 +869,6 @@ class Heap {
void SetConstructStubInvokeDeoptPCOffset(int pc_offset); void SetConstructStubInvokeDeoptPCOffset(int pc_offset);
void SetInterpreterEntryReturnPCOffset(int pc_offset); void SetInterpreterEntryReturnPCOffset(int pc_offset);
// Invalidates references in the given {code} object that are directly
// embedded within the instruction stream. Mutates write-protected code.
void InvalidateCodeEmbeddedObjects(Code code);
// Invalidates references in the given {code} object that are referenced // Invalidates references in the given {code} object that are referenced
// transitively from the deoptimization data. Mutates write-protected code. // transitively from the deoptimization data. Mutates write-protected code.
void InvalidateCodeDeoptimizationData(Code code); void InvalidateCodeDeoptimizationData(Code code);

View File

@ -1889,10 +1889,13 @@ void MarkCompactCollector::MarkDependentCodeForDeoptimization() {
HeapObject* object = weak_object_in_code.first; HeapObject* object = weak_object_in_code.first;
Code code = weak_object_in_code.second; Code code = weak_object_in_code.second;
if (!non_atomic_marking_state()->IsBlackOrGrey(object) && if (!non_atomic_marking_state()->IsBlackOrGrey(object) &&
!code->marked_for_deoptimization()) { !code->embedded_objects_cleared()) {
code->SetMarkedForDeoptimization("weak objects"); if (!code->marked_for_deoptimization()) {
code->InvalidateEmbeddedObjects(heap_); code->SetMarkedForDeoptimization("weak objects");
have_code_to_deoptimize_ = true; have_code_to_deoptimize_ = true;
}
code->ClearEmbeddedObjects(heap_);
DCHECK(code->embedded_objects_cleared());
} }
} }
} }
@ -2686,11 +2689,6 @@ class EvacuationWeakObjectRetainer : public WeakObjectRetainer {
} }
}; };
// Return true if the given code is deoptimized or will be deoptimized.
bool MarkCompactCollector::WillBeDeoptimized(Code code) {
return code->is_optimized_code() && code->marked_for_deoptimization();
}
void MarkCompactCollector::RecordLiveSlotsOnPage(Page* page) { void MarkCompactCollector::RecordLiveSlotsOnPage(Page* page) {
EvacuateRecordOnlyVisitor visitor(heap()); EvacuateRecordOnlyVisitor visitor(heap());
LiveObjectVisitor::VisitBlackObjectsNoFail(page, non_atomic_marking_state(), LiveObjectVisitor::VisitBlackObjectsNoFail(page, non_atomic_marking_state(),

View File

@ -715,8 +715,6 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
explicit MarkCompactCollector(Heap* heap); explicit MarkCompactCollector(Heap* heap);
~MarkCompactCollector() override; ~MarkCompactCollector() override;
bool WillBeDeoptimized(Code code);
void ComputeEvacuationHeuristics(size_t area_size, void ComputeEvacuationHeuristics(size_t area_size,
int* target_fragmentation_percent, int* target_fragmentation_percent,
size_t* max_evacuated_bytes); size_t* max_evacuated_bytes);

View File

@ -14381,7 +14381,7 @@ void ObjectVisitor::VisitRelocInfo(RelocIterator* it) {
} }
} }
void Code::InvalidateEmbeddedObjects(Heap* heap) { void Code::ClearEmbeddedObjects(Heap* heap) {
HeapObject* undefined = ReadOnlyRoots(heap).undefined_value(); HeapObject* undefined = ReadOnlyRoots(heap).undefined_value();
int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
for (RelocIterator it(*this, mode_mask); !it.done(); it.next()) { for (RelocIterator it(*this, mode_mask); !it.done(); it.next()) {
@ -14390,6 +14390,7 @@ void Code::InvalidateEmbeddedObjects(Heap* heap) {
it.rinfo()->set_target_object(heap, undefined, SKIP_WRITE_BARRIER); it.rinfo()->set_target_object(heap, undefined, SKIP_WRITE_BARRIER);
} }
} }
set_embedded_objects_cleared(true);
} }

View File

@ -512,6 +512,20 @@ void Code::set_marked_for_deoptimization(bool flag) {
code_data_container()->set_kind_specific_flags(updated); code_data_container()->set_kind_specific_flags(updated);
} }
bool Code::embedded_objects_cleared() const {
DCHECK(kind() == OPTIMIZED_FUNCTION);
int flags = code_data_container()->kind_specific_flags();
return EmbeddedObjectsClearedField::decode(flags);
}
void Code::set_embedded_objects_cleared(bool flag) {
DCHECK(kind() == OPTIMIZED_FUNCTION);
DCHECK_IMPLIES(flag, marked_for_deoptimization());
int previous = code_data_container()->kind_specific_flags();
int updated = EmbeddedObjectsClearedField::update(previous, flag);
code_data_container()->set_kind_specific_flags(updated);
}
bool Code::deopt_already_counted() const { bool Code::deopt_already_counted() const {
DCHECK(kind() == OPTIMIZED_FUNCTION); DCHECK(kind() == OPTIMIZED_FUNCTION);
int flags = code_data_container()->kind_specific_flags(); int flags = code_data_container()->kind_specific_flags();

View File

@ -76,7 +76,9 @@ class Code : public HeapObjectPtr {
// [relocation_info]: Code relocation information // [relocation_info]: Code relocation information
DECL_ACCESSORS(relocation_info, ByteArray) DECL_ACCESSORS(relocation_info, ByteArray)
void InvalidateEmbeddedObjects(Heap* heap);
// This function should be called only from GC.
void ClearEmbeddedObjects(Heap* heap);
// [deoptimization_data]: Array containing data for deopt. // [deoptimization_data]: Array containing data for deopt.
DECL_ACCESSORS(deoptimization_data, FixedArray) DECL_ACCESSORS(deoptimization_data, FixedArray)
@ -164,10 +166,16 @@ class Code : public HeapObjectPtr {
inline void set_handler_table_offset(int offset); inline void set_handler_table_offset(int offset);
// [marked_for_deoptimization]: For kind OPTIMIZED_FUNCTION tells whether // [marked_for_deoptimization]: For kind OPTIMIZED_FUNCTION tells whether
// the code is going to be deoptimized because of dead embedded maps. // the code is going to be deoptimized.
inline bool marked_for_deoptimization() const; inline bool marked_for_deoptimization() const;
inline void set_marked_for_deoptimization(bool flag); inline void set_marked_for_deoptimization(bool flag);
// [embedded_objects_cleared]: For kind OPTIMIZED_FUNCTION tells whether
// the embedded objects in the code marked for deoptimization were cleared.
// Note that embedded_objects_cleared() implies marked_for_deoptimization().
inline bool embedded_objects_cleared() const;
inline void set_embedded_objects_cleared(bool flag);
// [deopt_already_counted]: For kind OPTIMIZED_FUNCTION tells whether // [deopt_already_counted]: For kind OPTIMIZED_FUNCTION tells whether
// the code was already deoptimized. // the code was already deoptimized.
inline bool deopt_already_counted() const; inline bool deopt_already_counted() const;
@ -416,6 +424,7 @@ class Code : public HeapObjectPtr {
// KindSpecificFlags layout (STUB, BUILTIN and OPTIMIZED_FUNCTION) // KindSpecificFlags layout (STUB, BUILTIN and OPTIMIZED_FUNCTION)
#define CODE_KIND_SPECIFIC_FLAGS_BIT_FIELDS(V, _) \ #define CODE_KIND_SPECIFIC_FLAGS_BIT_FIELDS(V, _) \
V(MarkedForDeoptimizationField, bool, 1, _) \ V(MarkedForDeoptimizationField, bool, 1, _) \
V(EmbeddedObjectsClearedField, bool, 1, _) \
V(DeoptAlreadyCountedField, bool, 1, _) \ V(DeoptAlreadyCountedField, bool, 1, _) \
V(CanHaveWeakObjectsField, bool, 1, _) \ V(CanHaveWeakObjectsField, bool, 1, _) \
V(IsConstructStubField, bool, 1, _) \ V(IsConstructStubField, bool, 1, _) \

View File

@ -3860,6 +3860,7 @@ TEST(CellsInOptimizedCodeAreWeak) {
} }
CHECK(code->marked_for_deoptimization()); CHECK(code->marked_for_deoptimization());
CHECK(code->embedded_objects_cleared());
} }
@ -3902,6 +3903,7 @@ TEST(ObjectsInOptimizedCodeAreWeak) {
} }
CHECK(code->marked_for_deoptimization()); CHECK(code->marked_for_deoptimization());
CHECK(code->embedded_objects_cleared());
} }
TEST(NewSpaceObjectsInOptimizedCode) { TEST(NewSpaceObjectsInOptimizedCode) {
@ -3962,6 +3964,52 @@ TEST(NewSpaceObjectsInOptimizedCode) {
} }
CHECK(code->marked_for_deoptimization()); CHECK(code->marked_for_deoptimization());
CHECK(code->embedded_objects_cleared());
}
TEST(ObjectsInEagerlyDeoptimizedCodeAreWeak) {
if (FLAG_always_opt || !FLAG_opt) return;
FLAG_allow_natives_syntax = true;
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
v8::internal::Heap* heap = CcTest::heap();
if (!isolate->use_optimizer()) return;
HandleScope outer_scope(heap->isolate());
Handle<Code> code;
{
LocalContext context;
HandleScope scope(heap->isolate());
CompileRun(
"function bar() {"
" return foo(1);"
"};"
"function foo(x) { with (x) { return 1 + x; } };"
"%NeverOptimizeFunction(foo);"
"bar();"
"bar();"
"bar();"
"%OptimizeFunctionOnNextCall(bar);"
"bar();"
"%DeoptimizeFunction(bar);");
Handle<JSFunction> bar = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CcTest::global()
->Get(context.local(), v8_str("bar"))
.ToLocalChecked())));
code = scope.CloseAndEscape(Handle<Code>(bar->code(), isolate));
}
CHECK(code->marked_for_deoptimization());
// Now make sure that a gc should get rid of the function
for (int i = 0; i < 4; i++) {
CcTest::CollectAllGarbage();
}
CHECK(code->marked_for_deoptimization());
CHECK(code->embedded_objects_cleared());
} }
static Handle<JSFunction> OptimizeDummyFunction(v8::Isolate* isolate, static Handle<JSFunction> OptimizeDummyFunction(v8::Isolate* isolate,