Skip to content

Add “try-load” and “try-store” instructions #23

Description

@pcwalton

JITting emulators like http://copy.sh/v86 incur significant overhead from having to check page tables on every memory access. Traditionally, emulators implement this by trapping SIGSEGV. The signal handler isn’t necessary, however. One could imagine a “try load” and “try store” pair of instructions that take a block of code to execute if the load would otherwise trap. A simple implementation of WebAssembly could emit a compare against the page protections and jump. A more optimizing implementation could catch the SIGSEGV and alter the wasm program counter.

Activity

  1. bvisness commented on Nov 7, 2025

    @bvisness
    Collaborator

    A simple implementation of WebAssembly could emit a compare against the page protections and jump.

    What page protections are you referring to? I could imagine this being related to either the static memory protection or virtual mode sub-proposals, but I'm not exactly sure what you have in mind. Currently today it seems like you could simply do a bounds check against memory.length.

  2. pcwalton commented on Nov 7, 2025

    @pcwalton
    Author

    This would be an enhancement to virtual mode. Static memory protection isn't usable for emulators of systems with MMUs as the guest OS can configure memory however it wants.

    I believe that, when combined with #22, this would be sufficient to efficiently implement JITs of systems with MMUs on top of wasm. See https://github.com/copy/v86/blob/master/docs/how-it-works.md for how wasm-based JITs deal with memory loads and stores today: you can see that they essentially implement "try-load" and "try-store" manually by checking against per-page "valid" bits. This proposal would essentially lift those "try-load" and "try-store" patterns into wasm instructions, which a wasm VM could then optimize into the most efficient patterns if the host system allows it.

  3. pcwalton commented on Nov 8, 2025

    @pcwalton
    Author

    I imagine it would look something like this at the WAT level:

    (type.tryload ... instructions ...)
    (type.trystore ... instructions ...)

    where type is a type that can be used in a load or store and ... instructions ... is a list of instructions.

    The semantics are:

    • type.tryload functions as a type.load, except that if and only if the instruction would trap due to a memory fault (out of bounds or reading from a no-access location), the instructions are executed instead. The instructions are expected to push a value of the appropriate type to the stack (i.e. they end in an instruction of the same type as the memory load).
    • type.trystore functions as a type.store, except that if and only if the instruction would trap due to a memory fault (out of bounds or writing to a no-access or read-only location), the instructions are executed instead. The size of the stack after the instructions execute must be the same as the size of the stack before the instructions execute (i.e. the instructions return void).
  4. pcwalton commented on Nov 8, 2025

    @pcwalton
    Author

    I'm told that this would indeed be sufficient for v86 when combined with the virtual memory proposal, as long as the instruction blocks can be exited just as an if block can (i.e. there can be jumps out of that block).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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