summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2022-11-07 12:45:01 -0800
committerGitHub <noreply@github.com>2022-11-07 12:45:01 -0800
commitb48e4de5ea13434ded315b2bc99a713db0361f63 (patch)
treea1c24b6f79c624f9affb538e1daf18e67d386743 /src/wasm
parent52079ae8ea3abec509c3184720d1824b9093cb2f (diff)
downloadbinaryen-b48e4de5ea13434ded315b2bc99a713db0361f63.tar.gz
binaryen-b48e4de5ea13434ded315b2bc99a713db0361f63.tar.bz2
binaryen-b48e4de5ea13434ded315b2bc99a713db0361f63.zip
Implement `array.new_data` and `array.new_elem` (#5214)
In order to test them, fix the binary and text parsers to accept passive data segments even if a module has no memory. In addition to parsing and emitting the new instructions, also implement their validation and interpretation. Test the interpretation directly with wasm-shell tests adapted from the upstream spec tests. Running the upstream spec tests directly would require fixing too many bugs in the legacy text parser, so it will have to wait for the new text parser to be ready.
Diffstat (limited to 'src/wasm')
-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
7 files changed, 184 insertions, 10 deletions
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");
}