diff options
author | Thomas Lively <tlively@google.com> | 2023-08-22 12:57:02 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-22 17:57:02 +0000 |
commit | 9eb408bf06da91838245dcda33553c356190a643 (patch) | |
tree | 5696ecdc90a2f5f76a8f23d49d2f52b4dfc72363 /src/wasm/wasm-ir-builder.cpp | |
parent | 1d95fdc7805ef2f29b8b8b0cce8f7c8cc385edd0 (diff) | |
download | binaryen-9eb408bf06da91838245dcda33553c356190a643.tar.gz binaryen-9eb408bf06da91838245dcda33553c356190a643.tar.bz2 binaryen-9eb408bf06da91838245dcda33553c356190a643.zip |
Factor IRBuilder utility out of the new wat parser (#5880)
Add an IRBuilder utility in a new wasm-ir-builder.h header. IRBuilder is
extremely similar to Builder, except that it manages building full trees of
Binaryen IR from a linear sequence of instructions, whereas Builder only builds
a single IR node at a time. To build full IR trees, IRBuilder maintains an
internal stack of expressions, popping children off the stack and pushing the
new node onto the stack whenever it builds a new node.
In addition to providing makeXYZ function to allocate, initialize, and finalize
new IR nodes, IRBuilder also provides a visit() method that can be used when the
user has already allocated the IR nodes and only needs to reconstruct the
connections between them. This will be useful in outlining both for constructing
outlined functions and for reconstructing functions around arbitrary outlined
holes.
Besides the new wat parser and outlining, this new utility can also eventually
be used in the binary parser and to convert from Poppy IR back to Binaryen IR if
that ever becomes necessary.
To simplify this initial change, IRBuilder exposes the same interface as the
code it replaces in the wat parser. A future change requiring more extensive
changes to the wat parser will simplify this interface. Also, since the new code
is tested only via the new wat parser, it only supports building instructions
that were already supported by the new wat parser to avoid trying to support any
instructions without corresponding testing. Implementing support for the
remaining instructions is left as future work.
Diffstat (limited to 'src/wasm/wasm-ir-builder.cpp')
-rw-r--r-- | src/wasm/wasm-ir-builder.cpp | 703 |
1 files changed, 703 insertions, 0 deletions
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 |