Skip to content

Injecting platform specific instructions or blocks as inlined function imports at compile-time (capabilities) #103

@ttraenkler

Description

@ttraenkler

The discussion during the CG presentation seemed to centered mostly around the question of how to in general handle the tradeoff between portability and platform specific capabilities as this could break compatibility with certain platforms, unexpected performance cliffs, manually curating alternative code paths, or unconsciously introducing non determinism or runtime traps by importing a module as a sub-dependency using a non portable feature.

I would like to come back to the suggestion on slide 11 of the presentation given at the in person CG meeting, to handle "Relaxed Instructions as Imports" since this to me seems to be the most desirable of those options.

As stated in the slide the upside would be:

  1. "Sidesteps introducing a new kind of non-determinism in WAsm": In other words, this moves the problem outside of the module by allowing the module consumers to take the decision best for their use case by providing the instruction as a normal function import that resolves how this instruction is handled: It could be fast but non deterministic between architectures, forced to emulate one version in software on architectures that do not support the instruction or could implement it completely differently with an arbitrary function that applies custom logic or alternative code paths (if/else) matching the needs of the consumer in their specific case, which gives control to the consumer instead of forcing a one size fits all hurting either determinism or performance constraints. In contrast to a simple macro like if/else block, this decision could be taken locally just for one module, and also would put the module consumer in control of the implementation, who would otherwise be bound to the solution deemed most suitable by the module implementer. If the instruction would not be provided as an import explicitly, the host could either have a globally set default depending on the normal solution in this environment, or it could be provided as "undefined" so the module could detect this capability does not exist and if it cannot handle it would "trap" predictably at compile or link time instead of an undesirable runtime trap or provide a default implementation itself. This solutions seems to encompass both the ups of the other suggestions "drop consistency" and "explicit tests for arch-specifics" and similar to WASI capabilities limits the blast radius of the taken tradeoff for the module. I believe this could also be a nice feature detection mechanism, by simple allowing an optional import for the non core Wasm features (that the host could provide by default if the consumer does not object or override).

Regarding the two downsides mentioned on slide 11:

  1. "The difference is mostly theoretical, moving non-determinism from Wasm into environment doesn’t change anything in practice": I assume this is in comparison to the explicit tests for arch specifics (slide 12). Imho it is not mostly theoretical, as it is much more flexible and puts control into the hands of the consumer that can pick a default or customize the behavior based on their platform and task requirements. "Explicit tests for arch-specifics" in contrast would take the control out of the hands of the consumer how the instruction is implemented and forcing them to answer a simple yes/no for all modules to enable this feature. So lost would be the consumer choice per module and the freedom to taking application requirements into consideration by adapting the implementation to it, which the module implementer without knowledge of the use case naturally cannot do.

  2. "Mozilla suggested this does not fit well into SpiderMonkey": What in specific is the problem with this suggestion in relation with SpiderMonkey? Would this be resolved with "pre-imports" as proposed in @rossberg Staged compilation and module linking presentation by allowing to provide the implementation at the time of module compilation as compare to instantiation?

The potential "downside" of platform specific instructions as function imports could be that modules cannot rely on these instructions to behave identical across modules anymore, so they should be seen as (potentially hardware accelerated) functions instead - but this stems from the nature of "relaxed" platform specific instructions itself, so I believe is a better tradeoff to make than introducing a one size fits all solution globally and stop out certain use cases completely or splitting the ecosystem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions