Commit 0c7ee927 authored by mtrofin's avatar mtrofin Committed by Commit bot

[wasm] Complete separation of compilation and instantiation

Support for serializing/deserializing the compiled wasm module.

We want to reuse the javascript snapshotting mechanics, at least in the
short term, when we still use the JS heap for the compiled wasm code.
Given that a module may be compiled in one v8 instance and then
instantiated later, in a different instance, whatever information we need
at instantiation time must also be serializable.

We currently hold on to the un-decoded wasm bytes, for enabling
debugging scenarios. This imposes a ~20% penalty on the memory
requirements of the wasm compiled code. We do not need this data
otherwise, for runtime, and it is sensible to consider eventually loading it
on demand. Therefore, I intentionally avoided relying on it and re-
decoding the wasm module data, and instead saved the information
necessary to support instantiation.

Given how whatever we need to persist must be serializable, the CL
uses a structure made out of serializable objects (fixed arrays mostly)
for storing this information. I preferred going this route rather than
adding more wasm-specific support to the serializer, given that we want
to eventually move off the JS heap, and therefore the serializer.

Additionally, it turns out this extra information is relatively not complex:
minimal structure, little nesting depth, mostly simple data like numbers
or byte blobs, or opaque data like compiled functions.

This CL also moves export compilation ahead of instantiation time.

This change added a helper getter to FixedArray, to make typed retrieval
of elements easier.

BUG=

Review-Url: https://codereview.chromium.org/2094563002
Cr-Commit-Position: refs/heads/master@{#37348}
parent f99f6333
......@@ -2955,29 +2955,31 @@ void WasmGraphBuilder::SetSourcePosition(Node* node,
}
static void RecordFunctionCompilation(CodeEventListener::LogEventsAndTags tag,
CompilationInfo* info,
Isolate* isolate, Handle<Code> code,
const char* message, uint32_t index,
wasm::WasmName func_name) {
Isolate* isolate = info->isolate();
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
ScopedVector<char> buffer(128);
SNPrintF(buffer, "%s#%d:%.*s", message, index, func_name.length(),
func_name.start());
Handle<String> name_str =
isolate->factory()->NewStringFromAsciiChecked(buffer.start());
Handle<String> script_str =
isolate->factory()->NewStringFromAsciiChecked("(WASM)");
Handle<Code> code = info->code();
Handle<SharedFunctionInfo> shared =
isolate->factory()->NewSharedFunctionInfo(name_str, code, false);
PROFILE(isolate, CodeCreateEvent(tag, AbstractCode::cast(*code), *shared,
*script_str, 0, 0));
}
}
Handle<JSFunction> CompileJSToWasmWrapper(
Isolate* isolate, wasm::ModuleEnv* module, Handle<String> name,
Handle<Code> wasm_code, Handle<JSObject> module_object, uint32_t index) {
const wasm::WasmName& module_name,
const wasm::WasmName& func_name) {
DCHECK(isolate->logger()->is_logging_code_events() ||
isolate->is_profiling());
ScopedVector<char> buffer(128);
SNPrintF(buffer, "%s#%d:%.*s:%.*s", message, index, module_name.length(),
module_name.start(), func_name.length(), func_name.start());
Handle<String> name_str =
isolate->factory()->NewStringFromAsciiChecked(buffer.start());
Handle<String> script_str =
isolate->factory()->NewStringFromAsciiChecked("(WASM)");
Handle<SharedFunctionInfo> shared =
isolate->factory()->NewSharedFunctionInfo(name_str, code, false);
PROFILE(isolate, CodeCreateEvent(tag, AbstractCode::cast(*code), *shared,
*script_str, 0, 0));
}
Handle<JSFunction> CompileJSToWasmWrapper(Isolate* isolate,
wasm::ModuleEnv* module,
Handle<String> name,
Handle<Code> wasm_code,
uint32_t index) {
const wasm::WasmFunction* func = &module->module->functions[index];
//----------------------------------------------------------------------------
......@@ -2990,7 +2992,6 @@ Handle<JSFunction> CompileJSToWasmWrapper(
shared->set_internal_formal_parameter_count(params);
Handle<JSFunction> function = isolate->factory()->NewFunction(
isolate->wasm_function_map(), name, MaybeHandle<Code>());
function->SetInternalField(0, *module_object);
function->set_shared(*shared);
//----------------------------------------------------------------------------
......@@ -3056,9 +3057,13 @@ Handle<JSFunction> CompileJSToWasmWrapper(
buffer.Dispose();
}
RecordFunctionCompilation(
CodeEventListener::FUNCTION_TAG, &info, "js-to-wasm", index,
module->module->GetName(func->name_offset, func->name_length));
if (isolate->logger()->is_logging_code_events() ||
isolate->is_profiling()) {
RecordFunctionCompilation(
CodeEventListener::FUNCTION_TAG, isolate, code, "js-to-wasm", index,
wasm::WasmName("export"),
module->module->GetName(func->name_offset, func->name_length));
}
// Set the JSFunction's machine code.
function->set_code(*code);
}
......@@ -3067,9 +3072,9 @@ Handle<JSFunction> CompileJSToWasmWrapper(
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate,
Handle<JSFunction> function,
wasm::FunctionSig* sig,
wasm::WasmName module_name,
wasm::WasmName function_name) {
wasm::FunctionSig* sig, uint32_t index,
Handle<String> import_module,
MaybeHandle<String> import_function) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
......@@ -3128,10 +3133,21 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate,
if (debugging) {
buffer.Dispose();
}
RecordFunctionCompilation(CodeEventListener::FUNCTION_TAG, &info,
"wasm-to-js", 0, module_name);
}
if (isolate->logger()->is_logging_code_events() || isolate->is_profiling()) {
const char* function_name = nullptr;
int function_name_size = 0;
if (!import_function.is_null()) {
Handle<String> handle = import_function.ToHandleChecked();
function_name = handle->ToCString().get();
function_name_size = handle->length();
}
RecordFunctionCompilation(
CodeEventListener::FUNCTION_TAG, isolate, code, "wasm-to-js", index,
{import_module->ToCString().get(), import_module->length()},
{function_name, function_name_size});
}
return code;
}
......@@ -3294,11 +3310,14 @@ Handle<Code> WasmCompilationUnit::FinishCompilation() {
Handle<Code> code = info_.code();
DCHECK(!code.is_null());
RecordFunctionCompilation(
CodeEventListener::FUNCTION_TAG, &info_, "WASM_function",
function_->func_index,
module_env_->module->GetName(function_->name_offset,
function_->name_length));
if (isolate_->logger()->is_logging_code_events() ||
isolate_->is_profiling()) {
RecordFunctionCompilation(
CodeEventListener::FUNCTION_TAG, isolate_, code, "WASM_function",
function_->func_index, wasm::WasmName("module"),
module_env_->module->GetName(function_->name_offset,
function_->name_length));
}
if (FLAG_trace_wasm_decode_time) {
double compile_ms = compile_timer.Elapsed().InMillisecondsF();
......
......@@ -79,15 +79,17 @@ class WasmCompilationUnit final {
// Wraps a JS function, producing a code object that can be called from WASM.
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate,
Handle<JSFunction> function,
wasm::FunctionSig* sig,
wasm::WasmName module_name,
wasm::WasmName function_name);
wasm::FunctionSig* sig, uint32_t index,
Handle<String> import_module,
MaybeHandle<String> import_function);
// Wraps a given wasm code object, producing a JSFunction that can be called
// from JavaScript.
Handle<JSFunction> CompileJSToWasmWrapper(
Isolate* isolate, wasm::ModuleEnv* module, Handle<String> name,
Handle<Code> wasm_code, Handle<JSObject> module_object, uint32_t index);
Handle<JSFunction> CompileJSToWasmWrapper(Isolate* isolate,
wasm::ModuleEnv* module,
Handle<String> name,
Handle<Code> wasm_code,
uint32_t index);
// Abstracts details of building TurboFan graph nodes for WASM to separate
// the WASM decoder from the internal details of TurboFan.
......
......@@ -2306,12 +2306,24 @@ Handle<Object> FixedArray::get(FixedArray* array, int index, Isolate* isolate) {
return handle(array->get(index), isolate);
}
template <class T>
MaybeHandle<T> FixedArray::GetValue(int index) const {
Object* obj = get(index);
if (obj->IsUndefined(GetIsolate())) return MaybeHandle<T>();
return Handle<T>(T::cast(obj));
}
template <class T>
Handle<T> FixedArray::GetValueChecked(int index) const {
Object* obj = get(index);
CHECK(!obj->IsUndefined(GetIsolate()));
return Handle<T>(T::cast(obj));
}
bool FixedArray::is_the_hole(int index) {
return get(index) == GetHeap()->the_hole_value();
}
void FixedArray::set(int index, Smi* value) {
DCHECK(map() != GetHeap()->fixed_cow_array_map());
DCHECK(index >= 0 && index < this->length());
......@@ -3981,6 +3993,9 @@ byte ByteArray::get(int index) {
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
}
const byte* ByteArray::data() const {
return reinterpret_cast<const byte*>(FIELD_ADDR_CONST(this, kHeaderSize));
}
void ByteArray::set(int index, byte value) {
DCHECK(index >= 0 && index < this->length());
......
......@@ -2644,6 +2644,12 @@ class FixedArray: public FixedArrayBase {
inline Object* get(int index) const;
static inline Handle<Object> get(FixedArray* array, int index,
Isolate* isolate);
template <class T>
MaybeHandle<T> GetValue(int index) const;
template <class T>
Handle<T> GetValueChecked(int index) const;
// Setter that uses write barrier.
inline void set(int index, Object* value);
inline bool is_the_hole(int index);
......@@ -4420,6 +4426,7 @@ class ByteArray: public FixedArrayBase {
// Setter and getter.
inline byte get(int index);
inline void set(int index, byte value);
inline const byte* data() const;
// Copy in / copy out whole byte slices.
inline void copy_out(int index, byte* buffer, int length);
......
......@@ -14,7 +14,7 @@ namespace internal {
template <typename T>
class Signature : public ZoneObject {
public:
Signature(size_t return_count, size_t parameter_count, T* reps)
Signature(size_t return_count, size_t parameter_count, const T* reps)
: return_count_(return_count),
parameter_count_(parameter_count),
reps_(reps) {}
......@@ -32,6 +32,8 @@ class Signature : public ZoneObject {
return reps_[index];
}
const T* raw_data() const { return reps_; }
// For incrementally building signatures.
class Builder {
public:
......@@ -71,7 +73,7 @@ class Signature : public ZoneObject {
protected:
size_t return_count_;
size_t parameter_count_;
T* reps_;
const T* reps_;
};
} // namespace internal
......
......@@ -192,9 +192,14 @@ i::MaybeHandle<i::JSObject> InstantiateModuleCommon(
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
object = result.val->Instantiate(isolate, ffi, memory);
if (!object.is_null()) {
args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
i::MaybeHandle<i::FixedArray> compiled_module =
result.val->CompileFunctions(isolate);
if (!compiled_module.is_null()) {
object = i::wasm::WasmModule::Instantiate(
isolate, compiled_module.ToHandleChecked(), ffi, memory);
if (!object.is_null()) {
args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
}
}
}
......
This diff is collapsed.
......@@ -224,10 +224,12 @@ struct WasmModule {
}
// Creates a new instantiation of the module in the given isolate.
MaybeHandle<JSObject> Instantiate(Isolate* isolate, Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory) const;
static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
Handle<FixedArray> compiled_module,
Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory);
Handle<FixedArray> CompileFunctions(Isolate* isolate) const;
MaybeHandle<FixedArray> CompileFunctions(Isolate* isolate) const;
uint32_t FunctionTableSize() const {
if (indirect_table_size > 0) {
......
......@@ -186,10 +186,9 @@ class TestingModule : public ModuleEnv {
Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle(
*v8::Local<v8::Function>::Cast(CompileRun(source))));
uint32_t index = AddFunction(sig, Handle<Code>::null());
WasmName module_name = ArrayVector("test");
WasmName function_name;
Handle<Code> code = CompileWasmToJSWrapper(isolate_, jsfunc, sig,
module_name, function_name);
Handle<Code> code =
CompileWasmToJSWrapper(isolate_, jsfunc, sig, index,
Handle<String>::null(), Handle<String>::null());
instance->function_code[index] = code;
return index;
}
......@@ -200,8 +199,10 @@ class TestingModule : public ModuleEnv {
Handle<JSObject> module_object = Handle<JSObject>(0, isolate_);
Handle<Code> code = instance->function_code[index];
WasmJs::InstallWasmFunctionMap(isolate_, isolate_->native_context());
return compiler::CompileJSToWasmWrapper(isolate_, this, name, code,
module_object, index);
Handle<JSFunction> ret =
compiler::CompileJSToWasmWrapper(isolate_, this, name, code, index);
ret->SetInternalField(0, *module_object);
return ret;
}
void SetFunctionCode(uint32_t index, Handle<Code> code) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment