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
|
|
|
|
2019-05-17 13:50:43 +00:00
|
|
|
#include "src/base/platform/time.h"
|
2018-07-10 13:15:29 +00:00
|
|
|
#include "src/compilation-statistics.h"
|
2019-05-21 09:30:15 +00:00
|
|
|
#include "src/diagnostics/code-tracer.h"
|
2019-05-22 07:55:37 +00:00
|
|
|
#include "src/execution/frames.h"
|
2019-05-20 09:15:06 +00:00
|
|
|
#include "src/logging/counters.h"
|
2018-12-25 00:19:47 +00:00
|
|
|
#include "src/objects/heap-number.h"
|
2018-05-23 13:29:02 +00:00
|
|
|
#include "src/objects/js-promise.h"
|
2019-05-23 13:27:57 +00:00
|
|
|
#include "src/utils/ostreams.h"
|
2019-05-23 08:51:46 +00:00
|
|
|
#include "src/objects/objects-inl.h"
|
2018-08-17 12:35:29 +00:00
|
|
|
#include "src/wasm/function-compiler.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"
|
2019-01-14 16:03:40 +00:00
|
|
|
#include "src/wasm/module-instantiate.h"
|
2018-04-27 13:38:40 +00:00
|
|
|
#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 {
|
|
|
|
|
2019-04-29 08:28:00 +00:00
|
|
|
#define TRACE_CODE_GC(...) \
|
|
|
|
do { \
|
|
|
|
if (FLAG_trace_wasm_code_gc) PrintF("[wasm-gc] " __VA_ARGS__); \
|
|
|
|
} while (false)
|
|
|
|
|
2019-01-25 10:44:42 +00:00
|
|
|
namespace {
|
2019-04-16 11:48:09 +00:00
|
|
|
// A task to log a set of {WasmCode} objects in an isolate. It does not own any
|
|
|
|
// data itself, since it is owned by the platform, so lifetime is not really
|
|
|
|
// bound to the wasm engine.
|
2019-01-25 10:44:42 +00:00
|
|
|
class LogCodesTask : public Task {
|
|
|
|
public:
|
2019-04-16 11:48:09 +00:00
|
|
|
LogCodesTask(base::Mutex* mutex, LogCodesTask** task_slot, Isolate* isolate,
|
|
|
|
WasmEngine* engine)
|
|
|
|
: mutex_(mutex),
|
|
|
|
task_slot_(task_slot),
|
|
|
|
isolate_(isolate),
|
|
|
|
engine_(engine) {
|
2019-02-05 13:22:24 +00:00
|
|
|
DCHECK_NOT_NULL(task_slot);
|
|
|
|
DCHECK_NOT_NULL(isolate);
|
|
|
|
}
|
|
|
|
|
|
|
|
~LogCodesTask() {
|
|
|
|
// If the platform deletes this task before executing it, we also deregister
|
|
|
|
// it to avoid use-after-free from still-running background threads.
|
|
|
|
if (!cancelled()) DeregisterTask();
|
2019-04-15 14:39:15 +00:00
|
|
|
}
|
2019-01-25 10:44:42 +00:00
|
|
|
|
|
|
|
void Run() override {
|
2019-02-05 13:22:24 +00:00
|
|
|
if (cancelled()) return;
|
|
|
|
DeregisterTask();
|
2019-04-16 11:48:09 +00:00
|
|
|
engine_->LogOutstandingCodesForIsolate(isolate_);
|
2019-01-25 10:44:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Cancel() {
|
|
|
|
// Cancel will only be called on Isolate shutdown, which happens on the
|
|
|
|
// Isolate's foreground thread. Thus no synchronization needed.
|
|
|
|
isolate_ = nullptr;
|
|
|
|
}
|
|
|
|
|
2019-02-05 13:22:24 +00:00
|
|
|
bool cancelled() const { return isolate_ == nullptr; }
|
|
|
|
|
|
|
|
void DeregisterTask() {
|
|
|
|
// The task will only be deregistered from the foreground thread (executing
|
|
|
|
// this task or calling its destructor), thus we do not need synchronization
|
|
|
|
// on this field access.
|
|
|
|
if (task_slot_ == nullptr) return; // already deregistered.
|
|
|
|
// Remove this task from the {IsolateInfo} in the engine. The next
|
|
|
|
// logging request will allocate and schedule a new task.
|
|
|
|
base::MutexGuard guard(mutex_);
|
|
|
|
DCHECK_EQ(this, *task_slot_);
|
|
|
|
*task_slot_ = nullptr;
|
|
|
|
task_slot_ = nullptr;
|
|
|
|
}
|
|
|
|
|
2019-01-25 10:44:42 +00:00
|
|
|
private:
|
|
|
|
// The mutex of the WasmEngine.
|
|
|
|
base::Mutex* const mutex_;
|
|
|
|
// The slot in the WasmEngine where this LogCodesTask is stored. This is
|
2019-02-05 13:22:24 +00:00
|
|
|
// cleared by this task before execution or on task destruction.
|
|
|
|
LogCodesTask** task_slot_;
|
2019-01-25 10:44:42 +00:00
|
|
|
Isolate* isolate_;
|
2019-04-16 11:48:09 +00:00
|
|
|
WasmEngine* const engine_;
|
2019-01-25 10:44:42 +00:00
|
|
|
};
|
2019-04-09 13:05:37 +00:00
|
|
|
|
|
|
|
class WasmGCForegroundTask : public Task {
|
|
|
|
public:
|
|
|
|
explicit WasmGCForegroundTask(Isolate* isolate) : isolate_(isolate) {
|
|
|
|
DCHECK_NOT_NULL(isolate);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Run() final {
|
|
|
|
if (isolate_ == nullptr) return; // cancelled.
|
|
|
|
WasmEngine* engine = isolate_->wasm_engine();
|
|
|
|
// If the foreground task is executing, there is no wasm code active. Just
|
|
|
|
// report an empty set of live wasm code.
|
2019-05-13 13:35:17 +00:00
|
|
|
#ifdef ENABLE_SLOW_DCHECKS
|
|
|
|
for (StackFrameIterator it(isolate_); !it.done(); it.Advance()) {
|
|
|
|
DCHECK_NE(StackFrame::WASM_COMPILED, it.frame()->type());
|
|
|
|
}
|
|
|
|
#endif
|
2019-04-09 13:05:37 +00:00
|
|
|
engine->ReportLiveCodeForGC(isolate_, Vector<WasmCode*>{});
|
|
|
|
}
|
|
|
|
|
|
|
|
void Cancel() { isolate_ = nullptr; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
Isolate* isolate_;
|
|
|
|
};
|
|
|
|
|
2019-01-25 10:44:42 +00:00
|
|
|
} // namespace
|
|
|
|
|
2019-04-09 13:05:37 +00:00
|
|
|
struct WasmEngine::CurrentGCInfo {
|
2019-05-17 08:33:10 +00:00
|
|
|
explicit CurrentGCInfo(int8_t gc_sequence_index)
|
|
|
|
: gc_sequence_index(gc_sequence_index) {
|
|
|
|
DCHECK_NE(0, gc_sequence_index);
|
|
|
|
}
|
|
|
|
|
2019-04-09 13:05:37 +00:00
|
|
|
// Set of isolates that did not scan their stack yet for used WasmCode, and
|
|
|
|
// their scheduled foreground task.
|
|
|
|
std::unordered_map<Isolate*, WasmGCForegroundTask*> outstanding_isolates;
|
|
|
|
|
|
|
|
// Set of dead code. Filled with all potentially dead code on initialization.
|
|
|
|
// Code that is still in-use is removed by the individual isolates.
|
|
|
|
std::unordered_set<WasmCode*> dead_code;
|
2019-05-07 09:54:59 +00:00
|
|
|
|
2019-05-17 08:33:10 +00:00
|
|
|
// The number of GCs triggered in the native module that triggered this GC.
|
|
|
|
// This is stored in the histogram for each participating isolate during
|
|
|
|
// execution of that isolate's foreground task.
|
|
|
|
const int8_t gc_sequence_index;
|
|
|
|
|
2019-05-07 09:54:59 +00:00
|
|
|
// If during this GC, another GC was requested, we skipped that other GC (we
|
|
|
|
// only run one GC at a time). Remember though to trigger another one once
|
2019-05-17 08:33:10 +00:00
|
|
|
// this one finishes. {next_gc_sequence_index} is 0 if no next GC is needed,
|
|
|
|
// and >0 otherwise. It stores the {num_code_gcs_triggered} of the native
|
|
|
|
// module which triggered the next GC.
|
|
|
|
int8_t next_gc_sequence_index = 0;
|
2019-05-17 13:50:43 +00:00
|
|
|
|
|
|
|
// The start time of this GC; used for tracing and sampled via {Counters}.
|
|
|
|
// Can be null ({TimeTicks::IsNull()}) if timer is not high resolution.
|
|
|
|
base::TimeTicks start_time;
|
2019-04-09 13:05:37 +00:00
|
|
|
};
|
|
|
|
|
2019-01-25 10:35:37 +00:00
|
|
|
struct WasmEngine::IsolateInfo {
|
2019-02-06 11:19:03 +00:00
|
|
|
explicit IsolateInfo(Isolate* isolate)
|
2019-05-17 13:50:43 +00:00
|
|
|
: log_codes(WasmCode::ShouldBeLogged(isolate)),
|
|
|
|
async_counters(isolate->async_counters()) {
|
2019-01-25 10:44:42 +00:00
|
|
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
|
|
|
v8::Platform* platform = V8::GetCurrentPlatform();
|
|
|
|
foreground_task_runner = platform->GetForegroundTaskRunner(v8_isolate);
|
|
|
|
}
|
|
|
|
|
2019-04-16 11:48:09 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
~IsolateInfo() {
|
|
|
|
// Before destructing, the {WasmEngine} must have cleared outstanding code
|
|
|
|
// to log.
|
|
|
|
DCHECK_EQ(0, code_to_log.size());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-01-25 10:35:37 +00:00
|
|
|
// All native modules that are being used by this Isolate (currently only
|
|
|
|
// grows, never shrinks).
|
|
|
|
std::set<NativeModule*> native_modules;
|
2019-01-25 10:44:42 +00:00
|
|
|
|
2019-02-06 11:19:03 +00:00
|
|
|
// Caches whether code needs to be logged on this isolate.
|
|
|
|
bool log_codes;
|
|
|
|
|
2019-01-25 10:44:42 +00:00
|
|
|
// The currently scheduled LogCodesTask.
|
|
|
|
LogCodesTask* log_codes_task = nullptr;
|
|
|
|
|
2019-04-16 11:48:09 +00:00
|
|
|
// The vector of code objects that still need to be logged in this isolate.
|
|
|
|
std::vector<WasmCode*> code_to_log;
|
|
|
|
|
2019-01-25 10:44:42 +00:00
|
|
|
// The foreground task runner of the isolate (can be called from background).
|
|
|
|
std::shared_ptr<v8::TaskRunner> foreground_task_runner;
|
2019-05-17 13:50:43 +00:00
|
|
|
|
|
|
|
const std::shared_ptr<Counters> async_counters;
|
2019-01-25 10:35:37 +00:00
|
|
|
};
|
|
|
|
|
2019-04-09 14:00:09 +00:00
|
|
|
struct WasmEngine::NativeModuleInfo {
|
|
|
|
// Set of isolates using this NativeModule.
|
|
|
|
std::unordered_set<Isolate*> isolates;
|
|
|
|
|
2019-04-29 08:24:11 +00:00
|
|
|
// Set of potentially dead code. This set holds one ref for each code object,
|
|
|
|
// until code is detected to be really dead. At that point, the ref count is
|
|
|
|
// decremented and code is move to the {dead_code} set. If the code is finally
|
|
|
|
// deleted, it is also removed from {dead_code}.
|
2019-04-09 14:00:09 +00:00
|
|
|
std::unordered_set<WasmCode*> potentially_dead_code;
|
2019-04-29 08:24:11 +00:00
|
|
|
|
|
|
|
// Code that is not being executed in any isolate any more, but the ref count
|
|
|
|
// did not drop to zero yet.
|
|
|
|
std::unordered_set<WasmCode*> dead_code;
|
2019-05-17 08:33:10 +00:00
|
|
|
|
|
|
|
// Number of code GCs triggered because code in this native module became
|
|
|
|
// potentially dead.
|
|
|
|
int8_t num_code_gcs_triggered = 0;
|
2019-04-09 14:00:09 +00:00
|
|
|
};
|
|
|
|
|
2018-09-18 13:05:45 +00:00
|
|
|
WasmEngine::WasmEngine()
|
2018-12-28 13:51:59 +00:00
|
|
|
: code_manager_(&memory_tracker_, FLAG_wasm_max_code_space * MB) {}
|
2018-07-10 13:15:29 +00:00
|
|
|
|
2018-08-01 11:18:46 +00:00
|
|
|
WasmEngine::~WasmEngine() {
|
2019-01-23 11:49:36 +00:00
|
|
|
// Synchronize on all background compile tasks.
|
|
|
|
background_compile_task_manager_.CancelAndWait();
|
2018-08-01 11:18:46 +00:00
|
|
|
// All AsyncCompileJobs have been canceled.
|
2019-02-01 14:57:59 +00:00
|
|
|
DCHECK(async_compile_jobs_.empty());
|
2018-09-25 11:24:09 +00:00
|
|
|
// All Isolates have been deregistered.
|
|
|
|
DCHECK(isolates_.empty());
|
2019-01-25 10:35:37 +00:00
|
|
|
// All NativeModules did die.
|
2019-04-09 14:00:09 +00:00
|
|
|
DCHECK(native_modules_.empty());
|
2018-08-01 11:18:46 +00:00
|
|
|
}
|
2018-07-10 13:15:29 +00:00
|
|
|
|
2018-08-08 14:54:44 +00:00
|
|
|
bool WasmEngine::SyncValidate(Isolate* isolate, const WasmFeatures& enabled,
|
|
|
|
const ModuleWireBytes& bytes) {
|
2018-01-17 14:46:27 +00:00
|
|
|
// 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 =
|
2018-08-08 14:54:44 +00:00
|
|
|
DecodeWasmModule(enabled, bytes.start(), bytes.end(), true, kWasmOrigin,
|
2018-07-20 12:55:40 +00:00
|
|
|
isolate->counters(), allocator());
|
2018-01-17 14:46:27 +00:00
|
|
|
return result.ok();
|
|
|
|
}
|
|
|
|
|
2018-11-21 21:19:18 +00:00
|
|
|
MaybeHandle<AsmWasmData> WasmEngine::SyncCompileTranslatedAsmJs(
|
2018-01-18 10:52:52 +00:00
|
|
|
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
2018-11-21 21:19:18 +00:00
|
|
|
Vector<const byte> asm_js_offset_table_bytes,
|
|
|
|
Handle<HeapNumber> uses_bitset) {
|
2018-07-20 12:55:40 +00:00
|
|
|
ModuleResult result =
|
2018-08-08 14:54:44 +00:00
|
|
|
DecodeWasmModule(kAsmjsWasmFeatures, bytes.start(), bytes.end(), false,
|
|
|
|
kAsmJsOrigin, isolate->counters(), allocator());
|
2019-03-27 15:28:41 +00:00
|
|
|
if (result.failed()) {
|
|
|
|
// This happens once in a while when we have missed some limit check
|
|
|
|
// in the asm parser. Output an error message to help diagnose, but crash.
|
|
|
|
std::cout << result.error().message();
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2018-01-18 10:52:52 +00:00
|
|
|
|
2018-04-27 09:01:06 +00:00
|
|
|
// Transfer ownership of the WasmModule to the {Managed<WasmModule>} generated
|
2018-11-21 21:19:18 +00:00
|
|
|
// in {CompileToNativeModule}.
|
|
|
|
Handle<FixedArray> export_wrappers;
|
2019-03-12 16:10:58 +00:00
|
|
|
std::shared_ptr<NativeModule> native_module =
|
2018-11-21 21:19:18 +00:00
|
|
|
CompileToNativeModule(isolate, kAsmjsWasmFeatures, thrower,
|
|
|
|
std::move(result).value(), bytes, &export_wrappers);
|
|
|
|
if (!native_module) return {};
|
|
|
|
|
|
|
|
// Create heap objects for asm.js offset table to be stored in the module
|
|
|
|
// object.
|
|
|
|
Handle<ByteArray> asm_js_offset_table =
|
|
|
|
isolate->factory()->NewByteArray(asm_js_offset_table_bytes.length());
|
2019-04-29 11:06:49 +00:00
|
|
|
asm_js_offset_table->copy_in(0, asm_js_offset_table_bytes.begin(),
|
2018-11-21 21:19:18 +00:00
|
|
|
asm_js_offset_table_bytes.length());
|
|
|
|
|
|
|
|
return AsmWasmData::New(isolate, std::move(native_module), export_wrappers,
|
|
|
|
asm_js_offset_table, uses_bitset);
|
|
|
|
}
|
|
|
|
|
|
|
|
Handle<WasmModuleObject> WasmEngine::FinalizeTranslatedAsmJs(
|
|
|
|
Isolate* isolate, Handle<AsmWasmData> asm_wasm_data,
|
|
|
|
Handle<Script> script) {
|
|
|
|
std::shared_ptr<NativeModule> native_module =
|
2019-05-23 07:47:44 +00:00
|
|
|
asm_wasm_data->managed_native_module().get();
|
2018-11-21 21:19:18 +00:00
|
|
|
Handle<FixedArray> export_wrappers =
|
|
|
|
handle(asm_wasm_data->export_wrappers(), isolate);
|
|
|
|
size_t code_size_estimate =
|
|
|
|
wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
|
|
|
|
native_module->module());
|
|
|
|
|
|
|
|
Handle<WasmModuleObject> module_object =
|
|
|
|
WasmModuleObject::New(isolate, std::move(native_module), script,
|
|
|
|
export_wrappers, code_size_estimate);
|
|
|
|
module_object->set_asm_js_offset_table(asm_wasm_data->asm_js_offset_table());
|
|
|
|
return module_object;
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MaybeHandle<WasmModuleObject> WasmEngine::SyncCompile(
|
2018-08-08 14:54:44 +00:00
|
|
|
Isolate* isolate, const WasmFeatures& enabled, ErrorThrower* thrower,
|
|
|
|
const ModuleWireBytes& bytes) {
|
2018-07-20 12:55:40 +00:00
|
|
|
ModuleResult result =
|
2018-08-08 14:54:44 +00:00
|
|
|
DecodeWasmModule(enabled, bytes.start(), bytes.end(), false, kWasmOrigin,
|
2018-07-20 12:55:40 +00:00
|
|
|
isolate->counters(), allocator());
|
2018-01-18 10:52:52 +00:00
|
|
|
if (result.failed()) {
|
2019-02-01 13:51:23 +00:00
|
|
|
thrower->CompileFailed(result.error());
|
2018-01-18 10:52:52 +00:00
|
|
|
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}.
|
2018-11-21 21:19:18 +00:00
|
|
|
Handle<FixedArray> export_wrappers;
|
2019-03-12 16:10:58 +00:00
|
|
|
std::shared_ptr<NativeModule> native_module =
|
2018-11-21 21:19:18 +00:00
|
|
|
CompileToNativeModule(isolate, enabled, thrower,
|
|
|
|
std::move(result).value(), bytes, &export_wrappers);
|
|
|
|
if (!native_module) return {};
|
|
|
|
|
|
|
|
Handle<Script> script =
|
|
|
|
CreateWasmScript(isolate, bytes, native_module->module()->source_map_url);
|
|
|
|
size_t code_size_estimate =
|
|
|
|
wasm::WasmCodeManager::EstimateNativeModuleCodeSize(
|
|
|
|
native_module->module());
|
|
|
|
|
|
|
|
// Create the module object.
|
|
|
|
// TODO(clemensh): For the same module (same bytes / same hash), we should
|
|
|
|
// only have one WasmModuleObject. Otherwise, we might only set
|
|
|
|
// breakpoints on a (potentially empty) subset of the instances.
|
|
|
|
|
|
|
|
// Create the compiled module object and populate with compiled functions
|
|
|
|
// and information needed at instantiation time. This object needs to be
|
|
|
|
// serializable. Instantiation may occur off a deserialized version of this
|
|
|
|
// object.
|
|
|
|
Handle<WasmModuleObject> module_object =
|
|
|
|
WasmModuleObject::New(isolate, std::move(native_module), script,
|
|
|
|
export_wrappers, code_size_estimate);
|
|
|
|
|
|
|
|
// Finish the Wasm script now and make it public to the debugger.
|
|
|
|
isolate->debug()->OnAfterCompile(script);
|
|
|
|
return module_object;
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2019-02-07 13:06:39 +00:00
|
|
|
ErrorThrower thrower(isolate, "WebAssembly.instantiate()");
|
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;
|
|
|
|
}
|
|
|
|
|
2018-08-07 10:00:21 +00:00
|
|
|
if (isolate->has_pending_exception()) {
|
|
|
|
// The JS code executed during instantiation has thrown an exception.
|
|
|
|
// We have to move the exception to the promise chain.
|
2018-06-08 11:51:33 +00:00
|
|
|
Handle<Object> exception(isolate->pending_exception(), isolate);
|
|
|
|
isolate->clear_pending_exception();
|
|
|
|
*isolate->external_caught_exception_address() = false;
|
|
|
|
resolver->OnInstantiationFailed(exception);
|
2018-08-07 10:00:21 +00:00
|
|
|
thrower.Reset();
|
|
|
|
} else {
|
|
|
|
DCHECK(thrower.error());
|
|
|
|
resolver->OnInstantiationFailed(thrower.Reify());
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-24 21:22:27 +00:00
|
|
|
void WasmEngine::AsyncCompile(
|
2018-08-08 14:54:44 +00:00
|
|
|
Isolate* isolate, const WasmFeatures& enabled,
|
2018-08-13 13:35:54 +00:00
|
|
|
std::shared_ptr<CompilationResultResolver> resolver,
|
2019-05-20 11:14:29 +00:00
|
|
|
const ModuleWireBytes& bytes, bool is_shared,
|
|
|
|
const char* api_method_name_for_errors) {
|
2018-01-18 10:52:52 +00:00
|
|
|
if (!FLAG_wasm_async_compilation) {
|
|
|
|
// Asynchronous compilation disabled; fall back on synchronous compilation.
|
2019-05-20 11:14:29 +00:00
|
|
|
ErrorThrower thrower(isolate, api_method_name_for_errors);
|
2018-01-18 10:52:52 +00:00
|
|
|
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());
|
2018-08-02 09:50:08 +00:00
|
|
|
ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
|
2018-08-08 14:54:44 +00:00
|
|
|
module_object = SyncCompile(isolate, enabled, &thrower, bytes_copy);
|
2018-01-18 10:52:52 +00:00
|
|
|
} else {
|
|
|
|
// The wire bytes are not shared, OK to use them directly.
|
2018-08-08 14:54:44 +00:00
|
|
|
module_object = SyncCompile(isolate, enabled, &thrower, bytes);
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
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 =
|
2019-05-20 11:14:29 +00:00
|
|
|
StartStreamingCompilation(
|
|
|
|
isolate, enabled, handle(isolate->context(), isolate),
|
|
|
|
api_method_name_for_errors, 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
|
|
|
|
2019-05-20 11:14:29 +00:00
|
|
|
AsyncCompileJob* job =
|
|
|
|
CreateAsyncCompileJob(isolate, enabled, std::move(copy), bytes.length(),
|
|
|
|
handle(isolate->context(), isolate),
|
|
|
|
api_method_name_for_errors, std::move(resolver));
|
2018-05-09 13:48:47 +00:00
|
|
|
job->Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
|
2018-08-08 14:54:44 +00:00
|
|
|
Isolate* isolate, const WasmFeatures& enabled, Handle<Context> context,
|
2019-04-30 14:04:56 +00:00
|
|
|
const char* api_method_name,
|
2018-08-13 13:35:54 +00:00
|
|
|
std::shared_ptr<CompilationResultResolver> resolver) {
|
2019-01-08 13:58:01 +00:00
|
|
|
AsyncCompileJob* job =
|
2018-08-08 14:54:44 +00:00
|
|
|
CreateAsyncCompileJob(isolate, enabled, std::unique_ptr<byte[]>(nullptr),
|
2019-04-30 14:04:56 +00:00
|
|
|
0, context, api_method_name, std::move(resolver));
|
2018-05-09 13:48:47 +00:00
|
|
|
return job->CreateStreamingDecoder();
|
2018-01-18 10:52:52 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 15:18:38 +00:00
|
|
|
void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
|
2018-08-21 15:01:31 +00:00
|
|
|
uint32_t function_index, ExecutionTier tier) {
|
2018-08-23 14:44:28 +00:00
|
|
|
// Note we assume that "one-off" compilations can discard detected features.
|
|
|
|
WasmFeatures detected = kNoWasmFeatures;
|
2018-12-12 15:18:38 +00:00
|
|
|
WasmCompilationUnit::CompileWasmFunction(
|
2018-10-30 12:46:04 +00:00
|
|
|
isolate, native_module, &detected,
|
2018-08-21 15:01:31 +00:00
|
|
|
&native_module->module()->functions[function_index], tier);
|
2018-08-17 12:35:29 +00:00
|
|
|
}
|
|
|
|
|
2018-07-31 08:16:22 +00:00
|
|
|
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
|
|
|
|
Handle<WasmModuleObject> module_object) {
|
2018-12-11 10:37:32 +00:00
|
|
|
return module_object->shared_native_module();
|
2018-07-31 08:16:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
|
2019-01-25 10:35:37 +00:00
|
|
|
Isolate* isolate, std::shared_ptr<NativeModule> shared_native_module) {
|
|
|
|
NativeModule* native_module = shared_native_module.get();
|
|
|
|
ModuleWireBytes wire_bytes(native_module->wire_bytes());
|
|
|
|
const WasmModule* module = native_module->module();
|
2018-09-13 19:19:30 +00:00
|
|
|
Handle<Script> script =
|
|
|
|
CreateWasmScript(isolate, wire_bytes, module->source_map_url);
|
2019-01-25 10:35:37 +00:00
|
|
|
size_t code_size = native_module->committed_code_space();
|
2018-11-12 14:00:26 +00:00
|
|
|
Handle<WasmModuleObject> module_object = WasmModuleObject::New(
|
2019-01-25 10:35:37 +00:00
|
|
|
isolate, std::move(shared_native_module), script, code_size);
|
|
|
|
CompileJsToWasmWrappers(isolate, native_module->module(),
|
2018-11-21 21:19:18 +00:00
|
|
|
handle(module_object->export_wrappers(), isolate));
|
2019-01-25 10:35:37 +00:00
|
|
|
{
|
|
|
|
base::MutexGuard lock(&mutex_);
|
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
|
|
|
isolates_[isolate]->native_modules.insert(native_module);
|
2019-04-09 14:00:09 +00:00
|
|
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
|
|
|
native_modules_[native_module]->isolates.insert(isolate);
|
2019-01-25 10:35:37 +00:00
|
|
|
}
|
2018-07-31 08:16:22 +00:00
|
|
|
return module_object;
|
|
|
|
}
|
|
|
|
|
2018-07-10 13:15:29 +00:00
|
|
|
CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard 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-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard 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() {
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2018-07-16 11:52:11 +00:00
|
|
|
if (code_tracer_ == nullptr) code_tracer_.reset(new CodeTracer(-1));
|
|
|
|
return code_tracer_.get();
|
|
|
|
}
|
|
|
|
|
2019-01-08 13:58:01 +00:00
|
|
|
AsyncCompileJob* WasmEngine::CreateAsyncCompileJob(
|
2018-08-08 14:54:44 +00:00
|
|
|
Isolate* isolate, const WasmFeatures& enabled,
|
|
|
|
std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,
|
2019-04-30 14:04:56 +00:00
|
|
|
const char* api_method_name,
|
2018-08-13 13:35:54 +00:00
|
|
|
std::shared_ptr<CompilationResultResolver> resolver) {
|
2019-01-08 13:58:01 +00:00
|
|
|
AsyncCompileJob* job =
|
|
|
|
new AsyncCompileJob(isolate, enabled, std::move(bytes_copy), length,
|
2019-04-30 14:04:56 +00:00
|
|
|
context, api_method_name, std::move(resolver));
|
2019-02-01 14:57:59 +00:00
|
|
|
// Pass ownership to the unique_ptr in {async_compile_jobs_}.
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2019-02-01 14:57:59 +00:00
|
|
|
async_compile_jobs_[job] = std::unique_ptr<AsyncCompileJob>(job);
|
2018-05-09 13:48:47 +00:00
|
|
|
return job;
|
|
|
|
}
|
|
|
|
|
2019-01-08 13:58:01 +00:00
|
|
|
std::unique_ptr<AsyncCompileJob> WasmEngine::RemoveCompileJob(
|
|
|
|
AsyncCompileJob* job) {
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2019-02-01 14:57:59 +00:00
|
|
|
auto item = async_compile_jobs_.find(job);
|
|
|
|
DCHECK(item != async_compile_jobs_.end());
|
2019-01-08 13:58:01 +00:00
|
|
|
std::unique_ptr<AsyncCompileJob> result = std::move(item->second);
|
2019-02-01 14:57:59 +00:00
|
|
|
async_compile_jobs_.erase(item);
|
2019-01-08 13:58:01 +00:00
|
|
|
return result;
|
2018-05-09 13:48:47 +00:00
|
|
|
}
|
|
|
|
|
2018-08-01 12:36:38 +00:00
|
|
|
bool WasmEngine::HasRunningCompileJob(Isolate* isolate) {
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2018-09-25 11:24:09 +00:00
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
2019-02-01 14:57:59 +00:00
|
|
|
for (auto& entry : async_compile_jobs_) {
|
2018-08-01 12:36:38 +00:00
|
|
|
if (entry.first->isolate() == isolate) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-01-08 13:58:01 +00:00
|
|
|
void WasmEngine::DeleteCompileJobsOnIsolate(Isolate* isolate) {
|
2019-01-25 10:35:37 +00:00
|
|
|
// Under the mutex get all jobs to delete. Then delete them without holding
|
|
|
|
// the mutex, such that deletion can reenter the WasmEngine.
|
|
|
|
std::vector<std::unique_ptr<AsyncCompileJob>> jobs_to_delete;
|
|
|
|
{
|
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
2019-02-01 14:57:59 +00:00
|
|
|
for (auto it = async_compile_jobs_.begin();
|
|
|
|
it != async_compile_jobs_.end();) {
|
2019-01-25 10:35:37 +00:00
|
|
|
if (it->first->isolate() != isolate) {
|
|
|
|
++it;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
jobs_to_delete.push_back(std::move(it->second));
|
2019-02-01 14:57:59 +00:00
|
|
|
it = async_compile_jobs_.erase(it);
|
2018-07-26 13:08:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-17 13:50:43 +00:00
|
|
|
namespace {
|
|
|
|
int GetGCTimeMicros(base::TimeTicks start) {
|
|
|
|
DCHECK(!start.IsNull());
|
|
|
|
int64_t duration_us = (base::TimeTicks::Now() - start).InMicroseconds();
|
|
|
|
return static_cast<int>(
|
|
|
|
std::min(std::max(int64_t{0}, duration_us), int64_t{kMaxInt}));
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
2018-09-25 11:24:09 +00:00
|
|
|
void WasmEngine::AddIsolate(Isolate* isolate) {
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2018-09-25 11:24:09 +00:00
|
|
|
DCHECK_EQ(0, isolates_.count(isolate));
|
2019-01-25 10:44:42 +00:00
|
|
|
isolates_.emplace(isolate, base::make_unique<IsolateInfo>(isolate));
|
2019-01-25 10:35:37 +00:00
|
|
|
|
|
|
|
// Install sampling GC callback.
|
|
|
|
// TODO(v8:7424): For now we sample module sizes in a GC callback. This will
|
|
|
|
// bias samples towards apps with high memory pressure. We should switch to
|
|
|
|
// using sampling based on regular intervals independent of the GC.
|
|
|
|
auto callback = [](v8::Isolate* v8_isolate, v8::GCType type,
|
|
|
|
v8::GCCallbackFlags flags, void* data) {
|
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
2019-03-11 12:08:14 +00:00
|
|
|
Counters* counters = isolate->counters();
|
2019-01-25 10:35:37 +00:00
|
|
|
WasmEngine* engine = isolate->wasm_engine();
|
|
|
|
base::MutexGuard lock(&engine->mutex_);
|
|
|
|
DCHECK_EQ(1, engine->isolates_.count(isolate));
|
2019-03-11 12:08:14 +00:00
|
|
|
for (auto* native_module : engine->isolates_[isolate]->native_modules) {
|
|
|
|
native_module->SampleCodeSize(counters, NativeModule::kSampling);
|
2019-01-25 10:35:37 +00:00
|
|
|
}
|
2019-05-17 13:50:43 +00:00
|
|
|
// If there is an ongoing code GC, sample its time here. This will record
|
|
|
|
// samples for very long-running or never ending GCs.
|
|
|
|
if (engine->current_gc_info_ &&
|
|
|
|
!engine->current_gc_info_->start_time.IsNull()) {
|
|
|
|
isolate->counters()->wasm_code_gc_time()->AddSample(
|
|
|
|
GetGCTimeMicros(engine->current_gc_info_->start_time));
|
|
|
|
}
|
2019-01-25 10:35:37 +00:00
|
|
|
};
|
|
|
|
isolate->heap()->AddGCEpilogueCallback(callback, v8::kGCTypeMarkSweepCompact,
|
|
|
|
nullptr);
|
2018-09-25 11:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::RemoveIsolate(Isolate* isolate) {
|
2018-10-12 13:52:49 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2019-01-25 10:35:37 +00:00
|
|
|
auto it = isolates_.find(isolate);
|
|
|
|
DCHECK_NE(isolates_.end(), it);
|
2019-04-16 11:48:09 +00:00
|
|
|
std::unique_ptr<IsolateInfo> info = std::move(it->second);
|
|
|
|
isolates_.erase(it);
|
|
|
|
for (NativeModule* native_module : info->native_modules) {
|
2019-04-09 14:00:09 +00:00
|
|
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
|
|
|
DCHECK_EQ(1, native_modules_[native_module]->isolates.count(isolate));
|
|
|
|
auto* info = native_modules_[native_module].get();
|
|
|
|
info->isolates.erase(isolate);
|
2019-04-09 13:05:37 +00:00
|
|
|
if (current_gc_info_) {
|
|
|
|
for (WasmCode* code : info->potentially_dead_code) {
|
|
|
|
current_gc_info_->dead_code.erase(code);
|
|
|
|
}
|
2019-05-07 12:26:25 +00:00
|
|
|
if (RemoveIsolateFromCurrentGC(isolate)) PotentiallyFinishCurrentGC();
|
2019-04-09 13:05:37 +00:00
|
|
|
}
|
2019-01-25 10:35:37 +00:00
|
|
|
}
|
2019-04-16 11:48:09 +00:00
|
|
|
if (auto* task = info->log_codes_task) task->Cancel();
|
|
|
|
if (!info->code_to_log.empty()) {
|
|
|
|
WasmCode::DecrementRefCount(VectorOf(info->code_to_log));
|
|
|
|
info->code_to_log.clear();
|
|
|
|
}
|
2019-01-25 10:35:37 +00:00
|
|
|
}
|
|
|
|
|
2019-01-25 10:44:42 +00:00
|
|
|
void WasmEngine::LogCode(WasmCode* code) {
|
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
NativeModule* native_module = code->native_module();
|
2019-04-09 14:00:09 +00:00
|
|
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
|
|
|
for (Isolate* isolate : native_modules_[native_module]->isolates) {
|
2019-01-25 10:44:42 +00:00
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
|
|
|
IsolateInfo* info = isolates_[isolate].get();
|
2019-02-06 11:19:03 +00:00
|
|
|
if (info->log_codes == false) continue;
|
2019-01-25 10:44:42 +00:00
|
|
|
if (info->log_codes_task == nullptr) {
|
|
|
|
auto new_task = base::make_unique<LogCodesTask>(
|
2019-04-16 11:48:09 +00:00
|
|
|
&mutex_, &info->log_codes_task, isolate, this);
|
2019-01-25 10:44:42 +00:00
|
|
|
info->log_codes_task = new_task.get();
|
|
|
|
info->foreground_task_runner->PostTask(std::move(new_task));
|
2019-04-23 14:17:45 +00:00
|
|
|
}
|
|
|
|
if (info->code_to_log.empty()) {
|
2019-04-16 14:24:42 +00:00
|
|
|
isolate->stack_guard()->RequestLogWasmCode();
|
2019-01-25 10:44:42 +00:00
|
|
|
}
|
2019-04-16 11:48:09 +00:00
|
|
|
info->code_to_log.push_back(code);
|
|
|
|
code->IncRef();
|
2019-01-25 10:44:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-06 11:19:03 +00:00
|
|
|
void WasmEngine::EnableCodeLogging(Isolate* isolate) {
|
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
auto it = isolates_.find(isolate);
|
|
|
|
DCHECK_NE(isolates_.end(), it);
|
|
|
|
it->second->log_codes = true;
|
|
|
|
}
|
|
|
|
|
2019-04-16 11:48:09 +00:00
|
|
|
void WasmEngine::LogOutstandingCodesForIsolate(Isolate* isolate) {
|
|
|
|
// If by now we should not log code any more, do not log it.
|
|
|
|
if (!WasmCode::ShouldBeLogged(isolate)) return;
|
|
|
|
|
2019-04-16 14:24:42 +00:00
|
|
|
// Under the mutex, get the vector of wasm code to log. Then log and decrement
|
|
|
|
// the ref count without holding the mutex.
|
|
|
|
std::vector<WasmCode*> code_to_log;
|
|
|
|
{
|
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
|
|
|
code_to_log.swap(isolates_[isolate]->code_to_log);
|
|
|
|
}
|
|
|
|
if (code_to_log.empty()) return;
|
|
|
|
for (WasmCode* code : code_to_log) {
|
2019-04-16 11:48:09 +00:00
|
|
|
code->LogCode(isolate);
|
|
|
|
}
|
2019-04-16 14:24:42 +00:00
|
|
|
WasmCode::DecrementRefCount(VectorOf(code_to_log));
|
2019-04-16 11:48:09 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 16:10:58 +00:00
|
|
|
std::shared_ptr<NativeModule> WasmEngine::NewNativeModule(
|
2019-01-25 10:35:37 +00:00
|
|
|
Isolate* isolate, const WasmFeatures& enabled, size_t code_size_estimate,
|
|
|
|
bool can_request_more, std::shared_ptr<const WasmModule> module) {
|
2019-03-12 16:10:58 +00:00
|
|
|
std::shared_ptr<NativeModule> native_module =
|
2019-01-25 10:35:37 +00:00
|
|
|
code_manager_.NewNativeModule(this, isolate, enabled, code_size_estimate,
|
|
|
|
can_request_more, std::move(module));
|
|
|
|
base::MutexGuard lock(&mutex_);
|
2019-04-09 14:00:09 +00:00
|
|
|
auto pair = native_modules_.insert(std::make_pair(
|
|
|
|
native_module.get(), base::make_unique<NativeModuleInfo>()));
|
|
|
|
DCHECK(pair.second); // inserted new entry.
|
|
|
|
pair.first->second.get()->isolates.insert(isolate);
|
2019-01-25 10:35:37 +00:00
|
|
|
isolates_[isolate]->native_modules.insert(native_module.get());
|
|
|
|
return native_module;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::FreeNativeModule(NativeModule* native_module) {
|
2019-05-13 12:27:48 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
auto it = native_modules_.find(native_module);
|
|
|
|
DCHECK_NE(native_modules_.end(), it);
|
|
|
|
for (Isolate* isolate : it->second->isolates) {
|
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
|
|
|
IsolateInfo* info = isolates_[isolate].get();
|
|
|
|
DCHECK_EQ(1, info->native_modules.count(native_module));
|
|
|
|
info->native_modules.erase(native_module);
|
|
|
|
// If there are {WasmCode} objects of the deleted {NativeModule}
|
|
|
|
// outstanding to be logged in this isolate, remove them. Decrementing the
|
|
|
|
// ref count is not needed, since the {NativeModule} dies anyway.
|
|
|
|
size_t remaining = info->code_to_log.size();
|
|
|
|
if (remaining > 0) {
|
|
|
|
for (size_t i = 0; i < remaining; ++i) {
|
|
|
|
while (i < remaining &&
|
|
|
|
info->code_to_log[i]->native_module() == native_module) {
|
|
|
|
// Move the last remaining item to this slot (this can be the same
|
|
|
|
// as {i}, which is OK).
|
|
|
|
info->code_to_log[i] = info->code_to_log[--remaining];
|
2019-04-16 16:46:33 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-13 12:27:48 +00:00
|
|
|
info->code_to_log.resize(remaining);
|
2019-01-25 10:35:37 +00:00
|
|
|
}
|
2019-05-13 12:27:48 +00:00
|
|
|
}
|
|
|
|
// If there is a GC running which has references to code contained in the
|
|
|
|
// deleted {NativeModule}, remove those references.
|
|
|
|
if (current_gc_info_) {
|
|
|
|
for (auto it = current_gc_info_->dead_code.begin(),
|
|
|
|
end = current_gc_info_->dead_code.end();
|
|
|
|
it != end;) {
|
|
|
|
if ((*it)->native_module() == native_module) {
|
|
|
|
it = current_gc_info_->dead_code.erase(it);
|
|
|
|
} else {
|
|
|
|
++it;
|
2019-04-26 10:07:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-13 12:27:48 +00:00
|
|
|
TRACE_CODE_GC("Native module %p died, reducing dead code objects to %zu.\n",
|
|
|
|
native_module, current_gc_info_->dead_code.size());
|
2019-01-25 10:35:37 +00:00
|
|
|
}
|
2019-05-13 12:27:48 +00:00
|
|
|
native_modules_.erase(it);
|
2018-09-25 11:24:09 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 14:51:56 +00:00
|
|
|
namespace {
|
|
|
|
class SampleTopTierCodeSizeTask : public CancelableTask {
|
|
|
|
public:
|
|
|
|
SampleTopTierCodeSizeTask(Isolate* isolate,
|
|
|
|
std::weak_ptr<NativeModule> native_module)
|
|
|
|
: CancelableTask(isolate),
|
|
|
|
isolate_(isolate),
|
|
|
|
native_module_(std::move(native_module)) {}
|
|
|
|
|
|
|
|
void RunInternal() override {
|
|
|
|
if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
|
|
|
|
native_module->SampleCodeSize(isolate_->counters(),
|
|
|
|
NativeModule::kAfterTopTier);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Isolate* const isolate_;
|
|
|
|
const std::weak_ptr<NativeModule> native_module_;
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
void WasmEngine::SampleTopTierCodeSizeInAllIsolates(
|
|
|
|
const std::shared_ptr<NativeModule>& native_module) {
|
|
|
|
base::MutexGuard lock(&mutex_);
|
2019-04-09 14:00:09 +00:00
|
|
|
DCHECK_EQ(1, native_modules_.count(native_module.get()));
|
|
|
|
for (Isolate* isolate : native_modules_[native_module.get()]->isolates) {
|
2019-03-11 14:51:56 +00:00
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
|
|
|
IsolateInfo* info = isolates_[isolate].get();
|
|
|
|
info->foreground_task_runner->PostTask(
|
|
|
|
base::make_unique<SampleTopTierCodeSizeTask>(isolate, native_module));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-09 13:05:37 +00:00
|
|
|
void WasmEngine::ReportLiveCodeForGC(Isolate* isolate,
|
|
|
|
Vector<WasmCode*> live_code) {
|
2019-04-29 08:28:00 +00:00
|
|
|
TRACE_CODE_GC("Isolate %d reporting %zu live code objects.\n", isolate->id(),
|
|
|
|
live_code.size());
|
2019-05-03 08:50:51 +00:00
|
|
|
base::MutexGuard guard(&mutex_);
|
2019-05-07 09:21:49 +00:00
|
|
|
// This report might come in late (note that we trigger both a stack guard and
|
|
|
|
// a foreground task). In that case, ignore it.
|
|
|
|
if (current_gc_info_ == nullptr) return;
|
2019-05-07 12:26:25 +00:00
|
|
|
if (!RemoveIsolateFromCurrentGC(isolate)) return;
|
2019-05-17 08:33:10 +00:00
|
|
|
isolate->counters()->wasm_module_num_triggered_code_gcs()->AddSample(
|
|
|
|
current_gc_info_->gc_sequence_index);
|
2019-05-03 08:50:51 +00:00
|
|
|
for (WasmCode* code : live_code) current_gc_info_->dead_code.erase(code);
|
2019-05-07 12:26:25 +00:00
|
|
|
PotentiallyFinishCurrentGC();
|
2019-04-09 13:05:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-07 09:21:49 +00:00
|
|
|
void WasmEngine::ReportLiveCodeFromStackForGC(Isolate* isolate) {
|
|
|
|
wasm::WasmCodeRefScope code_ref_scope;
|
|
|
|
std::unordered_set<wasm::WasmCode*> live_wasm_code;
|
|
|
|
for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
|
|
|
|
StackFrame* const frame = it.frame();
|
|
|
|
if (frame->type() != StackFrame::WASM_COMPILED) continue;
|
|
|
|
live_wasm_code.insert(WasmCompiledFrame::cast(frame)->wasm_code());
|
|
|
|
}
|
|
|
|
|
|
|
|
ReportLiveCodeForGC(isolate,
|
|
|
|
OwnedVector<WasmCode*>::Of(live_wasm_code).as_vector());
|
|
|
|
}
|
|
|
|
|
2019-04-09 14:00:09 +00:00
|
|
|
bool WasmEngine::AddPotentiallyDeadCode(WasmCode* code) {
|
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
auto it = native_modules_.find(code->native_module());
|
|
|
|
DCHECK_NE(native_modules_.end(), it);
|
2019-05-17 08:33:10 +00:00
|
|
|
NativeModuleInfo* info = it->second.get();
|
|
|
|
if (info->dead_code.count(code)) return false; // Code is already dead.
|
|
|
|
auto added = info->potentially_dead_code.insert(code);
|
2019-04-09 14:00:09 +00:00
|
|
|
if (!added.second) return false; // An entry already existed.
|
|
|
|
new_potentially_dead_code_size_ += code->instructions().size();
|
2019-04-26 10:03:38 +00:00
|
|
|
if (FLAG_wasm_code_gc) {
|
|
|
|
// Trigger a GC if 1MiB plus 10% of committed code are potentially dead.
|
|
|
|
size_t dead_code_limit =
|
|
|
|
FLAG_stress_wasm_code_gc
|
|
|
|
? 0
|
|
|
|
: 1 * MB + code_manager_.committed_code_space() / 10;
|
2019-05-07 09:54:59 +00:00
|
|
|
if (new_potentially_dead_code_size_ > dead_code_limit) {
|
2019-05-17 08:33:10 +00:00
|
|
|
bool inc_gc_count =
|
|
|
|
info->num_code_gcs_triggered < std::numeric_limits<int8_t>::max();
|
2019-05-07 09:54:59 +00:00
|
|
|
if (current_gc_info_ == nullptr) {
|
2019-05-17 08:33:10 +00:00
|
|
|
if (inc_gc_count) ++info->num_code_gcs_triggered;
|
2019-05-07 09:54:59 +00:00
|
|
|
TRACE_CODE_GC(
|
|
|
|
"Triggering GC (potentially dead: %zu bytes; limit: %zu bytes).\n",
|
|
|
|
new_potentially_dead_code_size_, dead_code_limit);
|
2019-05-17 08:33:10 +00:00
|
|
|
TriggerGC(info->num_code_gcs_triggered);
|
|
|
|
} else if (current_gc_info_->next_gc_sequence_index == 0) {
|
|
|
|
if (inc_gc_count) ++info->num_code_gcs_triggered;
|
2019-05-07 09:54:59 +00:00
|
|
|
TRACE_CODE_GC(
|
|
|
|
"Scheduling another GC after the current one (potentially dead: "
|
|
|
|
"%zu bytes; limit: %zu bytes).\n",
|
|
|
|
new_potentially_dead_code_size_, dead_code_limit);
|
2019-05-17 08:33:10 +00:00
|
|
|
current_gc_info_->next_gc_sequence_index = info->num_code_gcs_triggered;
|
|
|
|
DCHECK_NE(0, current_gc_info_->next_gc_sequence_index);
|
2019-05-07 09:54:59 +00:00
|
|
|
}
|
2019-04-26 10:03:38 +00:00
|
|
|
}
|
2019-04-09 13:05:37 +00:00
|
|
|
}
|
2019-04-09 14:00:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-03 08:50:51 +00:00
|
|
|
void WasmEngine::FreeDeadCode(const DeadCodeMap& dead_code) {
|
|
|
|
base::MutexGuard guard(&mutex_);
|
|
|
|
FreeDeadCodeLocked(dead_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::FreeDeadCodeLocked(const DeadCodeMap& dead_code) {
|
|
|
|
DCHECK(!mutex_.TryLock());
|
|
|
|
for (auto& dead_code_entry : dead_code) {
|
|
|
|
NativeModule* native_module = dead_code_entry.first;
|
|
|
|
const std::vector<WasmCode*>& code_vec = dead_code_entry.second;
|
2019-04-29 08:24:11 +00:00
|
|
|
DCHECK_EQ(1, native_modules_.count(native_module));
|
|
|
|
auto* info = native_modules_[native_module].get();
|
2019-05-03 08:50:51 +00:00
|
|
|
TRACE_CODE_GC("Freeing %zu code object%s of module %p.\n", code_vec.size(),
|
|
|
|
code_vec.size() == 1 ? "" : "s", native_module);
|
|
|
|
for (WasmCode* code : code_vec) {
|
2019-04-29 08:24:11 +00:00
|
|
|
DCHECK_EQ(1, info->dead_code.count(code));
|
|
|
|
info->dead_code.erase(code);
|
|
|
|
}
|
2019-05-03 08:50:51 +00:00
|
|
|
native_module->FreeCode(VectorOf(code_vec));
|
2019-04-29 08:24:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-17 08:33:10 +00:00
|
|
|
void WasmEngine::TriggerGC(int8_t gc_sequence_index) {
|
2019-04-09 13:05:37 +00:00
|
|
|
DCHECK_NULL(current_gc_info_);
|
|
|
|
DCHECK(FLAG_wasm_code_gc);
|
2019-05-17 08:33:10 +00:00
|
|
|
current_gc_info_.reset(new CurrentGCInfo(gc_sequence_index));
|
2019-05-17 13:50:43 +00:00
|
|
|
if (base::TimeTicks::IsHighResolution()) {
|
|
|
|
current_gc_info_->start_time = base::TimeTicks::Now();
|
|
|
|
}
|
2019-04-09 13:05:37 +00:00
|
|
|
// Add all potentially dead code to this GC, and trigger a GC task in each
|
|
|
|
// isolate.
|
|
|
|
for (auto& entry : native_modules_) {
|
|
|
|
NativeModuleInfo* info = entry.second.get();
|
|
|
|
if (info->potentially_dead_code.empty()) continue;
|
|
|
|
for (auto* isolate : native_modules_[entry.first]->isolates) {
|
|
|
|
auto& gc_task = current_gc_info_->outstanding_isolates[isolate];
|
|
|
|
if (!gc_task) {
|
|
|
|
auto new_task = base::make_unique<WasmGCForegroundTask>(isolate);
|
|
|
|
gc_task = new_task.get();
|
|
|
|
DCHECK_EQ(1, isolates_.count(isolate));
|
|
|
|
isolates_[isolate]->foreground_task_runner->PostTask(
|
|
|
|
std::move(new_task));
|
|
|
|
}
|
2019-05-07 09:21:49 +00:00
|
|
|
isolate->stack_guard()->RequestWasmCodeGC();
|
2019-04-09 13:05:37 +00:00
|
|
|
}
|
|
|
|
for (WasmCode* code : info->potentially_dead_code) {
|
|
|
|
current_gc_info_->dead_code.insert(code);
|
|
|
|
}
|
|
|
|
}
|
2019-04-29 08:28:00 +00:00
|
|
|
TRACE_CODE_GC(
|
|
|
|
"Starting GC. Total number of potentially dead code objects: %zu\n",
|
|
|
|
current_gc_info_->dead_code.size());
|
2019-04-09 13:05:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-07 12:26:25 +00:00
|
|
|
bool WasmEngine::RemoveIsolateFromCurrentGC(Isolate* isolate) {
|
|
|
|
DCHECK(!mutex_.TryLock());
|
|
|
|
DCHECK_NOT_NULL(current_gc_info_);
|
|
|
|
auto it = current_gc_info_->outstanding_isolates.find(isolate);
|
|
|
|
if (it == current_gc_info_->outstanding_isolates.end()) return false;
|
|
|
|
if (auto* fg_task = it->second) fg_task->Cancel();
|
|
|
|
current_gc_info_->outstanding_isolates.erase(it);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WasmEngine::PotentiallyFinishCurrentGC() {
|
|
|
|
DCHECK(!mutex_.TryLock());
|
|
|
|
TRACE_CODE_GC(
|
|
|
|
"Remaining dead code objects: %zu; outstanding isolates: %zu.\n",
|
|
|
|
current_gc_info_->dead_code.size(),
|
|
|
|
current_gc_info_->outstanding_isolates.size());
|
|
|
|
|
|
|
|
// If there are more outstanding isolates, return immediately.
|
|
|
|
if (!current_gc_info_->outstanding_isolates.empty()) return;
|
|
|
|
|
|
|
|
// All remaining code in {current_gc_info->dead_code} is really dead.
|
|
|
|
// Move it from the set of potentially dead code to the set of dead code,
|
|
|
|
// and decrement its ref count.
|
2019-05-17 13:50:43 +00:00
|
|
|
size_t num_freed = 0;
|
2019-05-07 12:26:25 +00:00
|
|
|
DeadCodeMap dead_code;
|
|
|
|
for (WasmCode* code : current_gc_info_->dead_code) {
|
|
|
|
DCHECK_EQ(1, native_modules_.count(code->native_module()));
|
|
|
|
auto* native_module_info = native_modules_[code->native_module()].get();
|
|
|
|
DCHECK_EQ(1, native_module_info->potentially_dead_code.count(code));
|
|
|
|
native_module_info->potentially_dead_code.erase(code);
|
|
|
|
DCHECK_EQ(0, native_module_info->dead_code.count(code));
|
|
|
|
native_module_info->dead_code.insert(code);
|
|
|
|
if (code->DecRefOnDeadCode()) {
|
|
|
|
dead_code[code->native_module()].push_back(code);
|
2019-05-17 13:50:43 +00:00
|
|
|
++num_freed;
|
2019-05-07 12:26:25 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-17 13:50:43 +00:00
|
|
|
|
|
|
|
FreeDeadCodeLocked(dead_code);
|
|
|
|
|
|
|
|
int duration_us = 0;
|
|
|
|
if (!current_gc_info_->start_time.IsNull()) {
|
|
|
|
duration_us = GetGCTimeMicros(current_gc_info_->start_time);
|
|
|
|
for (auto& entry : isolates_) {
|
|
|
|
entry.second->async_counters->wasm_code_gc_time()->AddSample(duration_us);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE_CODE_GC("Took %d us; found %zu dead code objects, freed %zu.\n",
|
|
|
|
duration_us, current_gc_info_->dead_code.size(), num_freed);
|
|
|
|
USE(num_freed);
|
|
|
|
|
2019-05-17 08:33:10 +00:00
|
|
|
int8_t next_gc_sequence_index = current_gc_info_->next_gc_sequence_index;
|
2019-05-07 12:26:25 +00:00
|
|
|
current_gc_info_.reset();
|
2019-05-17 08:33:10 +00:00
|
|
|
if (next_gc_sequence_index != 0) TriggerGC(next_gc_sequence_index);
|
2019-05-07 12:26:25 +00:00
|
|
|
}
|
|
|
|
|
2018-07-24 15:58:31 +00:00
|
|
|
namespace {
|
|
|
|
|
2019-01-02 11:35:04 +00:00
|
|
|
DEFINE_LAZY_LEAKY_OBJECT_GETTER(std::shared_ptr<WasmEngine>,
|
2019-02-13 00:33:17 +00:00
|
|
|
GetSharedWasmEngine)
|
2018-07-24 15:58:31 +00:00
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2018-09-25 11:24:09 +00:00
|
|
|
// static
|
2018-07-24 15:58:31 +00:00
|
|
|
void WasmEngine::InitializeOncePerProcess() {
|
|
|
|
if (!FLAG_wasm_shared_engine) return;
|
2019-01-02 11:35:04 +00:00
|
|
|
*GetSharedWasmEngine() = std::make_shared<WasmEngine>();
|
2018-07-24 15:58:31 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 11:24:09 +00:00
|
|
|
// static
|
2018-07-24 15:58:31 +00:00
|
|
|
void WasmEngine::GlobalTearDown() {
|
|
|
|
if (!FLAG_wasm_shared_engine) return;
|
2019-01-02 11:35:04 +00:00
|
|
|
GetSharedWasmEngine()->reset();
|
2018-07-24 15:58:31 +00:00
|
|
|
}
|
|
|
|
|
2018-09-25 11:24:09 +00:00
|
|
|
// static
|
2018-07-24 15:58:31 +00:00
|
|
|
std::shared_ptr<WasmEngine> WasmEngine::GetWasmEngine() {
|
2019-01-02 11:35:04 +00:00
|
|
|
if (FLAG_wasm_shared_engine) return *GetSharedWasmEngine();
|
|
|
|
return std::make_shared<WasmEngine>();
|
2018-07-24 15:58:31 +00:00
|
|
|
}
|
|
|
|
|
2018-10-30 12:32:17 +00:00
|
|
|
// {max_mem_pages} is declared in wasm-limits.h.
|
|
|
|
uint32_t max_mem_pages() {
|
|
|
|
STATIC_ASSERT(kV8MaxWasmMemoryPages <= kMaxUInt32);
|
|
|
|
return std::min(uint32_t{kV8MaxWasmMemoryPages}, FLAG_wasm_max_mem_pages);
|
|
|
|
}
|
|
|
|
|
2019-03-01 14:26:16 +00:00
|
|
|
// {max_table_init_entries} is declared in wasm-limits.h.
|
|
|
|
uint32_t max_table_init_entries() {
|
|
|
|
return std::min(uint32_t{kV8MaxWasmTableInitEntries},
|
|
|
|
FLAG_wasm_max_table_size);
|
|
|
|
}
|
|
|
|
|
2019-04-29 08:28:00 +00:00
|
|
|
#undef TRACE_CODE_GC
|
|
|
|
|
2018-01-17 14:46:27 +00:00
|
|
|
} // namespace wasm
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|