diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/wasm-ir-builder.h | 51 | ||||
-rw-r--r-- | src/wasm/wasm-ir-builder.cpp | 454 | ||||
-rw-r--r-- | src/wasm/wat-parser.cpp | 82 |
3 files changed, 349 insertions, 238 deletions
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 6a1b3f18a..3805a3e38 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -42,9 +42,7 @@ public: // Get the valid Binaryen IR expression representing the sequence of visited // instructions. The IRBuilder is reset and can be used with a fresh sequence // of instructions after this is called. - Expression* build(); - - [[nodiscard]] Result<std::vector<Expression*>> finishInstrs(); + [[nodiscard]] Result<Expression*> build(); // Call visit() on an existing Expression with its non-child fields // initialized to initialize the child fields and refinalize it. The specific @@ -56,11 +54,13 @@ public: [[nodiscard]] Result<> visitStructNew(StructNew*); [[nodiscard]] Result<> visitArrayNew(ArrayNew*); + [[nodiscard]] Result<> visitEnd(); + // Alternatively, call makeXYZ to have the IRBuilder allocate the nodes. This // is generally safer than calling `visit` because the function signatures // ensure that there are no missing fields. [[nodiscard]] Result<> makeNop(); - [[nodiscard]] Result<> makeBlock(); + [[nodiscard]] Result<> makeBlock(Name label, Type type); // [[nodiscard]] Result<> makeIf(); // [[nodiscard]] Result<> makeLoop(); // [[nodiscard]] Result<> makeBreak(); @@ -168,9 +168,6 @@ public: // [[nodiscard]] Result<> makeStringSliceWTF(); // [[nodiscard]] Result<> makeStringSliceIter(); - // TODO: make this private. - void pushScope(Type type) { scopeStack.push_back({{}, type}); } - void setFunction(Function* func) { this->func = func; } private: @@ -183,24 +180,42 @@ private: // to have. struct BlockCtx { std::vector<Expression*> exprStack; - Type type; + Block* block; + // Whether we have seen an unreachable instruction and are in + // stack-polymorphic unreachable mode. + bool unreachable = false; }; // The stack of block contexts currently being parsed. std::vector<BlockCtx> scopeStack; - std::vector<Expression*>& getExprStack(); - Type getResultType() { - assert(!scopeStack.empty()); - return scopeStack.back().type; + + BlockCtx& getScope() { + if (scopeStack.empty()) { + // We are not in a block context, so push a dummy scope. + scopeStack.push_back({{}, nullptr}); + } + return scopeStack.back(); } - // Whether we have seen an unreachable instruction and are in - // stack-polymorphic unreachable mode. - bool unreachable = false; + [[nodiscard]] Result<Index> addScratchLocal(Type); + [[nodiscard]] Result<Expression*> pop(); + void push(Expression*); + + struct HoistedVal { + // The index in the stack of the original value-producing expression. + Index valIndex; + // The local.get placed on the stack, if any. + LocalGet* get; + }; - Result<Index> addScratchLocal(Type); - [[nodiscard]] Result<> push(Expression*); - Result<Expression*> pop(); + // Find the last value-producing expression, if any, and hoist its value to + // the top of the stack using a scratch local if necessary. + [[nodiscard]] MaybeResult<HoistedVal> hoistLastValue(); + // Transform the stack as necessary such that the original producer of the + // hoisted value will be popped along with the final expression that produces + // the value, if they are different. May only be called directly after + // hoistLastValue(). + [[nodiscard]] Result<> packageHoistedValue(const HoistedVal&); }; } // namespace wasm diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 6786816f3..7e163fe4e 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -39,14 +39,6 @@ Result<> validateTypeAnnotation(HeapType type, Expression* child) { } // anonymous namespace -std::vector<Expression*>& IRBuilder::getExprStack() { - if (scopeStack.empty()) { - // We are not in a function, so push a dummy scope. - scopeStack.push_back({{}, Type::none}); - } - return scopeStack.back().exprStack; -} - Result<Index> IRBuilder::addScratchLocal(Type type) { if (!func) { return Err{"scratch local required, but there is no function context"}; @@ -55,125 +47,141 @@ Result<Index> IRBuilder::addScratchLocal(Type type) { return Builder::addVar(func, name, type); } -Result<> IRBuilder::push(Expression* expr) { - auto& exprStack = getExprStack(); +MaybeResult<IRBuilder::HoistedVal> IRBuilder::hoistLastValue() { + auto& stack = getScope().exprStack; + int index = stack.size() - 1; + for (; index >= 0; --index) { + if (stack[index]->type != Type::none) { + break; + } + } + if (index < 0) { + // There is no value-producing or unreachable expression. + return {}; + } + if (unsigned(index) == stack.size() - 1) { + // Value-producing expression already on top of the stack. + return HoistedVal{Index(index), nullptr}; + } + auto*& expr = stack[index]; + auto type = expr->type; + if (type == Type::unreachable) { + // Make sure the top of the stack also has an unreachable expression. + if (stack.back()->type != Type::unreachable) { + push(builder.makeUnreachable()); + } + return HoistedVal{Index(index), nullptr}; + } + // Hoist with a scratch local. + auto scratchIdx = addScratchLocal(type); + CHECK_ERR(scratchIdx); + expr = builder.makeLocalSet(*scratchIdx, expr); + auto* get = builder.makeLocalGet(*scratchIdx, type); + push(get); + return HoistedVal{Index(index), get}; +} + +Result<> IRBuilder::packageHoistedValue(const HoistedVal& hoisted) { + auto& scope = getScope(); + assert(!scope.exprStack.empty()); + + auto packageAsBlock = [&](Type type) { + // Create a block containing the producer of the hoisted value, the final + // get of the hoisted value, and everything in between. + std::vector<Expression*> exprs(scope.exprStack.begin() + hoisted.valIndex, + scope.exprStack.end()); + auto* block = builder.makeBlock(exprs, type); + scope.exprStack.resize(hoisted.valIndex); + push(block); + }; + + auto type = scope.exprStack.back()->type; + + if (!type.isTuple()) { + if (hoisted.get) { + packageAsBlock(type); + } + return Ok{}; + } + + // We need to break up the hoisted tuple. Create and push a block setting the + // tuple to a local and returning its first element, then push additional gets + // of each of its subsequent elements. Reuse the scratch local we used for + // hoisting, if it exists. + Index scratchIdx; + if (hoisted.get) { + // Update the get on top of the stack to just return the first element. + scope.exprStack.back() = builder.makeTupleExtract(hoisted.get, 0); + packageAsBlock(type[0]); + scratchIdx = hoisted.get->index; + } else { + auto scratch = addScratchLocal(type); + CHECK_ERR(scratch); + auto* block = builder.makeSequence( + builder.makeLocalSet(*scratch, scope.exprStack.back()), + builder.makeTupleExtract(builder.makeLocalGet(*scratch, type), 0), + type[0]); + scope.exprStack.pop_back(); + push(block); + scratchIdx = *scratch; + } + for (Index i = 1, size = type.size(); i < size; ++i) { + push(builder.makeTupleExtract(builder.makeLocalGet(scratchIdx, type), i)); + } + return Ok{}; +} + +void IRBuilder::push(Expression* expr) { + auto& scope = getScope(); if (expr->type == Type::unreachable) { // We want to avoid popping back past this most recent unreachable // instruction. Drop all prior instructions so they won't be consumed by // later instructions but will still be emitted for their side effects, if // any. - for (auto& expr : exprStack) { + for (auto& expr : scope.exprStack) { expr = builder.dropIfConcretelyTyped(expr); } - unreachable = true; - exprStack.push_back(expr); - } else if (expr->type.isTuple()) { - auto scratchIdx = addScratchLocal(expr->type); - CHECK_ERR(scratchIdx); - CHECK_ERR(push(builder.makeLocalSet(*scratchIdx, expr))); - for (Index i = 0; i < expr->type.size(); ++i) { - CHECK_ERR(push(builder.makeTupleExtract( - builder.makeLocalGet(*scratchIdx, expr->type), i))); - } - } else { - exprStack.push_back(expr); + scope.unreachable = true; } - return Ok{}; + scope.exprStack.push_back(expr); } Result<Expression*> IRBuilder::pop() { - auto& exprStack = getExprStack(); + auto& scope = getScope(); // Find the suffix of expressions that do not produce values. - auto firstNone = exprStack.size(); - for (; firstNone > 0; --firstNone) { - auto* expr = exprStack[firstNone - 1]; - if (expr->type != Type::none) { - break; - } - } + auto hoisted = hoistLastValue(); + CHECK_ERR(hoisted); - if (firstNone == 0) { + if (!hoisted) { // There are no expressions that produce values. - if (unreachable) { + if (scope.unreachable) { return builder.makeUnreachable(); } return Err{"popping from empty stack"}; } - if (firstNone == exprStack.size()) { - // The last expression produced a value. - auto expr = exprStack.back(); - exprStack.pop_back(); - return expr; - } + CHECK_ERR(packageHoistedValue(*hoisted)); - // We need to assemble a block of expressions that returns the value of the - // first one using a scratch local (unless it's unreachable, in which case - // we can throw the following expressions away). - auto* expr = exprStack[firstNone - 1]; - if (expr->type == Type::unreachable) { - exprStack.resize(firstNone - 1); - return expr; + auto* ret = scope.exprStack.back(); + scope.exprStack.pop_back(); + return ret; +} + +Result<Expression*> IRBuilder::build() { + if (scopeStack.empty()) { + return builder.makeNop(); } - auto scratchIdx = addScratchLocal(expr->type); - CHECK_ERR(scratchIdx); - std::vector<Expression*> exprs; - exprs.reserve(exprStack.size() - firstNone + 2); - exprs.push_back(builder.makeLocalSet(*scratchIdx, expr)); - exprs.insert(exprs.end(), exprStack.begin() + firstNone, exprStack.end()); - exprs.push_back(builder.makeLocalGet(*scratchIdx, expr->type)); - - exprStack.resize(firstNone - 1); - return builder.makeBlock(exprs, expr->type); -} - -Expression* IRBuilder::build() { - auto& exprStack = getExprStack(); - assert(scopeStack.size() == 1); - assert(exprStack.size() == 1); - - auto e = exprStack.back(); - exprStack.clear(); - unreachable = false; - return e; -} - -Result<std::vector<Expression*>> IRBuilder::finishInstrs() { - auto& exprStack = getExprStack(); - auto type = getResultType(); - - // We have finished parsing a sequence of instructions. Fix up the parsed - // instructions and reset the context for the next sequence. - if (type.isTuple()) { - std::vector<Expression*> elems(type.size()); - bool hadUnreachableElem = false; - for (size_t i = 0; i < elems.size(); ++i) { - auto elem = pop(); - CHECK_ERR(elem); - elems[elems.size() - 1 - i] = *elem; - if ((*elem)->type == Type::unreachable) { - // We don't want to pop back past an unreachable here. Push the - // unreachable back and throw away any post-unreachable values we have - // popped. - exprStack.push_back(*elem); - hadUnreachableElem = true; - break; - } - } - if (!hadUnreachableElem) { - exprStack.push_back(builder.makeTupleMake(std::move(elems))); - } - } else if (type != Type::none) { - // Ensure the last expression produces the value. - auto expr = pop(); - CHECK_ERR(expr); - exprStack.push_back(*expr); + if (scopeStack.size() > 1 || scopeStack.back().block != nullptr) { + return Err{"unfinished block context"}; } - unreachable = false; - auto ret = std::move(exprStack); - scopeStack.pop_back(); - return ret; + if (scopeStack.back().exprStack.size() > 1) { + return Err{"unused expressions without block context"}; + } + assert(scopeStack.back().exprStack.size() == 1); + auto* expr = scopeStack.back().exprStack.back(); + scopeStack.clear(); + return expr; } Result<> IRBuilder::visit(Expression* curr) { @@ -185,7 +193,8 @@ Result<> IRBuilder::visit(Expression* curr) { // for other kinds of nodes as well, as done above. ReFinalizeNode{}.visit(curr); } - return push(curr); + push(curr); + return Ok{}; } // Handle the common case of instructions with a constant number of children @@ -224,7 +233,7 @@ Result<> IRBuilder::visitExpression(Expression* curr) { } Result<> IRBuilder::visitBlock(Block* curr) { - // TODO: Handle popping scope and filling block here instead of externally. + scopeStack.push_back({{}, curr}); return Ok{}; } @@ -272,9 +281,75 @@ Result<> IRBuilder::visitArrayNew(ArrayNew* curr) { return Ok{}; } -Result<> IRBuilder::makeNop() { return push(builder.makeNop()); } +Result<> IRBuilder::visitEnd() { + if (scopeStack.empty() || !scopeStack.back().block) { + return Err{"unexpected end"}; + } -Result<> IRBuilder::makeBlock() { return push(builder.makeBlock()); } + auto& scope = scopeStack.back(); + Block* block = scope.block; + if (block->type.isTuple()) { + if (scope.unreachable) { + // We may not have enough concrete values on the stack to construct the + // full tuple, and if we tried to fill out the beginning of a tuple.make + // with additional popped `unreachable`s, that could cause a trap to + // happen before important side effects. Instead, just drop everything on + // the stack and finish with a single unreachable. + // + // TODO: Validate that the available expressions are a correct suffix of + // the expected type, since this will no longer be caught by normal + // validation? + for (auto& expr : scope.exprStack) { + expr = builder.dropIfConcretelyTyped(expr); + } + if (scope.exprStack.back()->type != Type::unreachable) { + scope.exprStack.push_back(builder.makeUnreachable()); + } + } else { + auto hoisted = hoistLastValue(); + CHECK_ERR(hoisted); + auto hoistedType = scope.exprStack.back()->type; + if (hoistedType.size() != block->type.size()) { + // We cannot propagate the hoisted value directly because it does not + // have the correct number of elements. Break it up if necessary and + // construct our returned tuple from parts. + CHECK_ERR(packageHoistedValue(*hoisted)); + std::vector<Expression*> elems(block->type.size()); + for (size_t i = 0; i < elems.size(); ++i) { + auto elem = pop(); + CHECK_ERR(elem); + elems[elems.size() - 1 - i] = *elem; + } + scope.exprStack.push_back(builder.makeTupleMake(std::move(elems))); + } + } + } else if (block->type.isConcrete()) { + // If the value is buried in none-typed expressions, we have to bring it to + // the top. + auto hoisted = hoistLastValue(); + CHECK_ERR(hoisted); + } + block->list.set(scope.exprStack); + // TODO: Track branches so we can know whether this block is a target and + // finalize more efficiently. + block->finalize(block->type); + scopeStack.pop_back(); + push(block); + return Ok{}; +} + +Result<> IRBuilder::makeNop() { + push(builder.makeNop()); + return Ok{}; +} + +Result<> IRBuilder::makeBlock(Name label, Type type) { + auto* block = wasm.allocator.alloc<Block>(); + block->name = label; + block->type = type; + scopeStack.push_back({{}, block}); + return Ok{}; +} // Result<> IRBuilder::makeIf() {} @@ -289,30 +364,34 @@ Result<> IRBuilder::makeBlock() { return push(builder.makeBlock()); } // Result<> IRBuilder::makeCallIndirect() {} Result<> IRBuilder::makeLocalGet(Index local) { - return push(builder.makeLocalGet(local, func->getLocalType(local))); + push(builder.makeLocalGet(local, func->getLocalType(local))); + return Ok{}; } Result<> IRBuilder::makeLocalSet(Index local) { LocalSet curr; CHECK_ERR(visitLocalSet(&curr)); - return push(builder.makeLocalSet(local, curr.value)); + push(builder.makeLocalSet(local, curr.value)); + return Ok{}; } Result<> IRBuilder::makeLocalTee(Index local) { LocalSet curr; CHECK_ERR(visitLocalSet(&curr)); - return push( - builder.makeLocalTee(local, curr.value, func->getLocalType(local))); + push(builder.makeLocalTee(local, curr.value, func->getLocalType(local))); + return Ok{}; } Result<> IRBuilder::makeGlobalGet(Name global) { - return push(builder.makeGlobalGet(global, wasm.getGlobal(global)->type)); + push(builder.makeGlobalGet(global, wasm.getGlobal(global)->type)); + return Ok{}; } Result<> IRBuilder::makeGlobalSet(Name global) { GlobalSet curr; CHECK_ERR(visitGlobalSet(&curr)); - return push(builder.makeGlobalSet(global, curr.value)); + push(builder.makeGlobalSet(global, curr.value)); + return Ok{}; } Result<> IRBuilder::makeLoad(unsigned bytes, @@ -323,23 +402,25 @@ Result<> IRBuilder::makeLoad(unsigned bytes, Name mem) { Load curr; CHECK_ERR(visitLoad(&curr)); - return push( - builder.makeLoad(bytes, signed_, offset, align, curr.ptr, type, mem)); + push(builder.makeLoad(bytes, signed_, offset, align, curr.ptr, type, mem)); + return Ok{}; } Result<> IRBuilder::makeStore( unsigned bytes, Address offset, unsigned align, Type type, Name mem) { Store curr; CHECK_ERR(visitStore(&curr)); - return push( + push( builder.makeStore(bytes, offset, align, curr.ptr, curr.value, type, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicLoad(unsigned bytes, Address offset, Type type, Name mem) { Load curr; CHECK_ERR(visitLoad(&curr)); - return push(builder.makeAtomicLoad(bytes, offset, curr.ptr, type, mem)); + push(builder.makeAtomicLoad(bytes, offset, curr.ptr, type, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicStore(unsigned bytes, @@ -348,16 +429,17 @@ Result<> IRBuilder::makeAtomicStore(unsigned bytes, Name mem) { Store curr; CHECK_ERR(visitStore(&curr)); - return push( - builder.makeAtomicStore(bytes, offset, curr.ptr, curr.value, type, mem)); + push(builder.makeAtomicStore(bytes, offset, curr.ptr, curr.value, type, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicRMW( AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem) { AtomicRMW curr; CHECK_ERR(visitAtomicRMW(&curr)); - return push( + push( builder.makeAtomicRMW(op, bytes, offset, curr.ptr, curr.value, type, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicCmpxchg(unsigned bytes, @@ -366,56 +448,64 @@ Result<> IRBuilder::makeAtomicCmpxchg(unsigned bytes, Name mem) { AtomicCmpxchg curr; CHECK_ERR(visitAtomicCmpxchg(&curr)); - return push(builder.makeAtomicCmpxchg( + push(builder.makeAtomicCmpxchg( bytes, offset, curr.ptr, curr.expected, curr.replacement, type, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicWait(Type type, Address offset, Name mem) { AtomicWait curr; CHECK_ERR(visitAtomicWait(&curr)); - return push(builder.makeAtomicWait( + push(builder.makeAtomicWait( curr.ptr, curr.expected, curr.timeout, type, offset, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicNotify(Address offset, Name mem) { AtomicNotify curr; CHECK_ERR(visitAtomicNotify(&curr)); - return push( - builder.makeAtomicNotify(curr.ptr, curr.notifyCount, offset, mem)); + push(builder.makeAtomicNotify(curr.ptr, curr.notifyCount, offset, mem)); + return Ok{}; } Result<> IRBuilder::makeAtomicFence() { - return push(builder.makeAtomicFence()); + push(builder.makeAtomicFence()); + return Ok{}; } Result<> IRBuilder::makeSIMDExtract(SIMDExtractOp op, uint8_t lane) { SIMDExtract curr; CHECK_ERR(visitSIMDExtract(&curr)); - return push(builder.makeSIMDExtract(op, curr.vec, lane)); + push(builder.makeSIMDExtract(op, curr.vec, lane)); + return Ok{}; } Result<> IRBuilder::makeSIMDReplace(SIMDReplaceOp op, uint8_t lane) { SIMDReplace curr; CHECK_ERR(visitSIMDReplace(&curr)); - return push(builder.makeSIMDReplace(op, curr.vec, lane, curr.value)); + push(builder.makeSIMDReplace(op, curr.vec, lane, curr.value)); + return Ok{}; } Result<> IRBuilder::makeSIMDShuffle(const std::array<uint8_t, 16>& lanes) { SIMDShuffle curr; CHECK_ERR(visitSIMDShuffle(&curr)); - return push(builder.makeSIMDShuffle(curr.left, curr.right, lanes)); + push(builder.makeSIMDShuffle(curr.left, curr.right, lanes)); + return Ok{}; } Result<> IRBuilder::makeSIMDTernary(SIMDTernaryOp op) { SIMDTernary curr; CHECK_ERR(visitSIMDTernary(&curr)); - return push(builder.makeSIMDTernary(op, curr.a, curr.b, curr.c)); + push(builder.makeSIMDTernary(op, curr.a, curr.b, curr.c)); + return Ok{}; } Result<> IRBuilder::makeSIMDShift(SIMDShiftOp op) { SIMDShift curr; CHECK_ERR(visitSIMDShift(&curr)); - return push(builder.makeSIMDShift(op, curr.vec, curr.shift)); + push(builder.makeSIMDShift(op, curr.vec, curr.shift)); + return Ok{}; } Result<> IRBuilder::makeSIMDLoad(SIMDLoadOp op, @@ -424,7 +514,8 @@ Result<> IRBuilder::makeSIMDLoad(SIMDLoadOp op, Name mem) { SIMDLoad curr; CHECK_ERR(visitSIMDLoad(&curr)); - return push(builder.makeSIMDLoad(op, offset, align, curr.ptr, mem)); + push(builder.makeSIMDLoad(op, offset, align, curr.ptr, mem)); + return Ok{}; } Result<> IRBuilder::makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op, @@ -434,48 +525,55 @@ Result<> IRBuilder::makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op, Name mem) { SIMDLoadStoreLane curr; CHECK_ERR(visitSIMDLoadStoreLane(&curr)); - return push(builder.makeSIMDLoadStoreLane( + push(builder.makeSIMDLoadStoreLane( op, offset, align, lane, curr.ptr, curr.vec, mem)); + return Ok{}; } Result<> IRBuilder::makeMemoryInit(Name data, Name mem) { MemoryInit curr; CHECK_ERR(visitMemoryInit(&curr)); - return push( - builder.makeMemoryInit(data, curr.dest, curr.offset, curr.size, mem)); + push(builder.makeMemoryInit(data, curr.dest, curr.offset, curr.size, mem)); + return Ok{}; } Result<> IRBuilder::makeDataDrop(Name data) { - return push(builder.makeDataDrop(data)); + push(builder.makeDataDrop(data)); + return Ok{}; } Result<> IRBuilder::makeMemoryCopy(Name destMem, Name srcMem) { MemoryCopy curr; CHECK_ERR(visitMemoryCopy(&curr)); - return push( + push( builder.makeMemoryCopy(curr.dest, curr.source, curr.size, destMem, srcMem)); + return Ok{}; } Result<> IRBuilder::makeMemoryFill(Name mem) { MemoryFill curr; CHECK_ERR(visitMemoryFill(&curr)); - return push(builder.makeMemoryFill(curr.dest, curr.value, curr.size, mem)); + push(builder.makeMemoryFill(curr.dest, curr.value, curr.size, mem)); + return Ok{}; } Result<> IRBuilder::makeConst(Literal val) { - return push(builder.makeConst(val)); + push(builder.makeConst(val)); + return Ok{}; } Result<> IRBuilder::makeUnary(UnaryOp op) { Unary curr; CHECK_ERR(visitUnary(&curr)); - return push(builder.makeUnary(op, curr.value)); + push(builder.makeUnary(op, curr.value)); + return Ok{}; } Result<> IRBuilder::makeBinary(BinaryOp op) { Binary curr; CHECK_ERR(visitBinary(&curr)); - return push(builder.makeBinary(op, curr.left, curr.right)); + push(builder.makeBinary(op, curr.left, curr.right)); + return Ok{}; } Result<> IRBuilder::makeSelect(std::optional<Type> type) { @@ -487,45 +585,53 @@ Result<> IRBuilder::makeSelect(std::optional<Type> type) { if (type && !Type::isSubType(built->type, *type)) { return Err{"select type does not match expected type"}; } - return push(built); + push(built); + return Ok{}; } Result<> IRBuilder::makeDrop() { Drop curr; CHECK_ERR(visitDrop(&curr)); - return push(builder.makeDrop(curr.value)); + push(builder.makeDrop(curr.value)); + return Ok{}; } Result<> IRBuilder::makeReturn() { Return curr; CHECK_ERR(visitReturn(&curr)); - return push(builder.makeReturn(curr.value)); + push(builder.makeReturn(curr.value)); + return Ok{}; } Result<> IRBuilder::makeMemorySize(Name mem) { - return push(builder.makeMemorySize(mem)); + push(builder.makeMemorySize(mem)); + return Ok{}; } Result<> IRBuilder::makeMemoryGrow(Name mem) { MemoryGrow curr; CHECK_ERR(visitMemoryGrow(&curr)); - return push(builder.makeMemoryGrow(curr.delta, mem)); + push(builder.makeMemoryGrow(curr.delta, mem)); + return Ok{}; } Result<> IRBuilder::makeUnreachable() { - return push(builder.makeUnreachable()); + push(builder.makeUnreachable()); + return Ok{}; } // Result<> IRBuilder::makePop() {} Result<> IRBuilder::makeRefNull(HeapType type) { - return push(builder.makeRefNull(type)); + push(builder.makeRefNull(type)); + return Ok{}; } Result<> IRBuilder::makeRefIsNull() { RefIsNull curr; CHECK_ERR(visitRefIsNull(&curr)); - return push(builder.makeRefIsNull(curr.value)); + push(builder.makeRefIsNull(curr.value)); + return Ok{}; } // Result<> IRBuilder::makeRefFunc() {} @@ -533,7 +639,8 @@ Result<> IRBuilder::makeRefIsNull() { Result<> IRBuilder::makeRefEq() { RefEq curr; CHECK_ERR(visitRefEq(&curr)); - return push(builder.makeRefEq(curr.left, curr.right)); + push(builder.makeRefEq(curr.left, curr.right)); + return Ok{}; } // Result<> IRBuilder::makeTableGet() {} @@ -557,13 +664,15 @@ Result<> IRBuilder::makeRefEq() { Result<> IRBuilder::makeI31New() { I31New curr; CHECK_ERR(visitI31New(&curr)); - return push(builder.makeI31New(curr.value)); + push(builder.makeI31New(curr.value)); + return Ok{}; } Result<> IRBuilder::makeI31Get(bool signed_) { I31Get curr; CHECK_ERR(visitI31Get(&curr)); - return push(builder.makeI31Get(curr.i31, signed_)); + push(builder.makeI31Get(curr.i31, signed_)); + return Ok{}; } // Result<> IRBuilder::makeCallRef() {} @@ -579,11 +688,13 @@ Result<> IRBuilder::makeStructNew(HeapType type) { // Differentiate from struct.new_default with a non-empty expression list. curr.operands.resize(type.getStruct().fields.size()); CHECK_ERR(visitStructNew(&curr)); - return push(builder.makeStructNew(type, std::move(curr.operands))); + push(builder.makeStructNew(type, std::move(curr.operands))); + return Ok{}; } Result<> IRBuilder::makeStructNewDefault(HeapType type) { - return push(builder.makeStructNew(type, {})); + push(builder.makeStructNew(type, {})); + return Ok{}; } Result<> IRBuilder::makeStructGet(HeapType type, Index field, bool signed_) { @@ -591,15 +702,16 @@ Result<> IRBuilder::makeStructGet(HeapType type, Index field, bool signed_) { StructGet curr; CHECK_ERR(visitStructGet(&curr)); CHECK_ERR(validateTypeAnnotation(type, curr.ref)); - return push( - builder.makeStructGet(field, curr.ref, fields[field].type, signed_)); + push(builder.makeStructGet(field, curr.ref, fields[field].type, signed_)); + return Ok{}; } Result<> IRBuilder::makeStructSet(HeapType type, Index field) { StructSet curr; CHECK_ERR(visitStructSet(&curr)); CHECK_ERR(validateTypeAnnotation(type, curr.ref)); - return push(builder.makeStructSet(field, curr.ref, curr.value)); + push(builder.makeStructSet(field, curr.ref, curr.value)); + return Ok{}; } Result<> IRBuilder::makeArrayNew(HeapType type) { @@ -607,25 +719,29 @@ Result<> IRBuilder::makeArrayNew(HeapType type) { // Differentiate from array.new_default with dummy initializer. curr.init = (Expression*)0x01; CHECK_ERR(visitArrayNew(&curr)); - return push(builder.makeArrayNew(type, curr.size, curr.init)); + push(builder.makeArrayNew(type, curr.size, curr.init)); + return Ok{}; } Result<> IRBuilder::makeArrayNewDefault(HeapType type) { ArrayNew curr; CHECK_ERR(visitArrayNew(&curr)); - return push(builder.makeArrayNew(type, curr.size)); + push(builder.makeArrayNew(type, curr.size)); + return Ok{}; } Result<> IRBuilder::makeArrayNewData(HeapType type, Name data) { ArrayNewData curr; CHECK_ERR(visitArrayNewData(&curr)); - return push(builder.makeArrayNewData(type, data, curr.offset, curr.size)); + push(builder.makeArrayNewData(type, data, curr.offset, curr.size)); + return Ok{}; } Result<> IRBuilder::makeArrayNewElem(HeapType type, Name elem) { ArrayNewElem curr; CHECK_ERR(visitArrayNewElem(&curr)); - return push(builder.makeArrayNewElem(type, elem, curr.offset, curr.size)); + push(builder.makeArrayNewElem(type, elem, curr.offset, curr.size)); + return Ok{}; } // Result<> IRBuilder::makeArrayNewFixed() {} @@ -634,21 +750,24 @@ Result<> IRBuilder::makeArrayGet(HeapType type, bool signed_) { ArrayGet curr; CHECK_ERR(visitArrayGet(&curr)); CHECK_ERR(validateTypeAnnotation(type, curr.ref)); - return push(builder.makeArrayGet( + push(builder.makeArrayGet( curr.ref, curr.index, type.getArray().element.type, signed_)); + return Ok{}; } Result<> IRBuilder::makeArraySet(HeapType type) { ArraySet curr; CHECK_ERR(visitArraySet(&curr)); CHECK_ERR(validateTypeAnnotation(type, curr.ref)); - return push(builder.makeArraySet(curr.ref, curr.index, curr.value)); + push(builder.makeArraySet(curr.ref, curr.index, curr.value)); + return Ok{}; } Result<> IRBuilder::makeArrayLen() { ArrayLen curr; CHECK_ERR(visitArrayLen(&curr)); - return push(builder.makeArrayLen(curr.ref)); + push(builder.makeArrayLen(curr.ref)); + return Ok{}; } Result<> IRBuilder::makeArrayCopy(HeapType destType, HeapType srcType) { @@ -656,16 +775,17 @@ Result<> IRBuilder::makeArrayCopy(HeapType destType, HeapType srcType) { CHECK_ERR(visitArrayCopy(&curr)); CHECK_ERR(validateTypeAnnotation(destType, curr.destRef)); CHECK_ERR(validateTypeAnnotation(srcType, curr.srcRef)); - return push(builder.makeArrayCopy( + push(builder.makeArrayCopy( curr.destRef, curr.destIndex, curr.srcRef, curr.srcIndex, curr.length)); + return Ok{}; } Result<> IRBuilder::makeArrayFill(HeapType type) { ArrayFill curr; CHECK_ERR(visitArrayFill(&curr)); CHECK_ERR(validateTypeAnnotation(type, curr.ref)); - return push( - builder.makeArrayFill(curr.ref, curr.index, curr.value, curr.size)); + push(builder.makeArrayFill(curr.ref, curr.index, curr.value, curr.size)); + return Ok{}; } // Result<> IRBuilder::makeArrayInitData() {} diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp index bf438ae68..3961cbb7f 100644 --- a/src/wasm/wat-parser.cpp +++ b/src/wasm/wat-parser.cpp @@ -676,7 +676,7 @@ struct NullInstrParserCtx { InstrsT finishInstrs(InstrsT&) { return Ok{}; } ExprT makeExpr(InstrsT) { return Ok{}; } - ExprT instrToExpr(InstrT) { return Ok{}; } + Result<ExprT> instrToExpr(InstrT) { return Ok{}; } template<typename HeapTypeT> FieldIdxT getFieldFromIdx(HeapTypeT, uint32_t) { return Ok{}; @@ -696,9 +696,11 @@ struct NullInstrParserCtx { MemargT getMemarg(uint64_t, uint32_t) { return Ok{}; } template<typename BlockTypeT> - InstrT makeBlock(Index, std::optional<Name>, BlockTypeT, InstrsT) { + InstrT makeBlock(Index, std::optional<Name>, BlockTypeT) { return Ok{}; } + InstrT finishBlock(Index, InstrsT) { return Ok{}; } + InstrT makeUnreachable(Index) { return Ok{}; } InstrT makeNop(Index) { return Ok{}; } InstrT makeBinary(Index, BinaryOp) { return Ok{}; } @@ -1280,7 +1282,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { // Keep track of instructions internally rather than letting the general // parser collect them. using InstrT = Ok; - using InstrsT = std::vector<Expression*>; + using InstrsT = Ok; using ExprT = Expression*; using FieldIdxT = Index; @@ -1334,12 +1336,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { HeapType getBlockTypeFromResult(const std::vector<Type> results) { assert(results.size() == 1); - irBuilder.pushScope(results[0]); return HeapType(Signature(Type::none, results[0])); } Result<HeapType> getBlockTypeFromTypeUse(Index pos, HeapType type) { - irBuilder.pushScope(type.getSignature().results); return type; } @@ -1347,11 +1347,9 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { void appendInstr(Ok&, InstrT instr) {} - Result<InstrsT> finishInstrs(Ok&) { - return withLoc(irBuilder.finishInstrs()); - } + Result<InstrsT> finishInstrs(Ok&) { return Ok{}; } - Expression* instrToExpr(Ok&) { return irBuilder.build(); } + Result<Expression*> instrToExpr(Ok&) { return irBuilder.build(); } GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; } @@ -1475,25 +1473,12 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { ImportNames*, TypeUseT, std::optional<LocalsT>, - std::optional<InstrsT> insts, - Index) { - Expression* body; - if (insts) { - switch (insts->size()) { - case 0: - body = builder.makeNop(); - break; - case 1: - body = insts->back(); - break; - default: - body = builder.makeBlock(*insts, wasm.functions[index]->getResults()); - break; - } - } else { - body = builder.makeNop(); - } - wasm.functions[index]->body = body; + std::optional<InstrsT>, + Index pos) { + CHECK_ERR(withLoc(pos, irBuilder.visitEnd())); + auto body = irBuilder.build(); + CHECK_ERR(withLoc(pos, body)); + wasm.functions[index]->body = *body; return Ok{}; } @@ -1537,16 +1522,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { return Builder::addVar(func, name, type); } - Expression* makeExpr(InstrsT& instrs) { - switch (instrs.size()) { - case 0: - return builder.makeNop(); - case 1: - return instrs.front(); - default: - return builder.makeBlock(instrs); - } - } + Result<Expression*> makeExpr(InstrsT& instrs) { return irBuilder.build(); } Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; } @@ -1560,20 +1536,16 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { return wasm.memories[0]->name; } - Result<> makeBlock(Index pos, - std::optional<Name> label, - HeapType type, - const std::vector<Expression*>& instrs) { + Result<> makeBlock(Index pos, std::optional<Name> label, HeapType type) { // TODO: validate labels? // TODO: Move error on input types to here? - auto results = type.getSignature().results; - Block* block = wasm.allocator.alloc<Block>(); - block->type = results; - if (label) { - block->name = *label; - } - block->list.set(instrs); - return withLoc(pos, irBuilder.visit(block)); + return withLoc(pos, + irBuilder.makeBlock(label ? *label : Name{}, + type.getSignature().results)); + } + + Result<> finishBlock(Index pos, InstrsT) { + return withLoc(pos, irBuilder.visitEnd()); } Result<> makeUnreachable(Index pos) { @@ -2595,6 +2567,8 @@ MaybeResult<typename Ctx::InstrT> block(Ctx& ctx, bool folded) { auto type = blocktype(ctx); CHECK_ERR(type); + ctx.makeBlock(pos, label, *type); + auto insts = instrs(ctx); CHECK_ERR(insts); @@ -2612,7 +2586,7 @@ MaybeResult<typename Ctx::InstrT> block(Ctx& ctx, bool folded) { } } - return ctx.makeBlock(pos, label, *type, std::move(*insts)); + return ctx.finishBlock(pos, std::move(*insts)); } template<typename Ctx> @@ -3745,7 +3719,9 @@ template<typename Ctx> MaybeResult<> data(Ctx& ctx) { } else if (ctx.in.takeLParen()) { auto inst = instr(ctx); CHECK_ERR(inst); - offset = ctx.instrToExpr(*inst); + auto offsetExpr = ctx.instrToExpr(*inst); + CHECK_ERR(offsetExpr); + offset = *offsetExpr; if (!ctx.in.takeRParen()) { return ctx.in.err("expected end of offset instruction"); } @@ -3893,7 +3869,7 @@ Result<> parseModule(Module& wasm, std::string_view input) { for (Index i = 0; i < decls.funcDefs.size(); ++i) { ctx.index = i; ctx.setFunction(wasm.functions[i].get()); - ctx.irBuilder.pushScope(ctx.func->getResults()); + CHECK_ERR(ctx.irBuilder.makeBlock(Name{}, ctx.func->getResults())); WithPosition with(ctx, decls.funcDefs[i].pos); auto parsed = func(ctx); CHECK_ERR(parsed); |