Commit 4aec7ba1 authored by ahaas's avatar ahaas Committed by Commit bot

[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

Committed: https://crrev.com/17215438659d8ff2d7d55f95226bf8a1477ccd79
Cr-Commit-Position: refs/heads/master@{#36178}

Review-Url: https://codereview.chromium.org/1961973002
Cr-Commit-Position: refs/heads/master@{#36207}
parent 130ccb95
......@@ -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,11 +2965,17 @@ 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(
isolate_->counters()->wasm_compile_function_time());
// TODO(ahaas): The counters are not thread-safe at the moment.
// HistogramTimerScope wasm_compile_function_time_scope(
// isolate_->counters()->wasm_compile_function_time());
if (FLAG_trace_wasm_compiler) {
OFStream os(stdout);
os << "Compiling WASM function "
......@@ -2972,9 +2986,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;
......@@ -3001,8 +3015,9 @@ class WasmCompilationUnit {
descriptor, source_positions));
ok_ = job_->OptimizeGraph() == CompilationJob::SUCCEEDED;
// TODO(bradnelson): Improve histogram handling of size_t.
isolate_->counters()->wasm_compile_function_peak_memory_bytes()->AddSample(
static_cast<int>(jsgraph->graph()->zone()->allocation_size()));
// TODO(ahaas): The counters are not thread-safe at the moment.
// isolate_->counters()->wasm_compile_function_peak_memory_bytes()->AddSample(
// static_cast<int>(jsgraph->graph()->zone()->allocation_size()));
if (FLAG_trace_wasm_decode_time) {
double pipeline_ms = pipeline_timer.Elapsed().InMillisecondsF();
......@@ -3059,6 +3074,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_;
......
......@@ -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,
......
......@@ -475,7 +475,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")
......
This diff is collapsed.
......@@ -79,11 +79,22 @@ static void InitSigTable() {
#undef SET_SIG_TABLE
}
class SigTable {
public:
SigTable() {
// TODO(ahaas): Move {InitSigTable} into the class.
InitSigTable();
}
FunctionSig* Signature(WasmOpcode opcode) const {
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleExprSigTable[static_cast<byte>(opcode)]]);
}
};
static base::LazyInstance<SigTable>::type sig_table = LAZY_INSTANCE_INITIALIZER;
FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) {
// TODO(titzer): use LazyInstance to make this thread safe.
if (kSimpleExprSigTable[kExprI32Add] == 0) InitSigTable();
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleExprSigTable[static_cast<byte>(opcode)]]);
return sig_table.Get().Signature(opcode);
}
// TODO(titzer): pull WASM_64 up to a common header.
......
// 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));
}
})();
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