summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py5
-rw-r--r--src/gen-s-parser.inc3
-rw-r--r--src/ir/ReFinalize.cpp1
-rw-r--r--src/ir/cost.h1
-rw-r--r--src/ir/effects.h5
-rw-r--r--src/passes/Print.cpp4
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp19
-rw-r--r--src/shell-interface.h16
-rw-r--r--src/tools/wasm-ctor-eval.cpp4
-rw-r--r--src/wasm-binary.h3
-rw-r--r--src/wasm-builder.h8
-rw-r--r--src/wasm-delegations-fields.def7
-rw-r--r--src/wasm-delegations.def1
-rw-r--r--src/wasm-interpreter.h69
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm.h12
-rw-r--r--src/wasm/wasm-binary.cpp19
-rw-r--r--src/wasm/wasm-s-parser.cpp10
-rw-r--r--src/wasm/wasm-stack.cpp5
-rw-r--r--src/wasm/wasm-validator.cpp15
-rw-r--r--src/wasm/wasm.cpp7
-rw-r--r--src/wasm2js.h4
-rw-r--r--test/binaryen.js/exception-handling.js.txt8
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt42
-rw-r--r--test/lit/table-operations.wast108
-rw-r--r--test/spec/table_get.wast79
26 files changed, 404 insertions, 52 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index cc9ffc7c3..0c71644aa 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -516,10 +516,12 @@ instructions = [
("f64x2.promote_low_f32x4", "makeUnary(s, UnaryOp::PromoteLowVecF32x4ToVecF64x2)"),
# reference types instructions
- # TODO Add table instructions
("ref.null", "makeRefNull(s)"),
("ref.is_null", "makeRefIs(s, RefIsNull)"),
("ref.func", "makeRefFunc(s)"),
+ ("ref.eq", "makeRefEq(s)"),
+ # TODO Add table instructions
+ ("table.get", "makeTableGet(s)"),
# exception handling instructions
("try", "makeTry(s)"),
("throw", "makeThrow(s)"),
@@ -532,7 +534,6 @@ instructions = [
("call_ref", "makeCallRef(s, /*isReturn=*/false)"),
("return_call_ref", "makeCallRef(s, /*isReturn=*/true)"),
# GC
- ("ref.eq", "makeRefEq(s)"),
("i31.new", "makeI31New(s)"),
("i31.get_s", "makeI31Get(s, true)"),
("i31.get_u", "makeI31Get(s, false)"),
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 69ce0dbbc..984ebf6b2 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3041,6 +3041,9 @@ switch (op[0]) {
}
case 't': {
switch (op[1]) {
+ case 'a':
+ if (strcmp(op, "table.get") == 0) { return makeTableGet(s); }
+ goto parse_error;
case 'h': {
switch (op[2]) {
case 'e':
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 0d089a7ce..4f9c7c849 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -135,6 +135,7 @@ void ReFinalize::visitRefFunc(RefFunc* curr) {
// this class has been meant to do.
}
void ReFinalize::visitRefEq(RefEq* curr) { curr->finalize(); }
+void ReFinalize::visitTableGet(TableGet* curr) { curr->finalize(); }
void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); }
diff --git a/src/ir/cost.h b/src/ir/cost.h
index 29fcb8dd4..12797b0b5 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -540,6 +540,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
CostType visitRefEq(RefEq* curr) {
return 1 + visit(curr->left) + visit(curr->right);
}
+ CostType visitTableGet(TableGet* curr) { return 1 + visit(curr->index); }
CostType visitTry(Try* curr) {
// We assume no exception will be thrown in most cases
return visit(curr->body);
diff --git a/src/ir/effects.h b/src/ir/effects.h
index ee45d8205..22e1e8149 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -589,6 +589,11 @@ private:
void visitRefIs(RefIs* curr) {}
void visitRefFunc(RefFunc* curr) {}
void visitRefEq(RefEq* curr) {}
+ void visitTableGet(TableGet* curr) {
+ // TODO: track readsTable/writesTable, like memory?
+ // Traps when the index is out of bounds for the table.
+ parent.implicitTrap = true;
+ }
void visitTry(Try* curr) {}
void visitThrow(Throw* curr) {
if (parent.tryDepth == 0) {
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 6a41961a2..4d6207b01 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1842,6 +1842,10 @@ struct PrintExpressionContents
printName(curr->func, o);
}
void visitRefEq(RefEq* curr) { printMedium(o, "ref.eq"); }
+ void visitTableGet(TableGet* curr) {
+ printMedium(o, "table.get ");
+ printName(curr->table, o);
+ }
void visitTry(Try* curr) {
printMedium(o, "try");
if (curr->name.is()) {
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 2b460a0fd..7436676ef 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -92,18 +92,18 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
}
}
+ // Add a reference to a table and all its segments and elements.
+ void maybeAddTable(Name name) {
+ maybeAdd(ModuleElement(ModuleElementKind::Table, name));
+ ModuleUtils::iterTableSegments(*module, name, [&](ElementSegment* segment) {
+ maybeAdd(ModuleElement(ModuleElementKind::ElementSegment, segment->name));
+ });
+ }
+
void visitCall(Call* curr) {
maybeAdd(ModuleElement(ModuleElementKind::Function, curr->target));
}
- void visitCallIndirect(CallIndirect* curr) {
- assert(!module->tables.empty() && "call-indirect to undefined table.");
- maybeAdd(ModuleElement(ModuleElementKind::Table, curr->table));
- ModuleUtils::iterTableSegments(
- *module, curr->table, [&](ElementSegment* segment) {
- maybeAdd(
- ModuleElement(ModuleElementKind::ElementSegment, segment->name));
- });
- }
+ void visitCallIndirect(CallIndirect* curr) { maybeAddTable(curr->table); }
void visitGlobalGet(GlobalGet* curr) {
maybeAdd(ModuleElement(ModuleElementKind::Global, curr->name));
@@ -128,6 +128,7 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
void visitRefFunc(RefFunc* curr) {
maybeAdd(ModuleElement(ModuleElementKind::Function, curr->func));
}
+ void visitTableGet(TableGet* curr) { maybeAddTable(curr->table); }
void visitThrow(Throw* curr) {
maybeAdd(ModuleElement(ModuleElementKind::Tag, curr->tag));
}
diff --git a/src/shell-interface.h b/src/shell-interface.h
index 96b96cf15..66608c37c 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -163,7 +163,6 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
}
auto& table = it->second;
-
if (index >= table.size()) {
trap("callTable overflow");
}
@@ -233,6 +232,21 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
}
}
+ Literal tableLoad(Name tableName, Address addr) override {
+
+ auto it = tables.find(tableName);
+ if (it == tables.end()) {
+ trap("tableGet on non-existing table");
+ }
+
+ auto& table = it->second;
+ if (addr >= table.size()) {
+ trap("tableGet overflow");
+ }
+
+ return table[addr];
+ }
+
bool growMemory(Address /*oldSize*/, Address newSize) override {
// Apply a reasonable limit on memory size, 1GB, to avoid DOS on the
// interpreter.
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 04ef10f57..61897dc60 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -343,6 +343,10 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
std::to_string(index));
}
+ Literal tableGet(Name tableName, Index index) {
+ throw FailToEvalException("table.get: TODO");
+ }
+
int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); }
uint8_t load8u(Address addr) override { return doLoad<uint8_t>(addr); }
int16_t load16s(Address addr) override { return doLoad<int16_t>(addr); }
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index aea9e56d8..c0225c270 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -474,6 +474,8 @@ enum ASTNodes {
GlobalGet = 0x23,
GlobalSet = 0x24,
+ TableGet = 0x25,
+
I32LoadMem = 0x28,
I64LoadMem = 0x29,
F32LoadMem = 0x2a,
@@ -1659,6 +1661,7 @@ public:
void visitRefIs(RefIs* curr, uint8_t code);
void visitRefFunc(RefFunc* curr);
void visitRefEq(RefEq* curr);
+ void visitTableGet(TableGet* curr);
void visitTryOrTryInBlock(Expression*& out);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index f992b1e03..d1cb5f16d 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -648,6 +648,14 @@ public:
ret->finalize();
return ret;
}
+ TableGet* makeTableGet(Name table, Expression* index, Type type) {
+ auto* ret = wasm.allocator.alloc<TableGet>();
+ ret->table = table;
+ ret->index = index;
+ ret->type = type;
+ ret->finalize();
+ return ret;
+ }
private:
Try* makeTry(Name name,
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index f5524d181..19a7df3a2 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -502,6 +502,13 @@ switch (DELEGATE_ID) {
DELEGATE_END(RefEq);
break;
}
+ case Expression::Id::TableGetId: {
+ DELEGATE_START(TableGet);
+ DELEGATE_FIELD_CHILD(TableGet, index);
+ DELEGATE_FIELD_NAME(TableGet, table);
+ DELEGATE_END(TableGet);
+ break;
+ }
case Expression::Id::TryId: {
DELEGATE_START(Try);
DELEGATE_FIELD_SCOPE_NAME_USE(Try, delegateTarget);
diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def
index 2579ac621..873eb2902 100644
--- a/src/wasm-delegations.def
+++ b/src/wasm-delegations.def
@@ -58,6 +58,7 @@ DELEGATE(RefNull);
DELEGATE(RefIs);
DELEGATE(RefFunc);
DELEGATE(RefEq);
+DELEGATE(TableGet);
DELEGATE(Try);
DELEGATE(Throw);
DELEGATE(Rethrow);
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 4e2d40e08..6ea9f9c9e 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1358,6 +1358,7 @@ public:
NOTE_EVAL2(left, right);
return Literal(int32_t(left == right));
}
+ Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitThrow(Throw* curr) {
NOTE_ENTER("Throw");
@@ -2157,6 +2158,10 @@ public:
NOTE_ENTER("CallRef");
return Flow(NONCONSTANT_FLOW);
}
+ Flow visitTableGet(TableGet* curr) {
+ NOTE_ENTER("TableGet");
+ return Flow(NONCONSTANT_FLOW);
+ }
Flow visitLoad(Load* curr) {
NOTE_ENTER("Load");
return Flow(NONCONSTANT_FLOW);
@@ -2441,6 +2446,9 @@ public:
tableStore(Name tableName, Address addr, const Literal& entry) {
WASM_UNREACHABLE("unimp");
}
+ virtual Literal tableLoad(Name tableName, Address addr) {
+ WASM_UNREACHABLE("unimp");
+ }
};
SubType* self() { return static_cast<SubType*>(this); }
@@ -2528,7 +2536,37 @@ private:
std::unordered_set<size_t> droppedSegments;
+ struct TableInterfaceInfo {
+ // The external interface in which the table is defined.
+ ExternalInterface* interface;
+ // The name the table has in that interface.
+ Name name;
+ };
+
+ TableInterfaceInfo getTableInterfaceInfo(Name name) {
+ auto* table = wasm.getTable(name);
+ if (table->imported()) {
+ auto& importedInstance = linkedInstances.at(table->module);
+ auto* tableExport = importedInstance->wasm.getExport(table->base);
+ return TableInterfaceInfo{importedInstance->externalInterface,
+ tableExport->value};
+ } else {
+ return TableInterfaceInfo{externalInterface, name};
+ }
+ }
+
void initializeTableContents() {
+ for (auto& table : wasm.tables) {
+ if (table->type.isNullable()) {
+ // Initial with nulls in a nullable table.
+ auto info = getTableInterfaceInfo(table->name);
+ auto null = Literal::makeNull(table->type);
+ for (Address i = 0; i < table->initial; i++) {
+ info.interface->tableStore(info.name, i, null);
+ }
+ }
+ }
+
ModuleUtils::iterActiveElementSegments(wasm, [&](ElementSegment* segment) {
Function dummyFunc;
dummyFunc.type = Signature(Type::none, Type::none);
@@ -2546,6 +2584,7 @@ private:
extInterface = inst->externalInterface;
tableName = inst->wasm.getExport(table->base)->value;
}
+
for (Index i = 0; i < segment->data.size(); ++i) {
Flow ret = runner.visit(segment->data[i]);
extInterface->tableStore(tableName, offset + i, ret.getSingleValue());
@@ -2686,6 +2725,7 @@ private:
}
return ret;
}
+
Flow visitCallIndirect(CallIndirect* curr) {
NOTE_ENTER("CallIndirect");
LiteralList arguments;
@@ -2701,21 +2741,9 @@ private:
Index index = target.getSingleValue().geti32();
Type type = curr->isReturn ? scope.function->getResults() : curr->type;
- Flow ret;
- auto* table = instance.wasm.getTable(curr->table);
- if (table->imported()) {
- auto inst = instance.linkedInstances.at(table->module);
- Export* tableExport = inst->wasm.getExport(table->base);
- ret = inst->externalInterface->callTable(tableExport->value,
- index,
- curr->sig,
- arguments,
- type,
- *instance.self());
- } else {
- ret = instance.externalInterface->callTable(
- curr->table, index, curr->sig, arguments, type, *instance.self());
- }
+ auto info = instance.getTableInterfaceInfo(curr->table);
+ Flow ret = info.interface->callTable(
+ info.name, index, curr->sig, arguments, type, *instance.self());
// TODO: make this a proper tail call (return first)
if (curr->isReturn) {
@@ -2755,6 +2783,17 @@ private:
return ret;
}
+ Flow visitTableGet(TableGet* curr) {
+ NOTE_ENTER("TableGet");
+ Flow index = this->visit(curr->index);
+ if (index.breaking()) {
+ return index;
+ }
+ auto info = instance.getTableInterfaceInfo(curr->table);
+ return info.interface->tableLoad(info.name,
+ index.getSingleValue().geti32());
+ }
+
Flow visitLocalGet(LocalGet* curr) {
NOTE_ENTER("LocalGet");
auto index = curr->index;
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index ef677cbf7..693b15ea2 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -265,6 +265,7 @@ private:
Expression* makeRefIs(Element& s, RefIsOp op);
Expression* makeRefFunc(Element& s);
Expression* makeRefEq(Element& s);
+ Expression* makeTableGet(Element& s);
Expression* makeTry(Element& s);
Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry);
Expression* makeThrow(Element& s);
diff --git a/src/wasm.h b/src/wasm.h
index 9c6062e48..697931082 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -624,6 +624,7 @@ public:
RefIsId,
RefFuncId,
RefEqId,
+ TableGetId,
TryId,
ThrowId,
RethrowId,
@@ -1264,6 +1265,17 @@ public:
void finalize();
};
+class TableGet : public SpecificExpression<Expression::TableGetId> {
+public:
+ TableGet(MixedArena& allocator) {}
+
+ Name table;
+
+ Expression* index;
+
+ void finalize();
+};
+
class Try : public SpecificExpression<Expression::TryId> {
public:
Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {}
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index cd1cfaa0f..a8eea2808 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -2761,6 +2761,8 @@ void WasmBinaryBuilder::processNames() {
for (auto* ref : refs) {
if (auto* callIndirect = ref->dynCast<CallIndirect>()) {
callIndirect->table = getTableName(index);
+ } else if (auto* get = ref->dynCast<TableGet>()) {
+ get->table = getTableName(index);
} else {
WASM_UNREACHABLE("Invalid type in table references");
}
@@ -3491,6 +3493,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
case BinaryConsts::BrOnNonNull:
maybeVisitBrOn(curr, code);
break;
+ case BinaryConsts::TableGet:
+ visitTableGet((curr = allocator.alloc<TableGet>())->cast<TableGet>());
+ break;
case BinaryConsts::Try:
visitTryOrTryInBlock(curr);
break;
@@ -4016,6 +4021,7 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) {
for (size_t i = 0; i < num; i++) {
curr->operands[num - i - 1] = popNonVoidExpression();
}
+ // Defer setting the table name for later, when we know it.
tableRefs[tableIdx].push_back(curr);
curr->finalize();
}
@@ -6170,6 +6176,19 @@ void WasmBinaryBuilder::visitRefEq(RefEq* curr) {
curr->finalize();
}
+void WasmBinaryBuilder::visitTableGet(TableGet* curr) {
+ BYN_TRACE("zz node: TableGet\n");
+ Index tableIdx = getU32LEB();
+ if (tableIdx >= tables.size()) {
+ throwError("bad table index");
+ }
+ curr->index = popNonVoidExpression();
+ curr->type = tables[tableIdx]->type;
+ curr->finalize();
+ // Defer setting the table name for later, when we know it.
+ tableRefs[tableIdx].push_back(curr);
+}
+
void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) {
BYN_TRACE("zz node: Try\n");
auto* curr = allocator.alloc<Try>();
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index e9ea6aaa5..573699eae 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2417,6 +2417,16 @@ Expression* SExpressionWasmBuilder::makeRefEq(Element& s) {
return ret;
}
+Expression* SExpressionWasmBuilder::makeTableGet(Element& s) {
+ auto tableName = s[1]->str();
+ auto* index = parseExpression(s[2]);
+ auto* table = wasm.getTableOrNull(tableName);
+ if (!table) {
+ throw ParseException("invalid table name in table.get", s.line, s.col);
+ }
+ return Builder(wasm).makeTableGet(tableName, index, table->type);
+}
+
// try can be either in the form of try-catch or try-delegate.
// try-catch is written in the folded wast format as
// (try
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 70b52cfbb..58bd29f6e 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1847,6 +1847,11 @@ void BinaryInstWriter::visitRefEq(RefEq* curr) {
o << int8_t(BinaryConsts::RefEq);
}
+void BinaryInstWriter::visitTableGet(TableGet* curr) {
+ o << int8_t(BinaryConsts::TableGet);
+ o << U32LEB(parent.getTableIndex(curr->table));
+}
+
void BinaryInstWriter::visitTry(Try* curr) {
breakStack.push_back(curr->name);
o << int8_t(BinaryConsts::Try);
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index d03fc2446..fae95980f 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -364,6 +364,7 @@ public:
void visitRefIs(RefIs* curr);
void visitRefFunc(RefFunc* curr);
void visitRefEq(RefEq* curr);
+ void visitTableGet(TableGet* curr);
void noteDelegate(Name name, Expression* curr);
void noteRethrow(Name name, Expression* curr);
void visitTry(Try* curr);
@@ -2031,6 +2032,20 @@ void FunctionValidator::visitRefEq(RefEq* curr) {
"ref.eq's right argument should be a subtype of eqref");
}
+void FunctionValidator::visitTableGet(TableGet* curr) {
+ shouldBeTrue(getModule()->features.hasReferenceTypes(),
+ curr,
+ "table.get requires reference types to be enabled");
+ 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.");
+ }
+}
+
void FunctionValidator::noteDelegate(Name name, Expression* curr) {
if (name != DELEGATE_CALLER_TARGET) {
shouldBeTrue(delegateTargetNames.count(name) != 0,
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 8565bfaa5..19ba5f265 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -818,6 +818,13 @@ void RefEq::finalize() {
}
}
+void TableGet::finalize() {
+ if (index->type == Type::unreachable) {
+ type = Type::unreachable;
+ }
+ // Otherwise, the type should have been set already.
+}
+
void Try::finalize() {
// If none of the component bodies' type is a supertype of the others, assume
// the current type is already correct. TODO: Calculate a proper LUB.
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 370546134..ef6c9af36 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -2167,6 +2167,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitTableGet(TableGet* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitTry(Try* curr) {
unimplemented(curr);
WASM_UNREACHABLE("unimp");
diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt
index 823b638f5..a1a1879bd 100644
--- a/test/binaryen.js/exception-handling.js.txt
+++ b/test/binaryen.js/exception-handling.js.txt
@@ -34,7 +34,7 @@
)
)
-getExpressionInfo(throw) = {"id":46,"type":1,"tag":"e"}
-getExpressionInfo(rethrow) = {"id":47,"type":1,"target":"l0"}
-getExpressionInfo(try_catch) = {"id":45,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0}
-getExpressionInfo(try_delegate) = {"id":45,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0}
+getExpressionInfo(throw) = {"id":47,"type":1,"tag":"e"}
+getExpressionInfo(rethrow) = {"id":48,"type":1,"target":"l0"}
+getExpressionInfo(try_catch) = {"id":46,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0}
+getExpressionInfo(try_delegate) = {"id":46,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0}
diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt
index 21413d87a..f971e1a07 100644
--- a/test/binaryen.js/kitchen-sink.js.txt
+++ b/test/binaryen.js/kitchen-sink.js.txt
@@ -88,27 +88,27 @@ RefNullId: 41
RefIsId: 42
RefFuncId: 43
RefEqId: 44
-TryId: 45
-ThrowId: 46
-RethrowId: 47
-TupleMakeId: 48
-TupleExtractId: 49
-I31NewId: 50
-I31GetId: 51
-CallRefId: 52
-RefTestId: 53
-RefCastId: 54
-BrOnId: 55
-RttCanonId: 56
-RttSubId: 57
-StructNewId: 58
-StructGetId: 59
-StructSetId: 60
-ArrayNewId: 61
-ArrayInitId: 62
-ArrayGetId: 63
-ArraySetId: 64
-ArrayLenId: 65
+TryId: 46
+ThrowId: 47
+RethrowId: 48
+TupleMakeId: 49
+TupleExtractId: 50
+I31NewId: 51
+I31GetId: 52
+CallRefId: 53
+RefTestId: 54
+RefCastId: 55
+BrOnId: 56
+RttCanonId: 57
+RttSubId: 58
+StructNewId: 59
+StructGetId: 60
+StructSetId: 61
+ArrayNewId: 62
+ArrayInitId: 63
+ArrayGetId: 64
+ArraySetId: 65
+ArrayLenId: 66
getExpressionInfo={"id":15,"type":4,"op":6}
(f32.neg
(f32.const -33.61199951171875)
diff --git a/test/lit/table-operations.wast b/test/lit/table-operations.wast
new file mode 100644
index 000000000..6e303b495
--- /dev/null
+++ b/test/lit/table-operations.wast
@@ -0,0 +1,108 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: wasm-as %s -all -g -o %t.wasm
+;; RUN: wasm-dis %t.wasm -all -o %t.wast
+;; RUN: wasm-as %s -all -o %t.nodebug.wasm
+;; RUN: wasm-dis %t.nodebug.wasm -all -o %t.nodebug.wast
+;; RUN: wasm-opt %t.wast -all -o %t.text.wast -g -S
+;; RUN: cat %t.wast | filecheck %s --check-prefix=CHECK-BINARY
+;; RUN: cat %t.nodebug.wast | filecheck %s --check-prefix=CHECK-NODEBUG
+;; RUN: cat %t.text.wast | filecheck %s --check-prefix=CHECK-TEXT
+
+(module
+ ;; CHECK-BINARY: (type $none_=>_none (func))
+
+ ;; CHECK-BINARY: (table $table-1 1 1 funcref)
+ ;; CHECK-TEXT: (type $none_=>_none (func))
+
+ ;; CHECK-TEXT: (table $table-1 1 1 funcref)
+ (table $table-1 funcref
+ (elem $foo)
+ )
+
+ ;; CHECK-BINARY: (table $table-2 3 3 funcref)
+ ;; CHECK-TEXT: (table $table-2 3 3 funcref)
+ (table $table-2 funcref
+ (elem $bar $bar $bar)
+ )
+
+ ;; CHECK-BINARY: (elem $0 (table $table-1) (i32.const 0) func $foo)
+
+ ;; CHECK-BINARY: (elem $1 (table $table-2) (i32.const 0) func $bar $bar $bar)
+
+ ;; CHECK-BINARY: (func $foo
+ ;; CHECK-BINARY-NEXT: (nop)
+ ;; CHECK-BINARY-NEXT: )
+ ;; CHECK-TEXT: (elem $0 (table $table-1) (i32.const 0) func $foo)
+
+ ;; CHECK-TEXT: (elem $1 (table $table-2) (i32.const 0) func $bar $bar $bar)
+
+ ;; CHECK-TEXT: (func $foo
+ ;; CHECK-TEXT-NEXT: (nop)
+ ;; CHECK-TEXT-NEXT: )
+ (func $foo
+ (nop)
+ )
+ ;; CHECK-BINARY: (func $bar
+ ;; CHECK-BINARY-NEXT: (drop
+ ;; CHECK-BINARY-NEXT: (table.get $table-1
+ ;; CHECK-BINARY-NEXT: (i32.const 0)
+ ;; CHECK-BINARY-NEXT: )
+ ;; CHECK-BINARY-NEXT: )
+ ;; CHECK-BINARY-NEXT: (drop
+ ;; CHECK-BINARY-NEXT: (table.get $table-2
+ ;; CHECK-BINARY-NEXT: (i32.const 100)
+ ;; CHECK-BINARY-NEXT: )
+ ;; CHECK-BINARY-NEXT: )
+ ;; CHECK-BINARY-NEXT: )
+ ;; CHECK-TEXT: (func $bar
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (table.get $table-1
+ ;; CHECK-TEXT-NEXT: (i32.const 0)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: (drop
+ ;; CHECK-TEXT-NEXT: (table.get $table-2
+ ;; CHECK-TEXT-NEXT: (i32.const 100)
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ ;; CHECK-TEXT-NEXT: )
+ (func $bar
+ (drop
+ (table.get $table-1
+ (i32.const 0)
+ )
+ )
+ (drop
+ (table.get $table-2
+ (i32.const 100)
+ )
+ )
+ )
+)
+;; CHECK-NODEBUG: (type $none_=>_none (func))
+
+;; CHECK-NODEBUG: (table $0 1 1 funcref)
+
+;; CHECK-NODEBUG: (table $1 3 3 funcref)
+
+;; CHECK-NODEBUG: (elem $0 (table $0) (i32.const 0) func $0)
+
+;; CHECK-NODEBUG: (elem $1 (table $1) (i32.const 0) func $1 $1 $1)
+
+;; CHECK-NODEBUG: (func $0
+;; CHECK-NODEBUG-NEXT: (nop)
+;; CHECK-NODEBUG-NEXT: )
+
+;; CHECK-NODEBUG: (func $1
+;; CHECK-NODEBUG-NEXT: (drop
+;; CHECK-NODEBUG-NEXT: (table.get $0
+;; CHECK-NODEBUG-NEXT: (i32.const 0)
+;; CHECK-NODEBUG-NEXT: )
+;; CHECK-NODEBUG-NEXT: )
+;; CHECK-NODEBUG-NEXT: (drop
+;; CHECK-NODEBUG-NEXT: (table.get $1
+;; CHECK-NODEBUG-NEXT: (i32.const 100)
+;; CHECK-NODEBUG-NEXT: )
+;; CHECK-NODEBUG-NEXT: )
+;; CHECK-NODEBUG-NEXT: )
diff --git a/test/spec/table_get.wast b/test/spec/table_get.wast
new file mode 100644
index 000000000..de5f6622a
--- /dev/null
+++ b/test/spec/table_get.wast
@@ -0,0 +1,79 @@
+(module
+ (table $t2 2 externref)
+ (table $t3 3 funcref)
+ (elem (table $t3) (i32.const 1) func $dummy)
+ (func $dummy)
+
+ (func (export "get-externref") (param $i i32) (result externref)
+ (table.get $t2 (local.get $i))
+ )
+ (func $f3 (export "get-funcref") (param $i i32) (result funcref)
+ (table.get $t3 (local.get $i))
+ )
+
+ (func (export "is_null-funcref") (param $i i32) (result i32)
+ (ref.is_null (call $f3 (local.get $i)))
+ )
+)
+
+(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern))
+
+(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func))
+(assert_return (invoke "is_null-funcref" (i32.const 1)) (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")
+
+
+;; Type errors
+
+(assert_invalid
+ (module
+ (table $t 10 externref)
+ (func $type-index-empty-vs-i32 (result externref)
+ (table.get $t)
+ )
+ )
+ "type mismatch"
+)
+(assert_invalid
+ (module
+ (table $t 10 externref)
+ (func $type-index-f32-vs-i32 (result externref)
+ (table.get $t (f32.const 1))
+ )
+ )
+ "type mismatch"
+)
+
+(assert_invalid
+ (module
+ (table $t 10 externref)
+ (func $type-result-externref-vs-empty
+ (table.get $t (i32.const 0))
+ )
+ )
+ "type mismatch"
+)
+(assert_invalid
+ (module
+ (table $t 10 externref)
+ (func $type-result-externref-vs-funcref (result funcref)
+ (table.get $t (i32.const 1))
+ )
+ )
+ "type mismatch"
+)
+
+(assert_invalid
+ (module
+ (table $t1 1 funcref)
+ (table $t2 1 externref)
+ (func $type-result-externref-vs-funcref-multi (result funcref)
+ (table.get $t2 (i32.const 0))
+ )
+ )
+ "type mismatch"
+)