<?xml version="1.0" encoding="utf-8"?>
<!-- generator="Kukkaisvoima version 7" -->
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
>
<channel>
<title>vmx: WASM</title>
<link>https://vmx.cx/cgi-bin/blog/index.cgi</link>
<description>Blog of Volker Mische</description>
<pubDate>Fri, 29 Jan 2021 15:00:17 +0200</pubDate>
<lastBuildDate>Fri, 29 Jan 2021 15:00:17 +0200</lastBuildDate>
<generator>http://23.fi/kukkaisvoima/</generator>
<language>en</language>
<item>
<title>WebAssembly multi-value return in today's Rust without wasm-bindgen
</title>
<link>https://vmx.cx/cgi-bin/blog/index.cgi/webassembly-multi-value-return-in-todays-rust-without-wasm-bindgen%3A2021-01-29%3Aen%2CWASM%2CRust</link>
<comments>https://vmx.cx/cgi-bin/blog/index.cgi/webassembly-multi-value-return-in-todays-rust-without-wasm-bindgen%3A2021-01-29%3Aen%2CWASM%2CRust#comments</comments>
<pubDate>Fri, 29 Jan 2021 15:00:17 +0200</pubDate>
<dc:creator>Volker Mische</dc:creator>
<category>en</category>
<category>WASM</category>
<category>Rust</category>
<guid isPermaLink="false">https://vmx.cx/cgi-bin/blog/index.cgi/webassembly-multi-value-return-in-todays-rust-without-wasm-bindgen%3A2021-01-29%3Aen%2CWASM%2CRust/</guid>
<description><![CDATA[ 
 [...]]]></description>
<content:encoded><![CDATA[

<p>The goal was to run some <a href="https://webassembly.org/">WebAssembly</a> within different host languages. I needed a WASM file that is independent of the host language, hence I decided to code the <a href="https://en.wikipedia.org/wiki/Foreign_function_interface">FFI</a> manually, without using any tooling like <a href="https://rustwasm.github.io/docs/wasm-bindgen/">wasm-bindgen</a>, which is JavaScript specific. It needed a bit of custom tooling, but in the end I succeeded in having a WASM binary that has a multi-value return, generated with today's Rust compiler, without using wasm-bindgen annotations.</p>
<h3>Introduction</h3>
<p>In my case I wanted to pass some bytes into the WASM module, do some processing and returning some other bytes. I found all information I needed in this excellent <a href="https://radu-matei.com/blog/practical-guide-to-wasm-memory/">A practical guide to WebAssembly memory</a> from <a href="https://twitter.com/matei_radu">radu</a>. There he mentions the <a href="https://github.com/WebAssembly/multi-value">WebAssembly multi-value proposal</a> and links to a blog post from 2019 called <a href="https://hacks.mozilla.org/2019/11/multi-value-all-the-wasm/">Multi-Value All The Wasm!</a> which explains its implementation for the Rust ecosystem.</p>
<p>As it's from 2019 I just went ahead and thought I can use multi-value returns in Rust.</p>
<h3>The journey</h3>
<p>My function signature for the FFI looks like this:</p>
<pre><code class="language-rust">pub extern &quot;C&quot; fn decode(data_ptr: *const u8, data_len: usize) -&gt; (*const u8, usize) { … }
</code></pre>
<p>When I compiled it, I got this warning:</p>
<pre><code>warning: `extern` fn uses type `(*const u8, usize)`, which is not FFI-safe
 --&gt; src/lib.rs:2:67
  |
2 | pub extern &quot;C&quot; fn decode(data_ptr: *const u8, data_len: usize) -&gt; (*const u8, usize) {
  |                                                                   ^^^^^^^^^^^^^^^^^^ not FFI-safe
  |
  = note: `#[warn(improper_ctypes_definitions)]` on by default
  = help: consider using a struct instead
  = note: tuples have unspecified layout
</code></pre>
<p>Multi-value returns are certainly not meant for C APIs, but for WASM it might still work, I thought. Running <a href="https://github.com/WebAssembly/wabt">wasm2wat</a> shows:</p>
<pre><code class="language-wat">(module
  (type (;0;) (func (param i32 i32 i32)))
  (func $decode (type 0) (param i32 i32 i32)
…
</code></pre>
<p>This clearly isn't a multi-value return. It doesn't even have a return at all, it takes 3 parameters, instead of the 2 the function definition has. I found an issue called <a href="https://github.com/rust-lang/rust/issues/73755">Multi value Wasm compilation #73755</a> and was puzzled why it doesn't work. Is this a regression? Why did it work in that blog post from 2019? I gave the <a href="https://hacks.mozilla.org/2019/11/multi-value-all-the-wasm/">Multi-Value All The Wasm!</a> blog post another read, and it turns out it explains all this in detail (look at the <code>wasm-bindgen</code> section). Back then it wasn't supported by the Rust compiler directly, but by wasm-bindgen.</p>
<p>So perhaps I can just use the wasm-bindgen command line tool and transform my compiled WASM binary into a multi-value return one. There is a command-line flag called <code>WASM_BINDGEN_MULTI_VALUE=1</code> to enable that transformation. Sadly that doesn't really work as it needs some <a href="https://github.com/WebAssembly/interface-types/">interface-types</a> present in the WASM binary (which I don't have).</p>
<p>Thanks to open source, the blog post about the implementation of the transformation feature and some trial an error, I was able to extract the pieces I needed and created a tool called <a href="https://github.com/vmx/wasm-multi-value-reverse-polyfill/">wasm-multi-value-reverse-polyfill</a>. I didn't need to do any of the hard parts, just some wiring up. I was now able to transform my WASM binary into a multi-value return one simply by running:</p>
<pre><code class="language-console">$ multi-value-reverse-polyfill ./target/wasm32-unknown-unknown/release/wasm_multi_value_retun_in_rust.wasm 'decode i32 i32'
Make `decode` function return `[I32, I32]`.
</code></pre>
<p>The WAT disassembly now looks like that:</p>
<pre><code class="language-wat">  (type (;0;) (func (param i32 i32) (result i32 i32)))
  (type (;1;) (func (param i32 i32 i32)))
  (func $decode_multivalue_shim (type 0) (param i32 i32) (result i32 i32)
…
</code></pre>
<p>There you go. There is now a shim function that has the multi-value return, which calls the original method. I can now use my newly created WASM binary with WebAssembly runtimes that support multi-value returns (like <a href="https://wasmer.io/">Wasmer</a> or <a href="https:https://nodejs.org/">Node.js</a>).</p>
<h3>Conclusion</h3>
<p>With <a href="https://github.com/vmx/wasm-multi-value-reverse-polyfill/">wasm-multi-value-reverse-polyfill</a> I'm now able to create multi-value return functions with the current Rust compiler without depending on all the magic wasm-bindgen is doing.</p>
]]></content:encoded>
<wfw:commentRss>https://vmx.cx/cgi-bin/blog/index.cgi/webassembly-multi-value-return-in-todays-rust-without-wasm-bindgen%3A2021-01-29%3Aen%2CWASM%2CRust/feed/</wfw:commentRss>
</item>
</channel>
</rss>
