Commit be8c688a authored by ahaas's avatar ahaas Committed by Commit bot

Revert of [wasm] Implement parallel compilation. (patchset #6 id:100001 of...

Revert of [wasm] Implement parallel compilation. (patchset #6 id:100001 of https://codereview.chromium.org/1961973002/ )

Reason for revert:
The ThreadSanitizer finds data races.

Original issue's description:
> [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}

TBR=bmeurer@chromium.org,mlippautz@chromium.org,mstarzinger@chromium.org,titzer@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Review-Url: https://codereview.chromium.org/1965243003
Cr-Commit-Position: refs/heads/master@{#36182}
parent 40f34541
......@@ -232,17 +232,14 @@ 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
builder_->HeapConstant(module->instance->context), // context
ExternalReference(f, jsgraph()->isolate())), // ref
jsgraph()->Int32Constant(fun->nargs), // arity
jsgraph()->Constant(module->instance->context), // context
*effect_ptr,
*control_ptr};
......@@ -893,8 +890,8 @@ Node* WasmGraphBuilder::Float64Constant(double value) {
return jsgraph()->Float64Constant(value);
}
Node* WasmGraphBuilder::HeapConstant(Handle<HeapObject> value) {
return jsgraph()->HeapConstant(value);
Node* WasmGraphBuilder::Constant(Handle<Object> value) {
return jsgraph()->Constant(value);
}
Node* WasmGraphBuilder::Branch(Node* cond, Node** true_node,
......@@ -1896,7 +1893,7 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args,
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = HeapConstant(module_->GetFunctionCode(index));
args[0] = Constant(module_->GetFunctionCode(index));
wasm::FunctionSig* sig = module_->GetFunctionSignature(index);
return BuildWasmCall(sig, args, position);
......@@ -1907,7 +1904,7 @@ Node* WasmGraphBuilder::CallImport(uint32_t index, Node** args,
DCHECK_NULL(args[0]);
// Add code object as constant.
args[0] = HeapConstant(module_->GetImportCode(index));
args[0] = Constant(module_->GetImportCode(index));
wasm::FunctionSig* sig = module_->GetImportSignature(index);
return BuildWasmCall(sig, args, position);
......@@ -2385,7 +2382,7 @@ void WasmGraphBuilder::BuildJSToWasmWrapper(Handle<Code> wasm_code,
graph()->start());
int pos = 0;
args[pos++] = HeapConstant(wasm_code);
args[pos++] = Constant(wasm_code);
// Convert JS parameters to WASM numbers.
for (int i = 0; i < wasm_count; i++) {
......@@ -2443,7 +2440,7 @@ void WasmGraphBuilder::BuildWasmToJSWrapper(Handle<JSFunction> function,
*effect_ = start;
*control_ = start;
// JS context is the last parameter.
Node* context = HeapConstant(Handle<Context>(function->context(), isolate));
Node* context = Constant(Handle<Context>(function->context(), isolate));
Node** args = Buffer(wasm_count + 7);
bool arg_count_before_args = false;
......@@ -2548,7 +2545,7 @@ Node* WasmGraphBuilder::FunctionTable() {
DCHECK(module_ && module_->instance &&
!module_->instance->function_table.is_null());
if (!function_table_) {
function_table_ = HeapConstant(module_->instance->function_table);
function_table_ = jsgraph()->Constant(module_->instance->function_table);
}
return function_table_;
}
......@@ -2888,7 +2885,7 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, wasm::ModuleEnv* module,
}
std::pair<JSGraph*, SourcePositionTable*> BuildGraphForWasmFunction(
JSGraph* jsgraph, wasm::ErrorThrower* thrower, Isolate* isolate,
Zone* zone, wasm::ErrorThrower* thrower, Isolate* isolate,
wasm::ModuleEnv*& module_env, const wasm::WasmFunction* function,
double* decode_ms) {
base::ElapsedTimer decode_timer;
......@@ -2896,13 +2893,16 @@ std::pair<JSGraph*, SourcePositionTable*> BuildGraphForWasmFunction(
decode_timer.Start();
}
// Create a TF graph during decoding.
Graph* graph = jsgraph->graph();
CommonOperatorBuilder* common = jsgraph->common();
MachineOperatorBuilder* machine = jsgraph->machine();
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);
SourcePositionTable* source_position_table =
new (jsgraph->zone()) SourcePositionTable(graph);
WasmGraphBuilder builder(jsgraph->zone(), jsgraph, function->sig,
source_position_table);
new (zone) SourcePositionTable(graph);
WasmGraphBuilder builder(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, jsgraph->zone(), function->sig);
Int64Lowering r(graph, machine, common, zone, function->sig);
r.LowerGraph();
}
......@@ -2948,14 +2948,6 @@ 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,
......@@ -2965,12 +2957,7 @@ class WasmCompilationUnit {
Code::ComputeFlags(Code::WASM_FUNCTION)),
job_(),
index_(index),
ok_(true) {
// Create and cache this node in the main thread.
jsgraph_->CEntryStubConstant(1);
}
Zone* graph_zone() { return graph_zone_.get(); }
ok_(true) {}
void ExecuteCompilation() {
HistogramTimerScope wasm_compile_function_time_scope(
......@@ -2985,9 +2972,9 @@ class WasmCompilationUnit {
double decode_ms = 0;
size_t node_count = 0;
base::SmartPointer<Zone> graph_zone(graph_zone_.Detach());
Zone zone(isolate_->allocator());
std::pair<JSGraph*, SourcePositionTable*> graph_result =
BuildGraphForWasmFunction(jsgraph_, thrower_, isolate_, module_env_,
BuildGraphForWasmFunction(&zone, thrower_, isolate_, module_env_,
function_, &decode_ms);
JSGraph* jsgraph = graph_result.first;
SourcePositionTable* source_positions = graph_result.second;
......@@ -3072,9 +3059,6 @@ 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* HeapConstant(Handle<HeapObject> value);
Node* Constant(Handle<Object> value);
Node* Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* Unop(wasm::WasmOpcode opcode, Node* input,
......
......@@ -461,8 +461,7 @@ DEFINE_BOOL(turbo_stress_instruction_scheduling, false,
// Flags for native WebAssembly.
DEFINE_BOOL(expose_wasm, false, "expose WASM interface to JavaScript")
DEFINE_INT(wasm_num_compilation_tasks, 0,
"number of parallel compilation tasks for wasm")
DEFINE_BOOL(wasm_parallel_compilation, false, "compile WASM code in parallel")
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")
......
......@@ -2,7 +2,6 @@
// 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"
......@@ -130,11 +129,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),
Handle<Object>::null());
desc, Code::KindField::encode(Code::WASM_FUNCTION), self);
code->set_constant_pool_offset(index + kPlaceholderMarker);
placeholder_code_[index] = code;
function_code_[index] = code;
......@@ -396,245 +395,6 @@ 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
......@@ -656,6 +416,10 @@ 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.
......@@ -702,6 +466,10 @@ 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;
......@@ -710,14 +478,25 @@ MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
module_env.linker = &linker;
module_env.origin = origin;
//-------------------------------------------------------------------------
// Compile wrappers to imported functions.
//-------------------------------------------------------------------------
if (!CompileWrappersToImportedFunctions(isolate, this, ffi, &instance,
&thrower, factory, &module_env,
total_code_size)) {
return MaybeHandle<JSObject>();
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 all functions in the module.
//-------------------------------------------------------------------------
......@@ -725,77 +504,103 @@ 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_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.
// Turn on the {CanonicalHandleScope} so that the background threads can
// use the node cache.
CanonicalHandleScope canonical(isolate);
// 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);
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);
}
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);
}
}
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;
}
}
// 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());
}
}
// Patch all direct call sites.
// Second pass: patch all direct call sites.
linker.Link(instance.function_table, this->function_table);
instance.js_object->SetInternalField(kWasmModuleFunctionTable,
Smi::FromInt(0));
......@@ -822,7 +627,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(total_code_size, function->code());
record_code_size(function->code());
desc.set_value(function);
Maybe<bool> status = JSReceiver::DefineOwnProperty(
isolate, exports_object, name, &desc, Object::THROW_ON_ERROR);
......
// 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