summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2023-03-16 11:45:40 -0500
committerGitHub <noreply@github.com>2023-03-16 09:45:40 -0700
commit2b326379e7deac4b37e0db5f064d06f7daf46aac (patch)
tree7df9d7608f5595cc2170d3620b426c6041756eeb
parent9a744b2b373eabbe3b64deaa6cddc09cde5e8fca (diff)
downloadbinaryen-2b326379e7deac4b37e0db5f064d06f7daf46aac.tar.gz
binaryen-2b326379e7deac4b37e0db5f064d06f7daf46aac.tar.bz2
binaryen-2b326379e7deac4b37e0db5f064d06f7daf46aac.zip
Add bulk-array.wast spec test outline (#5568)
Add spec/bulk-array.wast, which contains an outline of the tests that will be necessary for the upcoming bulk array instructions: array.copy (already implemented), array.fill, array.init_data, and array.init_elem. Although the test file does not actually contain any tests yet, it contains some setup code defining types, globals, and element segments that the tests will use. Fix miscellaneous bugs in parsing, validation, and printing to allow this setup code to run without issues.
-rw-r--r--src/passes/Print.cpp2
-rw-r--r--src/wasm/wasm-binary.cpp3
-rw-r--r--src/wasm/wasm-s-parser.cpp12
-rw-r--r--src/wasm/wasm-validator.cpp29
-rw-r--r--test/spec/bulk-array.wast225
5 files changed, 238 insertions, 33 deletions
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 4a4fe60c7..c9a717058 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -3283,7 +3283,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
} else {
for (auto* entry : curr->data) {
o << ' ';
- printExpression(entry, o);
+ visit(entry);
}
}
o << ')' << maybeNewLine;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 518a809fa..9368325b0 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -3222,9 +3222,6 @@ void WasmBinaryBuilder::readElementSegments() {
if (isPassive || hasTableIdx) {
if (usesExpressions) {
segment->type = getType();
- if (!segment->type.isFunction()) {
- throwError("Invalid type for a usesExpressions element segment");
- }
} else {
auto elemKind = getU32LEB();
if (elemKind != 0x0) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 522c3f3c6..77e265f16 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -3561,7 +3561,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
std::unique_ptr<Global> global = make_unique<Global>();
size_t i = 1;
- if (s[i]->dollared() && !(s[i]->isStr() && isType(s[i]->str()))) {
+ if (s[i]->dollared()) {
global->setExplicitName(s[i++]->str());
} else if (preParseImport) {
global->name = Name("gimport$" + std::to_string(globalCounter));
@@ -3572,7 +3572,6 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
globalNames.push_back(global->name);
bool mutable_ = false;
Type type = Type::none;
- bool exported = false;
Name importModule, importBase;
while (i < s.size() && s[i]->isList()) {
auto& inner = *s[i++];
@@ -3585,7 +3584,6 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
throw ParseException("duplicate export", s.line, s.col);
}
wasm.addExport(ex.release());
- exported = true;
} else if (elementStartsWith(inner, IMPORT)) {
importModule = inner[1]->str();
importBase = inner[2]->str();
@@ -3598,9 +3596,6 @@ void SExpressionWasmBuilder::parseGlobal(Element& s, bool preParseImport) {
break;
}
}
- if (exported && mutable_) {
- throw ParseException("cannot export a mutable global", s.line, s.col);
- }
if (type == Type::none) {
type = stringToType(s[i++]->str());
}
@@ -3784,11 +3779,6 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) {
segment->type = elementToType(*s[i]);
usesExpressions = true;
i += 1;
-
- if (!segment->type.isFunction()) {
- throw ParseException(
- "Invalid type for an element segment.", s.line, s.col);
- }
}
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index e0391a73d..123a15c1e 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -3405,13 +3405,9 @@ static void validateTables(Module& module, ValidationInfo& info) {
}
for (auto& segment : module.elementSegments) {
- // Since element segment items need to be constant expressions, that leaves
- // us with ref.null, ref.func and global.get. As a result, the only possible
- // type for element segments will be function references.
- // TODO: This is not true! Allow GC data here (#4846).
- info.shouldBeTrue(segment->type.isFunction(),
+ info.shouldBeTrue(segment->type.isRef(),
"elem",
- "element segment type must be of function type.");
+ "element segment type must be of reference type.");
info.shouldBeTrue(
segment->type.isNullable(),
"elem",
@@ -3443,18 +3439,15 @@ static void validateTables(Module& module, ValidationInfo& info) {
"elem",
"non-table segment offset should have no offset");
}
- // Avoid double checking items
- if (module.features.hasReferenceTypes()) {
- for (auto* expr : segment->data) {
- info.shouldBeTrue(Properties::isValidConstantExpression(module, expr),
- expr,
- "element must be a constant expression");
- info.shouldBeSubType(expr->type,
- segment->type,
- expr,
- "element must be a subtype of the segment type");
- validator.validate(expr);
- }
+ for (auto* expr : segment->data) {
+ info.shouldBeTrue(Properties::isValidConstantExpression(module, expr),
+ expr,
+ "element must be a constant expression");
+ info.shouldBeSubType(expr->type,
+ segment->type,
+ expr,
+ "element must be a subtype of the segment type");
+ validator.validate(expr);
}
}
}
diff --git a/test/spec/bulk-array.wast b/test/spec/bulk-array.wast
new file mode 100644
index 000000000..06c341000
--- /dev/null
+++ b/test/spec/bulk-array.wast
@@ -0,0 +1,225 @@
+(module
+ ;; Array types used in tests.
+ (type $i8 (array (mut i8)))
+ (type $i16 (array (mut i16)))
+ (type $i32 (array (mut i32)))
+ (type $anyref (array (mut anyref)))
+ (type $funcref (array (mut funcref)))
+ (type $externref (array (mut externref)))
+
+ ;; Array values used in tests. Reset in between tests with the "reset"
+ ;; function.
+ (global $i8 (mut (ref null $i8)) (ref.null none))
+ (global $i16 (mut (ref null $i16)) (ref.null none))
+ (global $i32 (mut (ref null $i32)) (ref.null none))
+ (global $anyref (mut (ref null $anyref)) (ref.null none))
+ (global $funcref (mut (ref null $funcref)) (ref.null none))
+ (global $externref (mut (ref null $externref)) (ref.null none))
+
+ ;; GC objects with distinct identities used in anyref tests.
+ (global $g1 (export "g1") (mut anyref) (array.new_fixed $i8))
+ (global $g2 (export "g2") (mut anyref) (array.new_fixed $i8))
+ (global $g3 (export "g3") (mut anyref) (array.new_fixed $i8))
+ (global $g4 (export "g4") (mut anyref) (array.new_fixed $i8))
+ (global $g5 (export "g5") (mut anyref) (array.new_fixed $i8))
+
+ ;; Functions with distinct return values used in funcref tests.
+ (func $f1 (result i32) (i32.const 0))
+ (func $f2 (result i32) (i32.const 1))
+ (func $f3 (result i32) (i32.const 2))
+ (func $f4 (result i32) (i32.const 3))
+ (func $f5 (result i32) (i32.const 4))
+
+ ;; Passive element segment used in array.init_elem tests.
+ (elem $elem anyref
+ (array.new_fixed $i8)
+ (array.new_fixed $i8)
+ (array.new_fixed $i8)
+ (array.new_fixed $i8)
+ (array.new_fixed $i8))
+
+ (table $tab anyref 5 5)
+
+ ;; Resets the array globals to known states.
+ (func (export "reset")
+ (global.set $i8
+ (array.new_fixed $i8
+ (i32.const 0)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 3)
+ (i32.const 4)))
+ (global.set $i16
+ (array.new_fixed $i16
+ (i32.const 0)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 3)
+ (i32.const 4)))
+ (global.set $i32
+ (array.new_fixed $i32
+ (i32.const 0)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 3)
+ (i32.const 4)))
+ (global.set $anyref
+ (array.new_fixed $anyref
+ (global.get $g1)
+ (global.get $g2)
+ (global.get $g3)
+ (global.get $g4)
+ (global.get $g5)))
+ (global.set $funcref
+ (array.new_fixed $funcref
+ (ref.func $f1)
+ (ref.func $f2)
+ (ref.func $f3)
+ (ref.func $f4)
+ (ref.func $f5)))
+ (global.set $externref
+ (array.new_fixed $externref
+ (extern.externalize (global.get $g1))
+ (extern.externalize (global.get $g2))
+ (extern.externalize (global.get $g3))
+ (extern.externalize (global.get $g4))
+ (extern.externalize (global.get $g5)))))
+)
+
+;; array.fill
+
+;; basic i8
+;; basic i16
+;; basic i32
+;; basic anyref
+;; basic funcref
+;; basic externref
+;; basic ref subtype
+;; basic ref nullability subtype
+
+;; zero size in bounds
+;; zero size at bounds
+;; zero size out of bounds traps
+
+;; out of bounds index traps
+;; out of bounds size traps
+;; out of bounds index + size traps
+
+;; null destination traps
+
+;; immutable field invalid
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; array.copy
+
+;; basic i8
+;; basic i16
+;; basic i32
+;; basic anyref
+;; basic funcref
+;; basic externref
+;; basic ref subtype
+;; basic ref nullability subtype
+
+;; same i8 no overlap
+;; same i8 overlap src first
+;; same i8 overlap dest first
+;; same i8 overlap complete
+
+;; same i32 no overlap
+;; same i32 overlap src first
+;; same i32 overlap dest first
+;; same i32 overlap complete
+
+;; same anyref no overlap
+;; same anyref overloap
+;; same anyref src first
+;; same anyref dest first
+;; same anyref overlap complete
+
+;; zero size in bounds
+;; zero size at dest bounds
+;; zero size at src bounds
+;; zero size out of dest bounds traps
+;; zero size out of src bounds traps
+
+;; out of bounds dest index traps
+;; out of bounds src index traps
+;; out of bounds dest size traps
+;; out of bounds src index traps
+;; out of bounds dest index + size traps
+;; out of bounds src index + size traps
+
+;; null dest traps
+;; null src traps
+
+;; immutable dest field invalid
+;; immutable src field ok
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; array.init_data
+
+;; basic i8
+;; basic i16
+;; basic i32
+;; basic f32
+
+;; zero size in bounds
+;; zero size at dest bounds
+;; zero size at src bounds
+;; zero size out of dest bounds traps
+;; zero size out of src bounds traps
+
+;; out of bounds dest index traps
+;; out of bounds src index traps
+;; out of bounds dest size traps
+;; out of bounds src size traps
+;; out of bounds src multiplied size traps
+;; out of bounds dest index + size traps
+;; out of bounds src index + size traps
+;; out of bounds src index + multiplied size traps
+
+;; null dest traps
+;; segment dropped traps
+
+;; immutable dest field invalid
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; out of bounds segment index invalid
+
+;; array.init_elem
+
+;; basic anyref
+;; basic funcref
+;; basic externref
+;; basic ref subtype
+;; basic ref nullability subtype
+
+;; zero size in bounds
+;; zero size at dest bounds
+;; zero size at src bounds
+;; zero size out of dest bounds traps
+;; zero size out of src bounds traps
+
+;; out of bounds dest index traps
+;; out of bounds src index traps
+;; out of bounds dest size traps
+;; out of bounds src size traps
+;; out of bounds dest index + size traps
+;; out of bounds src index + size traps
+
+;; null dest traps
+;; segment dropped traps
+
+;; immutable dest field invalid
+
+;; ref supertype invalid
+;; ref nullability supertype invalid
+
+;; out of bounds segment index invalid