diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/wasm-binary.h | 27 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 90 |
2 files changed, 105 insertions, 12 deletions
diff --git a/src/wasm-binary.h b/src/wasm-binary.h index ea18fdf67..bda793216 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1004,6 +1004,7 @@ enum ASTNodes { CallRef = 0x14, RetCallRef = 0x15, + Let = 0x17, // gc opcodes @@ -1348,6 +1349,7 @@ public: void requireFunctionContext(const char* error); void readFunctions(); + void readVars(); std::map<Export*, Index> exportIndices; std::vector<Export*> exportOrder; @@ -1368,6 +1370,29 @@ public: std::vector<Expression*> expressionStack; + // Each let block in the binary adds new locals to the bottom of the index + // space. That is, all previously-existing indexes are bumped to higher + // indexes. getAbsoluteLocalIndex does this computation. + // Note that we must track not just the number of locals added in each let, + // but also the absolute index from which they were allocated, as binaryen + // will add new locals as it goes for things like stacky code and tuples (so + // there isn't a simple way to get to the absolute index from a relative one). + // Hence each entry here is a pair of the number of items, and the absolute + // index they begin at. + struct LetData { + // How many items are defined in this let. + Index num; + // The absolute index from which they are allocated from. That is, if num is + // 5 and absoluteStart is 10, then we use indexes 10-14. + Index absoluteStart; + }; + std::vector<LetData> letStack; + + // Given a relative index of a local (the one used in the wasm binary), get + // the absolute one which takes into account lets, and is the one used in + // Binaryen IR. + Index getAbsoluteLocalIndex(Index index); + // Control flow structure parsing: these have not just the normal binary // data for an instruction, but also some bytes later on like "end" or "else". // We must be aware of the connection between those things, for debug info. @@ -1522,6 +1547,8 @@ public: void visitRethrow(Rethrow* curr); void visitBrOnExn(BrOnExn* curr); void visitCallRef(CallRef* curr); + // Let is lowered into a block. + void visitLet(Block* curr); void throwError(std::string text); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 9b4794997..bf2e4afef 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1833,15 +1833,9 @@ void WasmBinaryBuilder::readFunctions() { readNextDebugLocation(); BYN_TRACE("reading " << i << std::endl); - size_t numLocalTypes = getU32LEB(); - for (size_t t = 0; t < numLocalTypes; t++) { - auto num = getU32LEB(); - auto type = getConcreteType(); - while (num > 0) { - func->vars.push_back(type); - num--; - } - } + + readVars(); + std::swap(func->prologLocation, debugLocation); { // process the function body @@ -1854,6 +1848,7 @@ void WasmBinaryBuilder::readFunctions() { assert(breakStack.empty()); assert(expressionStack.empty()); assert(controlFlowStack.empty()); + assert(letStack.empty()); assert(depth == 0); func->body = getBlockOrSingleton(func->sig.results); assert(depth == 0); @@ -1863,6 +1858,7 @@ void WasmBinaryBuilder::readFunctions() { throwError("stack not empty on function exit"); } assert(controlFlowStack.empty()); + assert(letStack.empty()); if (pos != endOfFunction) { throwError("binary offset at function exit not at expected location"); } @@ -1875,6 +1871,18 @@ void WasmBinaryBuilder::readFunctions() { BYN_TRACE(" end function bodies\n"); } +void WasmBinaryBuilder::readVars() { + size_t numLocalTypes = getU32LEB(); + for (size_t t = 0; t < numLocalTypes; t++) { + auto num = getU32LEB(); + auto type = getConcreteType(); + while (num > 0) { + currFunction->vars.push_back(type); + num--; + } + } +} + void WasmBinaryBuilder::readExports() { BYN_TRACE("== readExports\n"); size_t num = getU32LEB(); @@ -2886,6 +2894,10 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { visitCallRef(call); break; } + case BinaryConsts::Let: { + visitLet((curr = allocator.alloc<Block>())->cast<Block>()); + break; + } case BinaryConsts::AtomicPrefix: { code = static_cast<uint8_t>(getU32LEB()); if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) { @@ -3056,6 +3068,36 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { return BinaryConsts::ASTNodes(code); } +Index WasmBinaryBuilder::getAbsoluteLocalIndex(Index index) { + // Wasm binaries put each let at the bottom of the index space, which may be + // good for binary size as often the uses of the let variables are close to + // the let itself. However, in Binaryen IR we just have a simple flat index + // space of absolute values, which we add to as we parse, and we depend on + // later optimizations to reorder locals for size. + // + // For example, if we have $x, then we add a let with $y, the binary would map + // 0 => y, 1 => x, while in Binaryen IR $x always stays at 0, and $y is added + // at 1. + // + // Compute the relative index in the let we were added. We start by looking at + // the last let added, and if we belong to it, we are already relative to it. + // We will continue relativizing as we go down, til we find our let. + int64_t relative = index; + for (auto i = int64_t(letStack.size()) - 1; i >= 0; i--) { + auto& info = letStack[i]; + int64_t currNum = info.num; + // There were |currNum| let items added in this let. Check if we were one of + // them. + if (relative < currNum) { + return info.absoluteStart + relative; + } + relative -= currNum; + } + // We were not a let, but a normal var from the beginning. In that case, after + // we subtracted the let items, we have the proper absolute index. + return relative; +} + void WasmBinaryBuilder::startControlFlow(Expression* curr) { if (DWARF && currFunction) { controlFlowStack.push_back(curr); @@ -3322,9 +3364,8 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) { void WasmBinaryBuilder::visitLocalGet(LocalGet* curr) { BYN_TRACE("zz node: LocalGet " << pos << std::endl); - ; requireFunctionContext("local.get"); - curr->index = getU32LEB(); + curr->index = getAbsoluteLocalIndex(getU32LEB()); if (curr->index >= currFunction->getNumLocals()) { throwError("bad local.get index"); } @@ -3335,7 +3376,7 @@ void WasmBinaryBuilder::visitLocalGet(LocalGet* curr) { void WasmBinaryBuilder::visitLocalSet(LocalSet* curr, uint8_t code) { BYN_TRACE("zz node: Set|LocalTee\n"); requireFunctionContext("local.set outside of function"); - curr->index = getU32LEB(); + curr->index = getAbsoluteLocalIndex(getU32LEB()); if (curr->index >= currFunction->getNumLocals()) { throwError("bad local.set index"); } @@ -5669,6 +5710,31 @@ void WasmBinaryBuilder::visitCallRef(CallRef* curr) { curr->finalize(sig.results); } +void WasmBinaryBuilder::visitLet(Block* curr) { + // A let is lowered into a block that contains the value, and we allocate + // locals as needed, which works as we remove non-nullability. + + startControlFlow(curr); + // Get the output type. + curr->type = getType(); + // Get the new local types. First, get the absolute index from which we will + // start to allocate them. + Index absoluteStart = currFunction->vars.size(); + readVars(); + Index numNewVars = currFunction->vars.size() - absoluteStart; + // Assign the values into locals. + Builder builder(wasm); + for (Index i = 0; i < numNewVars; i++) { + auto* value = popNonVoidExpression(); + curr->list.push_back(builder.makeLocalSet(absoluteStart + i, value)); + } + // Read the body, with adjusted local indexes. + letStack.emplace_back(LetData{numNewVars, absoluteStart}); + curr->list.push_back(getBlockOrSingleton(curr->type)); + letStack.pop_back(); + curr->finalize(curr->type); +} + bool WasmBinaryBuilder::maybeVisitI31New(Expression*& out, uint32_t code) { if (code != BinaryConsts::I31New) { return false; |