[wasm] Refactor import handling for 0xC.

Imports and exports in 0xC can be much more than functions, including
tables, memories, and globals. This CL refactors the underlying
organization of imports and exports to support these new import types.

BUG=

Review-Url: https://codereview.chromium.org/2390113003
Cr-Commit-Position: refs/heads/master@{#40033}
This commit is contained in:
titzer 2016-10-06 05:30:38 -07:00 committed by Commit bot
parent 94c8170a88
commit 599f8a8342
23 changed files with 1290 additions and 727 deletions

View File

@ -3151,8 +3151,8 @@ Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::ModuleEnv* module,
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
wasm::FunctionSig* sig, uint32_t index,
Handle<String> import_module,
MaybeHandle<String> import_function) {
Handle<String> module_name,
MaybeHandle<String> import_name) {
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
@ -3215,14 +3215,14 @@ Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
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();
if (!import_name.is_null()) {
Handle<String> handle = import_name.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()},
{module_name->ToCString().get(), module_name->length()},
{function_name, function_name_size});
}

View File

@ -84,8 +84,8 @@ class WasmCompilationUnit final {
// Wraps a JS function, producing a code object that can be called from WASM.
Handle<Code> CompileWasmToJSWrapper(Isolate* isolate, Handle<JSReceiver> target,
wasm::FunctionSig* sig, uint32_t index,
Handle<String> import_module,
MaybeHandle<String> import_function);
Handle<String> module_name,
MaybeHandle<String> import_name);
// Wraps a given wasm code object, producing a code object.
Handle<Code> CompileJSToWasmWrapper(Isolate* isolate, wasm::ModuleEnv* module,

View File

@ -318,7 +318,7 @@ class ModuleDecoder : public Decoder {
// ===== Imported global =========================================
import->index = static_cast<uint32_t>(module->globals.size());
module->globals.push_back(
{kAstStmt, false, NO_INIT, 0, true, false});
{kAstStmt, false, WasmInitExpr(), 0, true, false});
WasmGlobal* global = &module->globals.back();
global->type = consume_value_type();
global->mutability = consume_u8("mutability") != 0;
@ -399,7 +399,8 @@ class ModuleDecoder : public Decoder {
TRACE("DecodeGlobal[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
// Add an uninitialized global and pass a pointer to it.
module->globals.push_back({kAstStmt, false, NO_INIT, 0, false, false});
module->globals.push_back(
{kAstStmt, false, WasmInitExpr(), 0, false, false});
WasmGlobal* global = &module->globals.back();
DecodeGlobalInModule(module, i, global);
}
@ -545,9 +546,9 @@ class ModuleDecoder : public Decoder {
TRACE("DecodeDataSegment[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module->data_segments.push_back({
NO_INIT, // dest_addr
0, // source_offset
0 // source_size
WasmInitExpr(), // dest_addr
0, // source_offset
0 // source_size
});
WasmDataSegment* segment = &module->data_segments.back();
DecodeDataSegmentInModule(module, segment);
@ -647,13 +648,19 @@ class ModuleDecoder : public Decoder {
const byte* pos = pc();
global->init = consume_init_expr(module, kAstStmt);
switch (global->init.kind) {
case WasmInitExpr::kGlobalIndex:
if (global->init.val.global_index >= index) {
case WasmInitExpr::kGlobalIndex: {
uint32_t other_index = global->init.val.global_index;
if (other_index >= index) {
error("invalid global index in init expression");
} else if (module->globals[index].type != global->type) {
error("type mismatch in global initialization");
} else if (module->globals[other_index].type != global->type) {
error(pos, pos,
"type mismatch in global initialization "
"(from global #%u), expected %s, got %s",
other_index, WasmOpcodes::TypeName(global->type),
WasmOpcodes::TypeName(module->globals[other_index].type));
}
break;
}
default:
if (global->type != TypeOf(module, global->init)) {
error(pos, pos,

View File

@ -255,8 +255,9 @@ void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
}
uint32_t WasmModuleBuilder::AddGlobal(LocalType type, bool exported,
bool mutability) {
globals_.push_back({type, exported, mutability});
bool mutability,
const WasmInitExpr& init) {
globals_.push_back({type, exported, mutability, init});
return static_cast<uint32_t>(globals_.size() - 1);
}
@ -344,29 +345,64 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
for (auto global : globals_) {
buffer.write_u8(WasmOpcodes::LocalTypeCodeFor(global.type));
buffer.write_u8(global.mutability ? 1 : 0);
switch (global.type) {
case kAstI32: {
static const byte code[] = {WASM_I32V_1(0)};
switch (global.init.kind) {
case WasmInitExpr::kI32Const: {
DCHECK_EQ(kAstI32, global.type);
const byte code[] = {WASM_I32V_5(global.init.val.i32_const)};
buffer.write(code, sizeof(code));
break;
}
case kAstF32: {
static const byte code[] = {WASM_F32(0)};
case WasmInitExpr::kI64Const: {
DCHECK_EQ(kAstI64, global.type);
const byte code[] = {WASM_I64V_10(global.init.val.i64_const)};
buffer.write(code, sizeof(code));
break;
}
case kAstI64: {
static const byte code[] = {WASM_I64V_1(0)};
case WasmInitExpr::kF32Const: {
DCHECK_EQ(kAstF32, global.type);
const byte code[] = {WASM_F32(global.init.val.f32_const)};
buffer.write(code, sizeof(code));
break;
}
case kAstF64: {
static const byte code[] = {WASM_F64(0.0)};
case WasmInitExpr::kF64Const: {
DCHECK_EQ(kAstF64, global.type);
const byte code[] = {WASM_F64(global.init.val.f64_const)};
buffer.write(code, sizeof(code));
break;
}
default:
UNREACHABLE();
case WasmInitExpr::kGlobalIndex: {
const byte code[] = {kExprGetGlobal,
U32V_5(global.init.val.global_index)};
buffer.write(code, sizeof(code));
break;
}
default: {
// No initializer, emit a default value.
switch (global.type) {
case kAstI32: {
const byte code[] = {WASM_I32V_1(0)};
buffer.write(code, sizeof(code));
break;
}
case kAstI64: {
const byte code[] = {WASM_I64V_1(0)};
buffer.write(code, sizeof(code));
break;
}
case kAstF32: {
const byte code[] = {WASM_F32(0.0)};
buffer.write(code, sizeof(code));
break;
}
case kAstF64: {
const byte code[] = {WASM_F64(0.0)};
buffer.write(code, sizeof(code));
break;
}
default:
UNREACHABLE();
}
}
}
buffer.write_u8(kExprEnd);
}

View File

@ -212,7 +212,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
imports_[index].name_length = name_length;
}
WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
uint32_t AddGlobal(LocalType type, bool exported, bool mutability = true);
uint32_t AddGlobal(LocalType type, bool exported, bool mutability = true,
const WasmInitExpr& init = WasmInitExpr());
void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
uint32_t AddSignature(FunctionSig* sig);
void AddIndirectFunction(uint32_t index);
@ -241,6 +242,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
LocalType type;
bool exported;
bool mutability;
WasmInitExpr init;
};
struct WasmDataSegment {

File diff suppressed because it is too large Load Diff

View File

@ -86,12 +86,16 @@ struct WasmInitExpr {
double f64_const;
uint32_t global_index;
} val;
};
#define NO_INIT \
{ \
WasmInitExpr::kNone, { 0u } \
WasmInitExpr() : kind(kNone) {}
explicit WasmInitExpr(int32_t v) : kind(kI32Const) { val.i32_const = v; }
explicit WasmInitExpr(int64_t v) : kind(kI64Const) { val.i64_const = v; }
explicit WasmInitExpr(float v) : kind(kF32Const) { val.f32_const = v; }
explicit WasmInitExpr(double v) : kind(kF64Const) { val.f64_const = v; }
WasmInitExpr(WasmInitKind kind, uint32_t global_index) : kind(kGlobalIndex) {
val.global_index = global_index;
}
};
// Static representation of a WASM function.
struct WasmFunction {
@ -384,8 +388,9 @@ class WasmCompiledModule : public FixedArray {
#define CORE_WCM_PROPERTY_TABLE(MACRO) \
MACRO(OBJECT, FixedArray, code_table) \
MACRO(OBJECT, FixedArray, import_data) \
MACRO(OBJECT, FixedArray, imports) \
MACRO(OBJECT, FixedArray, exports) \
MACRO(OBJECT, FixedArray, inits) \
MACRO(OBJECT, FixedArray, startup_function) \
MACRO(OBJECT, FixedArray, indirect_function_tables) \
MACRO(OBJECT, String, module_bytes) \
@ -395,7 +400,6 @@ class WasmCompiledModule : public FixedArray {
MACRO(OBJECT, ByteArray, data_segments) \
MACRO(SMALL_NUMBER, uint32_t, globals_size) \
MACRO(OBJECT, JSArrayBuffer, heap) \
MACRO(SMALL_NUMBER, bool, export_memory) \
MACRO(SMALL_NUMBER, ModuleOrigin, origin) \
MACRO(WEAK_LINK, WasmCompiledModule, next_instance) \
MACRO(WEAK_LINK, WasmCompiledModule, prev_instance) \
@ -424,7 +428,6 @@ class WasmCompiledModule : public FixedArray {
static Handle<WasmCompiledModule> New(Isolate* isolate,
uint32_t min_memory_pages,
uint32_t globals_size,
bool export_memory,
ModuleOrigin origin);
static Handle<WasmCompiledModule> Clone(Isolate* isolate,
@ -454,9 +457,6 @@ class WasmCompiledModule : public FixedArray {
void PrintInstancesChain();
private:
#if DEBUG
static uint32_t instance_id_counter_;
#endif
void Init();
DISALLOW_IMPLICIT_CONSTRUCTORS(WasmCompiledModule);

View File

@ -429,3 +429,94 @@ TEST(Run_WasmModule_GrowMemOobVariableIndex) {
CHECK(try_catch.HasCaught());
isolate->clear_pending_exception();
}
TEST(Run_WasmModule_Global_init) {
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator);
TestSignatures sigs;
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
uint32_t global1 =
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(777777));
uint32_t global2 =
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(222222));
WasmFunctionBuilder* f1 = builder->AddFunction(sigs.i_v());
byte code[] = {
WASM_I32_ADD(WASM_GET_GLOBAL(global1), WASM_GET_GLOBAL(global2))};
f1->EmitCode(code, sizeof(code));
ExportAsMain(f1);
TestModule(&zone, builder, 999999);
}
template <typename CType>
static void RunWasmModuleGlobalInitTest(LocalType type, CType expected) {
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator);
TestSignatures sigs;
LocalType types[] = {type};
FunctionSig sig(1, 0, types);
for (int padding = 0; padding < 5; padding++) {
// Test with a simple initializer
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
for (int i = 0; i < padding; i++) { // pad global before
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(i + 20000));
}
uint32_t global =
builder->AddGlobal(type, false, false, WasmInitExpr(expected));
for (int i = 0; i < padding; i++) { // pad global after
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(i + 30000));
}
WasmFunctionBuilder* f1 = builder->AddFunction(&sig);
byte code[] = {WASM_GET_GLOBAL(global)};
f1->EmitCode(code, sizeof(code));
ExportAsMain(f1);
TestModule(&zone, builder, expected);
}
for (int padding = 0; padding < 5; padding++) {
// Test with a global index
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
for (int i = 0; i < padding; i++) { // pad global before
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(i + 40000));
}
uint32_t global1 =
builder->AddGlobal(type, false, false, WasmInitExpr(expected));
for (int i = 0; i < padding; i++) { // pad global middle
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(i + 50000));
}
uint32_t global2 = builder->AddGlobal(
type, false, false, WasmInitExpr(WasmInitExpr::kGlobalIndex, global1));
for (int i = 0; i < padding; i++) { // pad global after
builder->AddGlobal(kAstI32, false, false, WasmInitExpr(i + 60000));
}
WasmFunctionBuilder* f1 = builder->AddFunction(&sig);
byte code[] = {WASM_GET_GLOBAL(global2)};
f1->EmitCode(code, sizeof(code));
ExportAsMain(f1);
TestModule(&zone, builder, expected);
}
}
TEST(Run_WasmModule_Global_i32) {
RunWasmModuleGlobalInitTest<int32_t>(kAstI32, -983489);
RunWasmModuleGlobalInitTest<int32_t>(kAstI32, 11223344);
}
TEST(Run_WasmModule_Global_f32) {
RunWasmModuleGlobalInitTest<float>(kAstF32, -983.9f);
RunWasmModuleGlobalInitTest<float>(kAstF32, 1122.99f);
}
TEST(Run_WasmModule_Global_f64) {
RunWasmModuleGlobalInitTest<double>(kAstF64, -833.9);
RunWasmModuleGlobalInitTest<double>(kAstF64, 86374.25);
}

View File

@ -268,7 +268,7 @@ class TestingModule : public ModuleEnv {
byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type));
global_offset = (global_offset + size - 1) & ~(size - 1); // align
module_.globals.push_back(
{type, true, NO_INIT, global_offset, false, false});
{type, true, WasmInitExpr(), global_offset, false, false});
global_offset += size;
// limit number of globals.
CHECK_LT(global_offset, kMaxGlobalsSize);

View File

@ -14,8 +14,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
builder.addMemory(1,1, true);
builder.addImport("getValue", kSig_i);
builder.addFunction("f", kSig_i)
builder.addImport("getValue", kSig_i_v);
builder.addFunction("f", kSig_i_v)
.addBody([
kExprCallFunction, 0
]).exportFunc();

View File

@ -12,7 +12,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
builder.addMemory(1,1, true);
var kSig_v_i = makeSig([kAstI32], []);
var signature = builder.addType(kSig_v_i);
builder.addImport("some_value", kSig_i);
builder.addImport("some_value", kSig_i_v);
builder.addImport("writer", signature);
builder.addFunction("main", kSig_i_i)
@ -65,7 +65,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function RelationBetweenModuleAndClone() {
let builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([kExprI8Const, 42])
.exportFunc();
@ -81,7 +81,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function SerializeAfterInstantiation() {
let builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([kExprI8Const, 42])
.exportFunc();

View File

@ -11,7 +11,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
var kReturnValue = 88;
var builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([
kExprI8Const,
kReturnValue,
@ -32,7 +32,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([
kExprI8Const,
kReturnValue,
@ -57,7 +57,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
var builder = new WasmModuleBuilder();
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([
kExprI8Const,
kReturnValue,

View File

@ -94,7 +94,7 @@ print("Native function");
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_d);
var sig_index = builder.addType(kSig_d_v);
builder.addImport("func", sig_index);
builder.addFunction("main", sig_index)
.addBody([

View File

@ -10,7 +10,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestFunctionPrototype() {
var builder = new WasmModuleBuilder();
builder.addFunction("nine", kSig_i)
builder.addFunction("nine", kSig_i_v)
.addBody([kExprI8Const, 9])
.exportFunc();

View File

@ -0,0 +1,66 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function TestImported(type, val, expected) {
print("TestImported " + type + "(" + val +")" + " = " + expected);
var builder = new WasmModuleBuilder();
var sig = makeSig([], [type]);
var g = builder.addImportedGlobal("foo", undefined, type);
builder.addFunction("main", sig)
.addBody([kExprGetGlobal, g.index])
.exportAs("main");
builder.addGlobal(kAstI32); // pad
var instance = builder.instantiate({foo: val});
assertEquals(expected, instance.exports.main());
}
TestImported(kAstI32, 300.1, 300);
TestImported(kAstF32, 87234.87238, Math.fround(87234.87238));
TestImported(kAstF64, 77777.88888, 77777.88888);
TestImported(kAstF64, "89", 89);
function TestExported(type, val, expected) {
print("TestExported " + type + "(" + val +")" + " = " + expected);
var builder = new WasmModuleBuilder();
var sig = makeSig([type], []);
builder.addGlobal(kAstI32); // pad
var g = builder.addGlobal(type, false)
.exportAs("foo");
g.init = val;
builder.addGlobal(kAstI32); // pad
var instance = builder.instantiate();
assertEquals(expected, instance.exports.foo);
}
TestExported(kAstI32, 455.5, 455);
TestExported(kAstF32, -999.34343, Math.fround(-999.34343));
TestExported(kAstF64, 87347.66666, 87347.66666);
function TestImportedExported(type, val, expected) {
print("TestImportedExported " + type + "(" + val +")" + " = " + expected);
var builder = new WasmModuleBuilder();
var sig = makeSig([type], []);
var i = builder.addImportedGlobal("foo", undefined, type);
builder.addGlobal(kAstI32); // pad
var o = builder.addGlobal(type, false)
.exportAs("bar");
o.init_index = i;
builder.addGlobal(kAstI32); // pad
var instance = builder.instantiate({foo: val});
assertEquals(expected, instance.exports.bar);
}
TestImportedExported(kAstI32, 415.5, 415);
TestImportedExported(kAstF32, -979.34343, Math.fround(-979.34343));
TestImportedExported(kAstF64, 81347.66666, 81347.66666);

View File

@ -266,9 +266,9 @@ testCallPrint();
function testCallImport2(foo, bar, expected) {
var builder = new WasmModuleBuilder();
builder.addImport("foo", kSig_i);
builder.addImport("bar", kSig_i);
builder.addFunction("main", kSig_i)
builder.addImport("foo", kSig_i_v);
builder.addImport("bar", kSig_i_v);
builder.addFunction("main", kSig_i_v)
.addBody([
kExprCallFunction, 0, // --
kExprCallFunction, 1, // --

View File

@ -12,7 +12,7 @@ let nogc = () => {};
function newModule() {
let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, true);
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([kExprI32Const, 0, kExprI32LoadMem, 0, 0])
.exportFunc();

View File

@ -12,7 +12,7 @@ let kReturnValue = 117;
let buffer = (() => {
let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, true);
builder.addFunction("main", kSig_i)
builder.addFunction("main", kSig_i_v)
.addBody([kExprI8Const, kReturnValue])
.exportFunc();
@ -119,7 +119,7 @@ assertFalse(WebAssembly.validate(bytes(88, 88, 88, 88, 88, 88, 88, 88)));
builder.addMemory(1,1, true);
var kSig_v_i = makeSig([kAstI32], []);
var signature = builder.addType(kSig_v_i);
builder.addImport("some_value", kSig_i);
builder.addImport("some_value", kSig_i_v);
builder.addImport("writer", signature);
builder.addFunction("main", kSig_i_i)
@ -169,7 +169,7 @@ assertFalse(WebAssembly.validate(bytes(88, 88, 88, 88, 88, 88, 88, 88)));
(function GlobalsArePrivateToTheInstance() {
print("GlobalsArePrivateToTheInstance...");
var builder = new WasmModuleBuilder();
builder.addGlobal(kAstI32);
builder.addGlobal(kAstI32, true);
builder.addFunction("read", kSig_i_v)
.addBody([
kExprGetGlobal, 0])
@ -196,7 +196,7 @@ assertFalse(WebAssembly.validate(bytes(88, 88, 88, 88, 88, 88, 88, 88)));
var builder = new WasmModuleBuilder();
builder.addMemory(1,1, true);
builder.addFunction("f", kSig_i)
builder.addFunction("f", kSig_i_v)
.addBody([
kExprI32Const, 0,
kExprI32LoadMem, 0, 0

View File

@ -38,7 +38,7 @@ function assertVerifies(sig, body) {
}
assertVerifies(kSig_v_v, [kExprNop]);
assertVerifies(kSig_i, [kExprI8Const, 0]);
assertVerifies(kSig_i_v, [kExprI8Const, 0]);
// Arguments aren't allow to start functions.
assertFails(kSig_i_i, [kExprGetLocal, 0]);

View File

@ -12,7 +12,7 @@ var debug = true;
(function BasicTest() {
var module = new WasmModuleBuilder();
module.addMemory(1, 2, false);
module.addFunction("foo", kSig_i)
module.addFunction("foo", kSig_i_v)
.addBody([kExprI8Const, 11])
.exportAs("blarg");
@ -116,7 +116,7 @@ var debug = true;
(function BasicTestWithUint8Array() {
var module = new WasmModuleBuilder();
module.addMemory(1, 2, false);
module.addFunction("foo", kSig_i)
module.addFunction("foo", kSig_i_v)
.addBody([kExprI8Const, 17])
.exportAs("blarg");

View File

@ -89,8 +89,6 @@ var kExternalMemory = 2;
var kExternalGlobal = 3;
// Useful signatures
var kSig_i = makeSig([], [kAstI32]);
var kSig_d = makeSig([], [kAstF64]);
var kSig_i_i = makeSig([kAstI32], [kAstI32]);
var kSig_i_l = makeSig([kAstI64], [kAstI32]);
var kSig_i_ii = makeSig([kAstI32, kAstI32], [kAstI32]);
@ -100,6 +98,8 @@ var kSig_l_ll = makeSig([kAstI64, kAstI64], [kAstI64]);
var kSig_i_dd = makeSig([kAstF64, kAstF64], [kAstI32]);
var kSig_v_v = makeSig([], []);
var kSig_i_v = makeSig([], [kAstI32]);
var kSig_f_v = makeSig([], [kAstF64]);
var kSig_d_v = makeSig([], [kAstF64]);
var kSig_v_i = makeSig([kAstI32], []);
var kSig_v_ii = makeSig([kAstI32, kAstI32], []);
var kSig_v_iii = makeSig([kAstI32, kAstI32, kAstI32], []);

View File

@ -2,6 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Used for encoding f32 and double constants to bits.
let __buffer = new ArrayBuffer(8);
let byte_view = new Int8Array(__buffer);
let f32_view = new Float32Array(__buffer);
let f64_view = new Float64Array(__buffer);
class Binary extends Array {
emit_u8(val) {
this.push(val);
@ -19,7 +25,7 @@ class Binary extends Array {
this.push((val >> 24) & 0xff);
}
emit_varint(val) {
emit_u32v(val) {
while (true) {
let v = val & 0xff;
val = val >>> 7;
@ -40,7 +46,7 @@ class Binary extends Array {
emit_string(string) {
// When testing illegal names, we pass a byte array directly.
if (string instanceof Array) {
this.emit_varint(string.length);
this.emit_u32v(string.length);
this.emit_bytes(string);
return;
}
@ -48,7 +54,7 @@ class Binary extends Array {
// This is the hacky way to convert a JavaScript string to a UTF8 encoded
// string only containing single-byte characters.
let string_utf8 = unescape(encodeURIComponent(string));
this.emit_varint(string_utf8.length);
this.emit_u32v(string_utf8.length);
for (let i = 0; i < string_utf8.length; i++) {
this.emit_u8(string_utf8.charCodeAt(i));
}
@ -66,26 +72,26 @@ class Binary extends Array {
let section = new Binary;
content_generator(section);
// Emit section length.
this.emit_varint(section.length);
this.emit_u32v(section.length);
// Copy the temporary buffer.
this.push(...section);
}
}
class WasmFunctionBuilder {
constructor(name, type_index) {
constructor(module, name, type_index) {
this.module = module;
this.name = name;
this.type_index = type_index;
this.exports = [];
}
exportAs(name) {
this.exports.push(name);
this.module.exports.push({name: name, kind: kExternalFunction, index: this.index});
return this;
}
exportFunc() {
this.exports.push(this.name);
this.exportAs(this.name);
return this;
}
@ -100,17 +106,33 @@ class WasmFunctionBuilder {
}
}
class WasmGlobalBuilder {
constructor(module, type, mutable) {
this.module = module;
this.type = type;
this.mutable = mutable;
this.init = 0;
}
exportAs(name) {
this.module.exports.push({name: name, kind: kExternalGlobal, index: this.index});
return this;
}
}
class WasmModuleBuilder {
constructor() {
this.types = [];
this.imports = [];
this.exports = [];
this.globals = [];
this.functions = [];
this.exports = [];
this.table = [];
this.segments = [];
this.explicit = [];
this.pad = null;
this.num_imported_funcs = 0;
this.num_imported_globals = 0;
return this;
}
@ -139,29 +161,39 @@ class WasmModuleBuilder {
return this.types.length - 1;
}
addGlobal(local_type) {
this.globals.push(local_type);
return this.globals.length - 1;
addGlobal(local_type, mutable) {
let glob = new WasmGlobalBuilder(this, local_type, mutable);
glob.index = this.globals.length + this.num_imported_globals;
this.globals.push(glob);
return glob;
}
addFunction(name, type) {
let type_index = (typeof type) == "number" ? type : this.addType(type);
let func = new WasmFunctionBuilder(name, type_index);
func.index = this.functions.length + this.imports.length;
let func = new WasmFunctionBuilder(this, name, type_index);
func.index = this.functions.length + this.num_imported_funcs;
this.functions.push(func);
return func;
}
addImportWithModule(module, name, type) {
let type_index = (typeof type) == "number" ? type : this.addType(type);
this.imports.push({module: module, name: name, type: type_index});
return this.imports.length - 1;
this.imports.push({module: module, name: name, kind: kExternalFunction,
type: type_index});
return this.num_imported_funcs++;
}
addImport(name, type) {
return this.addImportWithModule(name, undefined, type);
}
addImportedGlobal(module, name, type) {
let o = {module: module, name: name, kind: kExternalGlobal, type: type,
mutable: false}
this.imports.push(o);
return this.num_imported_globals++;
}
addDataSegment(addr, data, init) {
this.segments.push({addr: addr, data: data, init: init});
return this.segments.length - 1;
@ -183,14 +215,14 @@ class WasmModuleBuilder {
if (wasm.types.length > 0) {
if (debug) print("emitting types @ " + binary.length);
binary.emit_section(kTypeSectionCode, section => {
section.emit_varint(wasm.types.length);
section.emit_u32v(wasm.types.length);
for (let type of wasm.types) {
section.emit_u8(kWasmFunctionTypeForm);
section.emit_varint(type.params.length);
section.emit_u32v(type.params.length);
for (let param of type.params) {
section.emit_u8(param);
}
section.emit_varint(type.results.length);
section.emit_u32v(type.results.length);
for (let result of type.results) {
section.emit_u8(result);
}
@ -202,12 +234,19 @@ class WasmModuleBuilder {
if (wasm.imports.length > 0) {
if (debug) print("emitting imports @ " + binary.length);
binary.emit_section(kImportSectionCode, section => {
section.emit_varint(wasm.imports.length);
section.emit_u32v(wasm.imports.length);
for (let imp of wasm.imports) {
section.emit_string(imp.module);
section.emit_string(imp.name || '');
section.emit_u8(kExternalFunction);
section.emit_varint(imp.type);
section.emit_u8(imp.kind);
if (imp.kind == kExternalFunction) {
section.emit_u32v(imp.type);
} else if (imp.kind == kExternalGlobal) {
section.emit_u32v(imp.type);
section.emit_u8(imp.mutable);
} else {
throw new Error("unknown/unsupported import kind " + imp.kind);
}
}
});
}
@ -215,16 +254,14 @@ class WasmModuleBuilder {
// Add functions declarations
let has_names = false;
let names = false;
let exports = 0;
if (wasm.functions.length > 0) {
if (debug) print("emitting function decls @ " + binary.length);
binary.emit_section(kFunctionSectionCode, section => {
section.emit_varint(wasm.functions.length);
section.emit_u32v(wasm.functions.length);
for (let func of wasm.functions) {
has_names = has_names || (func.name != undefined &&
func.name.length > 0);
exports += func.exports.length;
section.emit_varint(func.type_index);
section.emit_u32v(func.type_index);
}
});
}
@ -236,8 +273,8 @@ class WasmModuleBuilder {
section.emit_u8(1); // one table entry
section.emit_u8(kWasmAnyFunctionTypeForm);
section.emit_u8(1);
section.emit_varint(wasm.table.length);
section.emit_varint(wasm.table.length);
section.emit_u32v(wasm.table.length);
section.emit_u32v(wasm.table.length);
});
}
@ -246,9 +283,9 @@ class WasmModuleBuilder {
if (debug) print("emitting memory @ " + binary.length);
binary.emit_section(kMemorySectionCode, section => {
section.emit_u8(1); // one memory entry
section.emit_varint(kResizableMaximumFlag);
section.emit_varint(wasm.memory.min);
section.emit_varint(wasm.memory.max);
section.emit_u32v(kResizableMaximumFlag);
section.emit_u32v(wasm.memory.min);
section.emit_u32v(wasm.memory.max);
});
}
@ -256,28 +293,46 @@ class WasmModuleBuilder {
if (wasm.globals.length > 0) {
if (debug) print ("emitting globals @ " + binary.length);
binary.emit_section(kGlobalSectionCode, section => {
section.emit_varint(wasm.globals.length);
for (let global_type of wasm.globals) {
section.emit_u8(global_type);
section.emit_u8(true); // mutable
switch (global_type) {
section.emit_u32v(wasm.globals.length);
for (let global of wasm.globals) {
section.emit_u8(global.type);
section.emit_u8(global.mutable);
if ((typeof global.init_index) == "undefined") {
// Emit a constant initializer.
switch (global.type) {
case kAstI32:
section.emit_u8(kExprI32Const);
section.emit_u8(0);
section.emit_u32v(global.init);
break;
case kAstI64:
section.emit_u8(kExprI64Const);
section.emit_u8(0);
section.emit_u8(global.init);
break;
case kAstF32:
section.emit_u8(kExprF32Const);
section.emit_u32(0);
f32_view[0] = global.init;
section.emit_u8(byte_view[0]);
section.emit_u8(byte_view[1]);
section.emit_u8(byte_view[2]);
section.emit_u8(byte_view[3]);
break;
case kAstF64:
section.emit_u8(kExprI32Const);
section.emit_u32(0);
section.emit_u32(0);
section.emit_u8(kExprF64Const);
f64_view[0] = global.init;
section.emit_u8(byte_view[0]);
section.emit_u8(byte_view[1]);
section.emit_u8(byte_view[2]);
section.emit_u8(byte_view[3]);
section.emit_u8(byte_view[4]);
section.emit_u8(byte_view[5]);
section.emit_u8(byte_view[6]);
section.emit_u8(byte_view[7]);
break;
}
} else {
// Emit a global-index initializer.
section.emit_u8(kExprGetGlobal);
section.emit_u32v(global.init_index);
}
section.emit_u8(kExprEnd); // end of init expression
}
@ -286,16 +341,15 @@ class WasmModuleBuilder {
// Add export table.
var mem_export = (wasm.memory != undefined && wasm.memory.exp);
if (exports > 0 || mem_export) {
var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
if (exports_count > 0) {
if (debug) print("emitting exports @ " + binary.length);
binary.emit_section(kExportSectionCode, section => {
section.emit_varint(exports + (mem_export ? 1 : 0));
for (let func of wasm.functions) {
for (let exp of func.exports) {
section.emit_string(exp);
section.emit_u8(kExternalFunction);
section.emit_varint(func.index);
}
section.emit_u32v(exports_count);
for (let exp of wasm.exports) {
section.emit_string(exp.name);
section.emit_u8(exp.kind);
section.emit_u32v(exp.index);
}
if (mem_export) {
section.emit_string("memory");
@ -309,7 +363,7 @@ class WasmModuleBuilder {
if (wasm.start_index != undefined) {
if (debug) print("emitting start function @ " + binary.length);
binary.emit_section(kStartSectionCode, section => {
section.emit_varint(wasm.start_index);
section.emit_u32v(wasm.start_index);
});
}
@ -322,9 +376,9 @@ class WasmModuleBuilder {
section.emit_u8(kExprI32Const);
section.emit_u8(0);
section.emit_u8(kExprEnd);
section.emit_varint(wasm.table.length);
section.emit_u32v(wasm.table.length);
for (let index of wasm.table) {
section.emit_varint(index);
section.emit_u32v(index);
}
});
}
@ -334,7 +388,7 @@ class WasmModuleBuilder {
// emit function bodies
if (debug) print("emitting code @ " + binary.length);
binary.emit_section(kCodeSectionCode, section => {
section.emit_varint(wasm.functions.length);
section.emit_u32v(wasm.functions.length);
for (let func of wasm.functions) {
// Function body length will be patched later.
let local_decls = [];
@ -356,13 +410,13 @@ class WasmModuleBuilder {
}
let header = new Binary;
header.emit_varint(local_decls.length);
header.emit_u32v(local_decls.length);
for (let decl of local_decls) {
header.emit_varint(decl.count);
header.emit_u32v(decl.count);
header.emit_u8(decl.type);
}
section.emit_varint(header.length + func.body.length);
section.emit_u32v(header.length + func.body.length);
section.emit_bytes(header);
section.emit_bytes(func.body);
}
@ -373,13 +427,13 @@ class WasmModuleBuilder {
if (wasm.segments.length > 0) {
if (debug) print("emitting data segments @ " + binary.length);
binary.emit_section(kDataSectionCode, section => {
section.emit_varint(wasm.segments.length);
section.emit_u32v(wasm.segments.length);
for (let seg of wasm.segments) {
section.emit_u8(0); // linear memory index 0
section.emit_u8(kExprI32Const);
section.emit_varint(seg.addr);
section.emit_u32v(seg.addr);
section.emit_u8(kExprEnd);
section.emit_varint(seg.data.length);
section.emit_u32v(seg.data.length);
section.emit_bytes(seg.data);
}
});
@ -396,7 +450,7 @@ class WasmModuleBuilder {
if (debug) print("emitting names @ " + binary.length);
binary.emit_section(kUnknownSectionCode, section => {
section.emit_string("name");
section.emit_varint(wasm.functions.length);
section.emit_u32v(wasm.functions.length);
for (let func of wasm.functions) {
var name = func.name == undefined ? "" : func.name;
section.emit_string(name);

View File

@ -1283,7 +1283,7 @@ class TestModuleEnv : public ModuleEnv {
module = &mod;
}
byte AddGlobal(LocalType type, bool mutability = true) {
mod.globals.push_back({type, mutability, NO_INIT, 0, false, false});
mod.globals.push_back({type, mutability, WasmInitExpr(), 0, false, false});
CHECK(mod.globals.size() <= 127);
return static_cast<byte>(mod.globals.size() - 1);
}