Commit 8e5ac62d authored by mtrofin's avatar mtrofin Committed by Commit bot

[wasm] Compilation/Instantiation pipeline works off module object

Moved the compilation/instantiation pipeline to work off the
module object (JSObject), making the compiled module data (the
FixedArray) an implementation detail. This:
- simplifies the code by removing duplicate decode->compile->instantiate
sequences
- sets up the stage for "dressing up" the runtime model with
stronger typed APIs
- helps relanding this CL: https://codereview.chromium.org/2305903002/.
  It turns out that GCs during the cloning/instantiation events cause
trouble, and centering the source of truth on the module object helps
address this issue.

In the process, clarified cctest setup for wasm-capable isolates,
and changed signatures for consistency (using ModuleOrigin througout).

BUG=

Review-Url: https://codereview.chromium.org/2320723005
Cr-Commit-Position: refs/heads/master@{#39360}
parent 621f4af7
......@@ -201,15 +201,13 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
Handle<JSArrayBuffer> memory,
Handle<JSReceiver> foreign) {
i::Handle<i::JSObject> module(i::JSObject::cast(wasm_data->get(0)));
i::Handle<i::FixedArray> compiled(
i::FixedArray::cast(module->GetInternalField(0)));
i::Handle<i::FixedArray> foreign_globals(
i::FixedArray::cast(wasm_data->get(1)));
ErrorThrower thrower(isolate, "Asm.js -> WebAssembly instantiation");
i::MaybeHandle<i::JSObject> maybe_module_object =
i::wasm::WasmModule::Instantiate(isolate, compiled, foreign, memory);
i::wasm::WasmModule::Instantiate(isolate, module, foreign, memory);
if (maybe_module_object.is_null()) {
return MaybeHandle<Object>();
}
......
......@@ -133,13 +133,11 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
// Decode but avoid a redundant pass over function bodies for verification.
// Verification will happen during compilation.
i::Zone zone(isolate->allocator());
internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
isolate, &zone, start, end, false, origin);
i::MaybeHandle<i::JSObject> module_object =
i::wasm::CreateModuleObjectFromBytes(isolate, start, end, thrower,
origin);
i::MaybeHandle<i::JSObject> object;
if (result.failed()) {
thrower->Failed("", result);
} else {
if (!module_object.is_null()) {
// Success. Instantiate the module and return the object.
i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
if (args.Length() > 1 && args[1]->IsObject()) {
......@@ -154,19 +152,12 @@ i::MaybeHandle<i::JSObject> InstantiateModule(
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
i::MaybeHandle<i::FixedArray> compiled_module =
result.val->CompileFunctions(isolate, thrower);
if (!thrower->error()) {
DCHECK(!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()));
}
object = i::wasm::WasmModule::Instantiate(
isolate, module_object.ToHandleChecked(), ffi, memory);
if (!object.is_null()) {
args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
}
}
if (result.val) delete result.val;
return object;
}
......@@ -276,9 +267,6 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
return;
}
i::Handle<i::FixedArray> compiled_code = i::Handle<i::FixedArray>(
i::FixedArray::cast(module_obj->GetInternalField(0)));
i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
if (args.Length() > 1 && args[1]->IsObject()) {
Local<Object> obj = Local<Object>::Cast(args[1]);
......@@ -292,7 +280,7 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
}
i::MaybeHandle<i::JSObject> instance =
i::wasm::WasmModule::Instantiate(i_isolate, compiled_code, ffi, memory);
i::wasm::WasmModule::Instantiate(i_isolate, module_obj, ffi, memory);
if (instance.is_null()) {
thrower.Error("Could not instantiate module");
return;
......@@ -328,6 +316,47 @@ static Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
return function;
}
void WasmJs::SetupIsolateForWasm(Isolate* isolate) {
InstallWasmFunctionMap(isolate, isolate->native_context());
InstallWasmModuleSymbol(isolate, isolate->global_object(),
isolate->native_context());
}
void WasmJs::InstallWasmModuleSymbol(Isolate* isolate,
Handle<JSGlobalObject> global,
Handle<Context> context) {
Factory* factory = isolate->factory();
// Create private symbols.
Handle<Symbol> module_sym = factory->NewPrivateSymbol();
Handle<Symbol> instance_sym = factory->NewPrivateSymbol();
context->set_wasm_module_sym(*module_sym);
context->set_wasm_instance_sym(*instance_sym);
// Bind the WebAssembly object.
Handle<String> name = v8_str(isolate, "WebAssembly");
Handle<JSFunction> cons = factory->NewFunction(name);
JSFunction::SetInstancePrototype(
cons, Handle<Object>(context->initial_object_prototype(), isolate));
cons->shared()->set_instance_class_name(*name);
Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
JSObject::AddProperty(global, name, wasm_object, attributes);
// Install static methods on WebAssembly object.
InstallFunc(isolate, wasm_object, "compile", WebAssemblyCompile);
Handle<JSFunction> module_constructor =
InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule);
Handle<JSFunction> instance_constructor =
InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance);
i::Handle<i::Map> map = isolate->factory()->NewMap(
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize);
module_constructor->set_prototype_or_initial_map(*map);
map->SetConstructor(*module_constructor);
context->set_wasm_module_constructor(*module_constructor);
context->set_wasm_instance_constructor(*instance_constructor);
}
void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
if (!FLAG_expose_wasm && !FLAG_validate_asm) {
return;
......@@ -370,36 +399,7 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
JSObject::AddProperty(wasm_object, name, value, attributes);
}
}
// Create private symbols.
Handle<Symbol> module_sym = isolate->factory()->NewPrivateSymbol();
Handle<Symbol> instance_sym = isolate->factory()->NewPrivateSymbol();
context->set_wasm_module_sym(*module_sym);
context->set_wasm_instance_sym(*instance_sym);
// Bind the WebAssembly object.
Handle<String> name = v8_str(isolate, "WebAssembly");
Handle<JSFunction> cons = factory->NewFunction(name);
JSFunction::SetInstancePrototype(
cons, Handle<Object>(context->initial_object_prototype(), isolate));
cons->shared()->set_instance_class_name(*name);
Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
JSObject::AddProperty(global, name, wasm_object, attributes);
// Install static methods on WebAssembly object.
InstallFunc(isolate, wasm_object, "compile", WebAssemblyCompile);
Handle<JSFunction> module_constructor =
InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule);
Handle<JSFunction> instance_constructor =
InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance);
i::Handle<i::Map> map = isolate->factory()->NewMap(
i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize);
module_constructor->set_prototype_or_initial_map(*map);
map->SetConstructor(*module_constructor);
context->set_wasm_module_constructor(*module_constructor);
context->set_wasm_instance_constructor(*instance_constructor);
InstallWasmModuleSymbol(isolate, global, context);
}
void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
......
......@@ -20,6 +20,10 @@ class WasmJs {
public:
static void Install(Isolate* isolate, Handle<JSGlobalObject> global_object);
static void InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context);
static void InstallWasmModuleSymbol(Isolate* isolate,
Handle<JSGlobalObject> global,
Handle<Context> context);
static void SetupIsolateForWasm(Isolate* isolate);
};
} // namespace internal
......
......@@ -1371,14 +1371,17 @@ Handle<FixedArray> CloneModuleForInstance(Isolate* isolate,
// * installs a named property "memory" for that buffer if exported
// * installs named properties on the object for exported functions
// * compiles wasm code to machine code
MaybeHandle<JSObject> WasmModule::Instantiate(
Isolate* isolate, Handle<FixedArray> compiled_module,
Handle<JSReceiver> ffi, Handle<JSArrayBuffer> memory) {
MaybeHandle<JSObject> WasmModule::Instantiate(Isolate* isolate,
Handle<JSObject> module_object,
Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory) {
HistogramTimerScope wasm_instantiate_module_time_scope(
isolate->counters()->wasm_instantiate_module_time());
ErrorThrower thrower(isolate, "WasmModule::Instantiate()");
Factory* factory = isolate->factory();
Handle<FixedArray> compiled_module =
handle(FixedArray::cast(module_object->GetInternalField(0)));
compiled_module = CloneModuleForInstance(isolate, compiled_module);
// These fields are compulsory.
......
......@@ -234,7 +234,7 @@ struct WasmModule {
// Creates a new instantiation of the module in the given isolate.
static MaybeHandle<JSObject> Instantiate(Isolate* isolate,
Handle<FixedArray> compiled_module,
Handle<JSObject> module_object,
Handle<JSReceiver> ffi,
Handle<JSArrayBuffer> memory);
......
......@@ -29,9 +29,9 @@ void TestModule(Zone* zone, WasmModuleBuilder* builder,
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
WasmJs::InstallWasmFunctionMap(isolate, isolate->native_context());
int32_t result =
testing::CompileAndRunWasmModule(isolate, buffer.begin(), buffer.end());
WasmJs::SetupIsolateForWasm(isolate);
int32_t result = testing::CompileAndRunWasmModule(
isolate, buffer.begin(), buffer.end(), ModuleOrigin::kWasmOrigin);
CHECK_EQ(expected_result, result);
}
......@@ -183,7 +183,6 @@ TEST(Run_WasmModule_Global) {
}
TEST(Run_WasmModule_Serialization) {
FLAG_expose_wasm = true;
static const char* kFunctionName = "increment";
v8::base::AccountingAllocator allocator;
Zone zone(&allocator);
......@@ -201,20 +200,12 @@ TEST(Run_WasmModule_Serialization) {
ZoneBuffer buffer(&zone);
builder->WriteTo(buffer);
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
CcTest::InitIsolateOnce()->array_buffer_allocator();
v8::Isolate* v8_isolate = v8::Isolate::New(create_params);
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
v8::HandleScope new_scope(v8_isolate);
v8::Local<v8::Context> new_ctx = v8::Context::New(v8_isolate);
new_ctx->Enter();
Isolate* isolate = CcTest::InitIsolateOnce();
ErrorThrower thrower(isolate, "");
v8::WasmCompiledModule::SerializedModule data;
{
HandleScope scope(isolate);
WasmJs::SetupIsolateForWasm(isolate);
ModuleResult decoding_result = DecodeWasmModule(
isolate, &zone, buffer.begin(), buffer.end(), false, kWasmOrigin);
......@@ -234,14 +225,18 @@ TEST(Run_WasmModule_Serialization) {
data = v8_compiled_module->Serialize();
}
create_params.array_buffer_allocator = isolate->array_buffer_allocator();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
CcTest::InitIsolateOnce()->array_buffer_allocator();
isolate = reinterpret_cast<Isolate*>(v8_isolate);
v8::Isolate* v8_isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(v8_isolate);
v8::HandleScope new_scope(v8_isolate);
v8::Local<v8::Context> new_ctx = v8::Context::New(v8_isolate);
new_ctx->Enter();
isolate = reinterpret_cast<Isolate*>(v8_isolate);
WasmJs::SetupIsolateForWasm(isolate);
v8::MaybeLocal<v8::WasmCompiledModule> deserialized =
v8::WasmCompiledModule::Deserialize(v8_isolate, data);
......@@ -249,16 +244,15 @@ TEST(Run_WasmModule_Serialization) {
CHECK(deserialized.ToLocal(&compiled_module));
Handle<JSObject> module_object =
Handle<JSObject>::cast(v8::Utils::OpenHandle(*compiled_module));
Handle<FixedArray> compiled_part =
handle(FixedArray::cast(module_object->GetInternalField(0)));
Handle<JSObject> instance =
WasmModule::Instantiate(isolate, compiled_part,
WasmModule::Instantiate(isolate, module_object,
Handle<JSReceiver>::null(),
Handle<JSArrayBuffer>::null())
.ToHandleChecked();
Handle<Object> params[1] = {Handle<Object>(Smi::FromInt(41), isolate)};
int32_t result = testing::CallWasmFunctionForTesting(
isolate, instance, thrower, kFunctionName, 1, params);
isolate, instance, thrower, kFunctionName, 1, params,
ModuleOrigin::kWasmOrigin);
CHECK(result == 42);
new_ctx->Exit();
}
......
......@@ -59,25 +59,26 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
if (thrower.error()) return Handle<JSObject>::null();
MaybeHandle<FixedArray> compiled_module =
module->CompileFunctions(isolate, &thrower);
if (compiled_module.is_null()) return Handle<JSObject>::null();
return WasmModule::Instantiate(isolate, compiled_module.ToHandleChecked(),
// Although we decoded the module for some pre-validation, run the bytes
// again through the normal pipeline.
MaybeHandle<JSObject> module_object = CreateModuleObjectFromBytes(
isolate, module->module_start, module->module_end, &thrower,
ModuleOrigin::kWasmOrigin);
if (module_object.is_null()) return Handle<JSObject>::null();
return WasmModule::Instantiate(isolate, module_object.ToHandleChecked(),
Handle<JSReceiver>::null(),
Handle<JSArrayBuffer>::null())
.ToHandleChecked();
}
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, bool asm_js) {
const byte* module_end, ModuleOrigin origin) {
HandleScope scope(isolate);
Zone zone(isolate->allocator());
ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
std::unique_ptr<const WasmModule> module(DecodeWasmModuleForTesting(
isolate, &zone, thrower, module_start, module_end,
asm_js ? kAsmJsOrigin : kWasmOrigin));
isolate, &zone, thrower, module_start, module_end, origin));
if (module == nullptr) {
return -1;
......@@ -87,9 +88,9 @@ int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
if (instance.is_null()) {
return -1;
}
return CallWasmFunctionForTesting(isolate, instance, thrower,
asm_js ? "caller" : "main", 0, nullptr,
asm_js);
const char* f_name = origin == ModuleOrigin::kAsmJsOrigin ? "caller" : "main";
return CallWasmFunctionForTesting(isolate, instance, thrower, f_name, 0,
nullptr, origin);
}
int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower& thrower,
......@@ -150,9 +151,9 @@ int32_t InterpretWasmModule(Isolate* isolate, ErrorThrower& thrower,
int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
ErrorThrower& thrower, const char* name,
int argc, Handle<Object> argv[],
bool asm_js) {
ModuleOrigin origin) {
Handle<JSObject> exports_object;
if (asm_js) {
if (origin == ModuleOrigin::kAsmJsOrigin) {
exports_object = instance;
} else {
Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
......
......@@ -33,12 +33,12 @@ const Handle<JSObject> InstantiateModuleForTesting(Isolate* isolate,
int32_t CallWasmFunctionForTesting(Isolate* isolate, Handle<JSObject> instance,
ErrorThrower& thrower, const char* name,
int argc, Handle<Object> argv[],
bool asm_js = false);
ModuleOrigin origin);
// Decode, verify, and run the function labeled "main" in the
// given encoded module. The module should have no imports.
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
const byte* module_end, bool asm_js = false);
const byte* module_end, ModuleOrigin origin);
// Interprets the given module, starting at the function specified by
// {function_index}. The return type of the function has to be int32. The module
......
......@@ -32,9 +32,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
v8::internal::WasmJs::InstallWasmFunctionMap(i_isolate,
i_isolate->native_context());
v8::internal::wasm::testing::CompileAndRunWasmModule(i_isolate, data,
data + size, true);
v8::internal::WasmJs::SetupIsolateForWasm(i_isolate);
v8::internal::wasm::testing::CompileAndRunWasmModule(
i_isolate, data, data + size,
v8::internal::wasm::ModuleOrigin::kAsmJsOrigin);
return 0;
}
......@@ -50,15 +50,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ZoneBuffer buffer(&zone);
builder.WriteTo(buffer);
v8::internal::WasmJs::InstallWasmFunctionMap(i_isolate,
i_isolate->native_context());
v8::internal::WasmJs::SetupIsolateForWasm(i_isolate);
v8::internal::HandleScope scope(i_isolate);
ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
i_isolate, &zone, interpreter_thrower, buffer.begin(), buffer.end(),
kWasmOrigin));
v8::internal::wasm::ModuleOrigin::kWasmOrigin));
if (module == nullptr) {
return 0;
......@@ -86,7 +85,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::internal::handle(v8::internal::Smi::FromInt(1), i_isolate)};
result_compiled = testing::CallWasmFunctionForTesting(
i_isolate, instance, compiler_thrower, "main", arraysize(arguments),
arguments, false);
arguments, v8::internal::wasm::ModuleOrigin::kWasmOrigin);
}
if (result_interpreted == 0xdeadbeef) {
CHECK(i_isolate->has_pending_exception());
......
......@@ -32,9 +32,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(support->GetContext());
v8::TryCatch try_catch(isolate);
v8::internal::WasmJs::InstallWasmFunctionMap(i_isolate,
i_isolate->native_context());
v8::internal::wasm::testing::CompileAndRunWasmModule(i_isolate, data,
data + size, false);
v8::internal::WasmJs::SetupIsolateForWasm(i_isolate);
v8::internal::wasm::testing::CompileAndRunWasmModule(
i_isolate, data, data + size, v8::internal::wasm::kWasmOrigin);
return 0;
}
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