summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm-binary.h27
-rw-r--r--src/wasm/wasm-binary.cpp90
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;