summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/parser/context-decls.cpp13
-rw-r--r--src/parser/contexts.h29
-rw-r--r--src/parser/parsers.h43
-rw-r--r--src/passes/Print.cpp3
-rw-r--r--src/tools/wasm-shell.cpp5
-rw-r--r--src/wasm-binary.h2
-rw-r--r--src/wasm-builder.h12
-rw-r--r--src/wasm-interpreter.h37
-rw-r--r--src/wasm.h2
-rw-r--r--src/wasm/wasm-binary.cpp33
-rw-r--r--src/wasm/wasm-validator.cpp72
-rw-r--r--test/spec/table.wast68
-rw-r--r--test/spec/table_fill.wast193
-rw-r--r--test/spec/table_get.wast25
-rw-r--r--test/spec/table_set.wast12
-rw-r--r--test/spec/table_size.wast5
16 files changed, 375 insertions, 179 deletions
diff --git a/src/parser/context-decls.cpp b/src/parser/context-decls.cpp
index c78c47d60..c5b212038 100644
--- a/src/parser/context-decls.cpp
+++ b/src/parser/context-decls.cpp
@@ -82,10 +82,11 @@ Result<> ParseDeclsCtx::addFunc(Name name,
Result<Table*> ParseDeclsCtx::addTableDecl(Index pos,
Name name,
ImportNames* importNames,
- Limits limits) {
+ TableType type) {
auto t = std::make_unique<Table>();
- t->initial = limits.initial;
- t->max = limits.max ? *limits.max : Table::kUnlimitedSize;
+ t->indexType = type.indexType;
+ t->initial = type.limits.initial;
+ t->max = type.limits.max ? *type.limits.max : Table::kUnlimitedSize;
if (name.is()) {
if (wasm.getTableOrNull(name)) {
// TODO: if the existing table is not explicitly named, fix its name and
@@ -105,10 +106,10 @@ Result<Table*> ParseDeclsCtx::addTableDecl(Index pos,
Result<> ParseDeclsCtx::addTable(Name name,
const std::vector<Name>& exports,
ImportNames* import,
- Limits limits,
+ TableType type,
Index pos) {
CHECK_ERR(checkImport(pos, import));
- auto t = addTableDecl(pos, name, import, limits);
+ auto t = addTableDecl(pos, name, import, type);
CHECK_ERR(t);
CHECK_ERR(addExports(in, wasm, *t, exports, ExternalKind::Table));
// TODO: table annotations
@@ -138,7 +139,7 @@ Result<Memory*> ParseDeclsCtx::addMemoryDecl(Index pos,
ImportNames* importNames,
MemType type) {
auto m = std::make_unique<Memory>();
- m->indexType = type.type;
+ m->indexType = type.indexType;
m->initial = type.limits.initial;
m->max = type.limits.max ? *type.limits.max : Memory::kUnlimitedSize;
m->shared = type.shared;
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index cead35f60..8f0c6cdff 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -46,7 +46,7 @@ struct Limits {
};
struct MemType {
- Type type;
+ Type indexType;
Limits limits;
bool shared;
};
@@ -56,6 +56,11 @@ struct Memarg {
uint32_t align;
};
+struct TableType {
+ Type indexType;
+ Limits limits;
+};
+
// The location, possible name, and index in the respective module index space
// of a module-level definition in the input.
struct DefPos {
@@ -853,7 +858,7 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
using LimitsT = Limits;
using ElemListT = Index;
using DataStringT = std::vector<char>;
- using TableTypeT = Limits;
+ using TableTypeT = TableType;
using MemTypeT = MemType;
Lexer in;
@@ -942,7 +947,9 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
Limits getLimitsFromElems(Index elems) { return {elems, elems}; }
- Limits makeTableType(Limits limits, TypeT) { return limits; }
+ TableType makeTableType(Type indexType, Limits limits, TypeT) {
+ return {indexType, limits};
+ }
std::vector<char> makeDataString() { return {}; }
void appendDataString(std::vector<char>& data, std::string_view str) {
@@ -954,8 +961,8 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
return {size, size};
}
- MemType makeMemType(Type type, Limits limits, bool shared) {
- return {type, limits, shared};
+ MemType makeMemType(Type indexType, Limits limits, bool shared) {
+ return {indexType, limits, shared};
}
Result<TypeUseT>
@@ -975,10 +982,12 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::vector<Annotation>&&,
Index pos);
- Result<Table*>
- addTableDecl(Index pos, Name name, ImportNames* importNames, Limits limits);
+ Result<Table*> addTableDecl(Index pos,
+ Name name,
+ ImportNames* importNames,
+ TableType limits);
Result<>
- addTable(Name, const std::vector<Name>&, ImportNames*, Limits, Index);
+ addTable(Name, const std::vector<Name>&, ImportNames*, TableType, Index);
// TODO: Record index of implicit elem for use when parsing types and instrs.
Result<> addImplicitElems(TypeT, ElemListT&& elems);
@@ -1252,7 +1261,7 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
LimitsT getLimitsFromElems(ElemListT) { return Ok{}; }
- Type makeTableType(LimitsT, Type type) { return type; }
+ Type makeTableType(Type indexType, LimitsT, Type type) { return type; }
LimitsT getLimitsFromData(DataStringT) { return Ok{}; }
MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
@@ -1441,7 +1450,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
LimitsT getLimitsFromElems(std::vector<Expression*>& elems) { return Ok{}; }
- TableTypeT makeTableType(LimitsT, Type) { return Ok{}; }
+ TableTypeT makeTableType(Type, LimitsT, Type) { return Ok{}; }
struct CatchInfo {
Name tag;
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index 4d54b3655..30a828822 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -44,6 +44,8 @@ template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx&);
template<typename Ctx>
Result<typename Ctx::MemTypeT> memtypeContinued(Ctx&, Type indexType);
template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx&);
+template<typename Ctx>
+Result<typename Ctx::TableTypeT> tabletypeContinued(Ctx&, Type indexType);
template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx&);
template<typename Ctx> Result<uint32_t> tupleArity(Ctx&);
@@ -815,16 +817,28 @@ Result<typename Ctx::MemTypeT> memtypeContinued(Ctx& ctx, Type indexType) {
return ctx.makeMemType(indexType, *limits, shared);
}
-// tabletype ::= limits32 reftype
+// tabletype ::= (limits32 | 'i32' limits32 | 'i64' limit64) reftype
template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx& ctx) {
- auto limits = limits32(ctx);
+ Type indexType = Type::i32;
+ if (ctx.in.takeKeyword("i64"sv)) {
+ indexType = Type::i64;
+ } else {
+ ctx.in.takeKeyword("i32"sv);
+ }
+ return tabletypeContinued(ctx, indexType);
+}
+
+template<typename Ctx>
+Result<typename Ctx::TableTypeT> tabletypeContinued(Ctx& ctx, Type indexType) {
+ auto limits = indexType == Type::i32 ? limits32(ctx) : limits64(ctx);
CHECK_ERR(limits);
auto type = reftype(ctx);
CHECK_ERR(type);
+
if (!type) {
return ctx.in.err("expected reftype");
}
- return ctx.makeTableType(*limits, *type);
+ return ctx.makeTableType(indexType, *limits, *type);
}
// globaltype ::= t:valtype => const t
@@ -3049,8 +3063,8 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
}
// table ::= '(' 'table' id? ('(' 'export' name ')')*
-// '(' 'import' mod:name nm:name ')'? tabletype ')'
-// | '(' 'table' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')'? index_type? tabletype ')'
+// | '(' 'table' id? ('(' 'export' name ')')* index_type?
// reftype '(' 'elem' (elemexpr* | funcidx*) ')' ')'
template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
auto pos = ctx.in.getPos();
@@ -3069,6 +3083,13 @@ template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
auto import = inlineImport(ctx.in);
CHECK_ERR(import);
+ auto indexType = Type::i32;
+ if (ctx.in.takeKeyword("i64"sv)) {
+ indexType = Type::i64;
+ } else {
+ ctx.in.takeKeyword("i32"sv);
+ }
+
// Reftype if we have inline elements.
auto type = reftype(ctx);
CHECK_ERR(type);
@@ -3103,10 +3124,10 @@ template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of inline elems");
}
- ttype = ctx.makeTableType(ctx.getLimitsFromElems(list), *type);
+ ttype = ctx.makeTableType(indexType, ctx.getLimitsFromElems(list), *type);
elems = std::move(list);
} else {
- auto tabtype = tabletype(ctx);
+ auto tabtype = tabletypeContinued(ctx, indexType);
CHECK_ERR(tabtype);
ttype = *tabtype;
}
@@ -3124,10 +3145,10 @@ template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
return Ok{};
}
-// mem ::= '(' 'memory' id? ('(' 'export' name ')')* index_type?
-// ('(' 'data' b:datastring ')' | memtype) ')'
-// | '(' 'memory' id? ('(' 'export' name ')')*
-// '(' 'import' mod:name nm:name ')' memtype ')'
+// memory ::= '(' 'memory' id? ('(' 'export' name ')')* index_type?
+// ('(' 'data' b:datastring ')' | memtype) ')'
+// | '(' 'memory' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')' index_type? memtype ')'
template<typename Ctx> MaybeResult<> memory(Ctx& ctx) {
auto pos = ctx.in.getPos();
if (!ctx.in.takeSExprStart("memory"sv)) {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 99c86cec4..02eaea21c 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -3088,6 +3088,9 @@ void PrintSExpression::printTableHeader(Table* curr) {
o << '(';
printMedium(o, "table") << ' ';
curr->name.print(o) << ' ';
+ if (curr->is64()) {
+ o << "i64 ";
+ }
o << curr->initial;
if (curr->hasMax()) {
o << ' ' << curr->max;
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 625914cbc..e2709b8ae 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -378,6 +378,11 @@ protected:
spectest->addExport(
builder.makeExport("table", Name::fromInt(0), ExternalKind::Table));
+ spectest->addTable(builder.makeTable(
+ Name::fromInt(1), Type(HeapType::func, Nullable), 10, 20, Type::i64));
+ spectest->addExport(
+ builder.makeExport("table64", Name::fromInt(1), ExternalKind::Table));
+
Memory* memory =
spectest->addMemory(builder.makeMemory(Name::fromInt(0), 1, 2));
spectest->addExport(
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index b5aa83960..9610aed9f 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1552,6 +1552,8 @@ public:
// gets a memory in the combined import+defined space
Memory* getMemory(Index index);
+ // gets a table in the combined import+defined space
+ Table* getTable(Index index);
void getResizableLimits(Address& initial,
Address& max,
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 463faa04f..fbc680d17 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -86,10 +86,12 @@ public:
Type type = Type(HeapType::func,
Nullable),
Address initial = 0,
- Address max = Table::kMaxSize) {
+ Address max = Table::kMaxSize,
+ Type indexType = Type::i32) {
auto table = std::make_unique<Table>();
table->name = name;
table->type = type;
+ table->indexType = indexType;
table->initial = initial;
table->max = max;
return table;
@@ -658,6 +660,8 @@ public:
wasm.getMemory(memoryName)->is64());
}
+ bool isTable64(Name tableName) { return wasm.getTable(tableName)->is64(); }
+
MemorySize* makeMemorySize(Name memoryName,
MemoryInfo info = MemoryInfo::Unspecified) {
auto* ret = wasm.allocator.alloc<MemorySize>();
@@ -729,6 +733,9 @@ public:
TableSize* makeTableSize(Name table) {
auto* ret = wasm.allocator.alloc<TableSize>();
ret->table = table;
+ if (isTable64(table)) {
+ ret->type = Type::i64;
+ }
ret->finalize();
return ret;
}
@@ -737,6 +744,9 @@ public:
ret->table = table;
ret->value = value;
ret->delta = delta;
+ if (isTable64(table)) {
+ ret->type = Type::i64;
+ }
ret->finalize();
return ret;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index a9bba774b..bfc1a27c5 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -3086,8 +3086,11 @@ public:
return index;
}
auto info = getTableInterfaceInfo(curr->table);
- return info.interface->tableLoad(info.name,
- index.getSingleValue().geti32());
+ auto* table = wasm.getTable(info.name);
+ auto address = table->indexType == Type::i64
+ ? index.getSingleValue().geti64()
+ : index.getSingleValue().geti32();
+ return info.interface->tableLoad(info.name, address);
}
Flow visitTableSet(TableSet* curr) {
NOTE_ENTER("TableSet");
@@ -3100,17 +3103,20 @@ public:
return valueFlow;
}
auto info = getTableInterfaceInfo(curr->table);
- info.interface->tableStore(info.name,
- indexFlow.getSingleValue().geti32(),
- valueFlow.getSingleValue());
+ auto* table = wasm.getTable(info.name);
+ auto address = table->indexType == Type::i64
+ ? indexFlow.getSingleValue().geti64()
+ : indexFlow.getSingleValue().geti32();
+ info.interface->tableStore(info.name, address, valueFlow.getSingleValue());
return Flow();
}
Flow visitTableSize(TableSize* curr) {
NOTE_ENTER("TableSize");
auto info = getTableInterfaceInfo(curr->table);
+ auto* table = wasm.getTable(info.name);
Index tableSize = info.interface->tableSize(curr->table);
- return Literal::makeFromInt32(tableSize, Type::i32);
+ return Literal::makeFromInt64(tableSize, table->indexType);
}
Flow visitTableGrow(TableGrow* curr) {
@@ -3126,16 +3132,16 @@ public:
Name tableName = curr->table;
auto info = getTableInterfaceInfo(tableName);
- Index tableSize = info.interface->tableSize(tableName);
- Flow ret = Literal::makeFromInt32(tableSize, Type::i32);
- Flow fail = Literal::makeFromInt32(-1, Type::i32);
+ Index tableSize = info.interface->tableSize(info.name);
+ auto* table = self()->wasm.getTable(info.name);
+ Flow ret = Literal::makeFromInt64(tableSize, table->indexType);
+ Flow fail = Literal::makeFromInt64(-1, table->indexType);
Index delta = deltaFlow.getSingleValue().geti32();
if (tableSize >= uint32_t(-1) - delta) {
return fail;
}
- auto maxTableSize = self()->wasm.getTable(tableName)->max;
- if (uint64_t(tableSize) + uint64_t(delta) > uint64_t(maxTableSize)) {
+ if (uint64_t(tableSize) + uint64_t(delta) > uint64_t(table->max)) {
return fail;
}
Index newSize = tableSize + delta;
@@ -3168,9 +3174,14 @@ public:
Name tableName = curr->table;
auto info = getTableInterfaceInfo(tableName);
- Index dest = destFlow.getSingleValue().geti32();
+ auto* table = self()->wasm.getTable(info.name);
+ Index dest = table->indexType == Type::i64
+ ? destFlow.getSingleValue().geti64()
+ : destFlow.getSingleValue().geti32();
Literal value = valueFlow.getSingleValue();
- Index size = sizeFlow.getSingleValue().geti32();
+ Index size = table->indexType == Type::i64
+ ? sizeFlow.getSingleValue().geti64()
+ : sizeFlow.getSingleValue().geti32();
Index tableSize = info.interface->tableSize(tableName);
if (dest + size > tableSize) {
diff --git a/src/wasm.h b/src/wasm.h
index ebc7b908d..2f3f09ad5 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -2276,9 +2276,11 @@ public:
Address initial = 0;
Address max = kMaxSize;
+ Type indexType = Type::i32;
Type type = Type(HeapType::func, Nullable);
bool hasMax() { return max != kUnlimitedSize; }
+ bool is64() { return indexType == Type::i64; }
void clear() {
name = "";
initial = 0;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 7e90dfde4..c1e506c8e 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -746,7 +746,7 @@ void WasmBinaryWriter::writeTableDeclarations() {
table->max,
table->hasMax(),
/*shared=*/false,
- /*is64*/ false);
+ table->is64());
});
finishSection(start);
}
@@ -2462,6 +2462,13 @@ Name WasmBinaryReader::getGlobalName(Index index) {
return wasm.globals[index]->name;
}
+Table* WasmBinaryReader::getTable(Index index) {
+ if (index < wasm.tables.size()) {
+ return wasm.tables[index].get();
+ }
+ throwError("Table index out of range.");
+}
+
Name WasmBinaryReader::getTagName(Index index) {
if (index >= wasm.tags.size()) {
throwError("invalid tag index");
@@ -2555,18 +2562,14 @@ void WasmBinaryReader::readImports() {
table->type = getType();
bool is_shared;
- Type indexType;
getResizableLimits(table->initial,
table->max,
is_shared,
- indexType,
+ table->indexType,
Table::kUnlimitedSize);
if (is_shared) {
throwError("Tables may not be shared");
}
- if (indexType == Type::i64) {
- throwError("Tables may not be 64-bit");
- }
wasm.addTable(std::move(table));
break;
@@ -3355,16 +3358,14 @@ void WasmBinaryReader::readTableDeclarations() {
}
auto table = Builder::makeTable(Name::fromInt(i), elemType);
bool is_shared;
- Type indexType;
- getResizableLimits(
- table->initial, table->max, is_shared, indexType, Table::kUnlimitedSize);
+ getResizableLimits(table->initial,
+ table->max,
+ is_shared,
+ table->indexType,
+ Table::kUnlimitedSize);
if (is_shared) {
throwError("Tables may not be shared");
}
- if (indexType == Type::i64) {
- throwError("Tables may not be 64-bit");
- }
-
wasm.addTable(std::move(table));
}
}
@@ -5511,6 +5512,9 @@ bool WasmBinaryReader::maybeVisitTableSize(Expression*& out, uint32_t code) {
throwError("bad table index");
}
auto* curr = allocator.alloc<TableSize>();
+ if (getTable(tableIdx)->is64()) {
+ curr->type = Type::i64;
+ }
curr->finalize();
// Defer setting the table name for later, when we know it.
tableRefs[tableIdx].push_back(&curr->table);
@@ -5529,6 +5533,9 @@ bool WasmBinaryReader::maybeVisitTableGrow(Expression*& out, uint32_t code) {
auto* curr = allocator.alloc<TableGrow>();
curr->delta = popNonVoidExpression();
curr->value = popNonVoidExpression();
+ if (getTable(tableIdx)->is64()) {
+ curr->type = Type::i64;
+ }
curr->finalize();
// Defer setting the table name for later, when we know it.
tableRefs[tableIdx].push_back(&curr->table);
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index aad9a0582..8f3082b41 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -947,15 +947,16 @@ void FunctionValidator::visitCall(Call* curr) {
void FunctionValidator::visitCallIndirect(CallIndirect* curr) {
validateReturnCall(curr);
- shouldBeEqualOrFirstIsUnreachable(curr->target->type,
- Type(Type::i32),
- curr,
- "indirect call target must be an i32");
if (curr->target->type != Type::unreachable) {
auto* table = getModule()->getTableOrNull(curr->table);
- shouldBeTrue(!!table, curr, "call-indirect table must exist");
- if (table) {
+ if (shouldBeTrue(!!table, curr, "call-indirect table must exist")) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->target->type,
+ table->indexType,
+ curr,
+ "call-indirect call target must match the table index type");
+ shouldBeTrue(!!table, curr, "call-indirect table must exist");
shouldBeTrue(table->type.isFunction(),
curr,
"call-indirect table must be of function type.");
@@ -2267,13 +2268,19 @@ void FunctionValidator::visitTableGet(TableGet* curr) {
shouldBeTrue(getModule()->features.hasReferenceTypes(),
curr,
"table.get requires reference types [--enable-reference-types]");
- shouldBeEqualOrFirstIsUnreachable(
- curr->index->type, Type(Type::i32), curr, "table.get index must be an i32");
auto* table = getModule()->getTableOrNull(curr->table);
- if (shouldBeTrue(!!table, curr, "table.get table must exist") &&
- curr->type != Type::unreachable) {
- shouldBeEqual(
- curr->type, table->type, curr, "table.get must have same type as table.");
+ if (shouldBeTrue(!!table, curr, "table.get table must exist")) {
+ if (curr->type != Type::unreachable) {
+ shouldBeEqual(curr->type,
+ table->type,
+ curr,
+ "table.get must have same type as table.");
+ }
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->index->type,
+ table->indexType,
+ curr,
+ "table.get index must match the table index type.");
}
}
@@ -2281,15 +2288,19 @@ void FunctionValidator::visitTableSet(TableSet* curr) {
shouldBeTrue(getModule()->features.hasReferenceTypes(),
curr,
"table.set requires reference types [--enable-reference-types]");
- shouldBeEqualOrFirstIsUnreachable(
- curr->index->type, Type(Type::i32), curr, "table.set index must be an i32");
auto* table = getModule()->getTableOrNull(curr->table);
- if (shouldBeTrue(!!table, curr, "table.set table must exist") &&
- curr->type != Type::unreachable) {
- shouldBeSubType(curr->value->type,
- table->type,
- curr,
- "table.set value must have right type");
+ if (shouldBeTrue(!!table, curr, "table.set table must exist")) {
+ if (curr->type != Type::unreachable) {
+ shouldBeSubType(curr->value->type,
+ table->type,
+ curr,
+ "table.set value must have right type");
+ }
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->index->type,
+ table->indexType,
+ curr,
+ "table.set index must match the table index type.");
}
}
@@ -2315,7 +2326,7 @@ void FunctionValidator::visitTableGrow(TableGrow* curr) {
curr,
"table.grow value must have right type");
shouldBeEqual(curr->delta->type,
- Type(Type::i32),
+ table->indexType,
curr,
"table.grow must match table index type");
}
@@ -2331,11 +2342,17 @@ void FunctionValidator::visitTableFill(TableFill* curr) {
table->type,
curr,
"table.fill value must have right type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->dest->type,
+ table->indexType,
+ curr,
+ "table.fill dest must match table index type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->size->type,
+ table->indexType,
+ curr,
+ "table.fill size must match table index type");
}
- shouldBeEqualOrFirstIsUnreachable(
- curr->dest->type, Type(Type::i32), curr, "table.fill dest must be i32");
- shouldBeEqualOrFirstIsUnreachable(
- curr->size->type, Type(Type::i32), curr, "table.fill size must be i32");
}
void FunctionValidator::visitTableCopy(TableCopy* curr) {
@@ -3841,6 +3858,11 @@ static void validateTables(Module& module, ValidationInfo& info) {
"Only funcref and externref are valid for table type "
"(when gc is disabled)");
}
+ if (table->is64()) {
+ info.shouldBeTrue(module.features.hasMemory64(),
+ "memory",
+ "64-bit tables require memory64 [--enable-memory64]");
+ }
}
for (auto& segment : module.elementSegments) {
diff --git a/test/spec/table.wast b/test/spec/table.wast
index 54281a03e..d61a7d1b7 100644
--- a/test/spec/table.wast
+++ b/test/spec/table.wast
@@ -1,24 +1,76 @@
-
;; Test table section structure
(module (table 0 funcref))
-(module (table 0 (ref null func)))
(module (table 1 funcref))
(module (table 0 0 funcref))
(module (table 0 1 funcref))
-(module (table 0 1 (ref null func)))
(module (table 1 256 funcref))
-(module (table 0 65536 externref))
-;; (module (table 0 0xffff_ffff funcref))
+(module (table 0 65536 funcref))
+(module (table 0 0xffff_ffff funcref))
(module (table 0 funcref) (table 0 funcref))
(module (table (import "spectest" "table") 0 funcref) (table 0 funcref))
-(assert_invalid (module (elem (i32.const 0))) "unknown table")
-(assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table")
+(assert_invalid
+ (module (table 1 0 funcref))
+ "size minimum must not be greater than maximum"
+)
+(assert_invalid
+ (module (table 0xffff_ffff 0 funcref))
+ "size minimum must not be greater than maximum"
+)
+(assert_invalid
+ (module quote "(table 0x1_0000_0000 funcref)")
+ "table size must be at most 2^32-1"
+)
+(assert_invalid
+ (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)")
+ "table size must be at most 2^32-1"
+)
+(assert_invalid
+ (module quote "(table 0 0x1_0000_0000 funcref)")
+ "table size must be at most 2^32-1"
+)
+
+;; Same as above but with i64 index types
+
+(module (table i64 0 funcref))
+(module (table i64 1 funcref))
+(module (table i64 0 0 funcref))
+(module (table i64 0 1 funcref))
+(module (table i64 1 256 funcref))
+(module (table i64 0 65536 funcref))
+(module (table i64 0 0xffff_ffff funcref))
+
+(module (table i64 0 funcref) (table i64 0 funcref))
+(module (table (import "spectest" "table64") i64 0 funcref) (table i64 0 funcref))
(assert_invalid
- (module (table 1 0 funcref))
+ (module (table i64 1 0 funcref))
+ "size minimum must not be greater than maximum"
+)
+(assert_invalid
+ (module (table i64 0xffff_ffff 0 funcref))
"size minimum must not be greater than maximum"
)
+
+;; Elem segments with no table
+
+(assert_invalid (module (elem (i32.const 0))) "unknown table")
+(assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table")
+
+;; Duplicate table identifiers
+
+(assert_malformed (module quote
+ "(table $foo 1 funcref)"
+ "(table $foo 1 funcref)")
+ "duplicate table")
+(assert_malformed (module quote
+ "(import \"\" \"\" (table $foo 1 funcref))"
+ "(table $foo 1 funcref)")
+ "duplicate table")
+(assert_malformed (module quote
+ "(import \"\" \"\" (table $foo 1 funcref))"
+ "(import \"\" \"\" (table $foo 1 funcref))")
+ "duplicate table")
diff --git a/test/spec/table_fill.wast b/test/spec/table_fill.wast
index 79b5a6a83..5471e2e23 100644
--- a/test/spec/table_fill.wast
+++ b/test/spec/table_fill.wast
@@ -1,115 +1,138 @@
(module
- (type $f (func (result i32)))
+ (table $t 10 externref)
- (table $t 10 funcref)
-
- (func $0 (result i32)
- (i32.const 0)
- )
-
- (func $1 (result i32)
- (i32.const 1)
- )
-
- (func $2 (result i32)
- (i32.const 2)
- )
-
- (func $3 (result i32)
- (i32.const 3)
- )
-
- (func $4 (result i32)
- (i32.const 4)
- )
-
- (func (export "fill-0") (param $i i32) (param $n i32)
- (table.fill $t (local.get $i) (ref.func $0) (local.get $n))
- )
-
- (func (export "fill-1") (param $i i32) (param $n i32)
- (table.fill $t (local.get $i) (ref.func $1) (local.get $n))
+ (func (export "fill") (param $i i32) (param $r externref) (param $n i32)
+ (table.fill $t (local.get $i) (local.get $r) (local.get $n))
)
- (func (export "fill-2") (param $i i32) (param $n i32)
- (table.fill $t (local.get $i) (ref.func $2) (local.get $n))
+ (func (export "fill-abbrev") (param $i i32) (param $r externref) (param $n i32)
+ (table.fill (local.get $i) (local.get $r) (local.get $n))
)
- (func (export "fill-3") (param $i i32) (param $n i32)
- (table.fill $t (local.get $i) (ref.func $3) (local.get $n))
+ (func (export "get") (param $i i32) (result externref)
+ (table.get $t (local.get $i))
)
- (func (export "fill-4") (param $i i32) (param $n i32)
- (table.fill $t (local.get $i) (ref.func $4) (local.get $n))
- )
+ (table $t64 i64 10 externref)
- (func (export "fill-null") (param $i i32) (param $n i32)
- (table.fill $t (local.get $i) (ref.null func) (local.get $n))
+ (func (export "fill-t64") (param $i i64) (param $r externref) (param $n i64)
+ (table.fill $t64 (local.get $i) (local.get $r) (local.get $n))
)
- (func (export "get-null") (param $i i32) (result funcref)
- (table.get $t (local.get $i))
- )
-
- (func (export "get") (param $i i32) (result i32)
- (call_indirect $t (type $f) (local.get $i))
+ (func (export "get-t64") (param $i i64) (result externref)
+ (table.get $t64 (local.get $i))
)
)
-(assert_return (invoke "get-null" (i32.const 1)) (ref.null func))
-(assert_return (invoke "get-null" (i32.const 2)) (ref.null func))
-(assert_return (invoke "get-null" (i32.const 3)) (ref.null func))
-(assert_return (invoke "get-null" (i32.const 4)) (ref.null func))
-(assert_return (invoke "get-null" (i32.const 5)) (ref.null func))
-
-(assert_return (invoke "fill-1" (i32.const 2) (i32.const 3)))
-(assert_return (invoke "get-null" (i32.const 1)) (ref.null func))
-(assert_return (invoke "get" (i32.const 2)) (i32.const 1))
-(assert_return (invoke "get" (i32.const 3)) (i32.const 1))
-(assert_return (invoke "get" (i32.const 4)) (i32.const 1))
-(assert_return (invoke "get-null" (i32.const 5)) (ref.null func))
-
-(assert_return (invoke "fill-2" (i32.const 4) (i32.const 2)))
-(assert_return (invoke "get" (i32.const 3)) (i32.const 1))
-(assert_return (invoke "get" (i32.const 4)) (i32.const 2))
-(assert_return (invoke "get" (i32.const 5)) (i32.const 2))
-(assert_return (invoke "get-null" (i32.const 6)) (ref.null func))
-
-(assert_return (invoke "fill-3" (i32.const 4) (i32.const 0)))
-(assert_return (invoke "get" (i32.const 3)) (i32.const 1))
-(assert_return (invoke "get" (i32.const 4)) (i32.const 2))
-(assert_return (invoke "get" (i32.const 5)) (i32.const 2))
-
-(assert_return (invoke "fill-4" (i32.const 8) (i32.const 2)))
-(assert_return (invoke "get-null" (i32.const 7)) (ref.null func))
-(assert_return (invoke "get" (i32.const 8)) (i32.const 4))
-(assert_return (invoke "get" (i32.const 9)) (i32.const 4))
-
-(assert_return (invoke "fill-null" (i32.const 9) (i32.const 1)))
-(assert_return (invoke "get" (i32.const 8)) (i32.const 4))
-(assert_return (invoke "get-null" (i32.const 9)) (ref.null func))
-
-(assert_return (invoke "fill-1" (i32.const 10) (i32.const 0)))
-(assert_return (invoke "get-null" (i32.const 9)) (ref.null func))
+(assert_return (invoke "get" (i32.const 1)) (ref.null extern))
+(assert_return (invoke "get" (i32.const 2)) (ref.null extern))
+(assert_return (invoke "get" (i32.const 3)) (ref.null extern))
+(assert_return (invoke "get" (i32.const 4)) (ref.null extern))
+(assert_return (invoke "get" (i32.const 5)) (ref.null extern))
+
+;; (assert_return (invoke "fill" (i32.const 2) (ref.extern 1) (i32.const 3)))
+(assert_return (invoke "get" (i32.const 1)) (ref.null extern))
+;; (assert_return (invoke "get" (i32.const 2)) (ref.extern 1))
+;; (assert_return (invoke "get" (i32.const 3)) (ref.extern 1))
+;; (assert_return (invoke "get" (i32.const 4)) (ref.extern 1))
+(assert_return (invoke "get" (i32.const 5)) (ref.null extern))
+
+;; (assert_return (invoke "fill" (i32.const 4) (ref.extern 2) (i32.const 2)))
+;; (assert_return (invoke "get" (i32.const 3)) (ref.extern 1))
+;; (assert_return (invoke "get" (i32.const 4)) (ref.extern 2))
+;; (assert_return (invoke "get" (i32.const 5)) (ref.extern 2))
+(assert_return (invoke "get" (i32.const 6)) (ref.null extern))
+
+;; (assert_return (invoke "fill" (i32.const 4) (ref.extern 3) (i32.const 0)))
+;; (assert_return (invoke "get" (i32.const 3)) (ref.extern 1))
+;; (assert_return (invoke "get" (i32.const 4)) (ref.extern 2))
+;; (assert_return (invoke "get" (i32.const 5)) (ref.extern 2))
+
+;; (assert_return (invoke "fill" (i32.const 8) (ref.extern 4) (i32.const 2)))
+(assert_return (invoke "get" (i32.const 7)) (ref.null extern))
+;; (assert_return (invoke "get" (i32.const 8)) (ref.extern 4))
+;; (assert_return (invoke "get" (i32.const 9)) (ref.extern 4))
+
+(assert_return (invoke "fill-abbrev" (i32.const 9) (ref.null extern) (i32.const 1)))
+;; (assert_return (invoke "get" (i32.const 8)) (ref.extern 4))
+(assert_return (invoke "get" (i32.const 9)) (ref.null extern))
+
+;; (assert_return (invoke "fill" (i32.const 10) (ref.extern 5) (i32.const 0)))
+(assert_return (invoke "get" (i32.const 9)) (ref.null extern))
+
+;; (assert_trap
+;; (invoke "fill" (i32.const 8) (ref.extern 6) (i32.const 3))
+;; "out of bounds table access"
+;;)
+(assert_return (invoke "get" (i32.const 7)) (ref.null extern))
+;;(assert_return (invoke "get" (i32.const 8)) (ref.extern 4))
+(assert_return (invoke "get" (i32.const 9)) (ref.null extern))
(assert_trap
- (invoke "fill-2" (i32.const 8) (i32.const 3))
+ (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 0))
"out of bounds table access"
)
-(assert_return (invoke "get-null" (i32.const 7)) (ref.null func))
-(assert_return (invoke "get" (i32.const 8)) (i32.const 4))
-(assert_return (invoke "get-null" (i32.const 9)) (ref.null func))
(assert_trap
- (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 0))
+ (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 10))
"out of bounds table access"
)
+;; Same as above but for t64
+
+(assert_return (invoke "get-t64" (i64.const 1)) (ref.null extern))
+(assert_return (invoke "get-t64" (i64.const 2)) (ref.null extern))
+(assert_return (invoke "get-t64" (i64.const 3)) (ref.null extern))
+(assert_return (invoke "get-t64" (i64.const 4)) (ref.null extern))
+(assert_return (invoke "get-t64" (i64.const 5)) (ref.null extern))
+
+;; (assert_return (invoke "fill-t64" (i64.const 2) (ref.extern 1) (i64.const 3)))
+(assert_return (invoke "get-t64" (i64.const 1)) (ref.null extern))
+;; (assert_return (invoke "get-t64" (i64.const 2)) (ref.extern 1))
+;; (assert_return (invoke "get-t64" (i64.const 3)) (ref.extern 1))
+;; (assert_return (invoke "get-t64" (i64.const 4)) (ref.extern 1))
+(assert_return (invoke "get-t64" (i64.const 5)) (ref.null extern))
+
+;; (assert_return (invoke "fill-t64" (i64.const 4) (ref.extern 2) (i64.const 2)))
+;; (assert_return (invoke "get-t64" (i64.const 3)) (ref.extern 1))
+;; (assert_return (invoke "get-t64" (i64.const 4)) (ref.extern 2))
+;; (assert_return (invoke "get-t64" (i64.const 5)) (ref.extern 2))
+(assert_return (invoke "get-t64" (i64.const 6)) (ref.null extern))
+
+;; (assert_return (invoke "fill-t64" (i64.const 4) (ref.extern 3) (i64.const 0)))
+;; (assert_return (invoke "get-t64" (i64.const 3)) (ref.extern 1))
+;; (assert_return (invoke "get-t64" (i64.const 4)) (ref.extern 2))
+;; (assert_return (invoke "get-t64" (i64.const 5)) (ref.extern 2))
+
+;; (assert_return (invoke "fill-t64" (i64.const 8) (ref.extern 4) (i64.const 2)))
+(assert_return (invoke "get-t64" (i64.const 7)) (ref.null extern))
+;; (assert_return (invoke "get-t64" (i64.const 8)) (ref.extern 4))
+;; (assert_return (invoke "get-t64" (i64.const 9)) (ref.extern 4))
+
+(assert_return (invoke "fill-t64" (i64.const 9) (ref.null extern) (i64.const 1)))
+;; (assert_return (invoke "get-t64" (i64.const 8)) (ref.extern 4))
+(assert_return (invoke "get-t64" (i64.const 9)) (ref.null extern))
+
+;; (assert_return (invoke "fill-t64" (i64.const 10) (ref.extern 5) (i64.const 0)))
+(assert_return (invoke "get-t64" (i64.const 9)) (ref.null extern))
+
+;; (assert_trap
+;; (invoke "fill-t64" (i64.const 8) (ref.extern 6) (i64.const 3))
+;; "out of bounds table access"
+;; )
+(assert_return (invoke "get-t64" (i64.const 7)) (ref.null extern))
+;; (assert_return (invoke "get-t64" (i64.const 8)) (ref.extern 4))
+(assert_return (invoke "get-t64" (i64.const 9)) (ref.null extern))
+
(assert_trap
- (invoke "fill" (i32.const 11) (ref.null extern) (i32.const 10))
+ (invoke "fill-t64" (i64.const 11) (ref.null extern) (i64.const 0))
"out of bounds table access"
)
+(assert_trap
+ (invoke "fill-t64" (i64.const 11) (ref.null extern) (i64.const 10))
+ "out of bounds table access"
+)
;; Type errors
diff --git a/test/spec/table_get.wast b/test/spec/table_get.wast
index de5f6622a..cc24ba3ec 100644
--- a/test/spec/table_get.wast
+++ b/test/spec/table_get.wast
@@ -1,30 +1,45 @@
(module
(table $t2 2 externref)
(table $t3 3 funcref)
+ (table $t64 i64 3 funcref)
(elem (table $t3) (i32.const 1) func $dummy)
(func $dummy)
+ (func (export "init")
+ ;; (table.set $t2 (i32.const 1) (local.get $r))
+ (table.set $t3 (i32.const 2) (table.get $t3 (i32.const 1)))
+ )
+
(func (export "get-externref") (param $i i32) (result externref)
- (table.get $t2 (local.get $i))
+ (table.get (local.get $i))
)
(func $f3 (export "get-funcref") (param $i i32) (result funcref)
(table.get $t3 (local.get $i))
)
+ (func $f4 (export "get-funcref-t64") (param $i i64) (result funcref)
+ (table.get $t64 (local.get $i))
+ )
(func (export "is_null-funcref") (param $i i32) (result i32)
(ref.is_null (call $f3 (local.get $i)))
)
)
+;; (invoke "init" (ref.extern 1))
+(invoke "init")
+
(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern))
+;; (assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1))
(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func))
+(assert_return (invoke "get-funcref-t64" (i64.const 0)) (ref.null func))
(assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0))
+(assert_return (invoke "is_null-funcref" (i32.const 2)) (i32.const 0))
-(assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds")
-(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds")
-(assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds")
-(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds")
+(assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds table access")
+(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access")
+(assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds table access")
+(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access")
;; Type errors
diff --git a/test/spec/table_set.wast b/test/spec/table_set.wast
index 6034f146f..7ec3fc6c2 100644
--- a/test/spec/table_set.wast
+++ b/test/spec/table_set.wast
@@ -1,6 +1,7 @@
(module
(table $t2 1 externref)
(table $t3 2 funcref)
+ (table $t64 i64 2 funcref)
(elem (table $t3) (i32.const 1) func $dummy)
(func $dummy)
@@ -10,9 +11,12 @@
(func $f3 (export "get-funcref") (param $i i32) (result funcref)
(table.get $t3 (local.get $i))
)
+ (func $f4 (export "get-funcref-t64") (param $i i64) (result funcref)
+ (table.get $t64 (local.get $i))
+ )
(func (export "set-externref") (param $i i32) (param $r externref)
- (table.set $t2 (local.get $i) (local.get $r))
+ (table.set (local.get $i) (local.get $r))
)
(func (export "set-funcref") (param $i i32) (param $r funcref)
(table.set $t3 (local.get $i) (local.get $r))
@@ -20,6 +24,9 @@
(func (export "set-funcref-from") (param $i i32) (param $j i32)
(table.set $t3 (local.get $i) (table.get $t3 (local.get $j)))
)
+ (func (export "set-funcref-t64") (param $i i64) (param $r funcref)
+ (table.set $t64 (local.get $i) (local.get $r))
+ )
(func (export "is_null-funcref") (param $i i32) (result i32)
(ref.is_null (call $f3 (local.get $i)))
@@ -32,6 +39,9 @@
(assert_return (invoke "set-externref" (i32.const 0) (ref.null extern)))
(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern))
+(assert_return (invoke "set-funcref-t64" (i64.const 0) (ref.null func)))
+(assert_return (invoke "get-funcref-t64" (i64.const 0)) (ref.null func))
+
(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func))
(assert_return (invoke "set-funcref-from" (i32.const 0) (i32.const 1)))
(assert_return (invoke "is_null-funcref" (i32.const 0)) (i32.const 0))
diff --git a/test/spec/table_size.wast b/test/spec/table_size.wast
index ad293b5ee..71081d7e6 100644
--- a/test/spec/table_size.wast
+++ b/test/spec/table_size.wast
@@ -3,11 +3,13 @@
(table $t1 1 externref)
(table $t2 0 2 externref)
(table $t3 3 8 externref)
+ (table $t64 i64 42 42 externref)
- (func (export "size-t0") (result i32) (table.size $t0))
+ (func (export "size-t0") (result i32) table.size)
(func (export "size-t1") (result i32) (table.size $t1))
(func (export "size-t2") (result i32) (table.size $t2))
(func (export "size-t3") (result i32) (table.size $t3))
+ (func (export "size-t64") (result i64) (table.size $t64))
(func (export "grow-t0") (param $sz i32)
(drop (table.grow $t0 (ref.null extern) (local.get $sz)))
@@ -63,6 +65,7 @@
(assert_return (invoke "grow-t3" (i32.const 1)))
(assert_return (invoke "size-t3") (i32.const 8))
+(assert_return (invoke "size-t64") (i64.const 42))
;; Type errors