[HN Gopher] Show HN: I wrote a WebAssembly Interpreter and Toolk...
       ___________________________________________________________________
        
       Show HN: I wrote a WebAssembly Interpreter and Toolkit in C
        
       Author : 4984
       Score  : 79 points
       Date   : 2023-01-09 14:02 UTC (8 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | mouse_ wrote:
       | I'd love to see a benchmark of this vs. libwasm
       | https://github.com/SerenityOS/serenity/tree/master/Userland/...
        
       | muricula wrote:
       | Has this been fuzz tested? Fuzz testing is one of the best ways
       | of discovering security vulnerabilities in C code, and tools like
       | libfuzzer and asan or AFL make it easier than ever.
        
       | 4984 wrote:
       | I made Web49 because there are not many good tools for
       | WebAssembly out there. WABT is close, but the interpreter is too
       | slow and the tools megabytes in size each. Wasm3 is a bit faster
       | but only contains an interpreter, nothing else.
       | 
       | Tooling for WebAssembly is held mostly by the browser vendors. It
       | is such a nice format to work with when one removes all the
       | fluff. WebAssembly tooling should not take seconds to do what
       | should take milliseconds, and it should be able to be used as a
       | library, not just a command line program.
       | 
       | I developed a unique way to write interpreters based on threaded
       | code jumps and basic block versioning when I made MiniVM
       | (https://github.com/FastVM/minivm). It was both larger and more
       | dynamic than WebAssembly. Web49 started as a way to compile
       | WebAssembly to MiniVM, but soon pivoted into its own Interpreter
       | and tooling. I could not be happier with it in its current form
       | and am excited to see what else It can do, with more work.
        
         | [deleted]
        
         | haberman wrote:
         | > I developed a unique way to write interpreters based on
         | threaded code jumps and basic block versioning when I made
         | MiniVM (https://github.com/FastVM/minivm). It was both larger
         | and more dynamic than WebAssembly.
         | 
         | I'd be very interested to read more about this. It looks like
         | you are using "one big function" with computed goto (https://gi
         | thub.com/FastVM/Web49/blob/main/src/interp/interp....). My
         | experience working on this problem led me to the same
         | conclusion as Mike Pall, which is that compilers do not do well
         | with this pattern (particularly when it comes to register
         | allocation): http://lua-
         | users.org/lists/lua-l/2011-02/msg00742.html
         | 
         | I'm curious how you worked around the problem of poor register
         | allocation in the compiler. I've come to the conclusion that
         | tail calls are the best solution to this problem:
         | https://blog.reverberate.org/2021/04/21/musttail-efficient-i...
        
           | naasking wrote:
           | > My experience working on this problem led me to the same
           | conclusion as Mike Pall, which is that compilers do not do
           | well with this pattern
           | 
           | Note that that message is from twelve years ago. A lot's
           | changed since then, not just in compilers but in CPUs. Branch
           | prediction is a lot better now.
        
             | fwsgonzo wrote:
             | I read a research paper that proved that the branch
             | prediction issues are non-issue with modern predictors (eg.
             | TTAGE). It is of course true that register spills happen,
             | but it's not bad enough to want to write hand-written
             | assembly. Especially when you simulate AOT-compiled code
             | (eg. RISC-V and WASM), you will already be 3-10x faster
             | than Lua already. For _my_ purposes of using this kind of
             | emulator for scripting, it is already fine.
             | 
             | Throw instruction counting into the mix, and you can even
             | be faster than LuaJIT, although I'm not sure how it manages
             | to screw up the counting so badly. I wrote a little bit
             | about it here: https://medium.com/@fwsgonzo/time-to-first-
             | instruction-53a04...
        
             | haberman wrote:
             | Mike's primary complaint is bad register allocation. It is
             | very important to keep the most important state
             | consistently in registers. In my experience, compilers
             | still struggle to do good register allocation in big and
             | branchy functions.
             | 
             | Even perfect branch prediction cannot solve the problem of
             | unnecessary spills.
        
               | naasking wrote:
               | Very true. I imagine that grouping instructions that use
               | the same registers into their own functions would help
               | with that (arithmetic expressions tend to generate
               | sequences like this). Then you loop within this function
               | while the next instruction is in the same group, and only
               | return to the outer global instruction loop otherwise. If
               | you design the bytecode carefully, you can probably do
               | group checks with a simple bitmask.
        
               | 10000truths wrote:
               | Does providing a hint to the compiler using the register
               | keyword address the issue sufficiently?
        
               | haberman wrote:
               | No, most compilers ignore the register keyword, see:
               | https://stackoverflow.com/a/10675111
        
       | vmafficianado wrote:
       | Impressive!
       | 
       | Will work stop on MiniVM as result of Web49?
        
       | cornstalks wrote:
       | Can this share memory with the host? wasm3 doesn't allow this[1]
       | and requires you to allocate VM memory and pass it to the host,
       | but that has several downsides (some buffers come from external
       | sources so this requires a memcpy; the VM memory location isn't
       | stable so you can't store a pointer to it on the host; etc.).
       | 
       | I'm really interested in a fast interpreter-only Wasm VM that can
       | allow the host to share some of its memory with the VM.
       | 
       | [1]: https://github.com/wasm3/wasm3/issues/114
        
       | Octokiddie wrote:
       | Any ideas on why miniwasm performs better on all the benchmarks
       | except "trap," on which it performs decidedly worse?
        
         | 4984 wrote:
         | The benchmarks were run on MacOS, and actually execute an
         | interrupt for debugging, MacOS then checks if the process is
         | being debugged. Wasm3 just exit(1) and prints a message.
         | 
         | And as to why the rest are faster, I spent much time optimizing
         | the interpreter and learning what the best way to write
         | interpreters is. Its mostly jump threading and Mixed Data.
        
         | fwsgonzo wrote:
         | Don't take this as anything other than speculation: I wonder if
         | wasm3 is using musttail with opaque function calls in the
         | instruction handlers. It will demolish performance, which is
         | why I am only using computed gotos in mine (when available).
         | Even switch-case is faster than musttail when you have to leave
         | the tco-jumps. Which is (as an example) why one should not
         | measure performance by fibonacci number generation. :)
        
           | haberman wrote:
           | > I wonder if wasm3 is using musttail with opaque function
           | calls in the instruction handlers. It will demolish
           | performance, which is why I am only using computed gotos in
           | mine (when available). Even switch-case is faster than
           | musttail when you have to leave the tco-jumps.
           | 
           | This doesn't match with my experience. After working on this
           | problem a lot, I came to the conclusion that musttail with
           | opaque function calls is one of the best ways of getting good
           | code out of the compiler:
           | https://blog.reverberate.org/2021/04/21/musttail-
           | efficient-i...
        
             | fwsgonzo wrote:
             | I meant having an opaque function inside your instruction
             | handler. My assembly looks like crap if something doesn't
             | get inlined. Because I have no way of achieving this I
             | simply cannot use TCO. It runs fibonacci faster, but
             | anything that uses memory is way worse because it pushes
             | and pops a ton of registers on the instruction handler
             | itself, and not the slow-path opaque function.
             | 
             | An instruction handler here being a dispatch function. It
             | handles a single instruction.
             | 
             | Reading your post it says so under Limitations. Opaque
             | calls trashes performance. I guess we agree, but then again
             | I was just reading my assembly, so I had no reason to doubt
             | myself.
        
               | haberman wrote:
               | Yes our solution was to make all fallback functions into
               | tail calls. It solves the problem, but requires a lot of
               | discipline and can be a bit awkward.
               | 
               | I recently saw this, which is a very interesting approach
               | for using non-tail-call fallback functions without
               | trashing the code: https://chromium-
               | review.googlesource.com/c/v8/v8/+/4116584
        
               | fwsgonzo wrote:
               | That's very interesting! Thanks!
        
       | duped wrote:
       | How does it compare to Wasmtime? Do you have a list of supported
       | wasm features?
        
       | habibur wrote:
       | I am afraid it doesn't work on linux.
        
         | heleninboodler wrote:
         | Worked flawlessly for me on linux. The readme should mention
         | 'emcc' and 'wasm3' as prerequisites for following the bench.py
         | instructions, though.
        
           | thechao wrote:
           | I'm on macOS, and if I do this:                   > git clone
           | ...         > make -j         > ./bin/wasm2wat
           | ./test/core/address.wast
           | 
           | I get:                   >>> ./bin/wat2wasm
           | test/core/address.wast          unexpected word: `` byte=256
        
       ___________________________________________________________________
       (page generated 2023-01-09 23:01 UTC)