Skip to content

[RT] Add type to tables and element segments #3763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 6, 2021

Conversation

martianboy
Copy link
Contributor

This will add support for other reference types to tables and elems,
which was introduced in reference-types proposal and extended in typed
function references proposal. Non-nullable types are not supported yet,
since the spec only mentions the idea of using an initializer, but
it has remained a TODO item to this date.
Spec tests have also been partially updated to reflect the new changes
from upstream.

@martianboy
Copy link
Contributor Author

This includes and is based on the changes from #3753, so I marked this a draft until that one is merged.

@tlively @kripken @aheejin It's possible that I have some misunderstandings regarding the spec, specially when it comes to the new types introduced in GC proposal. Please take a look and let me know if something is incorrect. Thanks!

@martianboy martianboy force-pushed the table-types branch 2 times, most recently from d1cabad to e8206b1 Compare March 31, 2021 20:31
@martianboy martianboy marked this pull request as ready for review March 31, 2021 20:49
}
for (auto& curr : wasm.elementSegments) {
counts.maybeNote(curr->type);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tlively is this enough for making sure table & elem types are counted correctly everywhere, or have I missed something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me!

@martianboy
Copy link
Contributor Author

This is now ready for review, I've rebased it on top of the current main after #3753 was merged.

Copy link
Member

@tlively tlively left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! I only have a few questions :)

} else {
TypeNamePrinter(o, currModule).print(Type::funcref);
printType(o, curr->type, currModule);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this supposed to print the value type (e.g. anyref) or the heap type (e.g. any)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a value type. My understanding is that the only heap type allowed here is func which is used in the abbreviated form that doesn't use expressions. In all other cases, it should be a value type and items should be expressions.

Comment on lines +149 to +150
table->initial = 10;
table->max = 20;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these numbers come from?

Copy link
Contributor Author

@martianboy martianboy Apr 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the reference interpreter! This is an old mechanism for to link a few default global when running spec tests. More recent tests have (register ... instruction for this purpose, although we don't support it yet in wasm-shell.

I was trying to get more spec tests to pass, so I decided to dig into the differences between wasm-shell and the reference interpreter. Looks like table initialization from element segments is fully handled at module instantiation, including the bounds check. We check bounds at validation, which still makes this change useless. There are test cases like:

(module
  (import "spectest" "table" (table 0 funcref))
  (func $f)
  (elem (i32.const 0) $f)
)

that are a validation error in wasm-shell but pass in the reference interpreter, because the former (incorrectly) takes the table's initial to be 0, whereas the latter imports the table instance and sees that it has an initial of 10.

Again, to be clear, these difference haven't been fully addressed in this PR, so I may remove these from this PR as well if you think that's cleaner.

if (elemType != BinaryConsts::EncodedType::funcref) {
throwError("Non-funcref tables not yet supported");
auto elemType = getType();
if (!elemType.isRef()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this handle MVP tables correctly, too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In MVP all tables should be funcref, right? We defer checking features until validation.

@@ -3205,7 +3205,7 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) {
table->module = inner[1]->str();
table->base = inner[2]->str();
i++;
} else {
} else if (!elementStartsWith(inner, REF)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to ensure that tables have reference types? It seems like this wouldn't support abbreviations like funcref or anyref. I assume that those should be supported?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we are here after we saw s[i] to be a list. In that case, we still don't want to parse the type yet, just making sure it passes over the parse exception.

Comment on lines +183 to +189
;; (module
;; (import "spectest" "table" (table 0 funcref))
;; (func $f)
;; (elem (i32.const 0) $f)
;; )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these tests commented out? It would be good to add a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explained the reasons above, but sure I'll add a comment. Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment explaining the reason.

Comment on lines 2878 to 2880
info.shouldBeTrue(segment->type != Type::externref,
"elem",
"element segment type cannot be externref");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is surprising!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is only my understanding of the spec but haven't seen the spec text mention it anywhere, nor any spec tests that expects this to be invalid. But, here's my reasoning:

Element segments are only used to initialize tables and cannot be imported from outside. Given the fact that they cannot include function call expressions, there's no way for them to hold externref values. The only way for a wasm module to change items on an externref table is via the table instructions like table.set and table.copy that are allowed at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now changed to only allow function types for element segments. I added a comment block explaining the reason.

This will add support for other reference types to tables and elems.
@martianboy martianboy requested review from tlively and kripken April 2, 2021 19:45
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! No more comments from me.

@tlively
Copy link
Member

tlively commented Apr 5, 2021

I fuzzed this a bit and it looks like the fuzzer is producing invalid modules for seed 7344750093000028152. Can you reproduce that? (Besides fuzzing this LGTM, too!)

@martianboy
Copy link
Contributor Author

@tlively thanks I will look into it!

modules with more specific function type segments weren't handled
correctly. We now ensure that a funcref element segments and and table
exist in `setupTables`. Also when adding functions to a segment, first
we find one that has a compatible type.
@martianboy
Copy link
Contributor Author

@tlively Thanks for the catch! The fuzzer wasn't correctly handling modules that only have element segments with more specific function types. I fixed it by making sure at least one funcref element segment and table exist. See my last commit. On my local machine, the fuzzer can now run a few hundred iterations without a problem.

@tlively
Copy link
Member

tlively commented Apr 6, 2021

Excellent, thanks @martianboy!

@tlively tlively merged commit e9fcf44 into WebAssembly:main Apr 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants