summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py2
-rw-r--r--src/binaryen-c.h3
-rw-r--r--src/gen-s-parser.inc58
-rw-r--r--src/ir/ReFinalize.cpp1
-rw-r--r--src/ir/cost.h3
-rw-r--r--src/ir/effects.h5
-rw-r--r--src/ir/possible-contents.cpp20
-rw-r--r--src/literal.h4
-rw-r--r--src/passes/Print.cpp23
-rw-r--r--src/passes/RemoveUnusedModuleElements.cpp19
-rw-r--r--src/wasm-binary.h3
-rw-r--r--src/wasm-builder.h14
-rw-r--r--src/wasm-delegations-fields.def9
-rw-r--r--src/wasm-delegations.def1
-rw-r--r--src/wasm-interpreter.h62
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm.h18
-rw-r--r--src/wasm/literal.cpp51
-rw-r--r--src/wasm/wasm-binary.cpp32
-rw-r--r--src/wasm/wasm-s-parser.cpp12
-rw-r--r--src/wasm/wasm-stack.cpp16
-rw-r--r--src/wasm/wasm-validator.cpp68
-rw-r--r--src/wasm/wasm.cpp7
-rw-r--r--src/wasm/wat-parser.cpp8
-rw-r--r--src/wasm2js.h4
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt38
-rw-r--r--test/lit/arrays.wast72
-rw-r--r--test/spec/array-new-data.wast79
-rw-r--r--test/spec/array-new-elem.wast48
29 files changed, 636 insertions, 45 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 47ef4c600..80a62a197 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -590,6 +590,8 @@ instructions = [
("struct.set", "makeStructSet(s)"),
("array.new", "makeArrayNewStatic(s, false)"),
("array.new_default", "makeArrayNewStatic(s, true)"),
+ ("array.new_data", "makeArrayNewSeg(s, NewData)"),
+ ("array.new_elem", "makeArrayNewSeg(s, NewElem)"),
("array.init_static", "makeArrayInitStatic(s)"),
("array.get", "makeArrayGet(s)"),
("array.get_s", "makeArrayGet(s, true)"),
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index f5347a65b..8da248f07 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -1071,6 +1071,9 @@ BINARYEN_API BinaryenExpressionRef BinaryenArrayNew(BinaryenModuleRef module,
BinaryenHeapType type,
BinaryenExpressionRef size,
BinaryenExpressionRef init);
+
+// TODO: BinaryenArrayNewSeg
+
BINARYEN_API BinaryenExpressionRef
BinaryenArrayInit(BinaryenModuleRef module,
BinaryenHeapType type,
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 67f58b5e6..929634c6c 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -47,9 +47,25 @@ switch (op[0]) {
case '\0':
if (op == "array.new"sv) { return makeArrayNewStatic(s, false); }
goto parse_error;
- case '_':
- if (op == "array.new_default"sv) { return makeArrayNewStatic(s, true); }
- goto parse_error;
+ case '_': {
+ switch (op[10]) {
+ case 'd': {
+ switch (op[11]) {
+ case 'a':
+ if (op == "array.new_data"sv) { return makeArrayNewSeg(s, NewData); }
+ goto parse_error;
+ case 'e':
+ if (op == "array.new_default"sv) { return makeArrayNewStatic(s, true); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case 'e':
+ if (op == "array.new_elem"sv) { return makeArrayNewSeg(s, NewElem); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
default: goto parse_error;
}
}
@@ -3602,13 +3618,37 @@ switch (op[0]) {
return *ret;
}
goto parse_error;
- case '_':
- if (op == "array.new_default"sv) {
- auto ret = makeArrayNewStatic(ctx, pos, true);
- CHECK_ERR(ret);
- return *ret;
+ case '_': {
+ switch (op[10]) {
+ case 'd': {
+ switch (op[11]) {
+ case 'a':
+ if (op == "array.new_data"sv) {
+ auto ret = makeArrayNewSeg(ctx, pos, NewData);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ case 'e':
+ if (op == "array.new_default"sv) {
+ auto ret = makeArrayNewStatic(ctx, pos, true);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
+ case 'e':
+ if (op == "array.new_elem"sv) {
+ auto ret = makeArrayNewSeg(ctx, pos, NewElem);
+ CHECK_ERR(ret);
+ return *ret;
+ }
+ goto parse_error;
+ default: goto parse_error;
}
- goto parse_error;
+ }
default: goto parse_error;
}
}
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 1e77630ab..5ae6a67ce 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -164,6 +164,7 @@ void ReFinalize::visitStructNew(StructNew* curr) { curr->finalize(); }
void ReFinalize::visitStructGet(StructGet* curr) { curr->finalize(); }
void ReFinalize::visitStructSet(StructSet* curr) { curr->finalize(); }
void ReFinalize::visitArrayNew(ArrayNew* curr) { curr->finalize(); }
+void ReFinalize::visitArrayNewSeg(ArrayNewSeg* curr) { curr->finalize(); }
void ReFinalize::visitArrayInit(ArrayInit* curr) { curr->finalize(); }
void ReFinalize::visitArrayGet(ArrayGet* curr) { curr->finalize(); }
void ReFinalize::visitArraySet(ArraySet* curr) { curr->finalize(); }
diff --git a/src/ir/cost.h b/src/ir/cost.h
index ff9bade10..59b738b1b 100644
--- a/src/ir/cost.h
+++ b/src/ir/cost.h
@@ -636,6 +636,9 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
CostType visitArrayNew(ArrayNew* curr) {
return 4 + visit(curr->size) + maybeVisit(curr->init);
}
+ CostType visitArrayNewSeg(ArrayNewSeg* curr) {
+ return 4 + visit(curr->offset) + visit(curr->size);
+ }
CostType visitArrayInit(ArrayInit* curr) {
CostType ret = 4;
for (auto* child : curr->values) {
diff --git a/src/ir/effects.h b/src/ir/effects.h
index 2dfe616f9..70e9192c0 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -755,6 +755,11 @@ private:
}
}
void visitArrayNew(ArrayNew* curr) {}
+ void visitArrayNewSeg(ArrayNewSeg* curr) {
+ // Traps on out of bounds access to segments or access to dropped
+ // segments.
+ parent.implicitTrap = true;
+ }
void visitArrayInit(ArrayInit* curr) {}
void visitArrayGet(ArrayGet* curr) {
if (curr->ref->type.isNull()) {
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp
index 9c132fd0c..0c4e28568 100644
--- a/src/ir/possible-contents.cpp
+++ b/src/ir/possible-contents.cpp
@@ -896,6 +896,26 @@ struct InfoCollector
}
addRoot(curr, PossibleContents::exactType(curr->type));
}
+ void visitArrayNewSeg(ArrayNewSeg* curr) {
+ if (curr->type == Type::unreachable) {
+ return;
+ }
+ auto heapType = curr->type.getHeapType();
+ switch (curr->op) {
+ case NewData: {
+ Type elemType = heapType.getArray().element.type;
+ addRoot(DataLocation{heapType, 0},
+ PossibleContents::fromType(elemType));
+ return;
+ }
+ case NewElem: {
+ Type segType = getModule()->elementSegments[curr->segment]->type;
+ addRoot(DataLocation{heapType, 0}, PossibleContents::fromType(segType));
+ return;
+ }
+ }
+ WASM_UNREACHABLE("unexpected op");
+ }
void visitArrayInit(ArrayInit* curr) {
if (curr->type == Type::unreachable) {
return;
diff --git a/src/literal.h b/src/literal.h
index 7d7c778bc..213713a1f 100644
--- a/src/literal.h
+++ b/src/literal.h
@@ -197,6 +197,10 @@ public:
WASM_UNREACHABLE("unexpected type");
}
}
+
+ static Literal makeFromMemory(void* p, Type type);
+ static Literal makeFromMemory(void* p, const Field& field);
+
static Literal makeSignedMin(Type type) {
switch (type.getBasic()) {
case Type::i32:
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index d0f19f78e..6dbd54e73 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -2205,6 +2205,26 @@ struct PrintExpressionContents
o << ' ';
TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
}
+ void visitArrayNewSeg(ArrayNewSeg* curr) {
+ if (printUnreachableReplacement(curr)) {
+ return;
+ }
+ printMedium(o, "array.new_");
+ switch (curr->op) {
+ case NewData:
+ printMedium(o, "data");
+
+ break;
+ case NewElem:
+ printMedium(o, "elem");
+ break;
+ default:
+ WASM_UNREACHABLE("unexpected op");
+ }
+ o << ' ';
+ TypeNamePrinter(o, wasm).print(curr->type.getHeapType());
+ o << ' ' << curr->segment;
+ }
void visitArrayInit(ArrayInit* curr) {
if (printUnreachableReplacement(curr)) {
return;
@@ -2789,6 +2809,9 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
void visitArrayNew(ArrayNew* curr) {
maybePrintUnreachableReplacement(curr, curr->type);
}
+ void visitArrayNewSeg(ArrayNewSeg* curr) {
+ maybePrintUnreachableReplacement(curr, curr->type);
+ }
void visitArrayInit(ArrayInit* curr) {
maybePrintUnreachableReplacement(curr, curr->type);
}
diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp
index 0cafd9d9d..93cc33563 100644
--- a/src/passes/RemoveUnusedModuleElements.cpp
+++ b/src/passes/RemoveUnusedModuleElements.cpp
@@ -32,6 +32,7 @@
namespace wasm {
+// TODO: Add data segment, multiple memories (#5224)
enum class ModuleElementKind { Function, Global, Tag, Table, ElementSegment };
typedef std::pair<ModuleElementKind, Name> ModuleElement;
@@ -195,7 +196,10 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
void visitAtomicNotify(AtomicNotify* curr) { usesMemory = true; }
void visitAtomicFence(AtomicFence* curr) { usesMemory = true; }
void visitMemoryInit(MemoryInit* curr) { usesMemory = true; }
- void visitDataDrop(DataDrop* curr) { usesMemory = true; }
+ void visitDataDrop(DataDrop* curr) {
+ // TODO: Replace this with a use of a data segment (#5224).
+ usesMemory = true;
+ }
void visitMemoryCopy(MemoryCopy* curr) { usesMemory = true; }
void visitMemoryFill(MemoryFill* curr) { usesMemory = true; }
void visitMemorySize(MemorySize* curr) { usesMemory = true; }
@@ -227,6 +231,19 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> {
maybeAdd(ModuleElement(ModuleElementKind::Tag, tag));
}
}
+ void visitArrayNewSeg(ArrayNewSeg* curr) {
+ switch (curr->op) {
+ case NewData:
+ // TODO: Replace this with a use of the specific data segment (#5224).
+ usesMemory = true;
+ return;
+ case NewElem:
+ auto segment = module->elementSegments[curr->segment]->name;
+ maybeAdd(ModuleElement(ModuleElementKind::ElementSegment, segment));
+ return;
+ }
+ WASM_UNREACHABLE("unexpected op");
+ }
};
struct RemoveUnusedModuleElements : public Pass {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index db123515a..b0128cbc5 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1107,6 +1107,7 @@ enum ASTNodes {
StructSet = 0x06,
StructNew = 0x07,
StructNewDefault = 0x08,
+ ArrayNewElem = 0x10,
ArrayGet = 0x13,
ArrayGetS = 0x14,
ArrayGetU = 0x15,
@@ -1117,6 +1118,7 @@ enum ASTNodes {
ArrayInitStatic = 0x1a,
ArrayNew = 0x1b,
ArrayNewDefault = 0x1c,
+ ArrayNewData = 0x1d,
I31New = 0x20,
I31GetS = 0x21,
I31GetU = 0x22,
@@ -1706,6 +1708,7 @@ public:
bool maybeVisitStructGet(Expression*& out, uint32_t code);
bool maybeVisitStructSet(Expression*& out, uint32_t code);
bool maybeVisitArrayNew(Expression*& out, uint32_t code);
+ bool maybeVisitArrayNewSeg(Expression*& out, uint32_t code);
bool maybeVisitArrayInit(Expression*& out, uint32_t code);
bool maybeVisitArrayGet(Expression*& out, uint32_t code);
bool maybeVisitArraySet(Expression*& out, uint32_t code);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index df036b3b4..1eb31157d 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -935,6 +935,20 @@ public:
ret->finalize();
return ret;
}
+ ArrayNewSeg* makeArrayNewSeg(ArrayNewSegOp op,
+ HeapType type,
+ Index seg,
+ Expression* offset,
+ Expression* size) {
+ auto* ret = wasm.allocator.alloc<ArrayNewSeg>();
+ ret->op = op;
+ ret->segment = seg;
+ ret->offset = offset;
+ ret->size = size;
+ ret->type = Type(type, NonNullable);
+ ret->finalize();
+ return ret;
+ }
ArrayInit* makeArrayInit(HeapType type,
const std::vector<Expression*>& values) {
auto* ret = wasm.allocator.alloc<ArrayInit>();
diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def
index ff0f41d50..85a088dd8 100644
--- a/src/wasm-delegations-fields.def
+++ b/src/wasm-delegations-fields.def
@@ -664,6 +664,15 @@ switch (DELEGATE_ID) {
DELEGATE_END(ArrayNew);
break;
}
+ case Expression::Id::ArrayNewSegId: {
+ DELEGATE_START(ArrayNewSeg);
+ DELEGATE_FIELD_INT(ArrayNewSeg, op);
+ DELEGATE_FIELD_INT(ArrayNewSeg, segment);
+ DELEGATE_FIELD_CHILD(ArrayNewSeg, size);
+ DELEGATE_FIELD_CHILD(ArrayNewSeg, offset);
+ DELEGATE_END(ArrayNewSeg);
+ break;
+ }
case Expression::Id::ArrayInitId: {
DELEGATE_START(ArrayInit);
DELEGATE_FIELD_CHILD_VECTOR(ArrayInit, values);
diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def
index b88a55556..dbdef3a86 100644
--- a/src/wasm-delegations.def
+++ b/src/wasm-delegations.def
@@ -77,6 +77,7 @@ DELEGATE(StructNew);
DELEGATE(StructGet);
DELEGATE(StructSet);
DELEGATE(ArrayNew);
+DELEGATE(ArrayNewSeg);
DELEGATE(ArrayInit);
DELEGATE(ArrayGet);
DELEGATE(ArraySet);
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 8f7418719..9599d81cd 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1700,6 +1700,7 @@ public:
return Literal(std::make_shared<GCData>(curr->type.getHeapType(), data),
curr->type.getHeapType());
}
+ Flow visitArrayNewSeg(ArrayNewSeg* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitArrayInit(ArrayInit* curr) {
NOTE_ENTER("ArrayInit");
Index num = curr->values.size();
@@ -2187,6 +2188,10 @@ public:
NOTE_ENTER("SIMDLoadStoreLane");
return Flow(NONCONSTANT_FLOW);
}
+ Flow visitArrayNewSeg(ArrayNewSeg* curr) {
+ NOTE_ENTER("ArrayNewSeg");
+ return Flow(NONCONSTANT_FLOW);
+ }
Flow visitPop(Pop* curr) {
NOTE_ENTER("Pop");
return Flow(NONCONSTANT_FLOW);
@@ -3494,6 +3499,63 @@ public:
}
return {};
}
+ Flow visitArrayNewSeg(ArrayNewSeg* curr) {
+ NOTE_ENTER("ArrayNewSeg");
+ auto offsetFlow = self()->visit(curr->offset);
+ if (offsetFlow.breaking()) {
+ return offsetFlow;
+ }
+ auto sizeFlow = self()->visit(curr->size);
+ if (sizeFlow.breaking()) {
+ return sizeFlow;
+ }
+
+ auto offset = offsetFlow.getSingleValue().geti32();
+ auto size = sizeFlow.getSingleValue().geti32();
+
+ auto heapType = curr->type.getHeapType();
+ const auto& element = heapType.getArray().element;
+ auto elemType = heapType.getArray().element.type;
+
+ Literals contents;
+ contents.reserve(size);
+
+ switch (curr->op) {
+ case NewData: {
+ assert(curr->segment < wasm.dataSegments.size());
+ assert(elemType.isNumber());
+ const auto& seg = *wasm.dataSegments[curr->segment];
+ auto elemBytes = element.getByteSize();
+ auto end = (uint64_t)offset + size * elemBytes;
+ if ((size != 0ull && droppedSegments.count(curr->segment)) ||
+ end > seg.data.size()) {
+ trap("out of bounds segment access in array.new_data");
+ }
+ for (Index i = offset; i < end; i += elemBytes) {
+ auto addr = (void*)&seg.data[i];
+ contents.push_back(Literal::makeFromMemory(addr, element));
+ }
+ break;
+ }
+ case NewElem: {
+ assert(curr->segment < wasm.elementSegments.size());
+ const auto& seg = *wasm.elementSegments[curr->segment];
+ auto end = (uint64_t)offset + size;
+ // TODO: Handle dropped element segments once we support those.
+ if (end > seg.data.size()) {
+ trap("out of bounds segment access in array.new_elem");
+ }
+ for (Index i = offset; i < end; ++i) {
+ auto val = self()->visit(seg.data[i]).getSingleValue();
+ contents.push_back(val);
+ }
+ break;
+ }
+ default:
+ WASM_UNREACHABLE("unexpected op");
+ }
+ return Literal(std::make_shared<GCData>(heapType, contents), heapType);
+ }
Flow visitTry(Try* curr) {
NOTE_ENTER("Try");
try {
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 888f7d009..67459dba9 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -293,6 +293,7 @@ private:
Expression* makeStructGet(Element& s, bool signed_ = false);
Expression* makeStructSet(Element& s);
Expression* makeArrayNewStatic(Element& s, bool default_);
+ Expression* makeArrayNewSeg(Element& s, ArrayNewSegOp op);
Expression* makeArrayInitStatic(Element& s);
Expression* makeArrayGet(Element& s, bool signed_ = false);
Expression* makeArraySet(Element& s);
diff --git a/src/wasm.h b/src/wasm.h
index 2b890177e..3daff2c4c 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -573,6 +573,11 @@ enum RefAsOp {
ExternExternalize,
};
+enum ArrayNewSegOp {
+ NewData,
+ NewElem,
+};
+
enum BrOnOp {
BrOnNull,
BrOnNonNull,
@@ -719,6 +724,7 @@ public:
StructGetId,
StructSetId,
ArrayNewId,
+ ArrayNewSegId,
ArrayInitId,
ArrayGetId,
ArraySetId,
@@ -1605,6 +1611,18 @@ public:
void finalize();
};
+class ArrayNewSeg : public SpecificExpression<Expression::ArrayNewSegId> {
+public:
+ ArrayNewSeg(MixedArena& allocator) {}
+
+ ArrayNewSegOp op;
+ Index segment;
+ Expression* offset;
+ Expression* size;
+
+ void finalize();
+};
+
class ArrayInit : public SpecificExpression<Expression::ArrayInitId> {
public:
ArrayInit(MixedArena& allocator) : values(allocator) {}
diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp
index 8d60a4829..7890c7a42 100644
--- a/src/wasm/literal.cpp
+++ b/src/wasm/literal.cpp
@@ -235,6 +235,57 @@ Literal Literal::makeNegOne(Type type) {
return makeFromInt32(-1, type);
}
+Literal Literal::makeFromMemory(void* p, Type type) {
+ assert(type.isNumber());
+ switch (type.getBasic()) {
+ case Type::i32: {
+ int32_t i;
+ memcpy(&i, p, sizeof(i));
+ return Literal(i);
+ }
+ case Type::i64: {
+ int64_t i;
+ memcpy(&i, p, sizeof(i));
+ return Literal(i);
+ }
+ case Type::f32: {
+ int32_t i;
+ memcpy(&i, p, sizeof(i));
+ return Literal(bit_cast<float>(i));
+ }
+ case Type::f64: {
+ int64_t i;
+ memcpy(&i, p, sizeof(i));
+ return Literal(bit_cast<double>(i));
+ }
+ case Type::v128: {
+ uint8_t bytes[16];
+ memcpy(bytes, p, sizeof(bytes));
+ return Literal(bytes);
+ }
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+}
+
+Literal Literal::makeFromMemory(void* p, const Field& field) {
+ switch (field.packedType) {
+ case Field::not_packed:
+ return makeFromMemory(p, field.type);
+ case Field::i8: {
+ int8_t i;
+ memcpy(&i, p, sizeof(i));
+ return Literal(int32_t(i));
+ }
+ case Field::i16: {
+ int16_t i;
+ memcpy(&i, p, sizeof(i));
+ return Literal(int32_t(i));
+ }
+ }
+ WASM_UNREACHABLE("unexpected type");
+}
+
Literal Literal::standardizeNaN(const Literal& input) {
if (!std::isnan(input.getFloat())) {
return input;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index d023a56c6..e2bb0075b 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3122,12 +3122,15 @@ void WasmBinaryBuilder::readDataSegments() {
}
curr->setName(Name::fromInt(i), false);
curr->isPassive = flags & BinaryConsts::IsPassive;
- Index memIdx = 0;
- if (flags & BinaryConsts::HasIndex) {
- memIdx = getU32LEB();
- }
- memoryRefs[memIdx].push_back(&curr->memory);
- if (!curr->isPassive) {
+ if (curr->isPassive) {
+ curr->memory = Name();
+ curr->offset = nullptr;
+ } else {
+ Index memIdx = 0;
+ if (flags & BinaryConsts::HasIndex) {
+ memIdx = getU32LEB();
+ }
+ memoryRefs[memIdx].push_back(&curr->memory);
curr->offset = readExpression();
}
auto size = getU32LEB();
@@ -3973,6 +3976,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (maybeVisitArrayNew(curr, opcode)) {
break;
}
+ if (maybeVisitArrayNewSeg(curr, opcode)) {
+ break;
+ }
if (maybeVisitArrayInit(curr, opcode)) {
break;
}
@@ -7058,6 +7064,20 @@ bool WasmBinaryBuilder::maybeVisitArrayNew(Expression*& out, uint32_t code) {
return false;
}
+bool WasmBinaryBuilder::maybeVisitArrayNewSeg(Expression*& out, uint32_t code) {
+ if (code == BinaryConsts::ArrayNewData ||
+ code == BinaryConsts::ArrayNewElem) {
+ auto op = code == BinaryConsts::ArrayNewData ? NewData : NewElem;
+ auto heapType = getIndexedHeapType();
+ auto seg = getU32LEB();
+ auto* size = popNonVoidExpression();
+ auto* offset = popNonVoidExpression();
+ out = Builder(wasm).makeArrayNewSeg(op, heapType, seg, offset, size);
+ return true;
+ }
+ return false;
+}
+
bool WasmBinaryBuilder::maybeVisitArrayInit(Expression*& out, uint32_t code) {
if (code == BinaryConsts::ArrayInitStatic) {
auto heapType = getIndexedHeapType();
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index a54226194..a9fd2de77 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2892,6 +2892,15 @@ Expression* SExpressionWasmBuilder::makeArrayNewStatic(Element& s,
return Builder(wasm).makeArrayNew(heapType, size, init);
}
+Expression* SExpressionWasmBuilder::makeArrayNewSeg(Element& s,
+ ArrayNewSegOp op) {
+ auto heapType = parseHeapType(*s[1]);
+ Index seg = parseIndex(*s[2]);
+ Expression* offset = parseExpression(*s[3]);
+ Expression* size = parseExpression(*s[4]);
+ return Builder(wasm).makeArrayNewSeg(op, heapType, seg, offset, size);
+}
+
Expression* SExpressionWasmBuilder::makeArrayInitStatic(Element& s) {
auto heapType = parseHeapType(*s[1]);
size_t i = 2;
@@ -3292,9 +3301,6 @@ void SExpressionWasmBuilder::parseMemory(Element& s, bool preParseImport) {
}
void SExpressionWasmBuilder::parseData(Element& s) {
- if (wasm.memories.empty()) {
- throw ParseException("data but no memory", s.line, s.col);
- }
Index i = 1;
Name name = Name::fromInt(dataCounter++);
bool hasExplicitName = false;
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index f30e4be82..daeac60d6 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2132,6 +2132,22 @@ void BinaryInstWriter::visitArrayNew(ArrayNew* curr) {
parent.writeIndexedHeapType(curr->type.getHeapType());
}
+void BinaryInstWriter::visitArrayNewSeg(ArrayNewSeg* curr) {
+ o << int8_t(BinaryConsts::GCPrefix);
+ switch (curr->op) {
+ case NewData:
+ o << U32LEB(BinaryConsts::ArrayNewData);
+ break;
+ case NewElem:
+ o << U32LEB(BinaryConsts::ArrayNewElem);
+ break;
+ default:
+ WASM_UNREACHABLE("unexpected op");
+ }
+ parent.writeIndexedHeapType(curr->type.getHeapType());
+ o << U32LEB(curr->segment);
+}
+
void BinaryInstWriter::visitArrayInit(ArrayInit* curr) {
o << int8_t(BinaryConsts::GCPrefix);
o << U32LEB(BinaryConsts::ArrayInitStatic);
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index cb3841197..c1316f283 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -451,6 +451,7 @@ public:
void visitStructGet(StructGet* curr);
void visitStructSet(StructSet* curr);
void visitArrayNew(ArrayNew* curr);
+ void visitArrayNewSeg(ArrayNewSeg* curr);
void visitArrayInit(ArrayInit* curr);
void visitArrayGet(ArrayGet* curr);
void visitArraySet(ArraySet* curr);
@@ -2683,6 +2684,73 @@ void FunctionValidator::visitArrayNew(ArrayNew* curr) {
}
}
+void FunctionValidator::visitArrayNewSeg(ArrayNewSeg* curr) {
+ shouldBeTrue(getModule()->features.hasGC(),
+ curr,
+ "array.new_{data, elem} requires gc [--enable-gc]");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->offset->type,
+ Type(Type::i32),
+ curr,
+ "array.new_{data, elem} offset must be an i32");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->size->type,
+ Type(Type::i32),
+ curr,
+ "array.new_{data, elem} size must be an i32");
+ switch (curr->op) {
+ case NewData:
+ if (!shouldBeTrue(curr->segment < getModule()->dataSegments.size(),
+ curr,
+ "array.new_data segment index out of bounds")) {
+ return;
+ }
+ break;
+ case NewElem:
+ if (!shouldBeTrue(curr->segment < getModule()->elementSegments.size(),
+ curr,
+ "array.new_elem segment index out of bounds")) {
+ return;
+ }
+ break;
+ default:
+ WASM_UNREACHABLE("unexpected op");
+ }
+ if (curr->type == Type::unreachable) {
+ return;
+ }
+ if (!shouldBeTrue(
+ curr->type.isRef(),
+ curr,
+ "array.new_{data, elem} type should be an array reference")) {
+ return;
+ }
+ auto heapType = curr->type.getHeapType();
+ if (!shouldBeTrue(
+ heapType.isArray(),
+ curr,
+ "array.new_{data, elem} type shoudl 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()->elementSegments[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::visitArrayInit(ArrayInit* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 6347d83e5..a92b62343 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -1053,7 +1053,12 @@ void ArrayNew::finalize() {
if (size->type == Type::unreachable ||
(init && init->type == Type::unreachable)) {
type = Type::unreachable;
- return;
+ }
+}
+
+void ArrayNewSeg::finalize() {
+ if (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 1c1349e47..7705c9e5c 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -1907,6 +1907,8 @@ template<typename Ctx> Result<typename Ctx::InstrT> makeStructSet(Ctx&, Index);
template<typename Ctx>
Result<typename Ctx::InstrT> makeArrayNewStatic(Ctx&, Index, bool default_);
template<typename Ctx>
+Result<typename Ctx::InstrT> makeArrayNewSeg(Ctx&, Index, ArrayNewSegOp op);
+template<typename Ctx>
Result<typename Ctx::InstrT> makeArrayInitStatic(Ctx&, Index);
template<typename Ctx>
Result<typename Ctx::InstrT> makeArrayGet(Ctx&, Index, bool signed_ = false);
@@ -2892,6 +2894,12 @@ makeArrayNewStatic(Ctx& ctx, Index pos, bool default_) {
}
template<typename Ctx>
+Result<typename Ctx::InstrT>
+makeArrayNewSeg(Ctx& ctx, Index pos, ArrayNewSegOp op) {
+ return ctx.in.err("unimplemented instruction");
+}
+
+template<typename Ctx>
Result<typename Ctx::InstrT> makeArrayInitStatic(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 2ae22de09..982184510 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -2304,6 +2304,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitArrayNewSeg(ArrayNewSeg* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitArrayInit(ArrayInit* curr) {
unimplemented(curr);
WASM_UNREACHABLE("unimp");
diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt
index 89c49f2d5..9200bac2a 100644
--- a/test/binaryen.js/kitchen-sink.js.txt
+++ b/test/binaryen.js/kitchen-sink.js.txt
@@ -97,25 +97,25 @@ StructNewId: 60
StructGetId: 61
StructSetId: 62
ArrayNewId: 63
-ArrayInitId: 64
-ArrayGetId: 65
-ArraySetId: 66
-ArrayLenId: 67
-ArrayCopy: 68
-RefAs: 69
-StringNew: 70
-StringConst: 71
-StringMeasure: 72
-StringEncode: 73
-StringConcat: 74
-StringEq: 75
-StringAs: 76
-StringWTF8Advance: 77
-StringWTF16Get: 78
-StringIterNext: 79
-StringIterMove: 80
-StringSliceWTF: 81
-StringSliceIter: 82
+ArrayInitId: 65
+ArrayGetId: 66
+ArraySetId: 67
+ArrayLenId: 68
+ArrayCopy: 69
+RefAs: 70
+StringNew: 71
+StringConst: 72
+StringMeasure: 73
+StringEncode: 74
+StringConcat: 75
+StringEq: 76
+StringAs: 77
+StringWTF8Advance: 78
+StringWTF16Get: 79
+StringIterNext: 80
+StringIterMove: 81
+StringSliceWTF: 82
+StringSliceIter: 83
getExpressionInfo={"id":15,"type":4,"op":6}
(f32.neg
(f32.const -33.61199951171875)
diff --git a/test/lit/arrays.wast b/test/lit/arrays.wast
index 31aead1cd..4a262ef13 100644
--- a/test/lit/arrays.wast
+++ b/test/lit/arrays.wast
@@ -10,24 +10,46 @@
;; RUN: wasm-opt %s -all -S -o - | wasm-opt -all -S -o - | filecheck %s
(module
- (type $byte-array (array (mut i8)))
;; CHECK: (type $arrayref_=>_i32 (func (param arrayref) (result i32)))
;; CHECK: (type $ref|array|_=>_i32 (func (param (ref array)) (result i32)))
;; CHECK: (type $nullref_=>_i32 (func (param nullref) (result i32)))
- ;; CHECK: (func $len (param $a (ref array)) (result i32)
- ;; CHECK-NEXT: (array.len
- ;; CHECK-NEXT: (local.get $a)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
+ ;; CHECK: (type $none_=>_ref|$byte-array| (func (result (ref $byte-array))))
+
+ ;; CHECK: (type $none_=>_ref|$func-array| (func (result (ref $func-array))))
+
+ ;; CHECK: (type $byte-array (array (mut i8)))
;; ROUNDTRIP: (type $arrayref_=>_i32 (func (param arrayref) (result i32)))
;; ROUNDTRIP: (type $ref|array|_=>_i32 (func (param (ref array)) (result i32)))
;; ROUNDTRIP: (type $nullref_=>_i32 (func (param nullref) (result i32)))
+ ;; ROUNDTRIP: (type $none_=>_ref|$byte-array| (func (result (ref $byte-array))))
+
+ ;; ROUNDTRIP: (type $none_=>_ref|$func-array| (func (result (ref $func-array))))
+
+ ;; ROUNDTRIP: (type $byte-array (array (mut i8)))
+ (type $byte-array (array (mut i8)))
+ ;; CHECK: (type $func-array (array (mut funcref)))
+ ;; ROUNDTRIP: (type $func-array (array (mut funcref)))
+ (type $func-array (array (mut funcref)))
+
+ ;; CHECK: (data "hello")
+ ;; ROUNDTRIP: (data "hello")
+ (data "hello")
+ ;; CHECK: (elem func $len $impossible-len $unreachable-len)
+ ;; ROUNDTRIP: (elem func $len $impossible-len $unreachable-len)
+ (elem func $len $impossible-len $unreachable-len)
+
+
+ ;; CHECK: (func $len (param $a (ref array)) (result i32)
+ ;; CHECK-NEXT: (array.len
+ ;; CHECK-NEXT: (local.get $a)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
;; ROUNDTRIP: (func $len (param $a (ref array)) (result i32)
;; ROUNDTRIP-NEXT: (array.len
;; ROUNDTRIP-NEXT: (local.get $a)
@@ -85,4 +107,42 @@
(local.get $a)
)
)
+
+ ;; CHECK: (func $new-data (result (ref $byte-array))
+ ;; CHECK-NEXT: (array.new_data $byte-array 0
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 5)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; ROUNDTRIP: (func $new-data (result (ref $byte-array))
+ ;; ROUNDTRIP-NEXT: (array.new_data $byte-array 0
+ ;; ROUNDTRIP-NEXT: (i32.const 0)
+ ;; ROUNDTRIP-NEXT: (i32.const 5)
+ ;; ROUNDTRIP-NEXT: )
+ ;; ROUNDTRIP-NEXT: )
+ (func $new-data (result (ref $byte-array))
+ (array.new_data $byte-array 0
+ (i32.const 0)
+ (i32.const 5)
+ )
+ )
+
+ ;; CHECK: (func $new-elem (result (ref $func-array))
+ ;; CHECK-NEXT: (array.new_elem $func-array 0
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 3)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; ROUNDTRIP: (func $new-elem (result (ref $func-array))
+ ;; ROUNDTRIP-NEXT: (array.new_elem $func-array 0
+ ;; ROUNDTRIP-NEXT: (i32.const 0)
+ ;; ROUNDTRIP-NEXT: (i32.const 3)
+ ;; ROUNDTRIP-NEXT: )
+ ;; ROUNDTRIP-NEXT: )
+ (func $new-elem (result (ref $func-array))
+ (array.new_elem $func-array 0
+ (i32.const 0)
+ (i32.const 3)
+ )
+ )
)
diff --git a/test/spec/array-new-data.wast b/test/spec/array-new-data.wast
new file mode 100644
index 000000000..8ef62cb87
--- /dev/null
+++ b/test/spec/array-new-data.wast
@@ -0,0 +1,79 @@
+(module
+ (type $vec (array i8))
+ (type $mvec (array (mut i8)))
+
+ (data "\00\01\02\03\04")
+
+ (func $new (export "new") (result (ref $vec))
+ (array.new_data $vec 0 (i32.const 1) (i32.const 3))
+ )
+
+ (func $get (param $i i32) (param $v (ref $vec)) (result i32)
+ (array.get_u $vec (local.get $v) (local.get $i))
+ )
+ (func (export "get") (param $i i32) (result i32)
+ (call $get (local.get $i) (call $new))
+ )
+
+ (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y i32) (result i32)
+ (array.set $mvec (local.get $v) (local.get $i) (local.get $y))
+ (array.get_u $mvec (local.get $v) (local.get $i))
+ )
+ (func (export "set_get") (param $i i32) (param $y i32) (result i32)
+ (call $set_get (local.get $i)
+ (array.new_data $mvec 0 (i32.const 1) (i32.const 3))
+ (local.get $y)
+ )
+ )
+
+ (func $len (param $v (ref array)) (result i32)
+ (array.len (local.get $v))
+ )
+ (func (export "len") (result i32)
+ (call $len (call $new))
+ )
+)
+
+(assert_return (invoke "get" (i32.const 0)) (i32.const 1))
+(assert_return (invoke "set_get" (i32.const 1) (i32.const 7)) (i32.const 7))
+(assert_return (invoke "len") (i32.const 3))
+
+(module
+ (type $vec (array i32))
+ (type $mvec (array (mut i8)))
+
+ (data "\00\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00")
+
+ (func $new (export "new") (result (ref $vec))
+ (array.new_data $vec 0 (i32.const 1) (i32.const 3))
+ )
+
+ (func $get (param $i i32) (param $v (ref $vec)) (result i32)
+ (array.get $vec (local.get $v) (local.get $i))
+ )
+ (func (export "get") (param $i i32) (result i32)
+ (call $get (local.get $i) (call $new))
+ )
+
+ (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y i32) (result i32)
+ (array.set $mvec (local.get $v) (local.get $i) (local.get $y))
+ (array.get $mvec (local.get $v) (local.get $i))
+ )
+ (func (export "set_get") (param $i i32) (param $y i32) (result i32)
+ (call $set_get (local.get $i)
+ (array.new_data $mvec 0 (i32.const 1) (i32.const 3))
+ (local.get $y)
+ )
+ )
+
+ (func $len (param $v (ref array)) (result i32)
+ (array.len (local.get $v))
+ )
+ (func (export "len") (result i32)
+ (call $len (call $new))
+ )
+)
+
+(assert_return (invoke "get" (i32.const 0)) (i32.const 1))
+(assert_return (invoke "set_get" (i32.const 1) (i32.const 7)) (i32.const 7))
+(assert_return (invoke "len") (i32.const 3))
diff --git a/test/spec/array-new-elem.wast b/test/spec/array-new-elem.wast
new file mode 100644
index 000000000..a61122bd6
--- /dev/null
+++ b/test/spec/array-new-elem.wast
@@ -0,0 +1,48 @@
+(module
+ (type $vec (array funcref))
+ (type $mvec (array (mut funcref)))
+ (type $f (func (result i32)))
+
+ (elem func $ret0 $ret1 $ret2 $ret3 $ret4)
+
+ (func $ret0 (type $f) (i32.const 0))
+ (func $ret1 (type $f) (i32.const 1))
+ (func $ret2 (type $f) (i32.const 2))
+ (func $ret3 (type $f) (i32.const 3))
+ (func $ret4 (type $f) (i32.const 4))
+
+ (func $new (export "new") (result (ref $vec))
+ (array.new_elem $vec 0 (i32.const 1) (i32.const 3))
+ )
+
+ (func $get (param $i i32) (param $v (ref $vec)) (result i32)
+ (call_ref $f (ref.cast_static $f (array.get $vec (local.get $v) (local.get $i))))
+ )
+ (func (export "get") (param $i i32) (result i32)
+ (call $get (local.get $i) (call $new))
+ )
+
+ (func $set_get (param $i i32) (param $v (ref $mvec)) (param $y i32) (result i32)
+ (array.set $mvec (local.get $v) (local.get $i) (array.get $mvec (local.get $v) (local.get $y)))
+ (call_ref $f (ref.cast_static $f (array.get $mvec (local.get $v) (local.get $i))))
+ )
+ (func (export "set_get") (param $i i32) (param $y i32) (result i32)
+ (call $set_get
+ (local.get $i)
+ (array.new_elem $mvec 0 (i32.const 1) (i32.const 3))
+ (local.get $y)
+ )
+ )
+
+ (func $len (param $v (ref array)) (result i32)
+ (array.len (local.get $v))
+ )
+ (func (export "len") (result i32)
+ (call $len (call $new))
+ )
+)
+
+(assert_return (invoke "get" (i32.const 0)) (i32.const 1))
+(assert_return (invoke "get" (i32.const 1)) (i32.const 2))
+(assert_return (invoke "set_get" (i32.const 0) (i32.const 2)) (i32.const 3))
+(assert_return (invoke "len") (i32.const 3))