From 1c857357102547580d7b8444e4dc81c2113da34d Mon Sep 17 00:00:00 2001 From: mstarzinger Date: Wed, 22 Jul 2015 04:46:04 -0700 Subject: [PATCH] Allow for optimized code map to have zero entries. This allows the optimized code map to contain no context-dependent entries, but still hold one context-independent entry. This is a precursor to extending the lifetime of the context-independent entry. R=mvstanton@chromium.org Review URL: https://codereview.chromium.org/1249543005 Cr-Commit-Position: refs/heads/master@{#29788} --- src/code-stubs-hydrogen.cc | 106 +++++++++++++++++++------------------------ src/objects.cc | 8 +++- test/cctest/test-compiler.cc | 58 +++++++++++++++++++++++ 3 files changed, 111 insertions(+), 61 deletions(-) diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index 587496f..2168d1c 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -1965,71 +1965,59 @@ void CodeStubGraphBuilderBase::BuildInstallFromOptimizedCodeMap( is_optimized.Else(); { AddIncrementCounter(counters->fast_new_closure_try_optimized()); - // optimized_map points to fixed array of 3-element entries - // (native context, optimized code, literals). - // Map must never be empty, so check the first elements. + // The {optimized_map} points to fixed array of 4-element entries: + // (native context, optimized code, literals, ast-id). + // Iterate through the {optimized_map} backwards. After the loop, if no + // matching optimized code was found, install unoptimized code. + // for(i = map.length() - SharedFunctionInfo::kEntryLength; + // i >= SharedFunctionInfo::kEntriesStart; + // i -= SharedFunctionInfo::kEntryLength) { ... } HValue* first_entry_index = Add(SharedFunctionInfo::kEntriesStart); - IfBuilder already_in(this); - BuildCheckAndInstallOptimizedCode(js_function, native_context, &already_in, - optimized_map, first_entry_index); - already_in.Else(); + HValue* shared_function_entry_length = + Add(SharedFunctionInfo::kEntryLength); + LoopBuilder loop_builder(this, context(), LoopBuilder::kPostDecrement, + shared_function_entry_length); + HValue* array_length = Add( + optimized_map, nullptr, HObjectAccess::ForFixedArrayLength()); + HValue* start_pos = + AddUncasted(array_length, shared_function_entry_length); + HValue* slot_iterator = + loop_builder.BeginBody(start_pos, first_entry_index, Token::GTE); { - // Iterate through the rest of map backwards. Do not double check first - // entry. After the loop, if no matching optimized code was found, - // install unoptimized code. - // for(i = map.length() - SharedFunctionInfo::kEntryLength; - // i > SharedFunctionInfo::kEntriesStart; - // i -= SharedFunctionInfo::kEntryLength) { .. } - HValue* shared_function_entry_length = - Add(SharedFunctionInfo::kEntryLength); - LoopBuilder loop_builder(this, - context(), - LoopBuilder::kPostDecrement, - shared_function_entry_length); - HValue* array_length = Add( - optimized_map, nullptr, HObjectAccess::ForFixedArrayLength()); - HValue* start_pos = AddUncasted(array_length, - shared_function_entry_length); - HValue* slot_iterator = loop_builder.BeginBody(start_pos, - first_entry_index, - Token::GT); + IfBuilder done_check(this); + BuildCheckAndInstallOptimizedCode(js_function, native_context, + &done_check, optimized_map, + slot_iterator); + // Fall out of the loop + loop_builder.Break(); + } + loop_builder.EndBody(); + + // If {slot_iterator} is less than the first entry index, then we failed to + // find a context-dependent code and try context-independent code next. + IfBuilder no_optimized_code_check(this); + no_optimized_code_check.If( + slot_iterator, first_entry_index, Token::LT); + no_optimized_code_check.Then(); + { + IfBuilder shared_code_check(this); + HValue* shared_code = + Add(optimized_map, nullptr, + HObjectAccess::ForOptimizedCodeMapSharedCode()); + shared_code_check.IfNot( + shared_code, graph()->GetConstantUndefined()); + shared_code_check.Then(); { - IfBuilder done_check(this); - BuildCheckAndInstallOptimizedCode(js_function, native_context, - &done_check, - optimized_map, - slot_iterator); - // Fall out of the loop - loop_builder.Break(); + // Store the context-independent optimized code. + HValue* literals = Add(factory->empty_fixed_array()); + BuildInstallOptimizedCode(js_function, native_context, shared_code, + literals); } - loop_builder.EndBody(); - - // If slot_iterator equals first entry index, then we failed to find a - // context-dependent code and try context-independent code next. - IfBuilder no_optimized_code_check(this); - no_optimized_code_check.If( - slot_iterator, first_entry_index, Token::EQ); - no_optimized_code_check.Then(); + shared_code_check.Else(); { - IfBuilder shared_code_check(this); - HValue* shared_code = Add( - optimized_map, nullptr, - HObjectAccess::ForOptimizedCodeMapSharedCode()); - shared_code_check.IfNot( - shared_code, graph()->GetConstantUndefined()); - shared_code_check.Then(); - { - // Store the context-independent optimized code. - HValue* literals = Add(factory->empty_fixed_array()); - BuildInstallOptimizedCode(js_function, native_context, shared_code, - literals); - } - shared_code_check.Else(); - { - // Store the unoptimized code. - BuildInstallCode(js_function, shared_info); - } + // Store the unoptimized code. + BuildInstallCode(js_function, shared_info); } } } diff --git a/src/objects.cc b/src/objects.cc index fb02d8d..1bb3cb6 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -9542,7 +9542,10 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, // Always trim even when array is cleared because of heap verifier. GetHeap()->RightTrimFixedArray(code_map, length - dst); - if (code_map->length() == kEntriesStart) ClearOptimizedCodeMap(); + if (code_map->length() == kEntriesStart && + code_map->get(kSharedCodeIndex)->IsUndefined()) { + ClearOptimizedCodeMap(); + } } } @@ -9554,7 +9557,8 @@ void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { // Always trim even when array is cleared because of heap verifier. GetHeap()->RightTrimFixedArray(code_map, shrink_by); - if (code_map->length() == kEntriesStart) { + if (code_map->length() == kEntriesStart && + code_map->get(kSharedCodeIndex)->IsUndefined()) { ClearOptimizedCodeMap(); } } diff --git a/test/cctest/test-compiler.cc b/test/cctest/test-compiler.cc index 038d4d7..e7ced4d 100644 --- a/test/cctest/test-compiler.cc +++ b/test/cctest/test-compiler.cc @@ -456,6 +456,64 @@ TEST(OptimizedCodeSharing2) { } +// Test that optimized code for different closures is actually shared +// immediately by the FastNewClosureStub without context-dependent entries. +TEST(OptimizedCodeSharing3) { + if (FLAG_stress_compaction) return; + FLAG_allow_natives_syntax = true; + FLAG_cache_optimized_code = true; + FLAG_turbo_cache_shared_code = true; + const char* flag = "--turbo-filter=*"; + FlagList::SetFlagsFromString(flag, StrLength(flag)); + CcTest::InitializeVM(); + v8::HandleScope scope(CcTest::isolate()); + v8::Local script = v8_compile( + "function MakeClosure() {" + " return function() { return x; };" + "}"); + Handle reference_code; + { + LocalContext env; + env->Global()->Set(v8::String::NewFromUtf8(CcTest::isolate(), "x"), + v8::Integer::New(CcTest::isolate(), 23)); + script->GetUnboundScript()->BindToCurrentContext()->Run(); + CompileRun( + "var closure0 = MakeClosure();" + "%DebugPrint(closure0());" + "%OptimizeFunctionOnNextCall(closure0);" + "%DebugPrint(closure0());"); + Handle fun0 = v8::Utils::OpenHandle( + *v8::Local::Cast(env->Global()->Get(v8_str("closure0")))); + CHECK(fun0->IsOptimized() || !CcTest::i_isolate()->use_crankshaft()); + reference_code = handle(fun0->code()); + // Evict only the context-dependent entry from the optimized code map. This + // leaves it in a state where only the context-independent entry exists. + fun0->shared()->TrimOptimizedCodeMap(SharedFunctionInfo::kEntryLength); + } + for (int i = 0; i < 10; i++) { + LocalContext env; + env->Global()->Set(v8::String::NewFromUtf8(CcTest::isolate(), "x"), + v8::Integer::New(CcTest::isolate(), i)); + script->GetUnboundScript()->BindToCurrentContext()->Run(); + CompileRun( + "var closure0 = MakeClosure();" + "%DebugPrint(closure0());" + "%OptimizeFunctionOnNextCall(closure0);" + "%DebugPrint(closure0());" + "var closure1 = MakeClosure();" + "var closure2 = MakeClosure();"); + Handle fun1 = v8::Utils::OpenHandle( + *v8::Local::Cast(env->Global()->Get(v8_str("closure1")))); + Handle fun2 = v8::Utils::OpenHandle( + *v8::Local::Cast(env->Global()->Get(v8_str("closure2")))); + CHECK(fun1->IsOptimized() || !CcTest::i_isolate()->use_crankshaft()); + CHECK(fun2->IsOptimized() || !CcTest::i_isolate()->use_crankshaft()); + CHECK_EQ(*reference_code, fun1->code()); + CHECK_EQ(*reference_code, fun2->code()); + } +} + + TEST(CompileFunctionInContext) { CcTest::InitializeVM(); v8::HandleScope scope(CcTest::isolate()); -- 2.7.4