Skip to content

Commit

Permalink
Support more reference constants in wast scripts (WebAssembly#6865)
Browse files Browse the repository at this point in the history
Spec tests use constants like `ref.array` and `ref.eq` to assert that
exported function return references of the correct types. Support more
such constants in the wast parser.

Also fix a bug where the interpretation of `array.new_data` for arrays
of packed fields was not properly truncating the packed data. Move the
function for reading fields from memory from literal.cpp to
wasm-interpreter.h, where the function for truncating packed data lives.

Other bugs prevent us from enabling any more spec tests as a result of
this change, but we can get farther through several of them before
failing. Update the comments about the failures accordingly.
  • Loading branch information
tlively authored Aug 26, 2024
1 parent 95a280f commit dacc6e5
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 25 deletions.
6 changes: 3 additions & 3 deletions scripts/test/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,16 +452,16 @@ def get_tests(test_dir, extensions=[], recursive=False):
'table.wast', # Requires support for table default elements
'type-equivalence.wast', # Recursive types allowed by GC
'unreached-invalid.wast', # Requires more precise unreachable validation
'array.wast', # Requires ref.array wast constants
'array.wast', # Requires support for table default elements
'array_init_elem.wast', # Requires support for elem.drop
'br_if.wast', # Requires more precise branch validation
'br_on_cast.wast', # Requires sending values on br_on_cast
'br_on_cast_fail.wast', # Requires sending values on br_on_cast_fail
'extern.wast', # Requires ref.host wast constants
'i31.wast', # Requires ref.i31 wast constants
'i31.wast', # Requires support for table default elements
'ref_cast.wast', # Requires host references to not be externalized i31refs
'ref_test.wast', # Requires host references to not be externalized i31refs
'struct.wast', # Requires ref.struct wast constants
'struct.wast', # Duplicate field names not properly rejected
'type-rec.wast', # Requires wast `register` support
'type-subtyping.wast', # ShellExternalInterface::callTable does not handle subtyping
'call_indirect.wast', # Bug with 64-bit inline element segment parsing
Expand Down
1 change: 0 additions & 1 deletion src/literal.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ class Literal {
}

static Literal makeFromMemory(void* p, Type type);
static Literal makeFromMemory(void* p, const Field& field);

static Literal makeSignedMin(Type type) {
switch (type.getBasic()) {
Expand Down
28 changes: 28 additions & 0 deletions src/parser/wast-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,34 @@ Result<ExpectedResult> result(Lexer& in) {
return RefResult{HeapType::func};
}

if (in.takeSExprStart("ref.struct")) {
if (!in.takeRParen()) {
return in.err("expected end of ref.struct");
}
return RefResult{HeapType::struct_};
}

if (in.takeSExprStart("ref.array")) {
if (!in.takeRParen()) {
return in.err("expected end of ref.array");
}
return RefResult{HeapType::array};
}

if (in.takeSExprStart("ref.eq")) {
if (!in.takeRParen()) {
return in.err("expected end of ref.eq");
}
return RefResult{HeapType::eq};
}

if (in.takeSExprStart("ref.i31")) {
if (!in.takeRParen()) {
return in.err("expected end of ref.i31");
}
return RefResult{HeapType::i31};
}

if (in.takeSExprStart("ref.i31_shared")) {
if (!in.takeRParen()) {
return in.err("expected end of ref.i31_shared");
Expand Down
24 changes: 21 additions & 3 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2196,7 +2196,7 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
WASM_UNREACHABLE("unimp");
}

private:
protected:
// Truncate the value if we need to. The storage is just a list of Literals,
// so we can't just write the value like we would to a C struct field and
// expect it to truncate for us. Instead, we truncate so the stored value is
Expand Down Expand Up @@ -2231,6 +2231,24 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
}
return value;
}

Literal makeFromMemory(void* p, Field field) {
switch (field.packedType) {
case Field::not_packed:
return Literal::makeFromMemory(p, field.type);
case Field::i8: {
int8_t i;
memcpy(&i, p, sizeof(i));
return truncateForPacking(Literal(int32_t(i)), field);
}
case Field::i16: {
int16_t i;
memcpy(&i, p, sizeof(i));
return truncateForPacking(Literal(int32_t(i)), field);
}
}
WASM_UNREACHABLE("unexpected type");
}
};

// Execute a suspected constant expression (precompute and C-API).
Expand Down Expand Up @@ -3972,7 +3990,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
contents.reserve(size);
for (Index i = offset; i < end; i += elemBytes) {
auto addr = (void*)&seg.data[i];
contents.push_back(Literal::makeFromMemory(addr, element));
contents.push_back(this->makeFromMemory(addr, element));
}
return self()->makeGCData(contents, curr->type);
}
Expand Down Expand Up @@ -4052,7 +4070,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
}
for (size_t i = 0; i < sizeVal; i++) {
void* addr = (void*)&seg->data[offsetVal + i * elemSize];
data->values[indexVal + i] = Literal::makeFromMemory(addr, elem);
data->values[indexVal + i] = this->makeFromMemory(addr, elem);
}
return {};
}
Expand Down
18 changes: 0 additions & 18 deletions src/wasm/literal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,24 +294,6 @@ Literal Literal::makeFromMemory(void* p, Type 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;
Expand Down

0 comments on commit dacc6e5

Please sign in to comment.