summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp47
-rw-r--r--src/wasm/wasm-s-parser.cpp34
-rw-r--r--src/wasm/wasm-stack.cpp54
-rw-r--r--src/wasm/wasm-validator.cpp141
-rw-r--r--src/wasm/wasm.cpp19
-rw-r--r--src/wasm/wat-parser.cpp59
6 files changed, 221 insertions, 133 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 3975dd390..981932470 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -4053,10 +4053,10 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (maybeVisitStructSet(curr, opcode)) {
break;
}
- if (maybeVisitArrayNew(curr, opcode)) {
+ if (maybeVisitArrayNewData(curr, opcode)) {
break;
}
- if (maybeVisitArrayNewSeg(curr, opcode)) {
+ if (maybeVisitArrayNewElem(curr, opcode)) {
break;
}
if (maybeVisitArrayNewFixed(curr, opcode)) {
@@ -7139,7 +7139,8 @@ bool WasmBinaryBuilder::maybeVisitStructSet(Expression*& out, uint32_t code) {
return true;
}
-bool WasmBinaryBuilder::maybeVisitArrayNew(Expression*& out, uint32_t code) {
+bool WasmBinaryBuilder::maybeVisitArrayNewData(Expression*& out,
+ uint32_t code) {
if (code == BinaryConsts::ArrayNew || code == BinaryConsts::ArrayNewDefault) {
auto heapType = getIndexedHeapType();
auto* size = popNonVoidExpression();
@@ -7153,22 +7154,26 @@ bool WasmBinaryBuilder::maybeVisitArrayNew(Expression*& out, uint32_t code) {
return false;
}
-bool WasmBinaryBuilder::maybeVisitArrayNewSeg(Expression*& out, uint32_t code) {
+bool WasmBinaryBuilder::maybeVisitArrayNewElem(Expression*& out,
+ uint32_t code) {
if (code == BinaryConsts::ArrayNewData ||
code == BinaryConsts::ArrayNewElem) {
- auto op = code == BinaryConsts::ArrayNewData ? NewData : NewElem;
+ auto isData = code == BinaryConsts::ArrayNewData;
auto heapType = getIndexedHeapType();
auto segIdx = getU32LEB();
auto* size = popNonVoidExpression();
auto* offset = popNonVoidExpression();
- auto* built =
- Builder(wasm).makeArrayNewSeg(op, heapType, Name(), offset, size);
- if (op == NewData) {
- dataRefs[segIdx].push_back(&built->segment);
+ if (isData) {
+ auto* curr =
+ Builder(wasm).makeArrayNewData(heapType, Name(), offset, size);
+ dataRefs[segIdx].push_back(&curr->segment);
+ out = curr;
} else {
- elemRefs[segIdx].push_back(&built->segment);
+ auto* curr =
+ Builder(wasm).makeArrayNewElem(heapType, Name(), offset, size);
+ elemRefs[segIdx].push_back(&curr->segment);
+ out = curr;
}
- out = built;
return true;
}
return false;
@@ -7271,13 +7276,12 @@ bool WasmBinaryBuilder::maybeVisitArrayFill(Expression*& out, uint32_t code) {
}
bool WasmBinaryBuilder::maybeVisitArrayInit(Expression*& out, uint32_t code) {
- ArrayInitOp op;
+ bool isData = true;
switch (code) {
case BinaryConsts::ArrayInitData:
- op = InitData;
break;
case BinaryConsts::ArrayInitElem:
- op = InitElem;
+ isData = false;
break;
default:
return false;
@@ -7289,14 +7293,17 @@ bool WasmBinaryBuilder::maybeVisitArrayInit(Expression*& out, uint32_t code) {
auto* index = popNonVoidExpression();
auto* ref = popNonVoidExpression();
validateHeapTypeUsingChild(ref, heapType);
- auto* built =
- Builder(wasm).makeArrayInit(op, Name(), ref, index, offset, size);
- if (op == InitData) {
- dataRefs[segIdx].push_back(&built->segment);
+ if (isData) {
+ auto* curr =
+ Builder(wasm).makeArrayInitData(Name(), ref, index, offset, size);
+ dataRefs[segIdx].push_back(&curr->segment);
+ out = curr;
} else {
- elemRefs[segIdx].push_back(&built->segment);
+ auto* curr =
+ Builder(wasm).makeArrayInitElem(Name(), ref, index, offset, size);
+ elemRefs[segIdx].push_back(&curr->segment);
+ out = curr;
}
- out = built;
return true;
}
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 968c8bd45..44008bb75 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2974,14 +2974,20 @@ Expression* SExpressionWasmBuilder::makeArrayNew(Element& s, bool default_) {
return Builder(wasm).makeArrayNew(heapType, size, init);
}
-Expression* SExpressionWasmBuilder::makeArrayNewSeg(Element& s,
- ArrayNewSegOp op) {
+Expression* SExpressionWasmBuilder::makeArrayNewData(Element& s) {
auto heapType = parseHeapType(*s[1]);
- Name seg =
- op == NewData ? getDataSegmentName(*s[2]) : getElemSegmentName(*s[2]);
+ Name seg = getDataSegmentName(*s[2]);
Expression* offset = parseExpression(*s[3]);
Expression* size = parseExpression(*s[4]);
- return Builder(wasm).makeArrayNewSeg(op, heapType, seg, offset, size);
+ return Builder(wasm).makeArrayNewData(heapType, seg, offset, size);
+}
+
+Expression* SExpressionWasmBuilder::makeArrayNewElem(Element& s) {
+ auto heapType = parseHeapType(*s[1]);
+ Name seg = getElemSegmentName(*s[2]);
+ Expression* offset = parseExpression(*s[3]);
+ Expression* size = parseExpression(*s[4]);
+ return Builder(wasm).makeArrayNewElem(heapType, seg, offset, size);
}
Expression* SExpressionWasmBuilder::makeArrayNewFixed(Element& s) {
@@ -3051,16 +3057,26 @@ Expression* SExpressionWasmBuilder::makeArrayFill(Element& s) {
return Builder(wasm).makeArrayFill(ref, index, value, size);
}
-Expression* SExpressionWasmBuilder::makeArrayInit(Element& s, ArrayInitOp op) {
+Expression* SExpressionWasmBuilder::makeArrayInitData(Element& s) {
+ auto heapType = parseHeapType(*s[1]);
+ auto seg = getDataSegmentName(*s[2]);
+ auto ref = parseExpression(*s[3]);
+ validateHeapTypeUsingChild(ref, heapType, s);
+ auto index = parseExpression(*s[4]);
+ auto offset = parseExpression(*s[5]);
+ auto size = parseExpression(*s[6]);
+ return Builder(wasm).makeArrayInitData(seg, ref, index, offset, size);
+}
+
+Expression* SExpressionWasmBuilder::makeArrayInitElem(Element& s) {
auto heapType = parseHeapType(*s[1]);
- auto seg =
- op == InitData ? getDataSegmentName(*s[2]) : getElemSegmentName(*s[2]);
+ auto seg = getElemSegmentName(*s[2]);
auto ref = parseExpression(*s[3]);
validateHeapTypeUsingChild(ref, heapType, s);
auto index = parseExpression(*s[4]);
auto offset = parseExpression(*s[5]);
auto size = parseExpression(*s[6]);
- return Builder(wasm).makeArrayInit(op, seg, ref, index, offset, size);
+ return Builder(wasm).makeArrayInitElem(seg, ref, index, offset, size);
}
Expression* SExpressionWasmBuilder::makeRefAs(Element& s, RefAsOp op) {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 286b049aa..14c24d807 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2118,22 +2118,18 @@ void BinaryInstWriter::visitArrayNew(ArrayNew* curr) {
parent.writeIndexedHeapType(curr->type.getHeapType());
}
-void BinaryInstWriter::visitArrayNewSeg(ArrayNewSeg* curr) {
+void BinaryInstWriter::visitArrayNewData(ArrayNewData* curr) {
o << int8_t(BinaryConsts::GCPrefix);
- switch (curr->op) {
- case NewData:
- o << U32LEB(BinaryConsts::ArrayNewData);
- parent.writeIndexedHeapType(curr->type.getHeapType());
- o << U32LEB(parent.getDataSegmentIndex(curr->segment));
- break;
- case NewElem:
- o << U32LEB(BinaryConsts::ArrayNewElem);
- parent.writeIndexedHeapType(curr->type.getHeapType());
- o << U32LEB(parent.getElementSegmentIndex(curr->segment));
- break;
- default:
- WASM_UNREACHABLE("unexpected op");
- }
+ o << U32LEB(BinaryConsts::ArrayNewData);
+ parent.writeIndexedHeapType(curr->type.getHeapType());
+ o << U32LEB(parent.getDataSegmentIndex(curr->segment));
+}
+
+void BinaryInstWriter::visitArrayNewElem(ArrayNewElem* curr) {
+ o << int8_t(BinaryConsts::GCPrefix);
+ o << U32LEB(BinaryConsts::ArrayNewElem);
+ parent.writeIndexedHeapType(curr->type.getHeapType());
+ o << U32LEB(parent.getElementSegmentIndex(curr->segment));
}
void BinaryInstWriter::visitArrayNewFixed(ArrayNewFixed* curr) {
@@ -2194,26 +2190,26 @@ void BinaryInstWriter::visitArrayFill(ArrayFill* curr) {
parent.writeIndexedHeapType(curr->ref->type.getHeapType());
}
-void BinaryInstWriter::visitArrayInit(ArrayInit* curr) {
+void BinaryInstWriter::visitArrayInitData(ArrayInitData* curr) {
if (curr->ref->type.isNull()) {
emitUnreachable();
return;
}
o << int8_t(BinaryConsts::GCPrefix);
- switch (curr->op) {
- case InitData:
- o << U32LEB(BinaryConsts::ArrayInitData);
- parent.writeIndexedHeapType(curr->ref->type.getHeapType());
- o << U32LEB(parent.getDataSegmentIndex(curr->segment));
- break;
- case InitElem:
- o << U32LEB(BinaryConsts::ArrayInitElem);
- parent.writeIndexedHeapType(curr->ref->type.getHeapType());
- o << U32LEB(parent.getElementSegmentIndex(curr->segment));
- break;
- default:
- WASM_UNREACHABLE("unexpected op");
+ o << U32LEB(BinaryConsts::ArrayInitData);
+ parent.writeIndexedHeapType(curr->ref->type.getHeapType());
+ o << U32LEB(parent.getDataSegmentIndex(curr->segment));
+}
+
+void BinaryInstWriter::visitArrayInitElem(ArrayInitElem* curr) {
+ if (curr->ref->type.isNull()) {
+ emitUnreachable();
+ return;
}
+ o << int8_t(BinaryConsts::GCPrefix);
+ o << U32LEB(BinaryConsts::ArrayInitElem);
+ parent.writeIndexedHeapType(curr->ref->type.getHeapType());
+ o << U32LEB(parent.getElementSegmentIndex(curr->segment));
}
void BinaryInstWriter::visitRefAs(RefAs* curr) {
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 845f0f6b6..318657034 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -22,6 +22,7 @@
#include "ir/eh-utils.h"
#include "ir/features.h"
#include "ir/find_all.h"
+#include "ir/gc-type-utils.h"
#include "ir/global-utils.h"
#include "ir/intrinsics.h"
#include "ir/local-graph.h"
@@ -456,14 +457,18 @@ public:
void visitStructGet(StructGet* curr);
void visitStructSet(StructSet* curr);
void visitArrayNew(ArrayNew* curr);
- void visitArrayNewSeg(ArrayNewSeg* curr);
+ template<typename ArrayNew> void visitArrayNew(ArrayNew* curr);
+ void visitArrayNewData(ArrayNewData* curr);
+ void visitArrayNewElem(ArrayNewElem* curr);
void visitArrayNewFixed(ArrayNewFixed* curr);
void visitArrayGet(ArrayGet* curr);
void visitArraySet(ArraySet* curr);
void visitArrayLen(ArrayLen* curr);
void visitArrayCopy(ArrayCopy* curr);
void visitArrayFill(ArrayFill* curr);
- void visitArrayInit(ArrayInit* curr);
+ template<typename ArrayInit> void visitArrayInit(ArrayInit* curr);
+ void visitArrayInitData(ArrayInitData* curr);
+ void visitArrayInitElem(ArrayInitElem* curr);
void visitFunction(Function* curr);
// helpers
@@ -2703,7 +2708,8 @@ void FunctionValidator::visitArrayNew(ArrayNew* curr) {
}
}
-void FunctionValidator::visitArrayNewSeg(ArrayNewSeg* curr) {
+template<typename ArrayNew>
+void FunctionValidator::visitArrayNew(ArrayNew* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
"array.new_{data, elem} requires gc [--enable-gc]");
@@ -2717,24 +2723,6 @@ void FunctionValidator::visitArrayNewSeg(ArrayNewSeg* curr) {
Type(Type::i32),
curr,
"array.new_{data, elem} size must be an i32");
- switch (curr->op) {
- case NewData:
- if (!shouldBeTrue(getModule()->getDataSegment(curr->segment),
- curr,
- "array.new_data segment should exist")) {
- return;
- }
- break;
- case NewElem:
- if (!shouldBeTrue(getModule()->getElementSegment(curr->segment),
- curr,
- "array.new_elem segment should exist")) {
- return;
- }
- break;
- default:
- WASM_UNREACHABLE("unexpected op");
- }
if (curr->type == Type::unreachable) {
return;
}
@@ -2748,26 +2736,49 @@ void FunctionValidator::visitArrayNewSeg(ArrayNewSeg* curr) {
if (!shouldBeTrue(
heapType.isArray(),
curr,
- "array.new_{data, elem} type shoudl be an array reference")) {
+ "array.new_{data, elem} type should be an array reference")) {
return;
}
- auto elemType = heapType.getArray().element.type;
- switch (curr->op) {
- case NewData:
- shouldBeTrue(elemType.isNumber(),
- curr,
- "array.new_data result element type should be numeric");
- break;
- case NewElem:
- shouldBeSubType(getModule()->getElementSegment(curr->segment)->type,
- elemType,
- curr,
- "array.new_elem segment type should be a subtype of the "
- "result element type");
- break;
- default:
- WASM_UNREACHABLE("unexpected op");
+}
+
+void FunctionValidator::visitArrayNewData(ArrayNewData* curr) {
+ visitArrayNew(curr);
+
+ if (!shouldBeTrue(getModule()->getDataSegment(curr->segment),
+ curr,
+ "array.new_data segment should exist")) {
+ return;
}
+
+ auto field = GCTypeUtils::getField(curr->type);
+ if (!field) {
+ // A bottom type, or unreachable.
+ return;
+ }
+ shouldBeTrue(field->type.isNumber(),
+ curr,
+ "array.new_data result element type should be numeric");
+}
+
+void FunctionValidator::visitArrayNewElem(ArrayNewElem* curr) {
+ visitArrayNew(curr);
+
+ if (!shouldBeTrue(getModule()->getElementSegment(curr->segment),
+ curr,
+ "array.new_elem segment should exist")) {
+ return;
+ }
+
+ auto field = GCTypeUtils::getField(curr->type);
+ if (!field) {
+ // A bottom type, or unreachable.
+ return;
+ }
+ shouldBeSubType(getModule()->getElementSegment(curr->segment)->type,
+ field->type,
+ curr,
+ "array.new_elem segment type should be a subtype of the "
+ "result element type");
}
void FunctionValidator::visitArrayNewFixed(ArrayNewFixed* curr) {
@@ -2956,6 +2967,7 @@ void FunctionValidator::visitArrayFill(ArrayFill* curr) {
element.mutable_, curr, "array.fill destination must be mutable");
}
+template<typename ArrayInit>
void FunctionValidator::visitArrayInit(ArrayInit* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
@@ -2991,24 +3003,43 @@ void FunctionValidator::visitArrayInit(ArrayInit* curr) {
auto element = heapType.getArray().element;
shouldBeTrue(
element.mutable_, curr, "array.init_* destination must be mutable");
- if (curr->op == InitData) {
- shouldBeTrue(getModule()->getDataSegmentOrNull(curr->segment),
- curr,
- "array.init_data segment must exist");
- shouldBeTrue(element.type.isNumber(),
- curr,
- "array.init_data destination must be numeric");
- } else {
- assert(curr->op == InitElem);
- auto* seg = getModule()->getElementSegmentOrNull(curr->segment);
- if (!shouldBeTrue(seg, curr, "array.init_elem segment must exist")) {
- return;
- }
- shouldBeSubType(seg->type,
- element.type,
- curr,
- "array.init_elem segment type must match destination type");
+}
+
+void FunctionValidator::visitArrayInitData(ArrayInitData* curr) {
+ visitArrayInit(curr);
+
+ shouldBeTrue(getModule()->getDataSegmentOrNull(curr->segment),
+ curr,
+ "array.init_data segment must exist");
+
+ auto field = GCTypeUtils::getField(curr->ref->type);
+ if (!field) {
+ // A bottom type, or unreachable.
+ return;
}
+ shouldBeTrue(field->type.isNumber(),
+ curr,
+ "array.init_data destination must be numeric");
+}
+
+void FunctionValidator::visitArrayInitElem(ArrayInitElem* curr) {
+ visitArrayInit(curr);
+
+ auto* seg = getModule()->getElementSegmentOrNull(curr->segment);
+ if (!shouldBeTrue(seg, curr, "array.init_elem segment must exist")) {
+ return;
+ }
+
+ auto field = GCTypeUtils::getField(curr->ref->type);
+ if (!field) {
+ // A bottom type, or unreachable.
+ return;
+ }
+
+ shouldBeSubType(seg->type,
+ field->type,
+ curr,
+ "array.init_elem segment type must match destination type");
}
void FunctionValidator::visitFunction(Function* curr) {
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index f3e345895..c3f262998 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1057,7 +1057,13 @@ void ArrayNew::finalize() {
}
}
-void ArrayNewSeg::finalize() {
+void ArrayNewData::finalize() {
+ if (offset->type == Type::unreachable || size->type == Type::unreachable) {
+ type = Type::unreachable;
+ }
+}
+
+void ArrayNewElem::finalize() {
if (offset->type == Type::unreachable || size->type == Type::unreachable) {
type = Type::unreachable;
}
@@ -1118,7 +1124,16 @@ void ArrayFill::finalize() {
}
}
-void ArrayInit::finalize() {
+void ArrayInitData::finalize() {
+ if (ref->type == Type::unreachable || index->type == Type::unreachable ||
+ offset->type == Type::unreachable || size->type == Type::unreachable) {
+ type = Type::unreachable;
+ } else {
+ type = Type::none;
+ }
+}
+
+void ArrayInitElem::finalize() {
if (ref->type == Type::unreachable || index->type == Type::unreachable ||
offset->type == Type::unreachable || size->type == Type::unreachable) {
type = Type::unreachable;
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index 51527b5e3..528d0d1af 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -789,6 +789,10 @@ struct NullInstrParserCtx {
InstrT makeArrayNewData(Index, HeapTypeT, DataIdxT) {
return Ok{};
}
+ template<typename HeapTypeT>
+ InstrT makeArrayNewElem(Index, HeapTypeT, DataIdxT) {
+ return Ok{};
+ }
template<typename HeapTypeT> InstrT makeArrayGet(Index, HeapTypeT, bool) {
return Ok{};
}
@@ -2156,8 +2160,18 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
CHECK_ERR(size);
auto offset = pop(pos);
CHECK_ERR(offset);
- return push(pos,
- builder.makeArrayNewSeg(NewData, type, data, *offset, *size));
+ return push(pos, builder.makeArrayNewData(type, data, *offset, *size));
+ }
+
+ 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<> makeArrayGet(Index pos, HeapType type, bool signed_) {
@@ -2380,7 +2394,9 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeStructSet(Ctx&, Index);
template<typename Ctx>
Result<typename Ctx::InstrT> makeArrayNew(Ctx&, Index, bool default_);
template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayNewSeg(Ctx&, Index, ArrayNewSegOp op);
+Result<typename Ctx::InstrT> makeArrayNewData(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewElem(Ctx&, Index);
template<typename Ctx>
Result<typename Ctx::InstrT> makeArrayNewFixed(Ctx&, Index);
template<typename Ctx>
@@ -2390,7 +2406,9 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeArrayLen(Ctx&, Index);
template<typename Ctx> Result<typename Ctx::InstrT> makeArrayCopy(Ctx&, Index);
template<typename Ctx> Result<typename Ctx::InstrT> makeArrayFill(Ctx&, Index);
template<typename Ctx>
-Result<typename Ctx::InstrT> makeArrayInit(Ctx&, Index, ArrayInitOp);
+Result<typename Ctx::InstrT> makeArrayInitData(Ctx&, Index);
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayInitElem(Ctx&, Index);
template<typename Ctx>
Result<typename Ctx::InstrT> makeRefAs(Ctx&, Index, RefAsOp op);
template<typename Ctx>
@@ -3528,20 +3546,21 @@ Result<typename Ctx::InstrT> makeArrayNew(Ctx& ctx, Index pos, bool default_) {
}
template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeArrayNewSeg(Ctx& ctx, Index pos, ArrayNewSegOp op) {
+Result<typename Ctx::InstrT> makeArrayNewData(Ctx& ctx, Index pos) {
auto type = typeidx(ctx);
CHECK_ERR(type);
- switch (op) {
- case NewData: {
- auto data = dataidx(ctx);
- CHECK_ERR(data);
- return ctx.makeArrayNewData(pos, *type, *data);
- }
- case NewElem:
- return ctx.in.err("unimplemented instruction");
- }
- WASM_UNREACHABLE("unexpected op");
+ auto data = dataidx(ctx);
+ CHECK_ERR(data);
+ return ctx.makeArrayNewData(pos, *type, *data);
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewElem(Ctx& ctx, Index pos) {
+ auto type = typeidx(ctx);
+ CHECK_ERR(type);
+ auto data = dataidx(ctx);
+ CHECK_ERR(data);
+ return ctx.makeArrayNewElem(pos, *type, *data);
}
template<typename Ctx>
@@ -3585,8 +3604,12 @@ Result<typename Ctx::InstrT> makeArrayFill(Ctx& ctx, Index pos) {
}
template<typename Ctx>
-Result<typename Ctx::InstrT>
-makeArrayInit(Ctx& ctx, Index pos, ArrayInitOp op) {
+Result<typename Ctx::InstrT> makeArrayInitData(Ctx& ctx, Index pos) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayInitElem(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}