2018-01-17 14:46:27 +00:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
#include "src/wasm/wasm-engine.h"
|
2018-01-31 14:58:12 +00:00
|
|
|
|
2018-07-16 11:52:11 +00:00
|
|
|
#include "src/code-tracer.h"
|
2018-07-10 13:15:29 +00:00
|
|
|
#include "src/compilation-statistics.h"
|
2018-01-17 14:46:27 +00:00
|
|
|
#include "src/objects-inl.h"
|
2018-05-23 13:29:02 +00:00
|
|
|
#include "src/objects/js-promise.h"
|
2018-01-17 14:46:27 +00:00
|
|
|
#include "src/wasm/module-compiler.h"
|
2018-04-27 13:38:40 +00:00
|
|
|
#include "src/wasm/module-decoder.h"
|
|
|
|
#include "src/wasm/streaming-decoder.h"
|
2018-07-31 08:16:22 +00:00
|
|
|
#include "src/wasm/wasm-objects-inl.h"
|
2018-01-17 14:46:27 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace wasm {
|
|
|
|
|
2018-07-10 13:15:29 +00:00
|
|
|
WasmEngine::WasmEngine(std::unique_ptr<WasmCodeManager> code_manager)
|
|
|
|
: code_manager_(std::move(code_manager)) {}
|
|
|
|
|
2018-07-26 13:08:04 +00:00
|
|
|
WasmEngine::~WasmEngine() { TearDown(); }
|
2018-07-10 13:15:29 +00:00
|
|
|
|
2018-01-17 14:46:27 +00:00
|
|
|
bool WasmEngine::SyncValidate(Isolate* isolate, const ModuleWireBytes& bytes) {
|
|
|
|
// TODO(titzer): remove dependency on the isolate.
|
|
|
|
if (bytes.start() == nullptr || bytes.length() == 0) return false;
|
2018-07-20 12:55:40 +00:00
|
|
|
ModuleResult result =
|
|
|
|
DecodeWasmModule(bytes.start(), bytes.end(), true, kWasmOrigin,
|
|
|
|
isolate->counters(), allocator());
|
2018-01-17 14:46:27 +00:00
|
|
|
return result.ok();
|
|
|
|
}
|
|
|
|
|
2018-01-18 10:52:52 +00:00
|
|
|
MaybeHandle<WasmModuleObject> WasmEngine::SyncCompileTranslatedAsmJs(
|
|
|
|
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
|
|
|
Handle<Script> asm_js_script,
|
|
|
|
Vector<const byte> asm_js_offset_table_bytes) {
|
2018-07-20 12:55:40 +00:00
|
|
|
ModuleResult result =
|
|
|
|
DecodeWasmModule(bytes.start(), bytes.end(), false, kAsmJsOrigin,
|
|
|
|
isolate->counters(), allocator());
|
2018-01-18 10:52:52 +00:00
|
|
|
CHECK(!result.failed());
|
|
|
|
|
2018-04-27 09:01:06 +00:00
|
|
|
// Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
|
2018-01-18 10:52:52 +00:00
|
|
|
// in {CompileToModuleObject}.
|
|
|
|
return CompileToModuleObject(isolate, thrower, std::move(result.val), bytes,
|
|
|
|
asm_js_script, asm_js_offset_table_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
|
|
|
|
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
|
2018-07-20 12:55:40 +00:00
|
|
|
ModuleResult result =
|
|
|
|
DecodeWasmModule(bytes.start(), bytes.end(), false, kWasmOrigin,
|
|
|
|
isolate->counters(), allocator());
|
2018-01-18 10:52:52 +00:00
|
|
|
if (result.failed()) {
|
|
|
|
thrower->CompileFailed("Wasm decoding failed", result);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2018-04-27 09:01:06 +00:00
|
|
|
// Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
|
2018-01-18 10:52:52 +00:00
|
|
|
// in {CompileToModuleObject}.
|
|
|
|
return CompileToModuleObject(isolate, thrower, std::move(result.val), bytes,
|
|
|
|
Handle<Script>(), Vector<const byte>());
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeHandle<WasmInstanceObject> WasmEngine::SyncInstantiate(
|
|
|
|
Isolate* isolate, ErrorThrower* thrower,
|
|
|
|
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
|
|
|
|
MaybeHandle<JSArrayBuffer> memory) {
|
|
|
|
return InstantiateToInstanceObject(isolate, thrower, module_object, imports,
|
|
|
|
memory);
|
|
|
|
}
|
|
|
|
|
2018-05-24 21:22:27 +00:00
|
|
|
void WasmEngine::AsyncInstantiate(
|
|
|
|
Isolate* isolate, std::unique_ptr<InstantiationResultResolver> resolver,
|
|
|
|
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports) {
|
2018-01-18 10:52:52 +00:00
|
|
|
ErrorThrower thrower(isolate, nullptr);
|
2018-06-08 11:51:33 +00:00
|
|
|
// Instantiate a TryCatch so that caught exceptions won't progagate out.
|
|
|
|
// They will still be set as pending exceptions on the isolate.
|
|
|
|
// TODO(clemensh): Avoid TryCatch, use Execution::TryCall internally to invoke
|
|
|
|
// start function and report thrown exception explicitly via out argument.
|
|
|
|
v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
|
|
|
|
catcher.SetVerbose(false);
|
|
|
|
catcher.SetCaptureMessage(false);
|
|
|
|
|
2018-01-18 10:52:52 +00:00
|
|
|
MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
|
|
|
|
isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
|
2018-06-08 11:51:33 +00:00
|
|
|
|
|
|
|
if (!instance_object.is_null()) {
|
|
|
|
resolver->OnInstantiationSucceeded(instance_object.ToHandleChecked());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We either have a pending exception (if the start function threw), or an
|
|
|
|
// exception in the ErrorThrower.
|
|
|
|
DCHECK_EQ(1, isolate->has_pending_exception() + thrower.error());
|
2018-01-18 10:52:52 +00:00
|
|
|
if (thrower.error()) {
|
2018-05-24 21:22:27 +00:00
|
|
|
resolver->OnInstantiationFailed(thrower.Reify());
|
2018-06-08 11:51:33 +00:00
|
|
|
} else {
|
|
|
|
// The start function has thrown an exception. We have to move the
|
|
|
|
// exception to the promise chain.
|
|
|
|
Handle<Object> exception(isolate->pending_exception(), isolate);
|
|
|
|
isolate->clear_pending_exception();
|
|
|
|
DCHECK(*isolate->external_caught_exception_address());
|
|
|
|
*isolate->external_caught_exception_address() = false;
|
|
|
|
resolver->OnInstantiationFailed(exception);
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-24 21:22:27 +00:00
|
|
|
void WasmEngine::AsyncCompile(
|
|
|
|
Isolate* isolate, std::unique_ptr<CompilationResultResolver> resolver,
|
|
|
|
const ModuleWireBytes& bytes, bool is_shared) {
|
2018-01-18 10:52:52 +00:00
|
|
|
if (!FLAG_wasm_async_compilation) {
|
|
|
|
// Asynchronous compilation disabled; fall back on synchronous compilation.
|
|
|
|
ErrorThrower thrower(isolate, "WasmCompile");
|
|
|
|
MaybeHandle<WasmModuleObject> module_object;
|
|
|
|
if (is_shared) {
|
|
|
|
// Make a copy of the wire bytes to avoid concurrent modification.
|
|
|
|
std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
|
|
|
|
memcpy(copy.get(), bytes.start(), bytes.length());
|
|
|
|
i::wasm::ModuleWireBytes bytes_copy(copy.get(),
|
|
|
|
copy.get() + bytes.length());
|
|
|
|
module_object = SyncCompile(isolate, &thrower, bytes_copy);
|
|
|
|
} else {
|
|
|
|
// The wire bytes are not shared, OK to use them directly.
|
|
|
|
module_object = SyncCompile(isolate, &thrower, bytes);
|
|
|
|
}
|
|
|
|
if (thrower.error()) {
|
2018-05-24 21:22:27 +00:00
|
|
|
resolver->OnCompilationFailed(thrower.Reify());
|
2018-01-18 10:52:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
Handle<WasmModuleObject> module = module_object.ToHandleChecked();
|
2018-05-24 21:22:27 +00:00
|
|
|
resolver->OnCompilationSucceeded(module);
|
2018-01-18 10:52:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FLAG_wasm_test_streaming) {
|
|
|
|
std::shared_ptr<StreamingDecoder> streaming_decoder =
|
2018-05-24 21:22:27 +00:00
|
|
|
isolate->wasm_engine()->StartStreamingCompilation(
|
2018-06-11 09:53:20 +00:00
|
|
|
isolate, handle(isolate->context(), isolate), std::move(resolver));
|
2018-01-18 10:52:52 +00:00
|
|
|
streaming_decoder->OnBytesReceived(bytes.module_bytes());
|
|
|
|
streaming_decoder->Finish();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Make a copy of the wire bytes in case the user program changes them
|
|
|
|
// during asynchronous compilation.
|
|
|
|
std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
|
|
|
|
memcpy(copy.get(), bytes.start(), bytes.length());
|
2018-05-09 13:48:47 +00:00
|
|
|
|
2018-06-11 09:53:20 +00:00
|
|
|
AsyncCompileJob* job = CreateAsyncCompileJob(
|
|
|
|
isolate, std::move(copy), bytes.length(),
|
|
|
|
handle(isolate->context(), isolate), std::move(resolver));
|
2018-05-09 13:48:47 +00:00
|
|
|
job->Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
|
2018-05-24 21:22:27 +00:00
|
|
|
Isolate* isolate, Handle<Context> context,
|
|
|
|
std::unique_ptr<CompilationResultResolver> resolver) {
|
|
|
|
AsyncCompileJob* job =
|
|
|
|
CreateAsyncCompileJob(isolate, std::unique_ptr<byte[]>(nullptr), 0,
|
|
|
|
context, std::move(resolver));
|
2018-05-09 13:48:47 +00:00
|
|
|
return job->CreateStreamingDecoder();
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 08:16:22 +00:00
|
|
|
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
|
|
|
|
Handle<WasmModuleObject> module_object) {
|
|
|
|
return module_object->managed_native_module()->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
|
|
|
|
Isolate* isolate, std::shared_ptr<NativeModule> shared_module) {
|
|
|
|
CHECK_EQ(code_manager(), shared_module->code_manager());
|
|
|
|
Vector<const byte> wire_bytes = shared_module->wire_bytes();
|
|
|
|
Handle<Script> script = CreateWasmScript(isolate, wire_bytes);
|
|
|
|
Handle<WasmModuleObject> module_object =
|
|
|
|
WasmModuleObject::New(isolate, shared_module, script);
|
|
|
|
|
|
|
|
// TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}.
|
|
|
|
// This requires unlocking the code space here. This should eventually be
|
|
|
|
// moved into the allocator.
|
|
|
|
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
|
|
|
|
CompileJsToWasmWrappers(isolate, module_object);
|
|
|
|
return module_object;
|
|
|
|
}
|
|
|
|
|
2018-07-10 13:15:29 +00:00
|
|
|
CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
|
2018-07-16 11:52:11 +00:00
|
|
|
base::LockGuard<base::Mutex> guard(&mutex_);
|
2018-07-10 13:15:29 +00:00
|
|
|
if (compilation_stats_ == nullptr) {
|
|
|
|
compilation_stats_.reset(new CompilationStatistics());
|
|
|
|
}
|
|
|
|
return compilation_stats_.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::DumpAndResetTurboStatistics() {
|
2018-07-16 11:52:11 +00:00
|
|
|
base::LockGuard<base::Mutex> guard(&mutex_);
|
2018-07-10 13:15:29 +00:00
|
|
|
if (compilation_stats_ != nullptr) {
|
|
|
|
StdoutStream os;
|
|
|
|
os << AsPrintableStatistics{*compilation_stats_.get(), false} << std::endl;
|
|
|
|
}
|
|
|
|
compilation_stats_.reset();
|
|
|
|
}
|
|
|
|
|
2018-07-16 11:52:11 +00:00
|
|
|
CodeTracer* WasmEngine::GetCodeTracer() {
|
|
|
|
base::LockGuard<base::Mutex> guard(&mutex_);
|
|
|
|
if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
|
|
|
|
return code_tracer_.get();
|
|
|
|
}
|
|
|
|
|
2018-03-19 09:22:23 +00:00
|
|
|
void WasmEngine::Register(CancelableTaskManager* task_manager) {
|
2018-07-26 13:08:04 +00:00
|
|
|
base::LockGuard<base::Mutex> guard(&mutex_);
|
2018-03-19 09:22:23 +00:00
|
|
|
task_managers_.emplace_back(task_manager);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::Unregister(CancelableTaskManager* task_manager) {
|
2018-07-26 13:08:04 +00:00
|
|
|
base::LockGuard<base::Mutex> guard(&mutex_);
|
2018-03-19 09:22:23 +00:00
|
|
|
task_managers_.remove(task_manager);
|
|
|
|
}
|
|
|
|
|
2018-05-09 13:48:47 +00:00
|
|
|
AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
|
|
|
|
Isolate* isolate, std::unique_ptr<byte[]> bytes_copy, size_t length,
|
2018-05-24 21:22:27 +00:00
|
|
|
Handle<Context> context,
|
|
|
|
std::unique_ptr<CompilationResultResolver> resolver) {
|
|
|
|
AsyncCompileJob* job = new AsyncCompileJob(
|
|
|
|
isolate, std::move(bytes_copy), length, context, std::move(resolver));
|
2018-05-09 13:48:47 +00:00
|
|
|
// Pass ownership to the unique_ptr in {jobs_}.
|
|
|
|
jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
|
|
|
|
return job;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
|
|
|
|
AsyncCompileJob* job) {
|
|
|
|
auto item = jobs_.find(job);
|
|
|
|
DCHECK(item != jobs_.end());
|
|
|
|
std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
|
|
|
|
jobs_.erase(item);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-06-28 14:18:53 +00:00
|
|
|
void WasmEngine::AbortCompileJobsOnIsolate(Isolate* isolate) {
|
2018-05-09 13:48:47 +00:00
|
|
|
// Iterate over a copy of {jobs_}, because {job->Abort} modifies {jobs_}.
|
2018-06-28 14:18:53 +00:00
|
|
|
std::vector<AsyncCompileJob*> isolate_jobs;
|
2018-05-09 13:48:47 +00:00
|
|
|
|
2018-06-28 14:18:53 +00:00
|
|
|
for (auto& entry : jobs_) {
|
|
|
|
if (entry.first->isolate() != isolate) continue;
|
|
|
|
isolate_jobs.push_back(entry.first);
|
|
|
|
}
|
2018-05-09 13:48:47 +00:00
|
|
|
|
2018-06-28 14:18:53 +00:00
|
|
|
for (auto* job : isolate_jobs) job->Abort();
|
2018-05-09 13:48:47 +00:00
|
|
|
}
|
|
|
|
|
2018-07-26 13:08:04 +00:00
|
|
|
void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
|
|
|
|
for (auto it = jobs_.begin(); it != jobs_.end();) {
|
|
|
|
if (it->first->isolate() == isolate) {
|
|
|
|
it = jobs_.erase(it);
|
|
|
|
} else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 09:22:23 +00:00
|
|
|
void WasmEngine::TearDown() {
|
2018-07-26 13:08:04 +00:00
|
|
|
// Cancel all registered task managers. Locking not required since no more
|
|
|
|
// Isolates that could register or unregister task managers exist.
|
2018-03-19 09:22:23 +00:00
|
|
|
for (auto task_manager : task_managers_) {
|
|
|
|
task_manager->CancelAndWait();
|
|
|
|
}
|
|
|
|
|
2018-07-26 13:08:04 +00:00
|
|
|
// All AsyncCompileJobs have been canceled.
|
|
|
|
DCHECK(jobs_.empty());
|
2018-03-19 09:22:23 +00:00
|
|
|
}
|
|
|
|
|
2018-07-24 15:58:31 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
WasmEngine* AllocateNewWasmEngine() {
|
|
|
|
return new WasmEngine(std::unique_ptr<WasmCodeManager>(
|
|
|
|
new WasmCodeManager(kMaxWasmCodeMemory)));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct WasmEnginePointerConstructTrait final {
|
|
|
|
static void Construct(void* raw_ptr) {
|
|
|
|
auto engine_ptr = reinterpret_cast<std::shared_ptr<WasmEngine>*>(raw_ptr);
|
|
|
|
*engine_ptr = std::shared_ptr<WasmEngine>();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Holds the global shared pointer to the single {WasmEngine} that is intended
|
|
|
|
// to be shared among Isolates within the same process. The {LazyStaticInstance}
|
|
|
|
// here is required because {std::shared_ptr} has a non-trivial initializer.
|
|
|
|
base::LazyStaticInstance<std::shared_ptr<WasmEngine>,
|
|
|
|
WasmEnginePointerConstructTrait>::type
|
|
|
|
global_wasm_engine;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void WasmEngine::InitializeOncePerProcess() {
|
|
|
|
if (!FLAG_wasm_shared_engine) return;
|
|
|
|
global_wasm_engine.Pointer()->reset(AllocateNewWasmEngine());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::GlobalTearDown() {
|
|
|
|
if (!FLAG_wasm_shared_engine) return;
|
|
|
|
global_wasm_engine.Pointer()->reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
|
|
|
|
if (FLAG_wasm_shared_engine) return global_wasm_engine.Get();
|
|
|
|
return std::shared_ptr<WasmEngine>(AllocateNewWasmEngine());
|
|
|
|
}
|
|
|
|
|
2018-01-17 14:46:27 +00:00
|
|
|
} // namespace wasm
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|