-
Notifications
You must be signed in to change notification settings - Fork 757
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
Add anyref feature and type #3109
Conversation
src/wasm/wasm-type.cpp
Outdated
// FIXME: `anyref` is only valid here if the `anyref` feature is enabled, | ||
// but this information is not available within `Type` alone. | ||
return b.isRef() ? Type::anyref : Type::none; |
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.
Fixing this looks like it requires changing the signature of this function to getLeastUpperBound(Type a, Type b, FeatureSet features)
, with call sites aware of enabled features passing these in.
Only works this way currently because we are not generating, for example, an (if $condition $funcref $externref)
where anyref is not enabled, but an API user can do so yielding an unexpected (otherwise not enabled) anyref here. Do you prefer to tackle this issue in this PR?
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.
Unless I'm missing some use of this function, I believe it will only incorrectly return anyref
for invalid code. As long as validation will separately catch that invalidity, I'm tempted to not do anything with this issue besides document it. WDYT?
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.
Ah, it looks like this PR doesn't make any changes to validation yet. It would be good to fix that.
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.
Have a feeling that this will resurface eventually, but since the fuzzer isn't complaining I guess it is fine for now. Is the comment ok, or would you prefer a little more detail?
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.
Oh, going to check!
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.
On a first glimpse it looks like the validator uses Type::isSubType
for its checks. This then leads to a similar issue like this one for getLeastUpperBound
, but for isSubType
not knowing the enabled features I guess, where the check in isSubType
is right == Type::anyref
which can only happen in valid code if anyref
is enabled.
Can you point me into the right direction what else needs to be updated in the validator?
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.
On the other hand, there will be a(nother) validation error anyway if anyref
is used but not enabled, via Type::getFeatures()
.
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.
If this ends up generating anyref
in other programs where anyref
isn't enabled, eventually it will be caught by the validator, and that basically means one of Binaryen passes have a bug. So I think it's OK to keep this simple and not include types for now, but erroring out here might help Binaryen authors debug their code a little, or something. Anyway I think this should be OK for now.
I think isSubType
will be even more OK, because it doesn't create anyref
when it isn't enabled.
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.
On the other hand, there will be a(nother) validation error anyway if
anyref
is used but not enabled, viaType::getFeatures()
.
Aha, I had forgotten that this wouldn't require any changes in the validator itself. I agree with @aheejin that this should be fine, then. We can probably replace the FIXME
with a comment pointing out the issue but explaining that it will only happen with invalid code.
src/tools/fuzzing.h
Outdated
if (wasm.features.hasExceptionHandling()) { | ||
options.push_back(Type::exnref); | ||
} |
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.
Found that at most places, similar checks are not nested inside if (hasReferenceTypes)
. Not an urgent problem, but I'd imagine that this is going to lead to obscure issues where a user specifies --enable-anyref
but not --enable-reference-types
eventually.
One way to tackle this might be to implicitly enable reference types on setAnyref(true)
respectively setExceptionHandling(true)
, and implicitly disable anyref and exception handling on setReferenceTypes(false)
?
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.
Yeah this is a tricky issue. So far we've avoided having any implicit dependencies between features to keep the mental model as simple and predictable as possible. One possibility would be to make it a validation error to have anyref
enabled without having reference types enabled. That's a little more annoying for users, but on the other hand it keeps the features simple and there should not be real users of the --enable-anyref
feature anyway.
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.
Added a feature validation step, but naturally this now breaks the fuzzer because it doesn't know of implied features. Any suggestions?
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.
Ended up with fb1a789 for now
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 don't think the fuzzer will generate types that does not respect dependences. For example, we check for both FeatureSet::ExceptionHandling
and FeatureSet::ReferenceTypes
whenever we try to add exnref
or other EH instructions (By the way I just realized my PR for generating EH instructions for the fuzzer hasn't landed and is pending for months, but anyway). So I guess the fuzzer error you've seen was just complaining about the feature combination itself, right?
So I'm wondering, why do we need to impose the dependences in validator? Unless giving --enable-exception-handling
without --enable-reference-types
itself in the command line of a VM is an error, I don't think we should treat it as an error.
I think it is a subtle difference; using exnref
without enabling --enable-reference-types
might be an error. But Wasm spec doesn't say giving --enable-exception-handling
without also giving --enable-reference-types
is an error, so..
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 a user's perspective:
- If making missing required features validation errors: "Oh, I see"
- If refactoring all code locations: "Why isn't this option picked up, even though it's specified?"
- If enabling features implicitly: "Why does my binary suddenly utilize reference types, even though it isn't specified?"
From our perspective:
- If making missing required features validation errors: -
- If refactoring all code locations: "Look, a new issue"
- If enabling features implicitly: "Look, a new issue"
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.
The initial concern was that at most places we have code like
if (hasReferenceTypes) { add(funcref) add(externref) } if (hasExceptionHandling) { add(exnref); } if (hasAnyref) { add(anyref); }
instead of
if (hasReferenceTypes) { add(funcref) add(externref) if (hasExceptionHandling) { add(exnref); } if (hasAnyref) { add(anyref); } }
so it seems possible to yield an
exnref
oranyref
without reference types being enabled, or to otherwise get false positives, and a validation check lets us keep the code as-is.
I wonder where we do something like the first? The only place I can think of that creates exnref
or anyref
is the fuzzer, and I think it handles them correctly (or you handled them correctly in this PR). Validation or checking wouldn't be as problematic as creating them because they don't bother with anyref
of exnref
when they don't exist. I could be mistaken; if so please let me know.
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.
Checking again, there is one place where we are doing the former
binaryen/src/passes/InstrumentLocals.cpp
Lines 170 to 193 in 916ce6f
if (curr->features.hasReferenceTypes()) { | |
addImport(curr, | |
get_funcref, | |
{Type::i32, Type::i32, Type::funcref}, | |
Type::funcref); | |
addImport(curr, | |
set_funcref, | |
{Type::i32, Type::i32, Type::funcref}, | |
Type::funcref); | |
addImport(curr, | |
get_externref, | |
{Type::i32, Type::i32, Type::externref}, | |
Type::externref); | |
addImport(curr, | |
set_externref, | |
{Type::i32, Type::i32, Type::externref}, | |
Type::externref); | |
} | |
if (curr->features.hasExceptionHandling()) { | |
addImport( | |
curr, get_exnref, {Type::i32, Type::i32, Type::exnref}, Type::exnref); | |
addImport( | |
curr, set_exnref, {Type::i32, Type::i32, Type::exnref}, Type::exnref); | |
} |
and I somehow assumed that this is some sort of convention, even though it probably isn't. Would suggest to move the hasExceptionHandling
into hasReferenceTypes
there as well so there can't be instrumented get_exnref
/set_exnref
functions taking an exnref
type if reference types is not enabled, but exception handling is. Validating separately still seems fine to me, though, for clarity.
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.
(Haven't checked other locations where just hasExceptionHandling
is checked, but hasReferenceTypes
is not)
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.
Thanks for finding this. That is a special pass that generates imports, and I agree that moving hasExceptionHandling
condition into hasReferenceTypes
is the right call.. Can you add that to this PR if possible?
But I don't think there are many, if at all, passes that generates anyref
of exnref
out of thin air. (Except for the fuzzer, but it is not a pass) One function that can generate them and can be called from Binaryen passes is getLeastUpperBound
, as you pointed out, and if that function creates a type that's not valid that means we have a bug, and that will be caught by the validator. So I don't think there will be mass refactoring necessary.
I just checked what V8 and wabt do for feature dependencies, and actually they are both doing the implication thing, so if you only enable EH that automatically enables reference types too, even if you don't explicitly enable it. Maybe we should consider doing a similar thing in future.
Some early fuzzing:
|
This LGTM once those two conversations are resolved 👍 |
Thanks for doing this. I quickly glanced over a few re-enabled subtype tests and I am a little confused; some tests seem to have their supertype-subtype order reversed compared to the original tests; and it is hard to check that because, disabled tests in #3084 already have their types changed. The one I discovered was done by comparing my local code (without changes from #3084) and this PR. I just started looking at this code just now, but can you hold off on landing this? |
Yeah, these changes can be confusing. Previously we had
with
wit |
Oh sorry. You're right, and I was aware of that. I thought the case I meant was not that and really had a relationship reversed, but I just realized that the line wrapping changed and I was confused by that. Anyway, the case I discovered didn't have any problem. Sorry for the confusion and thanks! |
case Type::anyref: | ||
assert(literal.isNull() && "TODO: non-null anyref values"); | ||
o << "anyref(null)"; | ||
break; | ||
case Type::externref: | ||
assert(literal.isNull() && "TODO: non-null externref values"); | ||
o << "externref(null)"; |
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 which case do we need non-null anyref
and externref
literals?
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 came up earlier in that these are currently not supported by Literal
, which only can represent null
s of any reference type, but Thomas mentioned that we'd want to support these eventually, analogous to ExceptionPackage
for exnref
, so the interpreter can run code by representing non-null anyref
etc. at least abstractly.
src/tools/fuzzing.h
Outdated
if (wasm.features.hasExceptionHandling()) { | ||
options.push_back(Type::exnref); | ||
} |
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 don't think the fuzzer will generate types that does not respect dependences. For example, we check for both FeatureSet::ExceptionHandling
and FeatureSet::ReferenceTypes
whenever we try to add exnref
or other EH instructions (By the way I just realized my PR for generating EH instructions for the fuzzer hasn't landed and is pending for months, but anyway). So I guess the fuzzer error you've seen was just complaining about the feature combination itself, right?
So I'm wondering, why do we need to impose the dependences in validator? Unless giving --enable-exception-handling
without --enable-reference-types
itself in the command line of a VM is an error, I don't think we should treat it as an error.
I think it is a subtle difference; using exnref
without enabling --enable-reference-types
might be an error. But Wasm spec doesn't say giving --enable-exception-handling
without also giving --enable-reference-types
is an error, so..
Co-authored-by: Heejin Ahn <[email protected]>
Co-authored-by: Thomas Lively <[email protected]>
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.
LGTM, and thanks!
Please let me know whether the recent series of commits sufficiently addresses your concerns or if I misunderstood something :) |
CHANGELOG.md
Outdated
@@ -16,6 +16,9 @@ Current Trunk | |||
------------- | |||
- Remove asm2wasm, which supported Emscripten's fastcomp backend, after fastcomp | |||
was removed. | |||
- Enabling the exception handling feature now requires enabling the reference | |||
types feature as well since `exnref` depends on it. The same applies to the | |||
new anyref feature where `anyref` depends on reference types. |
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 has been the case before (that you have to specify dependent features too), so nothing has changed here. If we later add feature implying feature then we need to write something here.
But it'd be worth noting that --enable-anyref
is added, and what it does.
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.
Added this because it is a validation error now (as of the current state of this PR), but wasn't before. Previously one would only get a validation error further down the road if --enable-exception-handling
was specified, an exnref
emitted somehow but --enable-reference-types
omitted.
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.
Split the entry into two parts, with one mentioning the new feature and one mentioning that not enabling implied features is a validation error now.
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.
Oh sorry, I misinterpreted the comment. I actually forgot that this PR makes it a validation error if dependent flags aren't specified. I was not in favor of that, but maybe we should do feature implication later.
src/tools/fuzzing.h
Outdated
options.push_back(Type::externref); | ||
} | ||
options.push_back(Type::funcref); | ||
options.push_back(Type::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.
I'm not too opinionated about this, but the fuzzer still does not assume feature dependencies have been completely resolved; it checks that here and there. How about restoring this condition and maybe we can remove it when we add feature implication feature.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
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.
Gone ahead and restored the condition. Hope it's correct now :)
Feel free to merge if it looks ok to you now. Not quite confident that your concerns are addressed. Meanwhile looking for something useful to do, now eyeballing the name section, i.e. module and local names. |
Adds a custom
--enable-anyref
feature enabling just theanyref
type on top of reference types that allows us to test subtyping relationship (withexternref
,funcref
andexnref
) without having to enable the full set of GC features. Re-enables previously disabled tests.