-
Notifications
You must be signed in to change notification settings - Fork 787
[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
Conversation
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! |
d1cabad
to
e8206b1
Compare
} | ||
for (auto& curr : wasm.elementSegments) { | ||
counts.maybeNote(curr->type); | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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!
This is now ready for review, I've rebased it on top of the current main after #3753 was merged. |
There was a problem hiding this 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); |
There was a problem hiding this comment.
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
)?
There was a problem hiding this comment.
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.
table->initial = 10; | ||
table->max = 20; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
;; (module | ||
;; (import "spectest" "table" (table 0 funcref)) | ||
;; (func $f) | ||
;; (elem (i32.const 0) $f) | ||
;; ) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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.
src/wasm/wasm-validator.cpp
Outdated
info.shouldBeTrue(segment->type != Type::externref, | ||
"elem", | ||
"element segment type cannot be externref"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is surprising!
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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.
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!) |
@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.
@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 |
Excellent, thanks @martianboy! |
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.