diff options
author | Heejin Ahn <aheejin@gmail.com> | 2019-05-31 20:02:37 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-31 20:02:37 -0700 |
commit | fe99e3458f11d1a01fa3ad5b68883dbcba3903af (patch) | |
tree | 6f5eda61c7c7cba9c3b16be5e361cdc148d8b315 /src/wasm | |
parent | 7306f60a4474ca1fa948bddee5c068e7c2f635f6 (diff) | |
download | binaryen-fe99e3458f11d1a01fa3ad5b68883dbcba3903af.tar.gz binaryen-fe99e3458f11d1a01fa3ad5b68883dbcba3903af.tar.bz2 binaryen-fe99e3458f11d1a01fa3ad5b68883dbcba3903af.zip |
Add event section (#2151)
This adds support for the event and the event section, as specified in
https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md#changes-to-the-binary-model.
Wasm events are features that suspend the current execution and transfer
the control flow to a corresponding handler. Currently the only
supported event kind is exceptions.
For events, this includes support for
- Binary file reading/writing
- Wast file reading/writing
- Binaryen.js API
- Fuzzer
- Validation
- Metadce
- Passes: metrics, minify-imports-and-exports,
remove-unused-module-elements
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 95 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 149 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 3 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 35 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 47 |
5 files changed, 324 insertions, 5 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index d8ce18de5..91a2184e9 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -37,6 +37,7 @@ void WasmBinaryWriter::prepare() { ModuleUtils::BinaryIndexes indexes(*wasm); mappedFunctions = std::move(indexes.functionIndexes); mappedGlobals = std::move(indexes.globalIndexes); + mappedEvents = std::move(indexes.eventIndexes); importInfo = wasm::make_unique<ImportInfo>(*wasm); } @@ -63,6 +64,7 @@ void WasmBinaryWriter::write() { writeDataCount(); writeFunctions(); writeDataSegments(); + writeEvents(); if (debugInfo) { writeNames(); } @@ -245,6 +247,15 @@ void WasmBinaryWriter::writeImports() { o << binaryType(global->type); o << U32LEB(global->mutable_); }); + ModuleUtils::iterImportedEvents(*wasm, [&](Event* event) { + if (debug) { + std::cerr << "write one event" << std::endl; + } + writeImportHeader(event); + o << U32LEB(int32_t(ExternalKind::Event)); + o << U32LEB(event->attribute); + o << U32LEB(getFunctionTypeIndex(event->type)); + }); if (wasm->memory.imported()) { if (debug) { std::cerr << "write one memory" << std::endl; @@ -401,6 +412,9 @@ void WasmBinaryWriter::writeExports() { case ExternalKind::Global: o << U32LEB(getGlobalIndex(curr->value)); break; + case ExternalKind::Event: + o << U32LEB(getEventIndex(curr->value)); + break; default: WASM_UNREACHABLE(); } @@ -453,6 +467,11 @@ uint32_t WasmBinaryWriter::getGlobalIndex(Name name) { return mappedGlobals[name]; } +uint32_t WasmBinaryWriter::getEventIndex(Name name) { + assert(mappedEvents.count(name)); + return mappedEvents[name]; +} + void WasmBinaryWriter::writeFunctionTableDeclaration() { if (!wasm->table.exists || wasm->table.imported()) { return; @@ -493,6 +512,27 @@ void WasmBinaryWriter::writeTableElements() { finishSection(start); } +void WasmBinaryWriter::writeEvents() { + if (importInfo->getNumDefinedEvents() == 0) { + return; + } + if (debug) { + std::cerr << "== writeEvents" << std::endl; + } + auto start = startSection(BinaryConsts::Section::Event); + auto num = importInfo->getNumDefinedEvents(); + o << U32LEB(num); + ModuleUtils::iterDefinedEvents(*wasm, [&](Event* event) { + if (debug) { + std::cerr << "write one" << std::endl; + } + o << U32LEB(event->attribute); + o << U32LEB(getFunctionTypeIndex(event->type)); + }); + + finishSection(start); +} + void WasmBinaryWriter::writeNames() { bool hasContents = false; if (wasm->functions.size() > 0) { @@ -828,6 +868,9 @@ void WasmBinaryBuilder::read() { case BinaryConsts::Section::Table: readFunctionTableDeclaration(); break; + case BinaryConsts::Section::Event: + readEvents(); + break; default: { readUserSection(payloadLen); if (pos > oldPos + payloadLen) { @@ -1210,6 +1253,13 @@ Name WasmBinaryBuilder::getGlobalName(Index index) { return wasm.globals[index]->name; } +Name WasmBinaryBuilder::getEventName(Index index) { + if (index >= wasm.events.size()) { + throwError("invalid event index"); + } + return wasm.events[index]->name; +} + void WasmBinaryBuilder::getResizableLimits(Address& initial, Address& max, bool& shared, @@ -1310,6 +1360,23 @@ void WasmBinaryBuilder::readImports() { wasm.addGlobal(curr); break; } + case ExternalKind::Event: { + auto name = Name(std::string("eimport$") + std::to_string(i)); + auto attribute = getU32LEB(); + auto index = getU32LEB(); + if (index >= wasm.functionTypes.size()) { + 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; + auto* curr = + builder.makeEvent(name, attribute, type, std::move(params)); + curr->module = module; + curr->base = base; + wasm.addEvent(curr); + break; + } default: { throwError("bad import kind"); } } } @@ -1841,6 +1908,9 @@ void WasmBinaryBuilder::processFunctions() { case ExternalKind::Global: curr->value = getGlobalName(index); break; + case ExternalKind::Event: + curr->value = getEventName(index); + break; default: throwError("bad export kind"); } @@ -1954,6 +2024,31 @@ void WasmBinaryBuilder::readTableElements() { } } +void WasmBinaryBuilder::readEvents() { + if (debug) { + std::cerr << "== readEvents" << std::endl; + } + size_t numEvents = getU32LEB(); + if (debug) { + std::cerr << "num: " << numEvents << std::endl; + } + for (size_t i = 0; i < numEvents; i++) { + if (debug) { + std::cerr << "read one" << std::endl; + } + auto attribute = getU32LEB(); + auto typeIndex = getU32LEB(); + if (typeIndex >= wasm.functionTypes.size()) { + 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; + wasm.addEvent(Builder::makeEvent( + "event$" + std::to_string(i), attribute, type, std::move(params))); + } +} + static bool isIdChar(char ch) { return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '!' || ch == '#' || ch == '$' || diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 3a5dfbced..8ead42608 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -388,6 +388,8 @@ void SExpressionWasmBuilder::preParseImports(Element& curr) { parseTable(curr, true /* preParseImport */); } else if (id == MEMORY) { parseMemory(curr, true /* preParseImport */); + } else if (id == EVENT) { + parseEvent(curr, true /* preParseImport */); } else { throw ParseException( "fancy import we don't support yet", curr.line, curr.col); @@ -430,6 +432,9 @@ void SExpressionWasmBuilder::parseModuleElement(Element& curr) { if (id == TYPE) { return; // already done } + if (id == EVENT) { + return parseEvent(curr); + } std::cerr << "bad module element " << id.str << '\n'; throw ParseException("unknown module element", curr.line, curr.col); } @@ -473,6 +478,19 @@ Name SExpressionWasmBuilder::getGlobalName(Element& s) { } } +Name SExpressionWasmBuilder::getEventName(Element& s) { + if (s.dollared()) { + return s.str(); + } else { + // index + size_t offset = atoi(s.str().c_str()); + if (offset >= eventNames.size()) { + throw ParseException("unknown event in getEventName"); + } + return eventNames[offset]; + } +} + // Parse various forms of (param ...) or (local ...) element. This ignores all // parameter or local names when specified. std::vector<Type> SExpressionWasmBuilder::parseParamOrLocal(Element& s) { @@ -1877,6 +1895,8 @@ void SExpressionWasmBuilder::parseExport(Element& s) { ex->kind = ExternalKind::Table; } else if (elementStartsWith(inner, GLOBAL)) { ex->kind = ExternalKind::Global; + } else if (inner[0]->str() == EVENT) { + ex->kind = ExternalKind::Event; } else { throw ParseException("invalid export"); } @@ -1913,6 +1933,8 @@ void SExpressionWasmBuilder::parseImport(Element& s) { wasm.table.exists = true; } else if (elementStartsWith(*s[3], GLOBAL)) { kind = ExternalKind::Global; + } else if ((*s[3])[0]->str() == EVENT) { + kind = ExternalKind::Event; } else { newStyle = false; // either (param..) or (result..) } @@ -1936,6 +1958,9 @@ void SExpressionWasmBuilder::parseImport(Element& s) { name = Name("import$memory$" + std::to_string(0)); } else if (kind == ExternalKind::Table) { name = Name("import$table$" + std::to_string(0)); + } else if (kind == ExternalKind::Event) { + name = Name("import$event" + std::to_string(eventCounter++)); + eventNames.push_back(name); } else { throw ParseException("invalid import"); } @@ -1957,7 +1982,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) { if (kind == ExternalKind::Function) { FunctionType* functionType = nullptr; auto func = make_unique<Function>(); - parseTypeUse(inner, j, functionType, func->params, func->result); + j = parseTypeUse(inner, j, functionType, func->params, func->result); func->name = name; func->module = module; func->base = base; @@ -1968,9 +1993,9 @@ void SExpressionWasmBuilder::parseImport(Element& s) { Type type; bool mutable_ = false; if (inner[j]->isStr()) { - type = stringToType(inner[j]->str()); + type = stringToType(inner[j++]->str()); } else { - auto& inner2 = *inner[j]; + auto& inner2 = *inner[j++]; if (inner2[0]->str() != MUT) { throw ParseException("expected mut"); } @@ -1997,6 +2022,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) { } else { wasm.table.max = Table::kUnlimitedSize; } + j++; // funcref // ends with the table element type } else if (kind == ExternalKind::Memory) { wasm.memory.module = module; @@ -2007,10 +2033,32 @@ void SExpressionWasmBuilder::parseImport(Element& s) { throw ParseException("bad memory limit declaration"); } wasm.memory.shared = true; - parseMemoryLimits(limits, 1); + j = parseMemoryLimits(limits, 1); } else { - parseMemoryLimits(inner, j); + 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); + } + auto& attrElem = *inner[j++]; + if (!elementStartsWith(attrElem, ATTR) || attrElem.size() != 2) { + 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); + event->name = name; + event->module = module; + event->base = base; + event->type = functionType->name; + wasm.addEvent(event.release()); + } + // If there are more elements, they are invalid + if (j < inner.size()) { + throw ParseException("invalid element", inner[j]->line, inner[j]->col); } } @@ -2234,4 +2282,95 @@ void SExpressionWasmBuilder::parseType(Element& s) { wasm.addFunctionType(std::move(type)); } +void SExpressionWasmBuilder::parseEvent(Element& s, bool preParseImport) { + auto event = make_unique<Event>(); + size_t i = 1; + + // Parse name + if (s[i]->isStr() && s[i]->dollared()) { + auto& inner = *s[i++]; + event->name = inner.str(); + if (wasm.getEventOrNull(event->name)) { + throw ParseException("duplicate event", inner.line, inner.col); + } + } else { + event->name = Name::fromInt(eventCounter); + assert(!wasm.getEventOrNull(event->name)); + } + eventCounter++; + eventNames.push_back(event->name); + + // Parse import, if any + if (i < s.size() && elementStartsWith(*s[i], IMPORT)) { + assert(preParseImport && "import element in non-preParseImport mode"); + auto& importElem = *s[i++]; + if (importElem.size() != 3) { + throw ParseException("invalid import", importElem.line, importElem.col); + } + if (!importElem[1]->isStr() || importElem[1]->dollared()) { + throw ParseException( + "invalid import module name", importElem[1]->line, importElem[1]->col); + } + if (!importElem[2]->isStr() || importElem[2]->dollared()) { + throw ParseException( + "invalid import base name", importElem[2]->line, importElem[2]->col); + } + event->module = importElem[1]->str(); + event->base = importElem[2]->str(); + } + + // Parse export, if any + if (i < s.size() && elementStartsWith(*s[i], EXPORT)) { + auto& exportElem = *s[i++]; + if (event->module.is()) { + throw ParseException("import and export cannot be specified together", + exportElem.line, + exportElem.col); + } + if (exportElem.size() != 2) { + throw ParseException("invalid export", exportElem.line, exportElem.col); + } + if (!exportElem[1]->isStr() || exportElem[1]->dollared()) { + throw ParseException( + "invalid export name", exportElem[1]->line, exportElem[1]->col); + } + auto ex = make_unique<Export>(); + ex->name = exportElem[1]->str(); + if (wasm.getExportOrNull(ex->name)) { + throw ParseException( + "duplicate export", exportElem[1]->line, exportElem[1]->col); + } + ex->value = event->name; + ex->kind = ExternalKind::Event; + } + + // Parse attribute + if (i >= s.size()) { + throw ParseException("event does not have an attribute", s.line, s.col); + } + auto& attrElem = *s[i++]; + if (!elementStartsWith(attrElem, ATTR) || attrElem.size() != 2) { + throw ParseException("invalid attribute", attrElem.line, attrElem.col); + } + if (!attrElem[1]->isStr()) { + throw ParseException( + "invalid attribute", attrElem[1]->line, attrElem[1]->col); + } + 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; + + // If there are more elements, they are invalid + if (i < s.size()) { + throw ParseException("invalid element", s[i]->line, s[i]->col); + } + + wasm.addEvent(event.release()); +} + } // namespace wasm diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index ebaba3f24..2c2723bf6 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -68,6 +68,9 @@ FeatureSet getFeatures(Type type) { if (type == v128) { return FeatureSet::SIMD; } + if (type == except_ref) { + return FeatureSet::ExceptionHandling; + } return FeatureSet(); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 01fe7e976..1d12c2452 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -1745,6 +1745,10 @@ static void validateExports(Module& module, ValidationInfo& info) { info.shouldBeTrue(name == Name("0") || name == module.memory.name, name, "module memory exports must be found"); + } else if (exp->kind == ExternalKind::Event) { + info.shouldBeTrue(module.getEventOrNull(name), + name, + "module event exports must be found"); } else { WASM_UNREACHABLE(); } @@ -1855,6 +1859,36 @@ static void validateTable(Module& module, ValidationInfo& info) { } } +static void validateEvents(Module& module, ValidationInfo& info) { + if (!module.events.empty()) { + info.shouldBeTrue(module.features.hasExceptionHandling(), + module.events[0]->name, + "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.shouldBeTrue(!curr->params.empty(), + curr->name, + "There should be 1 or more values in an event type"); + info.shouldBeEqual(curr->attribute, + (unsigned)0, + curr->attribute, + "Currently only attribute 0 is supported"); + for (auto type : curr->params) { + info.shouldBeTrue(isIntegerType(type) || isFloatType(type), + 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"); + } +} + static void validateModule(Module& module, ValidationInfo& info) { // start if (module.start.is()) { @@ -1889,6 +1923,7 @@ bool WasmValidator::validate(Module& module, Flags flags) { validateGlobals(module, info); validateMemory(module, info); validateTable(module, info); + validateEvents(module, info); validateModule(module, info); } // validate additional internal IR details when in pass-debug mode diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index c32ead836..c543686ed 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -82,6 +82,8 @@ Name SPECTEST("spectest"); Name PRINT("print"); Name EXIT("exit"); Name SHARED("shared"); +Name EVENT("event"); +Name ATTR("attr"); // Expressions @@ -908,11 +910,20 @@ Function* Module::getFunction(Name name) { Global* Module::getGlobal(Name name) { auto iter = globalsMap.find(name); if (iter == globalsMap.end()) { + assert(false); Fatal() << "Module::getGlobal: " << name << " does not exist"; } return iter->second; } +Event* Module::getEvent(Name name) { + auto iter = eventsMap.find(name); + if (iter == eventsMap.end()) { + Fatal() << "Module::getEvent: " << name << " does not exist"; + } + return iter->second; +} + FunctionType* Module::getFunctionTypeOrNull(Name name) { auto iter = functionTypesMap.find(name); if (iter == functionTypesMap.end()) { @@ -945,6 +956,14 @@ Global* Module::getGlobalOrNull(Name name) { return iter->second; } +Event* Module::getEventOrNull(Name name) { + auto iter = eventsMap.find(name); + if (iter == eventsMap.end()) { + return nullptr; + } + return iter->second; +} + FunctionType* Module::addFunctionType(std::unique_ptr<FunctionType> curr) { if (!curr->name.is()) { Fatal() << "Module::addFunctionType: empty name"; @@ -1009,6 +1028,20 @@ Global* Module::addGlobal(Global* curr) { return curr; } +Event* Module::addEvent(Event* curr) { + if (!curr->name.is()) { + Fatal() << "Module::addEvent: empty name"; + } + if (getEventOrNull(curr->name)) { + Fatal() << "Module::addEvent: " << curr->name << " already exists"; + } + + events.emplace_back(curr); + + eventsMap[curr->name] = curr; + return curr; +} + void Module::addStart(const Name& s) { start = s; } void Module::removeFunctionType(Name name) { @@ -1051,6 +1084,16 @@ void Module::removeGlobal(Name name) { globalsMap.erase(name); } +void Module::removeEvent(Name name) { + for (size_t i = 0; i < events.size(); i++) { + if (events[i]->name == name) { + events.erase(events.begin() + i); + break; + } + } + eventsMap.erase(name); +} + // TODO: remove* for other elements void Module::updateMaps() { @@ -1070,6 +1113,10 @@ void Module::updateMaps() { for (auto& curr : globals) { globalsMap[curr->name] = curr.get(); } + eventsMap.clear(); + for (auto& curr : events) { + eventsMap[curr->name] = curr.get(); + } } void Module::clearDebugInfo() { debugInfoFileNames.clear(); } |