[wasm] Split the wasm compilation into two phases for parallel compilation.
Graph construction, graph scheduling, instruction selection, and register allocation has been moved to ExecuteCompilation, which will eventually be executed on the background threads. Code generation remains in FinishCompilation because it has to be executed by the main thread. Additionally, WasmCompilationUnits are finished more eagerly in wasm-module.cc to save memory. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/1942773002 Cr-Commit-Position: refs/heads/master@{#35973}
This commit is contained in:
parent
c89e6eb572
commit
18c380c396
@ -2956,85 +2956,105 @@ std::pair<JSGraph*, SourcePositionTable*> BuildGraphForWasmFunction(
|
||||
return std::make_pair(jsgraph, source_position_table);
|
||||
}
|
||||
|
||||
// Helper function to compile a single function.
|
||||
Handle<Code> CompileWasmFunction(wasm::ErrorThrower* thrower, Isolate* isolate,
|
||||
wasm::ModuleEnv* module_env,
|
||||
const wasm::WasmFunction* function) {
|
||||
HistogramTimerScope wasm_compile_function_time_scope(
|
||||
isolate->counters()->wasm_compile_function_time());
|
||||
if (FLAG_trace_wasm_compiler) {
|
||||
OFStream os(stdout);
|
||||
os << "Compiling WASM function "
|
||||
<< wasm::WasmFunctionName(function, module_env) << std::endl;
|
||||
os << std::endl;
|
||||
}
|
||||
class WasmCompilationUnit {
|
||||
public:
|
||||
WasmCompilationUnit(wasm::ErrorThrower* thrower, Isolate* isolate,
|
||||
wasm::ModuleEnv* module_env,
|
||||
const wasm::WasmFunction* function, uint32_t index)
|
||||
: thrower_(thrower),
|
||||
isolate_(isolate),
|
||||
module_env_(module_env),
|
||||
function_(function),
|
||||
compilation_zone_(isolate->allocator()),
|
||||
info_(function->name_length != 0
|
||||
? module_env->module->GetNameOrNull(function->name_offset,
|
||||
function->name_length)
|
||||
: ArrayVector("wasm"),
|
||||
isolate, &compilation_zone_,
|
||||
Code::ComputeFlags(Code::WASM_FUNCTION)),
|
||||
job_(),
|
||||
index_(index),
|
||||
ok_(true) {}
|
||||
|
||||
compiler::ZonePool zone_pool(isolate->allocator());
|
||||
compiler::ZonePool::Scope graph_zone_scope(&zone_pool);
|
||||
double decode_ms = 0;
|
||||
std::pair<JSGraph*, SourcePositionTable*> graph_result =
|
||||
BuildGraphForWasmFunction(graph_zone_scope.zone(), thrower, isolate,
|
||||
module_env, function, &decode_ms);
|
||||
JSGraph* jsgraph = graph_result.first;
|
||||
SourcePositionTable* source_positions = graph_result.second;
|
||||
void ExecuteCompilation() {
|
||||
HistogramTimerScope wasm_compile_function_time_scope(
|
||||
isolate_->counters()->wasm_compile_function_time());
|
||||
if (FLAG_trace_wasm_compiler) {
|
||||
OFStream os(stdout);
|
||||
os << "Compiling WASM function "
|
||||
<< wasm::WasmFunctionName(function_, module_env_) << std::endl;
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
if (jsgraph == nullptr) {
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
double decode_ms = 0;
|
||||
size_t node_count = 0;
|
||||
|
||||
base::ElapsedTimer compile_timer;
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
compile_timer.Start();
|
||||
}
|
||||
// Run the compiler pipeline to generate machine code.
|
||||
CallDescriptor* descriptor = wasm::ModuleEnv::GetWasmCallDescriptor(
|
||||
jsgraph->graph()->zone(), function->sig);
|
||||
if (jsgraph->machine()->Is32()) {
|
||||
descriptor = module_env->GetI32WasmCallDescriptor(jsgraph->graph()->zone(),
|
||||
descriptor);
|
||||
}
|
||||
Code::Flags flags = Code::ComputeFlags(Code::WASM_FUNCTION);
|
||||
// add flags here if a meaningful name is helpful for debugging.
|
||||
bool debugging =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
FLAG_print_opt_code || FLAG_trace_turbo || FLAG_trace_turbo_graph;
|
||||
#endif
|
||||
Vector<const char> func_name = module_env->module->GetNameOrNull(
|
||||
function->name_offset, function->name_length);
|
||||
Vector<char> buffer;
|
||||
if (func_name.is_empty()) {
|
||||
if (debugging) {
|
||||
buffer = Vector<char>::New(128);
|
||||
int chars = SNPrintF(buffer, "WASM_function_#%d", function->func_index);
|
||||
func_name = Vector<const char>::cast(buffer.SubVector(0, chars));
|
||||
} else {
|
||||
func_name = ArrayVector("wasm");
|
||||
Zone zone(isolate_->allocator());
|
||||
std::pair<JSGraph*, SourcePositionTable*> graph_result =
|
||||
BuildGraphForWasmFunction(&zone, thrower_, isolate_, module_env_,
|
||||
function_, &decode_ms);
|
||||
JSGraph* jsgraph = graph_result.first;
|
||||
SourcePositionTable* source_positions = graph_result.second;
|
||||
|
||||
if (jsgraph == nullptr) {
|
||||
ok_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
base::ElapsedTimer pipeline_timer;
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
node_count = jsgraph->graph()->NodeCount();
|
||||
pipeline_timer.Start();
|
||||
}
|
||||
|
||||
// Run the compiler pipeline to generate machine code.
|
||||
CallDescriptor* descriptor = wasm::ModuleEnv::GetWasmCallDescriptor(
|
||||
&compilation_zone_, function_->sig);
|
||||
if (jsgraph->machine()->Is32()) {
|
||||
descriptor =
|
||||
module_env_->GetI32WasmCallDescriptor(&compilation_zone_, descriptor);
|
||||
}
|
||||
job_.Reset(Pipeline::NewWasmCompilationJob(&info_, jsgraph->graph(),
|
||||
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()));
|
||||
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
double pipeline_ms = pipeline_timer.Elapsed().InMillisecondsF();
|
||||
PrintF(
|
||||
"wasm-compilation phase 1 ok: %d bytes, %0.3f ms decode, %zu nodes, "
|
||||
"%0.3f ms pipeline\n",
|
||||
static_cast<int>(function_->code_end_offset -
|
||||
function_->code_start_offset),
|
||||
decode_ms, node_count, pipeline_ms);
|
||||
}
|
||||
}
|
||||
CompilationInfo info(func_name, isolate, jsgraph->graph()->zone(), flags);
|
||||
base::SmartPointer<CompilationJob> job(Pipeline::NewWasmCompilationJob(
|
||||
&info, jsgraph->graph(), descriptor, source_positions));
|
||||
Handle<Code> code = Handle<Code>::null();
|
||||
if (job->OptimizeGraph() == CompilationJob::SUCCEEDED &&
|
||||
job->GenerateCode() == CompilationJob::SUCCEEDED) {
|
||||
code = info.code();
|
||||
}
|
||||
|
||||
buffer.Dispose();
|
||||
|
||||
if (!code.is_null()) {
|
||||
Handle<Code> FinishCompilation() {
|
||||
if (!ok_) {
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
if (job_->GenerateCode() != CompilationJob::SUCCEEDED) {
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
base::ElapsedTimer compile_timer;
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
compile_timer.Start();
|
||||
}
|
||||
Handle<Code> code = info_.code();
|
||||
DCHECK(!code.is_null());
|
||||
DCHECK(code->deoptimization_data() == nullptr ||
|
||||
code->deoptimization_data()->length() == 0);
|
||||
Handle<FixedArray> deopt_data =
|
||||
isolate->factory()->NewFixedArray(2, TENURED);
|
||||
if (!module_env->instance->js_object.is_null()) {
|
||||
deopt_data->set(0, *module_env->instance->js_object);
|
||||
deopt_data->set(1, Smi::FromInt(function->func_index));
|
||||
} else if (func_name.start() != nullptr) {
|
||||
MaybeHandle<String> maybe_name =
|
||||
isolate->factory()->NewStringFromUtf8(func_name);
|
||||
isolate_->factory()->NewFixedArray(2, TENURED);
|
||||
if (!module_env_->instance->js_object.is_null()) {
|
||||
deopt_data->set(0, *module_env_->instance->js_object);
|
||||
deopt_data->set(1, Smi::FromInt(function_->func_index));
|
||||
} else if (info_.GetDebugName().get() != nullptr) {
|
||||
MaybeHandle<String> maybe_name = isolate_->factory()->NewStringFromUtf8(
|
||||
CStrVector(info_.GetDebugName().get()));
|
||||
if (!maybe_name.is_null())
|
||||
deopt_data->set(0, *maybe_name.ToHandleChecked());
|
||||
}
|
||||
@ -3042,71 +3062,62 @@ Handle<Code> CompileWasmFunction(wasm::ErrorThrower* thrower, Isolate* isolate,
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
RecordFunctionCompilation(
|
||||
Logger::FUNCTION_TAG, &info, "WASM_function", function->func_index,
|
||||
module_env->module->GetName(function->name_offset,
|
||||
function->name_length));
|
||||
}
|
||||
Logger::FUNCTION_TAG, &info_, "WASM_function", function_->func_index,
|
||||
module_env_->module->GetName(function_->name_offset,
|
||||
function_->name_length));
|
||||
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
double compile_ms = compile_timer.Elapsed().InMillisecondsF();
|
||||
PrintF(
|
||||
"wasm-compile ok: %d bytes, %0.3f ms decode, %d nodes, %0.3f ms "
|
||||
"compile\n",
|
||||
static_cast<int>(function->code_end_offset -
|
||||
function->code_start_offset),
|
||||
decode_ms, static_cast<int>(jsgraph->graph()->NodeCount()), compile_ms);
|
||||
}
|
||||
// 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()));
|
||||
graph_zone_scope.Destroy();
|
||||
return code;
|
||||
}
|
||||
|
||||
class WasmCompilationUnit {
|
||||
public:
|
||||
WasmCompilationUnit(wasm::ErrorThrower* thrower, Isolate* isolate,
|
||||
wasm::ModuleEnv* module_env,
|
||||
const wasm::WasmFunction* function)
|
||||
: thrower_(thrower),
|
||||
isolate_(isolate),
|
||||
module_env_(module_env),
|
||||
function_(function) {}
|
||||
|
||||
void ExecuteCompilation() {
|
||||
if (function_->external) {
|
||||
return;
|
||||
if (FLAG_trace_wasm_decode_time) {
|
||||
double compile_ms = compile_timer.Elapsed().InMillisecondsF();
|
||||
PrintF("wasm-code-generation ok: %d bytes, %0.3f ms code generation\n",
|
||||
static_cast<int>(function_->code_end_offset -
|
||||
function_->code_start_offset),
|
||||
compile_ms);
|
||||
}
|
||||
// TODO(ahaas): The parallelizable parts of the compilation should move from
|
||||
// FinishCompilation to here.
|
||||
}
|
||||
|
||||
Handle<Code> FinishCompilation() {
|
||||
return CompileWasmFunction(thrower_, isolate_, module_env_, function_);
|
||||
return code;
|
||||
}
|
||||
|
||||
wasm::ErrorThrower* thrower_;
|
||||
Isolate* isolate_;
|
||||
wasm::ModuleEnv* module_env_;
|
||||
const wasm::WasmFunction* function_;
|
||||
Zone compilation_zone_;
|
||||
CompilationInfo info_;
|
||||
base::SmartPointer<CompilationJob> job_;
|
||||
uint32_t index_;
|
||||
bool ok_;
|
||||
};
|
||||
|
||||
WasmCompilationUnit* CreateWasmCompilationUnit(
|
||||
wasm::ErrorThrower* thrower, Isolate* isolate, wasm::ModuleEnv* module_env,
|
||||
const wasm::WasmFunction* function) {
|
||||
return new WasmCompilationUnit(thrower, isolate, module_env, function);
|
||||
const wasm::WasmFunction* function, uint32_t index) {
|
||||
return new WasmCompilationUnit(thrower, isolate, module_env, function, index);
|
||||
}
|
||||
|
||||
void ExecuteCompilation(WasmCompilationUnit* unit) {
|
||||
unit->ExecuteCompilation();
|
||||
}
|
||||
|
||||
uint32_t GetIndexOfWasmCompilationUnit(WasmCompilationUnit* unit) {
|
||||
return unit->index_;
|
||||
}
|
||||
|
||||
Handle<Code> FinishCompilation(WasmCompilationUnit* unit) {
|
||||
Handle<Code> result = unit->FinishCompilation();
|
||||
delete unit;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper function to compile a single function.
|
||||
Handle<Code> CompileWasmFunction(wasm::ErrorThrower* thrower, Isolate* isolate,
|
||||
wasm::ModuleEnv* module_env,
|
||||
const wasm::WasmFunction* function) {
|
||||
WasmCompilationUnit* unit =
|
||||
CreateWasmCompilationUnit(thrower, isolate, module_env, function, 0);
|
||||
ExecuteCompilation(unit);
|
||||
return FinishCompilation(unit);
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -55,14 +55,14 @@ Handle<JSFunction> CompileJSToWasmWrapper(
|
||||
|
||||
WasmCompilationUnit* CreateWasmCompilationUnit(
|
||||
wasm::ErrorThrower* thrower, Isolate* isolate, wasm::ModuleEnv* module_env,
|
||||
const wasm::WasmFunction* function);
|
||||
const wasm::WasmFunction* function, uint32_t index);
|
||||
|
||||
void ExecuteCompilation(WasmCompilationUnit* unit);
|
||||
|
||||
int GetIndexOfWasmCompilationUnit(WasmCompilationUnit* unit);
|
||||
|
||||
Handle<Code> FinishCompilation(WasmCompilationUnit* unit);
|
||||
|
||||
uint32_t GetIndexOfWasmCompilationUnit(WasmCompilationUnit* unit);
|
||||
|
||||
// Abstracts details of building TurboFan graph nodes for WASM to separate
|
||||
// the WASM decoder from the internal details of TurboFan.
|
||||
class WasmTrapHelper;
|
||||
|
@ -509,6 +509,9 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
|
||||
|
||||
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.
|
||||
@ -520,14 +523,26 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
|
||||
i++) {
|
||||
if (!functions[i].external) {
|
||||
compilation_units[i] = compiler::CreateWasmCompilationUnit(
|
||||
&thrower, isolate, &module_env, &functions[i]);
|
||||
&thrower, isolate, &module_env, &functions[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = FLAG_skip_compiling_wasm_funcs; i < functions.size();
|
||||
i++) {
|
||||
if (!functions[i].external) {
|
||||
compiler::ExecuteCompilation(compilation_units[i]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -554,7 +569,7 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
|
||||
func.sig, str, str_null);
|
||||
} else {
|
||||
if (FLAG_wasm_parallel_compilation) {
|
||||
code = compiler::FinishCompilation(compilation_units[i]);
|
||||
code = results[i];
|
||||
} else {
|
||||
// Compile the function.
|
||||
code = compiler::CompileWasmFunction(&thrower, isolate, &module_env,
|
||||
|
Loading…
Reference in New Issue
Block a user