blob: 89fe4fc60ccdd045e067cbc4e1c99bc779fda05a [file] [log] [blame] [edit]
/*
* Copyright 2016 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <fstream>
#include <iomanip>
#include "ir/eh-utils.h"
#include "ir/module-utils.h"
#include "ir/names.h"
#include "ir/table-utils.h"
#include "ir/type-updating.h"
#include "pass.h"
#include "support/bits.h"
#include "support/debug.h"
#include "support/stdckdint.h"
#include "support/string.h"
#include "wasm-binary.h"
#include "wasm-debug.h"
#include "wasm-limits.h"
#include "wasm-stack.h"
#define DEBUG_TYPE "binary"
namespace wasm {
void WasmBinaryWriter::prepare() {
// Collect function types and their frequencies. Collect information in each
// function in parallel, then merge.
indexedTypes = ModuleUtils::getOptimizedIndexedHeapTypes(*wasm);
for (Index i = 0, size = indexedTypes.types.size(); i < size; ++i) {
if (indexedTypes.types[i].isSignature()) {
signatureIndexes.insert({indexedTypes.types[i].getSignature(), i});
}
}
importInfo = std::make_unique<ImportInfo>(*wasm);
}
void WasmBinaryWriter::write() {
writeHeader();
writeDylinkSection();
initializeDebugInfo();
if (sourceMap) {
writeSourceMapProlog();
}
writeTypes();
writeImports();
writeFunctionSignatures();
writeTableDeclarations();
writeMemories();
writeTags();
if (wasm->features.hasStrings()) {
writeStrings();
}
writeGlobals();
writeExports();
writeStart();
writeElementSegments();
writeDataCount();
writeFunctions();
writeDataSegments();
if (debugInfo || emitModuleName) {
writeNames();
}
if (sourceMap && !sourceMapUrl.empty()) {
writeSourceMapUrl();
}
if (symbolMap.size() > 0) {
writeSymbolMap();
}
if (sourceMap) {
writeSourceMapEpilog();
}
#ifdef BUILD_LLVM_DWARF
// Update DWARF user sections after writing the data they refer to
// (function bodies), and before writing the user sections themselves.
if (Debug::hasDWARFSections(*wasm)) {
Debug::writeDWARFSections(*wasm, binaryLocations);
}
#endif
writeLateCustomSections();
writeFeaturesSection();
}
void WasmBinaryWriter::writeHeader() {
o << int32_t(BinaryConsts::Magic); // magic number \0asm
o << int32_t(BinaryConsts::Version);
}
int32_t WasmBinaryWriter::writeU32LEBPlaceholder() {
int32_t ret = o.size();
o << int32_t(0);
o << int8_t(0);
return ret;
}
void WasmBinaryWriter::writeResizableLimits(
Address initial, Address maximum, bool hasMaximum, bool shared, bool is64) {
uint32_t flags = (hasMaximum ? (uint32_t)BinaryConsts::HasMaximum : 0U) |
(shared ? (uint32_t)BinaryConsts::IsShared : 0U) |
(is64 ? (uint32_t)BinaryConsts::Is64 : 0U);
o << U32LEB(flags);
if (is64) {
o << U64LEB(initial);
if (hasMaximum) {
o << U64LEB(maximum);
}
} else {
o << U32LEB(initial);
if (hasMaximum) {
o << U32LEB(maximum);
}
}
}
template<typename T> int32_t WasmBinaryWriter::startSection(T code) {
o << uint8_t(code);
if (sourceMap) {
sourceMapLocationsSizeAtSectionStart = sourceMapLocations.size();
}
binaryLocationsSizeAtSectionStart = binaryLocations.expressions.size();
return writeU32LEBPlaceholder(); // section size to be filled in later
}
void WasmBinaryWriter::finishSection(int32_t start) {
// 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));
// We can move things back if the actual LEB for the size doesn't use the
// maximum 5 bytes. In that case we need to adjust offsets after we move
// things backwards.
auto adjustmentForLEBShrinking = MaxLEB32Bytes - sizeFieldSize;
if (adjustmentForLEBShrinking) {
// we can save some room, nice
assert(sizeFieldSize < MaxLEB32Bytes);
std::move(&o[start] + MaxLEB32Bytes,
&o[start] + MaxLEB32Bytes + size,
&o[start] + sizeFieldSize);
o.resize(o.size() - adjustmentForLEBShrinking);
if (sourceMap) {
for (auto i = sourceMapLocationsSizeAtSectionStart;
i < sourceMapLocations.size();
++i) {
sourceMapLocations[i].first -= adjustmentForLEBShrinking;
}
}
}
if (binaryLocationsSizeAtSectionStart != binaryLocations.expressions.size()) {
// We added the binary locations, adjust them: they must be relative
// to the code section.
assert(binaryLocationsSizeAtSectionStart == 0);
// The section type byte is right before the LEB for the size; we want
// offsets that are relative to the body, which is after that section type
// byte and the the size LEB.
auto body = start + sizeFieldSize;
// Offsets are relative to the body of the code section: after the
// section type byte and the size.
// Everything was moved by the adjustment, track that. After this,
// we are at the right absolute address.
// We are relative to the section start.
auto totalAdjustment = adjustmentForLEBShrinking + body;
for (auto& [_, locations] : binaryLocations.expressions) {
locations.start -= totalAdjustment;
locations.end -= totalAdjustment;
}
for (auto& [_, locations] : binaryLocations.functions) {
locations.start -= totalAdjustment;
locations.declarations -= totalAdjustment;
locations.end -= totalAdjustment;
}
for (auto& [_, locations] : binaryLocations.delimiters) {
for (auto& item : locations) {
item -= totalAdjustment;
}
}
}
}
int32_t WasmBinaryWriter::startSubsection(
BinaryConsts::CustomSections::Subsection code) {
return startSection(code);
}
void WasmBinaryWriter::finishSubsection(int32_t start) { finishSection(start); }
void WasmBinaryWriter::writeStart() {
if (!wasm->start.is()) {
return;
}
auto start = startSection(BinaryConsts::Section::Start);
o << U32LEB(getFunctionIndex(wasm->start.str));
finishSection(start);
}
void WasmBinaryWriter::writeMemories() {
if (importInfo->getNumDefinedMemories() == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Memory);
auto num = importInfo->getNumDefinedMemories();
o << U32LEB(num);
ModuleUtils::iterDefinedMemories(*wasm, [&](Memory* memory) {
writeResizableLimits(memory->initial,
memory->max,
memory->hasMax(),
memory->shared,
memory->is64());
});
finishSection(start);
}
void WasmBinaryWriter::writeTypes() {
if (indexedTypes.types.size() == 0) {
return;
}
// Count the number of recursion groups, which is the number of elements in
// the type section.
size_t numGroups = 0;
{
std::optional<RecGroup> lastGroup;
for (auto type : indexedTypes.types) {
auto currGroup = type.getRecGroup();
numGroups += lastGroup != currGroup;
lastGroup = currGroup;
}
}
// As a temporary measure, detect which types have subtypes and always use
// `sub` or `sub final` for these types. The standard says that types without
// `sub` or `sub final` are final, but we currently treat them as non-final.
// To avoid unsafe ambiguity, only use the short form for types that it would
// be safe to treat as final, i.e. types without subtypes.
std::vector<bool> hasSubtypes(indexedTypes.types.size());
for (auto type : indexedTypes.types) {
if (auto super = type.getDeclaredSuperType()) {
hasSubtypes[indexedTypes.indices[*super]] = true;
}
}
auto start = startSection(BinaryConsts::Section::Type);
o << U32LEB(numGroups);
std::optional<RecGroup> lastGroup = std::nullopt;
for (Index i = 0; i < indexedTypes.types.size(); ++i) {
auto type = indexedTypes.types[i];
// Check whether we need to start a new recursion group. Recursion groups of
// size 1 are implicit, so only emit a group header for larger groups.
auto currGroup = type.getRecGroup();
if (lastGroup != currGroup && currGroup.size() > 1) {
o << uint8_t(BinaryConsts::EncodedType::Rec) << U32LEB(currGroup.size());
}
lastGroup = currGroup;
// Emit the type definition.
auto super = type.getDeclaredSuperType();
if (super || type.isOpen()) {
if (type.isOpen()) {
o << uint8_t(BinaryConsts::EncodedType::Sub);
} else {
o << uint8_t(BinaryConsts::EncodedType::SubFinal);
}
if (super) {
o << U32LEB(1);
writeHeapType(*super);
} else {
o << U32LEB(0);
}
}
if (type.isShared()) {
o << uint8_t(BinaryConsts::EncodedType::SharedDef);
}
switch (type.getKind()) {
case HeapTypeKind::Func: {
o << uint8_t(BinaryConsts::EncodedType::Func);
auto sig = type.getSignature();
for (auto& sigType : {sig.params, sig.results}) {
o << U32LEB(sigType.size());
for (const auto& type : sigType) {
writeType(type);
}
}
break;
}
case HeapTypeKind::Struct: {
o << uint8_t(BinaryConsts::EncodedType::Struct);
auto fields = type.getStruct().fields;
o << U32LEB(fields.size());
for (const auto& field : fields) {
writeField(field);
}
break;
}
case HeapTypeKind::Array:
o << uint8_t(BinaryConsts::EncodedType::Array);
writeField(type.getArray().element);
break;
case HeapTypeKind::Cont:
o << uint8_t(BinaryConsts::EncodedType::Cont);
writeHeapType(type.getContinuation().type);
break;
case HeapTypeKind::Basic:
WASM_UNREACHABLE("unexpected kind");
}
}
finishSection(start);
}
void WasmBinaryWriter::writeImports() {
auto num = importInfo->getNumImports();
if (num == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Import);
o << U32LEB(num);
auto writeImportHeader = [&](Importable* import) {
writeInlineString(import->module.str);
writeInlineString(import->base.str);
};
ModuleUtils::iterImportedFunctions(*wasm, [&](Function* func) {
writeImportHeader(func);
o << U32LEB(int32_t(ExternalKind::Function));
o << U32LEB(getTypeIndex(func->type));
});
ModuleUtils::iterImportedGlobals(*wasm, [&](Global* global) {
writeImportHeader(global);
o << U32LEB(int32_t(ExternalKind::Global));
writeType(global->type);
o << U32LEB(global->mutable_);
});
ModuleUtils::iterImportedTags(*wasm, [&](Tag* tag) {
writeImportHeader(tag);
o << U32LEB(int32_t(ExternalKind::Tag));
o << uint8_t(0); // Reserved 'attribute' field. Always 0.
o << U32LEB(getTypeIndex(tag->sig));
});
ModuleUtils::iterImportedMemories(*wasm, [&](Memory* memory) {
writeImportHeader(memory);
o << U32LEB(int32_t(ExternalKind::Memory));
writeResizableLimits(memory->initial,
memory->max,
memory->hasMax(),
memory->shared,
memory->is64());
});
ModuleUtils::iterImportedTables(*wasm, [&](Table* table) {
writeImportHeader(table);
o << U32LEB(int32_t(ExternalKind::Table));
writeType(table->type);
writeResizableLimits(table->initial,
table->max,
table->hasMax(),
/*shared=*/false,
table->is64());
});
finishSection(start);
}
void WasmBinaryWriter::writeFunctionSignatures() {
if (importInfo->getNumDefinedFunctions() == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Function);
o << U32LEB(importInfo->getNumDefinedFunctions());
ModuleUtils::iterDefinedFunctions(
*wasm, [&](Function* func) { o << U32LEB(getTypeIndex(func->type)); });
finishSection(start);
}
void WasmBinaryWriter::writeExpression(Expression* curr) {
BinaryenIRToBinaryWriter(*this, o).visit(curr);
}
void WasmBinaryWriter::writeFunctions() {
if (importInfo->getNumDefinedFunctions() == 0) {
return;
}
std::optional<ModuleStackIR> moduleStackIR;
if (options.generateStackIR) {
moduleStackIR.emplace(*wasm, options);
}
auto sectionStart = startSection(BinaryConsts::Section::Code);
o << U32LEB(importInfo->getNumDefinedFunctions());
bool DWARF = Debug::hasDWARFSections(*getModule());
ModuleUtils::iterDefinedFunctions(*wasm, [&](Function* func) {
assert(binaryLocationTrackedExpressionsForFunc.empty());
// Do not smear any debug location from the previous function.
writeNoDebugLocation();
size_t sourceMapLocationsSizeAtFunctionStart = sourceMapLocations.size();
size_t sizePos = writeU32LEBPlaceholder();
size_t start = o.size();
// Emit Stack IR if present.
StackIR* stackIR = nullptr;
if (moduleStackIR) {
stackIR = moduleStackIR->getStackIROrNull(func);
}
if (stackIR) {
StackIRToBinaryWriter writer(*this, o, func, *stackIR, sourceMap, DWARF);
writer.write();
if (debugInfo) {
funcMappedLocals[func->name] = std::move(writer.getMappedLocals());
}
} else {
BinaryenIRToBinaryWriter writer(*this, o, func, sourceMap, DWARF);
writer.write();
if (debugInfo) {
funcMappedLocals[func->name] = std::move(writer.getMappedLocals());
}
}
size_t size = o.size() - start;
assert(size <= std::numeric_limits<uint32_t>::max());
auto sizeFieldSize = o.writeAt(sizePos, U32LEB(size));
// We can move things back if the actual LEB for the size doesn't use the
// maximum 5 bytes. In that case we need to adjust offsets after we move
// things backwards.
auto adjustmentForLEBShrinking = MaxLEB32Bytes - sizeFieldSize;
if (adjustmentForLEBShrinking) {
// we can save some room, nice
assert(sizeFieldSize < MaxLEB32Bytes);
std::move(&o[start], &o[start] + size, &o[sizePos] + sizeFieldSize);
o.resize(o.size() - adjustmentForLEBShrinking);
if (sourceMap) {
for (auto i = sourceMapLocationsSizeAtFunctionStart;
i < sourceMapLocations.size();
++i) {
sourceMapLocations[i].first -= adjustmentForLEBShrinking;
}
}
for (auto* curr : binaryLocationTrackedExpressionsForFunc) {
// We added the binary locations, adjust them: they must be relative
// to the code section.
auto& span = binaryLocations.expressions[curr];
span.start -= adjustmentForLEBShrinking;
span.end -= adjustmentForLEBShrinking;
auto iter = binaryLocations.delimiters.find(curr);
if (iter != binaryLocations.delimiters.end()) {
for (auto& item : iter->second) {
item -= adjustmentForLEBShrinking;
}
}
}
}
if (!binaryLocationTrackedExpressionsForFunc.empty()) {
binaryLocations.functions[func] = BinaryLocations::FunctionLocations{
BinaryLocation(sizePos),
BinaryLocation(start - adjustmentForLEBShrinking),
BinaryLocation(o.size())};
}
tableOfContents.functionBodies.emplace_back(
func->name, sizePos + sizeFieldSize, size);
binaryLocationTrackedExpressionsForFunc.clear();
if (func->getParams().size() > WebLimitations::MaxFunctionParams) {
std::cerr << "Some VMs may not accept this binary because it has a large "
<< "number of parameters in function " << func->name << ".\n";
}
if (func->getNumLocals() > WebLimitations::MaxFunctionLocals) {
std::cerr << "Some VMs may not accept this binary because it has a large "
<< "number of locals in function " << func->name << ".\n";
}
});
finishSection(sectionStart);
}
void WasmBinaryWriter::writeStrings() {
assert(wasm->features.hasStrings());
// Scan the entire wasm to find the relevant strings.
// To find all the string literals we must scan all the code.
using StringSet = std::unordered_set<Name>;
struct StringWalker : public PostWalker<StringWalker> {
StringSet& strings;
StringWalker(StringSet& strings) : strings(strings) {}
void visitStringConst(StringConst* curr) { strings.insert(curr->string); }
};
ModuleUtils::ParallelFunctionAnalysis<StringSet> analysis(
*wasm, [&](Function* func, StringSet& strings) {
if (!func->imported()) {
StringWalker(strings).walk(func->body);
}
});
// Also walk the global module code (for simplicity, also add it to the
// function map, using a "function" key of nullptr).
auto& globalStrings = analysis.map[nullptr];
StringWalker(globalStrings).walkModuleCode(wasm);
// Generate the indexes from the combined set of necessary strings,
// which we sort for determinism.
StringSet allStrings;
for (auto& [func, strings] : analysis.map) {
for (auto& string : strings) {
allStrings.insert(string);
}
}
std::vector<Name> sorted;
for (auto& string : allStrings) {
sorted.push_back(string);
}
std::sort(sorted.begin(), sorted.end());
for (Index i = 0; i < sorted.size(); i++) {
stringIndexes[sorted[i]] = i;
}
auto num = sorted.size();
if (num == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Strings);
// Placeholder for future use in the spec.
o << U32LEB(0);
// The number of strings and then their contents.
o << U32LEB(num);
for (auto& string : sorted) {
// Re-encode from WTF-16 to WTF-8.
std::stringstream wtf8;
[[maybe_unused]] bool valid = String::convertWTF16ToWTF8(wtf8, string.str);
assert(valid);
// TODO: Use wtf8.view() once we have C++20.
writeInlineString(wtf8.str());
}
finishSection(start);
}
void WasmBinaryWriter::writeGlobals() {
if (importInfo->getNumDefinedGlobals() == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Global);
// Count and emit the total number of globals after tuple globals have been
// expanded into their constituent parts.
Index num = 0;
ModuleUtils::iterDefinedGlobals(
*wasm, [&num](Global* global) { num += global->type.size(); });
o << U32LEB(num);
ModuleUtils::iterDefinedGlobals(*wasm, [&](Global* global) {
size_t i = 0;
for (const auto& t : global->type) {
writeType(t);
o << U32LEB(global->mutable_);
if (global->type.size() == 1) {
writeExpression(global->init);
} else if (auto* make = global->init->dynCast<TupleMake>()) {
// Emit the proper lane for this global.
writeExpression(make->operands[i]);
} else {
// For now tuple globals must contain tuple.make. We could perhaps
// support more operations, like global.get, but the code would need to
// look something like this:
//
// auto parentIndex = getGlobalIndex(get->name);
// o << int8_t(BinaryConsts::GlobalGet) << U32LEB(parentIndex + i);
//
// That is, we must emit the instruction here, and not defer to
// writeExpression, as writeExpression writes an entire expression at a
// time (and not just one of the lanes). As emitting an instruction here
// is less clean, and there is no important use case for global.get of
// one tuple global to another, we disallow this.
WASM_UNREACHABLE("unsupported tuple global operation");
}
o << int8_t(BinaryConsts::End);
++i;
}
});
finishSection(start);
}
void WasmBinaryWriter::writeExports() {
if (wasm->exports.size() == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Export);
o << U32LEB(wasm->exports.size());
for (auto& curr : wasm->exports) {
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(getTableIndex(curr->value));
break;
case ExternalKind::Memory:
o << U32LEB(getMemoryIndex(curr->value));
break;
case ExternalKind::Global:
o << U32LEB(getGlobalIndex(curr->value));
break;
case ExternalKind::Tag:
o << U32LEB(getTagIndex(curr->value));
break;
default:
WASM_UNREACHABLE("unexpected extern kind");
}
}
finishSection(start);
}
void WasmBinaryWriter::writeDataCount() {
if (!wasm->features.hasBulkMemory() || !wasm->dataSegments.size()) {
return;
}
auto start = startSection(BinaryConsts::Section::DataCount);
o << U32LEB(wasm->dataSegments.size());
finishSection(start);
}
void WasmBinaryWriter::writeDataSegments() {
if (wasm->dataSegments.size() == 0) {
return;
}
if (wasm->dataSegments.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 "
<< "merge segments.\n";
}
auto start = startSection(BinaryConsts::Section::Data);
o << U32LEB(wasm->dataSegments.size());
for (auto& segment : wasm->dataSegments) {
uint32_t flags = 0;
Index memoryIndex = 0;
if (segment->isPassive) {
flags |= BinaryConsts::IsPassive;
} else {
memoryIndex = getMemoryIndex(segment->memory);
if (memoryIndex) {
flags |= BinaryConsts::HasIndex;
}
}
o << U32LEB(flags);
if (!segment->isPassive) {
if (memoryIndex) {
o << U32LEB(memoryIndex);
}
writeExpression(segment->offset);
o << int8_t(BinaryConsts::End);
}
writeInlineBuffer(segment->data.data(), segment->data.size());
}
finishSection(start);
}
uint32_t WasmBinaryWriter::getFunctionIndex(Name name) const {
auto it = indexes.functionIndexes.find(name);
assert(it != indexes.functionIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getTableIndex(Name name) const {
auto it = indexes.tableIndexes.find(name);
assert(it != indexes.tableIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getMemoryIndex(Name name) const {
auto it = indexes.memoryIndexes.find(name);
assert(it != indexes.memoryIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getGlobalIndex(Name name) const {
auto it = indexes.globalIndexes.find(name);
assert(it != indexes.globalIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getTagIndex(Name name) const {
auto it = indexes.tagIndexes.find(name);
assert(it != indexes.tagIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getDataSegmentIndex(Name name) const {
auto it = indexes.dataIndexes.find(name);
assert(it != indexes.dataIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getElementSegmentIndex(Name name) const {
auto it = indexes.elemIndexes.find(name);
assert(it != indexes.elemIndexes.end());
return it->second;
}
uint32_t WasmBinaryWriter::getTypeIndex(HeapType type) const {
auto it = indexedTypes.indices.find(type);
#ifndef NDEBUG
if (it == indexedTypes.indices.end()) {
std::cout << "Missing type: " << type << '\n';
assert(0);
}
#endif
return it->second;
}
uint32_t WasmBinaryWriter::getSignatureIndex(Signature sig) const {
auto it = signatureIndexes.find(sig);
#ifndef NDEBUG
if (it == signatureIndexes.end()) {
std::cout << "Missing signature: " << sig << '\n';
assert(0);
}
#endif
return it->second;
}
uint32_t WasmBinaryWriter::getStringIndex(Name string) const {
auto it = stringIndexes.find(string);
assert(it != stringIndexes.end());
return it->second;
}
void WasmBinaryWriter::writeTableDeclarations() {
if (importInfo->getNumDefinedTables() == 0) {
// std::cerr << std::endl << "(WasmBinaryWriter::writeTableDeclarations) No
// defined tables found. skipping" << std::endl;
return;
}
auto start = startSection(BinaryConsts::Section::Table);
auto num = importInfo->getNumDefinedTables();
o << U32LEB(num);
ModuleUtils::iterDefinedTables(*wasm, [&](Table* table) {
writeType(table->type);
writeResizableLimits(table->initial,
table->max,
table->hasMax(),
/*shared=*/false,
table->is64());
});
finishSection(start);
}
void WasmBinaryWriter::writeElementSegments() {
size_t elemCount = wasm->elementSegments.size();
auto needingElemDecl = TableUtils::getFunctionsNeedingElemDeclare(*wasm);
if (!needingElemDecl.empty()) {
elemCount++;
}
if (elemCount == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Element);
o << U32LEB(elemCount);
Type funcref = Type(HeapType::func, Nullable);
for (auto& segment : wasm->elementSegments) {
Index tableIdx = 0;
bool isPassive = segment->table.isNull();
// If the segment is MVP, we can use the shorter form.
bool usesExpressions = TableUtils::usesExpressions(segment.get(), wasm);
// The table index can and should be elided for active segments of table 0
// when table 0 has type funcref. This was the only type of segment
// supported by the MVP, which also did not support table indices in the
// segment encoding.
bool hasTableIndex = false;
if (!isPassive) {
tableIdx = getTableIndex(segment->table);
hasTableIndex =
tableIdx > 0 || wasm->getTable(segment->table)->type != funcref;
}
uint32_t flags = 0;
if (usesExpressions) {
flags |= BinaryConsts::UsesExpressions;
}
if (isPassive) {
flags |= BinaryConsts::IsPassive;
} else if (hasTableIndex) {
flags |= BinaryConsts::HasIndex;
}
o << U32LEB(flags);
if (!isPassive) {
if (hasTableIndex) {
o << U32LEB(tableIdx);
}
writeExpression(segment->offset);
o << int8_t(BinaryConsts::End);
}
if (isPassive || hasTableIndex) {
if (usesExpressions) {
// elemType
writeType(segment->type);
} else {
// MVP elemKind of funcref
o << U32LEB(0);
}
}
o << U32LEB(segment->data.size());
if (usesExpressions) {
for (auto* item : segment->data) {
writeExpression(item);
o << int8_t(BinaryConsts::End);
}
} else {
for (auto& item : segment->data) {
// We've ensured that all items are ref.func.
auto& name = item->cast<RefFunc>()->func;
o << U32LEB(getFunctionIndex(name));
}
}
}
if (!needingElemDecl.empty()) {
o << U32LEB(BinaryConsts::IsPassive | BinaryConsts::IsDeclarative);
o << U32LEB(0); // type (indicating funcref)
o << U32LEB(needingElemDecl.size());
for (auto name : needingElemDecl) {
o << U32LEB(indexes.functionIndexes[name]);
}
}
finishSection(start);
}
void WasmBinaryWriter::writeTags() {
if (importInfo->getNumDefinedTags() == 0) {
return;
}
auto start = startSection(BinaryConsts::Section::Tag);
auto num = importInfo->getNumDefinedTags();
o << U32LEB(num);
ModuleUtils::iterDefinedTags(*wasm, [&](Tag* tag) {
o << uint8_t(0); // Reserved 'attribute' field. Always 0.
o << U32LEB(getTypeIndex(tag->sig));
});
finishSection(start);
}
void WasmBinaryWriter::writeNames() {
auto start = startSection(BinaryConsts::Section::Custom);
writeInlineString(BinaryConsts::CustomSections::Name);
// module name
if (emitModuleName && wasm->name.is()) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameModule);
writeEscapedName(wasm->name.str);
finishSubsection(substart);
}
if (!debugInfo) {
// We were only writing the module name.
finishSection(start);
return;
}
// function names
{
std::vector<std::pair<Index, Function*>> functionsWithNames;
Index checked = 0;
auto check = [&](Function* curr) {
if (curr->hasExplicitName) {
functionsWithNames.push_back({checked, curr});
}
checked++;
};
ModuleUtils::iterImportedFunctions(*wasm, check);
ModuleUtils::iterDefinedFunctions(*wasm, check);
assert(checked == indexes.functionIndexes.size());
if (functionsWithNames.size() > 0) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameFunction);
o << U32LEB(functionsWithNames.size());
for (auto& [index, global] : functionsWithNames) {
o << U32LEB(index);
writeEscapedName(global->name.str);
}
finishSubsection(substart);
}
}
// local names
{
// Find all functions with at least one local name and only emit the
// subsection if there is at least one.
std::vector<std::pair<Index, Function*>> functionsWithLocalNames;
Index checked = 0;
auto check = [&](Function* curr) {
auto numLocals = curr->getNumLocals();
for (Index i = 0; i < numLocals; ++i) {
if (curr->hasLocalName(i)) {
functionsWithLocalNames.push_back({checked, curr});
break;
}
}
checked++;
};
ModuleUtils::iterImportedFunctions(*wasm, check);
ModuleUtils::iterDefinedFunctions(*wasm, check);
assert(checked == indexes.functionIndexes.size());
if (functionsWithLocalNames.size() > 0) {
// Otherwise emit those functions but only include locals with a name.
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameLocal);
o << U32LEB(functionsWithLocalNames.size());
Index emitted = 0;
for (auto& [index, func] : functionsWithLocalNames) {
// Pairs of (local index in IR, name).
std::vector<std::pair<Index, Name>> localsWithNames;
auto numLocals = func->getNumLocals();
for (Index indexInFunc = 0; indexInFunc < numLocals; ++indexInFunc) {
if (func->hasLocalName(indexInFunc)) {
Index indexInBinary;
auto iter = funcMappedLocals.find(func->name);
if (iter != funcMappedLocals.end()) {
// TODO: handle multivalue
indexInBinary = iter->second[{indexInFunc, 0}];
} else {
// No data on funcMappedLocals. That is only possible if we are an
// imported function, where there are no locals to map, and in
// that case the index is unchanged anyhow: parameters always have
// the same index, they are not mapped in any way.
assert(func->imported());
indexInBinary = indexInFunc;
}
localsWithNames.push_back(
{indexInBinary, func->getLocalName(indexInFunc)});
}
}
assert(localsWithNames.size());
std::sort(localsWithNames.begin(), localsWithNames.end());
o << U32LEB(index);
o << U32LEB(localsWithNames.size());
for (auto& [indexInBinary, name] : localsWithNames) {
o << U32LEB(indexInBinary);
writeEscapedName(name.str);
}
emitted++;
}
assert(emitted == functionsWithLocalNames.size());
finishSubsection(substart);
}
}
// type names
{
std::vector<HeapType> namedTypes;
for (auto type : indexedTypes.types) {
if (wasm->typeNames.count(type) && wasm->typeNames[type].name.is()) {
namedTypes.push_back(type);
}
}
if (!namedTypes.empty()) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameType);
o << U32LEB(namedTypes.size());
for (auto type : namedTypes) {
o << U32LEB(indexedTypes.indices[type]);
writeEscapedName(wasm->typeNames[type].name.str);
}
finishSubsection(substart);
}
}
// table names
{
std::vector<std::pair<Index, Table*>> tablesWithNames;
Index checked = 0;
auto check = [&](Table* curr) {
if (curr->hasExplicitName) {
tablesWithNames.push_back({checked, curr});
}
checked++;
};
ModuleUtils::iterImportedTables(*wasm, check);
ModuleUtils::iterDefinedTables(*wasm, check);
assert(checked == indexes.tableIndexes.size());
if (tablesWithNames.size() > 0) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameTable);
o << U32LEB(tablesWithNames.size());
for (auto& [index, table] : tablesWithNames) {
o << U32LEB(index);
writeEscapedName(table->name.str);
}
finishSubsection(substart);
}
}
// memory names
{
std::vector<std::pair<Index, Memory*>> memoriesWithNames;
Index checked = 0;
auto check = [&](Memory* curr) {
if (curr->hasExplicitName) {
memoriesWithNames.push_back({checked, curr});
}
checked++;
};
ModuleUtils::iterImportedMemories(*wasm, check);
ModuleUtils::iterDefinedMemories(*wasm, check);
assert(checked == indexes.memoryIndexes.size());
if (memoriesWithNames.size() > 0) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameMemory);
o << U32LEB(memoriesWithNames.size());
for (auto& [index, memory] : memoriesWithNames) {
o << U32LEB(index);
writeEscapedName(memory->name.str);
}
finishSubsection(substart);
}
}
// global names
{
std::vector<std::pair<Index, Global*>> globalsWithNames;
Index checked = 0;
auto check = [&](Global* curr) {
if (curr->hasExplicitName) {
globalsWithNames.push_back({checked, curr});
}
checked++;
};
ModuleUtils::iterImportedGlobals(*wasm, check);
ModuleUtils::iterDefinedGlobals(*wasm, check);
assert(checked == indexes.globalIndexes.size());
if (globalsWithNames.size() > 0) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameGlobal);
o << U32LEB(globalsWithNames.size());
for (auto& [index, global] : globalsWithNames) {
o << U32LEB(index);
writeEscapedName(global->name.str);
}
finishSubsection(substart);
}
}
// elem segment names
{
std::vector<std::pair<Index, ElementSegment*>> elemsWithNames;
Index checked = 0;
for (auto& curr : wasm->elementSegments) {
if (curr->hasExplicitName) {
elemsWithNames.push_back({checked, curr.get()});
}
checked++;
}
assert(checked == indexes.elemIndexes.size());
if (elemsWithNames.size() > 0) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameElem);
o << U32LEB(elemsWithNames.size());
for (auto& [index, elem] : elemsWithNames) {
o << U32LEB(index);
writeEscapedName(elem->name.str);
}
finishSubsection(substart);
}
}
// data segment names
{
Index count = 0;
for (auto& seg : wasm->dataSegments) {
if (seg->hasExplicitName) {
count++;
}
}
if (count) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameData);
o << U32LEB(count);
for (Index i = 0; i < wasm->dataSegments.size(); i++) {
auto& seg = wasm->dataSegments[i];
if (seg->hasExplicitName) {
o << U32LEB(i);
writeEscapedName(seg->name.str);
}
}
finishSubsection(substart);
}
}
// TODO: label, type, and element names
// see: https://github.com/WebAssembly/extended-name-section
// GC field names
if (wasm->features.hasGC()) {
std::vector<HeapType> relevantTypes;
for (auto& type : indexedTypes.types) {
if (type.isStruct() && wasm->typeNames.count(type) &&
!wasm->typeNames[type].fieldNames.empty()) {
relevantTypes.push_back(type);
}
}
if (!relevantTypes.empty()) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameField);
o << U32LEB(relevantTypes.size());
for (Index i = 0; i < relevantTypes.size(); i++) {
auto type = relevantTypes[i];
o << U32LEB(indexedTypes.indices[type]);
std::unordered_map<Index, Name>& fieldNames =
wasm->typeNames.at(type).fieldNames;
o << U32LEB(fieldNames.size());
for (auto& [index, name] : fieldNames) {
o << U32LEB(index);
writeEscapedName(name.str);
}
}
finishSubsection(substart);
}
}
// tag names
if (!wasm->tags.empty()) {
Index count = 0;
for (auto& tag : wasm->tags) {
if (tag->hasExplicitName) {
count++;
}
}
if (count) {
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::NameTag);
o << U32LEB(count);
for (Index i = 0; i < wasm->tags.size(); i++) {
auto& tag = wasm->tags[i];
if (tag->hasExplicitName) {
o << U32LEB(i);
writeEscapedName(tag->name.str);
}
}
finishSubsection(substart);
}
}
finishSection(start);
}
void WasmBinaryWriter::writeSourceMapUrl() {
auto start = startSection(BinaryConsts::Section::Custom);
writeInlineString(BinaryConsts::CustomSections::SourceMapUrl);
writeInlineString(sourceMapUrl.c_str());
finishSection(start);
}
void WasmBinaryWriter::writeSymbolMap() {
std::ofstream file(symbolMap);
auto write = [&](Function* func) {
file << getFunctionIndex(func->name) << ":" << func->name.str << std::endl;
};
ModuleUtils::iterImportedFunctions(*wasm, write);
ModuleUtils::iterDefinedFunctions(*wasm, write);
file.close();
}
void WasmBinaryWriter::initializeDebugInfo() {
lastDebugLocation = {0, /* lineNumber = */ 1, 0, std::nullopt};
}
void WasmBinaryWriter::writeSourceMapProlog() {
*sourceMap << "{\"version\":3,";
for (const auto& section : wasm->customSections) {
if (section.name == BinaryConsts::CustomSections::BuildId) {
U32LEB ret;
size_t pos = 0;
ret.read([&]() { return section.data[pos++]; });
if (section.data.size() != pos + ret.value) {
std::cerr
<< "warning: build id section with an incorrect size detected!\n";
break;
}
*sourceMap << "\"debugId\":\"";
for (size_t i = pos; i < section.data.size(); i++) {
*sourceMap << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<int>(static_cast<uint8_t>(section.data[i]));
}
*sourceMap << "\",";
break;
}
}
*sourceMap << "\"sources\":[";
for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) {
if (i > 0) {
*sourceMap << ",";
}
// TODO respect JSON string encoding, e.g. quotes and control chars.
*sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\"";
}
*sourceMap << "],\"names\":[";
for (size_t i = 0; i < wasm->debugInfoSymbolNames.size(); i++) {
if (i > 0) {
*sourceMap << ",";
}
// TODO respect JSON string encoding, e.g. quotes and control chars.
*sourceMap << "\"" << wasm->debugInfoSymbolNames[i] << "\"";
}
*sourceMap << "],\"mappings\":\"";
}
static void writeBase64VLQ(std::ostream& out, int32_t n) {
uint32_t value = n >= 0 ? n << 1 : ((-n) << 1) | 1;
while (1) {
uint32_t digit = value & 0x1F;
value >>= 5;
if (!value) {
// last VLQ digit -- base64 codes 'A'..'Z', 'a'..'f'
out << char(digit < 26 ? 'A' + digit : 'a' + digit - 26);
break;
}
// 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 ? '+' : '/');
}
}
void WasmBinaryWriter::writeSourceMapEpilog() {
// write source map entries
size_t lastOffset = 0;
BinaryLocation lastFileIndex = 0;
BinaryLocation lastLineNumber = 1;
BinaryLocation lastColumnNumber = 0;
BinaryLocation lastSymbolNameIndex = 0;
for (const auto& [offset, loc] : sourceMapLocations) {
if (lastOffset > 0) {
*sourceMap << ",";
}
writeBase64VLQ(*sourceMap, int32_t(offset - lastOffset));
lastOffset = offset;
if (loc) {
writeBase64VLQ(*sourceMap, int32_t(loc->fileIndex - lastFileIndex));
lastFileIndex = loc->fileIndex;
writeBase64VLQ(*sourceMap, int32_t(loc->lineNumber - lastLineNumber));
lastLineNumber = loc->lineNumber;
writeBase64VLQ(*sourceMap, int32_t(loc->columnNumber - lastColumnNumber));
lastColumnNumber = loc->columnNumber;
if (loc->symbolNameIndex) {
writeBase64VLQ(*sourceMap,
int32_t(*loc->symbolNameIndex - lastSymbolNameIndex));
lastSymbolNameIndex = *loc->symbolNameIndex;
}
}
}
*sourceMap << "\"}";
}
void WasmBinaryWriter::writeLateCustomSections() {
for (auto& section : wasm->customSections) {
if (section.name != BinaryConsts::CustomSections::Dylink) {
writeCustomSection(section);
}
}
}
void WasmBinaryWriter::writeCustomSection(const CustomSection& section) {
auto start = startSection(BinaryConsts::Custom);
writeInlineString(section.name.c_str());
for (size_t i = 0; i < section.data.size(); i++) {
o << uint8_t(section.data[i]);
}
finishSection(start);
}
void WasmBinaryWriter::writeFeaturesSection() {
if (!wasm->hasFeaturesSection || wasm->features.isMVP()) {
return;
}
// TODO(tlively): unify feature names with rest of toolchain and use
// FeatureSet::toString()
auto toString = [](FeatureSet::Feature f) {
switch (f) {
case FeatureSet::Atomics:
return BinaryConsts::CustomSections::AtomicsFeature;
case FeatureSet::MutableGlobals:
return BinaryConsts::CustomSections::MutableGlobalsFeature;
case FeatureSet::TruncSat:
return BinaryConsts::CustomSections::TruncSatFeature;
case FeatureSet::SIMD:
return BinaryConsts::CustomSections::SIMD128Feature;
case FeatureSet::BulkMemory:
return BinaryConsts::CustomSections::BulkMemoryFeature;
case FeatureSet::SignExt:
return BinaryConsts::CustomSections::SignExtFeature;
case FeatureSet::ExceptionHandling:
return BinaryConsts::CustomSections::ExceptionHandlingFeature;
case FeatureSet::TailCall:
return BinaryConsts::CustomSections::TailCallFeature;
case FeatureSet::ReferenceTypes:
return BinaryConsts::CustomSections::ReferenceTypesFeature;
case FeatureSet::Multivalue:
return BinaryConsts::CustomSections::MultivalueFeature;
case FeatureSet::GC:
return BinaryConsts::CustomSections::GCFeature;
case FeatureSet::Memory64:
return BinaryConsts::CustomSections::Memory64Feature;
case FeatureSet::RelaxedSIMD:
return BinaryConsts::CustomSections::RelaxedSIMDFeature;
case FeatureSet::ExtendedConst:
return BinaryConsts::CustomSections::ExtendedConstFeature;
case FeatureSet::Strings:
return BinaryConsts::CustomSections::StringsFeature;
case FeatureSet::MultiMemory:
return BinaryConsts::CustomSections::MultiMemoryFeature;
case FeatureSet::TypedContinuations:
return BinaryConsts::CustomSections::TypedContinuationsFeature;
case FeatureSet::SharedEverything:
return BinaryConsts::CustomSections::SharedEverythingFeature;
case FeatureSet::FP16:
return BinaryConsts::CustomSections::FP16Feature;
case FeatureSet::None:
case FeatureSet::Default:
case FeatureSet::All:
break;
}
WASM_UNREACHABLE("unexpected feature flag");
};
std::vector<const char*> features;
wasm->features.iterFeatures(
[&](FeatureSet::Feature f) { features.push_back(toString(f)); });
auto start = startSection(BinaryConsts::Custom);
writeInlineString(BinaryConsts::CustomSections::TargetFeatures);
o << U32LEB(features.size());
for (auto& f : features) {
o << uint8_t(BinaryConsts::FeatureUsed);
writeInlineString(f);
}
finishSection(start);
}
void WasmBinaryWriter::writeLegacyDylinkSection() {
if (!wasm->dylinkSection) {
return;
}
auto start = startSection(BinaryConsts::Custom);
writeInlineString(BinaryConsts::CustomSections::Dylink);
o << U32LEB(wasm->dylinkSection->memorySize);
o << U32LEB(wasm->dylinkSection->memoryAlignment);
o << U32LEB(wasm->dylinkSection->tableSize);
o << U32LEB(wasm->dylinkSection->tableAlignment);
o << U32LEB(wasm->dylinkSection->neededDynlibs.size());
for (auto& neededDynlib : wasm->dylinkSection->neededDynlibs) {
writeInlineString(neededDynlib.str);
}
finishSection(start);
}
void WasmBinaryWriter::writeDylinkSection() {
if (!wasm->dylinkSection) {
return;
}
if (wasm->dylinkSection->isLegacy) {
writeLegacyDylinkSection();
return;
}
auto start = startSection(BinaryConsts::Custom);
writeInlineString(BinaryConsts::CustomSections::Dylink0);
auto substart =
startSubsection(BinaryConsts::CustomSections::Subsection::DylinkMemInfo);
o << U32LEB(wasm->dylinkSection->memorySize);
o << U32LEB(wasm->dylinkSection->memoryAlignment);
o << U32LEB(wasm->dylinkSection->tableSize);
o << U32LEB(wasm->dylinkSection->tableAlignment);
finishSubsection(substart);
if (wasm->dylinkSection->neededDynlibs.size()) {
substart =
startSubsection(BinaryConsts::CustomSections::Subsection::DylinkNeeded);
o << U32LEB(wasm->dylinkSection->neededDynlibs.size());
for (auto& neededDynlib : wasm->dylinkSection->neededDynlibs) {
writeInlineString(neededDynlib.str);
}
finishSubsection(substart);
}
writeData(wasm->dylinkSection->tail.data(), wasm->dylinkSection->tail.size());
finishSection(start);
}
void WasmBinaryWriter::writeDebugLocation(const Function::DebugLocation& loc) {
if (loc == lastDebugLocation) {
return;
}
auto offset = o.size();
sourceMapLocations.emplace_back(offset, &loc);
lastDebugLocation = loc;
}
void WasmBinaryWriter::writeNoDebugLocation() {
// Emit an indication that there is no debug location there (so that
// we do not get "smeared" with debug info from anything before or
// after us).
//
// We don't need to write repeated "no debug info" indications, as a
// single one is enough to make it clear that the debug information
// before us is valid no longer. We also don't need to write one if
// there is nothing before us.
if (!sourceMapLocations.empty() &&
sourceMapLocations.back().second != nullptr) {
sourceMapLocations.emplace_back(o.size(), nullptr);
// Initialize the state of debug info to indicate there is no current
// debug info relevant. This sets |lastDebugLocation| to a dummy value,
// so that later places with debug info can see that they differ from
// it (without this, if we had some debug info, then a nullptr for none,
// and then the same debug info, we could get confused).
initializeDebugInfo();
}
}
void WasmBinaryWriter::writeDebugLocation(Expression* curr, Function* func) {
if (sourceMap) {
auto& debugLocations = func->debugLocations;
auto iter = debugLocations.find(curr);
if (iter != debugLocations.end() && iter->second) {
// There is debug information here, write it out.
writeDebugLocation(*(iter->second));
} else {
// This expression has no debug location.
writeNoDebugLocation();
}
}
// If this is an instruction in a function, and if the original wasm had
// binary locations tracked, then track it in the output as well.
if (func && !func->expressionLocations.empty()) {
binaryLocations.expressions[curr] =
BinaryLocations::Span{BinaryLocation(o.size()), 0};
binaryLocationTrackedExpressionsForFunc.push_back(curr);
}
}
void WasmBinaryWriter::writeDebugLocationEnd(Expression* curr, Function* func) {
if (func && !func->expressionLocations.empty()) {
auto& span = binaryLocations.expressions.at(curr);
span.end = o.size();
}
}
void WasmBinaryWriter::writeExtraDebugLocation(Expression* curr,
Function* func,
size_t id) {
if (func && !func->expressionLocations.empty()) {
binaryLocations.delimiters[curr][id] = o.size();
}
}
void WasmBinaryWriter::writeData(const char* data, size_t size) {
for (size_t i = 0; i < size; i++) {
o << int8_t(data[i]);
}
}
void WasmBinaryWriter::writeInlineString(std::string_view name) {
o << U32LEB(name.size());
writeData(name.data(), name.size());
}
static bool isHexDigit(char ch) {
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F');
}
static int decodeHexNibble(char ch) {
return ch <= '9' ? ch & 15 : (ch & 15) + 9;
}
void WasmBinaryWriter::writeEscapedName(std::string_view name) {
if (name.find('\\') == std::string_view::npos) {
writeInlineString(name);
return;
}
// decode escaped by escapeName (see below) function names
std::string unescaped;
for (size_t i = 0; i < name.size();) {
char ch = name[i++];
// support only `\xx` escapes; ignore invalid or unsupported escapes
if (ch != '\\' || i + 1 >= name.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])));
i += 2;
}
writeInlineString({unescaped.data(), unescaped.size()});
}
void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) {
o << U32LEB(size);
writeData(data, size);
}
void WasmBinaryWriter::writeType(Type type) {
if (type.isRef()) {
// The only reference types allowed without GC are funcref, externref, and
// exnref. We internally use more refined versions of those types, but we
// cannot emit those without GC.
if (!wasm->features.hasGC()) {
auto ht = type.getHeapType();
if (ht.isMaybeShared(HeapType::string)) {
// Do not overgeneralize stringref to anyref. We have tests that when a
// stringref is expected, we actually get a stringref. If we see a
// string, the stringref feature must be enabled.
type = Type(HeapTypes::string.getBasic(ht.getShared()), Nullable);
} else {
// Only the top type (func, extern, exn) is available, and only the
// nullable version.
type = Type(type.getHeapType().getTop(), Nullable);
}
}
auto heapType = type.getHeapType();
if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) {
switch (heapType.getBasic(Unshared)) {
case HeapType::ext:
o << S32LEB(BinaryConsts::EncodedType::externref);
return;
case HeapType::any:
o << S32LEB(BinaryConsts::EncodedType::anyref);
return;
case HeapType::func:
o << S32LEB(BinaryConsts::EncodedType::funcref);
return;
case HeapType::cont:
o << S32LEB(BinaryConsts::EncodedType::contref);
return;
case HeapType::eq:
o << S32LEB(BinaryConsts::EncodedType::eqref);
return;
case HeapType::i31:
o << S32LEB(BinaryConsts::EncodedType::i31ref);
return;
case HeapType::struct_:
o << S32LEB(BinaryConsts::EncodedType::structref);
return;
case HeapType::array:
o << S32LEB(BinaryConsts::EncodedType::arrayref);
return;
case HeapType::exn:
o << S32LEB(BinaryConsts::EncodedType::exnref);
return;
case HeapType::string:
o << S32LEB(BinaryConsts::EncodedType::stringref);
return;
case HeapType::none:
o << S32LEB(BinaryConsts::EncodedType::nullref);
return;
case HeapType::noext:
o << S32LEB(BinaryConsts::EncodedType::nullexternref);
return;
case HeapType::nofunc:
o << S32LEB(BinaryConsts::EncodedType::nullfuncref);
return;
case HeapType::noexn:
o << S32LEB(BinaryConsts::EncodedType::nullexnref);
return;
case HeapType::nocont:
o << S32LEB(BinaryConsts::EncodedType::nullcontref);
return;
}
}
if (type.isNullable()) {
o << S32LEB(BinaryConsts::EncodedType::nullable);
} else {
o << S32LEB(BinaryConsts::EncodedType::nonnullable);
}
writeHeapType(type.getHeapType());
return;
}
int ret = 0;
TODO_SINGLE_COMPOUND(type);
switch (type.getBasic()) {
// None only used for block signatures. TODO: Separate out?
case Type::none:
ret = BinaryConsts::EncodedType::Empty;
break;
case Type::i32:
ret = BinaryConsts::EncodedType::i32;
break;
case Type::i64:
ret = BinaryConsts::EncodedType::i64;
break;
case Type::f32:
ret = BinaryConsts::EncodedType::f32;
break;
case Type::f64:
ret = BinaryConsts::EncodedType::f64;
break;
case Type::v128:
ret = BinaryConsts::EncodedType::v128;
break;
default:
WASM_UNREACHABLE("unexpected type");
}
o << S32LEB(ret);
}
void WasmBinaryWriter::writeHeapType(HeapType type) {
// ref.null always has a bottom heap type in Binaryen IR, but those types are
// only actually valid with GC. Otherwise, emit the corresponding valid top
// types instead.
if (!wasm->features.hasGC()) {
type = type.getTop();
}
if (!type.isBasic()) {
o << S64LEB(getTypeIndex(type)); // TODO: Actually s33
return;
}
int ret = 0;
if (type.isShared()) {
o << S32LEB(BinaryConsts::EncodedType::Shared);
}
switch (type.getBasic(Unshared)) {
case HeapType::ext:
ret = BinaryConsts::EncodedHeapType::ext;
break;
case HeapType::func:
ret = BinaryConsts::EncodedHeapType::func;
break;
case HeapType::cont:
ret = BinaryConsts::EncodedHeapType::cont;
break;
case HeapType::any:
ret = BinaryConsts::EncodedHeapType::any;
break;
case HeapType::eq:
ret = BinaryConsts::EncodedHeapType::eq;
break;
case HeapType::i31:
ret = BinaryConsts::EncodedHeapType::i31;
break;
case HeapType::struct_:
ret = BinaryConsts::EncodedHeapType::struct_;
break;
case HeapType::array:
ret = BinaryConsts::EncodedHeapType::array;
break;
case HeapType::exn:
ret = BinaryConsts::EncodedHeapType::exn;
break;
case HeapType::string:
ret = BinaryConsts::EncodedHeapType::string;
break;
case HeapType::none:
ret = BinaryConsts::EncodedHeapType::none;
break;
case HeapType::noext:
ret = BinaryConsts::EncodedHeapType::noext;
break;
case HeapType::nofunc:
ret = BinaryConsts::EncodedHeapType::nofunc;
break;
case HeapType::noexn:
ret = BinaryConsts::EncodedHeapType::noexn;
break;
case HeapType::nocont:
ret = BinaryConsts::EncodedHeapType::nocont;
break;
}
o << S64LEB(ret); // TODO: Actually s33
}
void WasmBinaryWriter::writeIndexedHeapType(HeapType type) {
o << U32LEB(getTypeIndex(type));
}
void WasmBinaryWriter::writeField(const Field& field) {
if (field.type == Type::i32 && field.packedType != Field::not_packed) {
if (field.packedType == Field::i8) {
o << S32LEB(BinaryConsts::EncodedType::i8);
} else if (field.packedType == Field::i16) {
o << S32LEB(BinaryConsts::EncodedType::i16);
} else {
WASM_UNREACHABLE("invalid packed type");
}
} else {
writeType(field.type);
}
o << U32LEB(field.mutable_);
}
// reader
WasmBinaryReader::WasmBinaryReader(Module& wasm,
FeatureSet features,
const std::vector<char>& input,
const std::vector<char>& sourceMap)
: wasm(wasm), allocator(wasm.allocator), input(input), builder(wasm),
sourceMapReader(sourceMap) {
wasm.features = features;
}
bool WasmBinaryReader::hasDWARFSections() {
size_t pos = 0;
getInt32(pos); // magic
getInt32(pos); // version
bool has = false;
while (more(pos)) {
uint8_t sectionCode = getInt8(pos);
uint32_t payloadLen = getU32LEB(pos);
if (uint64_t(pos) + uint64_t(payloadLen) > input.size()) {
throwError(pos, "Section extends beyond end of input");
}
auto oldPos = pos;
if (sectionCode == BinaryConsts::Section::Custom) {
auto sectionName = getInlineString(pos);
if (Debug::isDWARFSection(sectionName)) {
has = true;
break;
}
}
pos = oldPos + payloadLen;
}
return has;
}
void WasmBinaryReader::read() {
if (DWARF) {
// In order to update dwarf, we must store info about each IR node's
// binary position. This has noticeable memory overhead, so we don't do it
// by default: the user must request it by setting "DWARF", and even if so
// we scan ahead to see that there actually *are* DWARF sections, so that
// we don't do unnecessary work.
if (!hasDWARFSections()) {
DWARF = false;
}
}
// Skip ahead and read the name section so we know what names to use when we
// construct module elements.
if (debugInfo) {
findAndReadNames();
}
size_t pos = 0;
readHeader(pos);
sourceMapReader.readHeader(wasm);
// Read sections until the end
while (more(pos)) {
uint8_t sectionCode = getInt8(pos);
uint32_t payloadLen = getU32LEB(pos);
if (uint64_t(pos) + uint64_t(payloadLen) > input.size()) {
throwError(pos, "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::Custom &&
!seenSections.insert(sectionCode).second) {
throwError(pos,
"section seen more than once: " + std::to_string(sectionCode));
}
switch (sectionCode) {
case BinaryConsts::Section::Start:
readStart(pos);
break;
case BinaryConsts::Section::Memory:
readMemories(pos);
break;
case BinaryConsts::Section::Type:
readTypes(pos);
break;
case BinaryConsts::Section::Import:
readImports(pos);
break;
case BinaryConsts::Section::Function:
readFunctionSignatures(pos);
break;
case BinaryConsts::Section::Code:
if (DWARF) {
codeSectionLocation = pos;
}
readFunctions(pos);
break;
case BinaryConsts::Section::Export:
readExports(pos);
break;
case BinaryConsts::Section::Element:
readElementSegments(pos);
break;
case BinaryConsts::Section::Strings:
readStrings(pos);
break;
case BinaryConsts::Section::Global:
readGlobals(pos);
break;
case BinaryConsts::Section::Data:
readDataSegments(pos);
break;
case BinaryConsts::Section::DataCount:
readDataSegmentCount(pos);
break;
case BinaryConsts::Section::Table:
readTableDeclarations(pos);
break;
case BinaryConsts::Section::Tag:
readTags(pos);
break;
case BinaryConsts::Section::Custom: {
readCustomSection(pos, payloadLen);
if (pos > oldPos + payloadLen) {
throwError(
pos,
"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;
break;
}
default:
throwError(pos,
std::string("unrecognized section ID: ") +
std::to_string(sectionCode));
}
// make sure we advanced exactly past this section
if (pos != oldPos + payloadLen) {
throwError(pos,
"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));
}
}
validateBinary(pos);
}
void WasmBinaryReader::readCustomSection(size_t& pos, size_t payloadLen) {
auto oldPos = pos;
Name sectionName = getInlineString(pos);
size_t read = pos - oldPos;
if (read > payloadLen) {
throwError(pos, "bad user section size");
}
payloadLen -= read;
if (sectionName.equals(BinaryConsts::CustomSections::Name)) {
// We already read the name section before anything else.
pos += payloadLen;
} else if (sectionName.equals(BinaryConsts::CustomSections::TargetFeatures)) {
readFeatures(pos, payloadLen);
} else if (sectionName.equals(BinaryConsts::CustomSections::Dylink)) {
readDylink(pos, payloadLen);
} else if (sectionName.equals(BinaryConsts::CustomSections::Dylink0)) {
readDylink0(pos, payloadLen);
} else {
// an unfamiliar custom section
if (sectionName.equals(BinaryConsts::CustomSections::Linking)) {
std::cerr
<< "warning: linking section is present, so this is not a standard "
"wasm file - binaryen cannot handle this properly!\n";
}
wasm.customSections.resize(wasm.customSections.size() + 1);
auto& section = wasm.customSections.back();
section.name = sectionName.str;
auto data = getByteView(pos, payloadLen);
section.data = {data.begin(), data.end()};
}
}
std::string_view WasmBinaryReader::getByteView(size_t& pos, size_t size) {
if (size > input.size() || pos > input.size() - size) {
throwError(pos, "unexpected end of input");
}
pos += size;
return {input.data() + (pos - size), size};
}
uint8_t WasmBinaryReader::getInt8(size_t& pos) {
if (!more(pos)) {
throwError(pos, "unexpected end of input");
}
return input[pos++];
}
uint16_t WasmBinaryReader::getInt16(size_t& pos) {
auto ret = uint16_t(getInt8(pos));
ret |= uint16_t(getInt8(pos)) << 8;
return ret;
}
uint32_t WasmBinaryReader::getInt32(size_t& pos) {
auto ret = uint32_t(getInt16(pos));
ret |= uint32_t(getInt16(pos)) << 16;
return ret;
}
uint64_t WasmBinaryReader::getInt64(size_t& pos) {
auto ret = uint64_t(getInt32(pos));
ret |= uint64_t(getInt32(pos)) << 32;
return ret;
}
uint8_t WasmBinaryReader::getLaneIndex(size_t& pos, size_t lanes) {
auto ret = getInt8(pos);
if (ret >= lanes) {
throwError(pos, "Illegal lane index");
}
return ret;
}
Literal WasmBinaryReader::getFloat32Literal(size_t& pos) {
auto ret = Literal(getInt32(pos));
ret = ret.castToF32();
return ret;
}
Literal WasmBinaryReader::getFloat64Literal(size_t& pos) {
auto ret = Literal(getInt64(pos));
ret = ret.castToF64();
return ret;
}
Literal WasmBinaryReader::getVec128Literal(size_t& pos) {
std::array<uint8_t, 16> bytes;
for (auto i = 0; i < 16; ++i) {
bytes[i] = getInt8(pos);
}
auto ret = Literal(bytes.data());
return ret;
}
uint32_t WasmBinaryReader::getU32LEB(size_t& pos) {
U32LEB ret;
ret.read([&]() { return getInt8(pos); });
return ret.value;
}
uint64_t WasmBinaryReader::getU64LEB(size_t& pos) {
U64LEB ret;
ret.read([&]() { return getInt8(pos); });
return ret.value;
}
int32_t WasmBinaryReader::getS32LEB(size_t& pos) {
S32LEB ret;
ret.read([&]() { return (int8_t)getInt8(pos); });
return ret.value;
}
int64_t WasmBinaryReader::getS64LEB(size_t& pos) {
S64LEB ret;
ret.read([&]() { return (int8_t)getInt8(pos); });
return ret.value;
}
bool WasmBinaryReader::getBasicType(size_t& pos, int32_t code, Type& out) {
switch (code) {
case BinaryConsts::EncodedType::i32:
out = Type::i32;
return true;
case BinaryConsts::EncodedType::i64:
out = Type::i64;
return true;
case BinaryConsts::EncodedType::f32:
out = Type::f32;
return true;
case BinaryConsts::EncodedType::f64:
out = Type::f64;
return true;
case BinaryConsts::EncodedType::v128:
out = Type::v128;
return true;
case BinaryConsts::EncodedType::funcref:
out = Type(HeapType::func, Nullable);
return true;
case BinaryConsts::EncodedType::contref:
out = Type(HeapType::cont, Nullable);
return true;
case BinaryConsts::EncodedType::externref:
out = Type(HeapType::ext, Nullable);
return true;
case BinaryConsts::EncodedType::anyref:
out = Type(HeapType::any, Nullable);
return true;
case BinaryConsts::EncodedType::eqref:
out = Type(HeapType::eq, Nullable);
return true;
case BinaryConsts::EncodedType::i31ref:
out = Type(HeapType::i31, Nullable);
return true;
case BinaryConsts::EncodedType::structref:
out = Type(HeapType::struct_, Nullable);
return true;
case BinaryConsts::EncodedType::arrayref:
out = Type(HeapType::array, Nullable);
return true;
case BinaryConsts::EncodedType::exnref:
out = Type(HeapType::exn, Nullable);
return true;
case BinaryConsts::EncodedType::stringref:
out = Type(HeapType::string, Nullable);
return true;
case BinaryConsts::EncodedType::nullref:
out = Type(HeapType::none, Nullable);
return true;
case BinaryConsts::EncodedType::nullexternref:
out = Type(HeapType::noext, Nullable);
return true;
case BinaryConsts::EncodedType::nullfuncref:
out = Type(HeapType::nofunc, Nullable);
return true;
case BinaryConsts::EncodedType::nullexnref:
out = Type(HeapType::noexn, Nullable);
return true;
case BinaryConsts::EncodedType::nullcontref:
out = Type(HeapType::nocont, Nullable);
return true;
default:
return false;
}
}
bool WasmBinaryReader::getBasicHeapType(size_t& pos,
int64_t code,
HeapType& out) {
switch (code) {
case BinaryConsts::EncodedHeapType::func:
out = HeapType::func;
return true;
case BinaryConsts::EncodedHeapType::cont:
out = HeapType::cont;
return true;
case BinaryConsts::EncodedHeapType::ext:
out = HeapType::ext;
return true;
case BinaryConsts::EncodedHeapType::any:
out = HeapType::any;
return true;
case BinaryConsts::EncodedHeapType::eq:
out = HeapType::eq;
return true;
case BinaryConsts::EncodedHeapType::i31:
out = HeapType::i31;
return true;
case BinaryConsts::EncodedHeapType::struct_:
out = HeapType::struct_;
return true;
case BinaryConsts::EncodedHeapType::array:
out = HeapType::array;
return true;
case BinaryConsts::EncodedHeapType::exn:
out = HeapType::exn;
return true;
case BinaryConsts::EncodedHeapType::string:
out = HeapType::string;
return true;
case BinaryConsts::EncodedHeapType::none:
out = HeapType::none;
return true;
case BinaryConsts::EncodedHeapType::noext:
out = HeapType::noext;
return true;
case BinaryConsts::EncodedHeapType::nofunc:
out = HeapType::nofunc;
return true;
case BinaryConsts::EncodedHeapType::noexn:
out = HeapType::noexn;
return true;
case BinaryConsts::EncodedHeapType::nocont:
out = HeapType::nocont;
return true;
default:
return false;
}
}
Type WasmBinaryReader::getType(size_t& pos, int initial) {
// Single value types are negative; signature indices are non-negative
if (initial >= 0) {
// TODO: Handle block input types properly.
auto sig = getSignatureByTypeIndex(pos, initial);
if (sig.params != Type::none) {
throwError(pos, "control flow inputs are not supported yet");
}
return sig.results;
}
Type type;
if (getBasicType(pos, initial, type)) {
return type;
}
switch (initial) {
// None only used for block signatures. TODO: Separate out?
case BinaryConsts::EncodedType::Empty:
return Type::none;
case BinaryConsts::EncodedType::nullable:
return Type(getHeapType(pos), Nullable);
case BinaryConsts::EncodedType::nonnullable:
return Type(getHeapType(pos), NonNullable);
default:
throwError(pos, "invalid wasm type: " + std::to_string(initial));
}
WASM_UNREACHABLE("unexpected type");
}
Type WasmBinaryReader::getType(size_t& pos) {
return getType(pos, getS32LEB(pos));
}
HeapType WasmBinaryReader::getHeapType(size_t& pos) {
auto type = getS64LEB(pos); // TODO: Actually s33
// Single heap types are negative; heap type indices are non-negative
if (type >= 0) {
if (size_t(type) >= types.size()) {
throwError(pos, "invalid signature index: " + std::to_string(type));
}
return types[type];
}
auto share = Unshared;
if (type == BinaryConsts::EncodedType::Shared) {
share = Shared;
type = getS64LEB(pos); // TODO: Actually s33
}
HeapType ht;
if (getBasicHeapType(pos, type, ht)) {
return ht.getBasic(share);
} else {
throwError(pos, "invalid wasm heap type: " + std::to_string(type));
}
WASM_UNREACHABLE("unexpected type");
}
HeapType WasmBinaryReader::getIndexedHeapType(size_t& pos) {
auto index = getU32LEB(pos);
if (index >= types.size()) {
throwError(pos, "invalid heap type index: " + std::to_string(index));
}
return types[index];
}
Type WasmBinaryReader::getConcreteType(size_t& pos) {
auto type = getType(pos);
if (!type.isConcrete()) {
throwError(pos, "non-concrete type when one expected");
}
return type;
}
Name WasmBinaryReader::getInlineString(size_t& pos, bool requireValid) {
auto len = getU32LEB(pos);
auto data = getByteView(pos, len);
if (requireValid && !String::isUTF8(data)) {
throwError(pos, "invalid UTF-8 string");
}
return Name(data);
}
void WasmBinaryReader::readHeader(size_t& pos) {
auto magic = getInt32(pos);
if (magic != BinaryConsts::Magic) {
throwError(pos, "suprising value");
}
auto version = getInt32(pos);
if (version != BinaryConsts::Version) {
if (version == 0x1000d) {
throwError(pos,
"this looks like a wasm component, which Binaryen does not "
"support yet (see "
"https://github.com/WebAssembly/binaryen/issues/6728)");
}
throwError(pos, "invalid version");
}
}
void WasmBinaryReader::readStart(size_t& pos) {
startIndex = getU32LEB(pos);
wasm.start = getFunctionName(pos, startIndex);
}
static Name makeName(std::string prefix, size_t counter) {
return Name(prefix + std::to_string(counter));
}
// Look up a name from the names section or use a validated version of the
// provided name. Return the name and whether it is explicit in the input.
static std::pair<Name, bool>
getOrMakeName(const std::unordered_map<Index, Name>& nameMap,
Index i,
Name name,
std::unordered_set<Name>& usedNames) {
if (auto it = nameMap.find(i); it != nameMap.end()) {
return {it->second, true};
} else {
auto valid = Names::getValidNameGivenExisting(name, usedNames);
usedNames.insert(valid);
return {valid, false};
}
}
void WasmBinaryReader::readMemories(size_t& pos) {
auto num = getU32LEB(pos);
auto numImports = wasm.memories.size();
std::unordered_set<Name> usedNames;
for (auto& [index, name] : memoryNames) {
if (index >= num + numImports) {
std::cerr << "warning: memory index out of bounds in name section: "
<< name << " at index " << index << '\n';
}
usedNames.insert(name);
}
for (size_t i = 0; i < num; i++) {
auto [name, isExplicit] =
getOrMakeName(memoryNames, numImports + i, makeName("", i), usedNames);
auto memory = Builder::makeMemory(name);
memory->hasExplicitName = isExplicit;
getResizableLimits(pos,
memory->initial,
memory->max,
memory->shared,
memory->addressType,
Memory::kUnlimitedSize);
wasm.addMemory(std::move(memory));
}
}
void WasmBinaryReader::readTypes(size_t& pos) {
TypeBuilder builder(getU32LEB(pos));
auto readHeapType = [&]() -> HeapType {
int64_t htCode = getS64LEB(pos); // TODO: Actually s33
auto share = Unshared;
if (htCode == BinaryConsts::EncodedType::Shared) {
share = Shared;
htCode = getS64LEB(pos); // TODO: Actually s33
}
HeapType ht;
if (getBasicHeapType(pos, htCode, ht)) {
return ht.getBasic(share);
}
if (size_t(htCode) >= builder.size()) {
throwError(pos, "invalid type index: " + std::to_string(htCode));
}
return builder.getTempHeapType(size_t(htCode));
};
auto makeType = [&](int32_t typeCode) {
Type type;
if (getBasicType(pos, typeCode, type)) {
return type;
}
switch (typeCode) {
case BinaryConsts::EncodedType::nullable:
case BinaryConsts::EncodedType::nonnullable: {
auto nullability = typeCode == BinaryConsts::EncodedType::nullable
? Nullable
: NonNullable;
HeapType ht = readHeapType();
if (ht.isBasic()) {
return Type(ht, nullability);
}
return builder.getTempRefType(ht, nullability);
}
default:
throwError(pos, "unexpected type index: " + std::to_string(typeCode));
}
WASM_UNREACHABLE("unexpected type");
};
auto readType = [&]() { return makeType(getS32LEB(pos)); };
auto readSignatureDef = [&]() {
std::vector<Type> params;
std::vector<Type> results;
size_t numParams = getU32LEB(pos);
for (size_t j = 0; j < numParams; j++) {
params.push_back(readType());
}
auto numResults = getU32LEB(pos);
for (size_t j = 0; j < numResults; j++) {
results.push_back(readType());
}
return Signature(builder.getTempTupleType(params),
builder.getTempTupleType(results));
};
auto readContinuationDef = [&]() {
HeapType ht = readHeapType();
if (!ht.isSignature()) {
throw ParseException("cont types must be built from function types");
}
return Continuation(ht);
};
auto readMutability = [&]() {
switch (getU32LEB(pos)) {
case 0:
return Immutable;
case 1:
return Mutable;
default:
throw ParseException("Expected 0 or 1 for mutability");
}
};
auto readFieldDef = [&]() {
// The value may be a general wasm type, or one of the types only possible
// in a field.
auto typeCode = getS32LEB(pos);
if (typeCode == BinaryConsts::EncodedType::i8) {
auto mutable_ = readMutability();
return Field(Field::i8, mutable_);
}
if (typeCode == BinaryConsts::EncodedType::i16) {
auto mutable_ = readMutability();
return Field(Field::i16, mutable_);
}
// It's a regular wasm value.
auto type = makeType(typeCode);
auto mutable_ = readMutability();
return Field(type, mutable_);
};
auto readStructDef = [&]() {
FieldList fields;
size_t numFields = getU32LEB(pos);
for (size_t j = 0; j < numFields; j++) {
fields.push_back(readFieldDef());
}
return Struct(std::move(fields));
};
for (size_t i = 0; i < builder.size(); i++) {
auto form = getInt8(pos);
if (form == BinaryConsts::EncodedType::Rec) {
uint32_t groupSize = getU32LEB(pos);
if (groupSize == 0u) {
// TODO: Support groups of size zero by shrinking the builder.
throwError(pos, "Recursion groups of size zero not supported");
}
// The group counts as one element in the type section, so we have to
// allocate space for the extra types.
builder.grow(groupSize - 1);
builder.createRecGroup(i, groupSize);
form = getInt8(pos);
}
std::optional<uint32_t> superIndex;
if (form == BinaryConsts::EncodedType::Sub ||
form == BinaryConsts::EncodedType::SubFinal) {
if (form == BinaryConsts::EncodedType::Sub) {
builder[i].setOpen();
}
uint32_t supers = getU32LEB(pos);
if (supers > 0) {
if (supers != 1) {
throwError(pos,
"Invalid type definition with " + std::to_string(supers) +
" supertypes");
}
superIndex = getU32LEB(pos);
}
form = getInt8(pos);
}
if (form == BinaryConsts::SharedDef) {
builder[i].setShared();
form = getInt8(pos);
}
if (form == BinaryConsts::EncodedType::Func) {
builder[i] = readSignatureDef();
} else if (form == BinaryConsts::EncodedType::Cont) {
builder[i] = readContinuationDef();
} else if (form == BinaryConsts::EncodedType::Struct) {
builder[i] = readStructDef();
} else if (form == BinaryConsts::EncodedType::Array) {
builder[i] = Array(readFieldDef());
} else {
throwError(pos, "Bad type form " + std::to_string(form));
}
if (superIndex) {
if (*superIndex > builder.size()) {
throwError(
pos, "Out of bounds supertype index " + std::to_string(*superIndex));
}
builder[i].subTypeOf(builder[*superIndex]);
}
}
auto result = builder.build();
if (auto* err = result.getError()) {
Fatal() << "Invalid type: " << err->reason << " at index " << err->index;
}
types = std::move(*result);
// Record the type indices.
for (Index i = 0; i < types.size(); ++i) {
wasm.typeIndices.insert({types[i], i});
}
// Assign names from the names section.
for (auto& [index, name] : typeNames) {
if (index >= types.size()) {
std::cerr << "warning: type index out of bounds in name section: " << name
<< " at index " << index << '\n';
continue;
}
wasm.typeNames[types[index]].name = name;
}
for (auto& [index, fields] : fieldNames) {
if (index >= types.size()) {
std::cerr
<< "warning: type index out of bounds in name section: fields at index "
<< index << '\n';
continue;
}
if (!types[index].isStruct()) {
std::cerr << "warning: field names applied to non-struct type at index "
<< index << '\n';
continue;
}
auto& names = wasm.typeNames[types[index]].fieldNames;
for (auto& [field, name] : fields) {
if (field >= types[index].getStruct().fields.size()) {
std::cerr << "warning: field index out of bounds in name section: "
<< name << " at index " << field << " in type " << index
<< '\n';
continue;
}
names[field] = name;
}
}
}
Name WasmBinaryReader::getFunctionName(size_t& pos, Index index) {
if (index >= wasm.functions.size()) {
throwError(pos, "invalid function index");
}
return wasm.functions[index]->name;
}
Name WasmBinaryReader::getTableName(size_t& pos, Index index) {
if (index >= wasm.tables.size()) {
throwError(pos, "invalid table index");
}
return wasm.tables[index]->name;
}
Name WasmBinaryReader::getMemoryName(size_t& pos, Index index) {
if (index >= wasm.memories.size()) {
throwError(pos, "invalid memory index");
}
return wasm.memories[index]->name;
}
Name WasmBinaryReader::getGlobalName(size_t& pos, Index index) {
if (index >= wasm.globals.size()) {
throwError(pos, "invalid global index");
}
return wasm.globals[index]->name;
}
Name WasmBinaryReader::getTagName(size_t& pos, Index index) {
if (index >= wasm.tags.size()) {
throwError(pos, "invalid tag index");
}
return wasm.tags[index]->name;
}
Name WasmBinaryReader::getDataName(size_t& pos, Index index) {
if (index >= wasm.dataSegments.size()) {
throwError(pos, "invalid data segment index");
}
return wasm.dataSegments[index]->name;
}
Name WasmBinaryReader::getElemName(size_t& pos, Index index) {
if (index >= wasm.elementSegments.size()) {
throwError(pos, "invalid element segment index");
}
return wasm.elementSegments[index]->name;
}
void WasmBinaryReader::getResizableLimits(size_t& pos,
Address& initial,
Address& max,
bool& shared,
Type& addressType,
Address defaultIfNoMax) {
auto flags = getU32LEB(pos);
bool hasMax = (flags & BinaryConsts::HasMaximum) != 0;
bool isShared = (flags & BinaryConsts::IsShared) != 0;
bool is64 = (flags & BinaryConsts::Is64) != 0;
initial = is64 ? getU64LEB(pos) : getU32LEB(pos);
if (isShared && !hasMax) {
throwError(pos, "shared memory must have max size");
}
shared = isShared;
addressType = is64 ? Type::i64 : Type::i32;
if (hasMax) {
max = is64 ? getU64LEB(pos) : getU32LEB(pos);
} else {
max = defaultIfNoMax;
}
}
void WasmBinaryReader::readImports(size_t& pos) {
size_t num = getU32LEB(pos);
Builder builder(wasm);
std::unordered_set<Name> usedFunctionNames, usedTableNames, usedMemoryNames,
usedGlobalNames, usedTagNames;
for (size_t i = 0; i < num; i++) {
auto module = getInlineString(pos);
auto base = getInlineString(pos);
auto kind = (ExternalKind)getU32LEB(pos);
// 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, isExplicit] =
getOrMakeName(functionNames,
wasm.functions.size(),
makeName("fimport$", wasm.functions.size()),
usedFunctionNames);
auto index = getU32LEB(pos);
functionTypes.push_back(getTypeByIndex(pos, index));
auto type = getTypeByIndex(pos, index);
if (!type.isSignature()) {
throwError(
pos,
std::string("Imported function ") + module.toString() + '.' +
base.toString() +
"'s type must be a signature. Given: " + type.toString());
}
auto curr = builder.makeFunction(name, type, {});
curr->hasExplicitName = isExplicit;
curr->module = module;
curr->base = base;
setLocalNames(*curr, wasm.functions.size());
wasm.addFunction(std::move(curr));
break;
}
case ExternalKind::Table: {
auto [name, isExplicit] =
getOrMakeName(tableNames,
wasm.tables.size(),
makeName("timport$", wasm.tables.size()),
usedTableNames);
auto table = builder.makeTable(name);
table->hasExplicitName = isExplicit;
table->module = module;
table->base = base;
table->type = getType(pos);
bool is_shared;
getResizableLimits(pos,
table->initial,
table->max,
is_shared,
table->addressType,
Table::kUnlimitedSize);
if (is_shared) {
throwError(pos, "Tables may not be shared");
}
wasm.addTable(std::move(table));
break;
}
case ExternalKind::Memory: {
auto [name, isExplicit] =
getOrMakeName(memoryNames,
wasm.memories.size(),
makeName("mimport$", wasm.memories.size()),
usedMemoryNames);
auto memory = builder.makeMemory(name);
memory->hasExplicitName = isExplicit;
memory->module = module;
memory->base = base;
getResizableLimits(pos,
memory->initial,
memory->max,
memory->shared,
memory->addressType,
Memory::kUnlimitedSize);
wasm.addMemory(std::move(memory));
break;
}
case ExternalKind::Global: {
auto [name, isExplicit] =
getOrMakeName(globalNames,
wasm.globals.size(),
makeName("gimport$", wasm.globals.size()),
usedGlobalNames);
auto type = getConcreteType(pos);
auto mutable_ = getU32LEB(pos);
if (mutable_ & ~1) {
throwError(pos, "Global mutability must be 0 or 1");
}
auto curr =
builder.makeGlobal(name,
type,
nullptr,
mutable_ ? Builder::Mutable : Builder::Immutable);
curr->hasExplicitName = isExplicit;
curr->module = module;
curr->base = base;
wasm.addGlobal(std::move(curr));
break;
}
case ExternalKind::Tag: {
auto [name, isExplicit] =
getOrMakeName(tagNames,
wasm.tags.size(),
makeName("eimport$", wasm.tags.size()),
usedTagNames);
getInt8(pos); // Reserved 'attribute' field
auto index = getU32LEB(pos);
auto curr = builder.makeTag(name, getSignatureByTypeIndex(pos, index));
curr->hasExplicitName = isExplicit;
curr->module = module;
curr->base = base;
wasm.addTag(std::move(curr));
break;
}
default: {
throwError(pos, "bad import kind");
}
}
}
numFuncImports = wasm.functions.size();
}
void WasmBinaryReader::setLocalNames(Function& func, Index i) {
if (auto it = localNames.find(i); it != localNames.end()) {
for (auto& [local, name] : it->second) {
if (local >= func.getNumLocals()) {
std::cerr << "warning: local index out of bounds in name section: "
<< name << " at index " << local << " in function " << i
<< '\n';
continue;
}
func.setLocalName(local, name);
}
}
}
void WasmBinaryReader::readFunctionSignatures(size_t& pos) {
size_t num = getU32LEB(pos);
auto numImports = wasm.functions.size();
std::unordered_set<Name> usedNames;
for (auto& [index, name] : functionNames) {
if (index >= num + numImports) {
std::cerr << "warning: function index out of bounds in name section: "
<< name << " at index " << index << '\n';
}
usedNames.insert(name);
}
// Also check that the function indices in the local names subsection are
// in-bounds, even though we don't use them here.
for (auto& [index, locals] : localNames) {
if (index >= num + numImports) {
std::cerr << "warning: function index out of bounds in name section: "
"locals at index "
<< index << '\n';
}
}
for (size_t i = 0; i < num; i++) {
auto [name, isExplicit] =
getOrMakeName(functionNames, numImports + i, makeName("", i), usedNames);
auto index = getU32LEB(pos);
HeapType type = getTypeByIndex(pos, index);
functionTypes.push_back(type);
// Check that the type is a signature.
getSignatureByTypeIndex(pos, index);
auto func = Builder(wasm).makeFunction(name, type, {}, nullptr);
func->hasExplicitName = isExplicit;
wasm.addFunction(std::move(func));
}
}
HeapType WasmBinaryReader::getTypeByIndex(size_t& pos, Index index) {
if (index >= types.size()) {
throwError(pos,
"invalid type index " + std::to_string(index) + " / " +
std::to_string(types.size()));
}
return types[index];
}
HeapType WasmBinaryReader::getTypeByFunctionIndex(size_t& pos, Index index) {
if (index >= functionTypes.size()) {
throwError(pos, "invalid function index");
}
return functionTypes[index];
}
Signature WasmBinaryReader::getSignatureByTypeIndex(size_t& pos, Index index) {
auto heapType = getTypeByIndex(pos, index);
if (!heapType.isSignature()) {
throwError(pos, "invalid signature type " + heapType.toString());
}
return heapType.getSignature();
}
Signature WasmBinaryReader::getSignatureByFunctionIndex(size_t& pos,
Index index) {
auto heapType = getTypeByFunctionIndex(pos, index);
if (!heapType.isSignature()) {
throwError(pos, "invalid signature type " + heapType.toString());
}
return heapType.getSignature();
}
void WasmBinaryReader::readFunctions(size_t& pos) {
numFuncBodies = getU32LEB(pos);
if (numFuncBodies + numFuncImports != wasm.functions.size()) {
throwError(pos, "invalid function section size, must equal types");
}
if (DWARF) {
builder.setBinaryLocation(&pos, codeSectionLocation);
}
for (size_t i = 0; i < numFuncBodies; i++) {
auto sizePos = pos;
size_t size = getU32LEB(pos);
if (size == 0) {
throwError(pos, "empty function size");
}
Index endOfFunction = pos + size;
auto& func = wasm.functions[numFuncImports + i];
currFunction = func.get();
if (DWARF) {
func->funcLocation = BinaryLocations::FunctionLocations{
BinaryLocation(sizePos - codeSectionLocation),
BinaryLocation(pos - codeSectionLocation),
BinaryLocation(pos - codeSectionLocation + size)};
}
func->prologLocation = sourceMapReader.readDebugLocationAt(pos);
readVars(pos);
setLocalNames(*func, numFuncImports + i);
{
// Process the function body. Even if we are skipping function bodies we
// need to not skip the start function. That contains important code for
// wasm-emscripten-finalize in the form of pthread-related segment
// initializations. As this is just one function, it doesn't add
// significant time, so the optimization of skipping bodies is still very
// useful.
auto currFunctionIndex = wasm.functions.size();
bool isStart = startIndex == currFunctionIndex;
if (skipFunctionBodies && !isStart) {
// When skipping the function body we need to put something valid in
// their place so we validate. An unreachable is always acceptable
// there.
func->body = Builder(wasm).makeUnreachable();
// Skip reading the contents.
pos = endOfFunction;
} else {
auto start = builder.visitFunctionStart(func.get());
if (auto* err = start.getErr()) {
throwError(pos, err->msg);
}
while (pos < endOfFunction) {
auto inst = readInst(pos);
if (auto* err = inst.getErr()) {
throwError(pos, err->msg);
}
}
if (pos != endOfFunction) {
throwError(pos, "function overflowed its bounds");
}
if (!builder.empty()) {
throwError(pos, "expected function end");
}
}
}
sourceMapReader.finishFunction();
TypeUpdating::handleNonDefaultableLocals(func.get(), wasm);
currFunction = nullptr;
}
}
void WasmBinaryReader::readVars(size_t& pos) {
uint32_t totalVars = 0;
size_t numLocalTypes = getU32LEB(pos);
// Use a SmallVector as in the common (MVP) case there are only 4 possible
// types.
SmallVector<std::pair<uint32_t, Type>, 4> decodedVars;
decodedVars.reserve(numLocalTypes);
for (size_t t = 0; t < numLocalTypes; t++) {
auto num = getU32LEB(pos);
if (std::ckd_add(&totalVars, totalVars, num)) {
throwError(pos, "unaddressable number of locals");
}
auto type = getConcreteType(pos);
decodedVars.emplace_back(num, type);
}
currFunction->vars.reserve(totalVars);
for (auto [num, type] : decodedVars) {
while (num > 0) {
currFunction->vars.push_back(type);
num--;
}
}
}
Result<> WasmBinaryReader::readInst(size_t& pos) {
if (auto loc = sourceMapReader.readDebugLocationAt(pos)) {
builder.setDebugLocation(loc);
}
uint8_t code = getInt8(pos);
switch (code) {
case BinaryConsts::Block:
return builder.makeBlock(Name(), getType(pos));
case BinaryConsts::If:
return builder.makeIf(Name(), getType(pos));
case BinaryConsts::Loop:
return builder.makeLoop(Name(), getType(pos));
case BinaryConsts::Br:
return builder.makeBreak(getU32LEB(pos), false);
case BinaryConsts::BrIf:
return builder.makeBreak(getU32LEB(pos), true);
case BinaryConsts::BrTable: {
auto numTargets = getU32LEB(pos);
std::vector<Index> labels(numTargets);
for (Index i = 0; i < numTargets; ++i) {
labels[i] = getU32LEB(pos);
}
return builder.makeSwitch(labels, getU32LEB(pos));
}
case BinaryConsts::CallFunction:
case BinaryConsts::RetCallFunction:
return builder.makeCall(getFunctionName(pos, getU32LEB(pos)),
code == BinaryConsts::RetCallFunction);
case BinaryConsts::CallIndirect:
case BinaryConsts::RetCallIndirect: {
auto type = getIndexedHeapType(pos);
auto table = getTableName(pos, getU32LEB(pos));
return builder.makeCallIndirect(
table, type, code == BinaryConsts::RetCallIndirect);
}
case BinaryConsts::LocalGet:
return builder.makeLocalGet(getU32LEB(pos));
case BinaryConsts::LocalSet:
return builder.makeLocalSet(getU32LEB(pos));
case BinaryConsts::LocalTee:
return builder.makeLocalTee(getU32LEB(pos));
case BinaryConsts::GlobalGet:
return builder.makeGlobalGet(getGlobalName(pos, getU32LEB(pos)));
case BinaryConsts::GlobalSet:
return builder.makeGlobalSet(getGlobalName(pos, getU32LEB(pos)));
case BinaryConsts::Select:
return builder.makeSelect(std::nullopt);
case BinaryConsts::SelectWithType: {
auto numTypes = getU32LEB(pos);
std::vector<Type> types;
for (Index i = 0; i < numTypes; ++i) {
auto t = getType(pos);
if (!t.isConcrete()) {
return Err{"bad select type"};
}
types.push_back(t);
}
return builder.makeSelect(Type(types));
}
case BinaryConsts::Return:
return builder.makeReturn();
case BinaryConsts::Nop:
return builder.makeNop();
case BinaryConsts::Unreachable:
return builder.makeUnreachable();
case BinaryConsts::Drop:
return builder.makeDrop();
case BinaryConsts::End:
return builder.visitEnd();
case BinaryConsts::Else:
return builder.visitElse();
case BinaryConsts::Catch_Legacy:
return builder.visitCatch(getTagName(pos, getU32LEB(pos)));
case BinaryConsts::CatchAll_Legacy:
return builder.visitCatchAll();
case BinaryConsts::Delegate:
return builder.visitDelegate(getU32LEB(pos));
case BinaryConsts::RefNull:
return builder.makeRefNull(getHeapType(pos));
case BinaryConsts::RefIsNull:
return builder.makeRefIsNull();
case BinaryConsts::RefFunc:
return builder.makeRefFunc(getFunctionName(pos, getU32LEB(pos)));
case BinaryConsts::RefEq:
return builder.makeRefEq();
case BinaryConsts::RefAsNonNull:
return builder.makeRefAs(RefAsNonNull);
case BinaryConsts::BrOnNull:
return builder.makeBrOn(getU32LEB(pos), BrOnNull);
case BinaryConsts::BrOnNonNull:
return builder.makeBrOn(getU32LEB(pos), BrOnNonNull);
case BinaryConsts::TableGet:
return builder.makeTableGet(getTableName(pos, getU32LEB(pos)));
case BinaryConsts::TableSet:
return builder.makeTableSet(getTableName(pos, getU32LEB(pos)));
case BinaryConsts::Try:
return builder.makeTry(Name(), getType(pos));
case BinaryConsts::TryTable: {
auto type = getType(pos);
std::vector<Name> tags;
std::vector<Index> labels;
std::vector<bool> isRefs;
auto numHandlers = getU32LEB(pos);
for (Index i = 0; i < numHandlers; ++i) {
uint8_t code = getInt8(pos);
if (code == BinaryConsts::Catch || code == BinaryConsts::CatchRef) {
tags.push_back(getTagName(pos, getU32LEB(pos)));
} else {
tags.push_back(Name());
}
labels.push_back(getU32LEB(pos));
isRefs.push_back(code == BinaryConsts::CatchRef ||
code == BinaryConsts::CatchAllRef);
}
return builder.makeTryTable(Name(), type, tags, labels, isRefs);
}
case BinaryConsts::Throw:
return builder.makeThrow(getTagName(pos, getU32LEB(pos)));
case BinaryConsts::Rethrow:
return builder.makeRethrow(getU32LEB(pos));
case BinaryConsts::ThrowRef:
return builder.makeThrowRef();
case BinaryConsts::MemorySize:
return builder.makeMemorySize(getMemoryName(pos, getU32LEB(pos)));
case BinaryConsts::MemoryGrow:
return builder.makeMemoryGrow(getMemoryName(pos, getU32LEB(pos)));
case BinaryConsts::CallRef:
case BinaryConsts::RetCallRef:
return builder.makeCallRef(getIndexedHeapType(pos),
code == BinaryConsts::RetCallRef);
case BinaryConsts::ContBind: {
auto before = getIndexedHeapType(pos);
auto after = getIndexedHeapType(pos);
return builder.makeContBind(before, after);
}
case BinaryConsts::ContNew:
return builder.makeContNew(getIndexedHeapType(pos));
case BinaryConsts::Resume: {
auto type = getIndexedHeapType(pos);
std::vector<Name> tags;
std::vector<Index> labels;
auto numHandlers = getU32LEB(pos);
for (Index i = 0; i < numHandlers; ++i) {
tags.push_back(getTagName(pos, getU32LEB(pos)));
labels.push_back(getU32LEB(pos));
}
return builder.makeResume(type, tags, labels);
}
case BinaryConsts::Suspend:
return builder.makeSuspend(getTagName(pos, getU32LEB(pos)));
#define BINARY_INT(code) \
case BinaryConsts::I32##code: \
return builder.makeBinary(code##Int32); \
case BinaryConsts::I64##code: \
return builder.makeBinary(code##Int64);
#define BINARY_FLOAT(code) \
case BinaryConsts::F32##code: \
return builder.makeBinary(code##Float32); \
case BinaryConsts::F64##code: \
return builder.makeBinary(code##Float64);
#define BINARY_NUM(code) \
BINARY_INT(code) \
BINARY_FLOAT(code)
BINARY_NUM(Add);
BINARY_NUM(Sub);
BINARY_NUM(Mul);
BINARY_INT(DivS);
BINARY_INT(DivU);
BINARY_INT(RemS);
BINARY_INT(RemU);
BINARY_INT(And);
BINARY_INT(Or);
BINARY_INT(Xor);
BINARY_INT(Shl);
BINARY_INT(ShrU);
BINARY_INT(ShrS);
BINARY_INT(RotL);
BINARY_INT(RotR);
BINARY_FLOAT(Div);
BINARY_FLOAT(CopySign);
BINARY_FLOAT(Min);
BINARY_FLOAT(Max);
BINARY_NUM(Eq);
BINARY_NUM(Ne);
BINARY_INT(LtS);
BINARY_INT(LtU);
BINARY_INT(LeS);
BINARY_INT(LeU);
BINARY_INT(GtS);
BINARY_INT(GtU);
BINARY_INT(GeS);
BINARY_INT(GeU);
BINARY_FLOAT(Lt);
BINARY_FLOAT(Le);
BINARY_FLOAT(Gt);
BINARY_FLOAT(Ge);
#define UNARY_INT(code) \
case BinaryConsts::I32##code: \
return builder.makeUnary(code##Int32); \
case BinaryConsts::I64##code: \
return builder.makeUnary(code##Int64);
#define UNARY_FLOAT(code) \
case BinaryConsts::F32##code: \
return builder.makeUnary(code##Float32); \
case BinaryConsts::F64##code: \
return builder.makeUnary(code##Float64);
UNARY_INT(Clz);
UNARY_INT(Ctz);
UNARY_INT(Popcnt);
UNARY_INT(EqZ);
UNARY_FLOAT(Neg);
UNARY_FLOAT(Abs);
UNARY_FLOAT(Ceil);
UNARY_FLOAT(Floor);
UNARY_FLOAT(Nearest);
UNARY_FLOAT(Sqrt);
case BinaryConsts::F32UConvertI32:
return builder.makeUnary(ConvertUInt32ToFloat32);
case BinaryConsts::F64UConvertI32:
return builder.makeUnary(ConvertUInt32ToFloat64);
case BinaryConsts::F32SConvertI32:
return builder.makeUnary(ConvertSInt32ToFloat32);
case BinaryConsts::F64SConvertI32:
return builder.makeUnary(ConvertSInt32ToFloat64);
case BinaryConsts::F32UConvertI64:
return builder.makeUnary(ConvertUInt64ToFloat32);
case BinaryConsts::F64UConvertI64:
return builder.makeUnary(ConvertUInt64ToFloat64);
case BinaryConsts::F32SConvertI64:
return builder.makeUnary(ConvertSInt64ToFloat32);
case BinaryConsts::F64SConvertI64:
return builder.makeUnary(ConvertSInt64ToFloat64);
case BinaryConsts::I64SExtendI32:
return builder.makeUnary(ExtendSInt32);
case BinaryConsts::I64UExtendI32:
return builder.makeUnary(ExtendUInt32);
case BinaryConsts::I32WrapI64:
return builder.makeUnary(WrapInt64);
case BinaryConsts::I32UTruncF32:
return builder.makeUnary(TruncUFloat32ToInt32);
case BinaryConsts::I32UTruncF64:
return builder.makeUnary(TruncUFloat64ToInt32);
case BinaryConsts::I32STruncF32:
return builder.makeUnary(TruncSFloat32ToInt32);
case BinaryConsts::I32STruncF64:
return builder.makeUnary(TruncSFloat64ToInt32);
case BinaryConsts::I64UTruncF32:
return builder.makeUnary(TruncUFloat32ToInt64);
case BinaryConsts::I64UTruncF64:
return builder.makeUnary(TruncUFloat64ToInt64);
case BinaryConsts::I64STruncF32:
return builder.makeUnary(TruncSFloat32ToInt64);
case BinaryConsts::I64STruncF64:
return builder.makeUnary(TruncSFloat64ToInt64);
case BinaryConsts::F32Trunc:
return builder.makeUnary(TruncFloat32);
case BinaryConsts::F64Trunc:
return builder.makeUnary(TruncFloat64);
case BinaryConsts::F32DemoteI64:
return builder.makeUnary(DemoteFloat64);
case BinaryConsts::F64PromoteF32:
return builder.makeUnary(PromoteFloat32);
case BinaryConsts::I32ReinterpretF32:
return builder.makeUnary(ReinterpretFloat32);
case BinaryConsts::I64ReinterpretF64:
return builder.makeUnary(ReinterpretFloat64);
case BinaryConsts::F32ReinterpretI32:
return builder.makeUnary(ReinterpretInt32);
case BinaryConsts::F64ReinterpretI64:
return builder.makeUnary(ReinterpretInt64);
case BinaryConsts::I32ExtendS8:
return builder.makeUnary(ExtendS8Int32);
case BinaryConsts::I32ExtendS16:
return builder.makeUnary(ExtendS16Int32);
case BinaryConsts::I64ExtendS8:
return builder.makeUnary(ExtendS8Int64);
case BinaryConsts::I64ExtendS16:
return builder.makeUnary(ExtendS16Int64);
case BinaryConsts::I64ExtendS32:
return builder.makeUnary(ExtendS32Int64);
case BinaryConsts::I32Const:
return builder.makeConst(Literal(getS32LEB(pos)));
case BinaryConsts::I64Const:
return builder.makeConst(Literal(getS64LEB(pos)));
case BinaryConsts::F32Const:
return builder.makeConst(getFloat32Literal(pos));
case BinaryConsts::F64Const:
return builder.makeConst(getFloat64Literal(pos));
case BinaryConsts::I32LoadMem8S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(1, true, offset, align, Type::i32, mem);
}
case BinaryConsts::I32LoadMem8U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(1, false, offset, align, Type::i32, mem);
}
case BinaryConsts::I32LoadMem16S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(2, true, offset, align, Type::i32, mem);
}
case BinaryConsts::I32LoadMem16U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(2, false, offset, align, Type::i32, mem);
}
case BinaryConsts::I32LoadMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(4, false, offset, align, Type::i32, mem);
}
case BinaryConsts::I64LoadMem8S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(1, true, offset, align, Type::i64, mem);
}
case BinaryConsts::I64LoadMem8U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(1, false, offset, align, Type::i64, mem);
}
case BinaryConsts::I64LoadMem16S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(2, true, offset, align, Type::i64, mem);
}
case BinaryConsts::I64LoadMem16U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(2, false, offset, align, Type::i64, mem);
}
case BinaryConsts::I64LoadMem32S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(4, true, offset, align, Type::i64, mem);
}
case BinaryConsts::I64LoadMem32U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(4, false, offset, align, Type::i64, mem);
}
case BinaryConsts::I64LoadMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(8, false, offset, align, Type::i64, mem);
}
case BinaryConsts::F32LoadMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(4, false, offset, align, Type::f32, mem);
}
case BinaryConsts::F64LoadMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(8, false, offset, align, Type::f64, mem);
}
case BinaryConsts::I32StoreMem8: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(1, offset, align, Type::i32, mem);
}
case BinaryConsts::I32StoreMem16: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(2, offset, align, Type::i32, mem);
}
case BinaryConsts::I32StoreMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(4, offset, align, Type::i32, mem);
}
case BinaryConsts::I64StoreMem8: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(1, offset, align, Type::i64, mem);
}
case BinaryConsts::I64StoreMem16: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(2, offset, align, Type::i64, mem);
}
case BinaryConsts::I64StoreMem32: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(4, offset, align, Type::i64, mem);
}
case BinaryConsts::I64StoreMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(8, offset, align, Type::i64, mem);
}
case BinaryConsts::F32StoreMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(4, offset, align, Type::f32, mem);
}
case BinaryConsts::F64StoreMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(8, offset, align, Type::f64, mem);
}
case BinaryConsts::AtomicPrefix: {
auto op = getU32LEB(pos);
switch (op) {
case BinaryConsts::I32AtomicLoad8U: {
// TODO: pass align through for validation.
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(1, offset, Type::i32, mem);
}
case BinaryConsts::I32AtomicLoad16U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(2, offset, Type::i32, mem);
}
case BinaryConsts::I32AtomicLoad: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(4, offset, Type::i32, mem);
}
case BinaryConsts::I64AtomicLoad8U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(1, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicLoad16U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(2, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicLoad32U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(4, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicLoad: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicLoad(8, offset, Type::i64, mem);
}
case BinaryConsts::I32AtomicStore8: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(1, offset, Type::i32, mem);
}
case BinaryConsts::I32AtomicStore16: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(2, offset, Type::i32, mem);
}
case BinaryConsts::I32AtomicStore: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(4, offset, Type::i32, mem);
}
case BinaryConsts::I64AtomicStore8: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(1, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicStore16: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(2, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicStore32: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(4, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicStore: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicStore(8, offset, Type::i64, mem);
}
#define RMW(op) \
case BinaryConsts::I32AtomicRMW##op: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 4, offset, Type::i32, mem); \
} \
case BinaryConsts::I32AtomicRMW##op##8U: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 1, offset, Type::i32, mem); \
} \
case BinaryConsts::I32AtomicRMW##op##16U: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 2, offset, Type::i32, mem); \
} \
case BinaryConsts::I64AtomicRMW##op: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 8, offset, Type::i64, mem); \
} \
case BinaryConsts::I64AtomicRMW##op##8U: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 1, offset, Type::i64, mem); \
} \
case BinaryConsts::I64AtomicRMW##op##16U: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 2, offset, Type::i64, mem); \
} \
case BinaryConsts::I64AtomicRMW##op##32U: { \
auto [mem, align, offset] = getMemarg(pos); \
return builder.makeAtomicRMW(RMW##op, 4, offset, Type::i64, mem); \
}
RMW(Add);
RMW(Sub);
RMW(And);
RMW(Or);
RMW(Xor);
RMW(Xchg);
case BinaryConsts::I32AtomicCmpxchg: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(4, offset, Type::i32, mem);
}
case BinaryConsts::I32AtomicCmpxchg8U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(1, offset, Type::i32, mem);
}
case BinaryConsts::I32AtomicCmpxchg16U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(2, offset, Type::i32, mem);
}
case BinaryConsts::I64AtomicCmpxchg: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(8, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicCmpxchg8U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(1, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicCmpxchg16U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(2, offset, Type::i64, mem);
}
case BinaryConsts::I64AtomicCmpxchg32U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicCmpxchg(4, offset, Type::i64, mem);
}
case BinaryConsts::I32AtomicWait: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicWait(Type::i32, offset, mem);
}
case BinaryConsts::I64AtomicWait: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicWait(Type::i64, offset, mem);
}
case BinaryConsts::AtomicNotify: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeAtomicNotify(offset, mem);
}
case BinaryConsts::AtomicFence:
if (getInt8(pos) != 0) {
return Err{"expected 0x00 byte immediate on atomic.fence"};
}
return builder.makeAtomicFence();
}
return Err{"unknown atomic operation"};
}
case BinaryConsts::MiscPrefix: {
auto op = getU32LEB(pos);
switch (op) {
case BinaryConsts::I32STruncSatF32:
return builder.makeUnary(TruncSatSFloat32ToInt32);
case BinaryConsts::I32UTruncSatF32:
return builder.makeUnary(TruncSatUFloat32ToInt32);
case BinaryConsts::I32STruncSatF64:
return builder.makeUnary(TruncSatSFloat64ToInt32);
case BinaryConsts::I32UTruncSatF64:
return builder.makeUnary(TruncSatUFloat64ToInt32);
case BinaryConsts::I64STruncSatF32:
return builder.makeUnary(TruncSatSFloat32ToInt64);
case BinaryConsts::I64UTruncSatF32:
return builder.makeUnary(TruncSatUFloat32ToInt64);
case BinaryConsts::I64STruncSatF64:
return builder.makeUnary(TruncSatSFloat64ToInt64);
case BinaryConsts::I64UTruncSatF64:
return builder.makeUnary(TruncSatUFloat64ToInt64);
case BinaryConsts::MemoryInit: {
auto data = getDataName(pos, getU32LEB(pos));
auto mem = getMemoryName(pos, getU32LEB(pos));
return builder.makeMemoryInit(data, mem);
}
case BinaryConsts::DataDrop:
return builder.makeDataDrop(getDataName(pos, getU32LEB(pos)));
case BinaryConsts::MemoryCopy: {
auto dest = getMemoryName(pos, getU32LEB(pos));
auto src = getMemoryName(pos, getU32LEB(pos));
return builder.makeMemoryCopy(dest, src);
}
case BinaryConsts::MemoryFill:
return builder.makeMemoryFill(getMemoryName(pos, getU32LEB(pos)));
case BinaryConsts::TableSize:
return builder.makeTableSize(getTableName(pos, getU32LEB(pos)));
case BinaryConsts::TableGrow:
return builder.makeTableGrow(getTableName(pos, getU32LEB(pos)));
case BinaryConsts::TableFill:
return builder.makeTableFill(getTableName(pos, getU32LEB(pos)));
case BinaryConsts::TableCopy: {
auto dest = getTableName(pos, getU32LEB(pos));
auto src = getTableName(pos, getU32LEB(pos));
return builder.makeTableCopy(dest, src);
}
case BinaryConsts::TableInit: {
auto elem = getElemName(pos, getU32LEB(pos));
auto table = getTableName(pos, getU32LEB(pos));
return builder.makeTableInit(elem, table);
}
case BinaryConsts::F32_F16LoadMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(2, false, offset, align, Type::f32, mem);
}
case BinaryConsts::F32_F16StoreMem: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(2, offset, align, Type::f32, mem);
}
}
return Err{"unknown misc operation"};
}
case BinaryConsts::SIMDPrefix: {
auto op = getU32LEB(pos);
switch (op) {
case BinaryConsts::I8x16Eq:
return builder.makeBinary(EqVecI8x16);
case BinaryConsts::I8x16Ne:
return builder.makeBinary(NeVecI8x16);
case BinaryConsts::I8x16LtS:
return builder.makeBinary(LtSVecI8x16);
case BinaryConsts::I8x16LtU:
return builder.makeBinary(LtUVecI8x16);
case BinaryConsts::I8x16GtS:
return builder.makeBinary(GtSVecI8x16);
case BinaryConsts::I8x16GtU:
return builder.makeBinary(GtUVecI8x16);
case BinaryConsts::I8x16LeS:
return builder.makeBinary(LeSVecI8x16);
case BinaryConsts::I8x16LeU:
return builder.makeBinary(LeUVecI8x16);
case BinaryConsts::I8x16GeS:
return builder.makeBinary(GeSVecI8x16);
case BinaryConsts::I8x16GeU:
return builder.makeBinary(GeUVecI8x16);
case BinaryConsts::I16x8Eq:
return builder.makeBinary(EqVecI16x8);
case BinaryConsts::I16x8Ne:
return builder.makeBinary(NeVecI16x8);
case BinaryConsts::I16x8LtS:
return builder.makeBinary(LtSVecI16x8);
case BinaryConsts::I16x8LtU:
return builder.makeBinary(LtUVecI16x8);
case BinaryConsts::I16x8GtS:
return builder.makeBinary(GtSVecI16x8);
case BinaryConsts::I16x8GtU:
return builder.makeBinary(GtUVecI16x8);
case BinaryConsts::I16x8LeS:
return builder.makeBinary(LeSVecI16x8);
case BinaryConsts::I16x8LeU:
return builder.makeBinary(LeUVecI16x8);
case BinaryConsts::I16x8GeS:
return builder.makeBinary(GeSVecI16x8);
case BinaryConsts::I16x8GeU:
return builder.makeBinary(GeUVecI16x8);
case BinaryConsts::I32x4Eq:
return builder.makeBinary(EqVecI32x4);
case BinaryConsts::I32x4Ne:
return builder.makeBinary(NeVecI32x4);
case BinaryConsts::I32x4LtS:
return builder.makeBinary(LtSVecI32x4);
case BinaryConsts::I32x4LtU:
return builder.makeBinary(LtUVecI32x4);
case BinaryConsts::I32x4GtS:
return builder.makeBinary(GtSVecI32x4);
case BinaryConsts::I32x4GtU:
return builder.makeBinary(GtUVecI32x4);
case BinaryConsts::I32x4LeS:
return builder.makeBinary(LeSVecI32x4);
case BinaryConsts::I32x4LeU:
return builder.makeBinary(LeUVecI32x4);
case BinaryConsts::I32x4GeS:
return builder.makeBinary(GeSVecI32x4);
case BinaryConsts::I32x4GeU:
return builder.makeBinary(GeUVecI32x4);
case BinaryConsts::I64x2Eq:
return builder.makeBinary(EqVecI64x2);
case BinaryConsts::I64x2Ne:
return builder.makeBinary(NeVecI64x2);
case BinaryConsts::I64x2LtS:
return builder.makeBinary(LtSVecI64x2);
case BinaryConsts::I64x2GtS:
return builder.makeBinary(GtSVecI64x2);
case BinaryConsts::I64x2LeS:
return builder.makeBinary(LeSVecI64x2);
case BinaryConsts::I64x2GeS:
return builder.makeBinary(GeSVecI64x2);
case BinaryConsts::F16x8Eq:
return builder.makeBinary(EqVecF16x8);
case BinaryConsts::F16x8Ne:
return builder.makeBinary(NeVecF16x8);
case BinaryConsts::F16x8Lt:
return builder.makeBinary(LtVecF16x8);
case BinaryConsts::F16x8Gt:
return builder.makeBinary(GtVecF16x8);
case BinaryConsts::F16x8Le:
return builder.makeBinary(LeVecF16x8);
case BinaryConsts::F16x8Ge:
return builder.makeBinary(GeVecF16x8);
case BinaryConsts::F32x4Eq:
return builder.makeBinary(EqVecF32x4);
case BinaryConsts::F32x4Ne:
return builder.makeBinary(NeVecF32x4);
case BinaryConsts::F32x4Lt:
return builder.makeBinary(LtVecF32x4);
case BinaryConsts::F32x4Gt:
return builder.makeBinary(GtVecF32x4);
case BinaryConsts::F32x4Le:
return builder.makeBinary(LeVecF32x4);
case BinaryConsts::F32x4Ge:
return builder.makeBinary(GeVecF32x4);
case BinaryConsts::F64x2Eq:
return builder.makeBinary(EqVecF64x2);
case BinaryConsts::F64x2Ne:
return builder.makeBinary(NeVecF64x2);
case BinaryConsts::F64x2Lt:
return builder.makeBinary(LtVecF64x2);
case BinaryConsts::F64x2Gt:
return builder.makeBinary(GtVecF64x2);
case BinaryConsts::F64x2Le:
return builder.makeBinary(LeVecF64x2);
case BinaryConsts::F64x2Ge:
return builder.makeBinary(GeVecF64x2);
case BinaryConsts::V128And:
return builder.makeBinary(AndVec128);
case BinaryConsts::V128Or:
return builder.makeBinary(OrVec128);
case BinaryConsts::V128Xor:
return builder.makeBinary(XorVec128);
case BinaryConsts::V128Andnot:
return builder.makeBinary(AndNotVec128);
case BinaryConsts::I8x16Add:
return builder.makeBinary(AddVecI8x16);
case BinaryConsts::I8x16AddSatS:
return builder.makeBinary(AddSatSVecI8x16);
case BinaryConsts::I8x16AddSatU:
return builder.makeBinary(AddSatUVecI8x16);
case BinaryConsts::I8x16Sub:
return builder.makeBinary(SubVecI8x16);
case BinaryConsts::I8x16SubSatS:
return builder.makeBinary(SubSatSVecI8x16);
case BinaryConsts::I8x16SubSatU:
return builder.makeBinary(SubSatUVecI8x16);
case BinaryConsts::I8x16MinS:
return builder.makeBinary(MinSVecI8x16);
case BinaryConsts::I8x16MinU:
return builder.makeBinary(MinUVecI8x16);
case BinaryConsts::I8x16MaxS:
return builder.makeBinary(MaxSVecI8x16);
case BinaryConsts::I8x16MaxU:
return builder.makeBinary(MaxUVecI8x16);
case BinaryConsts::I8x16AvgrU:
return builder.makeBinary(AvgrUVecI8x16);
case BinaryConsts::I16x8Add:
return builder.makeBinary(AddVecI16x8);
case BinaryConsts::I16x8AddSatS:
return builder.makeBinary(AddSatSVecI16x8);
case BinaryConsts::I16x8AddSatU:
return builder.makeBinary(AddSatUVecI16x8);
case BinaryConsts::I16x8Sub:
return builder.makeBinary(SubVecI16x8);
case BinaryConsts::I16x8SubSatS:
return builder.makeBinary(SubSatSVecI16x8);
case BinaryConsts::I16x8SubSatU:
return builder.makeBinary(SubSatUVecI16x8);
case BinaryConsts::I16x8Mul:
return builder.makeBinary(MulVecI16x8);
case BinaryConsts::I16x8MinS:
return builder.makeBinary(MinSVecI16x8);
case BinaryConsts::I16x8MinU:
return builder.makeBinary(MinUVecI16x8);
case BinaryConsts::I16x8MaxS:
return builder.makeBinary(MaxSVecI16x8);
case BinaryConsts::I16x8MaxU:
return builder.makeBinary(MaxUVecI16x8);
case BinaryConsts::I16x8AvgrU:
return builder.makeBinary(AvgrUVecI16x8);
case BinaryConsts::I16x8Q15MulrSatS:
return builder.makeBinary(Q15MulrSatSVecI16x8);
case BinaryConsts::I16x8ExtmulLowI8x16S:
return builder.makeBinary(ExtMulLowSVecI16x8);
case BinaryConsts::I16x8ExtmulHighI8x16S:
return builder.makeBinary(ExtMulHighSVecI16x8);
case BinaryConsts::I16x8ExtmulLowI8x16U:
return builder.makeBinary(ExtMulLowUVecI16x8);
case BinaryConsts::I16x8ExtmulHighI8x16U:
return builder.makeBinary(ExtMulHighUVecI16x8);
case BinaryConsts::I32x4Add:
return builder.makeBinary(AddVecI32x4);
case BinaryConsts::I32x4Sub:
return builder.makeBinary(SubVecI32x4);
case BinaryConsts::I32x4Mul:
return builder.makeBinary(MulVecI32x4);
case BinaryConsts::I32x4MinS:
return builder.makeBinary(MinSVecI32x4);
case BinaryConsts::I32x4MinU:
return builder.makeBinary(MinUVecI32x4);
case BinaryConsts::I32x4MaxS:
return builder.makeBinary(MaxSVecI32x4);
case BinaryConsts::I32x4MaxU:
return builder.makeBinary(MaxUVecI32x4);
case BinaryConsts::I32x4DotI16x8S:
return builder.makeBinary(DotSVecI16x8ToVecI32x4);
case BinaryConsts::I32x4ExtmulLowI16x8S:
return builder.makeBinary(ExtMulLowSVecI32x4);
case BinaryConsts::I32x4ExtmulHighI16x8S:
return builder.makeBinary(ExtMulHighSVecI32x4);
case BinaryConsts::I32x4ExtmulLowI16x8U:
return builder.makeBinary(ExtMulLowUVecI32x4);
case BinaryConsts::I32x4ExtmulHighI16x8U:
return builder.makeBinary(ExtMulHighUVecI32x4);
case BinaryConsts::I64x2Add:
return builder.makeBinary(AddVecI64x2);
case BinaryConsts::I64x2Sub:
return builder.makeBinary(SubVecI64x2);
case BinaryConsts::I64x2Mul:
return builder.makeBinary(MulVecI64x2);
case BinaryConsts::I64x2ExtmulLowI32x4S:
return builder.makeBinary(ExtMulLowSVecI64x2);
case BinaryConsts::I64x2ExtmulHighI32x4S:
return builder.makeBinary(ExtMulHighSVecI64x2);
case BinaryConsts::I64x2ExtmulLowI32x4U:
return builder.makeBinary(ExtMulLowUVecI64x2);
case BinaryConsts::I64x2ExtmulHighI32x4U:
return builder.makeBinary(ExtMulHighUVecI64x2);
case BinaryConsts::F16x8Add:
return builder.makeBinary(AddVecF16x8);
case BinaryConsts::F16x8Sub:
return builder.makeBinary(SubVecF16x8);
case BinaryConsts::F16x8Mul:
return builder.makeBinary(MulVecF16x8);
case BinaryConsts::F16x8Div:
return builder.makeBinary(DivVecF16x8);
case BinaryConsts::F16x8Min:
return builder.makeBinary(MinVecF16x8);
case BinaryConsts::F16x8Max:
return builder.makeBinary(MaxVecF16x8);
case BinaryConsts::F16x8Pmin:
return builder.makeBinary(PMinVecF16x8);
case BinaryConsts::F16x8Pmax:
return builder.makeBinary(PMaxVecF16x8);
case BinaryConsts::F32x4Add:
return builder.makeBinary(AddVecF32x4);
case BinaryConsts::F32x4Sub:
return builder.makeBinary(SubVecF32x4);
case BinaryConsts::F32x4Mul:
return builder.makeBinary(MulVecF32x4);
case BinaryConsts::F32x4Div:
return builder.makeBinary(DivVecF32x4);
case BinaryConsts::F32x4Min:
return builder.makeBinary(MinVecF32x4);
case BinaryConsts::F32x4Max:
return builder.makeBinary(MaxVecF32x4);
case BinaryConsts::F32x4Pmin:
return builder.makeBinary(PMinVecF32x4);
case BinaryConsts::F32x4Pmax:
return builder.makeBinary(PMaxVecF32x4);
case BinaryConsts::F64x2Add:
return builder.makeBinary(AddVecF64x2);
case BinaryConsts::F64x2Sub:
return builder.makeBinary(SubVecF64x2);
case BinaryConsts::F64x2Mul:
return builder.makeBinary(MulVecF64x2);
case BinaryConsts::F64x2Div:
return builder.makeBinary(DivVecF64x2);
case BinaryConsts::F64x2Min:
return builder.makeBinary(MinVecF64x2);
case BinaryConsts::F64x2Max:
return builder.makeBinary(MaxVecF64x2);
case BinaryConsts::F64x2Pmin:
return builder.makeBinary(PMinVecF64x2);
case BinaryConsts::F64x2Pmax:
return builder.makeBinary(PMaxVecF64x2);
case BinaryConsts::I8x16NarrowI16x8S:
return builder.makeBinary(NarrowSVecI16x8ToVecI8x16);
case BinaryConsts::I8x16NarrowI16x8U:
return builder.makeBinary(NarrowUVecI16x8ToVecI8x16);
case BinaryConsts::I16x8NarrowI32x4S:
return builder.makeBinary(NarrowSVecI32x4ToVecI16x8);
case BinaryConsts::I16x8NarrowI32x4U:
return builder.makeBinary(NarrowUVecI32x4ToVecI16x8);
case BinaryConsts::I8x16Swizzle:
return builder.makeBinary(SwizzleVecI8x16);
case BinaryConsts::I8x16RelaxedSwizzle:
return builder.makeBinary(RelaxedSwizzleVecI8x16);
case BinaryConsts::F32x4RelaxedMin:
return builder.makeBinary(RelaxedMinVecF32x4);
case BinaryConsts::F32x4RelaxedMax:
return builder.makeBinary(RelaxedMaxVecF32x4);
case BinaryConsts::F64x2RelaxedMin:
return builder.makeBinary(RelaxedMinVecF64x2);
case BinaryConsts::F64x2RelaxedMax:
return builder.makeBinary(RelaxedMaxVecF64x2);
case BinaryConsts::I16x8RelaxedQ15MulrS:
return builder.makeBinary(RelaxedQ15MulrSVecI16x8);
case BinaryConsts::I16x8DotI8x16I7x16S:
return builder.makeBinary(DotI8x16I7x16SToVecI16x8);
case BinaryConsts::I8x16Splat:
return builder.makeUnary(SplatVecI8x16);
case BinaryConsts::I16x8Splat:
return builder.makeUnary(SplatVecI16x8);
case BinaryConsts::I32x4Splat:
return builder.makeUnary(SplatVecI32x4);
case BinaryConsts::I64x2Splat:
return builder.makeUnary(SplatVecI64x2);
case BinaryConsts::F16x8Splat:
return builder.makeUnary(SplatVecF16x8);
case BinaryConsts::F32x4Splat:
return builder.makeUnary(SplatVecF32x4);
case BinaryConsts::F64x2Splat:
return builder.makeUnary(SplatVecF64x2);
case BinaryConsts::V128Not:
return builder.makeUnary(NotVec128);
case BinaryConsts::V128AnyTrue:
return builder.makeUnary(AnyTrueVec128);
case BinaryConsts::I8x16Popcnt:
return builder.makeUnary(PopcntVecI8x16);
case BinaryConsts::I8x16Abs:
return builder.makeUnary(AbsVecI8x16);
case BinaryConsts::I8x16Neg:
return builder.makeUnary(NegVecI8x16);
case BinaryConsts::I8x16AllTrue:
return builder.makeUnary(AllTrueVecI8x16);
case BinaryConsts::I8x16Bitmask:
return builder.makeUnary(BitmaskVecI8x16);
case BinaryConsts::I16x8Abs:
return builder.makeUnary(AbsVecI16x8);
case BinaryConsts::I16x8Neg:
return builder.makeUnary(NegVecI16x8);
case BinaryConsts::I16x8AllTrue:
return builder.makeUnary(AllTrueVecI16x8);
case BinaryConsts::I16x8Bitmask:
return builder.makeUnary(BitmaskVecI16x8);
case BinaryConsts::I32x4Abs:
return builder.makeUnary(AbsVecI32x4);
case BinaryConsts::I32x4Neg:
return builder.makeUnary(NegVecI32x4);
case BinaryConsts::I32x4AllTrue:
return builder.makeUnary(AllTrueVecI32x4);
case BinaryConsts::I32x4Bitmask:
return builder.makeUnary(BitmaskVecI32x4);
case BinaryConsts::I64x2Abs:
return builder.makeUnary(AbsVecI64x2);
case BinaryConsts::I64x2Neg:
return builder.makeUnary(NegVecI64x2);
case BinaryConsts::I64x2AllTrue:
return builder.makeUnary(AllTrueVecI64x2);
case BinaryConsts::I64x2Bitmask:
return builder.makeUnary(BitmaskVecI64x2);
case BinaryConsts::F16x8Abs:
return builder.makeUnary(AbsVecF16x8);
case BinaryConsts::F16x8Neg:
return builder.makeUnary(NegVecF16x8);
case BinaryConsts::F16x8Sqrt:
return builder.makeUnary(SqrtVecF16x8);
case BinaryConsts::F16x8Ceil:
return builder.makeUnary(CeilVecF16x8);
case BinaryConsts::F16x8Floor:
return builder.makeUnary(FloorVecF16x8);
case BinaryConsts::F16x8Trunc:
return builder.makeUnary(TruncVecF16x8);
case BinaryConsts::F16x8Nearest:
return builder.makeUnary(NearestVecF16x8);
case BinaryConsts::F32x4Abs:
return builder.makeUnary(AbsVecF32x4);
case BinaryConsts::F32x4Neg:
return builder.makeUnary(NegVecF32x4);
case BinaryConsts::F32x4Sqrt:
return builder.makeUnary(SqrtVecF32x4);
case BinaryConsts::F32x4Ceil:
return builder.makeUnary(CeilVecF32x4);
case BinaryConsts::F32x4Floor:
return builder.makeUnary(FloorVecF32x4);
case BinaryConsts::F32x4Trunc:
return builder.makeUnary(TruncVecF32x4);
case BinaryConsts::F32x4Nearest:
return builder.makeUnary(NearestVecF32x4);
case BinaryConsts::F64x2Abs:
return builder.makeUnary(AbsVecF64x2);
case BinaryConsts::F64x2Neg:
return builder.makeUnary(NegVecF64x2);
case BinaryConsts::F64x2Sqrt:
return builder.makeUnary(SqrtVecF64x2);
case BinaryConsts::F64x2Ceil:
return builder.makeUnary(CeilVecF64x2);
case BinaryConsts::F64x2Floor:
return builder.makeUnary(FloorVecF64x2);
case BinaryConsts::F64x2Trunc:
return builder.makeUnary(TruncVecF64x2);
case BinaryConsts::F64x2Nearest:
return builder.makeUnary(NearestVecF64x2);
case BinaryConsts::I16x8ExtaddPairwiseI8x16S:
return builder.makeUnary(ExtAddPairwiseSVecI8x16ToI16x8);
case BinaryConsts::I16x8ExtaddPairwiseI8x16U:
return builder.makeUnary(ExtAddPairwiseUVecI8x16ToI16x8);
case BinaryConsts::I32x4ExtaddPairwiseI16x8S:
return builder.makeUnary(ExtAddPairwiseSVecI16x8ToI32x4);
case BinaryConsts::I32x4ExtaddPairwiseI16x8U:
return builder.makeUnary(ExtAddPairwiseUVecI16x8ToI32x4);
case BinaryConsts::I32x4TruncSatF32x4S:
return builder.makeUnary(TruncSatSVecF32x4ToVecI32x4);
case BinaryConsts::I32x4TruncSatF32x4U:
return builder.makeUnary(TruncSatUVecF32x4ToVecI32x4);
case BinaryConsts::F32x4ConvertI32x4S:
return builder.makeUnary(ConvertSVecI32x4ToVecF32x4);
case BinaryConsts::F32x4ConvertI32x4U:
return builder.makeUnary(ConvertUVecI32x4ToVecF32x4);
case BinaryConsts::I16x8ExtendLowI8x16S:
return builder.makeUnary(ExtendLowSVecI8x16ToVecI16x8);
case BinaryConsts::I16x8ExtendHighI8x16S:
return builder.makeUnary(ExtendHighSVecI8x16ToVecI16x8);
case BinaryConsts::I16x8ExtendLowI8x16U:
return builder.makeUnary(ExtendLowUVecI8x16ToVecI16x8);
case BinaryConsts::I16x8ExtendHighI8x16U:
return builder.makeUnary(ExtendHighUVecI8x16ToVecI16x8);
case BinaryConsts::I32x4ExtendLowI16x8S:
return builder.makeUnary(ExtendLowSVecI16x8ToVecI32x4);
case BinaryConsts::I32x4ExtendHighI16x8S:
return builder.makeUnary(ExtendHighSVecI16x8ToVecI32x4);
case BinaryConsts::I32x4ExtendLowI16x8U:
return builder.makeUnary(ExtendLowUVecI16x8ToVecI32x4);
case BinaryConsts::I32x4ExtendHighI16x8U:
return builder.makeUnary(ExtendHighUVecI16x8ToVecI32x4);
case BinaryConsts::I64x2ExtendLowI32x4S:
return builder.makeUnary(ExtendLowSVecI32x4ToVecI64x2);
case BinaryConsts::I64x2ExtendHighI32x4S:
return builder.makeUnary(ExtendHighSVecI32x4ToVecI64x2);
case BinaryConsts::I64x2ExtendLowI32x4U:
return builder.makeUnary(ExtendLowUVecI32x4ToVecI64x2);
case BinaryConsts::I64x2ExtendHighI32x4U:
return builder.makeUnary(ExtendHighUVecI32x4ToVecI64x2);
case BinaryConsts::F64x2ConvertLowI32x4S:
return builder.makeUnary(ConvertLowSVecI32x4ToVecF64x2);
case BinaryConsts::F64x2ConvertLowI32x4U:
return builder.makeUnary(ConvertLowUVecI32x4ToVecF64x2);
case BinaryConsts::I32x4TruncSatF64x2SZero:
return builder.makeUnary(TruncSatZeroSVecF64x2ToVecI32x4);
case BinaryConsts::I32x4TruncSatF64x2UZero:
return builder.makeUnary(TruncSatZeroUVecF64x2ToVecI32x4);
case BinaryConsts::F32x4DemoteF64x2Zero:
return builder.makeUnary(DemoteZeroVecF64x2ToVecF32x4);
case BinaryConsts::F64x2PromoteLowF32x4:
return builder.makeUnary(PromoteLowVecF32x4ToVecF64x2);
case BinaryConsts::I32x4RelaxedTruncF32x4S:
return builder.makeUnary(RelaxedTruncSVecF32x4ToVecI32x4);
case BinaryConsts::I32x4RelaxedTruncF32x4U:
return builder.makeUnary(RelaxedTruncUVecF32x4ToVecI32x4);
case BinaryConsts::I32x4RelaxedTruncF64x2SZero:
return builder.makeUnary(RelaxedTruncZeroSVecF64x2ToVecI32x4);
case BinaryConsts::I32x4RelaxedTruncF64x2UZero:
return builder.makeUnary(RelaxedTruncZeroUVecF64x2ToVecI32x4);
case BinaryConsts::I16x8TruncSatF16x8S:
return builder.makeUnary(TruncSatSVecF16x8ToVecI16x8);
case BinaryConsts::I16x8TruncSatF16x8U:
return builder.makeUnary(TruncSatUVecF16x8ToVecI16x8);
case BinaryConsts::F16x8ConvertI16x8S:
return builder.makeUnary(ConvertSVecI16x8ToVecF16x8);
case BinaryConsts::F16x8ConvertI16x8U:
return builder.makeUnary(ConvertUVecI16x8ToVecF16x8);
case BinaryConsts::I8x16ExtractLaneS:
return builder.makeSIMDExtract(ExtractLaneSVecI8x16,
getLaneIndex(pos, 16));
case BinaryConsts::I8x16ExtractLaneU:
return builder.makeSIMDExtract(ExtractLaneUVecI8x16,
getLaneIndex(pos, 16));
case BinaryConsts::I16x8ExtractLaneS:
return builder.makeSIMDExtract(ExtractLaneSVecI16x8,
getLaneIndex(pos, 8));
case BinaryConsts::I16x8ExtractLaneU:
return builder.makeSIMDExtract(ExtractLaneUVecI16x8,
getLaneIndex(pos, 8));
case BinaryConsts::I32x4ExtractLane:
return builder.makeSIMDExtract(ExtractLaneVecI32x4,
getLaneIndex(pos, 4));
case BinaryConsts::I64x2ExtractLane:
return builder.makeSIMDExtract(ExtractLaneVecI64x2,
getLaneIndex(pos, 2));
case BinaryConsts::F16x8ExtractLane:
return builder.makeSIMDExtract(ExtractLaneVecF16x8,
getLaneIndex(pos, 8));
case BinaryConsts::F32x4ExtractLane:
return builder.makeSIMDExtract(ExtractLaneVecF32x4,
getLaneIndex(pos, 4));
case BinaryConsts::F64x2ExtractLane:
return builder.makeSIMDExtract(ExtractLaneVecF64x2,
getLaneIndex(pos, 2));
case BinaryConsts::I8x16ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecI8x16,
getLaneIndex(pos, 16));
case BinaryConsts::I16x8ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecI16x8,
getLaneIndex(pos, 8));
case BinaryConsts::I32x4ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecI32x4,
getLaneIndex(pos, 4));
case BinaryConsts::I64x2ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecI64x2,
getLaneIndex(pos, 2));
case BinaryConsts::F16x8ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecF16x8,
getLaneIndex(pos, 8));
case BinaryConsts::F32x4ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecF32x4,
getLaneIndex(pos, 4));
case BinaryConsts::F64x2ReplaceLane:
return builder.makeSIMDReplace(ReplaceLaneVecF64x2,
getLaneIndex(pos, 2));
case BinaryConsts::I8x16Shuffle: {
std::array<uint8_t, 16> lanes;
for (Index i = 0; i < 16; ++i) {
lanes[i] = getLaneIndex(pos, 32);
}
return builder.makeSIMDShuffle(lanes);
}
case BinaryConsts::V128Bitselect:
return builder.makeSIMDTernary(Bitselect);
case BinaryConsts::I8x16Laneselect:
return builder.makeSIMDTernary(LaneselectI8x16);
case BinaryConsts::I16x8Laneselect:
return builder.makeSIMDTernary(LaneselectI16x8);
case BinaryConsts::I32x4Laneselect:
return builder.makeSIMDTernary(LaneselectI32x4);
case BinaryConsts::I64x2Laneselect:
return builder.makeSIMDTernary(LaneselectI64x2);
case BinaryConsts::F16x8RelaxedMadd:
return builder.makeSIMDTernary(RelaxedMaddVecF16x8);
case BinaryConsts::F16x8RelaxedNmadd:
return builder.makeSIMDTernary(RelaxedNmaddVecF16x8);
case BinaryConsts::F32x4RelaxedMadd:
return builder.makeSIMDTernary(RelaxedMaddVecF32x4);
case BinaryConsts::F32x4RelaxedNmadd:
return builder.makeSIMDTernary(RelaxedNmaddVecF32x4);
case BinaryConsts::F64x2RelaxedMadd:
return builder.makeSIMDTernary(RelaxedMaddVecF64x2);
case BinaryConsts::F64x2RelaxedNmadd:
return builder.makeSIMDTernary(RelaxedNmaddVecF64x2);
case BinaryConsts::I32x4DotI8x16I7x16AddS:
return builder.makeSIMDTernary(DotI8x16I7x16AddSToVecI32x4);
case BinaryConsts::I8x16Shl:
return builder.makeSIMDShift(ShlVecI8x16);
case BinaryConsts::I8x16ShrS:
return builder.makeSIMDShift(ShrSVecI8x16);
case BinaryConsts::I8x16ShrU:
return builder.makeSIMDShift(ShrUVecI8x16);
case BinaryConsts::I16x8Shl:
return builder.makeSIMDShift(ShlVecI16x8);
case BinaryConsts::I16x8ShrS:
return builder.makeSIMDShift(ShrSVecI16x8);
case BinaryConsts::I16x8ShrU:
return builder.makeSIMDShift(ShrUVecI16x8);
case BinaryConsts::I32x4Shl:
return builder.makeSIMDShift(ShlVecI32x4);
case BinaryConsts::I32x4ShrS:
return builder.makeSIMDShift(ShrSVecI32x4);
case BinaryConsts::I32x4ShrU:
return builder.makeSIMDShift(ShrUVecI32x4);
case BinaryConsts::I64x2Shl:
return builder.makeSIMDShift(ShlVecI64x2);
case BinaryConsts::I64x2ShrS:
return builder.makeSIMDShift(ShrSVecI64x2);
case BinaryConsts::I64x2ShrU:
return builder.makeSIMDShift(ShrUVecI64x2);
case BinaryConsts::V128Const:
return builder.makeConst(getVec128Literal(pos));
case BinaryConsts::V128Store: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeStore(16, offset, align, Type::v128, mem);
}
case BinaryConsts::V128Load: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeLoad(16, false, offset, align, Type::v128, mem);
}
case BinaryConsts::V128Load8Splat: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load8SplatVec128, offset, align, mem);
}
case BinaryConsts::V128Load16Splat: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load16SplatVec128, offset, align, mem);
}
case BinaryConsts::V128Load32Splat: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load32SplatVec128, offset, align, mem);
}
case BinaryConsts::V128Load64Splat: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load64SplatVec128, offset, align, mem);
}
case BinaryConsts::V128Load8x8S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load8x8SVec128, offset, align, mem);
}
case BinaryConsts::V128Load8x8U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load8x8UVec128, offset, align, mem);
}
case BinaryConsts::V128Load16x4S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load16x4SVec128, offset, align, mem);
}
case BinaryConsts::V128Load16x4U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load16x4UVec128, offset, align, mem);
}
case BinaryConsts::V128Load32x2S: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load32x2SVec128, offset, align, mem);
}
case BinaryConsts::V128Load32x2U: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load32x2UVec128, offset, align, mem);
}
case BinaryConsts::V128Load32Zero: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load32ZeroVec128, offset, align, mem);
}
case BinaryConsts::V128Load64Zero: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoad(Load64ZeroVec128, offset, align, mem);
}
case BinaryConsts::V128Load8Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Load8LaneVec128, offset, align, getLaneIndex(pos, 16), mem);
}
case BinaryConsts::V128Load16Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Load16LaneVec128, offset, align, getLaneIndex(pos, 8), mem);
}
case BinaryConsts::V128Load32Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Load32LaneVec128, offset, align, getLaneIndex(pos, 4), mem);
}
case BinaryConsts::V128Load64Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Load64LaneVec128, offset, align, getLaneIndex(pos, 2), mem);
}
case BinaryConsts::V128Store8Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Store8LaneVec128, offset, align, getLaneIndex(pos, 16), mem);
}
case BinaryConsts::V128Store16Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Store16LaneVec128, offset, align, getLaneIndex(pos, 8), mem);
}
case BinaryConsts::V128Store32Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Store32LaneVec128, offset, align, getLaneIndex(pos, 4), mem);
}
case BinaryConsts::V128Store64Lane: {
auto [mem, align, offset] = getMemarg(pos);
return builder.makeSIMDLoadStoreLane(
Store64LaneVec128, offset, align, getLaneIndex(pos, 2), mem);
}
}
return Err{"unknown SIMD operation"};
}
case BinaryConsts::GCPrefix: {
auto op = getU32LEB(pos);
switch (op) {
case BinaryConsts::RefI31:
return builder.makeRefI31(Unshared);
case BinaryConsts::RefI31Shared:
return builder.makeRefI31(Shared);
case BinaryConsts::I31GetS:
return builder.makeI31Get(true);
case BinaryConsts::I31GetU:
return builder.makeI31Get(false);
case BinaryConsts::RefTest:
return builder.makeRefTest(Type(getHeapType(pos), NonNullable));
case BinaryConsts::RefTestNull:
return builder.makeRefTest(Type(getHeapType(pos), Nullable));
case BinaryConsts::RefCast:
return builder.makeRefCast(Type(getHeapType(pos), NonNullable));
case BinaryConsts::RefCastNull:
return builder.makeRefCast(Type(getHeapType(pos), Nullable));
case BinaryConsts::BrOnCast:
case BinaryConsts::BrOnCastFail: {
auto flags = getInt8(pos);
auto label = getU32LEB(pos);
auto in =
Type(getHeapType(pos), (flags & 1) ? Nullable : NonNullable);
auto cast =
Type(getHeapType(pos), (flags & 2) ? Nullable : NonNullable);
auto kind = op == BinaryConsts::BrOnCast ? BrOnCast : BrOnCastFail;
return builder.makeBrOn(label, kind, in, cast);
}
case BinaryConsts::StructNew:
return builder.makeStructNew(getIndexedHeapType(pos));
case BinaryConsts::StructNewDefault:
return builder.makeStructNewDefault(getIndexedHeapType(pos));
case BinaryConsts::StructGet:
case BinaryConsts::StructGetS:
case BinaryConsts::StructGetU: {
auto type = getIndexedHeapType(pos);
auto field = getU32LEB(pos);
return builder.makeStructGet(
type, field, op == BinaryConsts::StructGetS);
}
case BinaryConsts::StructSet: {
auto type = getIndexedHeapType(pos);
auto field = getU32LEB(pos);
return builder.makeStructSet(type, field);
}
case BinaryConsts::ArrayNew:
return builder.makeArrayNew(getIndexedHeapType(pos));
case BinaryConsts::ArrayNewDefault:
return builder.makeArrayNewDefault(getIndexedHeapType(pos));
case BinaryConsts::ArrayNewFixed: {
auto type = getIndexedHeapType(pos);
auto arity = getU32LEB(pos);
return builder.makeArrayNewFixed(type, arity);
}
case BinaryConsts::ArrayNewData: {
auto type = getIndexedHeapType(pos);
auto data = getDataName(pos, getU32LEB(pos));
return builder.makeArrayNewData(type, data);
}
case BinaryConsts::ArrayNewElem: {
auto type = getIndexedHeapType(pos);
auto elem = getElemName(pos, getU32LEB(pos));
return builder.makeArrayNewElem(type, elem);
}
case BinaryConsts::ArrayGet:
case BinaryConsts::ArrayGetU:
return builder.makeArrayGet(getIndexedHeapType(pos), false);
case BinaryConsts::ArrayGetS:
return builder.makeArrayGet(getIndexedHeapType(pos), true);
case BinaryConsts::ArraySet:
return builder.makeArraySet(getIndexedHeapType(pos));
case BinaryConsts::ArrayLen:
return builder.makeArrayLen();
case BinaryConsts::ArrayCopy: {
auto dest = getIndexedHeapType(pos);
auto src = getIndexedHeapType(pos);
return builder.makeArrayCopy(dest, src);
}
case BinaryConsts::ArrayFill:
return builder.makeArrayFill(getIndexedHeapType(pos));
case BinaryConsts::ArrayInitData: {
auto type = getIndexedHeapType(pos);
auto data = getDataName(pos, getU32LEB(pos));
return builder.makeArrayInitData(type, data);
}
case BinaryConsts::ArrayInitElem: {
auto type = getIndexedHeapType(pos);
auto elem = getElemName(pos, getU32LEB(pos));
return builder.makeArrayInitElem(type, elem);
}
case BinaryConsts::StringNewLossyUTF8Array:
return builder.makeStringNew(StringNewLossyUTF8Array);
case BinaryConsts::StringNewWTF16Array:
return builder.makeStringNew(StringNewWTF16Array);
case BinaryConsts::StringFromCodePoint:
return builder.makeStringNew(StringNewFromCodePoint);
case BinaryConsts::StringAsWTF16:
// This turns into nothing because we do not represent stringviews in
// the IR.
return Ok{};
case BinaryConsts::StringConst:
return builder.makeStringConst(getIndexedString(pos));
case BinaryConsts::StringMeasureUTF8:
return builder.makeStringMeasure(StringMeasureUTF8);
case BinaryConsts::StringMeasureWTF16:
return builder.makeStringMeasure(StringMeasureWTF16);
case BinaryConsts::StringEncodeLossyUTF8Array:
return builder.makeStringEncode(StringEncodeLossyUTF8Array);
case BinaryConsts::StringEncodeWTF16Array:
return builder.makeStringEncode(StringEncodeWTF16Array);
case BinaryConsts::StringConcat:
return builder.makeStringConcat();
case BinaryConsts::StringEq:
return builder.makeStringEq(StringEqEqual);
case BinaryConsts::StringCompare:
return builder.makeStringEq(StringEqCompare);
case BinaryConsts::StringViewWTF16GetCodePoint:
return builder.makeStringWTF16Get();
case BinaryConsts::StringViewWTF16Slice:
return builder.makeStringSliceWTF();
case BinaryConsts::AnyConvertExtern:
return builder.makeRefAs(AnyConvertExtern);
case BinaryConsts::ExternConvertAny:
return builder.makeRefAs(ExternConvertAny);
}
return Err{"unknown GC operation"};
}
}
return Err{"unknown operation"};
}
void WasmBinaryReader::readExports(size_t& pos) {
size_t num = getU32LEB(pos);
std::unordered_set<Name> names;
for (size_t i = 0; i < num; i++) {
auto curr = std::make_unique<Export>();
curr->name = getInlineString(pos);
if (!names.emplace(curr->name).second) {
throwError(pos, "duplicate export name");
}
curr->kind = (ExternalKind)getU32LEB(pos);
auto* ex = wasm.addExport(std::move(curr));
auto index = getU32LEB(pos);
switch (ex->kind) {
case ExternalKind::Function:
ex->value = getFunctionName(pos, index);
continue;
case ExternalKind::Table:
ex->value = getTableName(pos, index);
continue;
case ExternalKind::Memory:
ex->value = getMemoryName(pos, index);
continue;
case ExternalKind::Global:
ex->value = getGlobalName(pos, index);
continue;
case ExternalKind::Tag:
ex->value = getTagName(pos, index);
continue;
case ExternalKind::Invalid:
break;
}
throwError(pos, "invalid export kind");
}
}
Expression* WasmBinaryReader::readExpression(size_t& pos) {
assert(builder.empty());
while (input[pos] != BinaryConsts::End) {
auto inst = readInst(pos);
if (auto* err = inst.getErr()) {
throwError(pos, err->msg);
}
}
++pos;
auto expr = builder.build();
if (auto* err = expr.getErr()) {
throwError(pos, err->msg);
}
return *expr;
}
void WasmBinaryReader::readStrings(size_t& pos) {
auto reserved = getU32LEB(pos);
if (reserved != 0) {
throwError(pos, "unexpected reserved value in strings");
}
size_t num = getU32LEB(pos);
for (size_t i = 0; i < num; i++) {
auto string = getInlineString(pos, false);
// Re-encode from WTF-8 to WTF-16.
std::stringstream wtf16;
if (!String::convertWTF8ToWTF16(wtf16, string.str)) {
throwError(pos, "invalid string constant");
}
// TODO: Use wtf16.view() once we have C++20.
strings.push_back(wtf16.str());
}
}
Name WasmBinaryReader::getIndexedString(size_t& pos) {
auto index = getU32LEB(pos);
if (index >= strings.size()) {
throwError(pos, "bad string index");
}
return strings[index];
}
void WasmBinaryReader::readGlobals(size_t& pos) {
size_t num = getU32LEB(pos);
auto numImports = wasm.globals.size();
std::unordered_set<Name> usedNames;
for (auto& [index, name] : globalNames) {
if (index >= num + numImports) {
std::cerr << "warning: global index out of bounds in name section: "
<< name << " at index " << index << '\n';
}
usedNames.insert(name);
}
for (size_t i = 0; i < num; i++) {
auto [name, isExplicit] = getOrMakeName(
globalNames, numImports + i, makeName("global$", i), usedNames);
auto type = getConcreteType(pos);
auto mutable_ = getU32LEB(pos);
if (mutable_ & ~1) {
throwError(pos, "Global mutability must be 0 or 1");
}
auto* init = readExpression(pos);
auto global = Builder::makeGlobal(
name, type, init, mutable_ ? Builder::Mutable : Builder::Immutable);
global->hasExplicitName = isExplicit;
wasm.addGlobal(std::move(global));
}
}
void WasmBinaryReader::validateBinary(size_t& pos) {
if (hasDataCount && wasm.dataSegments.size() != dataCount) {
throwError(pos, "Number of segments does not agree with DataCount section");
}
if (functionTypes.size() != numFuncImports + numFuncBodies) {
throwError(pos, "function and code sections have inconsistent lengths");
}
}
void WasmBinaryReader::createDataSegments(Index count) {
std::unordered_set<Name> usedNames;
for (auto& [index, name] : dataNames) {
if (index >= count) {
std::cerr << "warning: data index out of bounds in name section: " << name
<< " at index " << index << '\n';
}
usedNames.insert(name);
}
for (size_t i = 0; i < count; ++i) {
auto [name, isExplicit] =
getOrMakeName(dataNames, i, makeName("", i), usedNames);
auto curr = Builder::makeDataSegment(name);
curr->hasExplicitName = isExplicit;
wasm.addDataSegment(std::move(curr));
}
}
void WasmBinaryReader::readDataSegmentCount(size_t& pos) {
hasDataCount = true;
dataCount = getU32LEB(pos);
// Eagerly create the data segments so they are available during parsing of
// the code section.
createDataSegments(dataCount);
}
void WasmBinaryReader::readDataSegments(size_t& pos) {
auto num = getU32LEB(pos);
if (hasDataCount) {
if (num != dataCount) {
throwError(pos, "data count and data sections disagree on size");
}
} else {
// We haven't already created the data segments, so create them now.
createDataSegments(num);
}
assert(wasm.dataSegments.size() == num);
for (size_t i = 0; i < num; i++) {
auto& curr = wasm.dataSegments[i];
uint32_t flags = getU32LEB(pos);
if (flags > 2) {
throwError(pos,
"bad segment flags, must be 0, 1, or 2, not " +
std::to_string(flags));
}
curr->isPassive = flags & BinaryConsts::IsPassive;
if (curr->isPassive) {
curr->memory = Name();
curr->offset = nullptr;
} else {
Index memIdx = 0;
if (flags & BinaryConsts::HasIndex) {
memIdx = getU32LEB(pos);
}
curr->memory = getMemoryName(pos, memIdx);
curr->offset = readExpression(pos);
}
auto size = getU32LEB(pos);
auto data = getByteView(pos, size);
curr->data = {data.begin(), data.end()};
}
}
void WasmBinaryReader::readTableDeclarations(size_t& pos) {
auto num = getU32LEB(pos);
auto numImports = wasm.tables.size();
std::unordered_set<Name> usedNames;
for (auto& [index, name] : tableNames) {
if (index >= num + numImports) {
std::cerr << "warning: table index out of bounds in name section: "
<< name << " at index " << index << '\n';
}
usedNames.insert(name);
}
for (size_t i = 0; i < num; i++) {
auto [name, isExplicit] =
getOrMakeName(tableNames, numImports + i, makeName("", i), usedNames);
auto elemType = getType(pos);
if (!elemType.isRef()) {
throwError(pos, "Table type must be a reference type");
}
auto table = Builder::makeTable(name, elemType);
table->hasExplicitName = isExplicit;
bool is_shared;
getResizableLimits(pos,
table->initial,
table->max,
is_shared,
table->addressType,
Table::kUnlimitedSize);
if (is_shared) {
throwError(pos, "Tables may not be shared");
}
wasm.addTable(std::move(table));
}
}
void WasmBinaryReader::readElementSegments(size_t& pos) {
auto num = getU32LEB(pos);
if (num >= Table::kMaxSize) {
throwError(pos, "Too many segments");
}
std::unordered_set<Name> usedNames;
for (auto& [index, name] : elemNames) {
if (index >= num) {
std::cerr << "warning: elem index out of bounds in name section: " << name
<< " at index " << index << '\n';
}
usedNames.insert(name);
}
for (size_t i = 0; i < num; i++) {
auto [name, isExplicit] =
getOrMakeName(elemNames, i, makeName("", i), usedNames);
auto flags = getU32LEB(pos);
bool isPassive = (flags & BinaryConsts::IsPassive) != 0;
bool hasTableIdx = !isPassive && ((flags & BinaryConsts::HasIndex) != 0);
bool isDeclarative =
isPassive && ((flags & BinaryConsts::IsDeclarative) != 0);
bool usesExpressions = (flags & BinaryConsts::UsesExpressions) != 0;
if (isDeclarative) {
// Declared segments are needed in wasm text and binary, but not in
// Binaryen IR; skip over the segment
[[maybe_unused]] auto type = getU32LEB(pos);
auto num = getU32LEB(pos);
for (Index i = 0; i < num; i++) {
if (usesExpressions) {
readExpression(pos);
} else {
getU32LEB(pos);
}
}
continue;
}
auto segment = std::make_unique<ElementSegment>();
segment->setName(name, isExplicit);
if (!isPassive) {
Index tableIdx = 0;
if (hasTableIdx) {
tableIdx = getU32LEB(pos);
}
if (tableIdx >= wasm.tables.size()) {
throwError(pos, "Table index out of range.");
}
auto* table = wasm.tables[tableIdx].get();
segment->table = table->name;
segment->offset = readExpression(pos);
}
if (isPassive || hasTableIdx) {
if (usesExpressions) {
segment->type = getType(pos);
} else {
auto elemKind = getU32LEB(pos);
if (elemKind != 0x0) {
throwError(pos,
"Invalid kind (!= funcref(0)) since !usesExpressions.");
}
}
}
auto& segmentData = segment->data;
auto size = getU32LEB(pos);
if (usesExpressions) {
for (Index j = 0; j < size; j++) {
segmentData.push_back(readExpression(pos));
}
} else {
for (Index j = 0; j < size; j++) {
Index index = getU32LEB(pos);
auto sig = getTypeByFunctionIndex(pos, index);
auto* refFunc =
Builder(wasm).makeRefFunc(getFunctionName(pos, index), sig);
segmentData.push_back(refFunc);
}
}
wasm.addElementSegment(std::move(segment));
}
}
void WasmBinaryReader::readTags(size_t& pos) {
size_t num = getU32LEB(pos);
auto numImports = wasm.tags.size();
std::unordered_set<Name> usedNames;
for (auto& [index, name] : tagNames) {
if (index >= num + numImports) {
std::cerr << "warning: tag index out of bounds in name section: " << name
<< " at index " << index << '\n';
}
usedNames.insert(name);
}
for (size_t i = 0; i < num; i++) {
getInt8(pos); // Reserved 'attribute' field
auto [name, isExplicit] =
getOrMakeName(tagNames, numImports + i, makeName("tag$", i), usedNames);
auto typeIndex = getU32LEB(pos);
auto tag = Builder::makeTag(name, getSignatureByTypeIndex(pos, typeIndex));
tag->hasExplicitName = isExplicit;
wasm.addTag(std::move(tag));
}
}
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 == '~';
}
static char formatNibble(int nibble) {
return nibble < 10 ? '0' + nibble : 'a' - 10 + nibble;
}
Name WasmBinaryReader::escape(Name name) {
bool allIdChars = true;
for (char c : name.str) {
if (!(allIdChars = isIdChar(c))) {
break;
}
}
if (allIdChars) {
return name;
}
// encode name, if at least one non-idchar (per WebAssembly spec) was found
std::string escaped;
for (char c : name.str) {
if (isIdChar(c)) {
escaped.push_back(c);
continue;
}
// replace non-idchar with `\xx` escape
escaped.push_back('\\');
escaped.push_back(formatNibble(c >> 4));
escaped.push_back(formatNibble(c & 15));
}
return escaped;
}
namespace {
// Performs necessary processing of names from the name section before using
// them. Specifically it escapes and deduplicates them.
class NameProcessor {
public:
// Returns a unique, escaped name. Notes that name for the items to follow to
// keep them unique as well.
Name process(Name name) {
return deduplicate(WasmBinaryReader::escape(name));
}
private:
std::unordered_set<Name> usedNames;
Name deduplicate(Name base) {
auto name = Names::getValidNameGivenExisting(base, usedNames);
usedNames.insert(name);
return name;
}
};
} // anonymous namespace
void WasmBinaryReader::findAndReadNames() {
// Find the names section. Skip the magic and version.
size_t pos = 0;
getInt32(pos);
getInt32(pos);
Index payloadLen, sectionPos;
bool found = false;
while (more(pos)) {
uint8_t sectionCode = getInt8(pos);
payloadLen = getU32LEB(pos);
sectionPos = pos;
if (sectionCode == BinaryConsts::Section::Custom) {
auto sectionName = getInlineString(pos);
if (sectionName.equals(BinaryConsts::CustomSections::Name)) {
found = true;
break;
}
}
pos = sectionPos + payloadLen;
}
if (!found) {
// No names section to read.
pos = 0;
return;
}
// Read the names.
uint32_t lastType = 0;
while (pos < sectionPos + payloadLen) {
auto nameType = getU32LEB(pos);
if (lastType && nameType <= lastType) {
std::cerr << "warning: out-of-order name subsection: " << nameType
<< std::endl;
}
lastType = nameType;
auto subsectionSize = getU32LEB(pos);
auto subsectionPos = pos;
using Subsection = BinaryConsts::CustomSections::Subsection;
if (nameType == Subsection::NameModule) {
wasm.name = getInlineString(pos);
} else if (nameType == Subsection::NameFunction) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
functionNames[index] = name;
}
} else if (nameType == Subsection::NameLocal) {
auto numFuncs = getU32LEB(pos);
for (size_t i = 0; i < numFuncs; i++) {
auto funcIndex = getU32LEB(pos);
auto numLocals = getU32LEB(pos);
NameProcessor processor;
for (size_t j = 0; j < numLocals; j++) {
auto localIndex = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
localNames[funcIndex][localIndex] = name;
}
}
} else if (nameType == Subsection::NameType) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
typeNames[index] = name;
}
} else if (nameType == Subsection::NameTable) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
tableNames[index] = name;
}
} else if (nameType == Subsection::NameElem) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
elemNames[index] = name;
}
} else if (nameType == Subsection::NameMemory) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
memoryNames[index] = name;
}
} else if (nameType == Subsection::NameData) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
dataNames[index] = name;
}
} else if (nameType == Subsection::NameGlobal) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
globalNames[index] = name;
}
} else if (nameType == Subsection::NameField) {
auto numTypes = getU32LEB(pos);
for (size_t i = 0; i < numTypes; i++) {
auto typeIndex = getU32LEB(pos);
bool validType =
typeIndex < types.size() && types[typeIndex].isStruct();
if (!validType) {
std::cerr << "warning: invalid field index in name field section\n";
}
auto numFields = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < numFields; i++) {
auto fieldIndex = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
fieldNames[typeIndex][fieldIndex] = name;
}
}
} else if (nameType == Subsection::NameTag) {
auto num = getU32LEB(pos);
NameProcessor processor;
for (size_t i = 0; i < num; i++) {
auto index = getU32LEB(pos);
auto rawName = getInlineString(pos);
auto name = processor.process(rawName);
tagNames[index] = name;
}
} else {
std::cerr << "warning: unknown name subsection with id "
<< std::to_string(nameType) << " at " << pos << std::endl;
pos = subsectionPos + subsectionSize;
}
if (pos != subsectionPos + subsectionSize) {
throwError(pos, "bad names subsection position change");
}
}
if (pos != sectionPos + payloadLen) {
throwError(pos, "bad names section position change");
}
}
void WasmBinaryReader::readFeatures(size_t& pos, size_t payloadLen) {
wasm.hasFeaturesSection = true;
auto sectionPos = pos;
size_t numFeatures = getU32LEB(pos);
for (size_t i = 0; i < numFeatures; ++i) {
uint8_t prefix = getInt8(pos);
bool disallowed = prefix == BinaryConsts::FeatureDisallowed;
bool used = prefix == BinaryConsts::FeatureUsed;
if (!disallowed && !used) {
throwError(pos, "Unrecognized feature policy prefix");
}
Name name = getInlineString(pos);
if (pos > sectionPos + payloadLen) {
throwError(pos, "ill-formed string extends beyond section");
}
FeatureSet feature;
if (name == BinaryConsts::CustomSections::AtomicsFeature) {
feature = FeatureSet::Atomics;
} else if (name == BinaryConsts::CustomSections::BulkMemoryFeature) {
feature = FeatureSet::BulkMemory;
} else if (name == BinaryConsts::CustomSections::ExceptionHandlingFeature) {
feature = FeatureSet::ExceptionHandling;
} else if (name == BinaryConsts::CustomSections::MutableGlobalsFeature) {
feature = FeatureSet::MutableGlobals;
} else if (name == BinaryConsts::CustomSections::TruncSatFeature) {
feature = FeatureSet::TruncSat;
} else if (name == BinaryConsts::CustomSections::SignExtFeature) {
feature = FeatureSet::SignExt;
} else if (name == BinaryConsts::CustomSections::SIMD128Feature) {
feature = FeatureSet::SIMD;
} else if (name == BinaryConsts::CustomSections::TailCallFeature) {
feature = FeatureSet::TailCall;
} else if (name == BinaryConsts::CustomSections::ReferenceTypesFeature) {
feature = FeatureSet::ReferenceTypes;
} else if (name == BinaryConsts::CustomSections::MultivalueFeature) {
feature = FeatureSet::Multivalue;
} else if (name == BinaryConsts::CustomSections::GCFeature) {
feature = FeatureSet::GC;
} else if (name == BinaryConsts::CustomSections::Memory64Feature) {
feature = FeatureSet::Memory64;
} else if (name == BinaryConsts::CustomSections::RelaxedSIMDFeature) {
feature = FeatureSet::RelaxedSIMD;
} else if (name == BinaryConsts::CustomSections::ExtendedConstFeature) {
feature = FeatureSet::ExtendedConst;
} else if (name == BinaryConsts::CustomSections::StringsFeature) {
feature = FeatureSet::Strings;
} else if (name == BinaryConsts::CustomSections::MultiMemoryFeature) {
feature = FeatureSet::MultiMemory;
} else if (name ==
BinaryConsts::CustomSections::TypedContinuationsFeature) {
feature = FeatureSet::TypedContinuations;
} else if (name == BinaryConsts::CustomSections::SharedEverythingFeature) {
feature = FeatureSet::SharedEverything;
} else if (name == BinaryConsts::CustomSections::FP16Feature) {
feature = FeatureSet::FP16;
} else {
// Silently ignore unknown features (this may be and old binaryen running
// on a new wasm).
}
if (disallowed && wasm.features.has(feature)) {
std::cerr
<< "warning: feature " << feature.toString()
<< " was enabled by the user, but disallowed in the features section.";
}
if (used) {
wasm.features.enable(feature);
}
}
if (pos != sectionPos + payloadLen) {
throwError(pos, "bad features section size");
}
}
void WasmBinaryReader::readDylink(size_t& pos, size_t payloadLen) {
wasm.dylinkSection = std::make_unique<DylinkSection>();
auto sectionPos = pos;
wasm.dylinkSection->isLegacy = true;
wasm.dylinkSection->memorySize = getU32LEB(pos);
wasm.dylinkSection->memoryAlignment = getU32LEB(pos);
wasm.dylinkSection->tableSize = getU32LEB(pos);
wasm.dylinkSection->tableAlignment = getU32LEB(pos);
size_t numNeededDynlibs = getU32LEB(pos);
for (size_t i = 0; i < numNeededDynlibs; ++i) {
wasm.dylinkSection->neededDynlibs.push_back(getInlineString(pos));
}
if (pos != sectionPos + payloadLen) {
throwError(pos, "bad dylink section size");
}
}
void WasmBinaryReader::readDylink0(size_t& pos, size_t payloadLen) {
auto sectionPos = pos;
uint32_t lastType = 0;
wasm.dylinkSection = std::make_unique<DylinkSection>();
while (pos < sectionPos + payloadLen) {
auto oldPos = pos;
auto dylinkType = getU32LEB(pos);
if (lastType && dylinkType <= lastType) {
std::cerr << "warning: out-of-order dylink.0 subsection: " << dylinkType
<< std::endl;
}
lastType = dylinkType;
auto subsectionSize = getU32LEB(pos);
auto subsectionPos = pos;
if (dylinkType == BinaryConsts::CustomSections::Subsection::DylinkMemInfo) {
wasm.dylinkSection->memorySize = getU32LEB(pos);
wasm.dylinkSection->memoryAlignment = getU32LEB(pos);
wasm.dylinkSection->tableSize = getU32LEB(pos);
wasm.dylinkSection->tableAlignment = getU32LEB(pos);
} else if (dylinkType ==
BinaryConsts::CustomSections::Subsection::DylinkNeeded) {
size_t numNeededDynlibs = getU32LEB(pos);
for (size_t i = 0; i < numNeededDynlibs; ++i) {
wasm.dylinkSection->neededDynlibs.push_back(getInlineString(pos));
}
} else {
// Unknown subsection. Stop parsing now and store the rest of
// the section verbatim.
pos = oldPos;
size_t remaining = (sectionPos + payloadLen) - pos;
auto tail = getByteView(pos, remaining);
wasm.dylinkSection->tail = {tail.begin(), tail.end()};
break;
}
if (pos != subsectionPos + subsectionSize) {
throwError(pos, "bad dylink.0 subsection position change");
}
}
}
Index WasmBinaryReader::readMemoryAccess(size_t& pos,
Address& alignment,
Address& offset) {
auto rawAlignment = getU32LEB(pos);
bool hasMemIdx = false;
Index memIdx = 0;
// Check bit 6 in the alignment to know whether a memory index is present per:
// https://github.com/WebAssembly/multi-memory/blob/main/proposals/multi-memory/Overview.md
if (rawAlignment & (1 << (6))) {
hasMemIdx = true;
// Clear the bit before we parse alignment
rawAlignment = rawAlignment & ~(1 << 6);
}
if (rawAlignment > 8) {
throwError(pos, "Alignment must be of a reasonable size");
}
alignment = Bits::pow2(rawAlignment);
if (hasMemIdx) {
memIdx = getU32LEB(pos);
}
if (memIdx >= wasm.memories.size()) {
throwError(pos,
"Memory index out of range while reading memory alignment.");
}
auto* memory = wasm.memories[memIdx].get();
offset = memory->addressType == Type::i32 ? getU32LEB(pos) : getU64LEB(pos);
return memIdx;
}
// TODO: make this the only version
std::tuple<Name, Address, Address> WasmBinaryReader::getMemarg(size_t& pos) {
Address alignment, offset;
auto memIdx = readMemoryAccess(pos, alignment, offset);
return {getMemoryName(pos, memIdx), alignment, offset};
}
} // namespace wasm