[wasm] Implement parallel compilation.

With this CL it is possible to compile a wasm module with multiple
threads in parallel. Parallel compilation works as follows:

1)   The main thread allocates a compilation unit for each wasm function.
2)   The main thread spawns WasmCompilationTasks which run on the
     background threads.
3.a) The background threads and the main thread pick one compilation unit
     at a time and execute the parallel phase of the compilation unit.
     After finishing the execution of the parallel phase, the compilation
     unit is stored in a result queue.
3.b) If the result queue contains a compilation unit, the main thread
     dequeues it and finishes its compilation.
4)   After the execution of the parallel phase of all compilation units has
     started, the main thread waits for all WasmCompilationTasks to finish.
5)   The main thread finalizes the compilation of the module.

I'm going to add some additional tests before committing this CL.

R=titzer@chromium.org, bmeurer@chromium.org, mlippautz@chromium.org, mstarzinger@chromium.org

Review-Url: https://codereview.chromium.org/1961973002
Cr-Commit-Position: refs/heads/master@{#36178}
This commit is contained in:
ahaas 2016-05-11 07:06:54 -07:00 committed by Commit bot
parent 32049620d2
commit 1721543865
5 changed files with 454 additions and 142 deletions

View File

@ -232,14 +232,17 @@ class WasmTrapHelper : public ZoneObject {
CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
jsgraph()->zone(), f, fun->nargs, Operator::kNoProperties,
CallDescriptor::kNoFlags);
// CEntryStubConstant nodes have to be created and cached in the main
// thread. At the moment this is only done for CEntryStubConstant(1).
DCHECK_EQ(1, fun->result_size);
Node* inputs[] = {
jsgraph()->CEntryStubConstant(fun->result_size), // C entry
trap_reason_smi, // message id
trap_position_smi, // byte position
jsgraph()->ExternalConstant(
ExternalReference(f, jsgraph()->isolate())), // ref
jsgraph()->Int32Constant(fun->nargs), // arity
jsgraph()->Constant(module->instance->context), // context
ExternalReference(f, jsgraph()->isolate())), // ref
jsgraph()->Int32Constant(fun->nargs), // arity
builder_->HeapConstant(module->instance->context), // context
*effect_ptr,
*control_ptr};
@ -890,8 +893,8 @@ Node* WasmGraphBuilder::Float64Constant(double value) {
return jsgraph()->Float64Constant(value);
}
Node* WasmGraphBuilder::Constant(Handle<Object> value) {
return jsgraph()->Constant(value);
Node* WasmGraphBuilder::HeapConstant(Handle<HeapObject> value) {
return jsgraph()->HeapConstant(value);
}
Node* WasmGraphBuilder::Branch(Node* cond, Node** true_node,
@ -1893,7 +1896,7 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = Constant(module_->GetFunctionCode(index));
args[0] = HeapConstant(module_->GetFunctionCode(index));
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
return BuildWasmCall(sig, args, position);
@ -1904,7 +1907,7 @@ Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args,
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = Constant(module_->GetImportCode(index));
args[0] = HeapConstant(module_->GetImportCode(index));
wasm::FunctionSig* sig = module_->GetImportSignature(index);
return BuildWasmCall(sig, args, position);
@ -2382,7 +2385,7 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
graph()->start());
int pos = 0;
args[pos++] = Constant(wasm_code);
args[pos++] = HeapConstant(wasm_code);
// Convert JS parameters to WASM numbers.
for (int i = 0; i < wasm_count; i++) {
@ -2440,7 +2443,7 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
*effect_ = start;
*control_ = start;
// JS context is the last parameter.
Node* context = Constant(Handle<Context>(function->context(), isolate));
Node* context = HeapConstant(Handle<Context>(function->context(), isolate));
Node** args = Buffer(wasm_count + 7);
bool arg_count_before_args = false;
@ -2545,7 +2548,7 @@ Node* WasmGraphBuilder::FunctionTable() {
DCHECK(module_ && module_->instance &&
!module_->instance->function_table.is_null());
if (!function_table_) {
function_table_ = jsgraph()->Constant(module_->instance->function_table);
function_table_ = HeapConstant(module_->instance->function_table);
}
return function_table_;
}
@ -2885,7 +2888,7 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
}
std::pair<JSGraph*, SourcePositionTable*> BuildGraphForWasmFunction(
Zone* zone, wasm::ErrorThrower* thrower, Isolate* isolate,
JSGraph* jsgraph, wasm::ErrorThrower* thrower, Isolate* isolate,
wasm::ModuleEnv*& module_env, const wasm::WasmFunction* function,
double* decode_ms) {
base::ElapsedTimer decode_timer;
@ -2893,16 +2896,13 @@ std::pair<JSGraph*, SourcePositionTable*> BuildGraphForWasmFunction(
decode_timer.Start();
}
// Create a TF graph during decoding.
Graph* graph = new (zone) Graph(zone);
CommonOperatorBuilder* common = new (zone) CommonOperatorBuilder(zone);
MachineOperatorBuilder* machine = new (zone) MachineOperatorBuilder(
zone, MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags());
JSGraph* jsgraph =
new (zone) JSGraph(isolate, graph, common, nullptr, nullptr, machine);
Graph* graph = jsgraph->graph();
CommonOperatorBuilder* common = jsgraph->common();
MachineOperatorBuilder* machine = jsgraph->machine();
SourcePositionTable* source_position_table =
new (zone) SourcePositionTable(graph);
WasmGraphBuilder builder(zone, jsgraph, function->sig, source_position_table);
new (jsgraph->zone()) SourcePositionTable(graph);
WasmGraphBuilder builder(jsgraph->zone(), jsgraph, function->sig,
source_position_table);
wasm::FunctionBody body = {
module_env, function->sig, module_env->module->module_start,
module_env->module->module_start + function->code_start_offset,
@ -2911,7 +2911,7 @@ std::pair<JSGraph*, SourcePositionTable*> BuildGraphForWasmFunction(
wasm::BuildTFGraph(isolate->allocator(), &builder, body);
if (machine->Is32()) {
Int64Lowering r(graph, machine, common, zone, function->sig);
Int64Lowering r(graph, machine, common, jsgraph->zone(), function->sig);
r.LowerGraph();
}
@ -2948,6 +2948,14 @@ class WasmCompilationUnit {
isolate_(isolate),
module_env_(module_env),
function_(function),
graph_zone_(new Zone(isolate->allocator())),
jsgraph_(new (graph_zone()) JSGraph(
isolate, new (graph_zone()) Graph(graph_zone()),
new (graph_zone()) CommonOperatorBuilder(graph_zone()), nullptr,
nullptr,
new (graph_zone()) MachineOperatorBuilder(
graph_zone(), MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags()))),
compilation_zone_(isolate->allocator()),
info_(function->name_length != 0
? module_env->module->GetNameOrNull(function->name_offset,
@ -2957,7 +2965,12 @@ class WasmCompilationUnit {
Code::ComputeFlags(Code::WASM_FUNCTION)),
job_(),
index_(index),
ok_(true) {}
ok_(true) {
// Create and cache this node in the main thread.
jsgraph_->CEntryStubConstant(1);
}
Zone* graph_zone() { return graph_zone_.get(); }
void ExecuteCompilation() {
HistogramTimerScope wasm_compile_function_time_scope(
@ -2972,9 +2985,9 @@ class WasmCompilationUnit {
double decode_ms = 0;
size_t node_count = 0;
Zone zone(isolate_->allocator());
base::SmartPointer<Zone> graph_zone(graph_zone_.Detach());
std::pair<JSGraph*, SourcePositionTable*> graph_result =
BuildGraphForWasmFunction(&zone, thrower_, isolate_, module_env_,
BuildGraphForWasmFunction(jsgraph_, thrower_, isolate_, module_env_,
function_, &decode_ms);
JSGraph* jsgraph = graph_result.first;
SourcePositionTable* source_positions = graph_result.second;
@ -3059,6 +3072,9 @@ class WasmCompilationUnit {
Isolate* isolate_;
wasm::ModuleEnv* module_env_;
const wasm::WasmFunction* function_;
// The graph zone is deallocated at the end of ExecuteCompilation.
base::SmartPointer<Zone> graph_zone_;
JSGraph* jsgraph_;
Zone compilation_zone_;
CompilationInfo info_;
base::SmartPointer<CompilationJob> job_;

View File

@ -98,7 +98,7 @@ class WasmGraphBuilder {
Node* Int64Constant(int64_t value);
Node* Float32Constant(float value);
Node* Float64Constant(double value);
Node* Constant(Handle<Object> value);
Node* HeapConstant(Handle<HeapObject> value);
Node* Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* Unop(wasm::WasmOpcode opcode, Node* input,

View File

@ -461,7 +461,8 @@ DEFINE_BOOL(turbo_stress_instruction_scheduling, false,
// Flags for native WebAssembly.
DEFINE_BOOL(expose_wasm, false, "expose WASM interface to JavaScript")
DEFINE_BOOL(wasm_parallel_compilation, false, "compile WASM code in parallel")
DEFINE_INT(wasm_num_compilation_tasks, 0,
"number of parallel compilation tasks for wasm")
DEFINE_BOOL(trace_wasm_encoder, false, "trace encoding of wasm code")
DEFINE_BOOL(trace_wasm_decoder, false, "trace decoding of wasm code")
DEFINE_BOOL(trace_wasm_decode_time, false, "trace decoding time of wasm code")

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/atomic-utils.h"
#include "src/macro-assembler.h"
#include "src/objects.h"
#include "src/property-descriptor.h"
@ -129,11 +130,11 @@ class WasmLinker {
// Create a placeholder code object and encode the corresponding index in
// the {constant_pool_offset} field of the code object.
// TODO(titzer): placeholder code objects are somewhat dangerous.
Handle<Code> self(nullptr, isolate_);
byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions.
CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr};
Handle<Code> code = isolate_->factory()->NewCode(
desc, Code::KindField::encode(Code::WASM_FUNCTION), self);
desc, Code::KindField::encode(Code::WASM_FUNCTION),
Handle<Object>::null());
code->set_constant_pool_offset(index + kPlaceholderMarker);
placeholder_code_[index] = code;
function_code_[index] = code;
@ -395,6 +396,245 @@ static MaybeHandle<JSFunction> LookupFunction(
return Handle<JSFunction>::cast(function);
}
namespace {
// Fetches the compilation unit of a wasm function and executes its parallel
// phase.
bool FetchAndExecuteCompilationUnit(
Isolate* isolate,
std::vector<compiler::WasmCompilationUnit*>* compilation_units,
std::queue<compiler::WasmCompilationUnit*>* executed_units,
base::Mutex* result_mutex, base::AtomicNumber<size_t>* next_unit) {
DisallowHeapAllocation no_allocation;
DisallowHandleAllocation no_handles;
DisallowHandleDereference no_deref;
DisallowCodeDependencyChange no_dependency_change;
// - 1 because AtomicIntrement returns the value after the atomic increment.
size_t index = next_unit->Increment(1) - 1;
if (index >= compilation_units->size()) {
return false;
}
compiler::WasmCompilationUnit* unit = compilation_units->at(index);
if (unit != nullptr) {
compiler::ExecuteCompilation(unit);
{
base::LockGuard<base::Mutex> guard(result_mutex);
executed_units->push(unit);
}
}
return true;
}
class WasmCompilationTask : public CancelableTask {
public:
WasmCompilationTask(
Isolate* isolate,
std::vector<compiler::WasmCompilationUnit*>* compilation_units,
std::queue<compiler::WasmCompilationUnit*>* executed_units,
base::Semaphore* on_finished, base::Mutex* result_mutex,
base::AtomicNumber<size_t>* next_unit)
: CancelableTask(isolate),
isolate_(isolate),
compilation_units_(compilation_units),
executed_units_(executed_units),
on_finished_(on_finished),
result_mutex_(result_mutex),
next_unit_(next_unit) {}
void RunInternal() override {
while (FetchAndExecuteCompilationUnit(isolate_, compilation_units_,
executed_units_, result_mutex_,
next_unit_)) {
}
on_finished_->Signal();
}
Isolate* isolate_;
std::vector<compiler::WasmCompilationUnit*>* compilation_units_;
std::queue<compiler::WasmCompilationUnit*>* executed_units_;
base::Semaphore* on_finished_;
base::Mutex* result_mutex_;
base::AtomicNumber<size_t>* next_unit_;
};
void record_code_size(uint32_t& total_code_size, Code* code) {
if (FLAG_print_wasm_code_size) {
total_code_size += code->body_size() + code->relocation_info()->length();
}
}
bool CompileWrappersToImportedFunctions(Isolate* isolate, WasmModule* module,
const Handle<JSReceiver> ffi,
WasmModuleInstance* instance,
ErrorThrower* thrower, Factory* factory,
ModuleEnv* module_env,
uint32_t& total_code_size) {
uint32_t index = 0;
if (module->import_table.size() > 0) {
instance->import_code.reserve(module->import_table.size());
for (const WasmImport& import : module->import_table) {
WasmName module_name = module->GetNameOrNull(import.module_name_offset,
import.module_name_length);
WasmName function_name = module->GetNameOrNull(
import.function_name_offset, import.function_name_length);
MaybeHandle<JSFunction> function = LookupFunction(
*thrower, factory, ffi, index, module_name, function_name);
if (function.is_null()) return false;
Handle<Code> code = compiler::CompileWasmToJSWrapper(
isolate, module_env, function.ToHandleChecked(), import.sig,
module_name, function_name);
instance->import_code.push_back(code);
record_code_size(total_code_size, *code);
index++;
}
}
return true;
}
void InitializeParallelCompilation(
Isolate* isolate, std::vector<WasmFunction>& functions,
std::vector<compiler::WasmCompilationUnit*>& compilation_units,
ModuleEnv& module_env, ErrorThrower& thrower) {
// Create a placeholder code object for all functions.
// TODO(ahaas): Maybe we could skip this for external functions.
for (uint32_t i = 0; i < functions.size(); i++) {
module_env.linker->GetFunctionCode(i);
}
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size(); i++) {
if (!functions[i].external) {
compilation_units[i] = compiler::CreateWasmCompilationUnit(
&thrower, isolate, &module_env, &functions[i], i);
} else {
compilation_units[i] = nullptr;
}
}
}
uint32_t* StartCompilationTasks(
Isolate* isolate,
std::vector<compiler::WasmCompilationUnit*>& compilation_units,
std::queue<compiler::WasmCompilationUnit*>& executed_units,
const base::SmartPointer<base::Semaphore>& pending_tasks,
base::Mutex& result_mutex, base::AtomicNumber<size_t>& next_unit) {
const size_t num_tasks =
Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads());
uint32_t* task_ids = new uint32_t[num_tasks];
for (size_t i = 0; i < num_tasks; i++) {
WasmCompilationTask* task =
new WasmCompilationTask(isolate, &compilation_units, &executed_units,
pending_tasks.get(), &result_mutex, &next_unit);
task_ids[i] = task->id();
V8::GetCurrentPlatform()->CallOnBackgroundThread(
task, v8::Platform::kShortRunningTask);
}
return task_ids;
}
void WaitForCompilationTasks(
Isolate* isolate, uint32_t* task_ids,
const base::SmartPointer<base::Semaphore>& pending_tasks) {
const size_t num_tasks =
Min(static_cast<size_t>(FLAG_wasm_num_compilation_tasks),
V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads());
for (size_t i = 0; i < num_tasks; i++) {
// If the task has not started yet, then we abort it. Otherwise we wait for
// it to finish.
if (!isolate->cancelable_task_manager()->TryAbort(task_ids[i])) {
pending_tasks->Wait();
}
}
}
void FinishCompilationUnits(
WasmModule* module,
std::queue<compiler::WasmCompilationUnit*>& executed_units,
std::vector<Handle<Code>>& results, base::Mutex& result_mutex) {
while (!executed_units.empty()) {
compiler::WasmCompilationUnit* unit = nullptr;
{
base::LockGuard<base::Mutex> guard(&result_mutex);
unit = executed_units.front();
executed_units.pop();
}
int j = compiler::GetIndexOfWasmCompilationUnit(unit);
if (!module->functions[j].external) {
results[j] = compiler::FinishCompilation(unit);
}
}
}
bool FinishCompilation(Isolate* isolate, WasmModule* module,
const Handle<JSReceiver> ffi,
const std::vector<Handle<Code>>& results,
const WasmModuleInstance& instance,
const Handle<FixedArray>& code_table,
ErrorThrower& thrower, Factory* factory,
ModuleEnv& module_env, uint32_t& total_code_size,
PropertyDescriptor& desc) {
for (uint32_t i = FLAG_skip_compiling_wasm_funcs;
i < module->functions.size(); i++) {
const WasmFunction& func = module->functions[i];
if (thrower.error()) break;
DCHECK_EQ(i, func.func_index);
WasmName str = module->GetName(func.name_offset, func.name_length);
WasmName str_null = {nullptr, 0};
Handle<String> name = factory->InternalizeUtf8String(str);
Handle<Code> code = Handle<Code>::null();
Handle<JSFunction> function = Handle<JSFunction>::null();
if (func.external) {
// Lookup external function in FFI object.
MaybeHandle<JSFunction> function =
LookupFunction(thrower, factory, ffi, i, str, str_null);
if (function.is_null()) {
return false;
}
code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
function.ToHandleChecked(),
func.sig, str, str_null);
} else {
if (FLAG_wasm_num_compilation_tasks != 0) {
code = results[i];
} else {
// Compile the function.
code = compiler::CompileWasmFunction(&thrower, isolate, &module_env,
&func);
}
if (code.is_null()) {
thrower.Error("Compilation of #%d:%.*s failed.", i, str.length(),
str.start());
return false;
}
if (func.exported) {
function = compiler::CompileJSToWasmWrapper(
isolate, &module_env, name, code, instance.js_object, i);
record_code_size(total_code_size, function->code());
}
}
if (!code.is_null()) {
// Install the code into the linker table.
module_env.linker->Finish(i, code);
code_table->set(i, *code);
record_code_size(total_code_size, *code);
}
if (func.exported) {
// Exported functions are installed as read-only properties on the
// module.
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, instance.js_object, name, &desc, Object::THROW_ON_ERROR);
if (!status.IsJust())
thrower.Error("export of %.*s failed.", str.length(), str.start());
}
}
return true;
}
} // namespace
// Instantiates a wasm module as a JSObject.
// * allocates a backing store of {mem_size} bytes.
// * installs a named property "memory" for that buffer if exported
@ -416,10 +656,6 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
// objects created for this module.
// TODO(titzer): switch this to TRACE_EVENT
uint32_t total_code_size = 0;
auto record_code_size = [&total_code_size](Code* code) {
if (FLAG_print_wasm_code_size)
total_code_size += code->body_size() + code->relocation_info()->length();
};
//-------------------------------------------------------------------------
// Allocate the instance and its JS counterpart.
@ -466,10 +702,6 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
HistogramTimerScope wasm_compile_module_time_scope(
isolate->counters()->wasm_compile_module_time());
//-------------------------------------------------------------------------
// Compile wrappers to imported functions.
//-------------------------------------------------------------------------
uint32_t index = 0;
instance.function_table = BuildFunctionTable(isolate, this);
WasmLinker linker(isolate, functions.size());
ModuleEnv module_env;
@ -478,25 +710,14 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
module_env.linker = &linker;
module_env.origin = origin;
if (import_table.size() > 0) {
instance.import_code.reserve(import_table.size());
for (const WasmImport& import : import_table) {
WasmName module_name =
GetNameOrNull(import.module_name_offset, import.module_name_length);
WasmName function_name = GetNameOrNull(import.function_name_offset,
import.function_name_length);
MaybeHandle<JSFunction> function = LookupFunction(
thrower, factory, ffi, index, module_name, function_name);
if (function.is_null()) return MaybeHandle<JSObject>();
Handle<Code> code = compiler::CompileWasmToJSWrapper(
isolate, &module_env, function.ToHandleChecked(), import.sig,
module_name, function_name);
instance.import_code.push_back(code);
record_code_size(*code);
index++;
}
//-------------------------------------------------------------------------
// Compile wrappers to imported functions.
//-------------------------------------------------------------------------
if (!CompileWrappersToImportedFunctions(isolate, this, ffi, &instance,
&thrower, factory, &module_env,
total_code_size)) {
return MaybeHandle<JSObject>();
}
//-------------------------------------------------------------------------
// Compile all functions in the module.
//-------------------------------------------------------------------------
@ -504,103 +725,77 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
isolate->counters()->wasm_functions_per_module()->AddSample(
static_cast<int>(functions.size()));
// Data structures for the parallel compilation.
std::vector<compiler::WasmCompilationUnit*> compilation_units(
functions.size());
std::queue<compiler::WasmCompilationUnit*> executed_units;
std::vector<Handle<Code>> results(functions.size());
if (FLAG_wasm_parallel_compilation) {
// Create a placeholder code object for all functions.
// TODO(ahaas): Maybe we could skip this for external functions.
for (uint32_t i = 0; i < functions.size(); i++) {
linker.GetFunctionCode(i);
}
if (FLAG_wasm_num_compilation_tasks != 0) {
//-----------------------------------------------------------------------
// For parallel compilation:
// 1) The main thread allocates a compilation unit for each wasm function
// and stores them in the vector {compilation_units}.
// 2) The main thread spawns {WasmCompilationTask} instances which run on
// the background threads.
// 3.a) The background threads and the main thread pick one compilation
// unit at a time and execute the parallel phase of the compilation
// unit. After finishing the execution of the parallel phase, the
// result is enqueued in {executed_units}.
// 3.b) If {executed_units} contains a compilation unit, the main thread
// dequeues it and finishes the compilation.
// 4) After the parallel phase of all compilation units has started, the
// main thread waits for all {WasmCompilationTask} instances to finish.
// 5) The main thread finishes the compilation.
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size();
i++) {
if (!functions[i].external) {
compilation_units[i] = compiler::CreateWasmCompilationUnit(
&thrower, isolate, &module_env, &functions[i], i);
}
}
// Turn on the {CanonicalHandleScope} so that the background threads can
// use the node cache.
CanonicalHandleScope canonical(isolate);
index = FLAG_skip_compiling_wasm_funcs;
while (true) {
while (!executed_units.empty()) {
compiler::WasmCompilationUnit* unit = executed_units.front();
executed_units.pop();
int i = compiler::GetIndexOfWasmCompilationUnit(unit);
results[i] = compiler::FinishCompilation(unit);
}
if (index < functions.size()) {
if (!functions[index].external) {
compiler::ExecuteCompilation(compilation_units[index]);
executed_units.push(compilation_units[index]);
index++;
}
} else {
break;
}
// 1) The main thread allocates a compilation unit for each wasm function
// and stores them in the vector {compilation_units}.
InitializeParallelCompilation(isolate, functions, compilation_units,
module_env, thrower);
// Objects for the synchronization with the background threads.
base::SmartPointer<base::Semaphore> pending_tasks(new base::Semaphore(0));
base::Mutex result_mutex;
base::AtomicNumber<size_t> next_unit(
static_cast<size_t>(FLAG_skip_compiling_wasm_funcs));
// 2) The main thread spawns {WasmCompilationTask} instances which run on
// the background threads.
base::SmartArrayPointer<uint32_t> task_ids(
StartCompilationTasks(isolate, compilation_units, executed_units,
pending_tasks, result_mutex, next_unit));
// 3.a) The background threads and the main thread pick one compilation
// unit at a time and execute the parallel phase of the compilation
// unit. After finishing the execution of the parallel phase, the
// result is enqueued in {executed_units}.
while (FetchAndExecuteCompilationUnit(isolate, &compilation_units,
&executed_units, &result_mutex,
&next_unit)) {
// 3.b) If {executed_units} contains a compilation unit, the main thread
// dequeues it and finishes the compilation unit. Compilation units
// are finished concurrently to the background threads to save
// memory.
FinishCompilationUnits(this, executed_units, results, result_mutex);
}
// 4) After the parallel phase of all compilation units has started, the
// main thread waits for all {WasmCompilationTask} instances to finish.
WaitForCompilationTasks(isolate, task_ids.get(), pending_tasks);
// Finish the compilation of the remaining compilation units.
FinishCompilationUnits(this, executed_units, results, result_mutex);
}
// 5) The main thread finishes the compilation.
if (!FinishCompilation(isolate, this, ffi, results, instance, code_table,
thrower, factory, module_env, total_code_size,
desc)) {
return MaybeHandle<JSObject>();
}
// First pass: compile each function and initialize the code table.
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size();
i++) {
const WasmFunction& func = functions[i];
if (thrower.error()) break;
DCHECK_EQ(i, func.func_index);
WasmName str = GetName(func.name_offset, func.name_length);
WasmName str_null = {nullptr, 0};
Handle<String> name = factory->InternalizeUtf8String(str);
Handle<Code> code = Handle<Code>::null();
Handle<JSFunction> function = Handle<JSFunction>::null();
if (func.external) {
// Lookup external function in FFI object.
MaybeHandle<JSFunction> function =
LookupFunction(thrower, factory, ffi, i, str, str_null);
if (function.is_null()) return MaybeHandle<JSObject>();
code = compiler::CompileWasmToJSWrapper(isolate, &module_env,
function.ToHandleChecked(),
func.sig, str, str_null);
} else {
if (FLAG_wasm_parallel_compilation) {
code = results[i];
} else {
// Compile the function.
code = compiler::CompileWasmFunction(&thrower, isolate, &module_env,
&func);
}
if (code.is_null()) {
thrower.Error("Compilation of #%d:%.*s failed.", i, str.length(),
str.start());
return MaybeHandle<JSObject>();
}
if (func.exported) {
function = compiler::CompileJSToWasmWrapper(
isolate, &module_env, name, code, instance.js_object, i);
record_code_size(function->code());
}
}
if (!code.is_null()) {
// Install the code into the linker table.
linker.Finish(i, code);
code_table->set(i, *code);
record_code_size(*code);
}
if (func.exported) {
// Exported functions are installed as read-only properties on the
// module.
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, instance.js_object, name, &desc, Object::THROW_ON_ERROR);
if (!status.IsJust())
thrower.Error("export of %.*s failed.", str.length(), str.start());
}
}
// Second pass: patch all direct call sites.
// Patch all direct call sites.
linker.Link(instance.function_table, this->function_table);
instance.js_object->SetInternalField(kWasmModuleFunctionTable,
Smi::FromInt(0));
@ -627,7 +822,7 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
Handle<JSFunction> function = compiler::CompileJSToWasmWrapper(
isolate, &module_env, name, code, instance.js_object,
exp.func_index);
record_code_size(function->code());
record_code_size(total_code_size, function->code());
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);

View File

@ -0,0 +1,100 @@
// Copyright 2016 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: --expose-wasm --wasm-num-compilation-tasks=10
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function assertModule(module, memsize) {
// Check the module exists.
assertFalse(module === undefined);
assertFalse(module === null);
assertFalse(module === 0);
assertEquals("object", typeof module);
// Check the memory is an ArrayBuffer.
var mem = module.exports.memory;
assertFalse(mem === undefined);
assertFalse(mem === null);
assertFalse(mem === 0);
assertEquals("object", typeof mem);
assertTrue(mem instanceof ArrayBuffer);
for (var i = 0; i < 4; i++) {
module.exports.memory = 0; // should be ignored
assertEquals(mem, module.exports.memory);
}
assertEquals(memsize, module.exports.memory.byteLength);
}
function assertFunction(module, func) {
assertEquals("object", typeof module.exports);
var exp = module.exports[func];
assertFalse(exp === undefined);
assertFalse(exp === null);
assertFalse(exp === 0);
assertEquals("function", typeof exp);
return exp;
}
(function CompileFunctionsTest() {
var builder = new WasmModuleBuilder();
builder.addMemory(1, 1, true);
for (i = 0; i < 1000; i++) {
builder.addFunction("sub" + i, kSig_i_i)
.addBody([ // --
kExprGetLocal, 0, // --
kExprI32Const, i % 61, // --
kExprI32Sub]) // --
.exportFunc()
}
var module = builder.instantiate();
assertModule(module, kPageSize);
// Check the properties of the functions.
for (i = 0; i < 1000; i++) {
var sub = assertFunction(module, "sub" + i);
assertEquals(33 - (i % 61), sub(33));
}
})();
(function CallFunctionsTest() {
var builder = new WasmModuleBuilder();
var f = []
f[0] = builder.addFunction("add0", kSig_i_ii)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprI32Add, // --
])
.exportFunc()
builder.addMemory(1, 1, true);
for (i = 1; i < 256; i++) {
f[i] = builder.addFunction("add" + i, kSig_i_ii)
.addBody([ // --
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallFunction, kArity2, f[i >>> 1].index]) // --
.exportFunc()
}
var module = builder.instantiate();
assertModule(module, kPageSize);
// Check the properties of the functions.
for (i = 0; i < 256; i++) {
var add = assertFunction(module, "add" + i);
assertEquals(88, add(33, 55));
assertEquals(88888, add(33333, 55555));
assertEquals(8888888, add(3333333, 5555555));
}
})();