Commit dae20b0d authored by Mythri's avatar Mythri Committed by Commit Bot

Reland "Add support to produce code cache after execute"

Adds new API function to request code cache. Earlier code cache was
produced along with compile requests. This new API allows us to request
code cache after executing. Also adds support in the code serializer to
serialize after executing the script.

Bug: chromium:783124,chromium:789694
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: Id4e6a967e176e3e979dc4ccb9a37a353c70c3890
Reviewed-on: https://chromium-review.googlesource.com/797036Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49793}
parent e9c93223
...@@ -1576,6 +1576,14 @@ class V8_EXPORT ScriptCompiler { ...@@ -1576,6 +1576,14 @@ class V8_EXPORT ScriptCompiler {
Local<String> arguments[], size_t context_extension_count, Local<String> arguments[], size_t context_extension_count,
Local<Object> context_extensions[]); Local<Object> context_extensions[]);
/**
* Creates and returns code cache for the specified unbound_script.
* This will return nullptr if the script cannot be serialized. The
* CachedData returned by this function should be owned by the caller.
*/
static CachedData* CreateCodeCache(Local<UnboundScript> unbound_script,
Local<String> source);
private: private:
static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundInternal( static V8_WARN_UNUSED_RESULT MaybeLocal<UnboundScript> CompileUnboundInternal(
Isolate* isolate, Source* source, CompileOptions options, Isolate* isolate, Source* source, CompileOptions options,
......
...@@ -2660,6 +2660,26 @@ uint32_t ScriptCompiler::CachedDataVersionTag() { ...@@ -2660,6 +2660,26 @@ uint32_t ScriptCompiler::CachedDataVersionTag() {
static_cast<uint32_t>(internal::CpuFeatures::SupportedFeatures()))); static_cast<uint32_t>(internal::CpuFeatures::SupportedFeatures())));
} }
ScriptCompiler::CachedData* ScriptCompiler::CreateCodeCache(
Local<UnboundScript> unbound_script, Local<String> source) {
i::Handle<i::SharedFunctionInfo> shared =
i::Handle<i::SharedFunctionInfo>::cast(
Utils::OpenHandle(*unbound_script));
DCHECK(shared->is_toplevel());
i::Handle<i::Script> script(i::Script::cast(shared->script()));
i::Isolate* isolate = shared->GetIsolate();
// TODO(7110): Enable serialization of Asm modules once the AsmWasmData is
// context independent.
if (script->ContainsAsmModule()) return nullptr;
if (isolate->debug()->is_loaded()) return nullptr;
i::ScriptData* script_data =
i::CodeSerializer::Serialize(isolate, shared, Utils::OpenHandle(*source));
CachedData* result = new CachedData(
script_data->data(), script_data->length(), CachedData::BufferOwned);
script_data->ReleaseDataOwnership();
delete script_data;
return result;
}
MaybeLocal<Script> Script::Compile(Local<Context> context, Local<String> source, MaybeLocal<Script> Script::Compile(Local<Context> context, Local<String> source,
ScriptOrigin* origin) { ScriptOrigin* origin) {
......
...@@ -1177,15 +1177,6 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval( ...@@ -1177,15 +1177,6 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
namespace { namespace {
bool ContainsAsmModule(Handle<Script> script) {
DisallowHeapAllocation no_gc;
SharedFunctionInfo::ScriptIterator iter(script);
while (SharedFunctionInfo* info = iter.Next()) {
if (info->HasAsmWasmData()) return true;
}
return false;
}
bool ShouldProduceCodeCache(ScriptCompiler::CompileOptions options) { bool ShouldProduceCodeCache(ScriptCompiler::CompileOptions options) {
return options == ScriptCompiler::kProduceCodeCache || return options == ScriptCompiler::kProduceCodeCache ||
options == ScriptCompiler::kProduceFullCodeCache; options == ScriptCompiler::kProduceFullCodeCache;
...@@ -1573,7 +1564,7 @@ MaybeHandle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript( ...@@ -1573,7 +1564,7 @@ MaybeHandle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript(
compilation_cache->PutScript(source, context, language_mode, result, compilation_cache->PutScript(source, context, language_mode, result,
vector); vector);
if (ShouldProduceCodeCache(compile_options) && if (ShouldProduceCodeCache(compile_options) &&
!ContainsAsmModule(script)) { !script->ContainsAsmModule()) {
compile_timer.set_producing_code_cache(); compile_timer.set_producing_code_cache();
HistogramTimerScope histogram_timer( HistogramTimerScope histogram_timer(
......
This diff is collapsed.
...@@ -296,6 +296,7 @@ class ShellOptions { ...@@ -296,6 +296,7 @@ class ShellOptions {
num_isolates(1), num_isolates(1),
compile_options(v8::ScriptCompiler::kNoCompileOptions), compile_options(v8::ScriptCompiler::kNoCompileOptions),
stress_background_compile(false), stress_background_compile(false),
cache_code_after_execute(false),
isolate_sources(nullptr), isolate_sources(nullptr),
icu_data_file(nullptr), icu_data_file(nullptr),
natives_blob(nullptr), natives_blob(nullptr),
...@@ -330,6 +331,7 @@ class ShellOptions { ...@@ -330,6 +331,7 @@ class ShellOptions {
int num_isolates; int num_isolates;
v8::ScriptCompiler::CompileOptions compile_options; v8::ScriptCompiler::CompileOptions compile_options;
bool stress_background_compile; bool stress_background_compile;
bool cache_code_after_execute;
SourceGroup* isolate_sources; SourceGroup* isolate_sources;
const char* icu_data_file; const char* icu_data_file;
const char* natives_blob; const char* natives_blob;
...@@ -346,9 +348,6 @@ class ShellOptions { ...@@ -346,9 +348,6 @@ class ShellOptions {
class Shell : public i::AllStatic { class Shell : public i::AllStatic {
public: public:
static MaybeLocal<Script> CompileString(
Isolate* isolate, Local<String> source, Local<Value> name,
v8::ScriptCompiler::CompileOptions compile_options);
static bool ExecuteString(Isolate* isolate, Local<String> source, static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, bool print_result, Local<Value> name, bool print_result,
bool report_exceptions); bool report_exceptions);
...@@ -506,10 +505,16 @@ class Shell : public i::AllStatic { ...@@ -506,10 +505,16 @@ class Shell : public i::AllStatic {
int index); int index);
static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Context> context, static MaybeLocal<Module> FetchModuleTree(v8::Local<v8::Context> context,
const std::string& file_name); const std::string& file_name);
static ScriptCompiler::CachedData* LookupCodeCache(Isolate* isolate,
Local<Value> name);
static void StoreInCodeCache(Isolate* isolate, Local<Value> name,
const ScriptCompiler::CachedData* data);
// We may have multiple isolates running concurrently, so the access to // We may have multiple isolates running concurrently, so the access to
// the isolate_status_ needs to be concurrency-safe. // the isolate_status_ needs to be concurrency-safe.
static base::LazyMutex isolate_status_lock_; static base::LazyMutex isolate_status_lock_;
static std::map<Isolate*, bool> isolate_status_; static std::map<Isolate*, bool> isolate_status_;
static std::map<std::string, std::unique_ptr<ScriptCompiler::CachedData>>
cached_code_map_;
}; };
......
...@@ -13264,6 +13264,15 @@ bool Script::GetPositionInfo(Handle<Script> script, int position, ...@@ -13264,6 +13264,15 @@ bool Script::GetPositionInfo(Handle<Script> script, int position,
bool Script::IsUserJavaScript() { return type() == Script::TYPE_NORMAL; } bool Script::IsUserJavaScript() { return type() == Script::TYPE_NORMAL; }
bool Script::ContainsAsmModule() {
DisallowHeapAllocation no_gc;
SharedFunctionInfo::ScriptIterator iter(Handle<Script>(this));
while (SharedFunctionInfo* info = iter.Next()) {
if (info->HasAsmWasmData()) return true;
}
return false;
}
namespace { namespace {
bool GetPositionInfoSlow(const Script* script, int position, bool GetPositionInfoSlow(const Script* script, int position,
Script::PositionInfo* info) { Script::PositionInfo* info) {
......
...@@ -635,6 +635,14 @@ ByteArray* BytecodeArray::SourcePositionTable() { ...@@ -635,6 +635,14 @@ ByteArray* BytecodeArray::SourcePositionTable() {
->source_position_table(); ->source_position_table();
} }
void BytecodeArray::ClearFrameCacheFromSourcePositionTable() {
Object* maybe_table = source_position_table();
if (maybe_table->IsByteArray()) return;
DCHECK(maybe_table->IsSourcePositionTableWithFrameCache());
set_source_position_table(SourcePositionTableWithFrameCache::cast(maybe_table)
->source_position_table());
}
int BytecodeArray::BytecodeArraySize() { return SizeFor(this->length()); } int BytecodeArray::BytecodeArraySize() { return SizeFor(this->length()); }
int BytecodeArray::SizeIncludingMetadata() { int BytecodeArray::SizeIncludingMetadata() {
......
...@@ -791,6 +791,7 @@ class BytecodeArray : public FixedArrayBase { ...@@ -791,6 +791,7 @@ class BytecodeArray : public FixedArrayBase {
DECL_ACCESSORS(source_position_table, Object) DECL_ACCESSORS(source_position_table, Object)
inline ByteArray* SourcePositionTable(); inline ByteArray* SourcePositionTable();
inline void ClearFrameCacheFromSourcePositionTable();
DECL_CAST(BytecodeArray) DECL_CAST(BytecodeArray)
......
...@@ -119,6 +119,9 @@ class Script : public Struct { ...@@ -119,6 +119,9 @@ class Script : public Struct {
// Retrieve source position from where eval was called. // Retrieve source position from where eval was called.
int GetEvalPosition(); int GetEvalPosition();
// Check if the script contains any Asm modules.
bool ContainsAsmModule();
// Init line_ends array with source code positions of line ends. // Init line_ends array with source code positions of line ends.
static void InitLineEnds(Handle<Script> script); static void InitLineEnds(Handle<Script> script);
......
...@@ -106,12 +106,37 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, ...@@ -106,12 +106,37 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
} }
if (obj->IsScript()) { if (obj->IsScript()) {
Script* script_obj = Script::cast(obj);
DCHECK_NE(script_obj->compilation_type(), Script::COMPILATION_TYPE_EVAL);
// Wrapper object is a context-dependent JSValue. Reset it here. // Wrapper object is a context-dependent JSValue. Reset it here.
Script::cast(obj)->set_wrapper(isolate()->heap()->undefined_value()); script_obj->set_wrapper(isolate()->heap()->undefined_value());
// We want to differentiate between undefined and uninitialized_symbol for
// context_data for now. It is hack to allow debugging for scripts that are
// included as a part of custom snapshot. (see debug::Script::IsEmbedded())
Object* context_data = script_obj->context_data();
if (context_data != isolate()->heap()->undefined_value() &&
context_data != isolate()->heap()->uninitialized_symbol()) {
script_obj->set_context_data(isolate()->heap()->undefined_value());
}
// We don't want to serialize host options to avoid serializing unnecessary
// object graph.
FixedArray* host_options = script_obj->host_defined_options();
script_obj->set_host_defined_options(
isolate()->heap()->empty_fixed_array());
SerializeGeneric(obj, how_to_code, where_to_point);
script_obj->set_host_defined_options(host_options);
script_obj->set_context_data(context_data);
return;
} }
if (obj->IsSharedFunctionInfo()) { if (obj->IsSharedFunctionInfo()) {
SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
// TODO(7110): Enable serializing of Asm modules once the AsmWasmData
// is context independent.
DCHECK(!sfi->IsApiFunction() && !sfi->HasAsmWasmData());
// Do not serialize when a debugger is active.
DCHECK(sfi->debug_info()->IsSmi());
// Mark SFI to indicate whether the code is cached. // Mark SFI to indicate whether the code is cached.
bool was_deserialized = sfi->deserialized(); bool was_deserialized = sfi->deserialized();
sfi->set_deserialized(sfi->is_compiled()); sfi->set_deserialized(sfi->is_compiled());
...@@ -120,6 +145,11 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code, ...@@ -120,6 +145,11 @@ void CodeSerializer::SerializeObject(HeapObject* obj, HowToCode how_to_code,
return; return;
} }
if (obj->IsBytecodeArray()) {
// Clear the stack frame cache if present
BytecodeArray::cast(obj)->ClearFrameCacheFromSourcePositionTable();
}
// Past this point we should not see any (context-specific) maps anymore. // Past this point we should not see any (context-specific) maps anymore.
CHECK(!obj->IsMap()); CHECK(!obj->IsMap());
// There should be no references to the global object embedded. // There should be no references to the global object embedded.
......
...@@ -234,6 +234,13 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj, ...@@ -234,6 +234,13 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
void* backing_store = off_heap_backing_stores_[store_index->value()]; void* backing_store = off_heap_backing_stores_[store_index->value()];
fta->set_external_pointer(backing_store); fta->set_external_pointer(backing_store);
} }
} else if (obj->IsBytecodeArray()) {
// TODO(mythria): Remove these once we store the default values for these
// fields in the serializer.
BytecodeArray* bytecode_array = BytecodeArray::cast(obj);
bytecode_array->set_interrupt_budget(
interpreter::Interpreter::kInterruptBudget);
bytecode_array->set_osr_loop_nesting_level(0);
} }
// Check alignment. // Check alignment.
DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment())); DCHECK_EQ(0, Heap::GetFillToAlign(obj->address(), obj->RequiredAlignment()));
......
...@@ -57,6 +57,8 @@ ...@@ -57,6 +57,8 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
enum CodeCacheType { kLazy, kEager, kAfterExecute };
void DisableLazyDeserialization() { void DisableLazyDeserialization() {
// UNINITIALIZED tests do not set up the isolate sufficiently for lazy // UNINITIALIZED tests do not set up the isolate sufficiently for lazy
// deserialization to work. // deserialization to work.
...@@ -1838,8 +1840,8 @@ static void SerializerCodeEventListener(const v8::JitCodeEvent* event) { ...@@ -1838,8 +1840,8 @@ static void SerializerCodeEventListener(const v8::JitCodeEvent* event) {
} }
} }
v8::ScriptCompiler::CachedData* ProduceCache(const char* source, v8::ScriptCompiler::CachedData* ProduceCache(
bool eager = false) { const char* source, CodeCacheType cacheType = CodeCacheType::kLazy) {
v8::ScriptCompiler::CachedData* cache; v8::ScriptCompiler::CachedData* cache;
v8::Isolate::CreateParams create_params; v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
...@@ -1853,19 +1855,23 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source, ...@@ -1853,19 +1855,23 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source,
v8::Local<v8::String> source_str = v8_str(source); v8::Local<v8::String> source_str = v8_str(source);
v8::ScriptOrigin origin(v8_str("test")); v8::ScriptOrigin origin(v8_str("test"));
v8::ScriptCompiler::Source source(source_str, origin); v8::ScriptCompiler::Source source(source_str, origin);
v8::ScriptCompiler::CompileOptions options = v8::ScriptCompiler::CompileOptions options;
eager ? v8::ScriptCompiler::kProduceFullCodeCache switch (cacheType) {
: v8::ScriptCompiler::kProduceCodeCache; case CodeCacheType::kLazy:
options = v8::ScriptCompiler::kProduceCodeCache;
break;
case CodeCacheType::kEager:
options = v8::ScriptCompiler::kProduceFullCodeCache;
break;
case CodeCacheType::kAfterExecute:
options = v8::ScriptCompiler::kNoCompileOptions;
break;
default:
UNREACHABLE();
}
v8::Local<v8::UnboundScript> script = v8::Local<v8::UnboundScript> script =
v8::ScriptCompiler::CompileUnboundScript(isolate1, &source, options) v8::ScriptCompiler::CompileUnboundScript(isolate1, &source, options)
.ToLocalChecked(); .ToLocalChecked();
const v8::ScriptCompiler::CachedData* data = source.GetCachedData();
CHECK(data);
// Persist cached data.
uint8_t* buffer = NewArray<uint8_t>(data->length);
MemCopy(buffer, data->data, data->length);
cache = new v8::ScriptCompiler::CachedData(
buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
v8::Local<v8::Value> result = script->BindToCurrentContext() v8::Local<v8::Value> result = script->BindToCurrentContext()
->Run(isolate1->GetCurrentContext()) ->Run(isolate1->GetCurrentContext())
...@@ -1874,6 +1880,18 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source, ...@@ -1874,6 +1880,18 @@ v8::ScriptCompiler::CachedData* ProduceCache(const char* source,
result->ToString(isolate1->GetCurrentContext()).ToLocalChecked(); result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef")) CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef"))
.FromJust()); .FromJust());
if (cacheType == CodeCacheType::kAfterExecute) {
cache = ScriptCompiler::CreateCodeCache(script, source_str);
} else {
const ScriptCompiler::CachedData* data = source.GetCachedData();
CHECK(data);
uint8_t* buffer = NewArray<uint8_t>(data->length);
MemCopy(buffer, data->data, data->length);
cache = new v8::ScriptCompiler::CachedData(
buffer, data->length, v8::ScriptCompiler::CachedData::BufferOwned);
}
CHECK(cache);
} }
isolate1->Dispose(); isolate1->Dispose();
return cache; return cache;
...@@ -1936,7 +1954,8 @@ TEST(CodeSerializerIsolatesEager) { ...@@ -1936,7 +1954,8 @@ TEST(CodeSerializerIsolatesEager) {
" }" " }"
"}" "}"
"f()() + 'def'"; "f()() + 'def'";
v8::ScriptCompiler::CachedData* cache = ProduceCache(source, true); v8::ScriptCompiler::CachedData* cache =
ProduceCache(source, CodeCacheType::kEager);
v8::Isolate::CreateParams create_params; v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
...@@ -1974,6 +1993,68 @@ TEST(CodeSerializerIsolatesEager) { ...@@ -1974,6 +1993,68 @@ TEST(CodeSerializerIsolatesEager) {
isolate2->Dispose(); isolate2->Dispose();
} }
TEST(CodeSerializerAfterExecute) {
// We test that no compilations happen when running this code. Forcing
// to always optimize breaks this test.
bool prev_opt_value = FLAG_opt;
bool prev_always_opt_value = FLAG_always_opt;
FLAG_always_opt = false;
FLAG_opt = false;
const char* source = "function f() { return 'abc'; }; f() + 'def'";
v8::ScriptCompiler::CachedData* cache =
ProduceCache(source, CodeCacheType::kAfterExecute);
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
{
v8::Isolate::Scope iscope(isolate2);
v8::HandleScope scope(isolate2);
v8::Local<v8::Context> context = v8::Context::New(isolate2);
v8::Context::Scope context_scope(context);
v8::Local<v8::String> source_str = v8_str(source);
v8::ScriptOrigin origin(v8_str("test"));
v8::ScriptCompiler::Source source(source_str, origin, cache);
v8::Local<v8::UnboundScript> script;
{
DisallowCompilation no_compile_expected(
reinterpret_cast<Isolate*>(isolate2));
script = v8::ScriptCompiler::CompileUnboundScript(
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
.ToLocalChecked();
}
CHECK(!cache->rejected);
CheckDeserializedFlag(script);
Handle<SharedFunctionInfo> sfi = v8::Utils::OpenHandle(*script);
CHECK(sfi->HasBytecodeArray());
BytecodeArray* bytecode = sfi->bytecode_array();
CHECK_EQ(bytecode->interrupt_budget(),
interpreter::Interpreter::kInterruptBudget);
CHECK_EQ(bytecode->osr_loop_nesting_level(), 0);
{
DisallowCompilation no_compile_expected(
reinterpret_cast<Isolate*>(isolate2));
v8::Local<v8::Value> result = script->BindToCurrentContext()
->Run(isolate2->GetCurrentContext())
.ToLocalChecked();
v8::Local<v8::String> result_string =
result->ToString(isolate2->GetCurrentContext()).ToLocalChecked();
CHECK(
result_string->Equals(isolate2->GetCurrentContext(), v8_str("abcdef"))
.FromJust());
}
}
isolate2->Dispose();
// Restore the flags.
FLAG_always_opt = prev_always_opt_value;
FLAG_opt = prev_opt_value;
}
TEST(CodeSerializerFlagChange) { TEST(CodeSerializerFlagChange) {
const char* source = "function f() { return 'abc'; }; f() + 'def'"; const char* source = "function f() { return 'abc'; }; f() + 'def'";
v8::ScriptCompiler::CachedData* cache = ProduceCache(source); v8::ScriptCompiler::CachedData* cache = ProduceCache(source);
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --cache=after-execute
function g() {
function h() {
function k() { return 0; };
return k;
}
return h();
}
g();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment