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

Add support for snapshotting tables #29

Open
fitzgen opened this issue Aug 4, 2021 · 2 comments
Open

Add support for snapshotting tables #29

fitzgen opened this issue Aug 4, 2021 · 2 comments

Comments

@fitzgen
Copy link
Member

fitzgen commented Aug 4, 2021

Right now there are two potential kinds of tables:

  • funcref tables
  • externref tables

The latter are easy to support, since Wasm can't construct an externref, only receive it from the outside world. As long as we don't allow any externrefs into the Wasm during initialization, then all we have to do is record the initialized size of these tables.

funcref tables are a little harder. Wasm can create funcrefs via the ref.func instruction. So we need a way to figure out which function each wasmtime::FuncRef in a table we are snapshotting is (i.e. what is the local function index of the ith table element?). wasmtime doesn't expose anything like that. I have an idea, however:

  • during Wizer's instrumentation phase:
    • add two new globals to the module: $wizer_getting_func_index and $wizer_func_index
    • add the following snippet to the start of every function in the module:
      block
        global.get $wizer_getting_func_index
        i32.eqz
        br_if 0
        i32.const <this function's local index>
        global.set $wizer_func_index
        unreachable
      end
      
  • then, during snapshotting, to get a given funcref's local function index, we set the $wizer_getting_func_index global non-zero, call the function, and then it will set its local function index to the $wizer_func_index global and trap.
  • the dummy functions we create for imports will need a similar dance, but implemented in native code rather than Wasm. Something similar for wasi functions too.

The one problem is that local function index isn't quite enough in the face of module linking. Instead we will need both the instance "id" (whatever that this; DFS index?) as well as the local function index in that instance. More to think about w.r.t. module linking here...

@fitzgen
Copy link
Member Author

fitzgen commented Sep 1, 2021

The one problem is that local function index isn't quite enough in the face of module linking. Instead we will need both the instance "id" (whatever that this; DFS index?) as well as the local function index in that instance. More to think about w.r.t. module linking here...

We can flip our view of this problem to make it a lot easier: we don't need to know, for a given table, where all of its functions came from and how to get them in there. Because of the constraints that Wizer imposes on imports, we know that all the functions came from somewhere inside this module graph / instantiation tree. So each instance needs to ask for a given table that it has access to, which of these functions are from me? Then we save this info in the snapshot and emit them as element segments.

We can make this a little more efficient by associating a bitmap with each table of which functions we've already resolved, so we don't have to check every single function in the table for every instance that has access to that table. This would also let us catch errors where some future WASI proposal stuffed things into the function table, or if we supported initializing with arbitrary linkers: just assert that we checked off all bits in that bit map, meaning we've accounted for every function in the table.

@gkgoat1
Copy link

gkgoat1 commented Jan 30, 2025

Right now there are two potential kinds of tables:

  • funcref tables
  • externref tables

The latter are easy to support, since Wasm can't construct an externref, only receive it from the outside world. As long as we don't allow any externrefs into the Wasm during initialization, then all we have to do is record the initialized size of these tables.

This might cause us inconvenience if/when we support gc, as gc supports conversion between ref any and ref extern.

funcref tables are a little harder. Wasm can create funcrefs via the ref.func instruction. So we need a way to figure out which function each wasmtime::FuncRef in a table we are snapshotting is (i.e. what is the local function index of the ith table element?). wasmtime doesn't expose anything like that. I have an idea, however:

  • during Wizer's instrumentation phase:

    • add two new globals to the module: $wizer_getting_func_index and $wizer_func_index
    • add the following snippet to the start of every function in the module:
      block
        global.get $wizer_getting_func_index
        i32.eqz
        br_if 0
        i32.const <this function's local index>
        global.set $wizer_func_index
        unreachable
      end
      
  • then, during snapshotting, to get a given funcref's local function index, we set the $wizer_getting_func_index global non-zero, call the function, and then it will set its local function index to the $wizer_func_index global and trap.

  • the dummy functions we create for imports will need a similar dance, but implemented in native code rather than Wasm. Something similar for wasi functions too.

We could also wrap imports in a shim that does the instrumentation code, then calls it and returns.

The one problem is that local function index isn't quite enough in the face of module linking. Instead we will need both the instance "id" (whatever that this; DFS index?) as well as the local function index in that instance. More to think about w.r.t. module linking here...

Module linking was replaced by the component model, so this might be feasible again, as wizercurrently doesn't support it.

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

2 participants