diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 156 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 21 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 37 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 23 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 12 |
6 files changed, 162 insertions, 91 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 8f2fe3fe7..2336da912 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -16,6 +16,7 @@ #include <algorithm> #include <fstream> +#include <shared_mutex> #include "support/bits.h" #include "wasm-binary.h" @@ -24,15 +25,74 @@ namespace wasm { void WasmBinaryWriter::prepare() { - // we need function types for all our functions - for (auto& func : wasm->functions) { - if (func->type.isNull()) { - func->type = ensureFunctionType(getSig(func.get()), wasm)->name; + // Collect function types and their frequencies + using Counts = std::unordered_map<Signature, size_t>; + using AtomicCounts = std::unordered_map<Signature, std::atomic_size_t>; + Counts counts; + for (auto& curr : wasm->functions) { + counts[Signature(Type(curr->params), curr->result)]++; + } + for (auto& curr : wasm->events) { + counts[curr->sig]++; + } + + // Parallelize collection of call_indirect type counts + struct TypeCounter : WalkerPass<PostWalker<TypeCounter>> { + AtomicCounts& counts; + std::shared_timed_mutex& mutex; + TypeCounter(AtomicCounts& counts, std::shared_timed_mutex& mutex) + : counts(counts), mutex(mutex) {} + bool isFunctionParallel() override { return true; } + bool modifiesBinaryenIR() override { return false; } + void visitCallIndirect(CallIndirect* curr) { + auto* type = getModule()->getFunctionType(curr->fullType); + Signature sig(Type(type->params), type->result); + { + std::shared_lock<std::shared_timed_mutex> lock(mutex); + auto it = counts.find(sig); + if (it != counts.end()) { + it->second++; + return; + } + } + { + std::lock_guard<std::shared_timed_mutex> lock(mutex); + counts[sig]++; + } + } + Pass* create() override { return new TypeCounter(counts, mutex); } + }; + + std::shared_timed_mutex mutex; + AtomicCounts parallelCounts; + for (auto& kv : counts) { + parallelCounts[kv.first] = 0; + } + + TypeCounter counter(parallelCounts, mutex); + PassRunner runner(wasm); + runner.setIsNested(true); + counter.run(&runner, wasm); + + for (auto& kv : parallelCounts) { + counts[kv.first] += kv.second; + } + + std::vector<std::pair<Signature, size_t>> sorted(counts.begin(), + counts.end()); + std::sort(sorted.begin(), sorted.end(), [&](auto a, auto b) { + // order by frequency then simplicity + if (a.second != b.second) { + return a.second > b.second; + } else { + return a.first < b.first; } - // TODO: depending on upstream flux - // https://github.com/WebAssembly/spec/pull/301 might want this: - // assert(!func->type.isNull()); + }); + for (Index i = 0; i < sorted.size(); ++i) { + typeIndexes[sorted[i].first] = i; + types.push_back(sorted[i].first); } + importInfo = wasm::make_unique<ImportInfo>(*wasm); } @@ -173,43 +233,30 @@ void WasmBinaryWriter::writeMemory() { } void WasmBinaryWriter::writeTypes() { - if (wasm->functionTypes.size() == 0) { + if (types.size() == 0) { return; } if (debug) { std::cerr << "== writeTypes" << std::endl; } auto start = startSection(BinaryConsts::Section::Type); - o << U32LEB(wasm->functionTypes.size()); - for (auto& type : wasm->functionTypes) { + o << U32LEB(types.size()); + for (Index i = 0; i < types.size(); ++i) { + Signature& sig = types[i]; if (debug) { - std::cerr << "write one" << std::endl; + std::cerr << "write " << sig.params << " -> " << sig.results << std::endl; } o << S32LEB(BinaryConsts::EncodedType::Func); - o << U32LEB(type->params.size()); - for (auto param : type->params) { - o << binaryType(param); - } - if (type->result == none) { - o << U32LEB(0); - } else { - o << U32LEB(1); - o << binaryType(type->result); + for (auto& sigType : {sig.params, sig.results}) { + o << U32LEB(sigType.size()); + for (auto type : sigType.expand()) { + o << binaryType(type); + } } } finishSection(start); } -int32_t WasmBinaryWriter::getFunctionTypeIndex(Name type) { - // TODO: optimize - for (size_t i = 0; i < wasm->functionTypes.size(); i++) { - if (wasm->functionTypes[i]->name == type) { - return i; - } - } - abort(); -} - void WasmBinaryWriter::writeImports() { auto num = importInfo->getNumImports(); if (num == 0) { @@ -230,7 +277,7 @@ void WasmBinaryWriter::writeImports() { } writeImportHeader(func); o << U32LEB(int32_t(ExternalKind::Function)); - o << U32LEB(getFunctionTypeIndex(func->type)); + o << U32LEB(getTypeIndex(Signature(Type(func->params), func->result))); }); ModuleUtils::iterImportedGlobals(*wasm, [&](Global* global) { if (debug) { @@ -248,7 +295,7 @@ void WasmBinaryWriter::writeImports() { writeImportHeader(event); o << U32LEB(int32_t(ExternalKind::Event)); o << U32LEB(event->attribute); - o << U32LEB(getFunctionTypeIndex(event->type)); + o << U32LEB(getTypeIndex(event->sig)); }); if (wasm->memory.imported()) { if (debug) { @@ -289,7 +336,7 @@ void WasmBinaryWriter::writeFunctionSignatures() { if (debug) { std::cerr << "write one" << std::endl; } - o << U32LEB(getFunctionTypeIndex(func->type)); + o << U32LEB(getTypeIndex(Signature(Type(func->params), func->result))); }); finishSection(start); } @@ -451,19 +498,28 @@ void WasmBinaryWriter::writeDataSegments() { finishSection(start); } -uint32_t WasmBinaryWriter::getFunctionIndex(Name name) { - assert(indexes.functionIndexes.count(name)); - return indexes.functionIndexes[name]; +uint32_t WasmBinaryWriter::getFunctionIndex(Name name) const { + auto it = indexes.functionIndexes.find(name); + assert(it != indexes.functionIndexes.end()); + return it->second; +} + +uint32_t WasmBinaryWriter::getGlobalIndex(Name name) const { + auto it = indexes.globalIndexes.find(name); + assert(it != indexes.globalIndexes.end()); + return it->second; } -uint32_t WasmBinaryWriter::getGlobalIndex(Name name) { - assert(indexes.globalIndexes.count(name)); - return indexes.globalIndexes[name]; +uint32_t WasmBinaryWriter::getEventIndex(Name name) const { + auto it = indexes.eventIndexes.find(name); + assert(it != indexes.eventIndexes.end()); + return it->second; } -uint32_t WasmBinaryWriter::getEventIndex(Name name) { - assert(indexes.eventIndexes.count(name)); - return indexes.eventIndexes[name]; +uint32_t WasmBinaryWriter::getTypeIndex(Signature sig) const { + auto it = typeIndexes.find(sig); + assert(it != typeIndexes.end()); + return it->second; } void WasmBinaryWriter::writeFunctionTableDeclaration() { @@ -521,7 +577,7 @@ void WasmBinaryWriter::writeEvents() { std::cerr << "write one" << std::endl; } o << U32LEB(event->attribute); - o << U32LEB(getFunctionTypeIndex(event->type)); + o << U32LEB(getTypeIndex(event->sig)); }); finishSection(start); @@ -1364,10 +1420,9 @@ void WasmBinaryBuilder::readImports() { throwError("invalid event index " + std::to_string(index) + " / " + std::to_string(wasm.functionTypes.size())); } - Name type = wasm.functionTypes[index]->name; - std::vector<Type> params = wasm.functionTypes[index]->params; + Type params = Type(wasm.functionTypes[index]->params); auto* curr = - builder.makeEvent(name, attribute, type, std::move(params)); + builder.makeEvent(name, attribute, Signature(params, Type::none)); curr->module = module; curr->base = base; wasm.addEvent(curr); @@ -2039,10 +2094,9 @@ void WasmBinaryBuilder::readEvents() { throwError("invalid event index " + std::to_string(typeIndex) + " / " + std::to_string(wasm.functionTypes.size())); } - Name type = wasm.functionTypes[typeIndex]->name; - std::vector<Type> params = wasm.functionTypes[typeIndex]->params; + Type params = Type(wasm.functionTypes[typeIndex]->params); wasm.addEvent(Builder::makeEvent( - "event$" + std::to_string(i), attribute, type, std::move(params))); + "event$" + std::to_string(i), attribute, Signature(params, Type::none))); } } @@ -4603,7 +4657,7 @@ void WasmBinaryBuilder::visitThrow(Throw* curr) { } auto* event = wasm.events[index].get(); curr->event = event->name; - size_t num = event->params.size(); + size_t num = event->sig.params.size(); curr->operands.resize(num); for (size_t i = 0; i < num; i++) { curr->operands[num - i - 1] = popNonVoidExpression(); @@ -4637,7 +4691,7 @@ void WasmBinaryBuilder::visitBrOnExn(BrOnExn* curr) { // Copy params info into BrOnExn, because it is necessary when BrOnExn is // refinalized without the module. - curr->eventParams = event->params; + curr->sent = event->sig.params; curr->finalize(); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 307c94375..3a761fd17 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -1859,7 +1859,7 @@ Expression* SExpressionWasmBuilder::makeBrOnExn(Element& s) { assert(event && "br_on_exn's event must exist"); // Copy params info into BrOnExn, because it is necessary when BrOnExn is // refinalized without the module. - ret->eventParams = event->params; + ret->sent = event->sig.params; ret->finalize(); return ret; } @@ -2193,7 +2193,6 @@ void SExpressionWasmBuilder::parseImport(Element& s) { j = parseMemoryLimits(inner, j); } } else if (kind == ExternalKind::Event) { - FunctionType* functionType = nullptr; auto event = make_unique<Event>(); if (j >= inner.size()) { throw ParseException("event does not have an attribute", s.line, s.col); @@ -2203,12 +2202,14 @@ void SExpressionWasmBuilder::parseImport(Element& s) { throw ParseException("invalid attribute", attrElem.line, attrElem.col); } event->attribute = atoi(attrElem[1]->c_str()); - Type fakeResult; // just to call parseTypeUse - j = parseTypeUse(inner, j, functionType, event->params, fakeResult); + std::vector<Type> paramTypes; + FunctionType* fakeFunctionType; // just to call parseTypeUse + Type results; + j = parseTypeUse(inner, j, fakeFunctionType, paramTypes, results); event->name = name; event->module = module; event->base = base; - event->type = functionType->name; + event->sig = Signature(Type(paramTypes), results); wasm.addEvent(event.release()); } // If there are more elements, they are invalid @@ -2514,11 +2515,11 @@ void SExpressionWasmBuilder::parseEvent(Element& s, bool preParseImport) { event->attribute = atoi(attrElem[1]->c_str()); // Parse typeuse - FunctionType* functionType = nullptr; - Type fakeResult; // just co call parseTypeUse - i = parseTypeUse(s, i, functionType, event->params, fakeResult); - assert(functionType && "functionType should've been set by parseTypeUse"); - event->type = functionType->name; + std::vector<Type> paramTypes; + Type results; + FunctionType* fakeFunctionType; // just co call parseTypeUse + i = parseTypeUse(s, i, fakeFunctionType, paramTypes, results); + event->sig = Signature(Type(paramTypes), results); // If there are more elements, they are invalid if (i < s.size()) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index c1d4f1222..f1aaff93e 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -68,7 +68,9 @@ void BinaryInstWriter::visitCall(Call* curr) { void BinaryInstWriter::visitCallIndirect(CallIndirect* curr) { int8_t op = curr->isReturn ? BinaryConsts::RetCallIndirect : BinaryConsts::CallIndirect; - o << op << U32LEB(parent.getFunctionTypeIndex(curr->fullType)) + auto* type = parent.getModule()->getFunctionType(curr->fullType); + Signature sig(Type(type->params), type->result); + o << op << U32LEB(parent.getTypeIndex(sig)) << U32LEB(0); // Reserved flags field } diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 9d0f9b108..e114a5540 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -19,22 +19,28 @@ #include <sstream> #include <unordered_map> -#include "wasm-type.h" -#include "wasm-features.h" - #include "compiler-support.h" +#include "support/hash.h" +#include "wasm-features.h" +#include "wasm-type.h" template<> class std::hash<std::vector<wasm::Type>> { public: size_t operator()(const std::vector<wasm::Type>& types) const { - size_t res = 0; + uint32_t res = wasm::rehash(0, uint32_t(types.size())); for (auto vt : types) { - res ^= std::hash<uint32_t>{}(vt); + res = wasm::rehash(res, uint32_t(vt)); } return res; } }; +size_t std::hash<wasm::Signature>:: +operator()(const wasm::Signature& sig) const { + return std::hash<uint64_t>{}(uint64_t(sig.params) << 32 | + uint64_t(sig.results)); +} + namespace wasm { namespace { @@ -123,6 +129,27 @@ const std::vector<Type>& Type::expand() const { return *typeLists[id].get(); } +bool Type::operator<(const Type& other) const { + const std::vector<Type>& these = expand(); + const std::vector<Type>& others = other.expand(); + return std::lexicographical_compare( + these.begin(), + these.end(), + others.begin(), + others.end(), + [](const Type& a, const Type& b) { return uint32_t(a) < uint32_t(b); }); +} + +bool Signature::operator<(const Signature& other) const { + if (results < other.results) { + return true; + } else if (other.results < results) { + return false; + } else { + return params < other.params; + } +} + namespace { std::ostream& diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 05cb45d25..d3f03b76c 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -1703,14 +1703,15 @@ void FunctionValidator::visitThrow(Throw* curr) { if (!shouldBeTrue(!!event, curr, "throw's event must exist")) { return; } - if (!shouldBeTrue(curr->operands.size() == event->params.size(), + if (!shouldBeTrue(curr->operands.size() == event->sig.params.size(), curr, "event's param numbers must match")) { return; } + const std::vector<Type>& paramTypes = event->sig.params.expand(); for (size_t i = 0; i < curr->operands.size(); i++) { if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type, - event->params[i], + paramTypes[i], curr->operands[i], "event param types must match") && !info.quiet) { @@ -1731,10 +1732,10 @@ void FunctionValidator::visitRethrow(Rethrow* curr) { void FunctionValidator::visitBrOnExn(BrOnExn* curr) { Event* event = getModule()->getEventOrNull(curr->event); shouldBeTrue(event != nullptr, curr, "br_on_exn's event must exist"); - shouldBeTrue(event->params == curr->eventParams, + shouldBeTrue(event->sig.params == curr->sent, curr, "br_on_exn's event params and event's params are different"); - noteBreak(curr->name, curr->getSingleSentType(), curr); + noteBreak(curr->name, curr->sent, curr); shouldBeTrue(curr->exnref->type == unreachable || curr->exnref->type == exnref, curr, @@ -2110,23 +2111,19 @@ static void validateEvents(Module& module, ValidationInfo& info) { "Module has events (event-handling is disabled)"); } for (auto& curr : module.events) { - info.shouldBeTrue( - curr->type.is(), curr->name, "Event should have a valid type"); - FunctionType* ft = module.getFunctionType(curr->type); - info.shouldBeEqual( - ft->result, none, curr->name, "Event type's result type should be none"); info.shouldBeEqual(curr->attribute, (unsigned)0, curr->attribute, "Currently only attribute 0 is supported"); - for (auto type : curr->params) { + info.shouldBeEqual(curr->sig.results, + Type(Type::none), + curr->name, + "Event type's result type should be none"); + for (auto type : curr->sig.params.expand()) { info.shouldBeTrue(type.isInteger() || type.isFloat(), curr->name, "Values in an event should have integer or float type"); } - info.shouldBeTrue(curr->params == ft->params, - curr->name, - "Event's function type and internal type should match"); } } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index b137044b2..b71f7d854 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -219,7 +219,7 @@ struct TypeSeeker : public PostWalker<TypeSeeker> { void visitBrOnExn(BrOnExn* curr) { if (curr->name == targetName) { - types.push_back(curr->getSingleSentType()); + types.push_back(curr->sent); } } @@ -924,16 +924,6 @@ void BrOnExn::finalize() { } } -// br_on_exn's type is exnref, which it pushes onto the stack when it is not -// taken, but the type of the value it pushes onto the stack when it is taken -// should be the event type. So this is the type we 'send' to the block end when -// it is taken. Currently we don't support multi value return from a block, we -// pick the type of the first param from the event. -// TODO Remove this function and generalize event type after multi-value support -Type BrOnExn::getSingleSentType() { - return eventParams.empty() ? none : eventParams.front(); -} - void Push::finalize() { if (value->type == unreachable) { type = unreachable; |