[wasm] Move compilation methods to module-compiler.h
Note that this also makes it possible to move several classes into the module-compiler.cc file and inline their implementations. This also allows removing several uses of wasm-module.h from other places in V8 that include wasm-objects.h. R=yangguo@chromium.org,clemensh@chromium.org,ahaas@chromium.org Bug: Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: I303ee2bb49dc53c951d377a1b65699c1e0e91da7 Reviewed-on: https://chromium-review.googlesource.com/687494 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Ben Titzer <titzer@chromium.org> Cr-Commit-Position: refs/heads/master@{#48204}
This commit is contained in:
parent
40856948a6
commit
afb7bdc46a
@ -81,7 +81,6 @@
|
||||
#include "src/vm-state-inl.h"
|
||||
#include "src/wasm/compilation-manager.h"
|
||||
#include "src/wasm/streaming-decoder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "src/parsing/scanner-character-streams.h"
|
||||
#include "src/parsing/scanner.h"
|
||||
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-js.h"
|
||||
#include "src/wasm/wasm-module-builder.h"
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/frame-array-inl.h"
|
||||
#include "src/string-builder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include "src/messages.h"
|
||||
#include "src/objects/debug-objects-inl.h"
|
||||
#include "src/snapshot/natives.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
|
||||
#include "include/v8-debug.h"
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include "src/visitors.h"
|
||||
#include "src/vm-state-inl.h"
|
||||
#include "src/wasm/compilation-manager.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/zone/accounting-allocator.h"
|
||||
|
||||
|
@ -71,7 +71,6 @@
|
||||
#include "src/string-stream.h"
|
||||
#include "src/unicode-cache-inl.h"
|
||||
#include "src/utils-inl.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/zone/zone.h"
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/objects/debug-objects-inl.h"
|
||||
#include "src/runtime/runtime.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "src/compiler.h"
|
||||
#include "src/isolate-inl.h"
|
||||
#include "src/messages.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "src/parsing/parse-info.h"
|
||||
#include "src/parsing/parsing.h"
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "src/objects/frame-array-inl.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/v8memory.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "src/objects.h"
|
||||
#include "src/snapshot/code-serializer.h"
|
||||
#include "src/transitions.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
|
||||
|
@ -361,6 +361,27 @@ class Decoder {
|
||||
}
|
||||
};
|
||||
|
||||
// Reference to a string in the wire bytes.
|
||||
class WireBytesRef {
|
||||
public:
|
||||
WireBytesRef() : WireBytesRef(0, 0) {}
|
||||
WireBytesRef(uint32_t offset, uint32_t length)
|
||||
: offset_(offset), length_(length) {
|
||||
DCHECK_IMPLIES(offset_ == 0, length_ == 0);
|
||||
DCHECK_LE(offset_, offset_ + length_); // no uint32_t overflow.
|
||||
}
|
||||
|
||||
uint32_t offset() const { return offset_; }
|
||||
uint32_t length() const { return length_; }
|
||||
uint32_t end_offset() const { return offset_ + length_; }
|
||||
bool is_empty() const { return length_ == 0; }
|
||||
bool is_set() const { return offset_ != 0; }
|
||||
|
||||
private:
|
||||
uint32_t offset_;
|
||||
uint32_t length_;
|
||||
};
|
||||
|
||||
#undef TRACE
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
|
@ -10,13 +10,15 @@
|
||||
#include "src/asmjs/asm-js.h"
|
||||
#include "src/assembler-inl.h"
|
||||
#include "src/base/template-utils.h"
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/code-stubs.h"
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/counters.h"
|
||||
#include "src/property-descriptor.h"
|
||||
#include "src/wasm/compilation-manager.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-code-specialization.h"
|
||||
#include "src/wasm/wasm-js.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects-inl.h"
|
||||
#include "src/wasm/wasm-result.h"
|
||||
|
||||
@ -45,6 +47,857 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
// A class compiling an entire module.
|
||||
class ModuleCompiler {
|
||||
public:
|
||||
ModuleCompiler(Isolate* isolate, WasmModule* module,
|
||||
Handle<Code> centry_stub);
|
||||
|
||||
// The actual runnable task that performs compilations in the background.
|
||||
class CompilationTask : public CancelableTask {
|
||||
public:
|
||||
ModuleCompiler* compiler_;
|
||||
explicit CompilationTask(ModuleCompiler* compiler)
|
||||
: CancelableTask(&compiler->background_task_manager_),
|
||||
compiler_(compiler) {}
|
||||
|
||||
void RunInternal() override {
|
||||
while (compiler_->executed_units_.CanAcceptWork() &&
|
||||
compiler_->FetchAndExecuteCompilationUnit()) {
|
||||
}
|
||||
|
||||
compiler_->OnBackgroundTaskStopped();
|
||||
}
|
||||
};
|
||||
|
||||
// The CompilationUnitBuilder builds compilation units and stores them in an
|
||||
// internal buffer. The buffer is moved into the working queue of the
|
||||
// ModuleCompiler when {Commit} is called.
|
||||
class CompilationUnitBuilder {
|
||||
public:
|
||||
explicit CompilationUnitBuilder(ModuleCompiler* compiler)
|
||||
: compiler_(compiler) {}
|
||||
|
||||
~CompilationUnitBuilder() { DCHECK(units_.empty()); }
|
||||
|
||||
void AddUnit(compiler::ModuleEnv* module_env, const WasmFunction* function,
|
||||
uint32_t buffer_offset, Vector<const uint8_t> bytes,
|
||||
WasmName name) {
|
||||
units_.emplace_back(new compiler::WasmCompilationUnit(
|
||||
compiler_->isolate_, module_env,
|
||||
wasm::FunctionBody{function->sig, buffer_offset, bytes.begin(),
|
||||
bytes.end()},
|
||||
name, function->func_index, compiler_->centry_stub_,
|
||||
compiler_->counters()));
|
||||
}
|
||||
|
||||
void Commit() {
|
||||
{
|
||||
base::LockGuard<base::Mutex> guard(
|
||||
&compiler_->compilation_units_mutex_);
|
||||
compiler_->compilation_units_.insert(
|
||||
compiler_->compilation_units_.end(),
|
||||
std::make_move_iterator(units_.begin()),
|
||||
std::make_move_iterator(units_.end()));
|
||||
}
|
||||
units_.clear();
|
||||
}
|
||||
|
||||
void Clear() { units_.clear(); }
|
||||
|
||||
private:
|
||||
ModuleCompiler* compiler_;
|
||||
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> units_;
|
||||
};
|
||||
|
||||
class CodeGenerationSchedule {
|
||||
public:
|
||||
explicit CodeGenerationSchedule(
|
||||
base::RandomNumberGenerator* random_number_generator,
|
||||
size_t max_memory = 0);
|
||||
|
||||
void Schedule(std::unique_ptr<compiler::WasmCompilationUnit>&& item);
|
||||
|
||||
bool IsEmpty() const { return schedule_.empty(); }
|
||||
|
||||
std::unique_ptr<compiler::WasmCompilationUnit> GetNext();
|
||||
|
||||
bool CanAcceptWork() const;
|
||||
|
||||
bool ShouldIncreaseWorkload() const;
|
||||
|
||||
void EnableThrottling() { throttle_ = true; }
|
||||
|
||||
private:
|
||||
size_t GetRandomIndexInSchedule();
|
||||
|
||||
base::RandomNumberGenerator* random_number_generator_ = nullptr;
|
||||
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> schedule_;
|
||||
const size_t max_memory_;
|
||||
bool throttle_ = false;
|
||||
base::AtomicNumber<size_t> allocated_memory_{0};
|
||||
};
|
||||
|
||||
Counters* counters() const { return async_counters_.get(); }
|
||||
|
||||
// Run by each compilation task and by the main thread (i.e. in both
|
||||
// foreground and background threads). The no_finisher_callback is called
|
||||
// within the result_mutex_ lock when no finishing task is running, i.e. when
|
||||
// the finisher_is_running_ flag is not set.
|
||||
bool FetchAndExecuteCompilationUnit(
|
||||
std::function<void()> no_finisher_callback = nullptr);
|
||||
|
||||
void OnBackgroundTaskStopped();
|
||||
|
||||
void EnableThrottling() { executed_units_.EnableThrottling(); }
|
||||
|
||||
bool CanAcceptWork() const { return executed_units_.CanAcceptWork(); }
|
||||
|
||||
bool ShouldIncreaseWorkload() const {
|
||||
return executed_units_.ShouldIncreaseWorkload();
|
||||
}
|
||||
|
||||
size_t InitializeCompilationUnits(const std::vector<WasmFunction>& functions,
|
||||
const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env);
|
||||
|
||||
void RestartCompilationTasks();
|
||||
|
||||
size_t FinishCompilationUnits(std::vector<Handle<Code>>& results,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
bool IsFinisherRunning() const { return finisher_is_running_; }
|
||||
|
||||
void SetFinisherIsRunning(bool value);
|
||||
|
||||
MaybeHandle<Code> FinishCompilationUnit(ErrorThrower* thrower,
|
||||
int* func_index);
|
||||
|
||||
void CompileInParallel(const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env,
|
||||
std::vector<Handle<Code>>& results,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
void CompileSequentially(const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env,
|
||||
std::vector<Handle<Code>>& results,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
void ValidateSequentially(const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
static MaybeHandle<WasmModuleObject> CompileToModuleObject(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
std::unique_ptr<WasmModule> module, const ModuleWireBytes& wire_bytes,
|
||||
Handle<Script> asm_js_script,
|
||||
Vector<const byte> asm_js_offset_table_bytes);
|
||||
|
||||
private:
|
||||
MaybeHandle<WasmModuleObject> CompileToModuleObjectInternal(
|
||||
ErrorThrower* thrower, std::unique_ptr<WasmModule> module,
|
||||
const ModuleWireBytes& wire_bytes, Handle<Script> asm_js_script,
|
||||
Vector<const byte> asm_js_offset_table_bytes);
|
||||
|
||||
Isolate* isolate_;
|
||||
WasmModule* module_;
|
||||
const std::shared_ptr<Counters> async_counters_;
|
||||
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>>
|
||||
compilation_units_;
|
||||
base::Mutex compilation_units_mutex_;
|
||||
CodeGenerationSchedule executed_units_;
|
||||
base::Mutex result_mutex_;
|
||||
const size_t num_background_tasks_;
|
||||
// This flag should only be set while holding result_mutex_.
|
||||
bool finisher_is_running_ = false;
|
||||
CancelableTaskManager background_task_manager_;
|
||||
size_t stopped_compilation_tasks_ = 0;
|
||||
base::Mutex tasks_mutex_;
|
||||
Handle<Code> centry_stub_;
|
||||
};
|
||||
|
||||
class JSToWasmWrapperCache {
|
||||
public:
|
||||
Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate,
|
||||
wasm::WasmModule* module,
|
||||
Handle<Code> wasm_code,
|
||||
uint32_t index) {
|
||||
const wasm::WasmFunction* func = &module->functions[index];
|
||||
int cached_idx = sig_map_.Find(func->sig);
|
||||
if (cached_idx >= 0) {
|
||||
Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]);
|
||||
// Now patch the call to wasm code.
|
||||
for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) {
|
||||
DCHECK(!it.done());
|
||||
Code* target =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (target->kind() == Code::WASM_FUNCTION ||
|
||||
target->kind() == Code::WASM_TO_JS_FUNCTION ||
|
||||
target->builtin_index() == Builtins::kIllegal ||
|
||||
target->builtin_index() == Builtins::kWasmCompileLazy) {
|
||||
it.rinfo()->set_target_address(isolate,
|
||||
wasm_code->instruction_start());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
Handle<Code> code =
|
||||
compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index);
|
||||
uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig);
|
||||
DCHECK_EQ(code_cache_.size(), new_cache_idx);
|
||||
USE(new_cache_idx);
|
||||
code_cache_.push_back(code);
|
||||
return code;
|
||||
}
|
||||
|
||||
private:
|
||||
// sig_map_ maps signatures to an index in code_cache_.
|
||||
wasm::SignatureMap sig_map_;
|
||||
std::vector<Handle<Code>> code_cache_;
|
||||
};
|
||||
|
||||
// A helper class to simplify instantiating a module from a compiled module.
|
||||
// It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule},
|
||||
// etc.
|
||||
class InstanceBuilder {
|
||||
public:
|
||||
InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object,
|
||||
MaybeHandle<JSReceiver> ffi,
|
||||
MaybeHandle<JSArrayBuffer> memory,
|
||||
WeakCallbackInfo<void>::Callback instance_finalizer_callback);
|
||||
|
||||
// Build an instance, in all of its glory.
|
||||
MaybeHandle<WasmInstanceObject> Build();
|
||||
|
||||
private:
|
||||
// Represents the initialized state of a table.
|
||||
struct TableInstance {
|
||||
Handle<WasmTableObject> table_object; // WebAssembly.Table instance
|
||||
Handle<FixedArray> js_wrappers; // JSFunctions exported
|
||||
Handle<FixedArray> function_table; // internal code array
|
||||
Handle<FixedArray> signature_table; // internal sig array
|
||||
};
|
||||
|
||||
// A pre-evaluated value to use in import binding.
|
||||
struct SanitizedImport {
|
||||
Handle<String> module_name;
|
||||
Handle<String> import_name;
|
||||
Handle<Object> value;
|
||||
};
|
||||
|
||||
Isolate* isolate_;
|
||||
WasmModule* const module_;
|
||||
const std::shared_ptr<Counters> async_counters_;
|
||||
ErrorThrower* thrower_;
|
||||
Handle<WasmModuleObject> module_object_;
|
||||
MaybeHandle<JSReceiver> ffi_;
|
||||
MaybeHandle<JSArrayBuffer> memory_;
|
||||
Handle<JSArrayBuffer> globals_;
|
||||
Handle<WasmCompiledModule> compiled_module_;
|
||||
std::vector<TableInstance> table_instances_;
|
||||
std::vector<Handle<JSFunction>> js_wrappers_;
|
||||
JSToWasmWrapperCache js_to_wasm_cache_;
|
||||
WeakCallbackInfo<void>::Callback instance_finalizer_callback_;
|
||||
std::vector<SanitizedImport> sanitized_imports_;
|
||||
|
||||
const std::shared_ptr<Counters>& async_counters() const {
|
||||
return async_counters_;
|
||||
}
|
||||
Counters* counters() const { return async_counters().get(); }
|
||||
|
||||
// Helper routines to print out errors with imports.
|
||||
#define ERROR_THROWER_WITH_MESSAGE(TYPE) \
|
||||
void Report##TYPE(const char* error, uint32_t index, \
|
||||
Handle<String> module_name, Handle<String> import_name) { \
|
||||
thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \
|
||||
index, module_name->ToCString().get(), \
|
||||
import_name->ToCString().get(), error); \
|
||||
} \
|
||||
\
|
||||
MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \
|
||||
Handle<String> module_name) { \
|
||||
thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \
|
||||
module_name->ToCString().get(), error); \
|
||||
return MaybeHandle<Object>(); \
|
||||
}
|
||||
|
||||
ERROR_THROWER_WITH_MESSAGE(LinkError)
|
||||
ERROR_THROWER_WITH_MESSAGE(TypeError)
|
||||
|
||||
// Look up an import value in the {ffi_} object.
|
||||
MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
|
||||
Handle<String> import_name);
|
||||
|
||||
// Look up an import value in the {ffi_} object specifically for linking an
|
||||
// asm.js module. This only performs non-observable lookups, which allows
|
||||
// falling back to JavaScript proper (and hence re-executing all lookups) if
|
||||
// module instantiation fails.
|
||||
MaybeHandle<Object> LookupImportAsm(uint32_t index,
|
||||
Handle<String> import_name);
|
||||
|
||||
uint32_t EvalUint32InitExpr(const WasmInitExpr& expr);
|
||||
|
||||
// Load data segments into the memory.
|
||||
void LoadDataSegments(Address mem_addr, size_t mem_size);
|
||||
|
||||
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value);
|
||||
|
||||
void SanitizeImports();
|
||||
|
||||
Handle<FixedArray> SetupWasmToJSImportsTable(
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
// Process the imports, including functions, tables, globals, and memory, in
|
||||
// order, loading them from the {ffi_} object. Returns the number of imported
|
||||
// functions.
|
||||
int ProcessImports(Handle<FixedArray> code_table,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
template <typename T>
|
||||
T* GetRawGlobalPtr(WasmGlobal& global);
|
||||
|
||||
// Process initialization of globals.
|
||||
void InitGlobals();
|
||||
|
||||
// Allocate memory for a module instance as a new JSArrayBuffer.
|
||||
Handle<JSArrayBuffer> AllocateMemory(uint32_t num_pages);
|
||||
|
||||
bool NeedsWrappers() const;
|
||||
|
||||
// Process the exports, creating wrappers for functions, tables, memories,
|
||||
// and globals.
|
||||
void ProcessExports(Handle<WasmInstanceObject> instance,
|
||||
Handle<WasmCompiledModule> compiled_module);
|
||||
|
||||
void InitializeTables(Handle<WasmInstanceObject> instance,
|
||||
CodeSpecialization* code_specialization);
|
||||
|
||||
void LoadTableSegments(Handle<FixedArray> code_table,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
};
|
||||
|
||||
// TODO(titzer): move to wasm-objects.cc
|
||||
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
|
||||
WasmInstanceObject* owner = reinterpret_cast<WasmInstanceObject*>(*p);
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
|
||||
// If a link to shared memory instances exists, update the list of memory
|
||||
// instances before the instance is destroyed.
|
||||
WasmCompiledModule* compiled_module = owner->compiled_module();
|
||||
TRACE("Finalizing %d {\n", compiled_module->instance_id());
|
||||
DCHECK(compiled_module->has_weak_wasm_module());
|
||||
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
|
||||
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
Handle<FixedArray> code_table = compiled_module->code_table();
|
||||
for (int i = 0; i < code_table->length(); ++i) {
|
||||
Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
|
||||
int index = code->trap_handler_index()->value();
|
||||
if (index >= 0) {
|
||||
trap_handler::ReleaseHandlerData(index);
|
||||
code->set_trap_handler_index(Smi::FromInt(trap_handler::kInvalidIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since the order of finalizers is not guaranteed, it can be the case
|
||||
// that {instance->compiled_module()->module()}, which is a
|
||||
// {Managed<WasmModule>} has been collected earlier in this GC cycle.
|
||||
// Weak references to this instance won't be cleared until
|
||||
// the next GC cycle, so we need to manually break some links (such as
|
||||
// the weak references from {WasmMemoryObject::instances}.
|
||||
if (owner->has_memory_object()) {
|
||||
Handle<WasmMemoryObject> memory(owner->memory_object(), isolate);
|
||||
Handle<WasmInstanceObject> instance(owner, isolate);
|
||||
WasmMemoryObject::RemoveInstance(isolate, memory, instance);
|
||||
}
|
||||
|
||||
// weak_wasm_module may have been cleared, meaning the module object
|
||||
// was GC-ed. In that case, there won't be any new instances created,
|
||||
// and we don't need to maintain the links between instances.
|
||||
if (!weak_wasm_module->cleared()) {
|
||||
WasmModuleObject* wasm_module =
|
||||
WasmModuleObject::cast(weak_wasm_module->value());
|
||||
WasmCompiledModule* current_template = wasm_module->compiled_module();
|
||||
|
||||
TRACE("chain before {\n");
|
||||
TRACE_CHAIN(current_template);
|
||||
TRACE("}\n");
|
||||
|
||||
DCHECK(!current_template->has_weak_prev_instance());
|
||||
WeakCell* next = compiled_module->maybe_ptr_to_weak_next_instance();
|
||||
WeakCell* prev = compiled_module->maybe_ptr_to_weak_prev_instance();
|
||||
|
||||
if (current_template == compiled_module) {
|
||||
if (next == nullptr) {
|
||||
WasmCompiledModule::Reset(isolate, compiled_module);
|
||||
} else {
|
||||
WasmCompiledModule* next_compiled_module =
|
||||
WasmCompiledModule::cast(next->value());
|
||||
WasmModuleObject::cast(wasm_module)
|
||||
->set_compiled_module(next_compiled_module);
|
||||
DCHECK_NULL(prev);
|
||||
next_compiled_module->reset_weak_prev_instance();
|
||||
}
|
||||
} else {
|
||||
DCHECK(!(prev == nullptr && next == nullptr));
|
||||
// the only reason prev or next would be cleared is if the
|
||||
// respective objects got collected, but if that happened,
|
||||
// we would have relinked the list.
|
||||
if (prev != nullptr) {
|
||||
DCHECK(!prev->cleared());
|
||||
if (next == nullptr) {
|
||||
WasmCompiledModule::cast(prev->value())->reset_weak_next_instance();
|
||||
} else {
|
||||
WasmCompiledModule::cast(prev->value())
|
||||
->set_ptr_to_weak_next_instance(next);
|
||||
}
|
||||
}
|
||||
if (next != nullptr) {
|
||||
DCHECK(!next->cleared());
|
||||
if (prev == nullptr) {
|
||||
WasmCompiledModule::cast(next->value())->reset_weak_prev_instance();
|
||||
} else {
|
||||
WasmCompiledModule::cast(next->value())
|
||||
->set_ptr_to_weak_prev_instance(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("chain after {\n");
|
||||
TRACE_CHAIN(wasm_module->compiled_module());
|
||||
TRACE("}\n");
|
||||
}
|
||||
compiled_module->reset_weak_owning_instance();
|
||||
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
|
||||
TRACE("}\n");
|
||||
}
|
||||
|
||||
bool SyncValidate(Isolate* isolate, const ModuleWireBytes& bytes) {
|
||||
if (bytes.start() == nullptr || bytes.length() == 0) return false;
|
||||
ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(),
|
||||
bytes.end(), true, kWasmOrigin);
|
||||
return result.ok();
|
||||
}
|
||||
|
||||
MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
Handle<Script> asm_js_script,
|
||||
Vector<const byte> asm_js_offset_table_bytes) {
|
||||
ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(),
|
||||
bytes.end(), false, kAsmJsOrigin);
|
||||
if (result.failed()) {
|
||||
thrower->CompileFailed("Wasm decoding failed", result);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Transfer ownership of the WasmModule to the {WasmModuleWrapper} generated
|
||||
// in {CompileToModuleObject}.
|
||||
return ModuleCompiler::CompileToModuleObject(
|
||||
isolate, thrower, std::move(result.val), bytes, asm_js_script,
|
||||
asm_js_offset_table_bytes);
|
||||
}
|
||||
|
||||
MaybeHandle<WasmModuleObject> SyncCompile(Isolate* isolate,
|
||||
ErrorThrower* thrower,
|
||||
const ModuleWireBytes& bytes) {
|
||||
if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) {
|
||||
thrower->CompileError("Wasm code generation disallowed in this context");
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO(titzer): only make a copy of the bytes if SharedArrayBuffer
|
||||
std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
|
||||
memcpy(copy.get(), bytes.start(), bytes.length());
|
||||
ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
|
||||
|
||||
ModuleResult result = SyncDecodeWasmModule(
|
||||
isolate, bytes_copy.start(), bytes_copy.end(), false, kWasmOrigin);
|
||||
if (result.failed()) {
|
||||
thrower->CompileFailed("Wasm decoding failed", result);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Transfer ownership of the WasmModule to the {WasmModuleWrapper} generated
|
||||
// in {CompileToModuleObject}.
|
||||
return ModuleCompiler::CompileToModuleObject(
|
||||
isolate, thrower, std::move(result.val), bytes_copy, Handle<Script>(),
|
||||
Vector<const byte>());
|
||||
}
|
||||
|
||||
MaybeHandle<WasmInstanceObject> SyncInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
|
||||
MaybeHandle<JSArrayBuffer> memory) {
|
||||
InstanceBuilder builder(isolate, thrower, module_object, imports, memory,
|
||||
&InstanceFinalizer);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory) {
|
||||
MaybeHandle<WasmModuleObject> module = SyncCompile(isolate, thrower, bytes);
|
||||
DCHECK_EQ(thrower->error(), module.is_null());
|
||||
if (module.is_null()) return {};
|
||||
|
||||
return SyncInstantiate(isolate, thrower, module.ToHandleChecked(),
|
||||
Handle<JSReceiver>::null(),
|
||||
Handle<JSArrayBuffer>::null());
|
||||
}
|
||||
|
||||
void RejectPromise(Isolate* isolate, Handle<Context> context,
|
||||
ErrorThrower& thrower, Handle<JSPromise> promise) {
|
||||
Local<Promise::Resolver> resolver =
|
||||
Utils::PromiseToLocal(promise).As<Promise::Resolver>();
|
||||
auto maybe = resolver->Reject(Utils::ToLocal(context),
|
||||
Utils::ToLocal(thrower.Reify()));
|
||||
CHECK_IMPLIES(!maybe.FromMaybe(false), isolate->has_scheduled_exception());
|
||||
}
|
||||
|
||||
void ResolvePromise(Isolate* isolate, Handle<Context> context,
|
||||
Handle<JSPromise> promise, Handle<Object> result) {
|
||||
Local<Promise::Resolver> resolver =
|
||||
Utils::PromiseToLocal(promise).As<Promise::Resolver>();
|
||||
auto maybe =
|
||||
resolver->Resolve(Utils::ToLocal(context), Utils::ToLocal(result));
|
||||
CHECK_IMPLIES(!maybe.FromMaybe(false), isolate->has_scheduled_exception());
|
||||
}
|
||||
|
||||
void AsyncInstantiate(Isolate* isolate, Handle<JSPromise> promise,
|
||||
Handle<WasmModuleObject> module_object,
|
||||
MaybeHandle<JSReceiver> imports) {
|
||||
ErrorThrower thrower(isolate, nullptr);
|
||||
MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
|
||||
isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
|
||||
if (thrower.error()) {
|
||||
RejectPromise(isolate, handle(isolate->context()), thrower, promise);
|
||||
return;
|
||||
}
|
||||
ResolvePromise(isolate, handle(isolate->context()), promise,
|
||||
instance_object.ToHandleChecked());
|
||||
}
|
||||
|
||||
void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
|
||||
const ModuleWireBytes& bytes) {
|
||||
if (!FLAG_wasm_async_compilation) {
|
||||
ErrorThrower thrower(isolate, "WasmCompile");
|
||||
// Compile the module.
|
||||
MaybeHandle<WasmModuleObject> module_object =
|
||||
SyncCompile(isolate, &thrower, bytes);
|
||||
if (thrower.error()) {
|
||||
RejectPromise(isolate, handle(isolate->context()), thrower, promise);
|
||||
return;
|
||||
}
|
||||
Handle<WasmModuleObject> module = module_object.ToHandleChecked();
|
||||
ResolvePromise(isolate, handle(isolate->context()), promise, module);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAG_wasm_test_streaming) {
|
||||
std::shared_ptr<StreamingDecoder> streaming_decoder =
|
||||
isolate->wasm_compilation_manager()->StartStreamingCompilation(
|
||||
isolate, handle(isolate->context()), promise);
|
||||
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());
|
||||
isolate->wasm_compilation_manager()->StartAsyncCompileJob(
|
||||
isolate, std::move(copy), bytes.length(), handle(isolate->context()),
|
||||
promise);
|
||||
}
|
||||
|
||||
Handle<Code> CompileLazy(Isolate* isolate) {
|
||||
HistogramTimerScope lazy_time_scope(
|
||||
isolate->counters()->wasm_lazy_compilation_time());
|
||||
|
||||
// Find the wasm frame which triggered the lazy compile, to get the wasm
|
||||
// instance.
|
||||
StackFrameIterator it(isolate);
|
||||
// First frame: C entry stub.
|
||||
DCHECK(!it.done());
|
||||
DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
|
||||
it.Advance();
|
||||
// Second frame: WasmCompileLazy builtin.
|
||||
DCHECK(!it.done());
|
||||
Handle<Code> lazy_compile_code(it.frame()->LookupCode(), isolate);
|
||||
DCHECK_EQ(Builtins::kWasmCompileLazy, lazy_compile_code->builtin_index());
|
||||
Handle<WasmInstanceObject> instance;
|
||||
Handle<FixedArray> exp_deopt_data;
|
||||
int func_index = -1;
|
||||
if (lazy_compile_code->deoptimization_data()->length() > 0) {
|
||||
// Then it's an indirect call or via JS->wasm wrapper.
|
||||
DCHECK_LE(2, lazy_compile_code->deoptimization_data()->length());
|
||||
exp_deopt_data = handle(lazy_compile_code->deoptimization_data(), isolate);
|
||||
auto* weak_cell = WeakCell::cast(exp_deopt_data->get(0));
|
||||
instance = handle(WasmInstanceObject::cast(weak_cell->value()), isolate);
|
||||
func_index = Smi::ToInt(exp_deopt_data->get(1));
|
||||
}
|
||||
it.Advance();
|
||||
// Third frame: The calling wasm code or js-to-wasm wrapper.
|
||||
DCHECK(!it.done());
|
||||
DCHECK(it.frame()->is_js_to_wasm() || it.frame()->is_wasm_compiled());
|
||||
Handle<Code> caller_code = handle(it.frame()->LookupCode(), isolate);
|
||||
if (it.frame()->is_js_to_wasm()) {
|
||||
DCHECK(!instance.is_null());
|
||||
} else if (instance.is_null()) {
|
||||
// Then this is a direct call (otherwise we would have attached the instance
|
||||
// via deopt data to the lazy compile stub). Just use the instance of the
|
||||
// caller.
|
||||
instance =
|
||||
handle(WasmInstanceObject::GetOwningInstance(*caller_code), isolate);
|
||||
}
|
||||
int offset =
|
||||
static_cast<int>(it.frame()->pc() - caller_code->instruction_start());
|
||||
// Only patch the caller code if this is *no* indirect call.
|
||||
// exp_deopt_data will be null if the called function is not exported at all,
|
||||
// and its length will be <= 2 if all entries in tables were already patched.
|
||||
// Note that this check is conservative: If the first call to an exported
|
||||
// function is direct, we will just patch the export tables, and only on the
|
||||
// second call we will patch the caller.
|
||||
bool patch_caller = caller_code->kind() == Code::JS_TO_WASM_FUNCTION ||
|
||||
exp_deopt_data.is_null() || exp_deopt_data->length() <= 2;
|
||||
|
||||
Handle<Code> compiled_code = WasmCompiledModule::CompileLazy(
|
||||
isolate, instance, caller_code, offset, func_index, patch_caller);
|
||||
if (!exp_deopt_data.is_null() && exp_deopt_data->length() > 2) {
|
||||
// See EnsureExportedLazyDeoptData: exp_deopt_data[2...(len-1)] are pairs of
|
||||
// <export_table, index> followed by undefined values.
|
||||
// Use this information here to patch all export tables.
|
||||
DCHECK_EQ(0, exp_deopt_data->length() % 2);
|
||||
for (int idx = 2, end = exp_deopt_data->length(); idx < end; idx += 2) {
|
||||
if (exp_deopt_data->get(idx)->IsUndefined(isolate)) break;
|
||||
FixedArray* exp_table = FixedArray::cast(exp_deopt_data->get(idx));
|
||||
int exp_index = Smi::ToInt(exp_deopt_data->get(idx + 1));
|
||||
DCHECK(exp_table->get(exp_index) == *lazy_compile_code);
|
||||
exp_table->set(exp_index, *compiled_code);
|
||||
}
|
||||
// After processing, remove the list of exported entries, such that we don't
|
||||
// do the patching redundantly.
|
||||
Handle<FixedArray> new_deopt_data =
|
||||
isolate->factory()->CopyFixedArrayUpTo(exp_deopt_data, 2, TENURED);
|
||||
lazy_compile_code->set_deoptimization_data(*new_deopt_data);
|
||||
}
|
||||
|
||||
return compiled_code;
|
||||
}
|
||||
|
||||
compiler::ModuleEnv CreateModuleEnvFromCompiledModule(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
WasmModule* module = compiled_module->module();
|
||||
|
||||
std::vector<GlobalHandleAddress> function_tables;
|
||||
std::vector<GlobalHandleAddress> signature_tables;
|
||||
std::vector<SignatureMap*> signature_maps;
|
||||
|
||||
int num_function_tables = static_cast<int>(module->function_tables.size());
|
||||
for (int i = 0; i < num_function_tables; ++i) {
|
||||
FixedArray* ft = compiled_module->ptr_to_function_tables();
|
||||
FixedArray* st = compiled_module->ptr_to_signature_tables();
|
||||
|
||||
// TODO(clemensh): defer these handles for concurrent compilation.
|
||||
function_tables.push_back(WasmCompiledModule::GetTableValue(ft, i));
|
||||
signature_tables.push_back(WasmCompiledModule::GetTableValue(st, i));
|
||||
signature_maps.push_back(&module->function_tables[i].map);
|
||||
}
|
||||
|
||||
std::vector<Handle<Code>> empty_code;
|
||||
|
||||
compiler::ModuleEnv result = {
|
||||
module, // --
|
||||
function_tables, // --
|
||||
signature_tables, // --
|
||||
signature_maps, // --
|
||||
empty_code, // --
|
||||
BUILTIN_CODE(isolate, WasmCompileLazy), // --
|
||||
reinterpret_cast<uintptr_t>( // --
|
||||
compiled_module->GetEmbeddedMemStartOrNull()), // --
|
||||
compiled_module->GetEmbeddedMemSizeOrZero(), // --
|
||||
reinterpret_cast<uintptr_t>( // --
|
||||
compiled_module->GetGlobalsStartOrNull()) // --
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
void LazyCompilationOrchestrator::CompileFunction(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index) {
|
||||
Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
|
||||
isolate);
|
||||
if (Code::cast(compiled_module->code_table()->get(func_index))->kind() ==
|
||||
Code::WASM_FUNCTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
compiler::ModuleEnv module_env =
|
||||
CreateModuleEnvFromCompiledModule(isolate, compiled_module);
|
||||
|
||||
const uint8_t* module_start = compiled_module->module_bytes()->GetChars();
|
||||
|
||||
const WasmFunction* func = &module_env.module->functions[func_index];
|
||||
FunctionBody body{func->sig, func->code.offset(),
|
||||
module_start + func->code.offset(),
|
||||
module_start + func->code.end_offset()};
|
||||
// TODO(wasm): Refactor this to only get the name if it is really needed for
|
||||
// tracing / debugging.
|
||||
std::string func_name;
|
||||
{
|
||||
WasmName name = Vector<const char>::cast(
|
||||
compiled_module->GetRawFunctionName(func_index));
|
||||
// Copy to std::string, because the underlying string object might move on
|
||||
// the heap.
|
||||
func_name.assign(name.start(), static_cast<size_t>(name.length()));
|
||||
}
|
||||
ErrorThrower thrower(isolate, "WasmLazyCompile");
|
||||
compiler::WasmCompilationUnit unit(isolate, &module_env, body,
|
||||
CStrVector(func_name.c_str()), func_index,
|
||||
CEntryStub(isolate, 1).GetCode());
|
||||
unit.ExecuteCompilation();
|
||||
MaybeHandle<Code> maybe_code = unit.FinishCompilation(&thrower);
|
||||
|
||||
// If there is a pending error, something really went wrong. The module was
|
||||
// verified before starting execution with lazy compilation.
|
||||
// This might be OOM, but then we cannot continue execution anyway.
|
||||
// TODO(clemensh): According to the spec, we can actually skip validation at
|
||||
// module creation time, and return a function that always traps here.
|
||||
CHECK(!thrower.error());
|
||||
Handle<Code> code = maybe_code.ToHandleChecked();
|
||||
|
||||
Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(2, TENURED);
|
||||
Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
|
||||
// TODO(wasm): Introduce constants for the indexes in wasm deopt data.
|
||||
deopt_data->set(0, *weak_instance);
|
||||
deopt_data->set(1, Smi::FromInt(func_index));
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
DCHECK_EQ(Builtins::kWasmCompileLazy,
|
||||
Code::cast(compiled_module->code_table()->get(func_index))
|
||||
->builtin_index());
|
||||
compiled_module->code_table()->set(func_index, *code);
|
||||
|
||||
// Now specialize the generated code for this instance.
|
||||
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
|
||||
CodeSpecialization code_specialization(isolate, &specialization_zone);
|
||||
code_specialization.RelocateDirectCalls(instance);
|
||||
code_specialization.ApplyToWasmCode(*code, SKIP_ICACHE_FLUSH);
|
||||
Assembler::FlushICache(isolate, code->instruction_start(),
|
||||
code->instruction_size());
|
||||
auto counters = isolate->counters();
|
||||
counters->wasm_lazily_compiled_functions()->Increment();
|
||||
counters->wasm_generated_code_size()->Increment(code->body_size());
|
||||
counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
|
||||
}
|
||||
|
||||
int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
|
||||
int offset) {
|
||||
DCHECK(!iterator.done());
|
||||
int byte_pos;
|
||||
do {
|
||||
byte_pos = iterator.source_position().ScriptOffset();
|
||||
iterator.Advance();
|
||||
} while (!iterator.done() && iterator.code_offset() <= offset);
|
||||
return byte_pos;
|
||||
}
|
||||
|
||||
Handle<Code> LazyCompilationOrchestrator::CompileLazy(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<Code> caller,
|
||||
int call_offset, int exported_func_index, bool patch_caller) {
|
||||
struct NonCompiledFunction {
|
||||
int offset;
|
||||
int func_index;
|
||||
};
|
||||
std::vector<NonCompiledFunction> non_compiled_functions;
|
||||
int func_to_return_idx = exported_func_index;
|
||||
Decoder decoder(nullptr, nullptr);
|
||||
bool is_js_to_wasm = caller->kind() == Code::JS_TO_WASM_FUNCTION;
|
||||
Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
|
||||
isolate);
|
||||
|
||||
if (is_js_to_wasm) {
|
||||
non_compiled_functions.push_back({0, exported_func_index});
|
||||
} else if (patch_caller) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
SeqOneByteString* module_bytes = compiled_module->module_bytes();
|
||||
SourcePositionTableIterator source_pos_iterator(
|
||||
caller->SourcePositionTable());
|
||||
DCHECK_EQ(2, caller->deoptimization_data()->length());
|
||||
int caller_func_index = Smi::ToInt(caller->deoptimization_data()->get(1));
|
||||
const byte* func_bytes =
|
||||
module_bytes->GetChars() +
|
||||
compiled_module->module()->functions[caller_func_index].code.offset();
|
||||
for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done();
|
||||
it.next()) {
|
||||
Code* callee =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
|
||||
// TODO(clemensh): Introduce safe_cast<T, bool> which (D)CHECKS
|
||||
// (depending on the bool) against limits of T and then static_casts.
|
||||
size_t offset_l = it.rinfo()->pc() - caller->instruction_start();
|
||||
DCHECK_GE(kMaxInt, offset_l);
|
||||
int offset = static_cast<int>(offset_l);
|
||||
int byte_pos =
|
||||
AdvanceSourcePositionTableIterator(source_pos_iterator, offset);
|
||||
int called_func_index =
|
||||
ExtractDirectCallIndex(decoder, func_bytes + byte_pos);
|
||||
non_compiled_functions.push_back({offset, called_func_index});
|
||||
// Call offset one instruction after the call. Remember the last called
|
||||
// function before that offset.
|
||||
if (offset < call_offset) func_to_return_idx = called_func_index;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(clemensh): compile all functions in non_compiled_functions in
|
||||
// background, wait for func_to_return_idx.
|
||||
CompileFunction(isolate, instance, func_to_return_idx);
|
||||
|
||||
if (is_js_to_wasm || patch_caller) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
// Now patch the code object with all functions which are now compiled.
|
||||
int idx = 0;
|
||||
for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done();
|
||||
it.next()) {
|
||||
Code* callee =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
|
||||
DCHECK_GT(non_compiled_functions.size(), idx);
|
||||
int called_func_index = non_compiled_functions[idx].func_index;
|
||||
// Check that the callee agrees with our assumed called_func_index.
|
||||
DCHECK_IMPLIES(callee->deoptimization_data()->length() > 0,
|
||||
Smi::ToInt(callee->deoptimization_data()->get(1)) ==
|
||||
called_func_index);
|
||||
if (is_js_to_wasm) {
|
||||
DCHECK_EQ(func_to_return_idx, called_func_index);
|
||||
} else {
|
||||
DCHECK_EQ(non_compiled_functions[idx].offset,
|
||||
it.rinfo()->pc() - caller->instruction_start());
|
||||
}
|
||||
++idx;
|
||||
Handle<Code> callee_compiled(
|
||||
Code::cast(compiled_module->code_table()->get(called_func_index)));
|
||||
if (callee_compiled->builtin_index() == Builtins::kWasmCompileLazy) {
|
||||
DCHECK_NE(func_to_return_idx, called_func_index);
|
||||
continue;
|
||||
}
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, callee_compiled->kind());
|
||||
it.rinfo()->set_target_address(isolate,
|
||||
callee_compiled->instruction_start());
|
||||
}
|
||||
DCHECK_EQ(non_compiled_functions.size(), idx);
|
||||
}
|
||||
|
||||
Code* ret =
|
||||
Code::cast(compiled_module->code_table()->get(func_to_return_idx));
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, ret->kind());
|
||||
return handle(ret, isolate);
|
||||
}
|
||||
|
||||
ModuleCompiler::CodeGenerationSchedule::CodeGenerationSchedule(
|
||||
base::RandomNumberGenerator* random_number_generator, size_t max_memory)
|
||||
: random_number_generator_(random_number_generator),
|
||||
@ -106,18 +959,6 @@ ModuleCompiler::ModuleCompiler(Isolate* isolate, WasmModule* module,
|
||||
centry_stub_(centry_stub) {}
|
||||
|
||||
// The actual runnable task that performs compilations in the background.
|
||||
ModuleCompiler::CompilationTask::CompilationTask(ModuleCompiler* compiler)
|
||||
: CancelableTask(&compiler->background_task_manager_),
|
||||
compiler_(compiler) {}
|
||||
|
||||
void ModuleCompiler::CompilationTask::RunInternal() {
|
||||
while (compiler_->executed_units_.CanAcceptWork() &&
|
||||
compiler_->FetchAndExecuteCompilationUnit()) {
|
||||
}
|
||||
|
||||
compiler_->OnBackgroundTaskStopped();
|
||||
}
|
||||
|
||||
void ModuleCompiler::OnBackgroundTaskStopped() {
|
||||
base::LockGuard<base::Mutex> guard(&tasks_mutex_);
|
||||
++stopped_compilation_tasks_;
|
||||
@ -733,38 +1574,6 @@ MaybeHandle<WasmModuleObject> ModuleCompiler::CompileToModuleObjectInternal(
|
||||
return WasmModuleObject::New(isolate_, compiled_module);
|
||||
}
|
||||
|
||||
Handle<Code> JSToWasmWrapperCache::CloneOrCompileJSToWasmWrapper(
|
||||
Isolate* isolate, wasm::WasmModule* module, Handle<Code> wasm_code,
|
||||
uint32_t index) {
|
||||
const wasm::WasmFunction* func = &module->functions[index];
|
||||
int cached_idx = sig_map_.Find(func->sig);
|
||||
if (cached_idx >= 0) {
|
||||
Handle<Code> code = isolate->factory()->CopyCode(code_cache_[cached_idx]);
|
||||
// Now patch the call to wasm code.
|
||||
for (RelocIterator it(*code, RelocInfo::kCodeTargetMask);; it.next()) {
|
||||
DCHECK(!it.done());
|
||||
Code* target =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (target->kind() == Code::WASM_FUNCTION ||
|
||||
target->kind() == Code::WASM_TO_JS_FUNCTION ||
|
||||
target->builtin_index() == Builtins::kIllegal ||
|
||||
target->builtin_index() == Builtins::kWasmCompileLazy) {
|
||||
it.rinfo()->set_target_address(isolate, wasm_code->instruction_start());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
Handle<Code> code =
|
||||
compiler::CompileJSToWasmWrapper(isolate, module, wasm_code, index);
|
||||
uint32_t new_cache_idx = sig_map_.FindOrInsert(func->sig);
|
||||
DCHECK_EQ(code_cache_.size(), new_cache_idx);
|
||||
USE(new_cache_idx);
|
||||
code_cache_.push_back(code);
|
||||
return code;
|
||||
}
|
||||
|
||||
InstanceBuilder::InstanceBuilder(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> ffi,
|
||||
@ -2065,6 +2874,42 @@ void AsyncCompileJob::Abort() {
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncStreamingProcessor final : public StreamingProcessor {
|
||||
public:
|
||||
explicit AsyncStreamingProcessor(AsyncCompileJob* job);
|
||||
|
||||
bool ProcessModuleHeader(Vector<const uint8_t> bytes,
|
||||
uint32_t offset) override;
|
||||
|
||||
bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
|
||||
uint32_t offset) override;
|
||||
|
||||
bool ProcessCodeSectionHeader(size_t functions_count,
|
||||
uint32_t offset) override;
|
||||
|
||||
bool ProcessFunctionBody(Vector<const uint8_t> bytes,
|
||||
uint32_t offset) override;
|
||||
|
||||
void OnFinishedChunk() override;
|
||||
|
||||
void OnFinishedStream(std::unique_ptr<uint8_t[]> bytes,
|
||||
size_t length) override;
|
||||
|
||||
void OnError(DecodeResult result) override;
|
||||
|
||||
void OnAbort() override;
|
||||
|
||||
private:
|
||||
// Finishes the AsyncCOmpileJob with an error.
|
||||
void FinishAsyncCompileJobWithError(ResultBase result);
|
||||
|
||||
ModuleDecoder decoder_;
|
||||
AsyncCompileJob* job_;
|
||||
std::unique_ptr<ModuleCompiler::CompilationUnitBuilder>
|
||||
compilation_unit_builder_;
|
||||
uint32_t next_function_ = 0;
|
||||
};
|
||||
|
||||
std::shared_ptr<StreamingDecoder> AsyncCompileJob::CreateStreamingDecoder() {
|
||||
DCHECK_NULL(stream_);
|
||||
stream_.reset(
|
||||
@ -2686,3 +3531,4 @@ void AsyncStreamingProcessor::OnAbort() {
|
||||
#undef TRACE_COMPILE
|
||||
#undef TRACE_STREAMING
|
||||
#undef TRACE_CHAIN
|
||||
#undef ERROR_THROWER_WITH_MESSAGE
|
||||
|
@ -8,313 +8,72 @@
|
||||
#include <functional>
|
||||
|
||||
#include "src/base/atomic-utils.h"
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/cancelable-task.h"
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/isolate.h"
|
||||
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/streaming-decoder.h"
|
||||
#include "src/wasm/wasm-code-specialization.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "src/wasm/wasm-objects.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
// A class compiling an entire module.
|
||||
class ModuleCompiler {
|
||||
class ModuleCompiler;
|
||||
|
||||
V8_EXPORT_PRIVATE bool SyncValidate(Isolate* isolate,
|
||||
const ModuleWireBytes& bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompile(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
|
||||
MaybeHandle<JSArrayBuffer> memory);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory);
|
||||
|
||||
V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
|
||||
const ModuleWireBytes& bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE void AsyncInstantiate(Isolate* isolate,
|
||||
Handle<JSPromise> promise,
|
||||
Handle<WasmModuleObject> module_object,
|
||||
MaybeHandle<JSReceiver> imports);
|
||||
|
||||
// Triggered by the WasmCompileLazy builtin.
|
||||
// Walks the stack (top three frames) to determine the wasm instance involved
|
||||
// and which function to compile.
|
||||
// Then triggers WasmCompiledModule::CompileLazy, taking care of correctly
|
||||
// patching the call site or indirect function tables.
|
||||
// Returns either the Code object that has been lazily compiled, or Illegal if
|
||||
// an error occurred. In the latter case, a pending exception has been set,
|
||||
// which will be triggered when returning from the runtime function, i.e. the
|
||||
// Illegal builtin will never be called.
|
||||
Handle<Code> CompileLazy(Isolate* isolate);
|
||||
|
||||
// This class orchestrates the lazy compilation of wasm functions. It is
|
||||
// triggered by the WasmCompileLazy builtin.
|
||||
// It contains the logic for compiling and specializing wasm functions, and
|
||||
// patching the calling wasm code.
|
||||
// Once we support concurrent lazy compilation, this class will contain the
|
||||
// logic to actually orchestrate parallel execution of wasm compilation jobs.
|
||||
// TODO(clemensh): Implement concurrent lazy compilation.
|
||||
class LazyCompilationOrchestrator {
|
||||
void CompileFunction(Isolate*, Handle<WasmInstanceObject>, int func_index);
|
||||
|
||||
public:
|
||||
ModuleCompiler(Isolate* isolate, WasmModule* module,
|
||||
Handle<Code> centry_stub);
|
||||
|
||||
// The actual runnable task that performs compilations in the background.
|
||||
class CompilationTask : public CancelableTask {
|
||||
public:
|
||||
ModuleCompiler* compiler_;
|
||||
explicit CompilationTask(ModuleCompiler*);
|
||||
|
||||
void RunInternal() override;
|
||||
};
|
||||
|
||||
// The CompilationUnitBuilder builds compilation units and stores them in an
|
||||
// internal buffer. The buffer is moved into the working queue of the
|
||||
// ModuleCompiler when {Commit} is called.
|
||||
class CompilationUnitBuilder {
|
||||
public:
|
||||
explicit CompilationUnitBuilder(ModuleCompiler* compiler)
|
||||
: compiler_(compiler) {}
|
||||
|
||||
~CompilationUnitBuilder() { DCHECK(units_.empty()); }
|
||||
|
||||
void AddUnit(compiler::ModuleEnv* module_env, const WasmFunction* function,
|
||||
uint32_t buffer_offset, Vector<const uint8_t> bytes,
|
||||
WasmName name) {
|
||||
units_.emplace_back(new compiler::WasmCompilationUnit(
|
||||
compiler_->isolate_, module_env,
|
||||
wasm::FunctionBody{function->sig, buffer_offset, bytes.begin(),
|
||||
bytes.end()},
|
||||
name, function->func_index, compiler_->centry_stub_,
|
||||
compiler_->counters()));
|
||||
}
|
||||
|
||||
void Commit() {
|
||||
{
|
||||
base::LockGuard<base::Mutex> guard(
|
||||
&compiler_->compilation_units_mutex_);
|
||||
compiler_->compilation_units_.insert(
|
||||
compiler_->compilation_units_.end(),
|
||||
std::make_move_iterator(units_.begin()),
|
||||
std::make_move_iterator(units_.end()));
|
||||
}
|
||||
units_.clear();
|
||||
}
|
||||
|
||||
void Clear() { units_.clear(); }
|
||||
|
||||
private:
|
||||
ModuleCompiler* compiler_;
|
||||
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> units_;
|
||||
};
|
||||
|
||||
class CodeGenerationSchedule {
|
||||
public:
|
||||
explicit CodeGenerationSchedule(
|
||||
base::RandomNumberGenerator* random_number_generator,
|
||||
size_t max_memory = 0);
|
||||
|
||||
void Schedule(std::unique_ptr<compiler::WasmCompilationUnit>&& item);
|
||||
|
||||
bool IsEmpty() const { return schedule_.empty(); }
|
||||
|
||||
std::unique_ptr<compiler::WasmCompilationUnit> GetNext();
|
||||
|
||||
bool CanAcceptWork() const;
|
||||
|
||||
bool ShouldIncreaseWorkload() const;
|
||||
|
||||
void EnableThrottling() { throttle_ = true; }
|
||||
|
||||
private:
|
||||
size_t GetRandomIndexInSchedule();
|
||||
|
||||
base::RandomNumberGenerator* random_number_generator_ = nullptr;
|
||||
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>> schedule_;
|
||||
const size_t max_memory_;
|
||||
bool throttle_ = false;
|
||||
base::AtomicNumber<size_t> allocated_memory_{0};
|
||||
};
|
||||
|
||||
Counters* counters() const { return async_counters_.get(); }
|
||||
|
||||
// Run by each compilation task and by the main thread (i.e. in both
|
||||
// foreground and background threads). The no_finisher_callback is called
|
||||
// within the result_mutex_ lock when no finishing task is running, i.e. when
|
||||
// the finisher_is_running_ flag is not set.
|
||||
bool FetchAndExecuteCompilationUnit(
|
||||
std::function<void()> no_finisher_callback = nullptr);
|
||||
|
||||
void OnBackgroundTaskStopped();
|
||||
|
||||
void EnableThrottling() { executed_units_.EnableThrottling(); }
|
||||
|
||||
bool CanAcceptWork() const { return executed_units_.CanAcceptWork(); }
|
||||
|
||||
bool ShouldIncreaseWorkload() const {
|
||||
return executed_units_.ShouldIncreaseWorkload();
|
||||
}
|
||||
|
||||
size_t InitializeCompilationUnits(const std::vector<WasmFunction>& functions,
|
||||
const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env);
|
||||
|
||||
void RestartCompilationTasks();
|
||||
|
||||
size_t FinishCompilationUnits(std::vector<Handle<Code>>& results,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
bool IsFinisherRunning() const { return finisher_is_running_; }
|
||||
|
||||
void SetFinisherIsRunning(bool value);
|
||||
|
||||
MaybeHandle<Code> FinishCompilationUnit(ErrorThrower* thrower,
|
||||
int* func_index);
|
||||
|
||||
void CompileInParallel(const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env,
|
||||
std::vector<Handle<Code>>& results,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
void CompileSequentially(const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env,
|
||||
std::vector<Handle<Code>>& results,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
void ValidateSequentially(const ModuleWireBytes& wire_bytes,
|
||||
compiler::ModuleEnv* module_env,
|
||||
ErrorThrower* thrower);
|
||||
|
||||
static MaybeHandle<WasmModuleObject> CompileToModuleObject(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
std::unique_ptr<WasmModule> module, const ModuleWireBytes& wire_bytes,
|
||||
Handle<Script> asm_js_script,
|
||||
Vector<const byte> asm_js_offset_table_bytes);
|
||||
|
||||
private:
|
||||
MaybeHandle<WasmModuleObject> CompileToModuleObjectInternal(
|
||||
ErrorThrower* thrower, std::unique_ptr<WasmModule> module,
|
||||
const ModuleWireBytes& wire_bytes, Handle<Script> asm_js_script,
|
||||
Vector<const byte> asm_js_offset_table_bytes);
|
||||
|
||||
Isolate* isolate_;
|
||||
WasmModule* module_;
|
||||
const std::shared_ptr<Counters> async_counters_;
|
||||
std::vector<std::unique_ptr<compiler::WasmCompilationUnit>>
|
||||
compilation_units_;
|
||||
base::Mutex compilation_units_mutex_;
|
||||
CodeGenerationSchedule executed_units_;
|
||||
base::Mutex result_mutex_;
|
||||
const size_t num_background_tasks_;
|
||||
// This flag should only be set while holding result_mutex_.
|
||||
bool finisher_is_running_ = false;
|
||||
CancelableTaskManager background_task_manager_;
|
||||
size_t stopped_compilation_tasks_ = 0;
|
||||
base::Mutex tasks_mutex_;
|
||||
Handle<Code> centry_stub_;
|
||||
};
|
||||
|
||||
class JSToWasmWrapperCache {
|
||||
public:
|
||||
Handle<Code> CloneOrCompileJSToWasmWrapper(Isolate* isolate,
|
||||
wasm::WasmModule* module,
|
||||
Handle<Code> wasm_code,
|
||||
uint32_t index);
|
||||
|
||||
private:
|
||||
// sig_map_ maps signatures to an index in code_cache_.
|
||||
wasm::SignatureMap sig_map_;
|
||||
std::vector<Handle<Code>> code_cache_;
|
||||
};
|
||||
|
||||
// A helper class to simplify instantiating a module from a compiled module.
|
||||
// It closes over the {Isolate}, the {ErrorThrower}, the {WasmCompiledModule},
|
||||
// etc.
|
||||
class InstanceBuilder {
|
||||
public:
|
||||
InstanceBuilder(Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object,
|
||||
MaybeHandle<JSReceiver> ffi,
|
||||
MaybeHandle<JSArrayBuffer> memory,
|
||||
WeakCallbackInfo<void>::Callback instance_finalizer_callback);
|
||||
|
||||
// Build an instance, in all of its glory.
|
||||
MaybeHandle<WasmInstanceObject> Build();
|
||||
|
||||
private:
|
||||
// Represents the initialized state of a table.
|
||||
struct TableInstance {
|
||||
Handle<WasmTableObject> table_object; // WebAssembly.Table instance
|
||||
Handle<FixedArray> js_wrappers; // JSFunctions exported
|
||||
Handle<FixedArray> function_table; // internal code array
|
||||
Handle<FixedArray> signature_table; // internal sig array
|
||||
};
|
||||
|
||||
// A pre-evaluated value to use in import binding.
|
||||
struct SanitizedImport {
|
||||
Handle<String> module_name;
|
||||
Handle<String> import_name;
|
||||
Handle<Object> value;
|
||||
};
|
||||
|
||||
Isolate* isolate_;
|
||||
WasmModule* const module_;
|
||||
const std::shared_ptr<Counters> async_counters_;
|
||||
ErrorThrower* thrower_;
|
||||
Handle<WasmModuleObject> module_object_;
|
||||
MaybeHandle<JSReceiver> ffi_;
|
||||
MaybeHandle<JSArrayBuffer> memory_;
|
||||
Handle<JSArrayBuffer> globals_;
|
||||
Handle<WasmCompiledModule> compiled_module_;
|
||||
std::vector<TableInstance> table_instances_;
|
||||
std::vector<Handle<JSFunction>> js_wrappers_;
|
||||
JSToWasmWrapperCache js_to_wasm_cache_;
|
||||
WeakCallbackInfo<void>::Callback instance_finalizer_callback_;
|
||||
std::vector<SanitizedImport> sanitized_imports_;
|
||||
|
||||
const std::shared_ptr<Counters>& async_counters() const {
|
||||
return async_counters_;
|
||||
}
|
||||
Counters* counters() const { return async_counters().get(); }
|
||||
|
||||
// Helper routines to print out errors with imports.
|
||||
#define ERROR_THROWER_WITH_MESSAGE(TYPE) \
|
||||
void Report##TYPE(const char* error, uint32_t index, \
|
||||
Handle<String> module_name, Handle<String> import_name) { \
|
||||
thrower_->TYPE("Import #%d module=\"%s\" function=\"%s\" error: %s", \
|
||||
index, module_name->ToCString().get(), \
|
||||
import_name->ToCString().get(), error); \
|
||||
} \
|
||||
\
|
||||
MaybeHandle<Object> Report##TYPE(const char* error, uint32_t index, \
|
||||
Handle<String> module_name) { \
|
||||
thrower_->TYPE("Import #%d module=\"%s\" error: %s", index, \
|
||||
module_name->ToCString().get(), error); \
|
||||
return MaybeHandle<Object>(); \
|
||||
}
|
||||
|
||||
ERROR_THROWER_WITH_MESSAGE(LinkError)
|
||||
ERROR_THROWER_WITH_MESSAGE(TypeError)
|
||||
|
||||
// Look up an import value in the {ffi_} object.
|
||||
MaybeHandle<Object> LookupImport(uint32_t index, Handle<String> module_name,
|
||||
Handle<String> import_name);
|
||||
|
||||
// Look up an import value in the {ffi_} object specifically for linking an
|
||||
// asm.js module. This only performs non-observable lookups, which allows
|
||||
// falling back to JavaScript proper (and hence re-executing all lookups) if
|
||||
// module instantiation fails.
|
||||
MaybeHandle<Object> LookupImportAsm(uint32_t index,
|
||||
Handle<String> import_name);
|
||||
|
||||
uint32_t EvalUint32InitExpr(const WasmInitExpr& expr);
|
||||
|
||||
// Load data segments into the memory.
|
||||
void LoadDataSegments(Address mem_addr, size_t mem_size);
|
||||
|
||||
void WriteGlobalValue(WasmGlobal& global, Handle<Object> value);
|
||||
|
||||
void SanitizeImports();
|
||||
|
||||
Handle<FixedArray> SetupWasmToJSImportsTable(
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
// Process the imports, including functions, tables, globals, and memory, in
|
||||
// order, loading them from the {ffi_} object. Returns the number of imported
|
||||
// functions.
|
||||
int ProcessImports(Handle<FixedArray> code_table,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
|
||||
template <typename T>
|
||||
T* GetRawGlobalPtr(WasmGlobal& global);
|
||||
|
||||
// Process initialization of globals.
|
||||
void InitGlobals();
|
||||
|
||||
// Allocate memory for a module instance as a new JSArrayBuffer.
|
||||
Handle<JSArrayBuffer> AllocateMemory(uint32_t num_pages);
|
||||
|
||||
bool NeedsWrappers() const;
|
||||
|
||||
// Process the exports, creating wrappers for functions, tables, memories,
|
||||
// and globals.
|
||||
void ProcessExports(Handle<WasmInstanceObject> instance,
|
||||
Handle<WasmCompiledModule> compiled_module);
|
||||
|
||||
void InitializeTables(Handle<WasmInstanceObject> instance,
|
||||
CodeSpecialization* code_specialization);
|
||||
|
||||
void LoadTableSegments(Handle<FixedArray> code_table,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
Handle<Code> CompileLazy(Isolate*, Handle<WasmInstanceObject>,
|
||||
Handle<Code> caller, int call_offset,
|
||||
int exported_func_index, bool patch_caller);
|
||||
};
|
||||
|
||||
// Encapsulates all the state and steps of an asynchronous compilation.
|
||||
@ -431,42 +190,6 @@ class AsyncCompileJob {
|
||||
std::shared_ptr<StreamingDecoder> stream_;
|
||||
};
|
||||
|
||||
class AsyncStreamingProcessor final : public StreamingProcessor {
|
||||
public:
|
||||
explicit AsyncStreamingProcessor(AsyncCompileJob* job);
|
||||
|
||||
bool ProcessModuleHeader(Vector<const uint8_t> bytes,
|
||||
uint32_t offset) override;
|
||||
|
||||
bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
|
||||
uint32_t offset) override;
|
||||
|
||||
bool ProcessCodeSectionHeader(size_t functions_count,
|
||||
uint32_t offset) override;
|
||||
|
||||
bool ProcessFunctionBody(Vector<const uint8_t> bytes,
|
||||
uint32_t offset) override;
|
||||
|
||||
void OnFinishedChunk() override;
|
||||
|
||||
void OnFinishedStream(std::unique_ptr<uint8_t[]> bytes,
|
||||
size_t length) override;
|
||||
|
||||
void OnError(DecodeResult result) override;
|
||||
|
||||
void OnAbort() override;
|
||||
|
||||
private:
|
||||
// Finishes the AsyncCOmpileJob with an error.
|
||||
void FinishAsyncCompileJobWithError(ResultBase result);
|
||||
|
||||
ModuleDecoder decoder_;
|
||||
AsyncCompileJob* job_;
|
||||
std::unique_ptr<ModuleCompiler::CompilationUnitBuilder>
|
||||
compilation_unit_builder_;
|
||||
uint32_t next_function_ = 0;
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "src/objects.h"
|
||||
#include "src/parsing/parse-info.h"
|
||||
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-api.h"
|
||||
#include "src/wasm/wasm-js.h"
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "src/snapshot/snapshot.h"
|
||||
#include "src/v8.h"
|
||||
|
||||
#include "src/compiler/wasm-compiler.h"
|
||||
#include "src/wasm/compilation-manager.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
@ -90,156 +91,6 @@ void* TryAllocateBackingStore(Isolate* isolate, size_t size,
|
||||
}
|
||||
}
|
||||
|
||||
static void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter());
|
||||
WasmInstanceObject* owner = reinterpret_cast<WasmInstanceObject*>(*p);
|
||||
Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate());
|
||||
// If a link to shared memory instances exists, update the list of memory
|
||||
// instances before the instance is destroyed.
|
||||
WasmCompiledModule* compiled_module = owner->compiled_module();
|
||||
TRACE("Finalizing %d {\n", compiled_module->instance_id());
|
||||
DCHECK(compiled_module->has_weak_wasm_module());
|
||||
WeakCell* weak_wasm_module = compiled_module->ptr_to_weak_wasm_module();
|
||||
|
||||
if (trap_handler::UseTrapHandler()) {
|
||||
Handle<FixedArray> code_table = compiled_module->code_table();
|
||||
for (int i = 0; i < code_table->length(); ++i) {
|
||||
Handle<Code> code = code_table->GetValueChecked<Code>(isolate, i);
|
||||
int index = code->trap_handler_index()->value();
|
||||
if (index >= 0) {
|
||||
trap_handler::ReleaseHandlerData(index);
|
||||
code->set_trap_handler_index(Smi::FromInt(trap_handler::kInvalidIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since the order of finalizers is not guaranteed, it can be the case
|
||||
// that {instance->compiled_module()->module()}, which is a
|
||||
// {Managed<WasmModule>} has been collected earlier in this GC cycle.
|
||||
// Weak references to this instance won't be cleared until
|
||||
// the next GC cycle, so we need to manually break some links (such as
|
||||
// the weak references from {WasmMemoryObject::instances}.
|
||||
if (owner->has_memory_object()) {
|
||||
Handle<WasmMemoryObject> memory(owner->memory_object(), isolate);
|
||||
Handle<WasmInstanceObject> instance(owner, isolate);
|
||||
WasmMemoryObject::RemoveInstance(isolate, memory, instance);
|
||||
}
|
||||
|
||||
// weak_wasm_module may have been cleared, meaning the module object
|
||||
// was GC-ed. In that case, there won't be any new instances created,
|
||||
// and we don't need to maintain the links between instances.
|
||||
if (!weak_wasm_module->cleared()) {
|
||||
WasmModuleObject* wasm_module =
|
||||
WasmModuleObject::cast(weak_wasm_module->value());
|
||||
WasmCompiledModule* current_template = wasm_module->compiled_module();
|
||||
|
||||
TRACE("chain before {\n");
|
||||
TRACE_CHAIN(current_template);
|
||||
TRACE("}\n");
|
||||
|
||||
DCHECK(!current_template->has_weak_prev_instance());
|
||||
WeakCell* next = compiled_module->maybe_ptr_to_weak_next_instance();
|
||||
WeakCell* prev = compiled_module->maybe_ptr_to_weak_prev_instance();
|
||||
|
||||
if (current_template == compiled_module) {
|
||||
if (next == nullptr) {
|
||||
WasmCompiledModule::Reset(isolate, compiled_module);
|
||||
} else {
|
||||
WasmCompiledModule* next_compiled_module =
|
||||
WasmCompiledModule::cast(next->value());
|
||||
WasmModuleObject::cast(wasm_module)
|
||||
->set_compiled_module(next_compiled_module);
|
||||
DCHECK_NULL(prev);
|
||||
next_compiled_module->reset_weak_prev_instance();
|
||||
}
|
||||
} else {
|
||||
DCHECK(!(prev == nullptr && next == nullptr));
|
||||
// the only reason prev or next would be cleared is if the
|
||||
// respective objects got collected, but if that happened,
|
||||
// we would have relinked the list.
|
||||
if (prev != nullptr) {
|
||||
DCHECK(!prev->cleared());
|
||||
if (next == nullptr) {
|
||||
WasmCompiledModule::cast(prev->value())->reset_weak_next_instance();
|
||||
} else {
|
||||
WasmCompiledModule::cast(prev->value())
|
||||
->set_ptr_to_weak_next_instance(next);
|
||||
}
|
||||
}
|
||||
if (next != nullptr) {
|
||||
DCHECK(!next->cleared());
|
||||
if (prev == nullptr) {
|
||||
WasmCompiledModule::cast(next->value())->reset_weak_prev_instance();
|
||||
} else {
|
||||
WasmCompiledModule::cast(next->value())
|
||||
->set_ptr_to_weak_prev_instance(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("chain after {\n");
|
||||
TRACE_CHAIN(wasm_module->compiled_module());
|
||||
TRACE("}\n");
|
||||
}
|
||||
compiled_module->reset_weak_owning_instance();
|
||||
GlobalHandles::Destroy(reinterpret_cast<Object**>(p));
|
||||
TRACE("}\n");
|
||||
}
|
||||
|
||||
int AdvanceSourcePositionTableIterator(SourcePositionTableIterator& iterator,
|
||||
int offset) {
|
||||
DCHECK(!iterator.done());
|
||||
int byte_pos;
|
||||
do {
|
||||
byte_pos = iterator.source_position().ScriptOffset();
|
||||
iterator.Advance();
|
||||
} while (!iterator.done() && iterator.code_offset() <= offset);
|
||||
return byte_pos;
|
||||
}
|
||||
|
||||
void RecordLazyCodeStats(Code* code, Counters* counters) {
|
||||
counters->wasm_lazily_compiled_functions()->Increment();
|
||||
counters->wasm_generated_code_size()->Increment(code->body_size());
|
||||
counters->wasm_reloc_size()->Increment(code->relocation_info()->length());
|
||||
}
|
||||
|
||||
compiler::ModuleEnv CreateModuleEnvFromCompiledModule(
|
||||
Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
WasmModule* module = compiled_module->module();
|
||||
|
||||
std::vector<GlobalHandleAddress> function_tables;
|
||||
std::vector<GlobalHandleAddress> signature_tables;
|
||||
std::vector<SignatureMap*> signature_maps;
|
||||
|
||||
int num_function_tables = static_cast<int>(module->function_tables.size());
|
||||
for (int i = 0; i < num_function_tables; ++i) {
|
||||
FixedArray* ft = compiled_module->ptr_to_function_tables();
|
||||
FixedArray* st = compiled_module->ptr_to_signature_tables();
|
||||
|
||||
// TODO(clemensh): defer these handles for concurrent compilation.
|
||||
function_tables.push_back(WasmCompiledModule::GetTableValue(ft, i));
|
||||
signature_tables.push_back(WasmCompiledModule::GetTableValue(st, i));
|
||||
signature_maps.push_back(&module->function_tables[i].map);
|
||||
}
|
||||
|
||||
std::vector<Handle<Code>> empty_code;
|
||||
|
||||
compiler::ModuleEnv result = {
|
||||
module, // --
|
||||
function_tables, // --
|
||||
signature_tables, // --
|
||||
signature_maps, // --
|
||||
empty_code, // --
|
||||
BUILTIN_CODE(isolate, WasmCompileLazy), // --
|
||||
reinterpret_cast<uintptr_t>( // --
|
||||
compiled_module->GetEmbeddedMemStartOrNull()), // --
|
||||
compiled_module->GetEmbeddedMemSizeOrZero(), // --
|
||||
reinterpret_cast<uintptr_t>( // --
|
||||
compiled_module->GetGlobalsStartOrNull()) // --
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -741,376 +592,6 @@ Handle<FixedArray> DecodeLocalNames(
|
||||
return locals_names;
|
||||
}
|
||||
|
||||
bool SyncValidate(Isolate* isolate, const ModuleWireBytes& bytes) {
|
||||
if (bytes.start() == nullptr || bytes.length() == 0) return false;
|
||||
ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(),
|
||||
bytes.end(), true, kWasmOrigin);
|
||||
return result.ok();
|
||||
}
|
||||
|
||||
MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
Handle<Script> asm_js_script,
|
||||
Vector<const byte> asm_js_offset_table_bytes) {
|
||||
ModuleResult result = SyncDecodeWasmModule(isolate, bytes.start(),
|
||||
bytes.end(), false, kAsmJsOrigin);
|
||||
if (result.failed()) {
|
||||
thrower->CompileFailed("Wasm decoding failed", result);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Transfer ownership of the WasmModule to the {WasmModuleWrapper} generated
|
||||
// in {CompileToModuleObject}.
|
||||
return ModuleCompiler::CompileToModuleObject(
|
||||
isolate, thrower, std::move(result.val), bytes, asm_js_script,
|
||||
asm_js_offset_table_bytes);
|
||||
}
|
||||
|
||||
MaybeHandle<WasmModuleObject> SyncCompile(Isolate* isolate,
|
||||
ErrorThrower* thrower,
|
||||
const ModuleWireBytes& bytes) {
|
||||
if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) {
|
||||
thrower->CompileError("Wasm code generation disallowed in this context");
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO(titzer): only make a copy of the bytes if SharedArrayBuffer
|
||||
std::unique_ptr<byte[]> copy(new byte[bytes.length()]);
|
||||
memcpy(copy.get(), bytes.start(), bytes.length());
|
||||
ModuleWireBytes bytes_copy(copy.get(), copy.get() + bytes.length());
|
||||
|
||||
ModuleResult result = SyncDecodeWasmModule(
|
||||
isolate, bytes_copy.start(), bytes_copy.end(), false, kWasmOrigin);
|
||||
if (result.failed()) {
|
||||
thrower->CompileFailed("Wasm decoding failed", result);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Transfer ownership of the WasmModule to the {WasmModuleWrapper} generated
|
||||
// in {CompileToModuleObject}.
|
||||
return ModuleCompiler::CompileToModuleObject(
|
||||
isolate, thrower, std::move(result.val), bytes_copy, Handle<Script>(),
|
||||
Vector<const byte>());
|
||||
}
|
||||
|
||||
MaybeHandle<WasmInstanceObject> SyncInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
|
||||
MaybeHandle<JSArrayBuffer> memory) {
|
||||
InstanceBuilder builder(isolate, thrower, module_object, imports, memory,
|
||||
&InstanceFinalizer);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory) {
|
||||
MaybeHandle<WasmModuleObject> module = SyncCompile(isolate, thrower, bytes);
|
||||
DCHECK_EQ(thrower->error(), module.is_null());
|
||||
if (module.is_null()) return {};
|
||||
|
||||
return SyncInstantiate(isolate, thrower, module.ToHandleChecked(),
|
||||
Handle<JSReceiver>::null(),
|
||||
Handle<JSArrayBuffer>::null());
|
||||
}
|
||||
|
||||
void RejectPromise(Isolate* isolate, Handle<Context> context,
|
||||
ErrorThrower& thrower, Handle<JSPromise> promise) {
|
||||
Local<Promise::Resolver> resolver =
|
||||
Utils::PromiseToLocal(promise).As<Promise::Resolver>();
|
||||
auto maybe = resolver->Reject(Utils::ToLocal(context),
|
||||
Utils::ToLocal(thrower.Reify()));
|
||||
CHECK_IMPLIES(!maybe.FromMaybe(false), isolate->has_scheduled_exception());
|
||||
}
|
||||
|
||||
void ResolvePromise(Isolate* isolate, Handle<Context> context,
|
||||
Handle<JSPromise> promise, Handle<Object> result) {
|
||||
Local<Promise::Resolver> resolver =
|
||||
Utils::PromiseToLocal(promise).As<Promise::Resolver>();
|
||||
auto maybe =
|
||||
resolver->Resolve(Utils::ToLocal(context), Utils::ToLocal(result));
|
||||
CHECK_IMPLIES(!maybe.FromMaybe(false), isolate->has_scheduled_exception());
|
||||
}
|
||||
|
||||
void AsyncInstantiate(Isolate* isolate, Handle<JSPromise> promise,
|
||||
Handle<WasmModuleObject> module_object,
|
||||
MaybeHandle<JSReceiver> imports) {
|
||||
ErrorThrower thrower(isolate, nullptr);
|
||||
MaybeHandle<WasmInstanceObject> instance_object = SyncInstantiate(
|
||||
isolate, &thrower, module_object, imports, Handle<JSArrayBuffer>::null());
|
||||
if (thrower.error()) {
|
||||
RejectPromise(isolate, handle(isolate->context()), thrower, promise);
|
||||
return;
|
||||
}
|
||||
ResolvePromise(isolate, handle(isolate->context()), promise,
|
||||
instance_object.ToHandleChecked());
|
||||
}
|
||||
|
||||
void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
|
||||
const ModuleWireBytes& bytes) {
|
||||
if (!FLAG_wasm_async_compilation) {
|
||||
ErrorThrower thrower(isolate, "WasmCompile");
|
||||
// Compile the module.
|
||||
MaybeHandle<WasmModuleObject> module_object =
|
||||
SyncCompile(isolate, &thrower, bytes);
|
||||
if (thrower.error()) {
|
||||
RejectPromise(isolate, handle(isolate->context()), thrower, promise);
|
||||
return;
|
||||
}
|
||||
Handle<WasmModuleObject> module = module_object.ToHandleChecked();
|
||||
ResolvePromise(isolate, handle(isolate->context()), promise, module);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FLAG_wasm_test_streaming) {
|
||||
std::shared_ptr<StreamingDecoder> streaming_decoder =
|
||||
isolate->wasm_compilation_manager()->StartStreamingCompilation(
|
||||
isolate, handle(isolate->context()), promise);
|
||||
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());
|
||||
isolate->wasm_compilation_manager()->StartAsyncCompileJob(
|
||||
isolate, std::move(copy), bytes.length(), handle(isolate->context()),
|
||||
promise);
|
||||
}
|
||||
|
||||
Handle<Code> CompileLazy(Isolate* isolate) {
|
||||
HistogramTimerScope lazy_time_scope(
|
||||
isolate->counters()->wasm_lazy_compilation_time());
|
||||
|
||||
// Find the wasm frame which triggered the lazy compile, to get the wasm
|
||||
// instance.
|
||||
StackFrameIterator it(isolate);
|
||||
// First frame: C entry stub.
|
||||
DCHECK(!it.done());
|
||||
DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
|
||||
it.Advance();
|
||||
// Second frame: WasmCompileLazy builtin.
|
||||
DCHECK(!it.done());
|
||||
Handle<Code> lazy_compile_code(it.frame()->LookupCode(), isolate);
|
||||
DCHECK_EQ(Builtins::kWasmCompileLazy, lazy_compile_code->builtin_index());
|
||||
Handle<WasmInstanceObject> instance;
|
||||
Handle<FixedArray> exp_deopt_data;
|
||||
int func_index = -1;
|
||||
if (lazy_compile_code->deoptimization_data()->length() > 0) {
|
||||
// Then it's an indirect call or via JS->wasm wrapper.
|
||||
DCHECK_LE(2, lazy_compile_code->deoptimization_data()->length());
|
||||
exp_deopt_data = handle(lazy_compile_code->deoptimization_data(), isolate);
|
||||
auto* weak_cell = WeakCell::cast(exp_deopt_data->get(0));
|
||||
instance = handle(WasmInstanceObject::cast(weak_cell->value()), isolate);
|
||||
func_index = Smi::ToInt(exp_deopt_data->get(1));
|
||||
}
|
||||
it.Advance();
|
||||
// Third frame: The calling wasm code or js-to-wasm wrapper.
|
||||
DCHECK(!it.done());
|
||||
DCHECK(it.frame()->is_js_to_wasm() || it.frame()->is_wasm_compiled());
|
||||
Handle<Code> caller_code = handle(it.frame()->LookupCode(), isolate);
|
||||
if (it.frame()->is_js_to_wasm()) {
|
||||
DCHECK(!instance.is_null());
|
||||
} else if (instance.is_null()) {
|
||||
// Then this is a direct call (otherwise we would have attached the instance
|
||||
// via deopt data to the lazy compile stub). Just use the instance of the
|
||||
// caller.
|
||||
instance =
|
||||
handle(WasmInstanceObject::GetOwningInstance(*caller_code), isolate);
|
||||
}
|
||||
int offset =
|
||||
static_cast<int>(it.frame()->pc() - caller_code->instruction_start());
|
||||
// Only patch the caller code if this is *no* indirect call.
|
||||
// exp_deopt_data will be null if the called function is not exported at all,
|
||||
// and its length will be <= 2 if all entries in tables were already patched.
|
||||
// Note that this check is conservative: If the first call to an exported
|
||||
// function is direct, we will just patch the export tables, and only on the
|
||||
// second call we will patch the caller.
|
||||
bool patch_caller = caller_code->kind() == Code::JS_TO_WASM_FUNCTION ||
|
||||
exp_deopt_data.is_null() || exp_deopt_data->length() <= 2;
|
||||
|
||||
Handle<Code> compiled_code = WasmCompiledModule::CompileLazy(
|
||||
isolate, instance, caller_code, offset, func_index, patch_caller);
|
||||
if (!exp_deopt_data.is_null() && exp_deopt_data->length() > 2) {
|
||||
// See EnsureExportedLazyDeoptData: exp_deopt_data[2...(len-1)] are pairs of
|
||||
// <export_table, index> followed by undefined values.
|
||||
// Use this information here to patch all export tables.
|
||||
DCHECK_EQ(0, exp_deopt_data->length() % 2);
|
||||
for (int idx = 2, end = exp_deopt_data->length(); idx < end; idx += 2) {
|
||||
if (exp_deopt_data->get(idx)->IsUndefined(isolate)) break;
|
||||
FixedArray* exp_table = FixedArray::cast(exp_deopt_data->get(idx));
|
||||
int exp_index = Smi::ToInt(exp_deopt_data->get(idx + 1));
|
||||
DCHECK(exp_table->get(exp_index) == *lazy_compile_code);
|
||||
exp_table->set(exp_index, *compiled_code);
|
||||
}
|
||||
// After processing, remove the list of exported entries, such that we don't
|
||||
// do the patching redundantly.
|
||||
Handle<FixedArray> new_deopt_data =
|
||||
isolate->factory()->CopyFixedArrayUpTo(exp_deopt_data, 2, TENURED);
|
||||
lazy_compile_code->set_deoptimization_data(*new_deopt_data);
|
||||
}
|
||||
|
||||
return compiled_code;
|
||||
}
|
||||
|
||||
void LazyCompilationOrchestrator::CompileFunction(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index) {
|
||||
Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
|
||||
isolate);
|
||||
if (Code::cast(compiled_module->code_table()->get(func_index))->kind() ==
|
||||
Code::WASM_FUNCTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
compiler::ModuleEnv module_env =
|
||||
CreateModuleEnvFromCompiledModule(isolate, compiled_module);
|
||||
|
||||
const uint8_t* module_start = compiled_module->module_bytes()->GetChars();
|
||||
|
||||
const WasmFunction* func = &module_env.module->functions[func_index];
|
||||
FunctionBody body{func->sig, func->code.offset(),
|
||||
module_start + func->code.offset(),
|
||||
module_start + func->code.end_offset()};
|
||||
// TODO(wasm): Refactor this to only get the name if it is really needed for
|
||||
// tracing / debugging.
|
||||
std::string func_name;
|
||||
{
|
||||
WasmName name = Vector<const char>::cast(
|
||||
compiled_module->GetRawFunctionName(func_index));
|
||||
// Copy to std::string, because the underlying string object might move on
|
||||
// the heap.
|
||||
func_name.assign(name.start(), static_cast<size_t>(name.length()));
|
||||
}
|
||||
ErrorThrower thrower(isolate, "WasmLazyCompile");
|
||||
compiler::WasmCompilationUnit unit(isolate, &module_env, body,
|
||||
CStrVector(func_name.c_str()), func_index,
|
||||
CEntryStub(isolate, 1).GetCode());
|
||||
unit.ExecuteCompilation();
|
||||
MaybeHandle<Code> maybe_code = unit.FinishCompilation(&thrower);
|
||||
|
||||
// If there is a pending error, something really went wrong. The module was
|
||||
// verified before starting execution with lazy compilation.
|
||||
// This might be OOM, but then we cannot continue execution anyway.
|
||||
// TODO(clemensh): According to the spec, we can actually skip validation at
|
||||
// module creation time, and return a function that always traps here.
|
||||
CHECK(!thrower.error());
|
||||
Handle<Code> code = maybe_code.ToHandleChecked();
|
||||
|
||||
Handle<FixedArray> deopt_data = isolate->factory()->NewFixedArray(2, TENURED);
|
||||
Handle<WeakCell> weak_instance = isolate->factory()->NewWeakCell(instance);
|
||||
// TODO(wasm): Introduce constants for the indexes in wasm deopt data.
|
||||
deopt_data->set(0, *weak_instance);
|
||||
deopt_data->set(1, Smi::FromInt(func_index));
|
||||
code->set_deoptimization_data(*deopt_data);
|
||||
|
||||
DCHECK_EQ(Builtins::kWasmCompileLazy,
|
||||
Code::cast(compiled_module->code_table()->get(func_index))
|
||||
->builtin_index());
|
||||
compiled_module->code_table()->set(func_index, *code);
|
||||
|
||||
// Now specialize the generated code for this instance.
|
||||
Zone specialization_zone(isolate->allocator(), ZONE_NAME);
|
||||
CodeSpecialization code_specialization(isolate, &specialization_zone);
|
||||
code_specialization.RelocateDirectCalls(instance);
|
||||
code_specialization.ApplyToWasmCode(*code, SKIP_ICACHE_FLUSH);
|
||||
Assembler::FlushICache(isolate, code->instruction_start(),
|
||||
code->instruction_size());
|
||||
RecordLazyCodeStats(*code, isolate->counters());
|
||||
}
|
||||
|
||||
Handle<Code> LazyCompilationOrchestrator::CompileLazy(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<Code> caller,
|
||||
int call_offset, int exported_func_index, bool patch_caller) {
|
||||
struct NonCompiledFunction {
|
||||
int offset;
|
||||
int func_index;
|
||||
};
|
||||
std::vector<NonCompiledFunction> non_compiled_functions;
|
||||
int func_to_return_idx = exported_func_index;
|
||||
Decoder decoder(nullptr, nullptr);
|
||||
bool is_js_to_wasm = caller->kind() == Code::JS_TO_WASM_FUNCTION;
|
||||
Handle<WasmCompiledModule> compiled_module(instance->compiled_module(),
|
||||
isolate);
|
||||
|
||||
if (is_js_to_wasm) {
|
||||
non_compiled_functions.push_back({0, exported_func_index});
|
||||
} else if (patch_caller) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
SeqOneByteString* module_bytes = compiled_module->module_bytes();
|
||||
SourcePositionTableIterator source_pos_iterator(
|
||||
caller->SourcePositionTable());
|
||||
DCHECK_EQ(2, caller->deoptimization_data()->length());
|
||||
int caller_func_index = Smi::ToInt(caller->deoptimization_data()->get(1));
|
||||
const byte* func_bytes =
|
||||
module_bytes->GetChars() +
|
||||
compiled_module->module()->functions[caller_func_index].code.offset();
|
||||
for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done();
|
||||
it.next()) {
|
||||
Code* callee =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
|
||||
// TODO(clemensh): Introduce safe_cast<T, bool> which (D)CHECKS
|
||||
// (depending on the bool) against limits of T and then static_casts.
|
||||
size_t offset_l = it.rinfo()->pc() - caller->instruction_start();
|
||||
DCHECK_GE(kMaxInt, offset_l);
|
||||
int offset = static_cast<int>(offset_l);
|
||||
int byte_pos =
|
||||
AdvanceSourcePositionTableIterator(source_pos_iterator, offset);
|
||||
int called_func_index =
|
||||
ExtractDirectCallIndex(decoder, func_bytes + byte_pos);
|
||||
non_compiled_functions.push_back({offset, called_func_index});
|
||||
// Call offset one instruction after the call. Remember the last called
|
||||
// function before that offset.
|
||||
if (offset < call_offset) func_to_return_idx = called_func_index;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(clemensh): compile all functions in non_compiled_functions in
|
||||
// background, wait for func_to_return_idx.
|
||||
CompileFunction(isolate, instance, func_to_return_idx);
|
||||
|
||||
if (is_js_to_wasm || patch_caller) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
// Now patch the code object with all functions which are now compiled.
|
||||
int idx = 0;
|
||||
for (RelocIterator it(*caller, RelocInfo::kCodeTargetMask); !it.done();
|
||||
it.next()) {
|
||||
Code* callee =
|
||||
Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
|
||||
if (callee->builtin_index() != Builtins::kWasmCompileLazy) continue;
|
||||
DCHECK_GT(non_compiled_functions.size(), idx);
|
||||
int called_func_index = non_compiled_functions[idx].func_index;
|
||||
// Check that the callee agrees with our assumed called_func_index.
|
||||
DCHECK_IMPLIES(callee->deoptimization_data()->length() > 0,
|
||||
Smi::ToInt(callee->deoptimization_data()->get(1)) ==
|
||||
called_func_index);
|
||||
if (is_js_to_wasm) {
|
||||
DCHECK_EQ(func_to_return_idx, called_func_index);
|
||||
} else {
|
||||
DCHECK_EQ(non_compiled_functions[idx].offset,
|
||||
it.rinfo()->pc() - caller->instruction_start());
|
||||
}
|
||||
++idx;
|
||||
Handle<Code> callee_compiled(
|
||||
Code::cast(compiled_module->code_table()->get(called_func_index)));
|
||||
if (callee_compiled->builtin_index() == Builtins::kWasmCompileLazy) {
|
||||
DCHECK_NE(func_to_return_idx, called_func_index);
|
||||
continue;
|
||||
}
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, callee_compiled->kind());
|
||||
it.rinfo()->set_target_address(isolate,
|
||||
callee_compiled->instruction_start());
|
||||
}
|
||||
DCHECK_EQ(non_compiled_functions.size(), idx);
|
||||
}
|
||||
|
||||
Code* ret =
|
||||
Code::cast(compiled_module->code_table()->get(func_to_return_idx));
|
||||
DCHECK_EQ(Code::WASM_FUNCTION, ret->kind());
|
||||
return handle(ret, isolate);
|
||||
}
|
||||
|
||||
const char* ExternalKindName(WasmExternalKind kind) {
|
||||
switch (kind) {
|
||||
case kExternalFunction:
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/managed.h"
|
||||
#include "src/parsing/preparse-data.h"
|
||||
|
||||
#include "src/wasm/decoder.h"
|
||||
#include "src/wasm/signature-map.h"
|
||||
#include "src/wasm/wasm-opcodes.h"
|
||||
|
||||
@ -40,27 +41,6 @@ enum WasmExternalKind {
|
||||
kExternalGlobal = 3
|
||||
};
|
||||
|
||||
// Reference to a string in the wire bytes.
|
||||
class WireBytesRef {
|
||||
public:
|
||||
WireBytesRef() : WireBytesRef(0, 0) {}
|
||||
WireBytesRef(uint32_t offset, uint32_t length)
|
||||
: offset_(offset), length_(length) {
|
||||
DCHECK_IMPLIES(offset_ == 0, length_ == 0);
|
||||
DCHECK_LE(offset_, offset_ + length_); // no uint32_t overflow.
|
||||
}
|
||||
|
||||
uint32_t offset() const { return offset_; }
|
||||
uint32_t length() const { return length_; }
|
||||
uint32_t end_offset() const { return offset_ + length_; }
|
||||
bool is_empty() const { return length_ == 0; }
|
||||
bool is_set() const { return offset_ != 0; }
|
||||
|
||||
private:
|
||||
uint32_t offset_;
|
||||
uint32_t length_;
|
||||
};
|
||||
|
||||
// Static representation of a wasm function.
|
||||
struct WasmFunction {
|
||||
FunctionSig* sig; // signature of the function.
|
||||
@ -320,36 +300,6 @@ Handle<Code> UnwrapExportWrapper(Handle<JSFunction> export_wrapper);
|
||||
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
|
||||
int index, WasmFunction* function, Handle<Code> code);
|
||||
|
||||
//============================================================================
|
||||
//== Compilation and instantiation ===========================================
|
||||
//============================================================================
|
||||
V8_EXPORT_PRIVATE bool SyncValidate(Isolate* isolate,
|
||||
const ModuleWireBytes& bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompileTranslatedAsmJs(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
Handle<Script> asm_js_script, Vector<const byte> asm_js_offset_table_bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> SyncCompile(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower,
|
||||
Handle<WasmModuleObject> module_object, MaybeHandle<JSReceiver> imports,
|
||||
MaybeHandle<JSArrayBuffer> memory);
|
||||
|
||||
V8_EXPORT_PRIVATE MaybeHandle<WasmInstanceObject> SyncCompileAndInstantiate(
|
||||
Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes,
|
||||
MaybeHandle<JSReceiver> imports, MaybeHandle<JSArrayBuffer> memory);
|
||||
|
||||
V8_EXPORT_PRIVATE void AsyncCompile(Isolate* isolate, Handle<JSPromise> promise,
|
||||
const ModuleWireBytes& bytes);
|
||||
|
||||
V8_EXPORT_PRIVATE void AsyncInstantiate(Isolate* isolate,
|
||||
Handle<JSPromise> promise,
|
||||
Handle<WasmModuleObject> module_object,
|
||||
MaybeHandle<JSReceiver> imports);
|
||||
|
||||
#if V8_TARGET_ARCH_64_BIT
|
||||
const bool kGuardRegionsSupported = true;
|
||||
#else
|
||||
@ -364,33 +314,6 @@ inline bool EnableGuardRegions() {
|
||||
void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
|
||||
Handle<FixedArray> code_table);
|
||||
|
||||
// Triggered by the WasmCompileLazy builtin.
|
||||
// Walks the stack (top three frames) to determine the wasm instance involved
|
||||
// and which function to compile.
|
||||
// Then triggers WasmCompiledModule::CompileLazy, taking care of correctly
|
||||
// patching the call site or indirect function tables.
|
||||
// Returns either the Code object that has been lazily compiled, or Illegal if
|
||||
// an error occurred. In the latter case, a pending exception has been set,
|
||||
// which will be triggered when returning from the runtime function, i.e. the
|
||||
// Illegal builtin will never be called.
|
||||
Handle<Code> CompileLazy(Isolate* isolate);
|
||||
|
||||
// This class orchestrates the lazy compilation of wasm functions. It is
|
||||
// triggered by the WasmCompileLazy builtin.
|
||||
// It contains the logic for compiling and specializing wasm functions, and
|
||||
// patching the calling wasm code.
|
||||
// Once we support concurrent lazy compilation, this class will contain the
|
||||
// logic to actually orchestrate parallel execution of wasm compilation jobs.
|
||||
// TODO(clemensh): Implement concurrent lazy compilation.
|
||||
class LazyCompilationOrchestrator {
|
||||
void CompileFunction(Isolate*, Handle<WasmInstanceObject>, int func_index);
|
||||
|
||||
public:
|
||||
Handle<Code> CompileLazy(Isolate*, Handle<WasmInstanceObject>,
|
||||
Handle<Code> caller, int call_offset,
|
||||
int exported_func_index, bool patch_caller);
|
||||
};
|
||||
|
||||
const char* ExternalKindName(WasmExternalKind);
|
||||
|
||||
// TruncatedUserString makes it easy to output names up to a certain length, and
|
||||
@ -438,12 +361,6 @@ void ValidateOrphanedInstance(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
} // namespace testing
|
||||
|
||||
void ResolvePromise(Isolate* isolate, Handle<Context> context,
|
||||
Handle<JSPromise> promise, Handle<Object> result);
|
||||
|
||||
void RejectPromise(Isolate* isolate, Handle<Context> context,
|
||||
ErrorThrower& thrower, Handle<JSPromise> promise);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "src/debug/debug-interface.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/debug-objects-inl.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-code-specialization.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
@ -7,11 +7,12 @@
|
||||
|
||||
#include "src/debug/debug.h"
|
||||
#include "src/debug/interface-types.h"
|
||||
#include "src/managed.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/objects/script.h"
|
||||
#include "src/trap-handler/trap-handler.h"
|
||||
#include "src/wasm/decoder.h"
|
||||
#include "src/wasm/wasm-limits.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
||||
#include "src/heap/heap.h"
|
||||
|
||||
@ -23,7 +24,11 @@ namespace internal {
|
||||
namespace wasm {
|
||||
class InterpretedFrame;
|
||||
class WasmInterpreter;
|
||||
struct WasmModule;
|
||||
class SignatureMap;
|
||||
typedef Address GlobalHandleAddress;
|
||||
using ValueType = MachineRepresentation;
|
||||
using FunctionSig = Signature<ValueType>;
|
||||
} // namespace wasm
|
||||
|
||||
class WasmCompiledModule;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/snapshot/code-serializer.h"
|
||||
#include "src/version.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-module-builder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/property-descriptor.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/wasm-interpreter.h"
|
||||
#include "src/wasm/wasm-js.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-api.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "test/common/wasm/flag-utils.h"
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "include/v8.h"
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-api.h"
|
||||
#include "src/wasm/wasm-module-builder.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "src/isolate.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/wasm-module.h"
|
||||
#include "test/common/wasm/flag-utils.h"
|
||||
#include "test/common/wasm/wasm-module-runner.h"
|
||||
|
Loading…
Reference in New Issue
Block a user