[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:
parent
925a2006f5
commit
aac8ee84af
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user