Introduce the multi-value-xform crate

This crate provides a transformation to turn exported functions that use a
return pointer into exported functions that use multi-value.

Consider the following function:

```rust
pub extern "C" fn pair(a: u32, b: u32) -> [u32; 2] {
    [a, b]
}
```

LLVM will by default compile this down into the following Wasm:

```wasm
(func $pair (param i32 i32 i32)
  local.get 0
  local.get 2
  i32.store offset=4
  local.get 0
  local.get 1
  i32.store)
```

What's happening here is that the function is not directly returning the
pair at all, but instead the first `i32` parameter is a pointer to some
scratch space, and the return value is written into the scratch space. LLVM
does this because it doesn't yet have support for multi-value Wasm, and so
it only knows how to return a single value at a time.

Ideally, with multi-value, what we would like instead is this:

```wasm
(func $pair (param i32 i32) (result i32 i32)
  local.get 0
  local.get 1)
```

However, that's not what this transformation does at the moment. This
transformation is a little simpler than mutating existing functions to
produce a multi-value result, instead it introduces new functions that wrap
the original function and translate the return pointer to multi-value
results in this wrapper function.

With our running example, we end up with this:

```wasm
;; The original function.
(func $pair (param i32 i32 i32)
  local.get 0
  local.get 2
  i32.store offset=4
  local.get 0
  local.get 1
  i32.store)

(func $pairWrapper (param i32 i32) (result i32 i32)
  ;; Our return pointer that points to the scratch space we are allocating
  ;; on the shadow stack for calling `$pair`.
  (local i32)

  ;; Allocate space on the shadow stack for the result.
  global.get $shadowStackPointer
  i32.const 8
  i32.sub
  local.tee 2
  global.set $shadowStackPointer

  ;; Call `$pair` with our allocated shadow stack space for its results.
  local.get 2
  local.get 0
  local.get 1
  call $pair

  ;; Copy the return values from the shadow stack to the wasm stack.
  local.get 2
  i32.load
  local.get 2 offset=4
  i32.load

  ;; Finally, restore the shadow stack pointer.
  local.get 2
  i32.const 8
  i32.add
  global.set $shadowStackPointer)
```

This `$pairWrapper` function is what we actually end up exporting instead of
`$pair`.
This commit is contained in:
Nick Fitzgerald
2019-09-09 14:00:04 -07:00
parent 582b733967
commit 44c3f8ad2d
4 changed files with 356 additions and 0 deletions

View File

@@ -21,6 +21,7 @@ tempfile = "3.0"
walrus = "0.12.0"
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.50' }
wasm-bindgen-shared = { path = "../shared", version = '=0.2.50' }
wasm-bindgen-multi-value-xform = { path = '../multi-value-xform', version = '=0.2.50' }
wasm-bindgen-threads-xform = { path = '../threads-xform', version = '=0.2.50' }
wasm-bindgen-wasm-interpreter = { path = "../wasm-interpreter", version = '=0.2.50' }
wasm-webidl-bindings = "0.5.0"