summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mixed_arena.h10
-rw-r--r--src/wasm-builder.h15
-rw-r--r--src/wasm-ir-builder.h208
-rw-r--r--src/wasm.h14
-rw-r--r--src/wasm/CMakeLists.txt1
-rw-r--r--src/wasm/wasm-ir-builder.cpp703
-rw-r--r--src/wasm/wat-parser.cpp586
7 files changed, 1046 insertions, 491 deletions
diff --git a/src/mixed_arena.h b/src/mixed_arena.h
index 4d8cda0ac..530fa9daa 100644
--- a/src/mixed_arena.h
+++ b/src/mixed_arena.h
@@ -403,7 +403,15 @@ public:
ArenaVector(MixedArena& allocator) : allocator(allocator) {}
ArenaVector(ArenaVector<T>&& other) : allocator(other.allocator) {
- *this = other;
+ swap(other);
+ }
+
+ ArenaVector<T>& operator=(ArenaVector<T>&& other) {
+ if (this != &other) {
+ this->clear();
+ this->swap(other);
+ }
+ return *this;
}
void allocate(size_t size) {
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index cb7c5e745..2093b9419 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -893,6 +893,21 @@ public:
ret->finalize();
return ret;
}
+ StructNew* makeStructNew(HeapType type,
+ std::initializer_list<Expression*> args) {
+ auto* ret = wasm.allocator.alloc<StructNew>();
+ ret->operands.set(args);
+ ret->type = Type(type, NonNullable);
+ ret->finalize();
+ return ret;
+ }
+ StructNew* makeStructNew(HeapType type, ExpressionList&& args) {
+ auto* ret = wasm.allocator.alloc<StructNew>();
+ ret->operands = std::move(args);
+ ret->type = Type(type, NonNullable);
+ ret->finalize();
+ return ret;
+ }
template<typename T> StructNew* makeStructNew(HeapType type, const T& args) {
auto* ret = wasm.allocator.alloc<StructNew>();
ret->operands.set(args);
diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h
new file mode 100644
index 000000000..6a1b3f18a
--- /dev/null
+++ b/src/wasm-ir-builder.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef wasm_wasm_ir_builder_h
+#define wasm_wasm_ir_builder_h
+
+#include <vector>
+
+#include "support/result.h"
+#include "wasm-builder.h"
+#include "wasm-traversal.h"
+#include "wasm-type.h"
+#include "wasm.h"
+
+namespace wasm {
+
+// A utility for constructing valid Binaryen IR from arbitrary valid sequences
+// of WebAssembly instructions. The user is responsible for providing Expression
+// nodes with all of their non-child fields already filled out, and IRBuilder is
+// responsible for setting child fields and finalizing nodes.
+//
+// To use, call CHECK_ERR(visit(...)) or CHECK_ERR(makeXYZ(...)) on each
+// expression in the sequence, then call build().
+class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
+public:
+ IRBuilder(Module& wasm, Function* func = nullptr)
+ : wasm(wasm), func(func), builder(wasm) {}
+
+ // 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();
+
+ // Call visit() on an existing Expression with its non-child fields
+ // initialized to initialize the child fields and refinalize it. The specific
+ // visitors are internal implementation details.
+ [[nodiscard]] Result<> visit(Expression*);
+ [[nodiscard]] Result<> visitExpression(Expression*);
+ [[nodiscard]] Result<> visitBlock(Block*);
+ [[nodiscard]] Result<> visitReturn(Return*);
+ [[nodiscard]] Result<> visitStructNew(StructNew*);
+ [[nodiscard]] Result<> visitArrayNew(ArrayNew*);
+
+ // 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<> makeIf();
+ // [[nodiscard]] Result<> makeLoop();
+ // [[nodiscard]] Result<> makeBreak();
+ // [[nodiscard]] Result<> makeSwitch();
+ // [[nodiscard]] Result<> makeCall();
+ // [[nodiscard]] Result<> makeCallIndirect();
+ [[nodiscard]] Result<> makeLocalGet(Index local);
+ [[nodiscard]] Result<> makeLocalSet(Index local);
+ [[nodiscard]] Result<> makeLocalTee(Index local);
+ [[nodiscard]] Result<> makeGlobalGet(Name global);
+ [[nodiscard]] Result<> makeGlobalSet(Name global);
+ [[nodiscard]] Result<> makeLoad(unsigned bytes,
+ bool signed_,
+ Address offset,
+ unsigned align,
+ Type type,
+ Name mem);
+ [[nodiscard]] Result<> makeStore(
+ unsigned bytes, Address offset, unsigned align, Type type, Name mem);
+ [[nodiscard]] Result<>
+ makeAtomicLoad(unsigned bytes, Address offset, Type type, Name mem);
+ [[nodiscard]] Result<>
+ makeAtomicStore(unsigned bytes, Address offset, Type type, Name mem);
+ [[nodiscard]] Result<> makeAtomicRMW(
+ AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem);
+ [[nodiscard]] Result<>
+ makeAtomicCmpxchg(unsigned bytes, Address offset, Type type, Name mem);
+ [[nodiscard]] Result<> makeAtomicWait(Type type, Address offset, Name mem);
+ [[nodiscard]] Result<> makeAtomicNotify(Address offset, Name mem);
+ [[nodiscard]] Result<> makeAtomicFence();
+ [[nodiscard]] Result<> makeSIMDExtract(SIMDExtractOp op, uint8_t lane);
+ [[nodiscard]] Result<> makeSIMDReplace(SIMDReplaceOp op, uint8_t lane);
+ [[nodiscard]] Result<> makeSIMDShuffle(const std::array<uint8_t, 16>& lanes);
+ [[nodiscard]] Result<> makeSIMDTernary(SIMDTernaryOp op);
+ [[nodiscard]] Result<> makeSIMDShift(SIMDShiftOp op);
+ [[nodiscard]] Result<>
+ makeSIMDLoad(SIMDLoadOp op, Address offset, unsigned align, Name mem);
+ [[nodiscard]] Result<> makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op,
+ Address offset,
+ unsigned align,
+ uint8_t lane,
+ Name mem);
+ [[nodiscard]] Result<> makeMemoryInit(Name data, Name mem);
+ [[nodiscard]] Result<> makeDataDrop(Name data);
+ [[nodiscard]] Result<> makeMemoryCopy(Name destMem, Name srcMem);
+ [[nodiscard]] Result<> makeMemoryFill(Name mem);
+ [[nodiscard]] Result<> makeConst(Literal val);
+ [[nodiscard]] Result<> makeUnary(UnaryOp op);
+ [[nodiscard]] Result<> makeBinary(BinaryOp op);
+ [[nodiscard]] Result<> makeSelect(std::optional<Type> type = std::nullopt);
+ [[nodiscard]] Result<> makeDrop();
+ [[nodiscard]] Result<> makeReturn();
+ [[nodiscard]] Result<> makeMemorySize(Name mem);
+ [[nodiscard]] Result<> makeMemoryGrow(Name mem);
+ [[nodiscard]] Result<> makeUnreachable();
+ // [[nodiscard]] Result<> makePop();
+ [[nodiscard]] Result<> makeRefNull(HeapType type);
+ [[nodiscard]] Result<> makeRefIsNull();
+ // [[nodiscard]] Result<> makeRefFunc();
+ [[nodiscard]] Result<> makeRefEq();
+ // [[nodiscard]] Result<> makeTableGet();
+ // [[nodiscard]] Result<> makeTableSet();
+ // [[nodiscard]] Result<> makeTableSize();
+ // [[nodiscard]] Result<> makeTableGrow();
+ // [[nodiscard]] Result<> makeTry();
+ // [[nodiscard]] Result<> makeThrow();
+ // [[nodiscard]] Result<> makeRethrow();
+ // [[nodiscard]] Result<> makeTupleMake();
+ // [[nodiscard]] Result<> makeTupleExtract();
+ [[nodiscard]] Result<> makeI31New();
+ [[nodiscard]] Result<> makeI31Get(bool signed_);
+ // [[nodiscard]] Result<> makeCallRef();
+ // [[nodiscard]] Result<> makeRefTest();
+ // [[nodiscard]] Result<> makeRefCast();
+ // [[nodiscard]] Result<> makeBrOn();
+ [[nodiscard]] Result<> makeStructNew(HeapType type);
+ [[nodiscard]] Result<> makeStructNewDefault(HeapType type);
+ [[nodiscard]] Result<>
+ makeStructGet(HeapType type, Index field, bool signed_);
+ [[nodiscard]] Result<> makeStructSet(HeapType type, Index field);
+ [[nodiscard]] Result<> makeArrayNew(HeapType type);
+ [[nodiscard]] Result<> makeArrayNewDefault(HeapType type);
+ [[nodiscard]] Result<> makeArrayNewData(HeapType type, Name data);
+ [[nodiscard]] Result<> makeArrayNewElem(HeapType type, Name elem);
+ // [[nodiscard]] Result<> makeArrayNewFixed();
+ [[nodiscard]] Result<> makeArrayGet(HeapType type, bool signed_);
+ [[nodiscard]] Result<> makeArraySet(HeapType type);
+ [[nodiscard]] Result<> makeArrayLen();
+ [[nodiscard]] Result<> makeArrayCopy(HeapType destType, HeapType srcType);
+ [[nodiscard]] Result<> makeArrayFill(HeapType type);
+ // [[nodiscard]] Result<> makeArrayInitData();
+ // [[nodiscard]] Result<> makeArrayInitElem();
+ // [[nodiscard]] Result<> makeRefAs();
+ // [[nodiscard]] Result<> makeStringNew();
+ // [[nodiscard]] Result<> makeStringConst();
+ // [[nodiscard]] Result<> makeStringMeasure();
+ // [[nodiscard]] Result<> makeStringEncode();
+ // [[nodiscard]] Result<> makeStringConcat();
+ // [[nodiscard]] Result<> makeStringEq();
+ // [[nodiscard]] Result<> makeStringAs();
+ // [[nodiscard]] Result<> makeStringWTF8Advance();
+ // [[nodiscard]] Result<> makeStringWTF16Get();
+ // [[nodiscard]] Result<> makeStringIterNext();
+ // [[nodiscard]] Result<> makeStringIterMove();
+ // [[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:
+ Module& wasm;
+ Function* func;
+ Builder builder;
+
+ // The context for a single block scope, including the instructions parsed
+ // inside that scope so far and the ultimate result type we expect this block
+ // to have.
+ struct BlockCtx {
+ std::vector<Expression*> exprStack;
+ Type type;
+ };
+
+ // 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;
+ }
+
+ // Whether we have seen an unreachable instruction and are in
+ // stack-polymorphic unreachable mode.
+ bool unreachable = false;
+
+ Result<Index> addScratchLocal(Type);
+ [[nodiscard]] Result<> push(Expression*);
+ Result<Expression*> pop();
+};
+
+} // namespace wasm
+
+#endif // wasm_wasm_ir_builder_h
diff --git a/src/wasm.h b/src/wasm.h
index 90628c5bf..c2c2db79e 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1347,6 +1347,7 @@ public:
class RefIsNull : public SpecificExpression<Expression::RefIsNullId> {
public:
+ RefIsNull() = default;
RefIsNull(MixedArena& allocator) {}
Expression* value;
@@ -1366,6 +1367,7 @@ public:
class RefEq : public SpecificExpression<Expression::RefEqId> {
public:
+ RefEq() = default;
RefEq(MixedArena& allocator) {}
Expression* left;
@@ -1478,6 +1480,7 @@ public:
class I31New : public SpecificExpression<Expression::I31NewId> {
public:
+ I31New() = default;
I31New(MixedArena& allocator) {}
Expression* value;
@@ -1487,6 +1490,7 @@ public:
class I31Get : public SpecificExpression<Expression::I31GetId> {
public:
+ I31Get() = default;
I31Get(MixedArena& allocator) {}
Expression* i31;
@@ -1562,6 +1566,7 @@ public:
class StructGet : public SpecificExpression<Expression::StructGetId> {
public:
+ StructGet() = default;
StructGet(MixedArena& allocator) {}
Index index;
@@ -1574,6 +1579,7 @@ public:
class StructSet : public SpecificExpression<Expression::StructSetId> {
public:
+ StructSet() = default;
StructSet(MixedArena& allocator) {}
Index index;
@@ -1585,6 +1591,7 @@ public:
class ArrayNew : public SpecificExpression<Expression::ArrayNewId> {
public:
+ ArrayNew() = default;
ArrayNew(MixedArena& allocator) {}
// If set, then the initial value is assigned to all entries in the array. If
@@ -1600,6 +1607,7 @@ public:
class ArrayNewData : public SpecificExpression<Expression::ArrayNewDataId> {
public:
+ ArrayNewData() = default;
ArrayNewData(MixedArena& allocator) {}
Name segment;
@@ -1611,6 +1619,7 @@ public:
class ArrayNewElem : public SpecificExpression<Expression::ArrayNewElemId> {
public:
+ ArrayNewElem() = default;
ArrayNewElem(MixedArena& allocator) {}
Name segment;
@@ -1631,6 +1640,7 @@ public:
class ArrayGet : public SpecificExpression<Expression::ArrayGetId> {
public:
+ ArrayGet() = default;
ArrayGet(MixedArena& allocator) {}
Expression* ref;
@@ -1643,6 +1653,7 @@ public:
class ArraySet : public SpecificExpression<Expression::ArraySetId> {
public:
+ ArraySet() = default;
ArraySet(MixedArena& allocator) {}
Expression* ref;
@@ -1654,6 +1665,7 @@ public:
class ArrayLen : public SpecificExpression<Expression::ArrayLenId> {
public:
+ ArrayLen() = default;
ArrayLen(MixedArena& allocator) {}
Expression* ref;
@@ -1663,6 +1675,7 @@ public:
class ArrayCopy : public SpecificExpression<Expression::ArrayCopyId> {
public:
+ ArrayCopy() = default;
ArrayCopy(MixedArena& allocator) {}
Expression* destRef;
@@ -1676,6 +1689,7 @@ public:
class ArrayFill : public SpecificExpression<Expression::ArrayFillId> {
public:
+ ArrayFill() = default;
ArrayFill(MixedArena& allocator) {}
Expression* ref;
diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt
index 7b7f85698..30f52f9ee 100644
--- a/src/wasm/CMakeLists.txt
+++ b/src/wasm/CMakeLists.txt
@@ -8,6 +8,7 @@ set(wasm_SOURCES
wasm-emscripten.cpp
wasm-interpreter.cpp
wasm-io.cpp
+ wasm-ir-builder.cpp
wasm-s-parser.cpp
wasm-stack.cpp
wasm-type.cpp
diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp
new file mode 100644
index 000000000..6786816f3
--- /dev/null
+++ b/src/wasm/wasm-ir-builder.cpp
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2023 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 <cassert>
+
+#include "ir/names.h"
+#include "ir/utils.h"
+#include "wasm-ir-builder.h"
+
+using namespace std::string_literals;
+
+namespace wasm {
+
+namespace {
+
+Result<> validateTypeAnnotation(HeapType type, Expression* child) {
+ if (child->type == Type::unreachable) {
+ return Ok{};
+ }
+ if (!child->type.isRef() ||
+ !HeapType::isSubType(child->type.getHeapType(), type)) {
+ return Err{"invalid reference type on stack"};
+ }
+ return Ok{};
+}
+
+} // 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"};
+ }
+ Name name = Names::getValidLocalName(*func, "scratch");
+ return Builder::addVar(func, name, type);
+}
+
+Result<> IRBuilder::push(Expression* expr) {
+ auto& exprStack = getExprStack();
+ 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) {
+ 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);
+ }
+ return Ok{};
+}
+
+Result<Expression*> IRBuilder::pop() {
+ auto& exprStack = getExprStack();
+
+ // 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;
+ }
+ }
+
+ if (firstNone == 0) {
+ // There are no expressions that produce values.
+ if (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;
+ }
+
+ // 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 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);
+ }
+ unreachable = false;
+ auto ret = std::move(exprStack);
+ scopeStack.pop_back();
+ return ret;
+}
+
+Result<> IRBuilder::visit(Expression* curr) {
+ UnifiedExpressionVisitor<IRBuilder, Result<>>::visit(curr);
+ if (auto* block = curr->dynCast<Block>()) {
+ block->finalize(block->type);
+ } else {
+ // TODO: Call more efficient versions of finalize() that take the known type
+ // for other kinds of nodes as well, as done above.
+ ReFinalizeNode{}.visit(curr);
+ }
+ return push(curr);
+}
+
+// Handle the common case of instructions with a constant number of children
+// uniformly.
+Result<> IRBuilder::visitExpression(Expression* curr) {
+#define DELEGATE_ID curr->_id
+#define DELEGATE_START(id) [[maybe_unused]] auto* expr = curr->cast<id>();
+#define DELEGATE_FIELD_CHILD(id, field) \
+ auto field = pop(); \
+ CHECK_ERR(field); \
+ expr->field = *field;
+#define DELEGATE_END(id)
+
+#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \
+ WASM_UNREACHABLE("should have called visit" #id " because " #id \
+ " has optional child " #field);
+#define DELEGATE_FIELD_CHILD_VECTOR(id, field) \
+ WASM_UNREACHABLE("should have called visit" #id " because " #id \
+ " has child vector " #field);
+
+#define DELEGATE_FIELD_INT(id, field)
+#define DELEGATE_FIELD_INT_ARRAY(id, field)
+#define DELEGATE_FIELD_LITERAL(id, field)
+#define DELEGATE_FIELD_NAME(id, field)
+#define DELEGATE_FIELD_NAME_VECTOR(id, field)
+#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field)
+#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field)
+#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field)
+#define DELEGATE_FIELD_TYPE(id, field)
+#define DELEGATE_FIELD_HEAPTYPE(id, field)
+#define DELEGATE_FIELD_ADDRESS(id, field)
+
+#include "wasm-delegations-fields.def"
+
+ return Ok{};
+}
+
+Result<> IRBuilder::visitBlock(Block* curr) {
+ // TODO: Handle popping scope and filling block here instead of externally.
+ return Ok{};
+}
+
+Result<> IRBuilder::visitReturn(Return* curr) {
+ if (!func) {
+ return Err{"cannot return outside of a function"};
+ }
+ size_t n = func->getResults().size();
+ if (n == 0) {
+ curr->value = nullptr;
+ } else if (n == 1) {
+ auto val = pop();
+ CHECK_ERR(val);
+ curr->value = *val;
+ } else {
+ std::vector<Expression*> vals(n);
+ for (size_t i = 0; i < n; ++i) {
+ auto val = pop();
+ CHECK_ERR(val);
+ vals[n - i - 1] = *val;
+ }
+ curr->value = builder.makeTupleMake(vals);
+ }
+ return Ok{};
+}
+
+Result<> IRBuilder::visitStructNew(StructNew* curr) {
+ for (size_t i = 0, n = curr->operands.size(); i < n; ++i) {
+ auto val = pop();
+ CHECK_ERR(val);
+ curr->operands[n - 1 - i] = *val;
+ }
+ return Ok{};
+}
+
+Result<> IRBuilder::visitArrayNew(ArrayNew* curr) {
+ auto size = pop();
+ CHECK_ERR(size);
+ curr->size = *size;
+ if (!curr->isWithDefault()) {
+ auto init = pop();
+ CHECK_ERR(init);
+ curr->init = *init;
+ }
+ return Ok{};
+}
+
+Result<> IRBuilder::makeNop() { return push(builder.makeNop()); }
+
+Result<> IRBuilder::makeBlock() { return push(builder.makeBlock()); }
+
+// Result<> IRBuilder::makeIf() {}
+
+// Result<> IRBuilder::makeLoop() {}
+
+// Result<> IRBuilder::makeBreak() {}
+
+// Result<> IRBuilder::makeSwitch() {}
+
+// Result<> IRBuilder::makeCall() {}
+
+// Result<> IRBuilder::makeCallIndirect() {}
+
+Result<> IRBuilder::makeLocalGet(Index local) {
+ return push(builder.makeLocalGet(local, func->getLocalType(local)));
+}
+
+Result<> IRBuilder::makeLocalSet(Index local) {
+ LocalSet curr;
+ CHECK_ERR(visitLocalSet(&curr));
+ return push(builder.makeLocalSet(local, curr.value));
+}
+
+Result<> IRBuilder::makeLocalTee(Index local) {
+ LocalSet curr;
+ CHECK_ERR(visitLocalSet(&curr));
+ return push(
+ builder.makeLocalTee(local, curr.value, func->getLocalType(local)));
+}
+
+Result<> IRBuilder::makeGlobalGet(Name global) {
+ return push(builder.makeGlobalGet(global, wasm.getGlobal(global)->type));
+}
+
+Result<> IRBuilder::makeGlobalSet(Name global) {
+ GlobalSet curr;
+ CHECK_ERR(visitGlobalSet(&curr));
+ return push(builder.makeGlobalSet(global, curr.value));
+}
+
+Result<> IRBuilder::makeLoad(unsigned bytes,
+ bool signed_,
+ Address offset,
+ unsigned align,
+ Type type,
+ Name mem) {
+ Load curr;
+ CHECK_ERR(visitLoad(&curr));
+ return push(
+ builder.makeLoad(bytes, signed_, offset, align, curr.ptr, type, mem));
+}
+
+Result<> IRBuilder::makeStore(
+ unsigned bytes, Address offset, unsigned align, Type type, Name mem) {
+ Store curr;
+ CHECK_ERR(visitStore(&curr));
+ return push(
+ builder.makeStore(bytes, offset, align, curr.ptr, curr.value, type, mem));
+}
+
+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));
+}
+
+Result<> IRBuilder::makeAtomicStore(unsigned bytes,
+ Address offset,
+ Type type,
+ Name mem) {
+ Store curr;
+ CHECK_ERR(visitStore(&curr));
+ return push(
+ builder.makeAtomicStore(bytes, offset, curr.ptr, curr.value, type, mem));
+}
+
+Result<> IRBuilder::makeAtomicRMW(
+ AtomicRMWOp op, unsigned bytes, Address offset, Type type, Name mem) {
+ AtomicRMW curr;
+ CHECK_ERR(visitAtomicRMW(&curr));
+ return push(
+ builder.makeAtomicRMW(op, bytes, offset, curr.ptr, curr.value, type, mem));
+}
+
+Result<> IRBuilder::makeAtomicCmpxchg(unsigned bytes,
+ Address offset,
+ Type type,
+ Name mem) {
+ AtomicCmpxchg curr;
+ CHECK_ERR(visitAtomicCmpxchg(&curr));
+ return push(builder.makeAtomicCmpxchg(
+ bytes, offset, curr.ptr, curr.expected, curr.replacement, type, mem));
+}
+
+Result<> IRBuilder::makeAtomicWait(Type type, Address offset, Name mem) {
+ AtomicWait curr;
+ CHECK_ERR(visitAtomicWait(&curr));
+ return push(builder.makeAtomicWait(
+ curr.ptr, curr.expected, curr.timeout, type, offset, mem));
+}
+
+Result<> IRBuilder::makeAtomicNotify(Address offset, Name mem) {
+ AtomicNotify curr;
+ CHECK_ERR(visitAtomicNotify(&curr));
+ return push(
+ builder.makeAtomicNotify(curr.ptr, curr.notifyCount, offset, mem));
+}
+
+Result<> IRBuilder::makeAtomicFence() {
+ return push(builder.makeAtomicFence());
+}
+
+Result<> IRBuilder::makeSIMDExtract(SIMDExtractOp op, uint8_t lane) {
+ SIMDExtract curr;
+ CHECK_ERR(visitSIMDExtract(&curr));
+ return push(builder.makeSIMDExtract(op, curr.vec, lane));
+}
+
+Result<> IRBuilder::makeSIMDReplace(SIMDReplaceOp op, uint8_t lane) {
+ SIMDReplace curr;
+ CHECK_ERR(visitSIMDReplace(&curr));
+ return push(builder.makeSIMDReplace(op, curr.vec, lane, curr.value));
+}
+
+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));
+}
+
+Result<> IRBuilder::makeSIMDTernary(SIMDTernaryOp op) {
+ SIMDTernary curr;
+ CHECK_ERR(visitSIMDTernary(&curr));
+ return push(builder.makeSIMDTernary(op, curr.a, curr.b, curr.c));
+}
+
+Result<> IRBuilder::makeSIMDShift(SIMDShiftOp op) {
+ SIMDShift curr;
+ CHECK_ERR(visitSIMDShift(&curr));
+ return push(builder.makeSIMDShift(op, curr.vec, curr.shift));
+}
+
+Result<> IRBuilder::makeSIMDLoad(SIMDLoadOp op,
+ Address offset,
+ unsigned align,
+ Name mem) {
+ SIMDLoad curr;
+ CHECK_ERR(visitSIMDLoad(&curr));
+ return push(builder.makeSIMDLoad(op, offset, align, curr.ptr, mem));
+}
+
+Result<> IRBuilder::makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op,
+ Address offset,
+ unsigned align,
+ uint8_t lane,
+ Name mem) {
+ SIMDLoadStoreLane curr;
+ CHECK_ERR(visitSIMDLoadStoreLane(&curr));
+ return push(builder.makeSIMDLoadStoreLane(
+ op, offset, align, lane, curr.ptr, curr.vec, mem));
+}
+
+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));
+}
+
+Result<> IRBuilder::makeDataDrop(Name data) {
+ return push(builder.makeDataDrop(data));
+}
+
+Result<> IRBuilder::makeMemoryCopy(Name destMem, Name srcMem) {
+ MemoryCopy curr;
+ CHECK_ERR(visitMemoryCopy(&curr));
+ return push(
+ builder.makeMemoryCopy(curr.dest, curr.source, curr.size, destMem, srcMem));
+}
+
+Result<> IRBuilder::makeMemoryFill(Name mem) {
+ MemoryFill curr;
+ CHECK_ERR(visitMemoryFill(&curr));
+ return push(builder.makeMemoryFill(curr.dest, curr.value, curr.size, mem));
+}
+
+Result<> IRBuilder::makeConst(Literal val) {
+ return push(builder.makeConst(val));
+}
+
+Result<> IRBuilder::makeUnary(UnaryOp op) {
+ Unary curr;
+ CHECK_ERR(visitUnary(&curr));
+ return push(builder.makeUnary(op, curr.value));
+}
+
+Result<> IRBuilder::makeBinary(BinaryOp op) {
+ Binary curr;
+ CHECK_ERR(visitBinary(&curr));
+ return push(builder.makeBinary(op, curr.left, curr.right));
+}
+
+Result<> IRBuilder::makeSelect(std::optional<Type> type) {
+ Select curr;
+ CHECK_ERR(visitSelect(&curr));
+ auto* built =
+ type ? builder.makeSelect(curr.condition, curr.ifTrue, curr.ifFalse, *type)
+ : builder.makeSelect(curr.condition, curr.ifTrue, curr.ifFalse);
+ if (type && !Type::isSubType(built->type, *type)) {
+ return Err{"select type does not match expected type"};
+ }
+ return push(built);
+}
+
+Result<> IRBuilder::makeDrop() {
+ Drop curr;
+ CHECK_ERR(visitDrop(&curr));
+ return push(builder.makeDrop(curr.value));
+}
+
+Result<> IRBuilder::makeReturn() {
+ Return curr;
+ CHECK_ERR(visitReturn(&curr));
+ return push(builder.makeReturn(curr.value));
+}
+
+Result<> IRBuilder::makeMemorySize(Name mem) {
+ return push(builder.makeMemorySize(mem));
+}
+
+Result<> IRBuilder::makeMemoryGrow(Name mem) {
+ MemoryGrow curr;
+ CHECK_ERR(visitMemoryGrow(&curr));
+ return push(builder.makeMemoryGrow(curr.delta, mem));
+}
+
+Result<> IRBuilder::makeUnreachable() {
+ return push(builder.makeUnreachable());
+}
+
+// Result<> IRBuilder::makePop() {}
+
+Result<> IRBuilder::makeRefNull(HeapType type) {
+ return push(builder.makeRefNull(type));
+}
+
+Result<> IRBuilder::makeRefIsNull() {
+ RefIsNull curr;
+ CHECK_ERR(visitRefIsNull(&curr));
+ return push(builder.makeRefIsNull(curr.value));
+}
+
+// Result<> IRBuilder::makeRefFunc() {}
+
+Result<> IRBuilder::makeRefEq() {
+ RefEq curr;
+ CHECK_ERR(visitRefEq(&curr));
+ return push(builder.makeRefEq(curr.left, curr.right));
+}
+
+// Result<> IRBuilder::makeTableGet() {}
+
+// Result<> IRBuilder::makeTableSet() {}
+
+// Result<> IRBuilder::makeTableSize() {}
+
+// Result<> IRBuilder::makeTableGrow() {}
+
+// Result<> IRBuilder::makeTry() {}
+
+// Result<> IRBuilder::makeThrow() {}
+
+// Result<> IRBuilder::makeRethrow() {}
+
+// Result<> IRBuilder::makeTupleMake() {}
+
+// Result<> IRBuilder::makeTupleExtract() {}
+
+Result<> IRBuilder::makeI31New() {
+ I31New curr;
+ CHECK_ERR(visitI31New(&curr));
+ return push(builder.makeI31New(curr.value));
+}
+
+Result<> IRBuilder::makeI31Get(bool signed_) {
+ I31Get curr;
+ CHECK_ERR(visitI31Get(&curr));
+ return push(builder.makeI31Get(curr.i31, signed_));
+}
+
+// Result<> IRBuilder::makeCallRef() {}
+
+// Result<> IRBuilder::makeRefTest() {}
+
+// Result<> IRBuilder::makeRefCast() {}
+
+// Result<> IRBuilder::makeBrOn() {}
+
+Result<> IRBuilder::makeStructNew(HeapType type) {
+ StructNew curr(wasm.allocator);
+ // 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)));
+}
+
+Result<> IRBuilder::makeStructNewDefault(HeapType type) {
+ return push(builder.makeStructNew(type, {}));
+}
+
+Result<> IRBuilder::makeStructGet(HeapType type, Index field, bool signed_) {
+ const auto& fields = type.getStruct().fields;
+ StructGet curr;
+ CHECK_ERR(visitStructGet(&curr));
+ CHECK_ERR(validateTypeAnnotation(type, curr.ref));
+ return push(
+ builder.makeStructGet(field, curr.ref, fields[field].type, signed_));
+}
+
+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));
+}
+
+Result<> IRBuilder::makeArrayNew(HeapType type) {
+ ArrayNew curr;
+ // 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));
+}
+
+Result<> IRBuilder::makeArrayNewDefault(HeapType type) {
+ ArrayNew curr;
+ CHECK_ERR(visitArrayNew(&curr));
+ return push(builder.makeArrayNew(type, curr.size));
+}
+
+Result<> IRBuilder::makeArrayNewData(HeapType type, Name data) {
+ ArrayNewData curr;
+ CHECK_ERR(visitArrayNewData(&curr));
+ return push(builder.makeArrayNewData(type, data, curr.offset, curr.size));
+}
+
+Result<> IRBuilder::makeArrayNewElem(HeapType type, Name elem) {
+ ArrayNewElem curr;
+ CHECK_ERR(visitArrayNewElem(&curr));
+ return push(builder.makeArrayNewElem(type, elem, curr.offset, curr.size));
+}
+
+// Result<> IRBuilder::makeArrayNewFixed() {}
+
+Result<> IRBuilder::makeArrayGet(HeapType type, bool signed_) {
+ ArrayGet curr;
+ CHECK_ERR(visitArrayGet(&curr));
+ CHECK_ERR(validateTypeAnnotation(type, curr.ref));
+ return push(builder.makeArrayGet(
+ curr.ref, curr.index, type.getArray().element.type, signed_));
+}
+
+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));
+}
+
+Result<> IRBuilder::makeArrayLen() {
+ ArrayLen curr;
+ CHECK_ERR(visitArrayLen(&curr));
+ return push(builder.makeArrayLen(curr.ref));
+}
+
+Result<> IRBuilder::makeArrayCopy(HeapType destType, HeapType srcType) {
+ ArrayCopy curr;
+ CHECK_ERR(visitArrayCopy(&curr));
+ CHECK_ERR(validateTypeAnnotation(destType, curr.destRef));
+ CHECK_ERR(validateTypeAnnotation(srcType, curr.srcRef));
+ return push(builder.makeArrayCopy(
+ curr.destRef, curr.destIndex, curr.srcRef, curr.srcIndex, curr.length));
+}
+
+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));
+}
+
+// Result<> IRBuilder::makeArrayInitData() {}
+
+// Result<> IRBuilder::makeArrayInitElem() {}
+
+// Result<> IRBuilder::makeRefAs() {}
+
+// Result<> IRBuilder::makeStringNew() {}
+
+// Result<> IRBuilder::makeStringConst() {}
+
+// Result<> IRBuilder::makeStringMeasure() {}
+
+// Result<> IRBuilder::makeStringEncode() {}
+
+// Result<> IRBuilder::makeStringConcat() {}
+
+// Result<> IRBuilder::makeStringEq() {}
+
+// Result<> IRBuilder::makeStringAs() {}
+
+// Result<> IRBuilder::makeStringWTF8Advance() {}
+
+// Result<> IRBuilder::makeStringWTF16Get() {}
+
+// Result<> IRBuilder::makeStringIterNext() {}
+
+// Result<> IRBuilder::makeStringIterMove() {}
+
+// Result<> IRBuilder::makeStringSliceWTF() {}
+
+// Result<> IRBuilder::makeStringSliceIter() {}
+
+} // namespace wasm
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index 970901409..bf438ae68 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -18,6 +18,7 @@
#include "ir/names.h"
#include "support/name.h"
#include "wasm-builder.h"
+#include "wasm-ir-builder.h"
#include "wasm-type.h"
#include "wasm.h"
#include "wat-lexer.h"
@@ -1305,20 +1306,12 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
// local.get, etc.
Function* func = nullptr;
- // The context for a single block scope, including the instructions parsed
- // inside that scope so far and the ultimate result type we expect this block
- // to have.
- struct BlockCtx {
- std::vector<Expression*> exprStack;
- Type type;
- };
-
- // The stack of block contexts currently being parsed.
- std::vector<BlockCtx> scopeStack;
+ IRBuilder irBuilder;
- // Whether we have seen an unreachable instruction and are in
- // stack-polymorphic unreachable mode.
- bool unreachable = false;
+ void setFunction(Function* func) {
+ this->func = func;
+ irBuilder.setFunction(func);
+ }
ParseDefsCtx(std::string_view in,
Module& wasm,
@@ -1326,105 +1319,27 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
const std::unordered_map<Index, HeapType>& implicitTypes,
const IndexMap& typeIndices)
: TypeParserCtx(typeIndices), in(in), wasm(wasm), builder(wasm),
- types(types), implicitTypes(implicitTypes) {}
+ types(types), implicitTypes(implicitTypes), irBuilder(wasm) {}
- std::vector<Expression*>& getExprStack() {
- if (scopeStack.empty()) {
- // We are not in a function, so push a dummy scope.
- scopeStack.push_back({{}, Type::none});
+ template<typename T> Result<T> withLoc(Index pos, Result<T> res) {
+ if (auto err = res.getErr()) {
+ return in.err(pos, err->msg);
}
- return scopeStack.back().exprStack;
+ return res;
}
- void pushScope(Type type) { scopeStack.push_back({{}, type}); }
-
- Type getResultType() {
- assert(!scopeStack.empty());
- return scopeStack.back().type;
- }
-
- Result<> push(Index pos, Expression* expr) {
- auto& exprStack = getExprStack();
- 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) {
- expr = builder.dropIfConcretelyTyped(expr);
- }
- unreachable = true;
- exprStack.push_back(expr);
- } else if (expr->type.isTuple()) {
- auto scratchIdx = addScratchLocal(pos, expr->type);
- CHECK_ERR(scratchIdx);
- CHECK_ERR(push(pos, builder.makeLocalSet(*scratchIdx, expr)));
- for (Index i = 0; i < expr->type.size(); ++i) {
- CHECK_ERR(push(pos,
- builder.makeTupleExtract(
- builder.makeLocalGet(*scratchIdx, expr->type), i)));
- }
- } else {
- exprStack.push_back(expr);
- }
- return Ok{};
- }
-
- Result<Expression*> pop(Index pos) {
- auto& exprStack = getExprStack();
-
- // 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;
- }
- }
-
- if (firstNone == 0) {
- // There are no expressions that produce values.
- if (unreachable) {
- return builder.makeUnreachable();
- }
- return in.err(pos, "popping from empty stack");
- }
-
- if (firstNone == exprStack.size()) {
- // The last expression produced a value.
- auto expr = exprStack.back();
- exprStack.pop_back();
- return expr;
- }
-
- // 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 scratchIdx = addScratchLocal(pos, 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);
+ template<typename T> Result<T> withLoc(Result<T> res) {
+ return withLoc(in.getPos(), res);
}
HeapType getBlockTypeFromResult(const std::vector<Type> results) {
assert(results.size() == 1);
- pushScope(results[0]);
+ irBuilder.pushScope(results[0]);
return HeapType(Signature(Type::none, results[0]));
}
Result<HeapType> getBlockTypeFromTypeUse(Index pos, HeapType type) {
- pushScope(type.getSignature().results);
+ irBuilder.pushScope(type.getSignature().results);
return type;
}
@@ -1433,52 +1348,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
void appendInstr(Ok&, InstrT instr) {}
Result<InstrsT> finishInstrs(Ok&) {
- 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(self().in.getPos());
- 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(self().in.getPos());
- CHECK_ERR(expr);
- exprStack.push_back(*expr);
- }
- unreachable = false;
- auto ret = std::move(exprStack);
- scopeStack.pop_back();
- return ret;
+ return withLoc(irBuilder.finishInstrs());
}
- Expression* instrToExpr(Ok&) {
- auto& exprStack = getExprStack();
- assert(scopeStack.size() == 1);
- assert(exprStack.size() == 1);
-
- auto e = exprStack.back();
- exprStack.clear();
- unreachable = false;
- return e;
- }
+ Expression* instrToExpr(Ok&) { return irBuilder.build(); }
GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; }
@@ -1677,17 +1550,6 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
Memarg getMemarg(uint64_t offset, uint32_t align) { return {offset, align}; }
- Result<> validateTypeAnnotation(Index pos, HeapType type, Expression* child) {
- if (child->type == Type::unreachable) {
- return Ok{};
- }
- if (!child->type.isRef() ||
- !HeapType::isSubType(child->type.getHeapType(), type)) {
- return in.err(pos, "invalid reference type on stack");
- }
- return Ok{};
- }
-
Result<Name> getMemory(Index pos, Name* mem) {
if (mem) {
return *mem;
@@ -1705,126 +1567,88 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
// 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) {
- return push(pos, builder.makeBlock(*label, instrs, results));
- } else {
- return push(pos, builder.makeBlock(instrs, results));
+ block->name = *label;
}
+ block->list.set(instrs);
+ return withLoc(pos, irBuilder.visit(block));
}
Result<> makeUnreachable(Index pos) {
- return push(pos, builder.makeUnreachable());
+ return withLoc(pos, irBuilder.makeUnreachable());
}
- Result<> makeNop(Index pos) { return push(pos, builder.makeNop()); }
+ Result<> makeNop(Index pos) { return withLoc(pos, irBuilder.makeNop()); }
Result<> makeBinary(Index pos, BinaryOp op) {
- auto rhs = pop(pos);
- CHECK_ERR(rhs);
- auto lhs = pop(pos);
- CHECK_ERR(lhs);
- return push(pos, builder.makeBinary(op, *lhs, *rhs));
+ return withLoc(pos, irBuilder.makeBinary(op));
}
Result<> makeUnary(Index pos, UnaryOp op) {
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeUnary(op, *val));
+ return withLoc(pos, irBuilder.makeUnary(op));
}
Result<> makeSelect(Index pos, std::vector<Type>* res) {
- if (res && res->size() > 1) {
- return in.err(pos, "select may not have more than one result type");
- }
- auto cond = pop(pos);
- CHECK_ERR(cond);
- auto ifFalse = pop(pos);
- CHECK_ERR(ifFalse);
- auto ifTrue = pop(pos);
- CHECK_ERR(ifTrue);
- auto select = builder.makeSelect(*cond, *ifTrue, *ifFalse);
- if (res && !res->empty() && !Type::isSubType(select->type, res->front())) {
- return in.err(pos, "select type annotation is incorrect");
+ if (res && res->size()) {
+ if (res->size() > 1) {
+ return in.err(pos, "select may not have more than one result type");
+ }
+ return withLoc(pos, irBuilder.makeSelect((*res)[0]));
}
- return push(pos, builder.makeSelect(*cond, *ifTrue, *ifFalse));
+ return withLoc(pos, irBuilder.makeSelect());
}
- Result<> makeDrop(Index pos) {
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeDrop(*val));
- }
+ Result<> makeDrop(Index pos) { return withLoc(pos, irBuilder.makeDrop()); }
Result<> makeMemorySize(Index pos, Name* mem) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- return push(pos, builder.makeMemorySize(*m));
+ return withLoc(pos, irBuilder.makeMemorySize(*m));
}
Result<> makeMemoryGrow(Index pos, Name* mem) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeMemoryGrow(*val, *m));
+ return withLoc(pos, irBuilder.makeMemoryGrow(*m));
}
Result<> makeLocalGet(Index pos, Index local) {
- if (!func) {
- return in.err(pos, "local.get must be inside a function");
- }
- assert(local < func->getNumLocals());
- return push(pos, builder.makeLocalGet(local, func->getLocalType(local)));
+ return withLoc(pos, irBuilder.makeLocalGet(local));
}
Result<> makeLocalTee(Index pos, Index local) {
- if (!func) {
- return in.err(pos, "local.tee must be inside a function");
- }
- assert(local < func->getNumLocals());
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos,
- builder.makeLocalTee(local, *val, func->getLocalType(local)));
+ return withLoc(pos, irBuilder.makeLocalTee(local));
}
Result<> makeLocalSet(Index pos, Index local) {
- if (!func) {
- return in.err(pos, "local.set must be inside a function");
- }
- assert(local < func->getNumLocals());
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeLocalSet(local, *val));
+ return withLoc(pos, irBuilder.makeLocalSet(local));
}
Result<> makeGlobalGet(Index pos, Name global) {
- assert(wasm.getGlobalOrNull(global));
- auto type = wasm.getGlobal(global)->type;
- return push(pos, builder.makeGlobalGet(global, type));
+ return withLoc(pos, irBuilder.makeGlobalGet(global));
}
Result<> makeGlobalSet(Index pos, Name global) {
assert(wasm.getGlobalOrNull(global));
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeGlobalSet(global, *val));
+ return withLoc(pos, irBuilder.makeGlobalSet(global));
}
Result<> makeI32Const(Index pos, uint32_t c) {
- return push(pos, builder.makeConst(Literal(c)));
+ return withLoc(pos, irBuilder.makeConst(Literal(c)));
}
Result<> makeI64Const(Index pos, uint64_t c) {
- return push(pos, builder.makeConst(Literal(c)));
+ return withLoc(pos, irBuilder.makeConst(Literal(c)));
}
Result<> makeF32Const(Index pos, float c) {
- return push(pos, builder.makeConst(Literal(c)));
+ return withLoc(pos, irBuilder.makeConst(Literal(c)));
}
Result<> makeF64Const(Index pos, double c) {
- return push(pos, builder.makeConst(Literal(c)));
+ return withLoc(pos, irBuilder.makeConst(Literal(c)));
}
Result<> makeLoad(Index pos,
@@ -1836,168 +1660,103 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
if (isAtomic) {
- return push(pos,
- builder.makeAtomicLoad(bytes, memarg.offset, *ptr, type, *m));
+ return withLoc(pos,
+ irBuilder.makeAtomicLoad(bytes, memarg.offset, type, *m));
}
- return push(pos,
- builder.makeLoad(
- bytes, signed_, memarg.offset, memarg.align, *ptr, type, *m));
+ return withLoc(pos,
+ irBuilder.makeLoad(
+ bytes, signed_, memarg.offset, memarg.align, type, *m));
}
Result<> makeStore(
Index pos, Type type, int bytes, bool isAtomic, Name* mem, Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto val = pop(pos);
- CHECK_ERR(val);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
if (isAtomic) {
- return push(
- pos,
- builder.makeAtomicStore(bytes, memarg.offset, *ptr, *val, type, *m));
+ return withLoc(pos,
+ irBuilder.makeAtomicStore(bytes, memarg.offset, type, *m));
}
- return push(pos,
- builder.makeStore(
- bytes, memarg.offset, memarg.align, *ptr, *val, type, *m));
+ return withLoc(
+ pos, irBuilder.makeStore(bytes, memarg.offset, memarg.align, type, *m));
}
Result<> makeAtomicRMW(
Index pos, AtomicRMWOp op, Type type, int bytes, Name* mem, Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto val = pop(pos);
- CHECK_ERR(val);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
- return push(
- pos,
- builder.makeAtomicRMW(op, bytes, memarg.offset, *ptr, *val, type, *m));
+ return withLoc(pos,
+ irBuilder.makeAtomicRMW(op, bytes, memarg.offset, type, *m));
}
Result<>
makeAtomicCmpxchg(Index pos, Type type, int bytes, Name* mem, Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto replacement = pop(pos);
- CHECK_ERR(replacement);
- auto expected = pop(pos);
- CHECK_ERR(expected);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
- return push(
- pos,
- builder.makeAtomicCmpxchg(
- bytes, memarg.offset, *ptr, *expected, *replacement, type, *m));
+ return withLoc(pos,
+ irBuilder.makeAtomicCmpxchg(bytes, memarg.offset, type, *m));
}
Result<> makeAtomicWait(Index pos, Type type, Name* mem, Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto timeout = pop(pos);
- CHECK_ERR(timeout);
- auto expected = pop(pos);
- CHECK_ERR(expected);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
- return push(pos,
- builder.makeAtomicWait(
- *ptr, *expected, *timeout, type, memarg.offset, *m));
+ return withLoc(pos, irBuilder.makeAtomicWait(type, memarg.offset, *m));
}
Result<> makeAtomicNotify(Index pos, Name* mem, Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto count = pop(pos);
- CHECK_ERR(count);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
- return push(pos, builder.makeAtomicNotify(*ptr, *count, memarg.offset, *m));
+ return withLoc(pos, irBuilder.makeAtomicNotify(memarg.offset, *m));
}
Result<> makeAtomicFence(Index pos) {
- return push(pos, builder.makeAtomicFence());
+ return withLoc(pos, irBuilder.makeAtomicFence());
}
Result<> makeSIMDExtract(Index pos, SIMDExtractOp op, uint8_t lane) {
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeSIMDExtract(op, *val, lane));
+ return withLoc(pos, irBuilder.makeSIMDExtract(op, lane));
}
Result<> makeSIMDReplace(Index pos, SIMDReplaceOp op, uint8_t lane) {
- auto val = pop(pos);
- CHECK_ERR(val);
- auto vec = pop(pos);
- CHECK_ERR(vec);
- return push(pos, builder.makeSIMDReplace(op, *vec, lane, *val));
+ return withLoc(pos, irBuilder.makeSIMDReplace(op, lane));
}
Result<> makeSIMDShuffle(Index pos, const std::array<uint8_t, 16>& lanes) {
- auto rhs = pop(pos);
- CHECK_ERR(rhs);
- auto lhs = pop(pos);
- CHECK_ERR(lhs);
- return push(pos, builder.makeSIMDShuffle(*lhs, *rhs, lanes));
+ return withLoc(pos, irBuilder.makeSIMDShuffle(lanes));
}
Result<> makeSIMDTernary(Index pos, SIMDTernaryOp op) {
- auto c = pop(pos);
- CHECK_ERR(c);
- auto b = pop(pos);
- CHECK_ERR(b);
- auto a = pop(pos);
- CHECK_ERR(a);
- return push(pos, builder.makeSIMDTernary(op, *a, *b, *c));
+ return withLoc(pos, irBuilder.makeSIMDTernary(op));
}
Result<> makeSIMDShift(Index pos, SIMDShiftOp op) {
- auto shift = pop(pos);
- CHECK_ERR(shift);
- auto vec = pop(pos);
- CHECK_ERR(vec);
- return push(pos, builder.makeSIMDShift(op, *vec, *shift));
+ return withLoc(pos, irBuilder.makeSIMDShift(op));
}
Result<> makeSIMDLoad(Index pos, SIMDLoadOp op, Name* mem, Memarg memarg) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
- return push(
- pos, builder.makeSIMDLoad(op, memarg.offset, memarg.align, *ptr, *m));
+ return withLoc(pos,
+ irBuilder.makeSIMDLoad(op, memarg.offset, memarg.align, *m));
}
Result<> makeSIMDLoadStoreLane(
Index pos, SIMDLoadStoreLaneOp op, Name* mem, Memarg memarg, uint8_t lane) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto vec = pop(pos);
- CHECK_ERR(vec);
- auto ptr = pop(pos);
- CHECK_ERR(ptr);
- return push(pos,
- builder.makeSIMDLoadStoreLane(
- op, memarg.offset, memarg.align, lane, *ptr, *vec, *m));
+ return withLoc(pos,
+ irBuilder.makeSIMDLoadStoreLane(
+ op, memarg.offset, memarg.align, lane, *m));
}
Result<> makeMemoryInit(Index pos, Name* mem, Name data) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto size = pop(pos);
- CHECK_ERR(size);
- auto offset = pop(pos);
- CHECK_ERR(offset);
- auto dest = pop(pos);
- CHECK_ERR(dest);
- return push(pos, builder.makeMemoryInit(data, *dest, *offset, *size, *m));
+ return withLoc(pos, irBuilder.makeMemoryInit(data, *m));
}
Result<> makeDataDrop(Index pos, Name data) {
- return push(pos, builder.makeDataDrop(data));
+ return withLoc(pos, irBuilder.makeDataDrop(data));
}
Result<> makeMemoryCopy(Index pos, Name* destMem, Name* srcMem) {
@@ -2005,240 +1764,87 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
CHECK_ERR(destMemory);
auto srcMemory = getMemory(pos, srcMem);
CHECK_ERR(srcMemory);
- auto size = pop(pos);
- CHECK_ERR(size);
- auto src = pop(pos);
- CHECK_ERR(src);
- auto dest = pop(pos);
- CHECK_ERR(dest);
- return push(
- pos, builder.makeMemoryCopy(*dest, *src, *size, *destMemory, *srcMemory));
+ return withLoc(pos, irBuilder.makeMemoryCopy(*destMemory, *srcMemory));
}
Result<> makeMemoryFill(Index pos, Name* mem) {
auto m = getMemory(pos, mem);
CHECK_ERR(m);
- auto size = pop(pos);
- CHECK_ERR(size);
- auto val = pop(pos);
- CHECK_ERR(val);
- auto dest = pop(pos);
- CHECK_ERR(dest);
- return push(pos, builder.makeMemoryFill(*dest, *val, *size, *m));
+ return withLoc(pos, irBuilder.makeMemoryFill(*m));
}
Result<> makeReturn(Index pos) {
- if (!func) {
- return in.err("cannot return outside of a function");
- }
- size_t n = func->getResults().size();
- if (n == 0) {
- return push(pos, builder.makeReturn());
- }
- if (n == 1) {
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeReturn(*val));
- }
- std::vector<Expression*> vals(n);
- for (size_t i = 0; i < n; ++i) {
- auto val = pop(pos);
- CHECK_ERR(val);
- vals[n - i - 1] = *val;
- }
- return push(pos, builder.makeReturn(builder.makeTupleMake(vals)));
+ return withLoc(pos, irBuilder.makeReturn());
}
Result<> makeRefNull(Index pos, HeapType type) {
- return push(pos, builder.makeRefNull(type));
+ return withLoc(pos, irBuilder.makeRefNull(type));
}
Result<> makeRefIsNull(Index pos) {
- auto ref = pop(pos);
- CHECK_ERR(ref);
- return push(pos, builder.makeRefIsNull(*ref));
+ return withLoc(pos, irBuilder.makeRefIsNull());
}
- Result<> makeRefEq(Index pos) {
- auto rhs = pop(pos);
- CHECK_ERR(rhs);
- auto lhs = pop(pos);
- CHECK_ERR(lhs);
- return push(pos, builder.makeRefEq(*lhs, *rhs));
- }
+ Result<> makeRefEq(Index pos) { return withLoc(pos, irBuilder.makeRefEq()); }
Result<> makeI31New(Index pos) {
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeI31New(*val));
+ return withLoc(pos, irBuilder.makeI31New());
}
Result<> makeI31Get(Index pos, bool signed_) {
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeI31Get(*val, signed_));
+ return withLoc(pos, irBuilder.makeI31Get(signed_));
}
Result<> makeStructNew(Index pos, HeapType type) {
- if (!type.isStruct()) {
- return in.err(pos, "expected struct type annotation");
- }
- size_t numOps = type.getStruct().fields.size();
- std::vector<Expression*> ops(numOps);
- for (size_t i = 0; i < numOps; ++i) {
- auto op = pop(pos);
- CHECK_ERR(op);
- ops[numOps - i - 1] = *op;
- }
- return push(pos, builder.makeStructNew(type, ops));
+ return withLoc(pos, irBuilder.makeStructNew(type));
}
Result<> makeStructNewDefault(Index pos, HeapType type) {
- return push(pos, builder.makeStructNew(type, std::array<Expression*, 0>{}));
+ return withLoc(pos, irBuilder.makeStructNewDefault(type));
}
Result<> makeStructGet(Index pos, HeapType type, Index field, bool signed_) {
- if (!type.isStruct()) {
- return in.err(pos, "expected struct type annotation");
- }
- const auto& fields = type.getStruct().fields;
- if (field >= fields.size()) {
- return in.err(pos, "struct field index out of bounds");
- }
- auto fieldType = fields[field].type;
- auto ref = pop(pos);
- CHECK_ERR(ref);
- CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
- return push(pos, builder.makeStructGet(field, *ref, fieldType, signed_));
+ return withLoc(pos, irBuilder.makeStructGet(type, field, signed_));
}
Result<> makeStructSet(Index pos, HeapType type, Index field) {
- if (!type.isStruct()) {
- return in.err(pos, "expected struct type annotation");
- }
- if (field >= type.getStruct().fields.size()) {
- return in.err(pos, "struct field index out of bounds");
- }
- auto val = pop(pos);
- CHECK_ERR(val);
- auto ref = pop(pos);
- CHECK_ERR(ref);
- CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
- return push(pos, builder.makeStructSet(field, *ref, *val));
+ return withLoc(pos, irBuilder.makeStructSet(type, field));
}
Result<> makeArrayNew(Index pos, HeapType type) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto size = pop(pos);
- CHECK_ERR(size);
- auto val = pop(pos);
- CHECK_ERR(val);
- return push(pos, builder.makeArrayNew(type, *size, *val));
+ return withLoc(pos, irBuilder.makeArrayNew(type));
}
Result<> makeArrayNewDefault(Index pos, HeapType type) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto size = pop(pos);
- CHECK_ERR(size);
- return push(pos, builder.makeArrayNew(type, *size));
+ return withLoc(pos, irBuilder.makeArrayNewDefault(type));
}
Result<> makeArrayNewData(Index pos, HeapType type, Name data) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto size = pop(pos);
- CHECK_ERR(size);
- auto offset = pop(pos);
- CHECK_ERR(offset);
- return push(pos, builder.makeArrayNewData(type, data, *offset, *size));
+ return withLoc(pos, irBuilder.makeArrayNewData(type, data));
}
- Result<> makeArrayNewElem(Index pos, HeapType type, Name data) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto size = pop(pos);
- CHECK_ERR(size);
- auto offset = pop(pos);
- CHECK_ERR(offset);
- return push(pos, builder.makeArrayNewElem(type, data, *offset, *size));
+ Result<> makeArrayNewElem(Index pos, HeapType type, Name elem) {
+ return withLoc(pos, irBuilder.makeArrayNewElem(type, elem));
}
Result<> makeArrayGet(Index pos, HeapType type, bool signed_) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto elemType = type.getArray().element.type;
- auto index = pop(pos);
- CHECK_ERR(index);
- auto ref = pop(pos);
- CHECK_ERR(ref);
- CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
- return push(pos, builder.makeArrayGet(*ref, *index, elemType, signed_));
+ return withLoc(pos, irBuilder.makeArrayGet(type, signed_));
}
Result<> makeArraySet(Index pos, HeapType type) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto val = pop(pos);
- CHECK_ERR(val);
- auto index = pop(pos);
- CHECK_ERR(index);
- auto ref = pop(pos);
- CHECK_ERR(ref);
- CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
- return push(pos, builder.makeArraySet(*ref, *index, *val));
+ return withLoc(pos, irBuilder.makeArraySet(type));
}
Result<> makeArrayLen(Index pos) {
- auto ref = pop(pos);
- CHECK_ERR(ref);
- return push(pos, builder.makeArrayLen(*ref));
+ return withLoc(pos, irBuilder.makeArrayLen());
}
Result<> makeArrayCopy(Index pos, HeapType destType, HeapType srcType) {
- if (!destType.isArray()) {
- return in.err(pos, "expected array destination type annotation");
- }
- if (!srcType.isArray()) {
- return in.err(pos, "expected array source type annotation");
- }
- auto len = pop(pos);
- CHECK_ERR(len);
- auto srcIdx = pop(pos);
- CHECK_ERR(srcIdx);
- auto srcRef = pop(pos);
- CHECK_ERR(srcRef);
- auto destIdx = pop(pos);
- CHECK_ERR(destIdx);
- auto destRef = pop(pos);
- CHECK_ERR(destRef);
- CHECK_ERR(validateTypeAnnotation(pos, srcType, *srcRef));
- CHECK_ERR(validateTypeAnnotation(pos, destType, *destRef));
- return push(
- pos, builder.makeArrayCopy(*destRef, *destIdx, *srcRef, *srcIdx, *len));
+ return withLoc(pos, irBuilder.makeArrayCopy(destType, srcType));
}
Result<> makeArrayFill(Index pos, HeapType type) {
- if (!type.isArray()) {
- return in.err(pos, "expected array type annotation");
- }
- auto size = pop(pos);
- CHECK_ERR(size);
- auto value = pop(pos);
- CHECK_ERR(value);
- auto index = pop(pos);
- CHECK_ERR(index);
- auto ref = pop(pos);
- CHECK_ERR(ref);
- CHECK_ERR(validateTypeAnnotation(pos, type, *ref));
- return push(pos, builder.makeArrayFill(*ref, *index, *value, *size));
+ return withLoc(pos, irBuilder.makeArrayFill(type));
}
};
@@ -4286,8 +3892,8 @@ Result<> parseModule(Module& wasm, std::string_view input) {
for (Index i = 0; i < decls.funcDefs.size(); ++i) {
ctx.index = i;
- ctx.func = wasm.functions[i].get();
- ctx.pushScope(ctx.func->getResults());
+ ctx.setFunction(wasm.functions[i].get());
+ ctx.irBuilder.pushScope(ctx.func->getResults());
WithPosition with(ctx, decls.funcDefs[i].pos);
auto parsed = func(ctx);
CHECK_ERR(parsed);