Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/abi/js.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -81,14 +84,22 @@ 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) {
return name == SCRATCH_LOAD_I32 || name == SCRATCH_STORE_I32 ||
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;
}

Expand Down
4 changes: 4 additions & 0 deletions src/asmjs/shared-constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
1 change: 1 addition & 0 deletions src/asmjs/shared-constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
73 changes: 57 additions & 16 deletions src/wasm2js.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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));
Expand All @@ -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),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we validate elsewhere that the module has only one table? If not, we might want to raise an error here and on the other operations if the table is not at index 0.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have an error up front about that, it is a built-in assumption throughout wasm2js.

// TODO: Remove this restriction when wasm2js can handle multiple tables
if (wasm->tables.size() > 1) {
Fatal() << "error: modules with multiple tables are not supported yet.";
}

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);
Expand Down Expand Up @@ -3024,6 +3035,36 @@ void Wasm2JSGlue::emitSpecialSupport() {
function wasm2js_memory_copy(dest, source, size) {
// TODO: traps on invalid things
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) {
Expand Down
89 changes: 89 additions & 0 deletions test/wasm2js/tables.2asm.js
Original file line number Diff line number Diff line change
@@ -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;
89 changes: 89 additions & 0 deletions test/wasm2js/tables.2asm.js.opt
Original file line number Diff line number Diff line change
@@ -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;
Loading