Code Monkey home page Code Monkey logo

feature-detection's People

Contributors

andrewscheidecker avatar backes avatar binji avatar bnjbvr avatar cellule avatar chfast avatar chicoxyzzy avatar dschuff avatar eqrion avatar flagxor avatar gahaas avatar ggreif avatar honry avatar jfbastien avatar juniorrojas avatar kg avatar kripken avatar lgalfaso avatar littledan avatar lukewagner avatar ms2ger avatar ngzhian avatar pepyakin avatar pjuftring avatar ppopth avatar rossberg avatar sunfishcode avatar swasey avatar tlively avatar xtuc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

seanpm2001

feature-detection's Issues

Do feature detection instructions need to appear in Wasm syntax?

The overview currently describes feature detection as being resolved at decode time so that feature_block is decoded as either a normal block or as an unreachable and features.supported is decoded as either a 0 or 1. This is a convenient framing of the proposal, but it is also unprecedented. Should we instead treat feature_block and features.supported as normal instructions and include them in the abstract syntax and validation algorithms?

@rossberg, I'd be particularly interested in your thoughts on this.

Feature Detection "is faster"

I have a problem with switching from a feature detection as originally proposed to an "is faster" approach in that hardware changes and what "is faster" means nothing from generation to generation. You will still have to detect the exact hardware to determine if it "is faster". Feature detection should probably be used for code path detection and not performance detection as that should be left up to the application IMO.

Clarify interactions with runtime vtable dispatch

I'd like to provide some context on this proposal from the perspective of open-source multimedia software (ffmpeg, x264, libass…): Usually, rather than using gcc/clang-style function multiversioning, we have multiple implementations of a given function with separate names (e.g. my_function_c, my_function_sse2, my_function_avx2, etc). These symbols exist in all builds for the relevant architecture(s) (here, x86/x86_64), and a runtime check determines which one to insert into a vtable (e.g. a function pointer for my_function would be populated with &my_function_sse2 if the SSE2 check passes but the AVX2 check fails).

Having all variants available at runtime also means that we can override the dispatch if needed (e.g. disable the AVX2 version at runtime to benchmark the SSE2 version, and call the C version to have a baseline to compare to during unit tests). It's also higher-performance than using a per-function runtime dispatcher symbol, since we only have to do the check once, and can eliminate the extra indirection layer afterwards.

The upshot of all this is:

  • Our use-case doesn't require any variation in function signatures for the same symbol between extensions
  • We also don't need any variation in data symbols or data types (if we do need any per-extension data, we just create a separate data symbol for each variant)
  • We do need the ability to have a symbol that uses an extension, but can still be referenced from a (non-taken) branch in a function that isn't versioned
  • From a compiler/linker perspective, the plain-C version of the function and the extended versions are generally in separate source files (and often in separate languages; the functions using ISA extensions are often implemented directly in the relevant assembly dialect, rather than C[++]-with-intrinsics).

It's not entirely clear how this ends up working from a code perspective in this proposal:

  • Can we just expose the symbols within an appropriate feature block, and have the symbols just not exist at runtime (so the function pointer would point to e.g. NULL, but we'd never call it?)
  • Can we have the symbols always exist at runtime, with their contents within a feature block, otherwise being no-op stubs?
  • With the concept proposed in #10, could we have two versions of the function, one real and one no-op stub (with the stub potentially being generated automatically by tooling)?
  • How do any of these options interact with any language? If we write the extended versions of functions in directly the .wat text format, what does that look like in code? What if we instead use C or AssemblyScript? Some of these questions might ultimately be up to compiler vendors to answer, but hopefully we can at least have some general ideas.

Does feature_block appear in text disassembly?

The explainer describes feature_block as a 'decoding time' feature.

If a binary to text converter is run on a module, would this imply that the feature_block's present in the binary must be resolved (based off the supported features of the tool?) during the conversion to text? e.g. a features.supported would be printed as the resolved i32.const?

In addition, how would text to binary work? My understanding of the spec is that there is the abstract syntax, and conversions from the binary/text formats to/from the abstract syntax. If this is specified in the binary format and not in the abstract syntax, how can a conversion from text to binary be specified?

Ensure functionality can be polyfilled in JS

The discussion I've seen on this proposal has largely been around use with future extensions (notably future additions to SIMD), but there are current extensions that would benefit massively from the functionality being discussed, so I think it's important that the design at least attempt to avoid precluding JS-based polyfills.

For instance, I'd like to extend some open-source libraries (e.g. ffmpeg, libass, OpenSSL…) to use some features made available in some already-broadly-implemented extensions, including:

  • Bulk memory operations
  • BigInt
  • Basic 128-bit SIMD
  • Threading and atomics

These features (with the exception of SIMD) are implemented in all major browser engines today, but in the same way that ffmpeg maintains compatibility back to at least Windows XP (and will likely support 7 for quite a while after), we need to support wasm implementations back to MVP. Currently, this would mean providing compile-time flags that consumers must enable to get newer features, which produces an untenable amount of build fragmentation and complexity already. This essentially bars these projects from making use of any of the wasm features that would be required for them to be seriously usable on the web platform.

It seems like this proposal should allow for JavaScript to stream down a wasm module, parse it at a high level, recognize feature blocks, and discard (or replace with no-ops?) anything that isn't supported by the current engine. Correct me if I'm wrong?

If I'm right about this generally being doable, I think the main thing this needs is to assign feature IDs to already-existing features. I suppose even without officially-assigned ones, tooling could always just define its own and shift the rest around them, but having this standardized would be best.

What features should be specified?

In principle features could be arbitrarily fine- or course-grained and could be retroactively applied to any instructions already in the spec. In practice, though, only features corresponding to toolchain-level target features that contain targeted, performance sensitive instructions that don't have a short path to widespread adoption will be useful.

I propose that the only feature we define to start out is "simd128", corresponding to the merged SIMD proposal. (If relaxed-simd ships before feature detection, it should have a feature as well.)

Here's why we might not want to define separate features for other proposals:

  • non-trapping float-to-int conversions, sign-extension operations, and bulk memory operations: These instructions are already widely supported, so the feature would not be useful.
  • exception handling, tail calls: It would be difficult to use these instructions conditionally while preserving program semantics without changing the entire compilation scheme, which would lead to code bloat, so these features would not be useful in practice.
  • threads and atomics: Properly using this feature conditionally would require changes to the memory section, which this feature detection proposal does not support. Embedders not wishing to support multithreading should implement this proposal but not provide any facilities to create new threads.

Are there any other features it would make sense to define for a feature detection MVP?

Assigning an opcode to `features.supported` instruction

What is the intended opcode for the features.supported instruction?

Options:

  • At the end of the opcode space for the MVP opcodes (i.e. 0xc5)
  • As a pre-fixed post-MVP opcode at the end of the opcode space (we can bikeshed which one based on the other proposals in flight)

features_block to feature.if/else

After seeing @tlively's presentation today, I offhandedly suggested using else to allow a feature-block to have an alternative. I think it would be somewhat elegant to generalize features_block ... end to feature.if [block_type] ... else ... end. Thus it would look a lot like a regular if, but the true block would be a binary blob that would not be decoded if the feature set is not supported.

We would have to decide what to do with block_type in the else block. One option would be to have a completely alternate type for else or to have the same block_type, but with alternative types substituted in.

How should features be specified?

The purpose of this proposal is to allow modules to validate even when they conditionally use instructions not supported by the engine, but the spec so far has no concept of optional or unsupported instructions. All instructions are currently either mandatory or do not exist as far as the spec is concerned.

One solution would be to avoid adding a concept of features to the normative spec, to specify features.supported and feature_block as succeeding if the feature bitvector is a particular constant and failing otherwise, and relying on non-normative text to document what each feature actually means. This is not a very satisfying solution because we want feature detection to be as well-specified and portable as any other part of WebAssembly.

A better option would be to add a notion of features to the spec, tag (every?) instruction with the feature(s?) it belongs to, and explicitly allow implementations to choose whether or not to support each feature. The spec would essentially be parameterized by the supported features.

Are there any problems with that approach? Are there better alternatives? @rossberg, I'd be particularly interested in your thoughts here.

Is there a dependency on LET?

ISTR that there was an assumption that a restriction of feature detection to the code section would still allow the conditional use of types that may not be present in all engines - eg, v128 would be usable for local variables within suitably conditionalized code for engines that support SIMD, though not in function signatures or on globals. Currently all locals are declared at the function head, not within any block. Yet feature_block is decoded as a block according to the overview, not allowing locals to be declared within it. Is there a hidden dependency on LET in here? LET is on the chopping block over in the function-references proposal.

Alternative sections

In the discussion on #6, two observations have been made:

  1. Limiting alternative code path choices to function granularity seems to be sufficient, at least for the primary gcc/clang use case.

  2. On the other hand, it is not sufficient to enable alternative choices in the code section alone:

    • If a choice affects a function's type signature, then at least the function section also needs to make a corresponding choice.
    • If one of the choices of type signature is not understood by all engines (e.g., using a new or optional type), then a corresponding choice has to be made in the type section already, where the function signatures are stored.
    • Alternative representations of constants may need to be stored in data sections.
    • In a scenario storing SIMD or alternative representations in e.g. GC types, the type section is also affected, as may be the global or element section.

In the light of this, I'd suggest to reconsider a more general mechanism operating on the level of sections.

The conditional sections proposal did that, but had one significant drawback, namely that it was too liberal and allowed the resulting sections to have completely different sizes (including absence), which would make it difficult for tools to process a module coherently.

We could refine this as follows:

  • Instead of a unary construct

    #if <condition> <section> #endif
    

    we change it to an n-ary construct

    #if <condition> <section> (#if <condition> <section>)* #else <section> #endif
    

    where all of the section alternatives must have the same type and size.

  • As before, this is combined with the ability to have multiple occurrences of each section type (like we already want for other reasons as well), such that a conditional can be reduced to a diff.

  • Separately, we can revisit what the representation of "conditions" is.

The n-ary construct mirrors the #if-#elif of C. Crucially, it enforces "well-formedness" of the index spaces created.

In terms of the binary format, such a section conditional would perhaps only store the section type and size once, as a form of "type annotation" on the conditional itself, instead of repeating it in every nested section (which then would only contain the section body).

Honestly, I expect that it is no more complex to define this conditional construct generically for all section types than to have separate equivalent constructs for (at least) code, function, and type section.

Concern with un-decoded bits / alternative for SIMD

I'd like to lay out a concern regarding feature-detection along with a way to address this concern while still addressing the needs of SIMD (which as @tlively was saying in his last presentation, seems to be our only short-term need for feature-detection).

The concern is that, despite our best intentions in the short-term, if we allow wasm modules to validate with completely un-decoded bits, then over the long-term (as wasm gets more toolchains and engines and business contexts), we'll end up with a lot of wasm modules containing non-standard bits. While it is certainly possible today for particular toolchains and engines to agree to produce/consume non-standard/pre-standard bits (which is actually an essential part of the process of standardization), there is a natural check-and-balance in that these non-standard/pre-standard bits will only run on those particular engines and fail to validate on the rest. If it becomes possible to produce modules with non-standard bits that validate on all wasm engines, then it becomes much easier to push out non-standard bits and circumvent the standardization process in general. As with most vague concerns, maybe there comes a time when this is a risk we want to take, but I think it's a good idea to hold off on crossing this Rubicon for as long as possible.

If we zoom in on the SIMD use case, I think a reasonable alternative solution makes sense (which is slightly different from what I've suggested before) based on the observation that the engineering-effort and engine-size required to decode, but not validate or compile, SIMD (or any other future wasm feature for that matter) is miniscule compared to the overall effort required to implement MVP wasm 1.0, and thus it's not worth trying to optimize away SIMD decoding on engines that don't support SIMD (classic Amdahl's Law).

Based on this observation, I think we can derive a reasonable compromise:

  • For any feature that needs to be permanently un-implemented on some engines or hardware/configurations, we specify a feature flag, testable via some instruction that returns whether that feature is "available" (as an i32 value).
  • When a feature is unavailable, the static and dynamic semantics treat all instructions of that feature as-if they were immediately preceded by an unreachable instruction, and thus they are decoded as dead code.
    • I'm assuming here we adopt Conrad's relaxed-dead-code-validation proposal so that this dead code is really-truly only decoded, not validated-in-fun-ways as it is today.
    • If a proposal introduces a new type, then because all the instructions that can observe the values of that type are unreachable, the type doesn't actually need to be implemented with runtime values. E.g., engines with SIMD unavailable wouldn't need to implement v128 with an actual 128-bit value -- they could use nothing or a dummy.
  • We adopt a general expectation in the CG that, once a wasm feature is standardized, all engines either implement it fully or implement it as unavailable -- but either way the engine doesn't permanently leave the feature as failing to decode.
    • Noting that an engine could initially implement a feature as unavailable and then later make it available as the feature was prioritized or the landscape changed.

Some other nice technical benefits from this approach are:

  • This seems relatively easy to specify and implement.
  • This doesn't add any complexity when dealing with code offsets in custom sections (WebAssembly DWARF, names, ...).
  • This doesn't introduce a general new mechanism that needs to be proposed as its own separate feature -- the next SIMD proposal could just do this for SIMD and avoid the scope creep of a more-general feature.
  • This establishes a path forward for adding additional SIMD instructions outside the current maximally-portable intersection: new instructions could be grouped into arbitrarily-fine-grained features, allowing the toolchain to simply branch in normal code.

With respect to the other major concepts of "version" and "profile":

  • There may still be reasons for defining different profiles (e.g., a "deterministic" profile still makes a lot of sense for unrelated reasons), but I think we'd try to minimize this and prefer defining unavailable features (all in the same default profile).
  • A monolithic, monotonically-increasing version would make sense to capture the set of features (always- and potentially-available) that a toolchain could expect to emit without validation error. Even when assuming version X which contains feature Y, if Y is specified to be potentially-unavailable, the toolchain will need to branch on Y's availability (by default).

At least, that's the best I've been able to come up with; happy to discuss!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.