Skip to content

Pure calls / future of call.without.effects #7574

@kripken

Description

@kripken

Atm we provide a way to say that a call has no side effects with an intrinsic, which is just an imported function:

//
// (import "binaryen-intrinsics" "call.without.effects"
// (func (..params..) (param $target funcref) (..results..)))
//
// call.without.effects can take any parameters, and in addition a funcref,
// and return any result.
//
// Precise semantics:
//
// * The optimizer will assume this instruction has no side effects.
// * Final lowering turns a call.without.effects into a call of the given
// function with the given parameters. (This will either be a direct call,
// or a call_ref; note that either way, the function reference that appears
// here must have the proper type - if not, you will get an error.)
//

The wasm spec has been adding similar code metadata in the form of branch hints and compilation hints. While call.without.effects is not meant to be used by VMs (it only make sense at the toolchain level), it may be nice to implement it similarly, to be consistent. That is, instead of the current intrinsic which is a specially-named function import, we could annotate the code the way those two proposals do:

  • In the text format, using custom annotations, something like:
(@metadata.code.pure "\00")
(call $target)
  • In the binary format, add a custom section, and the binary offsets there point to the instructions that are annotated.

The binary format takes more work this way - in particular, it is easy to get the offsets wrong - but it is more consistent with other things in wasm. Thoughts on the tradeoff?

Separately, there are two changes we may want to make to call.without.effects if we make a new version of it:

  • We can annotate the function itself (once) rather than all calls to it (many). That is, unless there are cases where some calls to the same target should be considered effect-free, but not others?
  • Atm call.without.effects only considers side effects (discussion), but it does not guarantee that it returns the same value for the same inputs. In particular, marking a call to a JS export that does Math.random() as call.without.effects would be wrong - two such calls cannot be folded together. We could make the new version also assume it returns the same value, and call it "pure", perhaps?

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