Skip to content
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

Feature Detection granularity #416

Closed
mtrofin opened this issue Oct 16, 2015 · 23 comments
Closed

Feature Detection granularity #416

mtrofin opened this issue Oct 16, 2015 · 23 comments
Milestone

Comments

@mtrofin
Copy link
Contributor

mtrofin commented Oct 16, 2015

(I realize this may be too early to discuss at this point, but can't see harm in logging the issue.)

We are considering allowing modules express dependencies on platform features (see feature test).

In one extreme, if the features are too fine grained, and browsers (or more generally, WebAssembly targets) support different mix&matches of them, we end up with a fragmented ecosystem.

In another extreme, features are coarse grained and effectivelly match the standard's (WebAssembly) versions, avoiding extra fragmentation. In this case, it may be simpler for developers to have their modules express platform dependencies as "at least version x" rather than the current, more flexible, proposal.

SIMD suggests a mix may be appropriate - have version granularity with a very small set of optional, queryable capabilities.

What concrete scenarios do we have/envision that would help guide the discussion?

@lukewagner
Copy link
Member

This is a good question and I'll give my current thinking but definitely open to hearing other points of view. I think the unit of has_feature should match the expected atomic unit of standardization+shipping and the guiding concern for the "right" unit of granularity should be enabling devs to write apps that are able to gracefully use features that aren't 100% ubiquitous in a similar manner to how they do this on the Web today.

Now, if we wanted to be symmetric with JS, we'd give each op/type/etc an associated feature string (defined automatically ∀ ops/types/etc). This is simple and definitely gives devs the power, but it seems to me that it would encourage implementation fragmentation: a coarser feature granularity would encourage vendors to implement an entire unit and thus promote convergence/portability of WebAssembly.

Thus, I think the choice of feature names should be explicit and ad hoc in the spec: each addition to the wasm spec post-v.1 would explicitly say what new feature-names it added to has_feature and partition the new ops/types/sections amount those feature-names.

Some examples:

  • SIMD we'll likely want to break into incremental releases (e.g., with SIMD v.1 at parity with the current SIMD.js proposal). Thus, I think we'd have a simd1 feature that covered SIMD v.1, and then simd2 for the next incremental release, etc.
  • In general, if we had a really big feature that had multiple orthogonally-useful and shippable components, we'd want to break it up into orthogonal sub-features. As an example, "service worker" would be too big for a single feature.
  • We may end up with ops that are permanently optional (there is an example with mprotect in FeatureTest.md), so each one of those would get a feature.
  • Some pieces of a feature might be controversial such that we'd want to break up this otherwise-atomic feature into the noncontroversial and controversial units. (Otherwise you could get into bad situations where two browsers both claimed to have "feature-name" but the actual implemented set differed.) We'd strive to avoid this, of course.

So, considering all this, my i32.min_s example in FeatureTest.md isn't realistic; it'd probably be part of a bigger feature set.

@jfbastien
Copy link
Member

FWIW C++ does something similar: http://wg21.link/n3694

@mtrofin
Copy link
Contributor Author

mtrofin commented Oct 16, 2015

Since both the core set of features as well as orthogonal ones (like SIMD or service workers, to use @lukewagner 's example) may reasonably evolve independently, do we see any value in explicitly representing version numbers, or do we expect that be done by having the platform say it supports both "stringName_1" and "stringName_2" (e.g. "simd v1" and "simd v2" - 2 opaque strings).

It does seem like the core set of features should have a label, too (e.g. "WebAssembly v1" "WebAssembly v2" - where new opcodes are introduced, and I use them)

@lukewagner
Copy link
Member

So one thing I just remembered is that there already is a way to test features without has_feature, and it's the same thing JS does for features that aren't simple property tests: try to eval some code and see if it fails. (In loader milestone 0, you "eval" via (ArrayBuffer -> Blob -> Object URL -> script.src); with milestone 1 there's a proper async API.)

Thinking about the question in this issue has made me realize that it is a rather serious and subjective task to define the right set and granularity of feature names and so it might be better to just punt and let people do it themselves. This puts slightly more pressure on convergence/portability, which I think is good.

If WebAssembly did one day get some permanently-optional feature (e.g., mprotect) then has_feature would make sense and engines could decode the operator even if they didn't support it (it would trap if called). But that is quite a different use case, the set of feature names would be much smaller, and empty for the MVP.

(Technically in C++ you can do this (and autconf et al do), but it's more of a bear and harder to abstract into a simple reusable library.)

Thoughts?

@jfbastien
Copy link
Member

@lukewagner Are you suggesting we just remove has_feature for now?

@kg
Copy link
Contributor

kg commented Oct 16, 2015

So one thing I just remembered is that there already is a way to test features without has_feature, and it's the same thing JS does for features that aren't simple property tests: try to eval some code and see if it fails. (In loader milestone 0, you "eval" via (ArrayBuffer -> Blob -> Object URL -> script.src); with milestone 1 there's a proper async API.)

Do we actually want people to attempt to evaluate dozens of wasm modules on startup just to do feature tests? The cost/complexity profile of that seems significant compared to a JS if statement/eval statement.

It does make sense as a solution for cases where you only test for one or two features.

We have previously discussed various techniques here based on the opcode table - do we polyfill unsupported ops we encounter, or block the module entirely based on seeing them, or block invocations of functions that use the unsupported ops, etc.

Function-at-a-time feature testing instead of entire-module feature testing is likely a thing people will want, but I'm not sure it makes sense to scope into the MVP. has_feature is currently most useful for choosing a function pointer's value (or a code path to execute) based on feature detection at runtime, which will require some underlying support (i.e. being able to compile parts of a module that don't use an unsupported opcode).

It's important to remember that not every 'feature' will be a new opcode; in some cases a 'feature' may mean that a particular restriction on an opcode has been relaxed or that the engine is making a new guarantee you can rely on.

@ghost
Copy link

ghost commented Oct 16, 2015

This issue seems more about managing sets of features. I think this issue can be deferred to an upper layer to define it's own feature sets based on calls to has_feature which should remain fine-grain.

I would encourage wasm implementations to work hard to minimize feature differences, and wasm might define these to coordinate work and to try and get implementations across these lines.

If the upper layers are defined by user defined code then this also needs to be able to be wasm and before compile-time calls to has_feature. Thus the upper layer will need to work without use of any optional compile-time features, but could make run-time calls to has_feature. This might impose some requirements on the design in this area.

@lukewagner
Copy link
Member

@jfbastien I haven't yet fully decided what I think but maybe "yes"; I'm just pointing out an option that I forgot we had which makes has_feature feel less necessary.

Do we actually want people to attempt to evaluate dozens of wasm modules on startup just to do
feature tests? The cost/complexity profile of that seems significant compared to a JS if
statement/eval statement.

Maybe it will be expensive, but maybe not; we can't yet confidently make this claim or know that people are going to want to independently test 40 features. As of the MVP, there would be zero feature tests, so we wouldn't have this problem for at least a few years :)

@dschuff
Copy link
Member

dschuff commented Oct 17, 2015

Saying that "autoconf does this" doesn't really make me want to do it :)

On Fri, Oct 16, 2015 at 3:09 PM Katelyn Gadd [email protected]
wrote:

So one thing I just remembered is that there already is a way to test
features without has_feature, and it's the same thing JS does for features
that aren't simple property tests: try to eval some code and see if it
fails. (In loader milestone 0, you "eval" via (ArrayBuffer -> Blob ->
Object URL -> script.src); with milestone 1 there's a proper async API.)

Do we actually want people to attempt to evaluate dozens of wasm modules
on startup just to do feature tests? The cost/complexity profile of that
seems significant compared to a JS if statement/eval statement.

This. Even aside from the cost, it seems really hacky to me. And if the
eval does fail, you might not even know why.

It's important to remember that not every 'feature' will be a new opcode;
in some cases a 'feature' may mean that a particular restriction on an
opcode has been relaxed or that the engine is making a new guarantee you
can rely on.

This is a really good point. In particular the very last possibility. If a
feature is just a new guarantee then the 'eval' trick will not work at all
because the module will load just fine, and might even run, and possibly
with not-obviously-wrong behavior.

@mtrofin
Copy link
Contributor Author

mtrofin commented Oct 17, 2015

About eval: do we expect a JavaScript execution environment to be always available wherever WebAssembly is, or do we envision WebAssembly stand-alone deployment scenarios, too? (I'm assuming "eval" refers to the JavaScript eval. Or maybe I'm missing something.)

@jfbastien
Copy link
Member

Standalone as well, see NonWeb. That section does need improvement (PRs welcome!).

@titzer
Copy link

titzer commented Oct 17, 2015

I echo the sentiment here that we should probably avoid a situation forcing
eval of lots of little modules to do feature detection. What we really want
is some sort of cheap way to detect if a function or section is a "valid"
section and then take steps if it is not valid. Those steps could be
declarative (e.g. use this function body for this function if this feature
is supported) or dynamic (e.g. try validating this bytecode, and if it
works, then use it, otherwise generate some other bytecode). There is
probably a graduation of things in the last category that we could explore.

On Fri, Oct 16, 2015 at 10:04 PM, JF Bastien [email protected]
wrote:

Standalone as well, see NonWeb
https://github.com/WebAssembly/design/blob/master/NonWeb.md. That
section does need improvement (PRs welcome!).


Reply to this email directly or view it on GitHub
#416 (comment).

@mtrofin
Copy link
Contributor Author

mtrofin commented Oct 17, 2015

Is there a strong reason to have this second "try validating if it works" model? I'm asking if there is a scenario where the declarative ("has_feature") model wouldn't be sufficient for what developers would want to express, and they'd benefit from this second one.

@ghost
Copy link

ghost commented Oct 17, 2015

I don't understand the 'eval of lots of little modules to do feature detection' proposal? WASM appears to have no requirement to even support eval, so how would this even be possible.

@titzer Are you suggesting avoiding fine-grain features, or avoiding supporting 'eval'?

If has_feature can only (or only efficiently) make block or function level choices then it is very limited. It should be possible to make lots of fine-grain code changes based on a feature and only a macro layer can do this effectively.

@binji
Copy link
Member

binji commented Oct 17, 2015

@JSStats it's not really eval; they're talking about compiling a small WASM module that uses a feature/opcode that you want to test and checking whether it succeeds (similar to autoconf).

@lukewagner
Copy link
Member

Is there a strong reason to have this second "try validating if it works" model?

It's not the second model; it's what you can do no matter what just by nature of having dynamic compilation in your host environment (which we're already positing, in FeatureTest.md and BinaryEncoding.md (for the specific layer).

Anyhow, my point above is not that has_feature is never necessary; it obviously can be in general. Rather, my point is that, in the MVP, has_feature is, by definition, not necessary, and it may be quite a while (during which time our understanding of feature detection/polyfilling will change) before it is.

Anyhow, I haven't made any strong argument to remove; I'm just pointing out that it's not strictly necessary in the MVP (which I don't think has been heretofore appreciated) in case we all thought it was better to hold off adding it. But that doesn't seem to be the case :)

@ghost
Copy link

ghost commented Oct 19, 2015

@lukewagner But designing for has_feature does seem to have implications for the binary format, a matter still needing a lot of work. Perhaps the decision will be to leave any rewriting base on has_feature to up stream layers, but this still needs to be decided and the implications for the binary format settled.

@mtrofin
Copy link
Contributor Author

mtrofin commented Oct 19, 2015

I agree with @JSStats. I imagine we want to support scenarios where post-MVP modules can run in an environment supporting just the MVP, but having some optional code taking dependency on post-MVP features (user didn't upgrade just yet the browser, but site leveraged the new feature - that type of stuff). So the scenario is actually a MVP scenario on a time delay: the MVP needs to be able to execute modules coming in with conditional compilation directives.

@lukewagner - If I understand your point correctly ("it's what you can do no matter what just by nature of having dynamic compilation [...]") - I think you're saying that supporting has_feature is the same as having eval - is that accurate?

If so, I don't think feature testing is equivalent to dynamic compilation. With the declarative semantics of has_feature, an environment can pre-compile ahead of time the whole module, executing no user code when doing that. Both "no execution of user code at compile time" and "ahead-of time compilation only" may be requirements in strict environments such as gaming consoles, for example. XBox, afaik, is an example - JIT-ing is not allowed there. In such scenarios, while "eval" might be given semantics through an interpreter, that wouldn't really match what a developer wanted to express when she said, for example "please use this SIMD code if the target supports that".

On that line of thinking, I don't think we can assume dynamic compilation is an invariant, but I think conditional compilation should be supported as part of the standard - at minimum as "no features supported".

@ghost
Copy link

ghost commented Oct 19, 2015

@mtrofin has_feature is not declarative as penned in now, it is a procedural operation.

With the layered design being discussed the environment would need to execute the upper layers of user defined code in order to compile the code. These upper layers might also be wasm if that helps. For example, it would need to execute an optional user define decompression stage and rewriting stage that might use has_feature.

If the XBox does not have the capacity for JIT in their browser then they probably do not have the capacity to compile up wasm either, but perhaps it is a smaller burden to write a light weight wasm compiler.

I presume the XBox has a JS interpreter that can dynamically create JS to be evaluated in this interpreter?

I don't think wasm requires compilation, so no assumption is being made that dynamic compilation is supported. When they talk about dynamic linking, it is within a model that works in an interpreter.

@mtrofin
Copy link
Contributor Author

mtrofin commented Oct 19, 2015

@JSStats I think the motivation for not allowing JIT-ing on some gaming consoles is not cost of engineering effort (if that's what you mean by burden), it is policy rather.

Are there folks in the working group that would be able to shed some light over this? I wonder if there's a more general scenario there that's worth understanding.

@ghost
Copy link

ghost commented Oct 19, 2015

@mtrofin Such an anti-JIT policy would be for apps though, not for an integrated web browser? The lack of a JIT in their integrated web browser suggests resource challenges.

An Anti-JIT policy for apps is probably in part because the system does not want to give such low level access, perhaps to enforce security etc. This should not be an issue for wasm as the app is not generating native code rather emitting wasm source code that the runtime will compile (or interpret).

@sunfishcode sunfishcode added this to the MVP milestone Oct 22, 2015
@kg kg mentioned this issue Jan 21, 2016
@MikeHolman
Copy link
Member

I missed this conversation earlier, but essentially Xbox didn't allow JIT in browser or apps because they believed it was a security risk. And to be fair, recent hack in PS4 came from bug in their js engine, so they are right to be paranoid about locking down the browser. That said, they have since reevaluated their position and I believe they have enabled our JIT in a recent update.

And even with JIT disabled, you're also right this is not an issue for wasm functionality (outside of perf) because it doesn't require native code generation, and we have an interpreter.

@mtrofin
Copy link
Contributor Author

mtrofin commented Feb 24, 2016

We will not add an opcode or a specifically-designed feature to the standard.

We are addressing the feature detection+selection scenarios by:

  • introducing a "Validate" API on the WASM object, which may be used for feature detection by attempting to validate canonical, tiny modules which use the feature being tested (e.g. for SIMD, they'd use a SIMD operand). It is expected that the author of a new feature also offer the test snippet for it, and that a feature may comprise of more than just one opcode - its detection, however, may choose just an opcode as a marker.
  • we will offer guidance to developers to perform feature detection using the Validate API. Developers will store on server side a separate WebAssembly binary for each combination of features they decide to support, which should be a natural fallout from them testing each such combination.

We will also offer guidance on the alternative to patch a WebAssembly module while streaming it, observing there are challenges to this approach. As patterns emerge, and as feedback is collected, the community may contribute tailored solutions here.

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

No branches or pull requests

9 participants