Fix candidate eviction in code flusher.
This fixes a corner case where a code flushing candidate was evicted from the candidate list without being revisited by the incremental marker. An explicit write-barrier makes sure it gets revisited. R=ulan@chromium.org BUG=chromium:159140 TEST=cctest/test-heap/Regress159140 Review URL: https://codereview.chromium.org/11451038 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13153 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
064bb33fdd
commit
31f1f2de22
@ -939,6 +939,10 @@ void CodeFlusher::EvictCandidate(JSFunction* function) {
|
||||
ASSERT(!function->next_function_link()->IsUndefined());
|
||||
Object* undefined = isolate_->heap()->undefined_value();
|
||||
|
||||
// The function is no longer a candidate, make sure it gets visited
|
||||
// again so that previous flushing decisions are revisited.
|
||||
isolate_->heap()->incremental_marking()->RecordWrites(function);
|
||||
|
||||
JSFunction* candidate = jsfunction_candidates_head_;
|
||||
JSFunction* next_candidate;
|
||||
if (candidate == function) {
|
||||
|
@ -2508,3 +2508,63 @@ TEST(Regression144230) {
|
||||
USE(global->SetProperty(*name, *call_function, NONE, kNonStrictMode));
|
||||
CompileRun("call();");
|
||||
}
|
||||
|
||||
|
||||
TEST(Regress159140) {
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
i::FLAG_flush_code_incrementally = true;
|
||||
InitializeVM();
|
||||
v8::HandleScope scope;
|
||||
|
||||
// Perform one initial GC to enable code flushing.
|
||||
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
|
||||
|
||||
// Prepare several closures that are all eligible for code flushing
|
||||
// because all reachable ones are not optimized. Make sure that the
|
||||
// optimized code object is directly reachable through a handle so
|
||||
// that it is marked black during incremental marking.
|
||||
Handle<Code> code;
|
||||
{
|
||||
HandleScope inner_scope;
|
||||
CompileRun("function h(x) {}"
|
||||
"function mkClosure() {"
|
||||
" return function(x) { return x + 1; };"
|
||||
"}"
|
||||
"var f = mkClosure();"
|
||||
"var g = mkClosure();"
|
||||
"f(1); f(2);"
|
||||
"g(1); g(2);"
|
||||
"h(1); h(2);"
|
||||
"%OptimizeFunctionOnNextCall(f); f(3);"
|
||||
"%OptimizeFunctionOnNextCall(h); h(3);");
|
||||
|
||||
Handle<JSFunction> f =
|
||||
v8::Utils::OpenHandle(
|
||||
*v8::Handle<v8::Function>::Cast(
|
||||
v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
|
||||
CHECK(f->is_compiled());
|
||||
CompileRun("f = null;");
|
||||
|
||||
Handle<JSFunction> g =
|
||||
v8::Utils::OpenHandle(
|
||||
*v8::Handle<v8::Function>::Cast(
|
||||
v8::Context::GetCurrent()->Global()->Get(v8_str("g"))));
|
||||
CHECK(g->is_compiled());
|
||||
const int kAgingThreshold = 6;
|
||||
for (int i = 0; i < kAgingThreshold; i++) {
|
||||
g->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
|
||||
}
|
||||
|
||||
code = inner_scope.CloseAndEscape(Handle<Code>(f->code()));
|
||||
}
|
||||
|
||||
// Simulate incremental marking so that the functions are enqueued as
|
||||
// code flushing candidates. Then optimize one function. Finally
|
||||
// finish the GC to complete code flushing.
|
||||
SimulateIncrementalMarking();
|
||||
CompileRun("%OptimizeFunctionOnNextCall(g); g(3);");
|
||||
HEAP->CollectAllGarbage(Heap::kNoGCFlags);
|
||||
|
||||
// Unoptimized code is missing and the deoptimizer will go ballistic.
|
||||
CompileRun("g('bozo');");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user