diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 76 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 61 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 44 |
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); + } } } } |