diff options
Diffstat (limited to 'src/wasm/wasm-binary.cpp')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 2599 |
1 files changed, 1905 insertions, 694 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index b8c3f1049..6369a79fa 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -17,10 +17,10 @@ #include <algorithm> #include <fstream> -#include "wasm-binary.h" -#include "wasm-stack.h" #include "ir/module-utils.h" #include "support/bits.h" +#include "wasm-binary.h" +#include "wasm-stack.h" namespace wasm { @@ -30,7 +30,9 @@ void WasmBinaryWriter::prepare() { if (func->type.isNull()) { func->type = ensureFunctionType(getSig(func.get()), wasm)->name; } - // TODO: depending on upstream flux https://github.com/WebAssembly/spec/pull/301 might want this: assert(!func->type.isNull()); + // TODO: depending on upstream flux + // https://github.com/WebAssembly/spec/pull/301 might want this: + // assert(!func->type.isNull()); } ModuleUtils::BinaryIndexes indexes(*wasm); mappedFunctions = std::move(indexes.functionIndexes); @@ -61,9 +63,12 @@ void WasmBinaryWriter::write() { writeDataCount(); writeFunctions(); writeDataSegments(); - if (debugInfo) writeNames(); - if (sourceMap && !sourceMapUrl.empty()) writeSourceMapUrl(); - if (symbolMap.size() > 0) writeSymbolMap(); + if (debugInfo) + writeNames(); + if (sourceMap && !sourceMapUrl.empty()) + writeSourceMapUrl(); + if (symbolMap.size() > 0) + writeSymbolMap(); if (sourceMap) { writeSourceMapEpilog(); @@ -76,7 +81,8 @@ void WasmBinaryWriter::write() { } void WasmBinaryWriter::writeHeader() { - if (debug) std::cerr << "== writeHeader" << std::endl; + if (debug) + std::cerr << "== writeHeader" << std::endl; o << int32_t(BinaryConsts::Magic); // magic number \0asm o << int32_t(BinaryConsts::Version); } @@ -88,11 +94,12 @@ int32_t WasmBinaryWriter::writeU32LEBPlaceholder() { return ret; } -void WasmBinaryWriter::writeResizableLimits(Address initial, Address maximum, - bool hasMaximum, bool shared) { - uint32_t flags = - (hasMaximum ? (uint32_t) BinaryConsts::HasMaximum : 0U) | - (shared ? (uint32_t) BinaryConsts::IsShared : 0U); +void WasmBinaryWriter::writeResizableLimits(Address initial, + Address maximum, + bool hasMaximum, + bool shared) { + uint32_t flags = (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) | + (shared ? (uint32_t)BinaryConsts::IsShared : 0U); o << U32LEB(flags); o << U32LEB(initial); if (hasMaximum) { @@ -100,63 +107,76 @@ void WasmBinaryWriter::writeResizableLimits(Address initial, Address maximum, } } -template<typename T> -int32_t WasmBinaryWriter::startSection(T code) { +template<typename T> int32_t WasmBinaryWriter::startSection(T code) { o << U32LEB(code); - if (sourceMap) sourceMapLocationsSizeAtSectionStart = sourceMapLocations.size(); + if (sourceMap) + sourceMapLocationsSizeAtSectionStart = sourceMapLocations.size(); return writeU32LEBPlaceholder(); // section size to be filled in later } void WasmBinaryWriter::finishSection(int32_t start) { - int32_t size = o.size() - start - MaxLEB32Bytes; // section size does not include the reserved bytes of the size field itself + // section size does not include the reserved bytes of the size field itself + int32_t size = o.size() - start - MaxLEB32Bytes; auto sizeFieldSize = o.writeAt(start, U32LEB(size)); if (sizeFieldSize != MaxLEB32Bytes) { // we can save some room, nice assert(sizeFieldSize < MaxLEB32Bytes); - std::move(&o[start] + MaxLEB32Bytes, &o[start] + MaxLEB32Bytes + size, &o[start] + sizeFieldSize); + std::move(&o[start] + MaxLEB32Bytes, + &o[start] + MaxLEB32Bytes + size, + &o[start] + sizeFieldSize); auto adjustment = MaxLEB32Bytes - sizeFieldSize; o.resize(o.size() - adjustment); if (sourceMap) { - for (auto i = sourceMapLocationsSizeAtSectionStart; i < sourceMapLocations.size(); ++i) { + for (auto i = sourceMapLocationsSizeAtSectionStart; + i < sourceMapLocations.size(); + ++i) { sourceMapLocations[i].first -= adjustment; } } } } -int32_t WasmBinaryWriter::startSubsection(BinaryConsts::UserSections::Subsection code) { +int32_t +WasmBinaryWriter::startSubsection(BinaryConsts::UserSections::Subsection code) { return startSection(code); } -void WasmBinaryWriter::finishSubsection(int32_t start) { - finishSection(start); -} +void WasmBinaryWriter::finishSubsection(int32_t start) { finishSection(start); } void WasmBinaryWriter::writeStart() { - if (!wasm->start.is()) return; - if (debug) std::cerr << "== writeStart" << std::endl; + if (!wasm->start.is()) + return; + if (debug) + std::cerr << "== writeStart" << std::endl; auto start = startSection(BinaryConsts::Section::Start); o << U32LEB(getFunctionIndex(wasm->start.str)); finishSection(start); } void WasmBinaryWriter::writeMemory() { - if (!wasm->memory.exists || wasm->memory.imported()) return; - if (debug) std::cerr << "== writeMemory" << std::endl; + if (!wasm->memory.exists || wasm->memory.imported()) + return; + if (debug) + std::cerr << "== writeMemory" << std::endl; auto start = startSection(BinaryConsts::Section::Memory); o << U32LEB(1); // Define 1 memory - writeResizableLimits(wasm->memory.initial, wasm->memory.max, - wasm->memory.hasMax(), wasm->memory.shared); + writeResizableLimits(wasm->memory.initial, + wasm->memory.max, + wasm->memory.hasMax(), + wasm->memory.shared); finishSection(start); } void WasmBinaryWriter::writeTypes() { - if (wasm->functionTypes.size() == 0) return; - if (debug) std::cerr << "== writeTypes" << std::endl; + if (wasm->functionTypes.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) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; o << S32LEB(BinaryConsts::EncodedType::Func); o << U32LEB(type->params.size()); for (auto param : type->params) { @@ -175,15 +195,18 @@ void WasmBinaryWriter::writeTypes() { 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; + if (wasm->functionTypes[i]->name == type) + return i; } abort(); } void WasmBinaryWriter::writeImports() { auto num = importInfo->getNumImports(); - if (num == 0) return; - if (debug) std::cerr << "== writeImports" << std::endl; + if (num == 0) + return; + if (debug) + std::cerr << "== writeImports" << std::endl; auto start = startSection(BinaryConsts::Section::Import); o << U32LEB(num); auto writeImportHeader = [&](Importable* import) { @@ -191,42 +214,54 @@ void WasmBinaryWriter::writeImports() { writeInlineString(import->base.str); }; ModuleUtils::iterImportedFunctions(*wasm, [&](Function* func) { - if (debug) std::cerr << "write one function" << std::endl; + if (debug) + std::cerr << "write one function" << std::endl; writeImportHeader(func); o << U32LEB(int32_t(ExternalKind::Function)); o << U32LEB(getFunctionTypeIndex(func->type)); }); ModuleUtils::iterImportedGlobals(*wasm, [&](Global* global) { - if (debug) std::cerr << "write one global" << std::endl; + if (debug) + std::cerr << "write one global" << std::endl; writeImportHeader(global); o << U32LEB(int32_t(ExternalKind::Global)); o << binaryType(global->type); o << U32LEB(global->mutable_); }); if (wasm->memory.imported()) { - if (debug) std::cerr << "write one memory" << std::endl; + if (debug) + std::cerr << "write one memory" << std::endl; writeImportHeader(&wasm->memory); o << U32LEB(int32_t(ExternalKind::Memory)); - writeResizableLimits(wasm->memory.initial, wasm->memory.max, - wasm->memory.hasMax(), wasm->memory.shared); + writeResizableLimits(wasm->memory.initial, + wasm->memory.max, + wasm->memory.hasMax(), + wasm->memory.shared); } if (wasm->table.imported()) { - if (debug) std::cerr << "write one table" << std::endl; + if (debug) + std::cerr << "write one table" << std::endl; writeImportHeader(&wasm->table); o << U32LEB(int32_t(ExternalKind::Table)); o << S32LEB(BinaryConsts::EncodedType::AnyFunc); - writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), /*shared=*/false); + writeResizableLimits(wasm->table.initial, + wasm->table.max, + wasm->table.hasMax(), + /*shared=*/false); } finishSection(start); } void WasmBinaryWriter::writeFunctionSignatures() { - if (importInfo->getNumDefinedFunctions() == 0) return; - if (debug) std::cerr << "== writeFunctionSignatures" << std::endl; + if (importInfo->getNumDefinedFunctions() == 0) + return; + if (debug) + std::cerr << "== writeFunctionSignatures" << std::endl; auto start = startSection(BinaryConsts::Section::Function); o << U32LEB(importInfo->getNumDefinedFunctions()); ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; o << U32LEB(getFunctionTypeIndex(func->type)); }); finishSection(start); @@ -237,27 +272,35 @@ void WasmBinaryWriter::writeExpression(Expression* curr) { } void WasmBinaryWriter::writeFunctions() { - if (importInfo->getNumDefinedFunctions() == 0) return; - if (debug) std::cerr << "== writeFunctions" << std::endl; + if (importInfo->getNumDefinedFunctions() == 0) + return; + if (debug) + std::cerr << "== writeFunctions" << std::endl; auto start = startSection(BinaryConsts::Section::Code); o << U32LEB(importInfo->getNumDefinedFunctions()); ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) { size_t sourceMapLocationsSizeAtFunctionStart = sourceMapLocations.size(); - if (debug) std::cerr << "write one at" << o.size() << std::endl; + if (debug) + std::cerr << "write one at" << o.size() << std::endl; size_t sizePos = writeU32LEBPlaceholder(); size_t start = o.size(); - if (debug) std::cerr << "writing" << func->name << std::endl; + if (debug) + std::cerr << "writing" << func->name << std::endl; // Emit Stack IR if present, and if we can if (func->stackIR && !sourceMap) { - if (debug) std::cerr << "write Stack IR" << std::endl; + if (debug) + std::cerr << "write Stack IR" << std::endl; StackIRFunctionStackWriter<WasmBinaryWriter>(func, *this, o, debug); } else { - if (debug) std::cerr << "write Binaryen IR" << std::endl; + if (debug) + std::cerr << "write Binaryen IR" << std::endl; FunctionStackWriter<WasmBinaryWriter>(func, *this, o, sourceMap, debug); } size_t size = o.size() - start; assert(size <= std::numeric_limits<uint32_t>::max()); - if (debug) std::cerr << "body size: " << size << ", writing at " << sizePos << ", next starts at " << o.size() << std::endl; + if (debug) + std::cerr << "body size: " << size << ", writing at " << sizePos + << ", next starts at " << o.size() << std::endl; auto sizeFieldSize = o.writeAt(sizePos, U32LEB(size)); if (sizeFieldSize != MaxLEB32Bytes) { // we can save some room, nice @@ -266,24 +309,30 @@ void WasmBinaryWriter::writeFunctions() { auto adjustment = MaxLEB32Bytes - sizeFieldSize; o.resize(o.size() - adjustment); if (sourceMap) { - for (auto i = sourceMapLocationsSizeAtFunctionStart; i < sourceMapLocations.size(); ++i) { + for (auto i = sourceMapLocationsSizeAtFunctionStart; + i < sourceMapLocations.size(); + ++i) { sourceMapLocations[i].first -= adjustment; } } } - tableOfContents.functionBodies.emplace_back(func->name, sizePos + sizeFieldSize, size); + tableOfContents.functionBodies.emplace_back( + func->name, sizePos + sizeFieldSize, size); }); finishSection(start); } void WasmBinaryWriter::writeGlobals() { - if (importInfo->getNumDefinedGlobals() == 0) return; - if (debug) std::cerr << "== writeglobals" << std::endl; + if (importInfo->getNumDefinedGlobals() == 0) + return; + if (debug) + std::cerr << "== writeglobals" << std::endl; auto start = startSection(BinaryConsts::Section::Global); auto num = importInfo->getNumDefinedGlobals(); o << U32LEB(num); ModuleUtils::iterDefinedGlobals(*wasm, [&](Global* global) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; o << binaryType(global->type); o << U32LEB(global->mutable_); writeExpression(global->init); @@ -293,22 +342,33 @@ void WasmBinaryWriter::writeGlobals() { } void WasmBinaryWriter::writeExports() { - if (wasm->exports.size() == 0) return; - if (debug) std::cerr << "== writeexports" << std::endl; + if (wasm->exports.size() == 0) + return; + if (debug) + std::cerr << "== writeexports" << std::endl; auto start = startSection(BinaryConsts::Section::Export); o << U32LEB(wasm->exports.size()); for (auto& curr : wasm->exports) { - if (debug) std::cerr << "write one" << std::endl; + if (debug) + std::cerr << "write one" << std::endl; writeInlineString(curr->name.str); o << U32LEB(int32_t(curr->kind)); switch (curr->kind) { - case ExternalKind::Function: o << U32LEB(getFunctionIndex(curr->value)); break; - case ExternalKind::Table: o << U32LEB(0); break; - case ExternalKind::Memory: o << U32LEB(0); break; - case ExternalKind::Global: o << U32LEB(getGlobalIndex(curr->value)); break; - default: WASM_UNREACHABLE(); + case ExternalKind::Function: + o << U32LEB(getFunctionIndex(curr->value)); + break; + case ExternalKind::Table: + o << U32LEB(0); + break; + case ExternalKind::Memory: + o << U32LEB(0); + break; + case ExternalKind::Global: + o << U32LEB(getGlobalIndex(curr->value)); + break; + default: + WASM_UNREACHABLE(); } - } finishSection(start); } @@ -323,7 +383,8 @@ void WasmBinaryWriter::writeDataCount() { } void WasmBinaryWriter::writeDataSegments() { - if (wasm->memory.segments.size() == 0) return; + if (wasm->memory.segments.size() == 0) + return; if (wasm->memory.segments.size() > WebLimitations::MaxDataSegments) { std::cerr << "Some VMs may not accept this binary because it has a large " << "number of data segments. Run the limit-segments pass to " @@ -357,12 +418,17 @@ uint32_t WasmBinaryWriter::getGlobalIndex(Name name) { } void WasmBinaryWriter::writeFunctionTableDeclaration() { - if (!wasm->table.exists || wasm->table.imported()) return; - if (debug) std::cerr << "== writeFunctionTableDeclaration" << std::endl; + if (!wasm->table.exists || wasm->table.imported()) + return; + if (debug) + std::cerr << "== writeFunctionTableDeclaration" << std::endl; auto start = startSection(BinaryConsts::Section::Table); o << U32LEB(1); // Declare 1 table. o << S32LEB(BinaryConsts::EncodedType::AnyFunc); - writeResizableLimits(wasm->table.initial, wasm->table.max, wasm->table.hasMax(), /*shared=*/false); + writeResizableLimits(wasm->table.initial, + wasm->table.max, + wasm->table.hasMax(), + /*shared=*/false); finishSection(start); } @@ -370,12 +436,14 @@ void WasmBinaryWriter::writeTableElements() { if (!wasm->table.exists || wasm->table.segments.size() == 0) { return; } - if (debug) std::cerr << "== writeTableElements" << std::endl; + if (debug) + std::cerr << "== writeTableElements" << std::endl; auto start = startSection(BinaryConsts::Section::Element); o << U32LEB(wasm->table.segments.size()); for (auto& segment : wasm->table.segments) { - o << U32LEB(0); // Table index; 0 in the MVP (and binaryen IR only has 1 table) + // Table index; 0 in the MVP (and binaryen IR only has 1 table) + o << U32LEB(0); writeExpression(segment.offset); o << int8_t(BinaryConsts::End); o << U32LEB(segment.data.size()); @@ -392,11 +460,14 @@ void WasmBinaryWriter::writeNames() { hasContents = true; getFunctionIndex(wasm->functions[0]->name); // generate mappedFunctions } - if (!hasContents) return; - if (debug) std::cerr << "== writeNames" << std::endl; + if (!hasContents) + return; + if (debug) + std::cerr << "== writeNames" << std::endl; auto start = startSection(BinaryConsts::Section::User); writeInlineString(BinaryConsts::UserSections::Name); - auto substart = startSubsection(BinaryConsts::UserSections::Subsection::NameFunction); + auto substart = + startSubsection(BinaryConsts::UserSections::Subsection::NameFunction); o << U32LEB(mappedFunctions.size()); Index emitted = 0; auto add = [&](Function* curr) { @@ -413,7 +484,8 @@ void WasmBinaryWriter::writeNames() { } void WasmBinaryWriter::writeSourceMapUrl() { - if (debug) std::cerr << "== writeSourceMapUrl" << std::endl; + if (debug) + std::cerr << "== writeSourceMapUrl" << std::endl; auto start = startSection(BinaryConsts::Section::User); writeInlineString(BinaryConsts::UserSections::SourceMapUrl); writeInlineString(sourceMapUrl.c_str()); @@ -431,13 +503,14 @@ void WasmBinaryWriter::writeSymbolMap() { } void WasmBinaryWriter::initializeDebugInfo() { - lastDebugLocation = { 0, /* lineNumber = */ 1, 0 }; + lastDebugLocation = {0, /* lineNumber = */ 1, 0}; } void WasmBinaryWriter::writeSourceMapProlog() { *sourceMap << "{\"version\":3,\"sources\":["; for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) { - if (i > 0) *sourceMap << ","; + if (i > 0) + *sourceMap << ","; // TODO respect JSON string encoding, e.g. quotes and control chars. *sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\""; } @@ -456,15 +529,17 @@ static void writeBase64VLQ(std::ostream& out, int32_t n) { } // more VLG digit will follow -- add continuation bit (0x20), // base64 codes 'g'..'z', '0'..'9', '+', '/' - out << char(digit < 20 ? 'g' + digit : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/'); + out << char(digit < 20 + ? 'g' + digit + : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/'); } } void WasmBinaryWriter::writeSourceMapEpilog() { // write source map entries size_t lastOffset = 0; - Function::DebugLocation lastLoc = { 0, /* lineNumber = */ 1, 0 }; - for (const auto &offsetAndlocPair : sourceMapLocations) { + Function::DebugLocation lastLoc = {0, /* lineNumber = */ 1, 0}; + for (const auto& offsetAndlocPair : sourceMapLocations) { if (lastOffset > 0) { *sourceMap << ","; } @@ -473,7 +548,8 @@ void WasmBinaryWriter::writeSourceMapEpilog() { writeBase64VLQ(*sourceMap, int32_t(offset - lastOffset)); writeBase64VLQ(*sourceMap, int32_t(loc.fileIndex - lastLoc.fileIndex)); writeBase64VLQ(*sourceMap, int32_t(loc.lineNumber - lastLoc.lineNumber)); - writeBase64VLQ(*sourceMap, int32_t(loc.columnNumber - lastLoc.columnNumber)); + writeBase64VLQ(*sourceMap, + int32_t(loc.columnNumber - lastLoc.columnNumber)); lastLoc = loc; lastOffset = offset; } @@ -516,20 +592,26 @@ void WasmBinaryWriter::writeFeaturesSection() { // FeatureSet::toString() auto toString = [](FeatureSet::Feature f) { switch (f) { - case FeatureSet::Atomics: return "atomics"; - case FeatureSet::MutableGlobals: return "mutable-globals"; - case FeatureSet::TruncSat: return "nontrapping-fptoint"; - case FeatureSet::SIMD: return "simd128"; - case FeatureSet::BulkMemory: return "bulk-memory"; - case FeatureSet::SignExt: return "sign-ext"; - default: WASM_UNREACHABLE(); + case FeatureSet::Atomics: + return "atomics"; + case FeatureSet::MutableGlobals: + return "mutable-globals"; + case FeatureSet::TruncSat: + return "nontrapping-fptoint"; + case FeatureSet::SIMD: + return "simd128"; + case FeatureSet::BulkMemory: + return "bulk-memory"; + case FeatureSet::SignExt: + return "sign-ext"; + default: + WASM_UNREACHABLE(); } }; std::vector<const char*> features; - wasm->features.iterFeatures([&](FeatureSet::Feature f) { - features.push_back(toString(f)); - }); + wasm->features.iterFeatures( + [&](FeatureSet::Feature f) { features.push_back(toString(f)); }); auto start = startSection(BinaryConsts::User); writeInlineString(BinaryConsts::UserSections::TargetFeatures); @@ -541,7 +623,6 @@ void WasmBinaryWriter::writeFeaturesSection() { finishSection(start); } - void WasmBinaryWriter::writeDebugLocation(const Function::DebugLocation& loc) { if (loc == lastDebugLocation) { return; @@ -568,7 +649,8 @@ void WasmBinaryWriter::writeInlineString(const char* name) { } static bool isHexDigit(char ch) { - return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); + return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F'); } static int decodeHexNibble(char ch) { @@ -586,11 +668,13 @@ void WasmBinaryWriter::writeEscapedName(const char* name) { for (int32_t i = 0; i < size;) { char ch = name[i++]; // support only `\xx` escapes; ignore invalid or unsupported escapes - if (ch != '\\' || i + 1 >= size || !isHexDigit(name[i]) || !isHexDigit(name[i + 1])) { + if (ch != '\\' || i + 1 >= size || !isHexDigit(name[i]) || + !isHexDigit(name[i + 1])) { unescaped.push_back(ch); continue; } - unescaped.push_back(char((decodeHexNibble(name[i]) << 4) | decodeHexNibble(name[i + 1]))); + unescaped.push_back( + char((decodeHexNibble(name[i]) << 4) | decodeHexNibble(name[i + 1]))); i += 2; } writeInlineString(unescaped.c_str()); @@ -606,19 +690,25 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) { void WasmBinaryWriter::emitBuffer(const char* data, size_t size) { assert(size > 0); buffersToWrite.emplace_back(data, size, o.size()); - o << uint32_t(0); // placeholder, we'll fill in the pointer to the buffer later when we have it + // placeholder, we'll fill in the pointer to the buffer later when we have it + o << uint32_t(0); } -void WasmBinaryWriter::emitString(const char *str) { - if (debug) std::cerr << "emitString " << str << std::endl; +void WasmBinaryWriter::emitString(const char* str) { + if (debug) + std::cerr << "emitString " << str << std::endl; emitBuffer(str, strlen(str) + 1); } void WasmBinaryWriter::finishUp() { - if (debug) std::cerr << "finishUp" << std::endl; + if (debug) + std::cerr << "finishUp" << std::endl; // finish buffers for (const auto& buffer : buffersToWrite) { - if (debug) std::cerr << "writing buffer" << (int)buffer.data[0] << "," << (int)buffer.data[1] << " at " << o.size() << " and pointer is at " << buffer.pointerLocation << std::endl; + if (debug) + std::cerr << "writing buffer" << (int)buffer.data[0] << "," + << (int)buffer.data[1] << " at " << o.size() + << " and pointer is at " << buffer.pointerLocation << std::endl; o.writeAt(buffer.pointerLocation, (uint32_t)o.size()); for (size_t i = 0; i < buffer.size; i++) { o << (uint8_t)buffer.data[i]; @@ -637,42 +727,71 @@ void WasmBinaryBuilder::read() { while (more()) { uint32_t sectionCode = getU32LEB(); uint32_t payloadLen = getU32LEB(); - if (pos + payloadLen > input.size()) throwError("Section extends beyond end of input"); + if (pos + payloadLen > input.size()) + throwError("Section extends beyond end of input"); auto oldPos = pos; - // note the section in the list of seen sections, as almost no sections can appear more than once, - // and verify those that shouldn't do not. - if (sectionCode != BinaryConsts::Section::User && sectionCode != BinaryConsts::Section::Code) { + // note the section in the list of seen sections, as almost no sections can + // appear more than once, and verify those that shouldn't do not. + if (sectionCode != BinaryConsts::Section::User && + sectionCode != BinaryConsts::Section::Code) { if (!seenSections.insert(BinaryConsts::Section(sectionCode)).second) { - throwError("section seen more than once: " + std::to_string(sectionCode)); + throwError("section seen more than once: " + + std::to_string(sectionCode)); } } switch (sectionCode) { - case BinaryConsts::Section::Start: readStart(); break; - case BinaryConsts::Section::Memory: readMemory(); break; - case BinaryConsts::Section::Type: readSignatures(); break; - case BinaryConsts::Section::Import: readImports(); break; - case BinaryConsts::Section::Function: readFunctionSignatures(); break; - case BinaryConsts::Section::Code: readFunctions(); break; - case BinaryConsts::Section::Export: readExports(); break; - case BinaryConsts::Section::Element: readTableElements(); break; + case BinaryConsts::Section::Start: + readStart(); + break; + case BinaryConsts::Section::Memory: + readMemory(); + break; + case BinaryConsts::Section::Type: + readSignatures(); + break; + case BinaryConsts::Section::Import: + readImports(); + break; + case BinaryConsts::Section::Function: + readFunctionSignatures(); + break; + case BinaryConsts::Section::Code: + readFunctions(); + break; + case BinaryConsts::Section::Export: + readExports(); + break; + case BinaryConsts::Section::Element: + readTableElements(); + break; case BinaryConsts::Section::Global: { readGlobals(); - // imports can read global imports, so we run getGlobalName and create the mapping - // but after we read globals, we need to add the internal globals too, so do that here + // imports can read global imports, so we run getGlobalName and create + // the mapping but after we read globals, we need to add the internal + // globals too, so do that here mappedGlobals.clear(); // wipe the mapping - getGlobalName(-1); // force rebuild + getGlobalName(-1); // force rebuild break; } - case BinaryConsts::Section::Data: readDataSegments(); break; - case BinaryConsts::Section::DataCount: readDataCount(); break; - case BinaryConsts::Section::Table: readFunctionTableDeclaration(); break; + case BinaryConsts::Section::Data: + readDataSegments(); + break; + case BinaryConsts::Section::DataCount: + readDataCount(); + break; + case BinaryConsts::Section::Table: + readFunctionTableDeclaration(); + break; default: { readUserSection(payloadLen); if (pos > oldPos + payloadLen) { - throwError("bad user section size, started at " + std::to_string(oldPos) + " plus payload " + std::to_string(payloadLen) + " not being equal to new position " + std::to_string(pos)); + throwError("bad user section size, started at " + + std::to_string(oldPos) + " plus payload " + + std::to_string(payloadLen) + + " not being equal to new position " + std::to_string(pos)); } pos = oldPos + payloadLen; } @@ -680,7 +799,9 @@ void WasmBinaryBuilder::read() { // make sure we advanced exactly past this section if (pos != oldPos + payloadLen) { - throwError("bad section size, started at " + std::to_string(oldPos) + " plus payload " + std::to_string(payloadLen) + " not being equal to new position " + std::to_string(pos)); + throwError("bad section size, started at " + std::to_string(oldPos) + + " plus payload " + std::to_string(payloadLen) + + " not being equal to new position " + std::to_string(pos)); } } @@ -703,7 +824,9 @@ void WasmBinaryBuilder::readUserSection(size_t payloadLen) { } else { // an unfamiliar custom section if (sectionName.equals(BinaryConsts::UserSections::Linking)) { - std::cerr << "warning: linking section is present, so this is not a standard wasm file - binaryen cannot handle this properly!\n"; + std::cerr + << "warning: linking section is present, so this is not a standard " + "wasm file - binaryen cannot handle this properly!\n"; } wasm.userSections.resize(wasm.userSections.size() + 1); auto& section = wasm.userSections.back(); @@ -717,107 +840,129 @@ void WasmBinaryBuilder::readUserSection(size_t payloadLen) { } uint8_t WasmBinaryBuilder::getInt8() { - if (!more()) throwError("unexpected end of input"); - if (debug) std::cerr << "getInt8: " << (int)(uint8_t)input[pos] << " (at " << pos << ")" << std::endl; + if (!more()) + throwError("unexpected end of input"); + if (debug) + std::cerr << "getInt8: " << (int)(uint8_t)input[pos] << " (at " << pos + << ")" << std::endl; return input[pos++]; } uint16_t WasmBinaryBuilder::getInt16() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = uint16_t(getInt8()); ret |= uint16_t(getInt8()) << 8; - if (debug) std::cerr << "getInt16: " << ret << "/0x" << std::hex << ret << std::dec << " ==>" << std::endl; + if (debug) + std::cerr << "getInt16: " << ret << "/0x" << std::hex << ret << std::dec + << " ==>" << std::endl; return ret; } uint32_t WasmBinaryBuilder::getInt32() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = uint32_t(getInt16()); ret |= uint32_t(getInt16()) << 16; - if (debug) std::cerr << "getInt32: " << ret << "/0x" << std::hex << ret << std::dec <<" ==>" << std::endl; + if (debug) + std::cerr << "getInt32: " << ret << "/0x" << std::hex << ret << std::dec + << " ==>" << std::endl; return ret; } uint64_t WasmBinaryBuilder::getInt64() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = uint64_t(getInt32()); ret |= uint64_t(getInt32()) << 32; - if (debug) std::cerr << "getInt64: " << ret << "/0x" << std::hex << ret << std::dec << " ==>" << std::endl; + if (debug) + std::cerr << "getInt64: " << ret << "/0x" << std::hex << ret << std::dec + << " ==>" << std::endl; return ret; } uint8_t WasmBinaryBuilder::getLaneIndex(size_t lanes) { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = getInt8(); - if (ret >= lanes) throwError("Illegal lane index"); - if (debug) std::cerr << "getLaneIndex(" << lanes << "): " << ret << " ==>" << std::endl; + if (ret >= lanes) + throwError("Illegal lane index"); + if (debug) + std::cerr << "getLaneIndex(" << lanes << "): " << ret << " ==>" + << std::endl; return ret; } Literal WasmBinaryBuilder::getFloat32Literal() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = Literal(getInt32()); ret = ret.castToF32(); - if (debug) std::cerr << "getFloat32: " << ret << " ==>" << std::endl; + if (debug) + std::cerr << "getFloat32: " << ret << " ==>" << std::endl; return ret; } Literal WasmBinaryBuilder::getFloat64Literal() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto ret = Literal(getInt64()); ret = ret.castToF64(); - if (debug) std::cerr << "getFloat64: " << ret << " ==>" << std::endl; + if (debug) + std::cerr << "getFloat64: " << ret << " ==>" << std::endl; return ret; } Literal WasmBinaryBuilder::getVec128Literal() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; std::array<uint8_t, 16> bytes; for (auto i = 0; i < 16; ++i) { bytes[i] = getInt8(); } auto ret = Literal(bytes.data()); - if (debug) std::cerr << "getVec128: " << ret << " ==>" << std::endl; + if (debug) + std::cerr << "getVec128: " << ret << " ==>" << std::endl; return ret; } uint32_t WasmBinaryBuilder::getU32LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; U32LEB ret; - ret.read([&]() { - return getInt8(); - }); - if (debug) std::cerr << "getU32LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return getInt8(); }); + if (debug) + std::cerr << "getU32LEB: " << ret.value << " ==>" << std::endl; return ret.value; } uint64_t WasmBinaryBuilder::getU64LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; U64LEB ret; - ret.read([&]() { - return getInt8(); - }); - if (debug) std::cerr << "getU64LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return getInt8(); }); + if (debug) + std::cerr << "getU64LEB: " << ret.value << " ==>" << std::endl; return ret.value; } int32_t WasmBinaryBuilder::getS32LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; S32LEB ret; - ret.read([&]() { - return (int8_t)getInt8(); - }); - if (debug) std::cerr << "getS32LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return (int8_t)getInt8(); }); + if (debug) + std::cerr << "getS32LEB: " << ret.value << " ==>" << std::endl; return ret.value; } int64_t WasmBinaryBuilder::getS64LEB() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; S64LEB ret; - ret.read([&]() { - return (int8_t)getInt8(); - }); - if (debug) std::cerr << "getS64LEB: " << ret.value << " ==>" << std::endl; + ret.read([&]() { return (int8_t)getInt8(); }); + if (debug) + std::cerr << "getS64LEB: " << ret.value << " ==>" << std::endl; return ret.value; } @@ -825,15 +970,19 @@ Type WasmBinaryBuilder::getType() { int type = getS32LEB(); switch (type) { // None only used for block signatures. TODO: Separate out? - case BinaryConsts::EncodedType::Empty: return none; - case BinaryConsts::EncodedType::i32: return i32; - case BinaryConsts::EncodedType::i64: return i64; - case BinaryConsts::EncodedType::f32: return f32; - case BinaryConsts::EncodedType::f64: return f64; - case BinaryConsts::EncodedType::v128: return v128; - default: { - throwError("invalid wasm type: " + std::to_string(type)); - } + case BinaryConsts::EncodedType::Empty: + return none; + case BinaryConsts::EncodedType::i32: + return i32; + case BinaryConsts::EncodedType::i64: + return i64; + case BinaryConsts::EncodedType::f32: + return f32; + case BinaryConsts::EncodedType::f64: + return f64; + case BinaryConsts::EncodedType::v128: + return v128; + default: { throwError("invalid wasm type: " + std::to_string(type)); } } WASM_UNREACHABLE(); } @@ -847,61 +996,74 @@ Type WasmBinaryBuilder::getConcreteType() { } Name WasmBinaryBuilder::getInlineString() { - if (debug) std::cerr << "<==" << std::endl; + if (debug) + std::cerr << "<==" << std::endl; auto len = getU32LEB(); std::string str; for (size_t i = 0; i < len; i++) { auto curr = char(getInt8()); if (curr == 0) { - throwError("inline string contains NULL (0). that is technically valid in wasm, but you shouldn't do it, and it's not supported in binaryen"); + throwError( + "inline string contains NULL (0). that is technically valid in wasm, " + "but you shouldn't do it, and it's not supported in binaryen"); } str = str + curr; } - if (debug) std::cerr << "getInlineString: " << str << " ==>" << std::endl; + if (debug) + std::cerr << "getInlineString: " << str << " ==>" << std::endl; return Name(str); } void WasmBinaryBuilder::verifyInt8(int8_t x) { int8_t y = getInt8(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::verifyInt16(int16_t x) { int16_t y = getInt16(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::verifyInt32(int32_t x) { int32_t y = getInt32(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::verifyInt64(int64_t x) { int64_t y = getInt64(); - if (x != y) throwError("surprising value"); + if (x != y) + throwError("surprising value"); } void WasmBinaryBuilder::ungetInt8() { assert(pos > 0); - if (debug) std::cerr << "ungetInt8 (at " << pos << ")" << std::endl; + if (debug) + std::cerr << "ungetInt8 (at " << pos << ")" << std::endl; pos--; } void WasmBinaryBuilder::readHeader() { - if (debug) std::cerr << "== readHeader" << std::endl; + if (debug) + std::cerr << "== readHeader" << std::endl; verifyInt32(BinaryConsts::Magic); verifyInt32(BinaryConsts::Version); } void WasmBinaryBuilder::readStart() { - if (debug) std::cerr << "== readStart" << std::endl; + if (debug) + std::cerr << "== readStart" << std::endl; startIndex = getU32LEB(); } void WasmBinaryBuilder::readMemory() { - if (debug) std::cerr << "== readMemory" << std::endl; + if (debug) + std::cerr << "== readMemory" << std::endl; auto numMemories = getU32LEB(); - if (!numMemories) return; + if (!numMemories) + return; if (numMemories != 1) { throwError("Must be exactly 1 memory"); } @@ -909,22 +1071,29 @@ void WasmBinaryBuilder::readMemory() { throwError("Memory cannot be both imported and defined"); } wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kUnlimitedSize); + getResizableLimits(wasm.memory.initial, + wasm.memory.max, + wasm.memory.shared, + Memory::kUnlimitedSize); } void WasmBinaryBuilder::readSignatures() { - if (debug) std::cerr << "== readSignatures" << std::endl; + if (debug) + std::cerr << "== readSignatures" << std::endl; size_t numTypes = getU32LEB(); - if (debug) std::cerr << "num: " << numTypes << std::endl; + if (debug) + std::cerr << "num: " << numTypes << std::endl; for (size_t i = 0; i < numTypes; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto curr = make_unique<FunctionType>(); auto form = getS32LEB(); if (form != BinaryConsts::EncodedType::Func) { throwError("bad signature form " + std::to_string(form)); } size_t numParams = getU32LEB(); - if (debug) std::cerr << "num params: " << numParams << std::endl; + if (debug) + std::cerr << "num params: " << numParams << std::endl; for (size_t j = 0; j < numParams; j++) { curr->params.push_back(getConcreteType()); } @@ -949,36 +1118,46 @@ Name WasmBinaryBuilder::getFunctionIndexName(Index i) { return wasm.functions[i]->name; } -void WasmBinaryBuilder::getResizableLimits(Address& initial, Address& max, bool &shared, Address defaultIfNoMax) { +void WasmBinaryBuilder::getResizableLimits(Address& initial, + Address& max, + bool& shared, + Address defaultIfNoMax) { auto flags = getU32LEB(); initial = getU32LEB(); bool hasMax = (flags & BinaryConsts::HasMaximum) != 0; bool isShared = (flags & BinaryConsts::IsShared) != 0; - if (isShared && !hasMax) throwError("shared memory must have max size"); + if (isShared && !hasMax) + throwError("shared memory must have max size"); shared = isShared; - if (hasMax) max = getU32LEB(); - else max = defaultIfNoMax; + if (hasMax) + max = getU32LEB(); + else + max = defaultIfNoMax; } void WasmBinaryBuilder::readImports() { - if (debug) std::cerr << "== readImports" << std::endl; + if (debug) + std::cerr << "== readImports" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; Builder builder(wasm); for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto module = getInlineString(); auto base = getInlineString(); auto kind = (ExternalKind)getU32LEB(); - // We set a unique prefix for the name based on the kind. This ensures no collisions - // between them, which can't occur here (due to the index i) but could occur later - // due to the names section. + // We set a unique prefix for the name based on the kind. This ensures no + // collisions between them, which can't occur here (due to the index i) but + // could occur later due to the names section. switch (kind) { case ExternalKind::Function: { auto name = Name(std::string("fimport$") + std::to_string(i)); auto index = getU32LEB(); if (index >= wasm.functionTypes.size()) { - throwError("invalid function index " + std::to_string(index) + " / " + std::to_string(wasm.functionTypes.size())); + throwError("invalid function index " + std::to_string(index) + " / " + + std::to_string(wasm.functionTypes.size())); } auto* functionType = wasm.functionTypes[index].get(); auto params = functionType->params; @@ -997,11 +1176,14 @@ void WasmBinaryBuilder::readImports() { wasm.table.name = Name(std::string("timport$") + std::to_string(i)); auto elementType = getS32LEB(); WASM_UNUSED(elementType); - if (elementType != BinaryConsts::EncodedType::AnyFunc) throwError("Imported table type is not AnyFunc"); + if (elementType != BinaryConsts::EncodedType::AnyFunc) + throwError("Imported table type is not AnyFunc"); wasm.table.exists = true; bool is_shared; - getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); - if (is_shared) throwError("Tables may not be shared"); + getResizableLimits( + wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); + if (is_shared) + throwError("Tables may not be shared"); break; } case ExternalKind::Memory: { @@ -1009,22 +1191,27 @@ void WasmBinaryBuilder::readImports() { wasm.memory.base = base; wasm.memory.name = Name(std::to_string(i)); wasm.memory.exists = true; - getResizableLimits(wasm.memory.initial, wasm.memory.max, wasm.memory.shared, Memory::kUnlimitedSize); + getResizableLimits(wasm.memory.initial, + wasm.memory.max, + wasm.memory.shared, + Memory::kUnlimitedSize); break; } case ExternalKind::Global: { auto name = Name(std::string("gimport$") + std::to_string(i)); auto type = getConcreteType(); auto mutable_ = getU32LEB(); - auto* curr = builder.makeGlobal(name, type, nullptr, mutable_ ? Builder::Mutable : Builder::Immutable); + auto* curr = + builder.makeGlobal(name, + type, + nullptr, + mutable_ ? Builder::Mutable : Builder::Immutable); curr->module = module; curr->base = base; wasm.addGlobal(curr); break; } - default: { - throwError("bad import kind"); - } + default: { throwError("bad import kind"); } } } } @@ -1041,11 +1228,14 @@ void WasmBinaryBuilder::requireFunctionContext(const char* error) { } void WasmBinaryBuilder::readFunctionSignatures() { - if (debug) std::cerr << "== readFunctionSignatures" << std::endl; + if (debug) + std::cerr << "== readFunctionSignatures" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto index = getU32LEB(); if (index >= wasm.functionTypes.size()) { throwError("invalid function type index for function"); @@ -1055,27 +1245,30 @@ void WasmBinaryBuilder::readFunctionSignatures() { } void WasmBinaryBuilder::readFunctions() { - if (debug) std::cerr << "== readFunctions" << std::endl; + if (debug) + std::cerr << "== readFunctions" << std::endl; size_t total = getU32LEB(); if (total != functionTypes.size()) { throwError("invalid function section size, must equal types"); } for (size_t i = 0; i < total; i++) { - if (debug) std::cerr << "read one at " << pos << std::endl; + if (debug) + std::cerr << "read one at " << pos << std::endl; size_t size = getU32LEB(); if (size == 0) { throwError("empty function size"); } endOfFunction = pos + size; - Function *func = new Function; + Function* func = new Function; func->name = Name::fromInt(i); currFunction = func; readNextDebugLocation(); auto type = functionTypes[i]; - if (debug) std::cerr << "reading " << i << std::endl; + if (debug) + std::cerr << "reading " << i << std::endl; func->type = type->name; func->result = type->result; for (size_t j = 0; j < type->params.size(); j++) { @@ -1093,7 +1286,8 @@ void WasmBinaryBuilder::readFunctions() { std::swap(func->prologLocation, debugLocation); { // process the function body - if (debug) std::cerr << "processing function: " << i << std::endl; + if (debug) + std::cerr << "processing function: " << i << std::endl; nextLabel = 0; debugLocation.clear(); willBeIgnored = false; @@ -1118,16 +1312,20 @@ void WasmBinaryBuilder::readFunctions() { debugLocation.clear(); functions.push_back(func); } - if (debug) std::cerr << " end function bodies" << std::endl; + if (debug) + std::cerr << " end function bodies" << std::endl; } void WasmBinaryBuilder::readExports() { - if (debug) std::cerr << "== readExports" << std::endl; + if (debug) + std::cerr << "== readExports" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; std::set<Name> names; for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto curr = new Export; curr->name = getInlineString(); if (names.count(curr->name) > 0) { @@ -1154,11 +1352,12 @@ static int32_t readBase64VLQ(std::istream& in) { value |= digit << shift; break; } - if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && - ch != '+' && ch != '/') { + if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') && ch != '+' && + ch != '/') { throw MapParseException("invalid VLQ digit"); } - uint32_t digit = ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31)); + uint32_t digit = + ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31)); value |= digit << shift; shift += 5; } @@ -1166,7 +1365,8 @@ static int32_t readBase64VLQ(std::istream& in) { } void WasmBinaryBuilder::readSourceMapHeader() { - if (!sourceMap) return; + if (!sourceMap) + return; auto skipWhitespace = [&]() { while (sourceMap->peek() == ' ' || sourceMap->peek() == '\n') @@ -1174,7 +1374,8 @@ void WasmBinaryBuilder::readSourceMapHeader() { }; auto maybeReadChar = [&](char expected) { - if (sourceMap->peek() != expected) return false; + if (sourceMap->peek() != expected) + return false; sourceMap->get(); return true; }; @@ -1193,7 +1394,8 @@ void WasmBinaryBuilder::readSourceMapHeader() { size_t pos; while (1) { int ch = sourceMap->get(); - if (ch == EOF) return false; + if (ch == EOF) + return false; if (ch == '\"') { if (matching) { // we matched a terminating quote. @@ -1226,7 +1428,8 @@ void WasmBinaryBuilder::readSourceMapHeader() { if (ch == EOF) { throw MapParseException("unexpected EOF in the middle of string"); } - if (ch == '\"') break; + if (ch == '\"') + break; vec.push_back(ch); } } @@ -1263,13 +1466,15 @@ void WasmBinaryBuilder::readSourceMapHeader() { // read first debug location uint32_t position = readBase64VLQ(*sourceMap); uint32_t fileIndex = readBase64VLQ(*sourceMap); - uint32_t lineNumber = readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number + uint32_t lineNumber = + readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number uint32_t columnNumber = readBase64VLQ(*sourceMap); - nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; + nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}}; } void WasmBinaryBuilder::readNextDebugLocation() { - if (!sourceMap) return; + if (!sourceMap) + return; while (nextDebugLocation.first && nextDebugLocation.first <= pos) { if (nextDebugLocation.first < pos) { @@ -1299,9 +1504,10 @@ void WasmBinaryBuilder::readNextDebugLocation() { int32_t lineNumberDelta = readBase64VLQ(*sourceMap); uint32_t lineNumber = nextDebugLocation.second.lineNumber + lineNumberDelta; int32_t columnNumberDelta = readBase64VLQ(*sourceMap); - uint32_t columnNumber = nextDebugLocation.second.columnNumber + columnNumberDelta; + uint32_t columnNumber = + nextDebugLocation.second.columnNumber + columnNumberDelta; - nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } }; + nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}}; } } @@ -1317,33 +1523,38 @@ Expression* WasmBinaryBuilder::readExpression() { } void WasmBinaryBuilder::readGlobals() { - if (debug) std::cerr << "== readGlobals" << std::endl; + if (debug) + std::cerr << "== readGlobals" << std::endl; size_t num = getU32LEB(); - if (debug) std::cerr << "num: " << num << std::endl; + if (debug) + std::cerr << "num: " << num << std::endl; for (size_t i = 0; i < num; i++) { - if (debug) std::cerr << "read one" << std::endl; + if (debug) + std::cerr << "read one" << std::endl; auto type = getConcreteType(); auto mutable_ = getU32LEB(); - if (mutable_ & ~1) throwError("Global mutability must be 0 or 1"); + if (mutable_ & ~1) + throwError("Global mutability must be 0 or 1"); auto* init = readExpression(); - wasm.addGlobal(Builder::makeGlobal( - "global$" + std::to_string(i), - type, - init, - mutable_ ? Builder::Mutable : Builder::Immutable - )); + wasm.addGlobal( + Builder::makeGlobal("global$" + std::to_string(i), + type, + init, + mutable_ ? Builder::Mutable : Builder::Immutable)); } } void WasmBinaryBuilder::processExpressions() { - if (debug) std::cerr << "== processExpressions" << std::endl; + if (debug) + std::cerr << "== processExpressions" << std::endl; unreachableInTheWasmSense = false; while (1) { Expression* curr; auto ret = readExpression(curr); if (!curr) { lastSeparator = ret; - if (debug) std::cerr << "== processExpressions finished" << std::endl; + if (debug) + std::cerr << "== processExpressions finished" << std::endl; return; } expressionStack.push_back(curr); @@ -1351,15 +1562,18 @@ void WasmBinaryBuilder::processExpressions() { // once we see something unreachable, we don't want to add anything else // to the stack, as it could be stacky code that is non-representable in // our AST. but we do need to skip it - // if there is nothing else here, just stop. otherwise, go into unreachable - // mode. peek to see what to do + // if there is nothing else here, just stop. otherwise, go into + // unreachable mode. peek to see what to do if (pos == endOfFunction) { throwError("Reached function end without seeing End opcode"); } - if (!more()) throwError("unexpected end of input"); + if (!more()) + throwError("unexpected end of input"); auto peek = input[pos]; if (peek == BinaryConsts::End || peek == BinaryConsts::Else) { - if (debug) std::cerr << "== processExpressions finished with unreachable" << std::endl; + if (debug) + std::cerr << "== processExpressions finished with unreachable" + << std::endl; readNextDebugLocation(); lastSeparator = BinaryConsts::ASTNodes(peek); pos++; @@ -1373,10 +1587,11 @@ void WasmBinaryBuilder::processExpressions() { } void WasmBinaryBuilder::skipUnreachableCode() { - if (debug) std::cerr << "== skipUnreachableCode" << std::endl; - // preserve the stack, and restore it. it contains the instruction that made us - // unreachable, and we can ignore anything after it. things after it may pop, - // we want to undo that + if (debug) + std::cerr << "== skipUnreachableCode" << std::endl; + // preserve the stack, and restore it. it contains the instruction that made + // us unreachable, and we can ignore anything after it. things after it may + // pop, we want to undo that auto savedStack = expressionStack; // note we are entering unreachable code, and note what the state as before so // we can restore it @@ -1387,12 +1602,14 @@ void WasmBinaryBuilder::skipUnreachableCode() { // result in uneachables being returned expressionStack.clear(); while (1) { - // set the unreachableInTheWasmSense flag each time, as sub-blocks may set and unset it + // set the unreachableInTheWasmSense flag each time, as sub-blocks may set + // and unset it unreachableInTheWasmSense = true; Expression* curr; auto ret = readExpression(curr); if (!curr) { - if (debug) std::cerr << "== skipUnreachableCode finished" << std::endl; + if (debug) + std::cerr << "== skipUnreachableCode finished" << std::endl; lastSeparator = ret; unreachableInTheWasmSense = false; willBeIgnored = before; @@ -1404,15 +1621,20 @@ void WasmBinaryBuilder::skipUnreachableCode() { } Expression* WasmBinaryBuilder::popExpression() { - if (debug) std::cerr << "== popExpression" << std::endl; + if (debug) + std::cerr << "== popExpression" << std::endl; if (expressionStack.empty()) { if (unreachableInTheWasmSense) { // in unreachable code, trying to pop past the polymorphic stack // area results in receiving unreachables - if (debug) std::cerr << "== popping unreachable from polymorphic stack" << std::endl; + if (debug) + std::cerr << "== popping unreachable from polymorphic stack" + << std::endl; return allocator.alloc<Unreachable>(); } - throwError("attempted pop from empty stack / beyond block start boundary at " + std::to_string(pos)); + throwError( + "attempted pop from empty stack / beyond block start boundary at " + + std::to_string(pos)); } // the stack is not empty, and we would not be going out of the current block auto ret = expressionStack.back(); @@ -1422,7 +1644,8 @@ Expression* WasmBinaryBuilder::popExpression() { Expression* WasmBinaryBuilder::popNonVoidExpression() { auto* ret = popExpression(); - if (ret->type != none) return ret; + if (ret->type != none) + return ret; // we found a void, so this is stacky code that we must handle carefully Builder builder(wasm); // add elements until we find a non-void @@ -1431,7 +1654,8 @@ Expression* WasmBinaryBuilder::popNonVoidExpression() { while (1) { auto* curr = popExpression(); expressions.push_back(curr); - if (curr->type != none) break; + if (curr->type != none) + break; } auto* block = builder.makeBlock(); while (!expressions.empty()) { @@ -1462,7 +1686,8 @@ Name WasmBinaryBuilder::getGlobalName(Index index) { ModuleUtils::iterImportedGlobals(wasm, add); ModuleUtils::iterDefinedGlobals(wasm, add); } - if (index == Index(-1)) return Name("null"); // just a force-rebuild + if (index == Index(-1)) + return Name("null"); // just a force-rebuild if (mappedGlobals.count(index) == 0) { throwError("bad global index"); } @@ -1493,10 +1718,17 @@ void WasmBinaryBuilder::processFunctions() { curr->value = getFunctionIndexName(index); break; } - case ExternalKind::Table: curr->value = Name::fromInt(0); break; - case ExternalKind::Memory: curr->value = Name::fromInt(0); break; - case ExternalKind::Global: curr->value = getGlobalName(index); break; - default: throwError("bad export kind"); + case ExternalKind::Table: + curr->value = Name::fromInt(0); + break; + case ExternalKind::Memory: + curr->value = Name::fromInt(0); + break; + case ExternalKind::Global: + curr->value = getGlobalName(index); + break; + default: + throwError("bad export kind"); } wasm.addExport(curr); } @@ -1523,13 +1755,15 @@ void WasmBinaryBuilder::processFunctions() { } void WasmBinaryBuilder::readDataCount() { - if (debug) std::cerr << "== readDataCount" << std::endl; + if (debug) + std::cerr << "== readDataCount" << std::endl; hasDataCount = true; dataCount = getU32LEB(); } void WasmBinaryBuilder::readDataSegments() { - if (debug) std::cerr << "== readDataSegments" << std::endl; + if (debug) + std::cerr << "== readDataSegments" << std::endl; auto num = getU32LEB(); for (size_t i = 0; i < num; i++) { Memory::Segment curr; @@ -1558,25 +1792,34 @@ void WasmBinaryBuilder::readDataSegments() { } void WasmBinaryBuilder::readFunctionTableDeclaration() { - if (debug) std::cerr << "== readFunctionTableDeclaration" << std::endl; + if (debug) + std::cerr << "== readFunctionTableDeclaration" << std::endl; auto numTables = getU32LEB(); - if (numTables != 1) throwError("Only 1 table definition allowed in MVP"); - if (wasm.table.exists) throwError("Table cannot be both imported and defined"); + if (numTables != 1) + throwError("Only 1 table definition allowed in MVP"); + if (wasm.table.exists) + throwError("Table cannot be both imported and defined"); wasm.table.exists = true; auto elemType = getS32LEB(); - if (elemType != BinaryConsts::EncodedType::AnyFunc) throwError("ElementType must be AnyFunc in MVP"); + if (elemType != BinaryConsts::EncodedType::AnyFunc) + throwError("ElementType must be AnyFunc in MVP"); bool is_shared; - getResizableLimits(wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); - if (is_shared) throwError("Tables may not be shared"); + getResizableLimits( + wasm.table.initial, wasm.table.max, is_shared, Table::kUnlimitedSize); + if (is_shared) + throwError("Tables may not be shared"); } void WasmBinaryBuilder::readTableElements() { - if (debug) std::cerr << "== readTableElements" << std::endl; + if (debug) + std::cerr << "== readTableElements" << std::endl; auto numSegments = getU32LEB(); - if (numSegments >= Table::kMaxSize) throwError("Too many segments"); + if (numSegments >= Table::kMaxSize) + throwError("Too many segments"); for (size_t i = 0; i < numSegments; i++) { auto tableIndex = getU32LEB(); - if (tableIndex != 0) throwError("Table elements must refer to table 0 in MVP"); + if (tableIndex != 0) + throwError("Table elements must refer to table 0 in MVP"); wasm.table.segments.emplace_back(readExpression()); auto& indexSegment = functionTable[i]; @@ -1588,20 +1831,21 @@ void WasmBinaryBuilder::readTableElements() { } static bool isIdChar(char ch) { - return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || - ch == '!' || ch == '#' || ch == '$' || ch == '%' || ch == '&' || ch == '\'' || ch == '*' || - ch == '+' || ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '<' || ch == '=' || - ch == '>' || ch == '?' || ch == '@' || ch == '^' || ch == '_' || ch == '`' || ch == '|' || - ch == '~'; + return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || + (ch >= 'a' && ch <= 'z') || ch == '!' || ch == '#' || ch == '$' || + ch == '%' || ch == '&' || ch == '\'' || ch == '*' || ch == '+' || + ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '<' || + ch == '=' || ch == '>' || ch == '?' || ch == '@' || ch == '^' || + ch == '_' || ch == '`' || ch == '|' || ch == '~'; } static char formatNibble(int nibble) { return nibble < 10 ? '0' + nibble : 'a' - 10 + nibble; } -static void escapeName(Name &name) { +static void escapeName(Name& name) { bool allIdChars = true; - for (const char *p = name.str; allIdChars && *p; p++) { + for (const char* p = name.str; allIdChars && *p; p++) { allIdChars = isIdChar(*p); } if (allIdChars) { @@ -1609,7 +1853,7 @@ static void escapeName(Name &name) { } // encode name, if at least one non-idchar (per WebAssembly spec) was found std::string escaped; - for (const char *p = name.str; *p; p++) { + for (const char* p = name.str; *p; p++) { char ch = *p; if (isIdChar(ch)) { escaped.push_back(ch); @@ -1624,7 +1868,8 @@ static void escapeName(Name &name) { } void WasmBinaryBuilder::readNames(size_t payloadLen) { - if (debug) std::cerr << "== readNames" << std::endl; + if (debug) + std::cerr << "== readNames" << std::endl; auto sectionPos = pos; while (pos < sectionPos + payloadLen) { auto nameType = getU32LEB(); @@ -1713,79 +1958,146 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (pos == endOfFunction) { throwError("Reached function end without seeing End opcode"); } - if (debug) std::cerr << "zz recurse into " << ++depth << " at " << pos << std::endl; + if (debug) + std::cerr << "zz recurse into " << ++depth << " at " << pos << std::endl; readNextDebugLocation(); std::set<Function::DebugLocation> currDebugLocation; if (debugLocation.size()) { currDebugLocation.insert(*debugLocation.begin()); } uint8_t code = getInt8(); - if (debug) std::cerr << "readExpression seeing " << (int)code << std::endl; + if (debug) + std::cerr << "readExpression seeing " << (int)code << std::endl; switch (code) { - case BinaryConsts::Block: visitBlock((curr = allocator.alloc<Block>())->cast<Block>()); break; - case BinaryConsts::If: visitIf((curr = allocator.alloc<If>())->cast<If>()); break; - case BinaryConsts::Loop: visitLoop((curr = allocator.alloc<Loop>())->cast<Loop>()); break; + case BinaryConsts::Block: + visitBlock((curr = allocator.alloc<Block>())->cast<Block>()); + break; + case BinaryConsts::If: + visitIf((curr = allocator.alloc<If>())->cast<If>()); + break; + case BinaryConsts::Loop: + visitLoop((curr = allocator.alloc<Loop>())->cast<Loop>()); + break; case BinaryConsts::Br: - case BinaryConsts::BrIf: visitBreak((curr = allocator.alloc<Break>())->cast<Break>(), code); break; // code distinguishes br from br_if - case BinaryConsts::TableSwitch: visitSwitch((curr = allocator.alloc<Switch>())->cast<Switch>()); break; - case BinaryConsts::CallFunction: visitCall((curr = allocator.alloc<Call>())->cast<Call>()); break; - case BinaryConsts::CallIndirect: visitCallIndirect((curr = allocator.alloc<CallIndirect>())->cast<CallIndirect>()); break; - case BinaryConsts::GetLocal: visitGetLocal((curr = allocator.alloc<GetLocal>())->cast<GetLocal>()); break; + case BinaryConsts::BrIf: + visitBreak((curr = allocator.alloc<Break>())->cast<Break>(), code); + break; // code distinguishes br from br_if + case BinaryConsts::TableSwitch: + visitSwitch((curr = allocator.alloc<Switch>())->cast<Switch>()); + break; + case BinaryConsts::CallFunction: + visitCall((curr = allocator.alloc<Call>())->cast<Call>()); + break; + case BinaryConsts::CallIndirect: + visitCallIndirect( + (curr = allocator.alloc<CallIndirect>())->cast<CallIndirect>()); + break; + case BinaryConsts::GetLocal: + visitGetLocal((curr = allocator.alloc<GetLocal>())->cast<GetLocal>()); + break; case BinaryConsts::TeeLocal: - case BinaryConsts::SetLocal: visitSetLocal((curr = allocator.alloc<SetLocal>())->cast<SetLocal>(), code); break; - case BinaryConsts::GetGlobal: visitGetGlobal((curr = allocator.alloc<GetGlobal>())->cast<GetGlobal>()); break; - case BinaryConsts::SetGlobal: visitSetGlobal((curr = allocator.alloc<SetGlobal>())->cast<SetGlobal>()); break; - case BinaryConsts::Select: visitSelect((curr = allocator.alloc<Select>())->cast<Select>()); break; - case BinaryConsts::Return: visitReturn((curr = allocator.alloc<Return>())->cast<Return>()); break; - case BinaryConsts::Nop: visitNop((curr = allocator.alloc<Nop>())->cast<Nop>()); break; - case BinaryConsts::Unreachable: visitUnreachable((curr = allocator.alloc<Unreachable>())->cast<Unreachable>()); break; - case BinaryConsts::Drop: visitDrop((curr = allocator.alloc<Drop>())->cast<Drop>()); break; + case BinaryConsts::SetLocal: + visitSetLocal((curr = allocator.alloc<SetLocal>())->cast<SetLocal>(), + code); + break; + case BinaryConsts::GetGlobal: + visitGetGlobal((curr = allocator.alloc<GetGlobal>())->cast<GetGlobal>()); + break; + case BinaryConsts::SetGlobal: + visitSetGlobal((curr = allocator.alloc<SetGlobal>())->cast<SetGlobal>()); + break; + case BinaryConsts::Select: + visitSelect((curr = allocator.alloc<Select>())->cast<Select>()); + break; + case BinaryConsts::Return: + visitReturn((curr = allocator.alloc<Return>())->cast<Return>()); + break; + case BinaryConsts::Nop: + visitNop((curr = allocator.alloc<Nop>())->cast<Nop>()); + break; + case BinaryConsts::Unreachable: + visitUnreachable( + (curr = allocator.alloc<Unreachable>())->cast<Unreachable>()); + break; + case BinaryConsts::Drop: + visitDrop((curr = allocator.alloc<Drop>())->cast<Drop>()); + break; case BinaryConsts::End: - case BinaryConsts::Else: curr = nullptr; break; + case BinaryConsts::Else: + curr = nullptr; + break; case BinaryConsts::AtomicPrefix: { code = static_cast<uint8_t>(getU32LEB()); - if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) break; - if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; - if (maybeVisitAtomicRMW(curr, code)) break; - if (maybeVisitAtomicCmpxchg(curr, code)) break; - if (maybeVisitAtomicWait(curr, code)) break; - if (maybeVisitAtomicNotify(curr, code)) break; + if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) + break; + if (maybeVisitStore(curr, code, /*isAtomic=*/true)) + break; + if (maybeVisitAtomicRMW(curr, code)) + break; + if (maybeVisitAtomicCmpxchg(curr, code)) + break; + if (maybeVisitAtomicWait(curr, code)) + break; + if (maybeVisitAtomicNotify(curr, code)) + break; throwError("invalid code after atomic prefix: " + std::to_string(code)); break; } case BinaryConsts::MiscPrefix: { auto opcode = getU32LEB(); - if (maybeVisitTruncSat(curr, opcode)) break; - if (maybeVisitMemoryInit(curr, opcode)) break; - if (maybeVisitDataDrop(curr, opcode)) break; - if (maybeVisitMemoryCopy(curr, opcode)) break; - if (maybeVisitMemoryFill(curr, opcode)) break; - throwError("invalid code after nontrapping float-to-int prefix: " + std::to_string(opcode)); + if (maybeVisitTruncSat(curr, opcode)) + break; + if (maybeVisitMemoryInit(curr, opcode)) + break; + if (maybeVisitDataDrop(curr, opcode)) + break; + if (maybeVisitMemoryCopy(curr, opcode)) + break; + if (maybeVisitMemoryFill(curr, opcode)) + break; + throwError("invalid code after nontrapping float-to-int prefix: " + + std::to_string(opcode)); break; } case BinaryConsts::SIMDPrefix: { auto opcode = getU32LEB(); - if (maybeVisitSIMDBinary(curr, opcode)) break; - if (maybeVisitSIMDUnary(curr, opcode)) break; - if (maybeVisitSIMDConst(curr, opcode)) break; - if (maybeVisitSIMDLoad(curr, opcode)) break; - if (maybeVisitSIMDStore(curr, opcode)) break; - if (maybeVisitSIMDExtract(curr, opcode)) break; - if (maybeVisitSIMDReplace(curr, opcode)) break; - if (maybeVisitSIMDShuffle(curr, opcode)) break; - if (maybeVisitSIMDBitselect(curr, opcode)) break; - if (maybeVisitSIMDShift(curr, opcode)) break; + if (maybeVisitSIMDBinary(curr, opcode)) + break; + if (maybeVisitSIMDUnary(curr, opcode)) + break; + if (maybeVisitSIMDConst(curr, opcode)) + break; + if (maybeVisitSIMDLoad(curr, opcode)) + break; + if (maybeVisitSIMDStore(curr, opcode)) + break; + if (maybeVisitSIMDExtract(curr, opcode)) + break; + if (maybeVisitSIMDReplace(curr, opcode)) + break; + if (maybeVisitSIMDShuffle(curr, opcode)) + break; + if (maybeVisitSIMDBitselect(curr, opcode)) + break; + if (maybeVisitSIMDShift(curr, opcode)) + break; throwError("invalid code after SIMD prefix: " + std::to_string(opcode)); break; } default: { // otherwise, the code is a subcode TODO: optimize - if (maybeVisitBinary(curr, code)) break; - if (maybeVisitUnary(curr, code)) break; - if (maybeVisitConst(curr, code)) break; - if (maybeVisitLoad(curr, code, /*isAtomic=*/false)) break; - if (maybeVisitStore(curr, code, /*isAtomic=*/false)) break; - if (maybeVisitHost(curr, code)) break; + if (maybeVisitBinary(curr, code)) + break; + if (maybeVisitUnary(curr, code)) + break; + if (maybeVisitConst(curr, code)) + break; + if (maybeVisitLoad(curr, code, /*isAtomic=*/false)) + break; + if (maybeVisitStore(curr, code, /*isAtomic=*/false)) + break; + if (maybeVisitHost(curr, code)) + break; throwError("bad node code " + std::to_string(code)); break; } @@ -1793,26 +2105,31 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (curr && currDebugLocation.size()) { currFunction->debugLocations[curr] = *currDebugLocation.begin(); } - if (debug) std::cerr << "zz recurse from " << depth-- << " at " << pos << std::endl; + if (debug) + std::cerr << "zz recurse from " << depth-- << " at " << pos << std::endl; return BinaryConsts::ASTNodes(code); } -void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) { +void WasmBinaryBuilder::pushBlockElements(Block* curr, + size_t start, + size_t end) { assert(start <= expressionStack.size()); assert(start <= end); assert(end <= expressionStack.size()); - // the first dropped element may be consumed by code later - it was on the stack first, - // and is the only thing left on the stack. there must be just one thing on the stack - // since we are at the end of a block context. note that we may need to drop more than - // one thing, since a bunch of concrete values may be all "consumed" by an unreachable - // (in which case, the first value can't be consumed anyhow, so it doesn't matter) + // the first dropped element may be consumed by code later - it was on the + // stack first, and is the only thing left on the stack. there must be just + // one thing on the stack since we are at the end of a block context. note + // that we may need to drop more than one thing, since a bunch of concrete + // values may be all "consumed" by an unreachable (in which case, the first + // value can't be consumed anyhow, so it doesn't matter) const Index NONE = -1; Index consumable = NONE; for (size_t i = start; i < end; i++) { auto* item = expressionStack[i]; curr->list.push_back(item); if (i < end - 1) { - // stacky&unreachable code may introduce elements that need to be dropped in non-final positions + // stacky&unreachable code may introduce elements that need to be dropped + // in non-final positions if (isConcreteType(item->type)) { curr->list.back() = Builder(wasm).makeDrop(item); if (consumable == NONE) { @@ -1825,7 +2142,8 @@ void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) expressionStack.resize(start); // if we have a consumable item and need it, use it if (consumable != NONE && curr->list.back()->type == none) { - requireFunctionContext("need an extra var in a non-function context, invalid wasm"); + requireFunctionContext( + "need an extra var in a non-function context, invalid wasm"); Builder builder(wasm); auto* item = curr->list[consumable]->cast<Drop>()->value; auto temp = builder.addVar(currFunction, item->type); @@ -1835,9 +2153,10 @@ void WasmBinaryBuilder::pushBlockElements(Block* curr, size_t start, size_t end) } void WasmBinaryBuilder::visitBlock(Block* curr) { - if (debug) std::cerr << "zz node: Block" << std::endl; - // special-case Block and de-recurse nested blocks in their first position, as that is - // a common pattern that can be very highly nested. + if (debug) + std::cerr << "zz node: Block" << std::endl; + // special-case Block and de-recurse nested blocks in their first position, as + // that is a common pattern that can be very highly nested. std::vector<Block*> stack; while (1) { curr->type = getType(); @@ -1862,7 +2181,8 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { while (stack.size() > 0) { curr = stack.back(); stack.pop_back(); - size_t start = expressionStack.size(); // everything after this, that is left when we see the marker, is ours + // everything after this, that is left when we see the marker, is ours + size_t start = expressionStack.size(); if (last) { // the previous block is our first-position element expressionStack.push_back(last); @@ -1874,7 +2194,9 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { throwError("block cannot pop from outside"); } pushBlockElements(curr, start, end); - curr->finalize(curr->type, breakTargetNames.find(curr->name) != breakTargetNames.end() /* hasBreak */); + curr->finalize(curr->type, + breakTargetNames.find(curr->name) != + breakTargetNames.end() /* hasBreak */); breakStack.pop_back(); breakTargetNames.erase(curr->name); } @@ -1906,7 +2228,8 @@ Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type) { } void WasmBinaryBuilder::visitIf(If* curr) { - if (debug) std::cerr << "zz node: If" << std::endl; + if (debug) + std::cerr << "zz node: If" << std::endl; curr->type = getType(); curr->condition = popNonVoidExpression(); curr->ifTrue = getBlockOrSingleton(curr->type); @@ -1920,7 +2243,8 @@ void WasmBinaryBuilder::visitIf(If* curr) { } void WasmBinaryBuilder::visitLoop(Loop* curr) { - if (debug) std::cerr << "zz node: Loop" << std::endl; + if (debug) + std::cerr << "zz node: Loop" << std::endl; curr->type = getType(); curr->name = getNextLabel(); breakStack.push_back({curr->name, 0}); @@ -1948,8 +2272,10 @@ void WasmBinaryBuilder::visitLoop(Loop* curr) { curr->finalize(curr->type); } -WasmBinaryBuilder::BreakTarget WasmBinaryBuilder::getBreakTarget(int32_t offset) { - if (debug) std::cerr << "getBreakTarget " << offset << std::endl; +WasmBinaryBuilder::BreakTarget +WasmBinaryBuilder::getBreakTarget(int32_t offset) { + if (debug) + std::cerr << "getBreakTarget " << offset << std::endl; if (breakStack.size() < 1 + size_t(offset)) { throwError("bad breakindex (low)"); } @@ -1957,42 +2283,52 @@ WasmBinaryBuilder::BreakTarget WasmBinaryBuilder::getBreakTarget(int32_t offset) if (index >= breakStack.size()) { throwError("bad breakindex (high)"); } - if (debug) std::cerr << "breaktarget "<< breakStack[index].name << " arity " << breakStack[index].arity << std::endl; + if (debug) + std::cerr << "breaktarget " << breakStack[index].name << " arity " + << breakStack[index].arity << std::endl; auto& ret = breakStack[index]; - // if the break is in literally unreachable code, then we will not emit it anyhow, - // so do not note that the target has breaks to it + // if the break is in literally unreachable code, then we will not emit it + // anyhow, so do not note that the target has breaks to it if (!willBeIgnored) { breakTargetNames.insert(ret.name); } return ret; } -void WasmBinaryBuilder::visitBreak(Break *curr, uint8_t code) { - if (debug) std::cerr << "zz node: Break, code "<< int32_t(code) << std::endl; +void WasmBinaryBuilder::visitBreak(Break* curr, uint8_t code) { + if (debug) + std::cerr << "zz node: Break, code " << int32_t(code) << std::endl; BreakTarget target = getBreakTarget(getU32LEB()); curr->name = target.name; - if (code == BinaryConsts::BrIf) curr->condition = popNonVoidExpression(); - if (target.arity) curr->value = popNonVoidExpression(); + if (code == BinaryConsts::BrIf) + curr->condition = popNonVoidExpression(); + if (target.arity) + curr->value = popNonVoidExpression(); curr->finalize(); } void WasmBinaryBuilder::visitSwitch(Switch* curr) { - if (debug) std::cerr << "zz node: Switch" << std::endl; + if (debug) + std::cerr << "zz node: Switch" << std::endl; curr->condition = popNonVoidExpression(); auto numTargets = getU32LEB(); - if (debug) std::cerr << "targets: "<< numTargets<<std::endl; + if (debug) + std::cerr << "targets: " << numTargets << std::endl; for (size_t i = 0; i < numTargets; i++) { curr->targets.push_back(getBreakTarget(getU32LEB()).name); } auto defaultTarget = getBreakTarget(getU32LEB()); curr->default_ = defaultTarget.name; - if (debug) std::cerr << "default: "<< curr->default_<<std::endl; - if (defaultTarget.arity) curr->value = popNonVoidExpression(); + if (debug) + std::cerr << "default: " << curr->default_ << std::endl; + if (defaultTarget.arity) + curr->value = popNonVoidExpression(); curr->finalize(); } void WasmBinaryBuilder::visitCall(Call* curr) { - if (debug) std::cerr << "zz node: Call" << std::endl; + if (debug) + std::cerr << "zz node: Call" << std::endl; auto index = getU32LEB(); FunctionType* type; if (index < functionImports.size()) { @@ -2017,14 +2353,16 @@ void WasmBinaryBuilder::visitCall(Call* curr) { } void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) { - if (debug) std::cerr << "zz node: CallIndirect" << std::endl; + if (debug) + std::cerr << "zz node: CallIndirect" << std::endl; auto index = getU32LEB(); if (index >= wasm.functionTypes.size()) { throwError("bad call_indirect function index"); } auto* fullType = wasm.functionTypes[index].get(); auto reserved = getU32LEB(); - if (reserved != 0) throwError("Invalid flags field in call_indirect"); + if (reserved != 0) + throwError("Invalid flags field in call_indirect"); curr->fullType = fullType->name; auto num = fullType->params.size(); curr->operands.resize(num); @@ -2037,7 +2375,8 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) { } void WasmBinaryBuilder::visitGetLocal(GetLocal* curr) { - if (debug) std::cerr << "zz node: GetLocal " << pos << std::endl; + if (debug) + std::cerr << "zz node: GetLocal " << pos << std::endl; requireFunctionContext("local.get"); curr->index = getU32LEB(); if (curr->index >= currFunction->getNumLocals()) { @@ -2047,8 +2386,9 @@ void WasmBinaryBuilder::visitGetLocal(GetLocal* curr) { curr->finalize(); } -void WasmBinaryBuilder::visitSetLocal(SetLocal *curr, uint8_t code) { - if (debug) std::cerr << "zz node: Set|TeeLocal" << std::endl; +void WasmBinaryBuilder::visitSetLocal(SetLocal* curr, uint8_t code) { + if (debug) + std::cerr << "zz node: Set|TeeLocal" << std::endl; requireFunctionContext("local.set outside of function"); curr->index = getU32LEB(); if (curr->index >= currFunction->getNumLocals()) { @@ -2061,14 +2401,16 @@ void WasmBinaryBuilder::visitSetLocal(SetLocal *curr, uint8_t code) { } void WasmBinaryBuilder::visitGetGlobal(GetGlobal* curr) { - if (debug) std::cerr << "zz node: GetGlobal " << pos << std::endl; + if (debug) + std::cerr << "zz node: GetGlobal " << pos << std::endl; auto index = getU32LEB(); curr->name = getGlobalName(index); curr->type = wasm.getGlobal(curr->name)->type; } void WasmBinaryBuilder::visitSetGlobal(SetGlobal* curr) { - if (debug) std::cerr << "zz node: SetGlobal" << std::endl; + if (debug) + std::cerr << "zz node: SetGlobal" << std::endl; auto index = getU32LEB(); curr->name = getGlobalName(index); curr->value = popNonVoidExpression(); @@ -2077,45 +2419,146 @@ void WasmBinaryBuilder::visitSetGlobal(SetGlobal* curr) { void WasmBinaryBuilder::readMemoryAccess(Address& alignment, Address& offset) { auto rawAlignment = getU32LEB(); - if (rawAlignment > 4) throwError("Alignment must be of a reasonable size"); + if (rawAlignment > 4) + throwError("Alignment must be of a reasonable size"); alignment = Pow2(rawAlignment); offset = getU32LEB(); } -bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code, bool isAtomic) { +bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, + uint8_t code, + bool isAtomic) { Load* curr; if (!isAtomic) { switch (code) { - case BinaryConsts::I32LoadMem8S: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i32; curr->signed_ = true; break; - case BinaryConsts::I32LoadMem8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i32; curr->signed_ = false; break; - case BinaryConsts::I32LoadMem16S: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i32; curr->signed_ = true; break; - case BinaryConsts::I32LoadMem16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i32; curr->signed_ = false; break; - case BinaryConsts::I32LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i32; break; - case BinaryConsts::I64LoadMem8S: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem16S: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem32S: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i64; curr->signed_ = true; break; - case BinaryConsts::I64LoadMem32U: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i64; curr->signed_ = false; break; - case BinaryConsts::I64LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 8; curr->type = i64; break; - case BinaryConsts::F32LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = f32; break; - case BinaryConsts::F64LoadMem: curr = allocator.alloc<Load>(); curr->bytes = 8; curr->type = f64; break; - default: return false; - } - if (debug) std::cerr << "zz node: Load" << std::endl; + case BinaryConsts::I32LoadMem8S: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i32; + curr->signed_ = true; + break; + case BinaryConsts::I32LoadMem8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i32; + curr->signed_ = false; + break; + case BinaryConsts::I32LoadMem16S: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i32; + curr->signed_ = true; + break; + case BinaryConsts::I32LoadMem16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i32; + curr->signed_ = false; + break; + case BinaryConsts::I32LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i32; + break; + case BinaryConsts::I64LoadMem8S: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i64; + curr->signed_ = true; + break; + case BinaryConsts::I64LoadMem8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i64; + curr->signed_ = false; + break; + case BinaryConsts::I64LoadMem16S: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i64; + curr->signed_ = true; + break; + case BinaryConsts::I64LoadMem16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i64; + curr->signed_ = false; + break; + case BinaryConsts::I64LoadMem32S: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i64; + curr->signed_ = true; + break; + case BinaryConsts::I64LoadMem32U: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i64; + curr->signed_ = false; + break; + case BinaryConsts::I64LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 8; + curr->type = i64; + break; + case BinaryConsts::F32LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = f32; + break; + case BinaryConsts::F64LoadMem: + curr = allocator.alloc<Load>(); + curr->bytes = 8; + curr->type = f64; + break; + default: + return false; + } + if (debug) + std::cerr << "zz node: Load" << std::endl; } else { switch (code) { - case BinaryConsts::I32AtomicLoad8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i32; break; - case BinaryConsts::I32AtomicLoad16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i32; break; - case BinaryConsts::I32AtomicLoad: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i32; break; - case BinaryConsts::I64AtomicLoad8U: curr = allocator.alloc<Load>(); curr->bytes = 1; curr->type = i64; break; - case BinaryConsts::I64AtomicLoad16U: curr = allocator.alloc<Load>(); curr->bytes = 2; curr->type = i64; break; - case BinaryConsts::I64AtomicLoad32U: curr = allocator.alloc<Load>(); curr->bytes = 4; curr->type = i64; break; - case BinaryConsts::I64AtomicLoad: curr = allocator.alloc<Load>(); curr->bytes = 8; curr->type = i64; break; - default: return false; + case BinaryConsts::I32AtomicLoad8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i32; + break; + case BinaryConsts::I32AtomicLoad16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i32; + break; + case BinaryConsts::I32AtomicLoad: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i32; + break; + case BinaryConsts::I64AtomicLoad8U: + curr = allocator.alloc<Load>(); + curr->bytes = 1; + curr->type = i64; + break; + case BinaryConsts::I64AtomicLoad16U: + curr = allocator.alloc<Load>(); + curr->bytes = 2; + curr->type = i64; + break; + case BinaryConsts::I64AtomicLoad32U: + curr = allocator.alloc<Load>(); + curr->bytes = 4; + curr->type = i64; + break; + case BinaryConsts::I64AtomicLoad: + curr = allocator.alloc<Load>(); + curr->bytes = 8; + curr->type = i64; + break; + default: + return false; } curr->signed_ = false; - if (debug) std::cerr << "zz node: AtomicLoad" << std::endl; + if (debug) + std::cerr << "zz node: AtomicLoad" << std::endl; } curr->isAtomic = isAtomic; @@ -2126,36 +2569,105 @@ bool WasmBinaryBuilder::maybeVisitLoad(Expression*& out, uint8_t code, bool isAt return true; } -bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic) { +bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, + uint8_t code, + bool isAtomic) { Store* curr; if (!isAtomic) { switch (code) { - case BinaryConsts::I32StoreMem8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i32; break; - case BinaryConsts::I32StoreMem16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i32; break; - case BinaryConsts::I32StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i32; break; - case BinaryConsts::I64StoreMem8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem32: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i64; break; - case BinaryConsts::I64StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 8; curr->valueType = i64; break; - case BinaryConsts::F32StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = f32; break; - case BinaryConsts::F64StoreMem: curr = allocator.alloc<Store>(); curr->bytes = 8; curr->valueType = f64; break; - default: return false; + case BinaryConsts::I32StoreMem8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i32; + break; + case BinaryConsts::I32StoreMem16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i32; + break; + case BinaryConsts::I32StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i32; + break; + case BinaryConsts::I64StoreMem8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i64; + break; + case BinaryConsts::I64StoreMem16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i64; + break; + case BinaryConsts::I64StoreMem32: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i64; + break; + case BinaryConsts::I64StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 8; + curr->valueType = i64; + break; + case BinaryConsts::F32StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = f32; + break; + case BinaryConsts::F64StoreMem: + curr = allocator.alloc<Store>(); + curr->bytes = 8; + curr->valueType = f64; + break; + default: + return false; } } else { switch (code) { - case BinaryConsts::I32AtomicStore8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i32; break; - case BinaryConsts::I32AtomicStore16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i32; break; - case BinaryConsts::I32AtomicStore: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i32; break; - case BinaryConsts::I64AtomicStore8: curr = allocator.alloc<Store>(); curr->bytes = 1; curr->valueType = i64; break; - case BinaryConsts::I64AtomicStore16: curr = allocator.alloc<Store>(); curr->bytes = 2; curr->valueType = i64; break; - case BinaryConsts::I64AtomicStore32: curr = allocator.alloc<Store>(); curr->bytes = 4; curr->valueType = i64; break; - case BinaryConsts::I64AtomicStore: curr = allocator.alloc<Store>(); curr->bytes = 8; curr->valueType = i64; break; - default: return false; + case BinaryConsts::I32AtomicStore8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i32; + break; + case BinaryConsts::I32AtomicStore16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i32; + break; + case BinaryConsts::I32AtomicStore: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i32; + break; + case BinaryConsts::I64AtomicStore8: + curr = allocator.alloc<Store>(); + curr->bytes = 1; + curr->valueType = i64; + break; + case BinaryConsts::I64AtomicStore16: + curr = allocator.alloc<Store>(); + curr->bytes = 2; + curr->valueType = i64; + break; + case BinaryConsts::I64AtomicStore32: + curr = allocator.alloc<Store>(); + curr->bytes = 4; + curr->valueType = i64; + break; + case BinaryConsts::I64AtomicStore: + curr = allocator.alloc<Store>(); + curr->bytes = 8; + curr->valueType = i64; + break; + default: + return false; } } curr->isAtomic = isAtomic; - if (debug) std::cerr << "zz node: Store" << std::endl; + if (debug) + std::cerr << "zz node: Store" << std::endl; readMemoryAccess(curr->align, curr->offset); curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); @@ -2165,41 +2677,60 @@ bool WasmBinaryBuilder::maybeVisitStore(Expression*& out, uint8_t code, bool isA } bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { - if (code < BinaryConsts::AtomicRMWOps_Begin || code > BinaryConsts::AtomicRMWOps_End) return false; + if (code < BinaryConsts::AtomicRMWOps_Begin || + code > BinaryConsts::AtomicRMWOps_End) + return false; auto* curr = allocator.alloc<AtomicRMW>(); // Set curr to the given opcode, type and size. -#define SET(opcode, optype, size) \ - curr->op = opcode; \ - curr->type = optype; \ +#define SET(opcode, optype, size) \ + curr->op = opcode; \ + curr->type = optype; \ curr->bytes = size // Handle the cases for all the valid types for a particular opcode -#define SET_FOR_OP(Op) \ - case BinaryConsts::I32AtomicRMW##Op: SET(Op, i32, 4); break; \ - case BinaryConsts::I32AtomicRMW##Op##8U: SET(Op, i32, 1); break; \ - case BinaryConsts::I32AtomicRMW##Op##16U: SET(Op, i32, 2); break; \ - case BinaryConsts::I64AtomicRMW##Op: SET(Op, i64, 8); break; \ - case BinaryConsts::I64AtomicRMW##Op##8U: SET(Op, i64, 1); break; \ - case BinaryConsts::I64AtomicRMW##Op##16U: SET(Op, i64, 2); break; \ - case BinaryConsts::I64AtomicRMW##Op##32U: SET(Op, i64, 4); break; - - switch(code) { +#define SET_FOR_OP(Op) \ + case BinaryConsts::I32AtomicRMW##Op: \ + SET(Op, i32, 4); \ + break; \ + case BinaryConsts::I32AtomicRMW##Op##8U: \ + SET(Op, i32, 1); \ + break; \ + case BinaryConsts::I32AtomicRMW##Op##16U: \ + SET(Op, i32, 2); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op: \ + SET(Op, i64, 8); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op##8U: \ + SET(Op, i64, 1); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op##16U: \ + SET(Op, i64, 2); \ + break; \ + case BinaryConsts::I64AtomicRMW##Op##32U: \ + SET(Op, i64, 4); \ + break; + + switch (code) { SET_FOR_OP(Add); SET_FOR_OP(Sub); SET_FOR_OP(And); SET_FOR_OP(Or); SET_FOR_OP(Xor); SET_FOR_OP(Xchg); - default: WASM_UNREACHABLE(); + default: + WASM_UNREACHABLE(); } #undef SET_FOR_OP #undef SET - if (debug) std::cerr << "zz node: AtomicRMW" << std::endl; + if (debug) + std::cerr << "zz node: AtomicRMW" << std::endl; Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != curr->bytes) throwError("Align of AtomicRMW must match size"); + if (readAlign != curr->bytes) + throwError("Align of AtomicRMW must match size"); curr->value = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); curr->finalize(); @@ -2207,30 +2738,50 @@ bool WasmBinaryBuilder::maybeVisitAtomicRMW(Expression*& out, uint8_t code) { return true; } -bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code) { - if (code < BinaryConsts::AtomicCmpxchgOps_Begin || code > BinaryConsts::AtomicCmpxchgOps_End) return false; +bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, + uint8_t code) { + if (code < BinaryConsts::AtomicCmpxchgOps_Begin || + code > BinaryConsts::AtomicCmpxchgOps_End) + return false; auto* curr = allocator.alloc<AtomicCmpxchg>(); // Set curr to the given type and size. -#define SET(optype, size) \ - curr->type = optype; \ +#define SET(optype, size) \ + curr->type = optype; \ curr->bytes = size switch (code) { - case BinaryConsts::I32AtomicCmpxchg: SET(i32, 4); break; - case BinaryConsts::I64AtomicCmpxchg: SET(i64, 8); break; - case BinaryConsts::I32AtomicCmpxchg8U: SET(i32, 1); break; - case BinaryConsts::I32AtomicCmpxchg16U: SET(i32, 2); break; - case BinaryConsts::I64AtomicCmpxchg8U: SET(i64, 1); break; - case BinaryConsts::I64AtomicCmpxchg16U: SET(i64, 2); break; - case BinaryConsts::I64AtomicCmpxchg32U: SET(i64, 4); break; - default: WASM_UNREACHABLE(); + case BinaryConsts::I32AtomicCmpxchg: + SET(i32, 4); + break; + case BinaryConsts::I64AtomicCmpxchg: + SET(i64, 8); + break; + case BinaryConsts::I32AtomicCmpxchg8U: + SET(i32, 1); + break; + case BinaryConsts::I32AtomicCmpxchg16U: + SET(i32, 2); + break; + case BinaryConsts::I64AtomicCmpxchg8U: + SET(i64, 1); + break; + case BinaryConsts::I64AtomicCmpxchg16U: + SET(i64, 2); + break; + case BinaryConsts::I64AtomicCmpxchg32U: + SET(i64, 4); + break; + default: + WASM_UNREACHABLE(); } - if (debug) std::cerr << "zz node: AtomicCmpxchg" << std::endl; + if (debug) + std::cerr << "zz node: AtomicCmpxchg" << std::endl; Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != curr->bytes) throwError("Align of AtomicCpxchg must match size"); + if (readAlign != curr->bytes) + throwError("Align of AtomicCpxchg must match size"); curr->replacement = popNonVoidExpression(); curr->expected = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); @@ -2240,38 +2791,49 @@ bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code) } bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) { - if (code < BinaryConsts::I32AtomicWait || code > BinaryConsts::I64AtomicWait) return false; + if (code < BinaryConsts::I32AtomicWait || code > BinaryConsts::I64AtomicWait) + return false; auto* curr = allocator.alloc<AtomicWait>(); switch (code) { - case BinaryConsts::I32AtomicWait: curr->expectedType = i32; break; - case BinaryConsts::I64AtomicWait: curr->expectedType = i64; break; - default: WASM_UNREACHABLE(); + case BinaryConsts::I32AtomicWait: + curr->expectedType = i32; + break; + case BinaryConsts::I64AtomicWait: + curr->expectedType = i64; + break; + default: + WASM_UNREACHABLE(); } curr->type = i32; - if (debug) std::cerr << "zz node: AtomicWait" << std::endl; + if (debug) + std::cerr << "zz node: AtomicWait" << std::endl; curr->timeout = popNonVoidExpression(); curr->expected = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != getTypeSize(curr->expectedType)) throwError("Align of AtomicWait must match size"); + if (readAlign != getTypeSize(curr->expectedType)) + throwError("Align of AtomicWait must match size"); curr->finalize(); out = curr; return true; } bool WasmBinaryBuilder::maybeVisitAtomicNotify(Expression*& out, uint8_t code) { - if (code != BinaryConsts::AtomicNotify) return false; + if (code != BinaryConsts::AtomicNotify) + return false; auto* curr = allocator.alloc<AtomicNotify>(); - if (debug) std::cerr << "zz node: AtomicNotify" << std::endl; + if (debug) + std::cerr << "zz node: AtomicNotify" << std::endl; curr->type = i32; curr->notifyCount = popNonVoidExpression(); curr->ptr = popNonVoidExpression(); Address readAlign; readMemoryAccess(readAlign, curr->offset); - if (readAlign != getTypeSize(curr->type)) throwError("Align of AtomicNotify must match size"); + if (readAlign != getTypeSize(curr->type)) + throwError("Align of AtomicNotify must match size"); curr->finalize(); out = curr; return true; @@ -2279,13 +2841,27 @@ bool WasmBinaryBuilder::maybeVisitAtomicNotify(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) { Const* curr; - if (debug) std::cerr << "zz node: Const, code " << code << std::endl; + if (debug) + std::cerr << "zz node: Const, code " << code << std::endl; switch (code) { - case BinaryConsts::I32Const: curr = allocator.alloc<Const>(); curr->value = Literal(getS32LEB()); break; - case BinaryConsts::I64Const: curr = allocator.alloc<Const>(); curr->value = Literal(getS64LEB()); break; - case BinaryConsts::F32Const: curr = allocator.alloc<Const>(); curr->value = getFloat32Literal(); break; - case BinaryConsts::F64Const: curr = allocator.alloc<Const>(); curr->value = getFloat64Literal(); break; - default: return false; + case BinaryConsts::I32Const: + curr = allocator.alloc<Const>(); + curr->value = Literal(getS32LEB()); + break; + case BinaryConsts::I64Const: + curr = allocator.alloc<Const>(); + curr->value = Literal(getS64LEB()); + break; + case BinaryConsts::F32Const: + curr = allocator.alloc<Const>(); + curr->value = getFloat32Literal(); + break; + case BinaryConsts::F64Const: + curr = allocator.alloc<Const>(); + curr->value = getFloat64Literal(); + break; + default: + return false; } curr->type = curr->value.type; out = curr; @@ -2296,67 +2872,225 @@ bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) { Unary* curr; switch (code) { - case BinaryConsts::I32Clz: curr = allocator.alloc<Unary>(); curr->op = ClzInt32; break; - case BinaryConsts::I64Clz: curr = allocator.alloc<Unary>(); curr->op = ClzInt64; break; - case BinaryConsts::I32Ctz: curr = allocator.alloc<Unary>(); curr->op = CtzInt32; break; - case BinaryConsts::I64Ctz: curr = allocator.alloc<Unary>(); curr->op = CtzInt64; break; - case BinaryConsts::I32Popcnt: curr = allocator.alloc<Unary>(); curr->op = PopcntInt32; break; - case BinaryConsts::I64Popcnt: curr = allocator.alloc<Unary>(); curr->op = PopcntInt64; break; - case BinaryConsts::I32EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZInt32; break; - case BinaryConsts::I64EqZ: curr = allocator.alloc<Unary>(); curr->op = EqZInt64; break; - case BinaryConsts::F32Neg: curr = allocator.alloc<Unary>(); curr->op = NegFloat32; break; - case BinaryConsts::F64Neg: curr = allocator.alloc<Unary>(); curr->op = NegFloat64; break; - case BinaryConsts::F32Abs: curr = allocator.alloc<Unary>(); curr->op = AbsFloat32; break; - case BinaryConsts::F64Abs: curr = allocator.alloc<Unary>(); curr->op = AbsFloat64; break; - case BinaryConsts::F32Ceil: curr = allocator.alloc<Unary>(); curr->op = CeilFloat32; break; - case BinaryConsts::F64Ceil: curr = allocator.alloc<Unary>(); curr->op = CeilFloat64; break; - case BinaryConsts::F32Floor: curr = allocator.alloc<Unary>(); curr->op = FloorFloat32; break; - case BinaryConsts::F64Floor: curr = allocator.alloc<Unary>(); curr->op = FloorFloat64; break; - case BinaryConsts::F32NearestInt: curr = allocator.alloc<Unary>(); curr->op = NearestFloat32; break; - case BinaryConsts::F64NearestInt: curr = allocator.alloc<Unary>(); curr->op = NearestFloat64; break; - case BinaryConsts::F32Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtFloat32; break; - case BinaryConsts::F64Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtFloat64; break; - case BinaryConsts::F32UConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt32ToFloat32; break; - case BinaryConsts::F64UConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt32ToFloat64; break; - case BinaryConsts::F32SConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt32ToFloat32; break; - case BinaryConsts::F64SConvertI32: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt32ToFloat64; break; - case BinaryConsts::F32UConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt64ToFloat32; break; - case BinaryConsts::F64UConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertUInt64ToFloat64; break; - case BinaryConsts::F32SConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt64ToFloat32; break; - case BinaryConsts::F64SConvertI64: curr = allocator.alloc<Unary>(); curr->op = ConvertSInt64ToFloat64; break; - - case BinaryConsts::I64STruncI32: curr = allocator.alloc<Unary>(); curr->op = ExtendSInt32; break; - case BinaryConsts::I64UTruncI32: curr = allocator.alloc<Unary>(); curr->op = ExtendUInt32; break; - case BinaryConsts::I32ConvertI64: curr = allocator.alloc<Unary>(); curr->op = WrapInt64; break; - - case BinaryConsts::I32UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt32; break; - case BinaryConsts::I32UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt32; break; - case BinaryConsts::I32STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt32; break; - case BinaryConsts::I32STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt32; break; - case BinaryConsts::I64UTruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat32ToInt64; break; - case BinaryConsts::I64UTruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncUFloat64ToInt64; break; - case BinaryConsts::I64STruncF32: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat32ToInt64; break; - case BinaryConsts::I64STruncF64: curr = allocator.alloc<Unary>(); curr->op = TruncSFloat64ToInt64; break; - - case BinaryConsts::F32Trunc: curr = allocator.alloc<Unary>(); curr->op = TruncFloat32; break; - case BinaryConsts::F64Trunc: curr = allocator.alloc<Unary>(); curr->op = TruncFloat64; break; - - case BinaryConsts::F32ConvertF64: curr = allocator.alloc<Unary>(); curr->op = DemoteFloat64; break; - case BinaryConsts::F64ConvertF32: curr = allocator.alloc<Unary>(); curr->op = PromoteFloat32; break; - case BinaryConsts::I32ReinterpretF32: curr = allocator.alloc<Unary>(); curr->op = ReinterpretFloat32; break; - case BinaryConsts::I64ReinterpretF64: curr = allocator.alloc<Unary>(); curr->op = ReinterpretFloat64; break; - case BinaryConsts::F32ReinterpretI32: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt32; break; - case BinaryConsts::F64ReinterpretI64: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt64; break; - - case BinaryConsts::I32ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int32; break; - case BinaryConsts::I32ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int32; break; - case BinaryConsts::I64ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int64; break; - case BinaryConsts::I64ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int64; break; - case BinaryConsts::I64ExtendS32: curr = allocator.alloc<Unary>(); curr->op = ExtendS32Int64; break; - - default: return false; - } - if (debug) std::cerr << "zz node: Unary" << std::endl; + case BinaryConsts::I32Clz: + curr = allocator.alloc<Unary>(); + curr->op = ClzInt32; + break; + case BinaryConsts::I64Clz: + curr = allocator.alloc<Unary>(); + curr->op = ClzInt64; + break; + case BinaryConsts::I32Ctz: + curr = allocator.alloc<Unary>(); + curr->op = CtzInt32; + break; + case BinaryConsts::I64Ctz: + curr = allocator.alloc<Unary>(); + curr->op = CtzInt64; + break; + case BinaryConsts::I32Popcnt: + curr = allocator.alloc<Unary>(); + curr->op = PopcntInt32; + break; + case BinaryConsts::I64Popcnt: + curr = allocator.alloc<Unary>(); + curr->op = PopcntInt64; + break; + case BinaryConsts::I32EqZ: + curr = allocator.alloc<Unary>(); + curr->op = EqZInt32; + break; + case BinaryConsts::I64EqZ: + curr = allocator.alloc<Unary>(); + curr->op = EqZInt64; + break; + case BinaryConsts::F32Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegFloat32; + break; + case BinaryConsts::F64Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegFloat64; + break; + case BinaryConsts::F32Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsFloat32; + break; + case BinaryConsts::F64Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsFloat64; + break; + case BinaryConsts::F32Ceil: + curr = allocator.alloc<Unary>(); + curr->op = CeilFloat32; + break; + case BinaryConsts::F64Ceil: + curr = allocator.alloc<Unary>(); + curr->op = CeilFloat64; + break; + case BinaryConsts::F32Floor: + curr = allocator.alloc<Unary>(); + curr->op = FloorFloat32; + break; + case BinaryConsts::F64Floor: + curr = allocator.alloc<Unary>(); + curr->op = FloorFloat64; + break; + case BinaryConsts::F32NearestInt: + curr = allocator.alloc<Unary>(); + curr->op = NearestFloat32; + break; + case BinaryConsts::F64NearestInt: + curr = allocator.alloc<Unary>(); + curr->op = NearestFloat64; + break; + case BinaryConsts::F32Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtFloat32; + break; + case BinaryConsts::F64Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtFloat64; + break; + case BinaryConsts::F32UConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt32ToFloat32; + break; + case BinaryConsts::F64UConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt32ToFloat64; + break; + case BinaryConsts::F32SConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt32ToFloat32; + break; + case BinaryConsts::F64SConvertI32: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt32ToFloat64; + break; + case BinaryConsts::F32UConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt64ToFloat32; + break; + case BinaryConsts::F64UConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUInt64ToFloat64; + break; + case BinaryConsts::F32SConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt64ToFloat32; + break; + case BinaryConsts::F64SConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSInt64ToFloat64; + break; + + case BinaryConsts::I64STruncI32: + curr = allocator.alloc<Unary>(); + curr->op = ExtendSInt32; + break; + case BinaryConsts::I64UTruncI32: + curr = allocator.alloc<Unary>(); + curr->op = ExtendUInt32; + break; + case BinaryConsts::I32ConvertI64: + curr = allocator.alloc<Unary>(); + curr->op = WrapInt64; + break; + + case BinaryConsts::I32UTruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat32ToInt32; + break; + case BinaryConsts::I32UTruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat64ToInt32; + break; + case BinaryConsts::I32STruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat32ToInt32; + break; + case BinaryConsts::I32STruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat64ToInt32; + break; + case BinaryConsts::I64UTruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat32ToInt64; + break; + case BinaryConsts::I64UTruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncUFloat64ToInt64; + break; + case BinaryConsts::I64STruncF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat32ToInt64; + break; + case BinaryConsts::I64STruncF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSFloat64ToInt64; + break; + + case BinaryConsts::F32Trunc: + curr = allocator.alloc<Unary>(); + curr->op = TruncFloat32; + break; + case BinaryConsts::F64Trunc: + curr = allocator.alloc<Unary>(); + curr->op = TruncFloat64; + break; + + case BinaryConsts::F32ConvertF64: + curr = allocator.alloc<Unary>(); + curr->op = DemoteFloat64; + break; + case BinaryConsts::F64ConvertF32: + curr = allocator.alloc<Unary>(); + curr->op = PromoteFloat32; + break; + case BinaryConsts::I32ReinterpretF32: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretFloat32; + break; + case BinaryConsts::I64ReinterpretF64: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretFloat64; + break; + case BinaryConsts::F32ReinterpretI32: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretInt32; + break; + case BinaryConsts::F64ReinterpretI64: + curr = allocator.alloc<Unary>(); + curr->op = ReinterpretInt64; + break; + + case BinaryConsts::I32ExtendS8: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS8Int32; + break; + case BinaryConsts::I32ExtendS16: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS16Int32; + break; + case BinaryConsts::I64ExtendS8: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS8Int64; + break; + case BinaryConsts::I64ExtendS16: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS16Int64; + break; + case BinaryConsts::I64ExtendS32: + curr = allocator.alloc<Unary>(); + curr->op = ExtendS32Int64; + break; + + default: + return false; + } + if (debug) + std::cerr << "zz node: Unary" << std::endl; curr->value = popNonVoidExpression(); curr->finalize(); out = curr; @@ -2366,17 +3100,43 @@ bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitTruncSat(Expression*& out, uint32_t code) { Unary* curr; switch (code) { - case BinaryConsts::I32STruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat32ToInt32; break; - case BinaryConsts::I32UTruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat32ToInt32; break; - case BinaryConsts::I32STruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat64ToInt32; break; - case BinaryConsts::I32UTruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat64ToInt32; break; - case BinaryConsts::I64STruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat32ToInt64; break; - case BinaryConsts::I64UTruncSatF32: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat32ToInt64; break; - case BinaryConsts::I64STruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatSFloat64ToInt64; break; - case BinaryConsts::I64UTruncSatF64: curr = allocator.alloc<Unary>(); curr->op = TruncSatUFloat64ToInt64; break; - default: return false; - } - if (debug) std::cerr << "zz node: Unary (nontrapping float-to-int)" << std::endl; + case BinaryConsts::I32STruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat32ToInt32; + break; + case BinaryConsts::I32UTruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat32ToInt32; + break; + case BinaryConsts::I32STruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat64ToInt32; + break; + case BinaryConsts::I32UTruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat64ToInt32; + break; + case BinaryConsts::I64STruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat32ToInt64; + break; + case BinaryConsts::I64UTruncSatF32: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat32ToInt64; + break; + case BinaryConsts::I64STruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSFloat64ToInt64; + break; + case BinaryConsts::I64UTruncSatF64: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUFloat64ToInt64; + break; + default: + return false; + } + if (debug) + std::cerr << "zz node: Unary (nontrapping float-to-int)" << std::endl; curr->value = popNonVoidExpression(); curr->finalize(); out = curr; @@ -2445,18 +3205,33 @@ bool WasmBinaryBuilder::maybeVisitMemoryFill(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { Binary* curr; -#define INT_TYPED_CODE(code) { \ - case BinaryConsts::I32##code: curr = allocator.alloc<Binary>(); curr->op = code##Int32; break; \ - case BinaryConsts::I64##code: curr = allocator.alloc<Binary>(); curr->op = code##Int64; break; \ - } -#define FLOAT_TYPED_CODE(code) { \ - case BinaryConsts::F32##code: curr = allocator.alloc<Binary>(); curr->op = code##Float32; break; \ - case BinaryConsts::F64##code: curr = allocator.alloc<Binary>(); curr->op = code##Float64; break; \ +#define INT_TYPED_CODE(code) \ + { \ + case BinaryConsts::I32##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Int32; \ + break; \ + case BinaryConsts::I64##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Int64; \ + break; \ + } +#define FLOAT_TYPED_CODE(code) \ + { \ + case BinaryConsts::F32##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Float32; \ + break; \ + case BinaryConsts::F64##code: \ + curr = allocator.alloc<Binary>(); \ + curr->op = code##Float64; \ + break; \ + } +#define TYPED_CODE(code) \ + { \ + INT_TYPED_CODE(code) \ + FLOAT_TYPED_CODE(code) \ } -#define TYPED_CODE(code) { \ - INT_TYPED_CODE(code) \ - FLOAT_TYPED_CODE(code) \ - } switch (code) { TYPED_CODE(Add); @@ -2492,9 +3267,11 @@ bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { FLOAT_TYPED_CODE(Le); FLOAT_TYPED_CODE(Gt); FLOAT_TYPED_CODE(Ge); - default: return false; + default: + return false; } - if (debug) std::cerr << "zz node: Binary" << std::endl; + if (debug) + std::cerr << "zz node: Binary" << std::endl; curr->right = popNonVoidExpression(); curr->left = popNonVoidExpression(); curr->finalize(); @@ -2508,85 +3285,315 @@ bool WasmBinaryBuilder::maybeVisitBinary(Expression*& out, uint8_t code) { bool WasmBinaryBuilder::maybeVisitSIMDBinary(Expression*& out, uint32_t code) { Binary* curr; switch (code) { - case BinaryConsts::I8x16Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI8x16; break; - case BinaryConsts::I8x16Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI8x16; break; - case BinaryConsts::I8x16LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI8x16; break; - case BinaryConsts::I8x16LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI8x16; break; - case BinaryConsts::I8x16GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI8x16; break; - case BinaryConsts::I8x16GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI8x16; break; - case BinaryConsts::I8x16LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI8x16; break; - case BinaryConsts::I8x16LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI8x16; break; - case BinaryConsts::I8x16GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI8x16; break; - case BinaryConsts::I8x16GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI8x16; break; - case BinaryConsts::I16x8Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI16x8; break; - case BinaryConsts::I16x8Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI16x8; break; - case BinaryConsts::I16x8LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI16x8; break; - case BinaryConsts::I16x8LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI16x8; break; - case BinaryConsts::I16x8GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI16x8; break; - case BinaryConsts::I16x8GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI16x8; break; - case BinaryConsts::I16x8LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI16x8; break; - case BinaryConsts::I16x8LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI16x8; break; - case BinaryConsts::I16x8GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI16x8; break; - case BinaryConsts::I16x8GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI16x8; break; - case BinaryConsts::I32x4Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecI32x4; break; - case BinaryConsts::I32x4Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecI32x4; break; - case BinaryConsts::I32x4LtS: curr = allocator.alloc<Binary>(); curr->op = LtSVecI32x4; break; - case BinaryConsts::I32x4LtU: curr = allocator.alloc<Binary>(); curr->op = LtUVecI32x4; break; - case BinaryConsts::I32x4GtS: curr = allocator.alloc<Binary>(); curr->op = GtSVecI32x4; break; - case BinaryConsts::I32x4GtU: curr = allocator.alloc<Binary>(); curr->op = GtUVecI32x4; break; - case BinaryConsts::I32x4LeS: curr = allocator.alloc<Binary>(); curr->op = LeSVecI32x4; break; - case BinaryConsts::I32x4LeU: curr = allocator.alloc<Binary>(); curr->op = LeUVecI32x4; break; - case BinaryConsts::I32x4GeS: curr = allocator.alloc<Binary>(); curr->op = GeSVecI32x4; break; - case BinaryConsts::I32x4GeU: curr = allocator.alloc<Binary>(); curr->op = GeUVecI32x4; break; - case BinaryConsts::F32x4Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecF32x4; break; - case BinaryConsts::F32x4Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecF32x4; break; - case BinaryConsts::F32x4Lt: curr = allocator.alloc<Binary>(); curr->op = LtVecF32x4; break; - case BinaryConsts::F32x4Gt: curr = allocator.alloc<Binary>(); curr->op = GtVecF32x4; break; - case BinaryConsts::F32x4Le: curr = allocator.alloc<Binary>(); curr->op = LeVecF32x4; break; - case BinaryConsts::F32x4Ge: curr = allocator.alloc<Binary>(); curr->op = GeVecF32x4; break; - case BinaryConsts::F64x2Eq: curr = allocator.alloc<Binary>(); curr->op = EqVecF64x2; break; - case BinaryConsts::F64x2Ne: curr = allocator.alloc<Binary>(); curr->op = NeVecF64x2; break; - case BinaryConsts::F64x2Lt: curr = allocator.alloc<Binary>(); curr->op = LtVecF64x2; break; - case BinaryConsts::F64x2Gt: curr = allocator.alloc<Binary>(); curr->op = GtVecF64x2; break; - case BinaryConsts::F64x2Le: curr = allocator.alloc<Binary>(); curr->op = LeVecF64x2; break; - case BinaryConsts::F64x2Ge: curr = allocator.alloc<Binary>(); curr->op = GeVecF64x2; break; - case BinaryConsts::V128And: curr = allocator.alloc<Binary>(); curr->op = AndVec128; break; - case BinaryConsts::V128Or: curr = allocator.alloc<Binary>(); curr->op = OrVec128; break; - case BinaryConsts::V128Xor: curr = allocator.alloc<Binary>(); curr->op = XorVec128; break; - case BinaryConsts::I8x16Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI8x16; break; - case BinaryConsts::I8x16AddSatS: curr = allocator.alloc<Binary>(); curr->op = AddSatSVecI8x16; break; - case BinaryConsts::I8x16AddSatU: curr = allocator.alloc<Binary>(); curr->op = AddSatUVecI8x16; break; - case BinaryConsts::I8x16Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI8x16; break; - case BinaryConsts::I8x16SubSatS: curr = allocator.alloc<Binary>(); curr->op = SubSatSVecI8x16; break; - case BinaryConsts::I8x16SubSatU: curr = allocator.alloc<Binary>(); curr->op = SubSatUVecI8x16; break; - case BinaryConsts::I8x16Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI8x16; break; - case BinaryConsts::I16x8Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI16x8; break; - case BinaryConsts::I16x8AddSatS: curr = allocator.alloc<Binary>(); curr->op = AddSatSVecI16x8; break; - case BinaryConsts::I16x8AddSatU: curr = allocator.alloc<Binary>(); curr->op = AddSatUVecI16x8; break; - case BinaryConsts::I16x8Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI16x8; break; - case BinaryConsts::I16x8SubSatS: curr = allocator.alloc<Binary>(); curr->op = SubSatSVecI16x8; break; - case BinaryConsts::I16x8SubSatU: curr = allocator.alloc<Binary>(); curr->op = SubSatUVecI16x8; break; - case BinaryConsts::I16x8Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI16x8; break; - case BinaryConsts::I32x4Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI32x4; break; - case BinaryConsts::I32x4Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI32x4; break; - case BinaryConsts::I32x4Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecI32x4; break; - case BinaryConsts::I64x2Add: curr = allocator.alloc<Binary>(); curr->op = AddVecI64x2; break; - case BinaryConsts::I64x2Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecI64x2; break; - case BinaryConsts::F32x4Add: curr = allocator.alloc<Binary>(); curr->op = AddVecF32x4; break; - case BinaryConsts::F32x4Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecF32x4; break; - case BinaryConsts::F32x4Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecF32x4; break; - case BinaryConsts::F32x4Div: curr = allocator.alloc<Binary>(); curr->op = DivVecF32x4; break; - case BinaryConsts::F32x4Min: curr = allocator.alloc<Binary>(); curr->op = MinVecF32x4; break; - case BinaryConsts::F32x4Max: curr = allocator.alloc<Binary>(); curr->op = MaxVecF32x4; break; - case BinaryConsts::F64x2Add: curr = allocator.alloc<Binary>(); curr->op = AddVecF64x2; break; - case BinaryConsts::F64x2Sub: curr = allocator.alloc<Binary>(); curr->op = SubVecF64x2; break; - case BinaryConsts::F64x2Mul: curr = allocator.alloc<Binary>(); curr->op = MulVecF64x2; break; - case BinaryConsts::F64x2Div: curr = allocator.alloc<Binary>(); curr->op = DivVecF64x2; break; - case BinaryConsts::F64x2Min: curr = allocator.alloc<Binary>(); curr->op = MinVecF64x2; break; - case BinaryConsts::F64x2Max: curr = allocator.alloc<Binary>(); curr->op = MaxVecF64x2; break; - default: return false; - } - if (debug) std::cerr << "zz node: Binary" << std::endl; + case BinaryConsts::I8x16Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecI8x16; + break; + case BinaryConsts::I8x16Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecI8x16; + break; + case BinaryConsts::I8x16LtS: + curr = allocator.alloc<Binary>(); + curr->op = LtSVecI8x16; + break; + case BinaryConsts::I8x16LtU: + curr = allocator.alloc<Binary>(); + curr->op = LtUVecI8x16; + break; + case BinaryConsts::I8x16GtS: + curr = allocator.alloc<Binary>(); + curr->op = GtSVecI8x16; + break; + case BinaryConsts::I8x16GtU: + curr = allocator.alloc<Binary>(); + curr->op = GtUVecI8x16; + break; + case BinaryConsts::I8x16LeS: + curr = allocator.alloc<Binary>(); + curr->op = LeSVecI8x16; + break; + case BinaryConsts::I8x16LeU: + curr = allocator.alloc<Binary>(); + curr->op = LeUVecI8x16; + break; + case BinaryConsts::I8x16GeS: + curr = allocator.alloc<Binary>(); + curr->op = GeSVecI8x16; + break; + case BinaryConsts::I8x16GeU: + curr = allocator.alloc<Binary>(); + curr->op = GeUVecI8x16; + break; + case BinaryConsts::I16x8Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecI16x8; + break; + case BinaryConsts::I16x8Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecI16x8; + break; + case BinaryConsts::I16x8LtS: + curr = allocator.alloc<Binary>(); + curr->op = LtSVecI16x8; + break; + case BinaryConsts::I16x8LtU: + curr = allocator.alloc<Binary>(); + curr->op = LtUVecI16x8; + break; + case BinaryConsts::I16x8GtS: + curr = allocator.alloc<Binary>(); + curr->op = GtSVecI16x8; + break; + case BinaryConsts::I16x8GtU: + curr = allocator.alloc<Binary>(); + curr->op = GtUVecI16x8; + break; + case BinaryConsts::I16x8LeS: + curr = allocator.alloc<Binary>(); + curr->op = LeSVecI16x8; + break; + case BinaryConsts::I16x8LeU: + curr = allocator.alloc<Binary>(); + curr->op = LeUVecI16x8; + break; + case BinaryConsts::I16x8GeS: + curr = allocator.alloc<Binary>(); + curr->op = GeSVecI16x8; + break; + case BinaryConsts::I16x8GeU: + curr = allocator.alloc<Binary>(); + curr->op = GeUVecI16x8; + break; + case BinaryConsts::I32x4Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecI32x4; + break; + case BinaryConsts::I32x4Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecI32x4; + break; + case BinaryConsts::I32x4LtS: + curr = allocator.alloc<Binary>(); + curr->op = LtSVecI32x4; + break; + case BinaryConsts::I32x4LtU: + curr = allocator.alloc<Binary>(); + curr->op = LtUVecI32x4; + break; + case BinaryConsts::I32x4GtS: + curr = allocator.alloc<Binary>(); + curr->op = GtSVecI32x4; + break; + case BinaryConsts::I32x4GtU: + curr = allocator.alloc<Binary>(); + curr->op = GtUVecI32x4; + break; + case BinaryConsts::I32x4LeS: + curr = allocator.alloc<Binary>(); + curr->op = LeSVecI32x4; + break; + case BinaryConsts::I32x4LeU: + curr = allocator.alloc<Binary>(); + curr->op = LeUVecI32x4; + break; + case BinaryConsts::I32x4GeS: + curr = allocator.alloc<Binary>(); + curr->op = GeSVecI32x4; + break; + case BinaryConsts::I32x4GeU: + curr = allocator.alloc<Binary>(); + curr->op = GeUVecI32x4; + break; + case BinaryConsts::F32x4Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecF32x4; + break; + case BinaryConsts::F32x4Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecF32x4; + break; + case BinaryConsts::F32x4Lt: + curr = allocator.alloc<Binary>(); + curr->op = LtVecF32x4; + break; + case BinaryConsts::F32x4Gt: + curr = allocator.alloc<Binary>(); + curr->op = GtVecF32x4; + break; + case BinaryConsts::F32x4Le: + curr = allocator.alloc<Binary>(); + curr->op = LeVecF32x4; + break; + case BinaryConsts::F32x4Ge: + curr = allocator.alloc<Binary>(); + curr->op = GeVecF32x4; + break; + case BinaryConsts::F64x2Eq: + curr = allocator.alloc<Binary>(); + curr->op = EqVecF64x2; + break; + case BinaryConsts::F64x2Ne: + curr = allocator.alloc<Binary>(); + curr->op = NeVecF64x2; + break; + case BinaryConsts::F64x2Lt: + curr = allocator.alloc<Binary>(); + curr->op = LtVecF64x2; + break; + case BinaryConsts::F64x2Gt: + curr = allocator.alloc<Binary>(); + curr->op = GtVecF64x2; + break; + case BinaryConsts::F64x2Le: + curr = allocator.alloc<Binary>(); + curr->op = LeVecF64x2; + break; + case BinaryConsts::F64x2Ge: + curr = allocator.alloc<Binary>(); + curr->op = GeVecF64x2; + break; + case BinaryConsts::V128And: + curr = allocator.alloc<Binary>(); + curr->op = AndVec128; + break; + case BinaryConsts::V128Or: + curr = allocator.alloc<Binary>(); + curr->op = OrVec128; + break; + case BinaryConsts::V128Xor: + curr = allocator.alloc<Binary>(); + curr->op = XorVec128; + break; + case BinaryConsts::I8x16Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI8x16; + break; + case BinaryConsts::I8x16AddSatS: + curr = allocator.alloc<Binary>(); + curr->op = AddSatSVecI8x16; + break; + case BinaryConsts::I8x16AddSatU: + curr = allocator.alloc<Binary>(); + curr->op = AddSatUVecI8x16; + break; + case BinaryConsts::I8x16Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI8x16; + break; + case BinaryConsts::I8x16SubSatS: + curr = allocator.alloc<Binary>(); + curr->op = SubSatSVecI8x16; + break; + case BinaryConsts::I8x16SubSatU: + curr = allocator.alloc<Binary>(); + curr->op = SubSatUVecI8x16; + break; + case BinaryConsts::I8x16Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecI8x16; + break; + case BinaryConsts::I16x8Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI16x8; + break; + case BinaryConsts::I16x8AddSatS: + curr = allocator.alloc<Binary>(); + curr->op = AddSatSVecI16x8; + break; + case BinaryConsts::I16x8AddSatU: + curr = allocator.alloc<Binary>(); + curr->op = AddSatUVecI16x8; + break; + case BinaryConsts::I16x8Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI16x8; + break; + case BinaryConsts::I16x8SubSatS: + curr = allocator.alloc<Binary>(); + curr->op = SubSatSVecI16x8; + break; + case BinaryConsts::I16x8SubSatU: + curr = allocator.alloc<Binary>(); + curr->op = SubSatUVecI16x8; + break; + case BinaryConsts::I16x8Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecI16x8; + break; + case BinaryConsts::I32x4Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI32x4; + break; + case BinaryConsts::I32x4Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI32x4; + break; + case BinaryConsts::I32x4Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecI32x4; + break; + case BinaryConsts::I64x2Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecI64x2; + break; + case BinaryConsts::I64x2Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecI64x2; + break; + case BinaryConsts::F32x4Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecF32x4; + break; + case BinaryConsts::F32x4Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecF32x4; + break; + case BinaryConsts::F32x4Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecF32x4; + break; + case BinaryConsts::F32x4Div: + curr = allocator.alloc<Binary>(); + curr->op = DivVecF32x4; + break; + case BinaryConsts::F32x4Min: + curr = allocator.alloc<Binary>(); + curr->op = MinVecF32x4; + break; + case BinaryConsts::F32x4Max: + curr = allocator.alloc<Binary>(); + curr->op = MaxVecF32x4; + break; + case BinaryConsts::F64x2Add: + curr = allocator.alloc<Binary>(); + curr->op = AddVecF64x2; + break; + case BinaryConsts::F64x2Sub: + curr = allocator.alloc<Binary>(); + curr->op = SubVecF64x2; + break; + case BinaryConsts::F64x2Mul: + curr = allocator.alloc<Binary>(); + curr->op = MulVecF64x2; + break; + case BinaryConsts::F64x2Div: + curr = allocator.alloc<Binary>(); + curr->op = DivVecF64x2; + break; + case BinaryConsts::F64x2Min: + curr = allocator.alloc<Binary>(); + curr->op = MinVecF64x2; + break; + case BinaryConsts::F64x2Max: + curr = allocator.alloc<Binary>(); + curr->op = MaxVecF64x2; + break; + default: + return false; + } + if (debug) + std::cerr << "zz node: Binary" << std::endl; curr->right = popNonVoidExpression(); curr->left = popNonVoidExpression(); curr->finalize(); @@ -2596,40 +3603,140 @@ bool WasmBinaryBuilder::maybeVisitSIMDBinary(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitSIMDUnary(Expression*& out, uint32_t code) { Unary* curr; switch (code) { - case BinaryConsts::I8x16Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI8x16; break; - case BinaryConsts::I16x8Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI16x8; break; - case BinaryConsts::I32x4Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI32x4; break; - case BinaryConsts::I64x2Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecI64x2; break; - case BinaryConsts::F32x4Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecF32x4; break; - case BinaryConsts::F64x2Splat: curr = allocator.alloc<Unary>(); curr->op = SplatVecF64x2; break; - case BinaryConsts::V128Not: curr = allocator.alloc<Unary>(); curr->op = NotVec128; break; - case BinaryConsts::I8x16Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI8x16; break; - case BinaryConsts::I8x16AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI8x16; break; - case BinaryConsts::I8x16AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI8x16; break; - case BinaryConsts::I16x8Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI16x8; break; - case BinaryConsts::I16x8AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI16x8; break; - case BinaryConsts::I16x8AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI16x8; break; - case BinaryConsts::I32x4Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI32x4; break; - case BinaryConsts::I32x4AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI32x4; break; - case BinaryConsts::I32x4AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI32x4; break; - case BinaryConsts::I64x2Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecI64x2; break; - case BinaryConsts::I64x2AnyTrue: curr = allocator.alloc<Unary>(); curr->op = AnyTrueVecI64x2; break; - case BinaryConsts::I64x2AllTrue: curr = allocator.alloc<Unary>(); curr->op = AllTrueVecI64x2; break; - case BinaryConsts::F32x4Abs: curr = allocator.alloc<Unary>(); curr->op = AbsVecF32x4; break; - case BinaryConsts::F32x4Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecF32x4; break; - case BinaryConsts::F32x4Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtVecF32x4; break; - case BinaryConsts::F64x2Abs: curr = allocator.alloc<Unary>(); curr->op = AbsVecF64x2; break; - case BinaryConsts::F64x2Neg: curr = allocator.alloc<Unary>(); curr->op = NegVecF64x2; break; - case BinaryConsts::F64x2Sqrt: curr = allocator.alloc<Unary>(); curr->op = SqrtVecF64x2; break; - case BinaryConsts::I32x4TruncSatSF32x4: curr = allocator.alloc<Unary>(); curr->op = TruncSatSVecF32x4ToVecI32x4; break; - case BinaryConsts::I32x4TruncSatUF32x4: curr = allocator.alloc<Unary>(); curr->op = TruncSatUVecF32x4ToVecI32x4; break; - case BinaryConsts::I64x2TruncSatSF64x2: curr = allocator.alloc<Unary>(); curr->op = TruncSatSVecF64x2ToVecI64x2; break; - case BinaryConsts::I64x2TruncSatUF64x2: curr = allocator.alloc<Unary>(); curr->op = TruncSatUVecF64x2ToVecI64x2; break; - case BinaryConsts::F32x4ConvertSI32x4: curr = allocator.alloc<Unary>(); curr->op = ConvertSVecI32x4ToVecF32x4; break; - case BinaryConsts::F32x4ConvertUI32x4: curr = allocator.alloc<Unary>(); curr->op = ConvertUVecI32x4ToVecF32x4; break; - case BinaryConsts::F64x2ConvertSI64x2: curr = allocator.alloc<Unary>(); curr->op = ConvertSVecI64x2ToVecF64x2; break; - case BinaryConsts::F64x2ConvertUI64x2: curr = allocator.alloc<Unary>(); curr->op = ConvertUVecI64x2ToVecF64x2; break; - default: return false; + case BinaryConsts::I8x16Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI8x16; + break; + case BinaryConsts::I16x8Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI16x8; + break; + case BinaryConsts::I32x4Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI32x4; + break; + case BinaryConsts::I64x2Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecI64x2; + break; + case BinaryConsts::F32x4Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecF32x4; + break; + case BinaryConsts::F64x2Splat: + curr = allocator.alloc<Unary>(); + curr->op = SplatVecF64x2; + break; + case BinaryConsts::V128Not: + curr = allocator.alloc<Unary>(); + curr->op = NotVec128; + break; + case BinaryConsts::I8x16Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI8x16; + break; + case BinaryConsts::I8x16AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI8x16; + break; + case BinaryConsts::I8x16AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI8x16; + break; + case BinaryConsts::I16x8Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI16x8; + break; + case BinaryConsts::I16x8AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI16x8; + break; + case BinaryConsts::I16x8AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI16x8; + break; + case BinaryConsts::I32x4Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI32x4; + break; + case BinaryConsts::I32x4AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI32x4; + break; + case BinaryConsts::I32x4AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI32x4; + break; + case BinaryConsts::I64x2Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecI64x2; + break; + case BinaryConsts::I64x2AnyTrue: + curr = allocator.alloc<Unary>(); + curr->op = AnyTrueVecI64x2; + break; + case BinaryConsts::I64x2AllTrue: + curr = allocator.alloc<Unary>(); + curr->op = AllTrueVecI64x2; + break; + case BinaryConsts::F32x4Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsVecF32x4; + break; + case BinaryConsts::F32x4Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecF32x4; + break; + case BinaryConsts::F32x4Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtVecF32x4; + break; + case BinaryConsts::F64x2Abs: + curr = allocator.alloc<Unary>(); + curr->op = AbsVecF64x2; + break; + case BinaryConsts::F64x2Neg: + curr = allocator.alloc<Unary>(); + curr->op = NegVecF64x2; + break; + case BinaryConsts::F64x2Sqrt: + curr = allocator.alloc<Unary>(); + curr->op = SqrtVecF64x2; + break; + case BinaryConsts::I32x4TruncSatSF32x4: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSVecF32x4ToVecI32x4; + break; + case BinaryConsts::I32x4TruncSatUF32x4: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUVecF32x4ToVecI32x4; + break; + case BinaryConsts::I64x2TruncSatSF64x2: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatSVecF64x2ToVecI64x2; + break; + case BinaryConsts::I64x2TruncSatUF64x2: + curr = allocator.alloc<Unary>(); + curr->op = TruncSatUVecF64x2ToVecI64x2; + break; + case BinaryConsts::F32x4ConvertSI32x4: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSVecI32x4ToVecF32x4; + break; + case BinaryConsts::F32x4ConvertUI32x4: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUVecI32x4ToVecF32x4; + break; + case BinaryConsts::F64x2ConvertSI64x2: + curr = allocator.alloc<Unary>(); + curr->op = ConvertSVecI64x2ToVecF64x2; + break; + case BinaryConsts::F64x2ConvertUI64x2: + curr = allocator.alloc<Unary>(); + curr->op = ConvertUVecI64x2ToVecF64x2; + break; + default: + return false; } curr->value = popNonVoidExpression(); curr->finalize(); @@ -2682,15 +3789,48 @@ bool WasmBinaryBuilder::maybeVisitSIMDStore(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitSIMDExtract(Expression*& out, uint32_t code) { SIMDExtract* curr; switch (code) { - case BinaryConsts::I8x16ExtractLaneS: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneSVecI8x16; curr->index = getLaneIndex(16); break; - case BinaryConsts::I8x16ExtractLaneU: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneUVecI8x16; curr->index = getLaneIndex(16); break; - case BinaryConsts::I16x8ExtractLaneS: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneSVecI16x8; curr->index = getLaneIndex(8); break; - case BinaryConsts::I16x8ExtractLaneU: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneUVecI16x8; curr->index = getLaneIndex(8); break; - case BinaryConsts::I32x4ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecI32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::I64x2ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecI64x2; curr->index = getLaneIndex(2); break; - case BinaryConsts::F32x4ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecF32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::F64x2ExtractLane: curr = allocator.alloc<SIMDExtract>(); curr->op = ExtractLaneVecF64x2; curr->index = getLaneIndex(2); break; - default: return false; + case BinaryConsts::I8x16ExtractLaneS: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneSVecI8x16; + curr->index = getLaneIndex(16); + break; + case BinaryConsts::I8x16ExtractLaneU: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneUVecI8x16; + curr->index = getLaneIndex(16); + break; + case BinaryConsts::I16x8ExtractLaneS: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneSVecI16x8; + curr->index = getLaneIndex(8); + break; + case BinaryConsts::I16x8ExtractLaneU: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneUVecI16x8; + curr->index = getLaneIndex(8); + break; + case BinaryConsts::I32x4ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecI32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::I64x2ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecI64x2; + curr->index = getLaneIndex(2); + break; + case BinaryConsts::F32x4ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecF32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::F64x2ExtractLane: + curr = allocator.alloc<SIMDExtract>(); + curr->op = ExtractLaneVecF64x2; + curr->index = getLaneIndex(2); + break; + default: + return false; } curr->vec = popNonVoidExpression(); curr->finalize(); @@ -2701,13 +3841,38 @@ bool WasmBinaryBuilder::maybeVisitSIMDExtract(Expression*& out, uint32_t code) { bool WasmBinaryBuilder::maybeVisitSIMDReplace(Expression*& out, uint32_t code) { SIMDReplace* curr; switch (code) { - case BinaryConsts::I8x16ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI8x16; curr->index = getLaneIndex(16); break; - case BinaryConsts::I16x8ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI16x8; curr->index = getLaneIndex(8); break; - case BinaryConsts::I32x4ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::I64x2ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecI64x2; curr->index = getLaneIndex(2); break; - case BinaryConsts::F32x4ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecF32x4; curr->index = getLaneIndex(4); break; - case BinaryConsts::F64x2ReplaceLane: curr = allocator.alloc<SIMDReplace>(); curr->op = ReplaceLaneVecF64x2; curr->index = getLaneIndex(2); break; - default: return false; + case BinaryConsts::I8x16ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI8x16; + curr->index = getLaneIndex(16); + break; + case BinaryConsts::I16x8ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI16x8; + curr->index = getLaneIndex(8); + break; + case BinaryConsts::I32x4ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::I64x2ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecI64x2; + curr->index = getLaneIndex(2); + break; + case BinaryConsts::F32x4ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecF32x4; + curr->index = getLaneIndex(4); + break; + case BinaryConsts::F64x2ReplaceLane: + curr = allocator.alloc<SIMDReplace>(); + curr->op = ReplaceLaneVecF64x2; + curr->index = getLaneIndex(2); + break; + default: + return false; } curr->value = popNonVoidExpression(); curr->vec = popNonVoidExpression(); @@ -2731,7 +3896,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDShuffle(Expression*& out, uint32_t code) { return true; } -bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, uint32_t code) { +bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, + uint32_t code) { if (code != BinaryConsts::V128Bitselect) { return false; } @@ -2747,19 +3913,56 @@ bool WasmBinaryBuilder::maybeVisitSIMDBitselect(Expression*& out, uint32_t code) bool WasmBinaryBuilder::maybeVisitSIMDShift(Expression*& out, uint32_t code) { SIMDShift* curr; switch (code) { - case BinaryConsts::I8x16Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI8x16; break; - case BinaryConsts::I8x16ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI8x16; break; - case BinaryConsts::I8x16ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI8x16; break; - case BinaryConsts::I16x8Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI16x8; break; - case BinaryConsts::I16x8ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI16x8; break; - case BinaryConsts::I16x8ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI16x8; break; - case BinaryConsts::I32x4Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI32x4; break; - case BinaryConsts::I32x4ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI32x4; break; - case BinaryConsts::I32x4ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI32x4; break; - case BinaryConsts::I64x2Shl: curr = allocator.alloc<SIMDShift>(); curr->op = ShlVecI64x2; break; - case BinaryConsts::I64x2ShrS: curr = allocator.alloc<SIMDShift>(); curr->op = ShrSVecI64x2; break; - case BinaryConsts::I64x2ShrU: curr = allocator.alloc<SIMDShift>(); curr->op = ShrUVecI64x2; break; - default: return false; + case BinaryConsts::I8x16Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI8x16; + break; + case BinaryConsts::I8x16ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI8x16; + break; + case BinaryConsts::I8x16ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI8x16; + break; + case BinaryConsts::I16x8Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI16x8; + break; + case BinaryConsts::I16x8ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI16x8; + break; + case BinaryConsts::I16x8ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI16x8; + break; + case BinaryConsts::I32x4Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI32x4; + break; + case BinaryConsts::I32x4ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI32x4; + break; + case BinaryConsts::I32x4ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI32x4; + break; + case BinaryConsts::I64x2Shl: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShlVecI64x2; + break; + case BinaryConsts::I64x2ShrS: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrSVecI64x2; + break; + case BinaryConsts::I64x2ShrU: + curr = allocator.alloc<SIMDShift>(); + curr->op = ShrUVecI64x2; + break; + default: + return false; } curr->shift = popNonVoidExpression(); curr->vec = popNonVoidExpression(); @@ -2769,7 +3972,8 @@ bool WasmBinaryBuilder::maybeVisitSIMDShift(Expression*& out, uint32_t code) { } void WasmBinaryBuilder::visitSelect(Select* curr) { - if (debug) std::cerr << "zz node: Select" << std::endl; + if (debug) + std::cerr << "zz node: Select" << std::endl; curr->condition = popNonVoidExpression(); curr->ifFalse = popNonVoidExpression(); curr->ifTrue = popNonVoidExpression(); @@ -2777,7 +3981,8 @@ void WasmBinaryBuilder::visitSelect(Select* curr) { } void WasmBinaryBuilder::visitReturn(Return* curr) { - if (debug) std::cerr << "zz node: Return" << std::endl; + if (debug) + std::cerr << "zz node: Return" << std::endl; requireFunctionContext("return"); if (currFunction->result != none) { curr->value = popNonVoidExpression(); @@ -2800,26 +4005,32 @@ bool WasmBinaryBuilder::maybeVisitHost(Expression*& out, uint8_t code) { curr->operands[0] = popNonVoidExpression(); break; } - default: return false; + default: + return false; } - if (debug) std::cerr << "zz node: Host" << std::endl; + if (debug) + std::cerr << "zz node: Host" << std::endl; auto reserved = getU32LEB(); - if (reserved != 0) throwError("Invalid reserved field on grow_memory/current_memory"); + if (reserved != 0) + throwError("Invalid reserved field on grow_memory/current_memory"); curr->finalize(); out = curr; return true; } void WasmBinaryBuilder::visitNop(Nop* curr) { - if (debug) std::cerr << "zz node: Nop" << std::endl; + if (debug) + std::cerr << "zz node: Nop" << std::endl; } void WasmBinaryBuilder::visitUnreachable(Unreachable* curr) { - if (debug) std::cerr << "zz node: Unreachable" << std::endl; + if (debug) + std::cerr << "zz node: Unreachable" << std::endl; } void WasmBinaryBuilder::visitDrop(Drop* curr) { - if (debug) std::cerr << "zz node: Drop" << std::endl; + if (debug) + std::cerr << "zz node: Drop" << std::endl; curr->value = popNonVoidExpression(); curr->finalize(); } |