summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp156
-rw-r--r--src/wasm/wasm-s-parser.cpp21
-rw-r--r--src/wasm/wasm-stack.cpp4
-rw-r--r--src/wasm/wasm-type.cpp37
-rw-r--r--src/wasm/wasm-validator.cpp23
-rw-r--r--src/wasm/wasm.cpp12
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;