[heap] Fix corner case in optimized code map processing.

This fixes a corner case where optimized code could still be reachable
through the optimized code map, even though unoptimized code for any
inlined function might have long been flushed.

R=ulan@chromium.org
TEST=cctest/test-heap/Regress513496
BUG=chromium:513496
LOG=n

Review URL: https://codereview.chromium.org/1415683011

Cr-Commit-Position: refs/heads/master@{#31857}
This commit is contained in:
mstarzinger 2015-11-06 06:28:51 -08:00 committed by Commit bot
parent 925a2006f5
commit aac8ee84af
2 changed files with 72 additions and 10 deletions

View File

@ -428,6 +428,13 @@ void StaticMarkingVisitor<StaticVisitor>::VisitCode(Map* map,
if (FLAG_age_code && !heap->isolate()->serializer_enabled()) {
code->MakeOlder(heap->mark_compact_collector()->marking_parity());
}
MarkCompactCollector* collector = heap->mark_compact_collector();
if (collector->is_code_flushing_enabled()) {
if (code->kind() == Code::OPTIMIZED_FUNCTION) {
// Visit all unoptimized code objects to prevent flushing them.
MarkInlinedFunctionsCode(heap, code);
}
}
code->CodeIterateBody<StaticVisitor>(heap);
}
@ -511,9 +518,6 @@ void StaticMarkingVisitor<StaticVisitor>::VisitJSFunction(Map* map,
} else {
// Visit all unoptimized code objects to prevent flushing them.
StaticVisitor::MarkObject(heap, function->shared()->code());
if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
MarkInlinedFunctionsCode(heap, function->code());
}
}
}
VisitJSFunctionStrongCode(heap, object);
@ -655,13 +659,15 @@ void StaticMarkingVisitor<StaticVisitor>::MarkInlinedFunctionsCode(Heap* heap,
// For optimized functions we should retain both non-optimized version
// of its code and non-optimized version of all inlined functions.
// This is required to support bailing out from inlined code.
DeoptimizationInputData* const data =
DeoptimizationInputData::cast(code->deoptimization_data());
FixedArray* const literals = data->LiteralArray();
int const inlined_count = data->InlinedFunctionCount()->value();
for (int i = 0; i < inlined_count; ++i) {
StaticVisitor::MarkObject(
heap, SharedFunctionInfo::cast(literals->get(i))->code());
if (code->deoptimization_data() != heap->empty_fixed_array()) {
DeoptimizationInputData* const data =
DeoptimizationInputData::cast(code->deoptimization_data());
FixedArray* const literals = data->LiteralArray();
int const inlined_count = data->InlinedFunctionCount()->value();
for (int i = 0; i < inlined_count; ++i) {
StaticVisitor::MarkObject(
heap, SharedFunctionInfo::cast(literals->get(i))->code());
}
}
}

View File

@ -4430,6 +4430,62 @@ TEST(Regress514122) {
}
TEST(Regress513496) {
i::FLAG_flush_optimized_code_cache = false;
i::FLAG_allow_natives_syntax = true;
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Heap* heap = isolate->heap();
HandleScope scope(isolate);
// Perfrom one initial GC to enable code flushing.
CcTest::heap()->CollectAllGarbage();
// Prepare an optimized closure with containing an inlined function. Then age
// the inlined unoptimized code to trigger code flushing but make sure the
// outer optimized code is kept in the optimized code map.
Handle<SharedFunctionInfo> shared;
{
HandleScope inner_scope(isolate);
CompileRun(
"function g(x) { return x + 1 }"
"function mkClosure() {"
" return function(x) { return g(x); };"
"}"
"var f = mkClosure();"
"f(1); f(2);"
"%OptimizeFunctionOnNextCall(f); f(3);");
Handle<JSFunction> g = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("g")))));
CHECK(g->shared()->is_compiled());
const int kAgingThreshold = 6;
for (int i = 0; i < kAgingThreshold; i++) {
g->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
}
Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f")))));
CHECK(f->is_compiled());
shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate));
CompileRun("f = null");
}
// Lookup the optimized code and keep it alive.
CodeAndLiterals result = shared->SearchOptimizedCodeMap(
isolate->context()->native_context(), BailoutId::None());
Handle<Code> optimized_code(result.code, isolate);
// Finish a full GC cycle so that the unoptimized code of 'g' is flushed even
// though the optimized code for 'f' is reachable via the optimized code map.
heap->CollectAllGarbage();
// Make a new closure that will get code installed from the code map.
// Unoptimized code is missing and the deoptimizer will go ballistic.
CompileRun("var h = mkClosure(); h('bozo');");
}
TEST(LargeObjectSlotRecording) {
FLAG_manual_evacuation_candidates_selection = true;
CcTest::InitializeVM();