summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-06-11 14:13:52 -0700
committerGitHub <noreply@github.com>2024-06-11 14:13:52 -0700
commit2dcf67049ef4d2cbcb2a65d367be97ae675f9d8a (patch)
tree6b2193693bfa44034baff985e8ccf38d3fa4af0a
parentcdd94a01ad02e944eaa9ba5e20a1129bef9ac305 (diff)
downloadbinaryen-2dcf67049ef4d2cbcb2a65d367be97ae675f9d8a.tar.gz
binaryen-2dcf67049ef4d2cbcb2a65d367be97ae675f9d8a.tar.bz2
binaryen-2dcf67049ef4d2cbcb2a65d367be97ae675f9d8a.zip
wasm2js: Add Table operations (#6650)
TableGet, Set, Size, Grow, Fill, Copy. Also move "null" into shared-constants, to make the code more consistent overall.
-rw-r--r--src/abi/js.h13
-rw-r--r--src/asmjs/shared-constants.cpp4
-rw-r--r--src/asmjs/shared-constants.h1
-rw-r--r--src/wasm2js.h73
-rw-r--r--test/wasm2js/tables.2asm.js89
-rw-r--r--test/wasm2js/tables.2asm.js.opt89
-rw-r--r--test/wasm2js/tables.wast46
7 files changed, 298 insertions, 17 deletions
diff --git a/src/abi/js.h b/src/abi/js.h
index e121e9b28..b8f56f14d 100644
--- a/src/abi/js.h
+++ b/src/abi/js.h
@@ -36,6 +36,9 @@ extern IString SCRATCH_STORE_F64;
extern IString MEMORY_INIT;
extern IString MEMORY_FILL;
extern IString MEMORY_COPY;
+extern IString TABLE_GROW;
+extern IString TABLE_FILL;
+extern IString TABLE_COPY;
extern IString DATA_DROP;
extern IString ATOMIC_WAIT_I32;
extern IString ATOMIC_RMW_I64;
@@ -81,6 +84,13 @@ inline void ensureHelpers(Module* wasm, IString specific = IString()) {
Type::i32);
ensureImport(GET_STASHED_BITS, {}, Type::i32);
ensureImport(TRAP, {}, Type::none);
+
+ if (wasm->features.hasReferenceTypes()) {
+ auto funcref = Type(HeapType::func, Nullable);
+ ensureImport(TABLE_GROW, {funcref, Type::i32}, Type::none);
+ ensureImport(TABLE_FILL, {Type::i32, funcref, Type::i32}, Type::none);
+ ensureImport(TABLE_COPY, {Type::i32, Type::i32, Type::i32}, Type::none);
+ }
}
inline bool isHelper(IString name) {
@@ -88,7 +98,8 @@ inline bool isHelper(IString name) {
name == SCRATCH_LOAD_F32 || name == SCRATCH_STORE_F32 ||
name == SCRATCH_LOAD_F64 || name == SCRATCH_STORE_F64 ||
name == ATOMIC_WAIT_I32 || name == MEMORY_INIT ||
- name == MEMORY_FILL || name == MEMORY_COPY || name == DATA_DROP ||
+ name == MEMORY_FILL || name == MEMORY_COPY || name == TABLE_GROW ||
+ name == TABLE_FILL || name == TABLE_COPY || name == DATA_DROP ||
name == ATOMIC_RMW_I64 || name == GET_STASHED_BITS || name == TRAP;
}
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp
index 57b9c4f87..bd924f179 100644
--- a/src/asmjs/shared-constants.cpp
+++ b/src/asmjs/shared-constants.cpp
@@ -62,6 +62,7 @@ IString ENV("env");
IString STACKTOP("STACKTOP");
IString STACK_MAX("STACK_MAX");
IString INSTRUMENT("instrument");
+IString LENGTH("length");
IString MATH_IMUL("Math_imul");
IString MATH_ABS("Math_abs");
IString MATH_CEIL("Math_ceil");
@@ -111,6 +112,9 @@ IString SCRATCH_STORE_F64("wasm2js_scratch_store_f64");
IString MEMORY_INIT("wasm2js_memory_init");
IString MEMORY_FILL("wasm2js_memory_fill");
IString MEMORY_COPY("wasm2js_memory_copy");
+IString TABLE_GROW("wasm2js_table_grow");
+IString TABLE_FILL("wasm2js_table_fill");
+IString TABLE_COPY("wasm2js_table_copy");
IString DATA_DROP("wasm2js_data_drop");
IString ATOMIC_WAIT_I32("wasm2js_atomic_wait_i32");
IString ATOMIC_RMW_I64("wasm2js_atomic_rmw_i64");
diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h
index e199b1361..d48da560e 100644
--- a/src/asmjs/shared-constants.h
+++ b/src/asmjs/shared-constants.h
@@ -65,6 +65,7 @@ extern IString ENV;
extern IString STACKTOP;
extern IString STACK_MAX;
extern IString INSTRUMENT;
+extern IString LENGTH;
extern IString MATH_IMUL;
extern IString MATH_ABS;
extern IString MATH_CLZ32;
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 2084c0453..dd4e61a51 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -672,12 +672,11 @@ void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) {
if (!table->imported()) {
TableUtils::FlatTable flat(*wasm, *table);
if (flat.valid) {
- Name null("null");
for (auto& name : flat.names) {
if (name.is()) {
name = fromName(name, NameScope::Top);
} else {
- name = null;
+ name = NULL_;
}
ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name));
}
@@ -2221,11 +2220,11 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
visit(curr->value, EXPRESSION_RESULT),
visit(curr->size, EXPRESSION_RESULT));
}
- Ref visitRefNull(RefNull* curr) { return ValueBuilder::makeName("null"); }
+ Ref visitRefNull(RefNull* curr) { return ValueBuilder::makeName(NULL_); }
Ref visitRefIsNull(RefIsNull* curr) {
return ValueBuilder::makeBinary(visit(curr->value, EXPRESSION_RESULT),
EQ,
- ValueBuilder::makeName("null"));
+ ValueBuilder::makeName(NULL_));
}
Ref visitRefFunc(RefFunc* curr) {
return ValueBuilder::makeName(fromName(curr->func, NameScope::Top));
@@ -2236,28 +2235,40 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
visit(curr->right, EXPRESSION_RESULT));
}
Ref visitTableGet(TableGet* curr) {
- unimplemented(curr);
- WASM_UNREACHABLE("unimp");
+ return ValueBuilder::makeSub(ValueBuilder::makeName(FUNCTION_TABLE),
+ visit(curr->index, EXPRESSION_RESULT));
}
Ref visitTableSet(TableSet* curr) {
- unimplemented(curr);
- WASM_UNREACHABLE("unimp");
+ auto sub = ValueBuilder::makeSub(ValueBuilder::makeName(FUNCTION_TABLE),
+ visit(curr->index, EXPRESSION_RESULT));
+ auto value = visit(curr->value, EXPRESSION_RESULT);
+ return ValueBuilder::makeBinary(sub, SET, value);
}
Ref visitTableSize(TableSize* curr) {
- unimplemented(curr);
- WASM_UNREACHABLE("unimp");
+ return ValueBuilder::makeDot(ValueBuilder::makeName(FUNCTION_TABLE),
+ ValueBuilder::makeName(LENGTH));
}
Ref visitTableGrow(TableGrow* curr) {
- unimplemented(curr);
- WASM_UNREACHABLE("unimp");
+ ABI::wasm2js::ensureHelpers(module, ABI::wasm2js::TABLE_GROW);
+ // Also ensure fill, as grow calls fill internally.
+ ABI::wasm2js::ensureHelpers(module, ABI::wasm2js::TABLE_FILL);
+ return ValueBuilder::makeCall(ABI::wasm2js::TABLE_GROW,
+ visit(curr->value, EXPRESSION_RESULT),
+ visit(curr->delta, EXPRESSION_RESULT));
}
Ref visitTableFill(TableFill* curr) {
- unimplemented(curr);
- WASM_UNREACHABLE("unimp");
+ ABI::wasm2js::ensureHelpers(module, ABI::wasm2js::TABLE_FILL);
+ return ValueBuilder::makeCall(ABI::wasm2js::TABLE_FILL,
+ visit(curr->dest, EXPRESSION_RESULT),
+ visit(curr->value, EXPRESSION_RESULT),
+ visit(curr->size, EXPRESSION_RESULT));
}
Ref visitTableCopy(TableCopy* curr) {
- unimplemented(curr);
- WASM_UNREACHABLE("unimp");
+ ABI::wasm2js::ensureHelpers(module, ABI::wasm2js::TABLE_COPY);
+ return ValueBuilder::makeCall(ABI::wasm2js::TABLE_COPY,
+ visit(curr->dest, EXPRESSION_RESULT),
+ visit(curr->source, EXPRESSION_RESULT),
+ visit(curr->size, EXPRESSION_RESULT));
}
Ref visitTry(Try* curr) {
unimplemented(curr);
@@ -3026,6 +3037,36 @@ void Wasm2JSGlue::emitSpecialSupport() {
bufferView.copyWithin(dest, source, source + size);
}
)";
+ } else if (import->base == ABI::wasm2js::TABLE_GROW) {
+ out << R"(
+ function wasm2js_table_grow(value, delta) {
+ // TODO: traps on invalid things
+ var oldSize = FUNCTION_TABLE.length;
+ FUNCTION_TABLE.length = oldSize + delta;
+ if (newSize > oldSize) {
+ __wasm_table_fill(oldSize, value, delta)
+ }
+ return oldSize;
+ }
+ )";
+ } else if (import->base == ABI::wasm2js::TABLE_FILL) {
+ out << R"(
+ function __wasm_table_fill(dest, value, size) {
+ // TODO: traps on invalid things
+ for (var i = 0; i < size; i++) {
+ FUNCTION_TABLE[dest + i] = value;
+ }
+ }
+ )";
+ } else if (import->base == ABI::wasm2js::TABLE_COPY) {
+ out << R"(
+ function __wasm_table_copy(dest, source, size) {
+ // TODO: traps on invalid things
+ for (var i = 0; i < size; i++) {
+ FUNCTION_TABLE[dest + i] = FUNCTION_TABLE[source + i];
+ }
+ }
+ )";
} else if (import->base == ABI::wasm2js::DATA_DROP) {
out << R"(
function wasm2js_data_drop(segment) {
diff --git a/test/wasm2js/tables.2asm.js b/test/wasm2js/tables.2asm.js
new file mode 100644
index 000000000..a7dd8fdd5
--- /dev/null
+++ b/test/wasm2js/tables.2asm.js
@@ -0,0 +1,89 @@
+import * as env from 'env';
+
+
+ function wasm2js_table_grow(value, delta) {
+ // TODO: traps on invalid things
+ var oldSize = FUNCTION_TABLE.length;
+ FUNCTION_TABLE.length = oldSize + delta;
+ if (newSize > oldSize) {
+ __wasm_table_fill(oldSize, value, delta)
+ }
+ return oldSize;
+ }
+
+ function __wasm_table_fill(dest, value, size) {
+ // TODO: traps on invalid things
+ for (var i = 0; i < size; i++) {
+ FUNCTION_TABLE[dest + i] = value;
+ }
+ }
+
+ function __wasm_table_copy(dest, source, size) {
+ // TODO: traps on invalid things
+ for (var i = 0; i < size; i++) {
+ FUNCTION_TABLE[dest + i] = FUNCTION_TABLE[source + i];
+ }
+ }
+
+function asmFunc(imports) {
+ var env = imports.env;
+ var FUNCTION_TABLE = env.table;
+ var Math_imul = Math.imul;
+ var Math_fround = Math.fround;
+ var Math_abs = Math.abs;
+ var Math_clz32 = Math.clz32;
+ var Math_min = Math.min;
+ var Math_max = Math.max;
+ var Math_floor = Math.floor;
+ var Math_ceil = Math.ceil;
+ var Math_trunc = Math.trunc;
+ var Math_sqrt = Math.sqrt;
+ function table_get() {
+ return FUNCTION_TABLE[1];
+ }
+
+ function table_set() {
+ FUNCTION_TABLE[1] = table_set;
+ }
+
+ function table_size() {
+ return FUNCTION_TABLE.length | 0;
+ }
+
+ function table_grow() {
+ return wasm2js_table_grow(table_grow, 42) | 0;
+ }
+
+ function table_fill(dest, value, size) {
+ dest = dest | 0;
+ size = size | 0;
+ wasm2js_table_fill(dest, value, size);
+ }
+
+ function table_copy(dest, source, size) {
+ dest = dest | 0;
+ source = source | 0;
+ size = size | 0;
+ wasm2js_table_copy(dest, source, size);
+ }
+
+ FUNCTION_TABLE[1] = table_get;
+ return {
+ "table_get": table_get,
+ "table_set": table_set,
+ "table_size": table_size,
+ "table_grow": table_grow,
+ "table_fill": table_fill,
+ "table_copy": table_copy
+ };
+}
+
+var retasmFunc = asmFunc({
+ "env": env,
+});
+export var table_get = retasmFunc.table_get;
+export var table_set = retasmFunc.table_set;
+export var table_size = retasmFunc.table_size;
+export var table_grow = retasmFunc.table_grow;
+export var table_fill = retasmFunc.table_fill;
+export var table_copy = retasmFunc.table_copy;
diff --git a/test/wasm2js/tables.2asm.js.opt b/test/wasm2js/tables.2asm.js.opt
new file mode 100644
index 000000000..f15b0cb11
--- /dev/null
+++ b/test/wasm2js/tables.2asm.js.opt
@@ -0,0 +1,89 @@
+import * as env from 'env';
+
+
+ function wasm2js_table_grow(value, delta) {
+ // TODO: traps on invalid things
+ var oldSize = FUNCTION_TABLE.length;
+ FUNCTION_TABLE.length = oldSize + delta;
+ if (newSize > oldSize) {
+ __wasm_table_fill(oldSize, value, delta)
+ }
+ return oldSize;
+ }
+
+ function __wasm_table_fill(dest, value, size) {
+ // TODO: traps on invalid things
+ for (var i = 0; i < size; i++) {
+ FUNCTION_TABLE[dest + i] = value;
+ }
+ }
+
+ function __wasm_table_copy(dest, source, size) {
+ // TODO: traps on invalid things
+ for (var i = 0; i < size; i++) {
+ FUNCTION_TABLE[dest + i] = FUNCTION_TABLE[source + i];
+ }
+ }
+
+function asmFunc(imports) {
+ var env = imports.env;
+ var FUNCTION_TABLE = env.table;
+ var Math_imul = Math.imul;
+ var Math_fround = Math.fround;
+ var Math_abs = Math.abs;
+ var Math_clz32 = Math.clz32;
+ var Math_min = Math.min;
+ var Math_max = Math.max;
+ var Math_floor = Math.floor;
+ var Math_ceil = Math.ceil;
+ var Math_trunc = Math.trunc;
+ var Math_sqrt = Math.sqrt;
+ function table_get() {
+ return FUNCTION_TABLE[1];
+ }
+
+ function table_set() {
+ FUNCTION_TABLE[1] = table_set;
+ }
+
+ function table_size() {
+ return FUNCTION_TABLE.length | 0;
+ }
+
+ function table_grow() {
+ return wasm2js_table_grow(table_grow, 42) | 0;
+ }
+
+ function table_fill($0, $1, $2) {
+ $0 = $0 | 0;
+ $2 = $2 | 0;
+ wasm2js_table_fill($0, $1, $2);
+ }
+
+ function table_copy($0, $1, $2) {
+ $0 = $0 | 0;
+ $1 = $1 | 0;
+ $2 = $2 | 0;
+ wasm2js_table_copy($0, $1, $2);
+ }
+
+ FUNCTION_TABLE[1] = table_get;
+ return {
+ "table_get": table_get,
+ "table_set": table_set,
+ "table_size": table_size,
+ "table_grow": table_grow,
+ "table_fill": table_fill,
+ "table_copy": table_copy
+ };
+}
+
+var retasmFunc = asmFunc({
+ "env": env,
+});
+export var table_get = retasmFunc.table_get;
+export var table_set = retasmFunc.table_set;
+export var table_size = retasmFunc.table_size;
+export var table_grow = retasmFunc.table_grow;
+export var table_fill = retasmFunc.table_fill;
+export var table_copy = retasmFunc.table_copy;
diff --git a/test/wasm2js/tables.wast b/test/wasm2js/tables.wast
new file mode 100644
index 000000000..56e85e40d
--- /dev/null
+++ b/test/wasm2js/tables.wast
@@ -0,0 +1,46 @@
+(module
+ (import "env" "table" (table $table 7 funcref))
+
+ (elem (i32.const 1) $table.get)
+
+ (func $table.get (export "table.get") (result funcref)
+ (table.get $table
+ (i32.const 1)
+ )
+ )
+
+ (func $table.set (export "table.set")
+ (table.set $table
+ (i32.const 1)
+ (ref.func $table.set)
+ )
+ )
+
+ (func $table.size (export "table.size") (result i32)
+ (table.size $table)
+ )
+
+ (func $table.grow (export "table.grow") (result i32)
+ (table.grow $table
+ (ref.func $table.grow)
+ (i32.const 42)
+ )
+ )
+
+ (func $table.fill (export "table.fill") (param $dest i32) (param $value funcref) (param $size i32)
+ (table.fill $table
+ (local.get $dest)
+ (local.get $value)
+ (local.get $size)
+ )
+ )
+
+ (func $table.copy (export "table.copy") (param $dest i32) (param $source i32) (param $size i32)
+ (table.copy $table $table
+ (local.get $dest)
+ (local.get $source)
+ (local.get $size)
+ )
+ )
+)
+