summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp49
-rw-r--r--src/binaryen-c.h27
-rw-r--r--src/gen-s-parser.inc14
-rw-r--r--src/ir/ReFinalize.cpp1
-rw-r--r--src/ir/cost.h3
-rw-r--r--src/ir/effects.h10
-rw-r--r--src/js/binaryen.js-post.js33
-rw-r--r--src/passes/Print.cpp4
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp1
-rw-r--r--src/shell-interface.h29
-rw-r--r--src/tools/wasm-ctor-eval.cpp24
-rw-r--r--src/wasm-binary.h3
-rw-r--r--src/wasm-builder.h8
-rw-r--r--src/wasm-delegations-fields.def8
-rw-r--r--src/wasm-delegations.def1
-rw-r--r--src/wasm-interpreter.h75
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm.h13
-rw-r--r--src/wasm/wasm-binary.cpp23
-rw-r--r--src/wasm/wasm-s-parser.cpp18
-rw-r--r--src/wasm/wasm-stack.cpp5
-rw-r--r--src/wasm/wasm-validator.cpp19
-rw-r--r--src/wasm/wasm.cpp12
-rw-r--r--src/wasm2js.h4
24 files changed, 343 insertions, 42 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index eab435caa..0a4b88a2b 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -1284,6 +1284,19 @@ BinaryenExpressionRef BinaryenTableSize(BinaryenModuleRef module,
Builder(*(Module*)module).makeTableSize(name));
}
+BinaryenExpressionRef BinaryenTableGrow(BinaryenModuleRef module,
+ const char* name,
+ BinaryenExpressionRef value,
+ BinaryenExpressionRef delta) {
+ if (value == nullptr) {
+ auto tableType = (*(Module*)module).getTableOrNull(name)->type;
+ value = BinaryenRefNull(module, (BinaryenType)tableType.getID());
+ }
+ return static_cast<Expression*>(
+ Builder(*(Module*)module)
+ .makeTableGrow(name, (Expression*)value, (Expression*)delta));
+}
+
BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module,
const char* name,
BinaryenExpressionRef body,
@@ -1941,6 +1954,42 @@ void BinaryenTableSizeSetTable(BinaryenExpressionRef expr, const char* table) {
assert(table);
static_cast<TableSize*>(expression)->table = table;
}
+// TableGrow
+const char* BinaryenTableGrowGetTable(BinaryenExpressionRef expr) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<TableGrow>());
+ return static_cast<TableGrow*>(expression)->table.c_str();
+}
+void BinaryenTableGrowSetTable(BinaryenExpressionRef expr, const char* table) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<TableGrow>());
+ assert(table);
+ static_cast<TableGrow*>(expression)->table = table;
+}
+BinaryenExpressionRef BinaryenTableGrowGetValue(BinaryenExpressionRef expr) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<TableGrow>());
+ return static_cast<TableGrow*>(expression)->value;
+}
+void BinaryenTableGrowSetValue(BinaryenExpressionRef expr,
+ BinaryenExpressionRef valueExpr) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<TableGrow>());
+ assert(valueExpr);
+ static_cast<TableGrow*>(expression)->value = (Expression*)valueExpr;
+}
+BinaryenExpressionRef BinaryenTableGrowGetDelta(BinaryenExpressionRef expr) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<TableGrow>());
+ return static_cast<TableGrow*>(expression)->delta;
+}
+void BinaryenTableGrowSetDelta(BinaryenExpressionRef expr,
+ BinaryenExpressionRef deltaExpr) {
+ auto* expression = (Expression*)expr;
+ assert(expression->is<TableGrow>());
+ assert(deltaExpr);
+ static_cast<TableGrow*>(expression)->delta = (Expression*)deltaExpr;
+}
// MemoryGrow
BinaryenExpressionRef BinaryenMemoryGrowGetDelta(BinaryenExpressionRef expr) {
auto* expression = (Expression*)expr;
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 3d57aca75..853f185e3 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -862,6 +862,11 @@ BinaryenTableSet(BinaryenModuleRef module,
BinaryenExpressionRef value);
BINARYEN_API BinaryenExpressionRef BinaryenTableSize(BinaryenModuleRef module,
const char* name);
+BINARYEN_API BinaryenExpressionRef
+BinaryenTableGrow(BinaryenModuleRef module,
+ const char* name,
+ BinaryenExpressionRef value,
+ BinaryenExpressionRef delta);
// Try: name can be NULL. delegateTarget should be NULL in try-catch.
BINARYEN_API BinaryenExpressionRef
BinaryenTry(BinaryenModuleRef module,
@@ -1247,6 +1252,26 @@ BINARYEN_API const char* BinaryenTableSizeGetTable(BinaryenExpressionRef expr);
// Sets the name of the table being accessed by a `table.size` expression.
BINARYEN_API void BinaryenTableSizeSetTable(BinaryenExpressionRef expr,
const char* table);
+
+// TableGrow
+
+// Gets the name of the table being accessed by a `table.grow` expression.
+BINARYEN_API const char* BinaryenTableGrowGetTable(BinaryenExpressionRef expr);
+// Sets the name of the table being accessed by a `table.grow` expression.
+BINARYEN_API void BinaryenTableGrowSetTable(BinaryenExpressionRef expr,
+ const char* table);
+// Gets the value expression of a `table.grow` expression.
+BINARYEN_API BinaryenExpressionRef
+BinaryenTableGrowGetValue(BinaryenExpressionRef expr);
+// Sets the value expression of a `table.grow` expression.
+BINARYEN_API void BinaryenTableGrowSetValue(BinaryenExpressionRef expr,
+ BinaryenExpressionRef valueExpr);
+// Gets the delta of a `table.grow` expression.
+BINARYEN_API BinaryenExpressionRef
+BinaryenTableGrowGetDelta(BinaryenExpressionRef expr);
+// Sets the delta of a `table.grow` expression.
+BINARYEN_API void BinaryenTableGrowSetDelta(BinaryenExpressionRef expr,
+ BinaryenExpressionRef deltaExpr);
// MemoryGrow
// Gets the delta of a `memory.grow` expression.
@@ -1254,7 +1279,7 @@ BINARYEN_API BinaryenExpressionRef
BinaryenMemoryGrowGetDelta(BinaryenExpressionRef expr);
// Sets the delta of a `memory.grow` expression.
BINARYEN_API void BinaryenMemoryGrowSetDelta(BinaryenExpressionRef expr,
- BinaryenExpressionRef delta);
+ BinaryenExpressionRef deltaExpr);
// Load
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 0d61244fd..47ba1e1df 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3043,9 +3043,17 @@ switch (op[0]) {
switch (op[1]) {
case 'a': {
switch (op[6]) {
- case 'g':
- if (strcmp(op, "table.get") == 0) { return makeTableGet(s); }
- goto parse_error;
+ case 'g': {
+ switch (op[7]) {
+ case 'e':
+ if (strcmp(op, "table.get") == 0) { return makeTableGet(s); }
+ goto parse_error;
+ case 'r':
+ if (strcmp(op, "table.grow") == 0) { return makeTableGrow(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
case 's': {
switch (op[7]) {
case 'e':
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index a9cf8050d..79a4c9386 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -138,6 +138,7 @@ void ReFinalize::visitRefEq(RefEq* curr) { curr->finalize(); }
void ReFinalize::visitTableGet(TableGet* curr) { curr->finalize(); }
void ReFinalize::visitTableSet(TableSet* curr) { curr->finalize(); }
void ReFinalize::visitTableSize(TableSize* curr) { curr->finalize(); }
+void ReFinalize::visitTableGrow(TableGrow* 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 0463d3310..bf9e48308 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -545,6 +545,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return 2 + visit(curr->index) + visit(curr->value);
}
CostType visitTableSize(TableSize* curr) { return 1; }
+ CostType visitTableGrow(TableGrow* curr) {
+ return 100 + visit(curr->value) + visit(curr->delta);
+ }
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 7074df755..e33a33818 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -534,7 +534,6 @@ private:
parent.writesMemory = true;
parent.implicitTrap = true;
}
- void visitTableSize(TableSize* curr) { parent.readsTable = true; }
void visitConst(Const* curr) {}
void visitUnary(Unary* curr) {
switch (curr->op) {
@@ -592,6 +591,7 @@ private:
parent.isAtomic = true;
}
void visitMemoryGrow(MemoryGrow* curr) {
+ // TODO: find out if calls is necessary here
parent.calls = true;
// memory.grow technically does a read-modify-write operation on the
// memory size in the successful case, modifying the set of valid
@@ -613,6 +613,14 @@ private:
parent.writesTable = true;
parent.implicitTrap = true;
}
+ void visitTableSize(TableSize* curr) { parent.readsTable = true; }
+ void visitTableGrow(TableGrow* curr) {
+ // table.grow technically does a read-modify-write operation on the
+ // table size in the successful case, modifying the set of valid
+ // indices, and just a read operation in the failure case
+ parent.readsTable = true;
+ parent.writesTable = true;
+ }
void visitTry(Try* curr) {}
void visitThrow(Throw* curr) {
if (parent.tryDepth == 0) {
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index 29729c4b1..cef5c989d 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -94,6 +94,7 @@ function initializeConstants() {
'TableGet',
'TableSet',
'TableSize',
+ 'TableGrow',
'Try',
'Throw',
'Rethrow',
@@ -675,6 +676,9 @@ function wrapModule(module, self = {}) {
},
'size'(name) {
return Module['_BinaryenTableSize'](module, strToStack(name));
+ },
+ 'grow'(name, value, delta) {
+ return Module['_BinaryenTableGrow'](module, strToStack(name), value, delta);
}
}
@@ -2835,6 +2839,14 @@ Module['getExpressionInfo'] = function(expr) {
'type': type,
'table': UTF8ToString(Module['_BinaryenTableSizeGetTable'](expr)),
};
+ case Module['TableGrowId']:
+ return {
+ 'id': id,
+ 'type': type,
+ 'table': UTF8ToString(Module['_BinaryenTableGrowGetTable'](expr)),
+ 'value': Module['_BinaryenTableGrowGetValue'](expr),
+ 'delta': Module['_BinaryenTableGrowGetDelta'](expr),
+ };
case Module['LoadId']:
return {
'id': id,
@@ -3850,6 +3862,27 @@ Module['TableSize'] = makeExpressionWrapper({
},
});
+Module['TableGrow'] = makeExpressionWrapper({
+ 'getTable'(expr) {
+ return UTF8ToString(Module['_BinaryenTableGrowGetTable'](expr));
+ },
+ 'setTable'(expr, name) {
+ preserveStack(() => { Module['_BinaryenTableGrowSetTable'](expr, strToStack(name)) });
+ },
+ 'getValue'(expr) {
+ return Module['_BinaryenTableGrowGetValue'](expr);
+ },
+ 'setValue'(expr, valueExpr) {
+ Module['_BinaryenTableGrowSetValue'](expr, valueExpr);
+ },
+ 'getDelta'(expr) {
+ return Module['_BinaryenTableGrowGetDelta'](expr);
+ },
+ 'setDelta'(expr, deltaExpr) {
+ Module['_BinaryenTableGrowSetDelta'](expr, deltaExpr);
+ }
+});
+
Module['MemorySize'] = makeExpressionWrapper({});
Module['MemoryGrow'] = makeExpressionWrapper({
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index fee59ce3b..96a1abe73 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1856,6 +1856,10 @@ struct PrintExpressionContents
printMedium(o, "table.size ");
printName(curr->table, o);
}
+ void visitTableGrow(TableGrow* curr) {
+ printMedium(o, "table.grow ");
+ 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 0f2c69e02..436e01885 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -131,6 +131,7 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
void visitTableGet(TableGet* curr) { maybeAddTable(curr->table); }
void visitTableSet(TableSet* curr) { maybeAddTable(curr->table); }
void visitTableSize(TableSize* curr) { maybeAddTable(curr->table); }
+ void visitTableGrow(TableGrow* 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 0c81680f5..6c6ddf7f2 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -223,27 +223,31 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
memory.set<std::array<uint8_t, 16>>(addr, value);
}
- void tableStore(Name tableName, Address addr, const Literal& entry) override {
+ Index tableSize(Name tableName) override {
+ return (Index)tables[tableName].size();
+ }
+
+ void tableStore(Name tableName, Index index, const Literal& entry) override {
auto& table = tables[tableName];
- if (addr >= table.size()) {
+ if (index >= table.size()) {
trap("out of bounds table access");
} else {
- table[addr] = entry;
+ table[index] = entry;
}
}
- Literal tableLoad(Name tableName, Address addr) override {
+ Literal tableLoad(Name tableName, Index index) override {
auto it = tables.find(tableName);
if (it == tables.end()) {
trap("tableGet on non-existing table");
}
auto& table = it->second;
- if (addr >= table.size()) {
+ if (index >= table.size()) {
trap("out of bounds table access");
}
- return table[addr];
+ return table[index];
}
bool growMemory(Address /*oldSize*/, Address newSize) override {
@@ -256,6 +260,19 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
return true;
}
+ bool growTable(Name name,
+ const Literal& value,
+ Index /*oldSize*/,
+ Index newSize) override {
+ // Apply a reasonable limit on table size, 1GB, to avoid DOS on the
+ // interpreter.
+ if (newSize > 1024 * 1024 * 1024) {
+ return false;
+ }
+ tables[name].resize(newSize, value);
+ return true;
+ }
+
void trap(const char* why) override {
std::cout << "[trap " << why << "]\n";
throw TrapException();
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 189ce9f71..54324ae5a 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -343,17 +343,16 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
std::to_string(index));
}
- Literal tableGet(Name tableName, Index index) {
- throw FailToEvalException("table.get: TODO");
+ Index tableSize(Name tableName) override {
+ throw FailToEvalException("table size");
}
- void tableSet(Name tableName, Index index, const Literal& value) {
- throw FailToEvalException("table.set: TODO");
+ Literal tableLoad(Name tableName, Index index) override {
+ throw FailToEvalException("table.get: TODO");
}
- Literal tableSize(Name tableName) {
- throw FailToEvalException("table.size: TODO");
- }
+ // called during initialization
+ void tableStore(Name tableName, Index index, const Literal& value) override {}
int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); }
uint8_t load8u(Address addr) override { return doLoad<uint8_t>(addr); }
@@ -377,12 +376,15 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface {
doStore<int64_t>(addr, value);
}
- // called during initialization, but we don't keep track of a table
- void tableStore(Name tableName, Address addr, const Literal& value) override {
+ bool growMemory(Address /*oldSize*/, Address /*newSize*/) override {
+ throw FailToEvalException("grow memory");
}
- bool growMemory(Address /*oldSize*/, Address newSize) override {
- throw FailToEvalException("grow memory");
+ bool growTable(Name /*name*/,
+ const Literal& /*value*/,
+ Index /*oldSize*/,
+ Index /*newSize*/) override {
+ throw FailToEvalException("grow table");
}
void trap(const char* why) override {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index ae2dd4656..e523d0994 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1025,6 +1025,7 @@ enum ASTNodes {
// reference types opcodes
+ TableGrow = 0x0f,
TableSize = 0x10,
RefNull = 0xd0,
RefIsNull = 0xd1,
@@ -1637,6 +1638,7 @@ public:
bool maybeVisitMemoryCopy(Expression*& out, uint32_t code);
bool maybeVisitMemoryFill(Expression*& out, uint32_t code);
bool maybeVisitTableSize(Expression*& out, uint32_t code);
+ bool maybeVisitTableGrow(Expression*& out, uint32_t code);
bool maybeVisitI31New(Expression*& out, uint32_t code);
bool maybeVisitI31Get(Expression*& out, uint32_t code);
bool maybeVisitRefTest(Expression*& out, uint32_t code);
@@ -1666,7 +1668,6 @@ public:
void visitRefEq(RefEq* curr);
void visitTableGet(TableGet* curr);
void visitTableSet(TableSet* curr);
- void visitTableSize(TableSize* 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 87167877a..b192afbac 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -670,6 +670,14 @@ public:
ret->finalize();
return ret;
}
+ TableGrow* makeTableGrow(Name table, Expression* value, Expression* delta) {
+ auto* ret = wasm.allocator.alloc<TableGrow>();
+ ret->table = table;
+ ret->value = value;
+ ret->delta = delta;
+ ret->finalize();
+ return ret;
+ }
private:
Try* makeTry(Name name,
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index dfb8aab4b..d1a2e1b83 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -522,6 +522,14 @@ switch (DELEGATE_ID) {
DELEGATE_END(TableSize);
break;
}
+ case Expression::Id::TableGrowId: {
+ DELEGATE_START(TableGrow);
+ DELEGATE_FIELD_CHILD(TableGrow, delta);
+ DELEGATE_FIELD_CHILD(TableGrow, value);
+ DELEGATE_FIELD_NAME(TableGrow, table);
+ DELEGATE_END(TableGrow);
+ 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 8d162dd90..c3f04674a 100644
--- a/src/wasm-delegations.def
+++ b/src/wasm-delegations.def
@@ -61,6 +61,7 @@ DELEGATE(RefEq);
DELEGATE(TableGet);
DELEGATE(TableSet);
DELEGATE(TableSize);
+DELEGATE(TableGrow);
DELEGATE(Try);
DELEGATE(Throw);
DELEGATE(Rethrow);
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 2179c4923..205c9f8cd 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1361,6 +1361,7 @@ public:
Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitTableSet(TableSet* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitTableSize(TableSize* curr) { WASM_UNREACHABLE("unimp"); }
+ Flow visitTableGrow(TableGrow* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitThrow(Throw* curr) {
NOTE_ENTER("Throw");
@@ -2172,6 +2173,10 @@ public:
NOTE_ENTER("TableSize");
return Flow(NONCONSTANT_FLOW);
}
+ Flow visitTableGrow(TableGrow* curr) {
+ NOTE_ENTER("TableGrow");
+ return Flow(NONCONSTANT_FLOW);
+ }
Flow visitLoad(Load* curr) {
NOTE_ENTER("Load");
return Flow(NONCONSTANT_FLOW);
@@ -2306,6 +2311,10 @@ public:
Type result,
SubType& instance) = 0;
virtual bool growMemory(Address oldSize, Address newSize) = 0;
+ virtual bool growTable(Name name,
+ const Literal& value,
+ Index oldSize,
+ Index newSize) = 0;
virtual void trap(const char* why) = 0;
virtual void hostLimit(const char* why) = 0;
virtual void throwException(const WasmException& exn) = 0;
@@ -2452,11 +2461,12 @@ public:
WASM_UNREACHABLE("unimp");
}
- virtual void
- tableStore(Name tableName, Address addr, const Literal& entry) {
+ virtual Index tableSize(Name tableName) = 0;
+
+ virtual void tableStore(Name tableName, Index index, const Literal& entry) {
WASM_UNREACHABLE("unimp");
}
- virtual Literal tableLoad(Name tableName, Address addr) {
+ virtual Literal tableLoad(Name tableName, Index index) {
WASM_UNREACHABLE("unimp");
}
};
@@ -2805,25 +2815,61 @@ private:
}
Flow visitTableSet(TableSet* curr) {
NOTE_ENTER("TableSet");
- Flow index = this->visit(curr->index);
- if (index.breaking()) {
- return index;
+ Flow indexFlow = this->visit(curr->index);
+ if (indexFlow.breaking()) {
+ return indexFlow;
}
- Flow flow = this->visit(curr->value);
- if (flow.breaking()) {
- return flow;
+ Flow valueFlow = this->visit(curr->value);
+ if (valueFlow.breaking()) {
+ return valueFlow;
}
auto info = instance.getTableInterfaceInfo(curr->table);
- info.interface->tableStore(
- info.name, index.getSingleValue().geti32(), flow.getSingleValue());
+ info.interface->tableStore(info.name,
+ indexFlow.getSingleValue().geti32(),
+ valueFlow.getSingleValue());
return Flow();
}
Flow visitTableSize(TableSize* curr) {
NOTE_ENTER("TableSize");
- auto table = instance.wasm.getTable(curr->table);
- // TODO: properly handle table size when TableGrow exists
- return Literal::makeFromInt32(table->initial, Type::i32);
+ auto info = instance.getTableInterfaceInfo(curr->table);
+ Index tableSize = info.interface->tableSize(curr->table);
+ return Literal::makeFromInt32(tableSize, Type::i32);
+ }
+
+ Flow visitTableGrow(TableGrow* curr) {
+ NOTE_ENTER("TableGrow");
+ Flow valueFlow = this->visit(curr->value);
+ if (valueFlow.breaking()) {
+ return valueFlow;
+ }
+ Flow deltaFlow = this->visit(curr->delta);
+ if (deltaFlow.breaking()) {
+ return deltaFlow;
+ }
+ Name tableName = curr->table;
+ auto info = instance.getTableInterfaceInfo(tableName);
+
+ Index tableSize = info.interface->tableSize(tableName);
+ Flow ret = Literal::makeFromInt32(tableSize, Type::i32);
+ Flow fail = Literal::makeFromInt32(-1, Type::i32);
+ Index delta = deltaFlow.getSingleValue().geti32();
+
+ if (tableSize >= uint32_t(-1) - delta) {
+ return fail;
+ }
+ auto maxTableSize = instance.self()->wasm.getTable(tableName)->max;
+ if (uint64_t(tableSize) + uint64_t(delta) > uint64_t(maxTableSize)) {
+ return fail;
+ }
+ Index newSize = tableSize + delta;
+ if (!info.interface->growTable(
+ tableName, valueFlow.getSingleValue(), tableSize, newSize)) {
+ // We failed to grow the table in practice, even though it was valid
+ // to try to do so.
+ return fail;
+ }
+ return ret;
}
Flow visitLocalGet(LocalGet* curr) {
@@ -3546,7 +3592,6 @@ public:
protected:
Address memorySize; // in pages
-
static const Index maxDepth = 250;
void trapIfGt(uint64_t lhs, uint64_t rhs, const char* msg) {
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index f5be7924f..d6f3b2f4e 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -268,6 +268,7 @@ private:
Expression* makeTableGet(Element& s);
Expression* makeTableSet(Element& s);
Expression* makeTableSize(Element& s);
+ Expression* makeTableGrow(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 197b8bd84..0c7b2a9ad 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -630,6 +630,7 @@ public:
TableGetId,
TableSetId,
TableSizeId,
+ TableGrowId,
TryId,
ThrowId,
RethrowId,
@@ -1312,6 +1313,18 @@ public:
void finalize();
};
+class TableGrow : public SpecificExpression<Expression::TableGrowId> {
+public:
+ TableGrow() { type = Type::i32; }
+ TableGrow(MixedArena& allocator) : TableGrow() {}
+
+ Name table;
+ Expression* value;
+ Expression* delta;
+
+ 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 e2dacdf70..9c8b83325 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -2798,6 +2798,8 @@ void WasmBinaryBuilder::processNames() {
set->table = getTableName(index);
} else if (auto* size = ref->dynCast<TableSize>()) {
size->table = getTableName(index);
+ } else if (auto* grow = ref->dynCast<TableGrow>()) {
+ grow->table = getTableName(index);
} else {
WASM_UNREACHABLE("Invalid type in table references");
}
@@ -3621,6 +3623,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (maybeVisitTableSize(curr, opcode)) {
break;
}
+ if (maybeVisitTableGrow(curr, opcode)) {
+ break;
+ }
throwError("invalid code after misc prefix: " + std::to_string(opcode));
break;
}
@@ -4941,6 +4946,24 @@ bool WasmBinaryBuilder::maybeVisitTableSize(Expression*& out, uint32_t code) {
return true;
}
+bool WasmBinaryBuilder::maybeVisitTableGrow(Expression*& out, uint32_t code) {
+ if (code != BinaryConsts::TableGrow) {
+ return false;
+ }
+ Index tableIdx = getU32LEB();
+ if (tableIdx >= tables.size()) {
+ throwError("bad table index");
+ }
+ auto* curr = allocator.alloc<TableGrow>();
+ curr->delta = popNonVoidExpression();
+ curr->value = popNonVoidExpression();
+ curr->finalize();
+ // Defer setting the table name for later, when we know it.
+ tableRefs[tableIdx].push_back(curr);
+ out = curr;
+ return true;
+}
+
bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) {
Binary* curr;
#define INT_TYPED_CODE(code) \
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index cea666f29..1e15c1faa 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2453,12 +2453,12 @@ Expression* SExpressionWasmBuilder::makeTableGet(Element& s) {
Expression* SExpressionWasmBuilder::makeTableSet(Element& s) {
auto tableName = s[1]->str();
- auto* index = parseExpression(s[2]);
- auto* value = parseExpression(s[3]);
auto* table = wasm.getTableOrNull(tableName);
if (!table) {
throw ParseException("invalid table name in table.set", s.line, s.col);
}
+ auto* index = parseExpression(s[2]);
+ auto* value = parseExpression(s[3]);
return Builder(wasm).makeTableSet(tableName, index, value);
}
@@ -2471,6 +2471,20 @@ Expression* SExpressionWasmBuilder::makeTableSize(Element& s) {
return Builder(wasm).makeTableSize(tableName);
}
+Expression* SExpressionWasmBuilder::makeTableGrow(Element& s) {
+ auto tableName = s[1]->str();
+ auto* table = wasm.getTableOrNull(tableName);
+ if (!table) {
+ throw ParseException("invalid table name in table.grow", s.line, s.col);
+ }
+ auto* value = parseExpression(s[2]);
+ if (!value->type.isRef()) {
+ throw ParseException("only reference types are valid for tables");
+ }
+ auto* delta = parseExpression(s[3]);
+ return Builder(wasm).makeTableGrow(tableName, value, delta);
+}
+
// 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 f81a0b7a0..f5be5d2c5 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -1862,6 +1862,11 @@ void BinaryInstWriter::visitTableSize(TableSize* curr) {
o << U32LEB(parent.getTableIndex(curr->table));
}
+void BinaryInstWriter::visitTableGrow(TableGrow* curr) {
+ o << int8_t(BinaryConsts::MiscPrefix) << U32LEB(BinaryConsts::TableGrow);
+ 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 0271884a4..f8669961c 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -367,6 +367,7 @@ public:
void visitTableGet(TableGet* curr);
void visitTableSet(TableSet* curr);
void visitTableSize(TableSize* curr);
+ void visitTableGrow(TableGrow* curr);
void noteDelegate(Name name, Expression* curr);
void noteRethrow(Name name, Expression* curr);
void visitTry(Try* curr);
@@ -2072,6 +2073,24 @@ void FunctionValidator::visitTableSize(TableSize* curr) {
shouldBeTrue(!!table, curr, "table.size table must exist");
}
+void FunctionValidator::visitTableGrow(TableGrow* curr) {
+ shouldBeTrue(getModule()->features.hasReferenceTypes(),
+ curr,
+ "table.grow requires reference types to be enabled");
+ auto* table = getModule()->getTableOrNull(curr->table);
+ if (shouldBeTrue(!!table, curr, "table.grow table must exist") &&
+ curr->type != Type::unreachable) {
+ shouldBeSubType(curr->value->type,
+ table->type,
+ curr,
+ "table.grow value must have right type");
+ shouldBeEqual(curr->delta->type,
+ Type(Type::i32),
+ curr,
+ "table.grow must match table index type");
+ }
+}
+
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 c42a1089d..015df26ac 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -822,9 +822,9 @@ void RefNull::finalize() {}
void RefIs::finalize() {
if (value->type == Type::unreachable) {
type = Type::unreachable;
- return;
+ } else {
+ type = Type::i32;
}
- type = Type::i32;
}
void RefFunc::finalize() {
@@ -861,6 +861,14 @@ void TableSize::finalize() {
// Nothing to do - the type must have been set already during construction.
}
+void TableGrow::finalize() {
+ if (delta->type == Type::unreachable || value->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = Type::i32;
+ }
+}
+
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 03d1fd1da..206f945b4 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -2179,6 +2179,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitTableGrow(TableGrow* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitTry(Try* curr) {
unimplemented(curr);
WASM_UNREACHABLE("unimp");