summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
21 files changed, 189 insertions, 25 deletions
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");