Commit 85b7f1cd authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[Parser] Add basic support for parallel IIFE parse / compile tasks.

Adds support for enqueuing parallel parse / compile tasks for eagerly
compiled IIFEs during parsing. If the --parallel-compile-tasks flag is
enabled, the parser will pre-parse eager top-level IIFEs and enqueue a
task on the compiler dispatcher to do the actual parsing / compilation
on a worker thread.

Currently we always enqueue the task, but we likely want to only
enqueue parallel tasks where the script has multiple IIFEs or a
substantial amount of top-level script code before the IIFE to avoid
the main thread having to immediately block on the parallel task. This
work will be done as a follow-up.

BUG=v8:8041

Change-Id: If68d7c374548cabd4ec32f1fb6752da7d6aaae6b
Reviewed-on: https://chromium-review.googlesource.com/c/1275354Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56593}
parent 5f9d7e80
......@@ -247,9 +247,6 @@ const AstRawString* AstValueFactory::CloneFromOtherFactory(
const AstRawString* result = GetString(
raw_string->hash_field(), raw_string->is_one_byte(),
Vector<const byte>(raw_string->raw_data(), raw_string->byte_length()));
// Check we weren't trying to clone a string that was already in this
// ast-value-factory.
DCHECK_NE(result, raw_string);
return result;
}
......
......@@ -818,6 +818,23 @@ MaybeHandle<SharedFunctionInfo> FinalizeTopLevel(
script->set_compilation_state(Script::COMPILATION_STATE_COMPILED);
}
// Register any pending parallel tasks with the associated SFI.
if (parse_info->parallel_tasks()) {
CompilerDispatcher* dispatcher = parse_info->parallel_tasks()->dispatcher();
for (auto& it : *parse_info->parallel_tasks()) {
FunctionLiteral* literal = it.first;
CompilerDispatcher::JobId job_id = it.second;
MaybeHandle<SharedFunctionInfo> maybe_shared_for_task =
script->FindSharedFunctionInfo(isolate, literal);
Handle<SharedFunctionInfo> shared_for_task;
if (maybe_shared_for_task.ToHandle(&shared_for_task)) {
// TODO(rmcilroy): Abort job if there is no shared function info for
// the literal.
dispatcher->RegisterSharedFunctionInfo(job_id, *shared_for_task);
}
}
}
return shared_info;
}
......
......@@ -934,7 +934,9 @@ DEFINE_BOOL(compilation_cache, true, "enable compilation cache")
DEFINE_BOOL(cache_prototype_transitions, true, "cache prototype transitions")
// compiler-dispatcher.cc
DEFINE_BOOL(parallel_compile_tasks, false, "enable parallel compile tasks")
DEFINE_BOOL(compiler_dispatcher, false, "enable compiler dispatcher")
DEFINE_IMPLICATION(parallel_compile_tasks, compiler_dispatcher)
DEFINE_BOOL(trace_compiler_dispatcher, false,
"trace compiler dispatcher activity")
......
......@@ -8,6 +8,7 @@
#include "src/ast/ast-value-factory.h"
#include "src/ast/ast.h"
#include "src/base/template-utils.h"
#include "src/compiler-dispatcher/compiler-dispatcher.h"
#include "src/heap/heap-inl.h"
#include "src/objects-inl.h"
#include "src/objects/scope-info.h"
......@@ -49,6 +50,9 @@ ParseInfo::ParseInfo(Isolate* isolate, AccountingAllocator* zone_allocator)
set_ast_string_constants(isolate->ast_string_constants());
if (isolate->is_block_code_coverage()) set_block_coverage_enabled();
if (isolate->is_collecting_type_profile()) set_collect_type_profile();
if (isolate->compiler_dispatcher()->IsEnabled()) {
parallel_tasks_.reset(new ParallelTasks(isolate->compiler_dispatcher()));
}
}
ParseInfo::ParseInfo(Isolate* isolate)
......@@ -225,5 +229,15 @@ void ParseInfo::set_script(Handle<Script> script) {
}
}
void ParseInfo::ParallelTasks::Enqueue(ParseInfo* outer_parse_info,
const AstRawString* function_name,
FunctionLiteral* literal) {
base::Optional<CompilerDispatcher::JobId> job_id =
dispatcher_->Enqueue(outer_parse_info, function_name, literal);
if (job_id) {
enqueued_jobs_.emplace_front(std::make_pair(literal, *job_id));
}
}
} // namespace internal
} // namespace v8
......@@ -26,6 +26,7 @@ class AccountingAllocator;
class AstRawString;
class AstStringConstants;
class AstValueFactory;
class CompilerDispatcher;
class DeclarationScope;
class FunctionLiteral;
class RuntimeCallStats;
......@@ -205,6 +206,31 @@ class V8_EXPORT_PRIVATE ParseInfo {
return &pending_error_handler_;
}
class ParallelTasks {
public:
explicit ParallelTasks(CompilerDispatcher* compiler_dispatcher)
: dispatcher_(compiler_dispatcher) {
DCHECK(dispatcher_);
}
void Enqueue(ParseInfo* outer_parse_info, const AstRawString* function_name,
FunctionLiteral* literal);
typedef std::forward_list<std::pair<FunctionLiteral*, uintptr_t>>::iterator
EnqueuedJobsIterator;
EnqueuedJobsIterator begin() { return enqueued_jobs_.begin(); }
EnqueuedJobsIterator end() { return enqueued_jobs_.end(); }
CompilerDispatcher* dispatcher() { return dispatcher_; }
private:
CompilerDispatcher* dispatcher_;
std::forward_list<std::pair<FunctionLiteral*, uintptr_t>> enqueued_jobs_;
};
ParallelTasks* parallel_tasks() { return parallel_tasks_.get(); }
//--------------------------------------------------------------------------
// TODO(titzer): these should not be part of ParseInfo.
//--------------------------------------------------------------------------
......@@ -289,6 +315,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
RuntimeCallStats* runtime_call_stats_;
Logger* logger_;
SourceRangeMap* source_range_map_; // Used when block coverage is enabled.
std::unique_ptr<ParallelTasks> parallel_tasks_;
//----------- Output of parsing and scope analysis ------------------------
FunctionLiteral* literal_;
......
......@@ -419,6 +419,7 @@ Parser::Parser(ParseInfo* info)
info->runtime_call_stats(), info->logger(),
info->script().is_null() ? -1 : info->script()->id(),
info->is_module(), true),
info_(info),
scanner_(info->unicode_cache(), info->character_stream(),
info->is_module()),
preparser_zone_(info->zone()->allocator(), ZONE_NAME),
......@@ -2576,6 +2577,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
const bool is_lazy =
eager_compile_hint == FunctionLiteral::kShouldLazyCompile;
const bool is_top_level = AllowsLazyParsingWithoutUnresolvedVariables();
const bool is_eager_top_level_function = !is_lazy && is_top_level;
const bool is_lazy_top_level_function = is_lazy && is_top_level;
const bool is_lazy_inner_function = is_lazy && !is_top_level;
......@@ -2606,9 +2608,17 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
const bool should_preparse_inner = parse_lazily() && is_lazy_inner_function;
// If parallel compile tasks are enabled, and the function is an eager
// top level function, then we can pre-parse the function and parse / compile
// in a parallel task on a worker thread.
bool should_post_parallel_task =
parse_lazily() && is_eager_top_level_function &&
FLAG_parallel_compile_tasks && info()->parallel_tasks() &&
scanner()->stream()->can_be_cloned_for_parallel_access();
// This may be modified later to reflect preparsing decision taken
bool should_preparse =
(parse_lazily() && is_lazy_top_level_function) || should_preparse_inner;
bool should_preparse = (parse_lazily() && is_lazy_top_level_function) ||
should_preparse_inner || should_post_parallel_task;
ZonePtrList<Statement>* body = nullptr;
int expected_property_count = -1;
......@@ -2641,6 +2651,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
&produced_preparsed_scope_data, is_lazy_top_level_function,
&eager_compile_hint, CHECK_OK);
if (!did_preparse_successfully) {
should_post_parallel_task = false;
body = ParseFunction(
function_name, pos, kind, function_type, scope, &num_parameters,
&function_length, &has_duplicate_parameters, &expected_property_count,
......@@ -2693,6 +2704,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
function_literal->set_function_token_position(function_token_pos);
function_literal->set_suspend_count(suspend_count);
if (should_post_parallel_task) {
// Start a parallel parse / compile task on the compiler dispatcher.
info()->parallel_tasks()->Enqueue(info(), function_name, function_literal);
}
if (should_infer_name) {
fni_.AddFunction(function_literal);
}
......
......@@ -1100,9 +1100,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
// object (used to implement the "export * as" syntax).
const AstRawString* NextInternalNamespaceExportName();
ParseInfo* info() const { return info_; }
// Parser's private field members.
friend class PreParserZoneScope; // Uses reusable_preparser().
ParseInfo* info_;
Scanner scanner_;
Zone preparser_zone_;
PreParser* reusable_preparser_;
......
......@@ -110,6 +110,11 @@ class Utf16CharacterStream {
}
}
// Returns true if the stream could access the V8 heap after construction.
bool can_be_cloned_for_parallel_access() const {
return can_be_cloned() && !can_access_heap();
}
// Returns true if the stream can be cloned with Clone.
// TODO(rmcilroy): Remove this once ChunkedStreams can be cloned.
virtual bool can_be_cloned() const = 0;
......
// Copyright 2018 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: --compiler-dispatcher --parallel-compile-tasks --use-external-strings
(function(a) {
assertEquals(a, "IIFE");
})("IIFE");
(function(a, ...rest) {
assertEquals(a, 1);
assertEquals(rest.length, 2);
assertEquals(rest[0], 2);
assertEquals(rest[1], 3);
})(1,2,3);
var outer_var = 42;
function lazy_outer() {
return 42;
}
var eager_outer = (function() { return 42; });
(function() {
assertEquals(outer_var, 42);
assertEquals(lazy_outer(), 42);
assertEquals(eager_outer(), 42);
})();
var gen = (function*() {
yield 1;
yield 2;
})();
assertEquals(gen.next().value, 1);
assertEquals(gen.next().value, 2);
var result = (function recursive(a=0) {
if (a == 1) {
return 42;
}
return recursive(1);
})();
assertEquals(result, 42);
var a = 42;
var b;
var c = (a, b = (function z(){ return a+1; })());
assertEquals(b, 43);
assertEquals(c, 43);
var c = (a, b = (function z(){ return a+1; })()) => { return b; };
assertEquals(c(314), 315);
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