summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp76
-rw-r--r--src/wasm/wasm-s-parser.cpp61
-rw-r--r--src/wasm/wasm-validator.cpp44
3 files changed, 136 insertions, 45 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 09d3287b0..5fff58045 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -571,9 +571,11 @@ void WasmBinaryWriter::writeElementSegments() {
Index tableIdx = 0;
bool isPassive = segment->table.isNull();
- // TODO(reference-types): add support for writing expressions instead of
- // function indices.
- bool usesExpressions = false;
+ // if all items are ref.func, we can use the shorter form.
+ bool usesExpressions =
+ std::any_of(segment->data.begin(),
+ segment->data.end(),
+ [](Expression* curr) { return !curr->is<RefFunc>(); });
bool hasTableIndex = false;
if (!isPassive) {
@@ -600,13 +602,27 @@ void WasmBinaryWriter::writeElementSegments() {
o << int8_t(BinaryConsts::End);
}
- if (!usesExpressions && (isPassive || hasTableIndex)) {
- // elemKind funcref
- o << U32LEB(0);
+ if (isPassive || hasTableIndex) {
+ if (usesExpressions) {
+ // elemType funcref
+ writeType(Type::funcref);
+ } else {
+ // elemKind funcref
+ o << U32LEB(0);
+ }
}
o << U32LEB(segment->data.size());
- for (auto& name : segment->data) {
- o << U32LEB(getFunctionIndex(name));
+ if (usesExpressions) {
+ for (auto* item : segment->data) {
+ writeExpression(item);
+ o << int8_t(BinaryConsts::End);
+ }
+ } else {
+ for (auto& item : segment->data) {
+ // We've ensured that all items are ref.func.
+ auto& name = item->cast<RefFunc>()->func;
+ o << U32LEB(getFunctionIndex(name));
+ }
}
}
@@ -2676,14 +2692,6 @@ void WasmBinaryBuilder::processNames() {
}
}
- for (auto& pair : functionTable) {
- auto i = pair.first;
- auto& indices = pair.second;
- for (auto j : indices) {
- wasm.elementSegments[i]->data.push_back(getFunctionName(j));
- }
- }
-
for (auto& iter : globalRefs) {
size_t index = iter.first;
auto& refs = iter.second;
@@ -2787,10 +2795,6 @@ void WasmBinaryBuilder::readElementSegments() {
continue;
}
- if (usesExpressions) {
- throwError("Only elem segments with function indexes are supported.");
- }
-
if (!isPassive) {
Index tableIdx = 0;
if (hasTableIdx) {
@@ -2819,17 +2823,35 @@ void WasmBinaryBuilder::readElementSegments() {
}
if (isPassive || hasTableIdx) {
- auto elemKind = getU32LEB();
- if (elemKind != 0x0) {
- throwError("Only funcref elem kinds are valid.");
+ if (usesExpressions) {
+ auto type = getType();
+ if (type != Type::funcref) {
+ throwError("Only funcref elem kinds are valid.");
+ }
+ } else {
+ auto elemKind = getU32LEB();
+ if (elemKind != 0x0) {
+ throwError("Only funcref elem kinds are valid.");
+ }
}
}
- size_t segmentIndex = functionTable.size();
- auto& indexSegment = functionTable[segmentIndex];
+ auto& segmentData = elementSegments.back()->data;
auto size = getU32LEB();
- for (Index j = 0; j < size; j++) {
- indexSegment.push_back(getU32LEB());
+ if (usesExpressions) {
+ for (Index j = 0; j < size; j++) {
+ segmentData.push_back(readExpression());
+ }
+ } else {
+ for (Index j = 0; j < size; j++) {
+ Index index = getU32LEB();
+ auto sig = getSignatureByFunctionIndex(index);
+ // Use a placeholder name for now
+ auto* refFunc = Builder(wasm).makeRefFunc(
+ Name::fromInt(index), Type(HeapType(sig), Nullable));
+ functionRefs[index].push_back(refFunc);
+ segmentData.push_back(refFunc);
+ }
}
}
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 45c7c9161..7774eac3e 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -3268,12 +3268,14 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) {
Index i = 1;
Name name = Name::fromInt(elemCounter++);
bool hasExplicitName = false;
+ bool isPassive = false;
+ bool usesExpressions = false;
if (table) {
Expression* offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
auto segment = std::make_unique<ElementSegment>(table->name, offset);
segment->setName(name, hasExplicitName);
- parseElemFinish(s, segment, i);
+ parseElemFinish(s, segment, i, false);
return;
}
@@ -3286,10 +3288,20 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) {
return;
}
- if (s[i]->isStr() && s[i]->str() == FUNC) {
+ if (s[i]->isStr()) {
+ if (s[i]->str() == FUNC) {
+ isPassive = true;
+ usesExpressions = false;
+ } else if (s[i]->str() == FUNCREF) {
+ isPassive = true;
+ usesExpressions = true;
+ }
+ }
+
+ if (isPassive) {
auto segment = std::make_unique<ElementSegment>();
segment->setName(name, hasExplicitName);
- parseElemFinish(s, segment, i + 1);
+ parseElemFinish(s, segment, i + 1, usesExpressions);
return;
}
@@ -3326,11 +3338,11 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) {
}
if (!oldStyle) {
- if (s[i]->str() != FUNC) {
- throw ParseException(
- "only the abbreviated form of elemList is supported.");
+ if (s[i]->str() == FUNCREF) {
+ usesExpressions = true;
+ } else if (s[i]->str() != FUNC) {
+ throw ParseException("expected func or funcref.");
}
- // ignore elemType for now
i += 1;
}
@@ -3340,13 +3352,40 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) {
auto segment = std::make_unique<ElementSegment>(table->name, offset);
segment->setName(name, hasExplicitName);
- parseElemFinish(s, segment, i);
+ parseElemFinish(s, segment, i, usesExpressions);
}
ElementSegment* SExpressionWasmBuilder::parseElemFinish(
- Element& s, std::unique_ptr<ElementSegment>& segment, Index i) {
- for (; i < s.size(); i++) {
- segment->data.push_back(getFunctionName(*s[i]));
+ Element& s,
+ std::unique_ptr<ElementSegment>& segment,
+ Index i,
+ bool usesExpressions) {
+
+ if (usesExpressions) {
+ for (; i < s.size(); i++) {
+ if (!s[i]->isList()) {
+ throw ParseException("expected a ref.* expression.");
+ }
+ auto& inner = *s[i];
+ if (elementStartsWith(inner, ITEM)) {
+ if (inner[1]->isList()) {
+ // (item (ref.func $f))
+ segment->data.push_back(parseExpression(inner[1]));
+ } else {
+ // (item ref.func $f)
+ inner.list().removeAt(0);
+ segment->data.push_back(parseExpression(inner));
+ }
+ } else {
+ segment->data.push_back(parseExpression(inner));
+ }
+ }
+ } else {
+ for (; i < s.size(); i++) {
+ auto func = getFunctionName(*s[i]);
+ segment->data.push_back(Builder(wasm).makeRefFunc(
+ func, Type(HeapType(functionSignatures[func]), Nullable)));
+ }
}
return wasm.addElementSegment(std::move(segment));
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 71ecf208e..52ba36872 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -1960,7 +1960,10 @@ void FunctionValidator::visitMemoryGrow(MemoryGrow* curr) {
}
void FunctionValidator::visitRefNull(RefNull* curr) {
- shouldBeTrue(getModule()->features.hasReferenceTypes(),
+ // If we are not in a function, this is a global location like a table. We
+ // allow RefNull there as we represent tables that way regardless of what
+ // features are enabled.
+ shouldBeTrue(!getFunction() || getModule()->features.hasReferenceTypes(),
curr,
"ref.null requires reference-types to be enabled");
shouldBeTrue(
@@ -1978,7 +1981,10 @@ void FunctionValidator::visitRefIs(RefIs* curr) {
}
void FunctionValidator::visitRefFunc(RefFunc* curr) {
- shouldBeTrue(getModule()->features.hasReferenceTypes(),
+ // If we are not in a function, this is a global location like a table. We
+ // allow RefFunc there as we represent tables that way regardless of what
+ // features are enabled.
+ shouldBeTrue(!getFunction() || getModule()->features.hasReferenceTypes(),
curr,
"ref.func requires reference-types to be enabled");
if (!info.validateGlobally) {
@@ -2799,11 +2805,29 @@ static void validateMemory(Module& module, ValidationInfo& info) {
}
static void validateTables(Module& module, ValidationInfo& info) {
+ FunctionValidator validator(module, &info);
+
if (!module.features.hasReferenceTypes()) {
info.shouldBeTrue(module.tables.size() <= 1,
"table",
"Only 1 table definition allowed in MVP (requires "
"--enable-reference-types)");
+ if (!module.tables.empty()) {
+ auto& table = module.tables.front();
+ for (auto& segment : module.elementSegments) {
+ info.shouldBeTrue(segment->table == table->name,
+ "elem",
+ "all element segments should refer to a single table "
+ "in MVP.");
+ for (auto* expr : segment->data) {
+ info.shouldBeTrue(
+ expr->is<RefFunc>(),
+ expr,
+ "all table elements must be non-null funcrefs in MVP.");
+ validator.validate(expr);
+ }
+ }
+ }
}
for (auto& segment : module.elementSegments) {
@@ -2820,11 +2844,17 @@ static void validateTables(Module& module, ValidationInfo& info) {
table->initial * Table::kPageSize),
segment->offset,
"table segment offset should be reasonable");
- FunctionValidator(module, &info).validate(segment->offset);
- }
- for (auto name : segment->data) {
- info.shouldBeTrue(
- module.getFunctionOrNull(name), name, "segment name should be valid");
+ validator.validate(segment->offset);
+ }
+ // Avoid double checking items
+ if (module.features.hasReferenceTypes()) {
+ for (auto* expr : segment->data) {
+ info.shouldBeTrue(
+ expr->is<RefFunc>() || expr->is<RefNull>(),
+ expr,
+ "element segment items must be either ref.func or ref.null func.");
+ validator.validate(expr);
+ }
}
}
}