Skip to content

Commit 4fc91fe

Browse files
Add support for non-nullable tables and init expressions (#8405)
Unignore previously ignored spec tests `instance.wast`, `ref_is_null.wast`, `table.wast`, `i31.wast`, and `global.wast` which now all pass. Also change the reason for ignoring `array.wast` as it does not (and has not in the past, AFAICT) rely on non-nullable table types. This introduces a breaking change in the C API, as it adds an extra argument to `addTable`. Update the changelog accordingly.
1 parent 29b2d42 commit 4fc91fe

32 files changed

+516
-87
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ v127
3636
and `setValueI64`, previously took a hi/low pair but now take a single value
3737
which can be bigint or a number. Passing two values to these APIs will now
3838
trigger an assertion. (#7984)
39+
- Add support for non-nullable table types and initialization expressions for
40+
tables. This comes with a breaking change to C API: `BinaryenAddTable` takes
41+
an additional `BinaryenExpressionRef` parameter to provide an initialization
42+
expression. This may be set to NULL for tables without an initializer. In JS
43+
this parameter is optional and so is not breaking. (#8405)
3944

4045
v126
4146
----

scripts/test/shared.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -416,28 +416,23 @@ def get_tests(test_dir, extensions=[], recursive=False):
416416
'data.wast', # Fail to parse data segment offset abbreviation
417417
'elem.wast', # Requires modeling empty declarative segments
418418
'func.wast', # Duplicate parameter names not properly rejected
419-
'global.wast', # Fail to parse table
420419
'if.wast', # Requires more precise unreachable validation
421420
'imports.wast', # Requires fixing handling of mutation to imported globals
422421
'proposals/threads/imports.wast', # Missing memory type validation on instantiation
423422
'proposals/threads/memory.wast', # Missing memory type validation on instantiation
424423
'annotations.wast', # String annotations IDs should be allowed
425-
'instance.wast', # Requires support for table default elements
426424
'table64.wast', # Requires validations for table size
427425
'tag.wast', # Non-empty tag results allowed by stack switching
428426
'local_init.wast', # Requires local validation to respect unnamed blocks
429427
'ref_func.wast', # Requires rejecting undeclared functions references
430-
'ref_is_null.wast', # Requires support for non-nullable reference types in tables
431428
'return_call_indirect.wast', # Requires more precise unreachable validation
432429
'select.wast', # Missing validation of type annotation on select
433-
'table.wast', # Requires support for table default elements
434430
'unreached-invalid.wast', # Requires more precise unreachable validation
435-
'array.wast', # Requires support for table default elements
431+
'array.wast', # Failure to parse element segment item abbreviation
436432
'br_if.wast', # Requires more precise branch validation
437433
'br_on_cast.wast', # Requires host references to not be externalized i31refs
438434
'br_on_cast_fail.wast', # Requires host references to not be externalized i31refs
439435
'extern.wast', # Requires ref.host wast constants
440-
'i31.wast', # Requires support for table default elements
441436
'ref_cast.wast', # Requires host references to not be externalized i31refs
442437
'ref_test.wast', # Requires host references to not be externalized i31refs
443438
'struct.wast', # Fails to roundtrip unnamed types e.g. `(ref 0)`

src/binaryen-c.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5339,8 +5339,10 @@ BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module,
53395339
const char* name,
53405340
BinaryenIndex initial,
53415341
BinaryenIndex maximum,
5342-
BinaryenType tableType) {
5342+
BinaryenType tableType,
5343+
BinaryenExpressionRef init) {
53435344
auto table = Builder::makeTable(name, Type(tableType), initial, maximum);
5345+
table->init = init;
53445346
table->hasExplicitName = true;
53455347
return ((Module*)module)->addTable(std::move(table));
53465348
}

src/binaryen-c.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2933,7 +2933,8 @@ BINARYEN_API BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module,
29332933
const char* table,
29342934
BinaryenIndex initial,
29352935
BinaryenIndex maximum,
2936-
BinaryenType tableType);
2936+
BinaryenType tableType,
2937+
BinaryenExpressionRef init);
29372938
BINARYEN_API void BinaryenRemoveTable(BinaryenModuleRef module,
29382939
const char* table);
29392940
BINARYEN_API BinaryenIndex BinaryenGetNumTables(BinaryenModuleRef module);

src/ir/subtype-exprs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
108108
self()->noteSubtype(global->init, global->type);
109109
}
110110
}
111+
void visitTable(Table* table) {
112+
if (table->init) {
113+
self()->noteSubtype(table->init, table->type);
114+
}
115+
}
111116
void visitElementSegment(ElementSegment* seg) {
112117
if (seg->offset) {
113118
self()->noteSubtype(seg->type,

src/js/binaryen.js-post.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2613,8 +2613,8 @@ function wrapModule(module, self = {}) {
26132613
self['getGlobal'] = function(name) {
26142614
return preserveStack(() => Module['_BinaryenGetGlobal'](module, strToStack(name)));
26152615
};
2616-
self['addTable'] = function(table, initial, maximum, type = Module['_BinaryenTypeFuncref']()) {
2617-
return preserveStack(() => Module['_BinaryenAddTable'](module, strToStack(table), initial, maximum, type));
2616+
self['addTable'] = function(table, initial, maximum, type = Module['_BinaryenTypeFuncref'](), init = null) {
2617+
return preserveStack(() => Module['_BinaryenAddTable'](module, strToStack(table), initial, maximum, type, init));
26182618
}
26192619
self['getTable'] = function(name) {
26202620
return preserveStack(() => Module['_BinaryenGetTable'](module, strToStack(name)));

src/parser/context-decls.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ Result<> ParseDeclsCtx::addTable(Name name,
108108
const std::vector<Name>& exports,
109109
ImportNames* import,
110110
TableType type,
111+
std::optional<ExprT>,
111112
Index pos) {
112113
CHECK_ERR(checkImport(pos, import));
113114
auto t = addTableDecl(pos, name, import, type);

src/parser/context-defs.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ Result<> ParseDefsCtx::addGlobal(Name,
6262
return Ok{};
6363
}
6464

65+
Result<> ParseDefsCtx::addTable(Name,
66+
const std::vector<Name>&,
67+
ImportNames*,
68+
TableTypeT,
69+
std::optional<ExprT> init,
70+
Index) {
71+
if (init) {
72+
wasm.tables[index]->init = *init;
73+
}
74+
return Ok{};
75+
}
76+
6577
Result<> ParseDefsCtx::addImplicitElems(Type,
6678
std::vector<Expression*>&& elems) {
6779
auto& e = wasm.elementSegments[implicitElemIndices.at(index)];

src/parser/contexts.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,8 +1130,12 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
11301130
Name name,
11311131
ImportNames* importNames,
11321132
TableType limits);
1133-
Result<>
1134-
addTable(Name, const std::vector<Name>&, ImportNames*, TableType, Index);
1133+
Result<> addTable(Name,
1134+
const std::vector<Name>&,
1135+
ImportNames*,
1136+
TableType,
1137+
std::optional<ExprT>,
1138+
Index);
11351139

11361140
// TODO: Record index of implicit elem for use when parsing types and instrs.
11371141
Result<> addImplicitElems(TypeT, ElemListT&& elems);
@@ -1522,8 +1526,12 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
15221526
return Ok{};
15231527
}
15241528

1525-
Result<> addTable(
1526-
Name, const std::vector<Name>&, ImportNames*, Type ttype, Index pos) {
1529+
Result<> addTable(Name,
1530+
const std::vector<Name>&,
1531+
ImportNames*,
1532+
Type ttype,
1533+
std::optional<ExprT> init,
1534+
Index pos) {
15271535
auto& t = wasm.tables[index];
15281536
if (!ttype.isRef()) {
15291537
return in.err(pos, "expected reference type");
@@ -1887,10 +1895,12 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
18871895
return Ok{};
18881896
}
18891897

1890-
Result<>
1891-
addTable(Name, const std::vector<Name>&, ImportNames*, TableTypeT, Index) {
1892-
return Ok{};
1893-
}
1898+
Result<> addTable(Name,
1899+
const std::vector<Name>&,
1900+
ImportNames*,
1901+
TableTypeT,
1902+
std::optional<ExprT>,
1903+
Index);
18941904

18951905
Result<>
18961906
addMemory(Name, const std::vector<Name>&, ImportNames*, TableTypeT, Index) {

src/parser/parsers.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3433,7 +3433,8 @@ template<typename Ctx> MaybeResult<> import_(Ctx& ctx) {
34333433
auto name = ctx.in.takeID();
34343434
auto type = tabletype(ctx);
34353435
CHECK_ERR(type);
3436-
CHECK_ERR(ctx.addTable(name ? *name : Name{}, {}, &names, *type, pos));
3436+
CHECK_ERR(ctx.addTable(
3437+
name ? *name : Name{}, {}, &names, *type, std::nullopt, pos));
34373438
} else if (ctx.in.takeSExprStart("memory"sv)) {
34383439
auto name = ctx.in.takeID();
34393440
auto type = memtype(ctx);
@@ -3526,7 +3527,11 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
35263527
}
35273528

35283529
// table ::= '(' 'table' id? ('(' 'export' name ')')*
3529-
// '(' 'import' mod:name nm:name ')'? index_type? tabletype ')'
3530+
// index_type? tabletype expr?
3531+
// ')'
3532+
// | '(' 'table' id? ('(' 'export' name ')')*
3533+
// '(' 'import' mod:name nm:name ')' index_type? tabletype
3534+
// ')'
35303535
// | '(' 'table' id? ('(' 'export' name ')')* index_type?
35313536
// reftype '(' 'elem' (elemexpr* | funcidx*) ')' ')'
35323537
template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
@@ -3559,6 +3564,7 @@ template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
35593564

35603565
std::optional<typename Ctx::TableTypeT> ttype;
35613566
std::optional<typename Ctx::ElemListT> elems;
3567+
std::optional<typename Ctx::ExprT> init;
35623568
if (type) {
35633569
// We should have inline elements.
35643570
if (!ctx.in.takeSExprStart("elem"sv)) {
@@ -3593,13 +3599,19 @@ template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
35933599
auto tabtype = tabletypeContinued(ctx, addressType);
35943600
CHECK_ERR(tabtype);
35953601
ttype = *tabtype;
3602+
if (ctx.in.peekLParen() && !import) {
3603+
// Imported tables cannot have initialization expression.
3604+
auto e = expr(ctx);
3605+
CHECK_ERR(e);
3606+
init = *e;
3607+
}
35963608
}
35973609

35983610
if (!ctx.in.takeRParen()) {
35993611
return ctx.in.err("expected end of table declaration");
36003612
}
36013613

3602-
CHECK_ERR(ctx.addTable(name, *exports, import.getPtr(), *ttype, pos));
3614+
CHECK_ERR(ctx.addTable(name, *exports, import.getPtr(), *ttype, init, pos));
36033615

36043616
if (elems) {
36053617
CHECK_ERR(ctx.addImplicitElems(*type, std::move(*elems)));

0 commit comments

Comments
 (0)