Skip to content

Commit

Permalink
Support ref.extern n in spec tests (#6858)
Browse files Browse the repository at this point in the history
Spec tests pass the value `ref.extern n`, where `n` is some integer,
into exported functions that expect to receive externrefs and receive
such values back out as return values. The payload serves to distinguish
externrefs so the test can assert that the correct one was returned.

Parse these values in wast scripts and represent them as externalized
i31refs carrying the payload. We will need a different representation
eventually, since some tests explicitly expect these externrefs to not
be i31refs, but this suffices to get several new tests passing.

To get the memory64 version of table_grow.wast passing, additionally fix
the interpreter to handle growing 64-bit tables correctly.

Delete the local versions of the upstream tests that can now be run
successfully.
  • Loading branch information
tlively authored Aug 21, 2024
1 parent adf53b3 commit 7889abf
Show file tree
Hide file tree
Showing 11 changed files with 29 additions and 2,234 deletions.
19 changes: 7 additions & 12 deletions scripts/test/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ def get_tests(test_dir, extensions=[], recursive=False):
'align.wast', # Alignment bit 6 used by multi-memory
'binary.wast', # memory.grow reserved byte a LEB in multi-memory
'block.wast', # Requires block parameters
'br_table.wast', # Requires ref.extern wast constants
'bulk.wast', # Requires table.init abbreviation with implicit table
'comments.wast', # Issue with carriage returns being treated as newlines
'const.wast', # Hex float constant not recognized as out of range
Expand All @@ -429,7 +428,7 @@ def get_tests(test_dir, extensions=[], recursive=False):
'float_exprs.wast', # Adding 0 and NaN should give canonical NaN
'float_misc.wast', # Rounding wrong on f64.sqrt
'func.wast', # Duplicate parameter names not properly rejected
'global.wast', # Requires ref.extern wast constants
'global.wast', # Globals allowed to refer to previous globals by GC
'if.wast', # Requires block parameters (on an if)
'imports.wast', # Requires wast `register` support
'linking.wast', # Requires wast `register` support
Expand All @@ -444,12 +443,12 @@ def get_tests(test_dir, extensions=[], recursive=False):
'try_table.wast', # Requires try_table interpretation
'br_on_non_null.wast', # Requires sending values on br_on_non_null
'br_on_null.wast', # Requires sending values on br_on_null
'local_init.wast', # Requires ref.extern wast constants
'local_init.wast', # Requires local validation to respect unnamed blocks
'ref_func.wast', # Requires rejecting undeclared functions references
'ref_is_null.wast', # Requires ref.extern wast constants
'ref_is_null.wast', # Requires ref.null wast constants
'ref_null.wast', # Requires ref.null wast constants
'return_call_indirect.wast', # Requires more precise unreachable validation
'select.wast', # Requires ref.extern wast constants
'select.wast', # Requires ref.null wast constants
'table.wast', # Requires support for table default elements
'type-equivalence.wast', # Recursive types allowed by GC
'unreached-invalid.wast', # Requires more precise unreachable validation
Expand All @@ -458,20 +457,16 @@ def get_tests(test_dir, extensions=[], recursive=False):
'br_if.wast', # Requires more precise branch validation
'br_on_cast.wast', # Requires sending values on br_on_cast
'br_on_cast_fail.wast', # Requires sending values on br_on_cast_fail
'extern.wast', # Requires ref.extern wast constants
'extern.wast', # Requires ref.host wast constants
'i31.wast', # Requires ref.i31 wast constants
'ref_cast.wast', # Requires ref.extern wast constants
'ref_test.wast', # Requires ref.extern wast constants
'ref_cast.wast', # Requires host references to not be externalized i31refs
'ref_test.wast', # Requires host references to not be externalized i31refs
'struct.wast', # Requires ref.struct wast constants
'type-rec.wast', # Requires wast `register` support
'type-subtyping.wast', # ShellExternalInterface::callTable does not handle subtyping
'call_indirect.wast', # Bug with 64-bit inline element segment parsing
'memory64.wast', # Multiple memories now allowed
'table_fill.wast', # Requires ref.extern wast constants
'table_get.wast', # Requires ref.extern wast constants
'table_grow.wast', # Requires ref.extern wast constants
'table_init.wast', # Requires support for elem.drop
'table_set.wast', # Requires ref.extern wast constants
'imports0.wast', # Requires wast `register` support
'imports2.wast', # Requires wast `register` support
'imports3.wast', # Requires wast `register` support
Expand Down
1 change: 1 addition & 0 deletions scripts/test/wasm2js.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
wasm2js_skipped_tests = [
'empty_imported_table.wast',
'br.wast', # depends on multivalue
'br_table.wast', # needs support for externref in assert_return
]


Expand Down
12 changes: 11 additions & 1 deletion src/parser/wast-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ using namespace std::string_view_literals;
namespace {

Result<Literal> const_(Lexer& in) {
// TODO: handle `ref.extern n` as well.
if (in.takeSExprStart("ref.extern"sv)) {
auto n = in.takeI32();
if (!n) {
return in.err("expected host reference payload");
}
if (!in.takeRParen()) {
return in.err("expected end of ref.extern");
}
// Represent host references as externalized i31s.
return Literal::makeI31(*n, Unshared).externalize();
}
return parseConst(in);
}

Expand Down
3 changes: 2 additions & 1 deletion src/tools/wasm-shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ struct Shell {
return Err{err.str()};
}
} else if (auto* ref = std::get_if<RefResult>(&expected)) {
if (!val.type.isRef() || val.type.getHeapType() != ref->type) {
if (!val.type.isRef() ||
!HeapType::isSubType(val.type.getHeapType(), ref->type)) {
err << "expected " << ref->type << " reference, got " << val
<< atIndex();
return Err{err.str()};
Expand Down
13 changes: 5 additions & 8 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3172,20 +3172,17 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
}
auto info = getTableInstanceInfo(curr->table);

Index tableSize = info.interface()->tableSize(info.name);
uint64_t tableSize = info.interface()->tableSize(info.name);
auto* table = info.instance->wasm.getTable(info.name);
Flow ret = Literal::makeFromInt64(tableSize, table->indexType);
Flow fail = Literal::makeFromInt64(-1, table->indexType);
Index delta = deltaFlow.getSingleValue().geti32();
uint64_t delta = deltaFlow.getSingleValue().getUnsigned();

if (tableSize >= uint32_t(-1) - delta) {
uint64_t newSize;
if (std::ckd_add(&newSize, tableSize, delta)) {
return fail;
}
if (uint64_t(tableSize) + uint64_t(delta) > uint64_t(table->max)) {
return fail;
}
Index newSize = tableSize + delta;
if (newSize > WebLimitations::MaxTableSize) {
if (newSize > table->max || newSize > WebLimitations::MaxTableSize) {
return fail;
}
if (!info.interface()->growTable(
Expand Down
3 changes: 3 additions & 0 deletions src/wasm/literal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,9 @@ bool Literal::operator==(const Literal& other) const {
if (type.getHeapType().isMaybeShared(HeapType::i31)) {
return i32 == other.i32;
}
if (type.getHeapType().isMaybeShared(HeapType::ext)) {
return internalize() == other.internalize();
}
WASM_UNREACHABLE("unexpected type");
}
WASM_UNREACHABLE("unexpected type");
Expand Down
Loading

0 comments on commit 7889abf

Please sign in to comment.