Commit 083a8d72 authored by bradnelson's avatar bradnelson Committed by Commit bot

[wasm][asm.js] Asm.js -> wasm custom parser.

Add the --fast-validate-asm option, which directs asm.js code
to a new parser + validator + wasm code generator,
which is then compiled using WebAssembly.

This parser takes advantage of asm.js structure to linearly parse
asm.js code, keeping a scope stack + a few additional tables to track
varibles.

BUG=v8:6090
BUG=v8:4203

R=mstarzinger@chromium.org,marja@chromium.org,vogelheim@chromium.org,kschimpf@chromium.org

Review-Url: https://codereview.chromium.org/2757693003
Cr-Commit-Position: refs/heads/master@{#44084}
parent bdf32cf1
......@@ -1044,6 +1044,8 @@ v8_source_set("v8_base") {
"src/asmjs/asm-js.cc",
"src/asmjs/asm-js.h",
"src/asmjs/asm-names.h",
"src/asmjs/asm-parser.cc",
"src/asmjs/asm-parser.h",
"src/asmjs/asm-scanner.cc",
"src/asmjs/asm-scanner.h",
"src/asmjs/asm-typer.cc",
......
......@@ -6,6 +6,7 @@
#include "src/api-natives.h"
#include "src/api.h"
#include "src/asmjs/asm-parser.h"
#include "src/asmjs/asm-typer.h"
#include "src/asmjs/asm-wasm-builder.h"
#include "src/assert-scope.h"
......@@ -165,24 +166,74 @@ bool IsStdlibMemberValid(i::Isolate* isolate, Handle<JSReceiver> stdlib,
MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
wasm::ZoneBuffer* module = nullptr;
wasm::ZoneBuffer* asm_offsets = nullptr;
Handle<FixedArray> uses_array;
Handle<FixedArray> foreign_globals;
base::ElapsedTimer asm_wasm_timer;
asm_wasm_timer.Start();
wasm::AsmWasmBuilder builder(info);
Handle<FixedArray> foreign_globals;
auto asm_wasm_result = builder.Run(&foreign_globals);
if (!asm_wasm_result.success) {
DCHECK(!info->isolate()->has_pending_exception());
if (!FLAG_suppress_asm_messages) {
MessageHandler::ReportMessage(info->isolate(),
builder.typer()->message_location(),
builder.typer()->error_message());
if (FLAG_fast_validate_asm) {
wasm::AsmJsParser parser(info->isolate(), info->zone(), info->script(),
info->literal()->start_position(),
info->literal()->end_position());
if (!parser.Run()) {
DCHECK(!info->isolate()->has_pending_exception());
if (!FLAG_suppress_asm_messages) {
MessageLocation location(info->script(), parser.failure_location(),
parser.failure_location());
Handle<String> message =
info->isolate()
->factory()
->NewStringFromUtf8(CStrVector(parser.failure_message()))
.ToHandleChecked();
Handle<JSMessageObject> error_message =
MessageHandler::MakeMessageObject(
info->isolate(), MessageTemplate::kAsmJsInvalid, &location,
message, Handle<JSArray>::null());
error_message->set_error_level(v8::Isolate::kMessageWarning);
MessageHandler::ReportMessage(info->isolate(), &location,
error_message);
}
return MaybeHandle<FixedArray>();
}
Zone* zone = info->zone();
module = new (zone) wasm::ZoneBuffer(zone);
parser.module_builder()->WriteTo(*module);
asm_offsets = new (zone) wasm::ZoneBuffer(zone);
parser.module_builder()->WriteAsmJsOffsetTable(*asm_offsets);
// TODO(bradnelson): Remove foreign_globals plumbing (as we don't need it
// for the new parser).
foreign_globals = info->isolate()->factory()->NewFixedArray(0);
uses_array = info->isolate()->factory()->NewFixedArray(
static_cast<int>(parser.stdlib_uses()->size()));
int count = 0;
for (auto i : *parser.stdlib_uses()) {
uses_array->set(count++, Smi::FromInt(i));
}
} else {
auto asm_wasm_result = builder.Run(&foreign_globals);
if (!asm_wasm_result.success) {
DCHECK(!info->isolate()->has_pending_exception());
if (!FLAG_suppress_asm_messages) {
MessageHandler::ReportMessage(info->isolate(),
builder.typer()->message_location(),
builder.typer()->error_message());
}
return MaybeHandle<FixedArray>();
}
module = asm_wasm_result.module_bytes;
asm_offsets = asm_wasm_result.asm_offset_table;
wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
uses_array = info->isolate()->factory()->NewFixedArray(
static_cast<int>(uses.size()));
int count = 0;
for (auto i : uses) {
uses_array->set(count++, Smi::FromInt(i));
}
return MaybeHandle<FixedArray>();
}
double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
wasm::ZoneBuffer* module = asm_wasm_result.module_bytes;
wasm::ZoneBuffer* asm_offsets = asm_wasm_result.asm_offset_table;
double asm_wasm_time = asm_wasm_timer.Elapsed().InMillisecondsF();
Vector<const byte> asm_offsets_vec(asm_offsets->begin(),
static_cast<int>(asm_offsets->size()));
......@@ -197,14 +248,6 @@ MaybeHandle<FixedArray> AsmJs::CompileAsmViaWasm(CompilationInfo* info) {
DCHECK_GE(module->end(), module->begin());
uintptr_t wasm_size = module->end() - module->begin();
wasm::AsmTyper::StdlibSet uses = builder.typer()->StdlibUses();
Handle<FixedArray> uses_array =
info->isolate()->factory()->NewFixedArray(static_cast<int>(uses.size()));
int count = 0;
for (auto i : uses) {
uses_array->set(count++, Smi::FromInt(i));
}
Handle<FixedArray> result =
info->isolate()->factory()->NewFixedArray(kWasmDataEntryCount);
result->set(kWasmDataCompiledModule, *compiled.ToHandleChecked());
......@@ -283,33 +326,36 @@ MaybeHandle<Object> AsmJs::InstantiateAsmWasm(i::Isolate* isolate,
}
i::Handle<i::Object> module_object = maybe_module_object.ToHandleChecked();
i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
wasm::AsmWasmBuilder::foreign_init_name));
i::Handle<i::Object> init =
i::Object::GetProperty(module_object, init_name).ToHandleChecked();
if (!FLAG_fast_validate_asm) {
i::Handle<i::Name> init_name(isolate->factory()->InternalizeUtf8String(
wasm::AsmWasmBuilder::foreign_init_name));
i::Handle<i::Object> init =
i::Object::GetProperty(module_object, init_name).ToHandleChecked();
i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
i::Handle<i::Object>* foreign_args_array =
new i::Handle<i::Object>[foreign_globals->length()];
for (int j = 0; j < foreign_globals->length(); j++) {
if (!foreign.is_null()) {
i::MaybeHandle<i::Name> name = i::Object::ToName(
isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
if (!name.is_null()) {
i::MaybeHandle<i::Object> val =
i::Object::GetProperty(foreign, name.ToHandleChecked());
if (!val.is_null()) {
foreign_args_array[j] = val.ToHandleChecked();
continue;
i::Handle<i::Object> undefined(isolate->heap()->undefined_value(), isolate);
i::Handle<i::Object>* foreign_args_array =
new i::Handle<i::Object>[foreign_globals->length()];
for (int j = 0; j < foreign_globals->length(); j++) {
if (!foreign.is_null()) {
i::MaybeHandle<i::Name> name = i::Object::ToName(
isolate, i::Handle<i::Object>(foreign_globals->get(j), isolate));
if (!name.is_null()) {
i::MaybeHandle<i::Object> val =
i::Object::GetProperty(foreign, name.ToHandleChecked());
if (!val.is_null()) {
foreign_args_array[j] = val.ToHandleChecked();
continue;
}
}
}
foreign_args_array[j] = undefined;
}
foreign_args_array[j] = undefined;
i::MaybeHandle<i::Object> retval =
i::Execution::Call(isolate, init, undefined, foreign_globals->length(),
foreign_args_array);
delete[] foreign_args_array;
DCHECK(!retval.is_null());
}
i::MaybeHandle<i::Object> retval = i::Execution::Call(
isolate, init, undefined, foreign_globals->length(), foreign_args_array);
delete[] foreign_args_array;
DCHECK(!retval.is_null());
i::Handle<i::Name> single_function_name(
isolate->factory()->InternalizeUtf8String(
......
This diff is collapsed.
This diff is collapsed.
......@@ -547,6 +547,8 @@ DEFINE_BOOL(wasm_break_on_decoder_error, false,
"debug break when wasm decoder encounters an error")
DEFINE_BOOL(validate_asm, false, "validate asm.js modules before compiling")
DEFINE_BOOL(fast_validate_asm, false,
"validate asm.js modules before compiling")
DEFINE_BOOL(suppress_asm_messages, false,
"don't emit asm.js related messages (for golden file testing)")
DEFINE_BOOL(trace_asm_time, false, "log asm.js timing info to the console")
......
......@@ -418,6 +418,8 @@
'asmjs/asm-js.cc',
'asmjs/asm-js.h',
'asmjs/asm-names.h',
'asmjs/asm-parser.cc',
'asmjs/asm-parser.h',
'asmjs/asm-scanner.cc',
'asmjs/asm-scanner.h',
'asmjs/asm-typer.cc',
......
......@@ -185,6 +185,17 @@ void WasmFunctionBuilder::SetAsmFunctionStartPosition(int position) {
last_asm_source_position_ = position;
}
void WasmFunctionBuilder::StashCode(std::vector<byte>* dst, size_t position) {
if (dst == nullptr) {
body_.resize(position);
return;
}
size_t len = body_.size() - position;
dst->resize(len);
memcpy(dst->data(), &body_[position], len);
body_.resize(position);
}
void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
buffer.write_u32v(signature_index_);
}
......@@ -194,8 +205,8 @@ void WasmFunctionBuilder::WriteExports(ZoneBuffer& buffer) const {
buffer.write_size(name.size());
buffer.write(reinterpret_cast<const byte*>(name.data()), name.size());
buffer.write_u8(kExternalFunction);
buffer.write_u32v(func_index_ +
static_cast<uint32_t>(builder_->imports_.size()));
buffer.write_u32v(func_index_ + static_cast<uint32_t>(
builder_->function_imports_.size()));
}
}
......@@ -212,7 +223,8 @@ void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
for (DirectCallIndex call : direct_calls_) {
buffer.patch_u32v(
base + call.offset,
call.direct_index + static_cast<uint32_t>(builder_->imports_.size()));
call.direct_index +
static_cast<uint32_t>(builder_->function_imports_.size()));
}
}
}
......@@ -237,7 +249,8 @@ void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const {
WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
: zone_(zone),
signatures_(zone),
imports_(zone),
function_imports_(zone),
global_imports_(zone),
functions_(zone),
data_segments_(zone),
indirect_functions_(zone),
......@@ -303,8 +316,15 @@ void WasmModuleBuilder::SetIndirectFunction(uint32_t indirect,
uint32_t WasmModuleBuilder::AddImport(const char* name, int name_length,
FunctionSig* sig) {
imports_.push_back({AddSignature(sig), name, name_length});
return static_cast<uint32_t>(imports_.size() - 1);
function_imports_.push_back({AddSignature(sig), name, name_length});
return static_cast<uint32_t>(function_imports_.size() - 1);
}
uint32_t WasmModuleBuilder::AddGlobalImport(const char* name, int name_length,
ValueType type) {
global_imports_.push_back(
{WasmOpcodes::ValueTypeCodeFor(type), name, name_length});
return static_cast<uint32_t>(global_imports_.size() - 1);
}
void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
......@@ -346,10 +366,19 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
}
// == Emit imports ===========================================================
if (imports_.size() > 0) {
if (global_imports_.size() + function_imports_.size() > 0) {
size_t start = EmitSection(kImportSectionCode, buffer);
buffer.write_size(imports_.size());
for (auto import : imports_) {
buffer.write_size(global_imports_.size() + function_imports_.size());
for (auto import : global_imports_) {
buffer.write_u32v(0); // module name length
buffer.write_u32v(import.name_length); // field name length
buffer.write(reinterpret_cast<const byte*>(import.name), // field name
import.name_length);
buffer.write_u8(kExternalGlobal);
buffer.write_u8(import.type_code);
buffer.write_u8(0); // immutable
}
for (auto import : function_imports_) {
buffer.write_u32v(0); // module name length
buffer.write_u32v(import.name_length); // field name length
buffer.write(reinterpret_cast<const byte*>(import.name), // field name
......@@ -478,7 +507,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
if (start_function_index_ >= 0) {
size_t start = EmitSection(kStartSectionCode, buffer);
buffer.write_u32v(start_function_index_ +
static_cast<uint32_t>(imports_.size()));
static_cast<uint32_t>(function_imports_.size()));
FixupSection(buffer, start);
}
......@@ -493,7 +522,8 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
buffer.write_size(indirect_functions_.size()); // element count
for (auto index : indirect_functions_) {
buffer.write_u32v(index + static_cast<uint32_t>(imports_.size()));
buffer.write_u32v(index +
static_cast<uint32_t>(function_imports_.size()));
}
FixupSection(buffer, start);
......@@ -535,9 +565,9 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
buffer.write_size(4);
buffer.write(reinterpret_cast<const byte*>("name"), 4);
// Emit the names.
size_t count = functions_.size() + imports_.size();
size_t count = functions_.size() + function_imports_.size();
buffer.write_size(count);
for (size_t i = 0; i < imports_.size(); i++) {
for (size_t i = 0; i < function_imports_.size(); i++) {
buffer.write_u8(0); // empty name for import
buffer.write_u8(0); // no local variables
}
......
......@@ -139,6 +139,10 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void AddAsmWasmOffset(int call_position, int to_number_position);
void SetAsmFunctionStartPosition(int position);
size_t GetPosition() const { return body_.size(); }
void FixupByte(size_t position, byte value) { body_[position] = value; }
void StashCode(std::vector<byte>* dst, size_t position);
void WriteSignature(ZoneBuffer& buffer) const;
void WriteExports(ZoneBuffer& buffer) const;
void WriteBody(ZoneBuffer& buffer) const;
......@@ -223,12 +227,13 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
// Building methods.
uint32_t AddImport(const char* name, int name_length, FunctionSig* sig);
void SetImportName(uint32_t index, const char* name, int name_length) {
imports_[index].name = name;
imports_[index].name_length = name_length;
function_imports_[index].name = name;
function_imports_[index].name_length = name_length;
}
WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
uint32_t AddGlobal(ValueType type, bool exported, bool mutability = true,
const WasmInitExpr& init = WasmInitExpr());
uint32_t AddGlobalImport(const char* name, int name_length, ValueType type);
void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
uint32_t AddSignature(FunctionSig* sig);
uint32_t AllocateIndirectFunctions(uint32_t count);
......@@ -257,6 +262,12 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
int name_length;
};
struct WasmGlobalImport {
ValueTypeCode type_code;
const char* name;
int name_length;
};
struct WasmGlobal {
ValueType type;
bool exported;
......@@ -272,7 +283,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
friend class WasmFunctionBuilder;
Zone* zone_;
ZoneVector<FunctionSig*> signatures_;
ZoneVector<WasmFunctionImport> imports_;
ZoneVector<WasmFunctionImport> function_imports_;
ZoneVector<WasmGlobalImport> global_imports_;
ZoneVector<WasmFunctionBuilder*> functions_;
ZoneVector<WasmDataSegment> data_segments_;
ZoneVector<uint32_t> indirect_functions_;
......
......@@ -1828,6 +1828,13 @@ class InstantiationHelper {
module_name, import_name);
return -1;
}
if (FLAG_fast_validate_asm) {
if (module_->globals[import.index].type == kWasmI32) {
value = Object::ToInt32(isolate_, value).ToHandleChecked();
} else {
value = Object::ToNumber(value).ToHandleChecked();
}
}
if (!value->IsNumber()) {
ReportLinkError("global import must be a number", index,
module_name, import_name);
......
......@@ -1147,7 +1147,8 @@ function TestForeignVariables() {
// Check that undefined values are converted to proper defaults.
TestCase({qux: 999}, 0, NaN, 0, NaN);
// Check that an undefined ffi is ok.
TestCase(undefined, 0, NaN, 0, NaN);
// TODO(v8:6127): Fix handling of this case and re-enable.
// TestCase(undefined, 0, NaN, 0, NaN);
// Check that true values are converted properly.
TestCase({foo: true, bar: true, baz: true}, 1, 1.0, 1, 1.0);
// Check that false values are converted properly.
......@@ -1186,7 +1187,8 @@ function TestForeignVariables() {
// Check that function values are converted properly.
TestCase({foo: TestCase, bar: TestCase, qux: TestCase}, 0, NaN, 0, NaN);
// Check that a missing ffi object is safe.
TestCase(undefined, 0, NaN, 0, NaN);
// TODO(v8:6127): Fix handling of this case and re-enable.
// TestCase(undefined, 0, NaN, 0, NaN);
}
print("TestForeignVariables...");
......
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