{
    "version": "https://jsonfeed.org/version/1",
    "title": "WebAssembly from the Ground Up Blog",
    "home_page_url": "https://wasmgroundup.com/blog",
    "description": "WebAssembly from the Ground Up Blog",
    "items": [
        {
            "id": "https://wasmgroundup.com/blog/wasm-vm-part-2",
            "content_html": "<style>[data-ch-theme=\"nord\"] {  --ch-t-colorScheme: dark;--ch-t-foreground: #d8dee9ff;--ch-t-background: #2e3440ff;--ch-t-lighter-inlineBackground: #2e3440e6;--ch-t-editor-background: #2e3440;--ch-t-editor-foreground: #d8dee9;--ch-t-editor-lineHighlightBackground: #3b4252;--ch-t-editor-rangeHighlightBackground: #434c5e52;--ch-t-editor-infoForeground: #3794FF;--ch-t-editor-selectionBackground: #434c5ecc;--ch-t-focusBorder: #3b4252;--ch-t-tab-activeBackground: #3b4252;--ch-t-tab-activeForeground: #d8dee9;--ch-t-tab-inactiveBackground: #2e3440;--ch-t-tab-inactiveForeground: #d8dee966;--ch-t-tab-border: #3b425200;--ch-t-tab-activeBorder: #88c0d000;--ch-t-editorGroup-border: #3b425201;--ch-t-editorGroupHeader-tabsBackground: #2e3440;--ch-t-editorLineNumber-foreground: #4c566a;--ch-t-input-background: #3b4252;--ch-t-input-foreground: #d8dee9;--ch-t-input-border: #3b4252;--ch-t-icon-foreground: #C5C5C5;--ch-t-sideBar-background: #2e3440;--ch-t-sideBar-foreground: #d8dee9;--ch-t-sideBar-border: #3b4252;--ch-t-list-activeSelectionBackground: #88c0d0;--ch-t-list-activeSelectionForeground: #2e3440;--ch-t-list-hoverBackground: #3b4252;--ch-t-list-hoverForeground: #eceff4; }</style>\n<!-- -->\n<!-- -->\n<!-- -->\n<p>In our last blog post, we implemented <a href=\"/blog/wasm-vm-part-1\">a WebAssembly interpreter that can do arithmetic and comparison operations</a> on <code>i32</code> values.</p>\n<p>In this post, we’re going to extend the interpreter: rather than only operating on literal values, we’ll add support for local and global variables. And just like before, we’re writing everything from scratch, in JavaScript — no libraries or frameworks.</p>\n<p>If that sounds like your cup of tea, keep reading.</p>\n<div class=\"wftgu-callout\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"️-setup\">🛠️ Setup<a href=\"#️-setup\" class=\"hash-link\" aria-label=\"Direct link to 🛠️ Setup\" title=\"Direct link to 🛠️ Setup\">​</a></h2><p>If you’d like to follow along by running the code yourself, then you should start by creating a file named <code>wasm-vm-02.js</code>. All the snippets we show will be added to that file. Let’s start with a few imports:</p><div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>import assert from 'node:assert';</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>import test from 'node:test';</span></div></div><br></code></div></div></div></div><p>We’ll also import a few definitions from the <a href=\"/blog/wasm-vm-part-1\">previous post</a>:</p><div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>import {i32, instrImm, VM} from './wasm-vm-01.js';</span></div></div><br></code></div></div></div></div><p>If you don’t have that code in a file already, you can go to the end of <a href=\"/blog/wasm-vm-part-1#checkpoint\">that post</a> and click “Copy up to here” under the last snippet (or copy it from <a href=\"https://stackblitz.com/github/wasmgroundup/blog-code?file=wasm-vm-01.js\" target=\"_blank\" rel=\"noopener noreferrer\">StackBlitz</a>) then paste it into a file called <code>wasm-vm-01.js</code>.</p><p>Okay, let’s get started.</p></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\"><p>See <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#indices%E2%91%A0\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;2.5.1 Indices</a> of the <em>WebAssembly Core Specification</em>.</p></aside><p>An important thing to know about WebAssembly is that inside a module, things aren’t referenced by name, but by <em>index</em>. Things of different types are grouped together in different <em>index spaces</em>, a fancy name for what is basically just an array.</p></div>\n<p>Local variables are no exception; they’re stored in the locals index space, which maps from an index to a value of one of the core Wasm types (<code>i32</code>, <code>i64</code>, <code>f32</code>, <code>f64</code>).</p>\n<p>To keep the code short and simple we’re going to extend the <code>VM</code> class and add a locals index space. It’s not really the “right way” to do it, but in the next post we’ll rewrite this class anyways, so let’s go with a simple solution:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class FlatFrame extends VM {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor(instructions, locals = []) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    super(instructions);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.locals = locals;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\"><p>As we explained in the previous post, some instructions have static parameters, which the spec refers to as <em>immediate arguments</em> (or just <em>immediates</em>).</p><p>You might also want to review our description of the <a href=\"/blog/wasm-vm-part-1#the-webassembly-execution-model\">WebAssembly execution model</a>.</p></aside><p>Now that we have a locals space, we need some instructions to work with it. Let’s define the first instruction: <code>local.get</code>, which takes the index as an immediate and pushes the variable’s current value onto the stack:</p></div>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const local = {};</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>local.get = (index) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instrImm(0x20, 'local.get', index, (vm, index) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    assertLocalIndexInRange(vm.locals, index);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const value = vm.locals[index];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    vm.push(value);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><br></code></div></div></div></div>\n<p>Since we don’t have an instruction to set local variables yet, let’s test it by creating a <code>FlatFrame</code> with a local variable initialized to <code>100</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('local.get', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new FlatFrame([local.get(0)], [i32(100)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(100)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>In the instruction implementation above we check if the index is in bounds (i.e., it refers to an existing variable). Let’s extract that check into a separate function that can be reused for the other instructions:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function assertLocalIndexInRange(locals, index) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  if (index &lt; 0 || index &gt;= locals.length) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    throw new Error(`Invalid local index: ${index}`);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Now we can implement the <code>local.set</code> instruction, which also takes the variable’s index as an immediate. But unlike <code>local.get</code>, it also requires a dynamic argument: the new value of the variable. It gets this value from the stack.</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>local.set = (index) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instrImm(0x21, 'local.set', index, (vm, index) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    assertLocalIndexInRange(vm.locals, index);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const value = vm.pop();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    vm.locals[index] = value;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><br></code></div></div></div></div>\n<p>Let’s test it:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('local.set', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new FlatFrame([i32.const(42), local.set(0)], [i32(0)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, []);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(vm.locals[0].value, 42);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">The name comes from the <a href=\"https://en.wikipedia.org/wiki/Piping_and_plumbing_fitting#Tee\" target=\"_blank\" rel=\"noopener noreferrer\">tee pipe fitting</a></aside><div><p>The <code>local.set</code> instruction pops the value from the stack, but sometimes you want to set a local variable and leave the original value in the stack too. For example, many languages (including JavaScript) support assignment <em>expressions</em>, which assign a value to a variable, but also produce a value:</p><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let a = 0;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>function plusOne(val) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return val + 1;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>plusOne(a = 6); // Returns 7</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log(a); // Prints '6'</span></div></div><br></code></div></div><p>That’s one example of where the <code>local.tee</code> instruction comes in handy. We can easily implement it, by using <code>peek()</code> to read the top-of-stack value without popping it:</p><div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>local.tee = (index) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instrImm(0x22, 'local.tee', index, (vm, index) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    assertLocalIndexInRange(vm.locals, index);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const value = vm.peek();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    vm.locals[index] = value;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><br></code></div></div></div></div></div></div>\n<p>Okay, let’s test it:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('local.tee', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new FlatFrame([i32.const(42), local.tee(0)], [i32(0)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(vm.locals[0].value, 42);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(42)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>Now we have local variables, but local to what? Right now they are the only kinds of variables we have.</p>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">For a better visual intuition of how code maps to call stack frames, see the <a href=\"https://pythontutor.com/javascript.html#mode=edit\" target=\"_blank\" rel=\"noopener noreferrer\">JavaScript version of Python Tutor</a>.</aside><p>The answer, of course, is that they’re local to a function — or, to be more specific, they’re local to a specific <em>call stack frame</em>. Hence the name of our <code>FlatFrame</code> class: we’re modeling a virtual machine with a single frame, rather than a stack of frames.</p></div>\n<p>In WebAssembly, like JavaScript, functions are grouped into <em>modules</em>. To make our locals truly <em>local</em>, we can put the <code>FlatFrame</code> inside a module instance. For now, we’ll still assume a single function and a single stack frame, stored in a <code>currentFrame</code> property.</p>\n<p>And then we can also add support for global variables, in a property named…you guessed it, <code>globals</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class MonoInstance {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor(instructions, locals = [], globals = []) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.currentFrame = new FlatFrame(instructions, locals);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.globals = globals;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>The <code>MonoInstance</code> constructor takes the same arguments as <code>FlatFrame</code>, plus an extra one for <code>globals</code>.</p>\n<p>To make all our previous instruction definitions still work and keep the new ones as simple as possible, let’s\n“wrap and forward” some of the <code>FlatFrame</code> methods in our new class:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class MonoInstance {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  get locals() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.currentFrame.locals;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  get stack() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.currentFrame.stack;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  push(value) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.currentFrame.push(value);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  pop() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.currentFrame.pop();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  peek() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.currentFrame.peek();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  popI32() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.currentFrame.popI32();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  popType(T) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.currentFrame.popType(T);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  step() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const {currentFrame} = this;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const instruction = currentFrame.instructions[currentFrame.pc];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    instruction.eval(this);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    currentFrame.pc += 1;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>To make sure everything works, we can run the <code>local.tee</code> test from above on our new <code>MonoInstance</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('local.tee for MonoInstance', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new MonoInstance([i32.const(42), local.tee(0)], [i32(0)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(vm.locals[0].value, 42);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(42)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>If you compare the tests, you’ll see that the only thing that changed is what class we instantiate for the <code>vm</code>.</p>\n<p>With local instructions completed we can now move to the global instructions. Let’s start with <code>global.get</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const global = {};</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>global.get = (index) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instrImm(0x23, 'global.get', index, (vm, index) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    assertGlobalIndexInRange(vm.globals, index);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const value = vm.globals[index];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    vm.push(value);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><br></code></div></div></div></div>\n<p>You may notice that it’s almost identical to <code>local.get</code>. The only difference is what index space it uses.</p>\n<p>As before, we define a function to check that the index is valid:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function assertGlobalIndexInRange(globals, index) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  if (index &lt; 0 || index &gt;= globals.length) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    throw new Error(`Invalid global index: ${index}`);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Okay — let’s test our new instruction:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('global.get', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instance = new MonoInstance([global.get(0)], [], [i32(100)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instance.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(instance.peek().value, 100);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>Finally, we’ll define <code>global.set</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>global.set = (index) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instrImm(0x24, 'global.set', index, (vm, index) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    assertGlobalIndexInRange(vm.globals, index);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const value = vm.pop();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    vm.globals[index] = value;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><br></code></div></div></div></div>\n<p>…and test it too:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('global.set', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instance = new MonoInstance(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    [i32.const(42), global.set(0)],</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    [],</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    [i32(0)],</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instance.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instance.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(instance.globals[0].value, 42);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>And we are done! 🎉</p>\n<div class=\"dinkus_ujf0\"></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">Did you notice that there’s no <code>global.tee</code>? Here’s an explanation for why it’s not defined: <a href=\"https://github.com/WebAssembly/design/issues/915\" target=\"_blank\" rel=\"noopener noreferrer\">WebAssembly/design/issues/915</a>.</aside><p>Not bad — with a fairly small amount of code, we were able to extend our WebAssembly VM to support local and global variables. In the next post, we’ll extend it further, and add support for multiple functions.</p></div>\n<p>Let’s finish up by exporting everything we’ll need in the next post:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-02.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-02.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>export * from './wasm-vm-01.js';</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>export {local, global};</span></div></div><br></code></div></div></div></div>\n<div class=\"copyBar_I1_d\"><div style=\"display:flex;justify-content:space-between;align-items:center\"><button class=\"copyBtn_aLB2\" data-runnable-path=\"/code/blog/wasm-vm-02/wasm-vm-02.js\" data-repo-file-path=\"wasm-vm-02.js\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" class=\"copyIcon_Lbpe\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6\" d=\"M8 16H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2m-6 12h8a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-8a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2z\"></path></svg>Copy up to here</button><div>🏁 <!-- -->wasm-vm-02<!-- -->.js</div><a href=\"https://stackblitz.com/github/wasmgroundup/blog-code?file=wasm-vm-02.js\" target=\"_blank\"><svg width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"inline ml-1 mr-1 relative top-[1px]\"><path d=\"M20.5 2h-5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h3.09L7.65 14.94a.5.5 0 0 0 0 .71l.7.7a.498.498 0 0 0 .71 0L20 5.41V8.5a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-5A1.5 1.5 0 0 0 20.5 2Z\" fill=\"currentColor\"></path><path d=\"M21.5 13h-1a.5.5 0 0 0-.5.5V20H4V4h6.5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-7A1.5 1.5 0 0 0 2 3.5v17A1.5 1.5 0 0 0 3.5 22h17a1.5 1.5 0 0 0 1.5-1.5v-7a.5.5 0 0 0-.5-.5Z\" fill=\"currentColor\"></path></svg>Open in StackBlitz</a></div></div>\n<hr>\n<div class=\"wftgu-callout\"><p>If you enjoyed this post, you should check out <a href=\"https://wasmgroundup.com\" target=\"_blank\" rel=\"noopener noreferrer\">WebAssembly from the Ground Up</a>&nbsp;—&nbsp;an online book to learn Wasm by building a simple compiler in JavaScript.</p><p>If you’d like to be notified when we publish the next post, you can sign up for our mailing list, where we share periodic updates on the book and interesting WebAssembly tidbits.</p><div class=\"subscribeForm_LiSh\"><form><div class=\"formRow_bEeJ\"><input type=\"email\" name=\"email\" required=\"\" placeholder=\"Your email address\" class=\"formInput_My36\"><button type=\"submit\" class=\"subscribeButton_cuQC\">Subscribe</button></div><div style=\"position:absolute;left:-5000px\" aria-hidden=\"true\"><input name=\"a_password\" tabindex=\"-1\" autocomplete=\"off\"></div></form></div></div>",
            "url": "https://wasmgroundup.com/blog/wasm-vm-part-2",
            "title": "A WebAssembly interpreter (Part 2)",
            "summary": "Adding local and global variable support to our Wasm Interpreter",
            "date_modified": "2026-02-20T00:00:00.000Z",
            "tags": [
                "code"
            ]
        },
        {
            "id": "https://wasmgroundup.com/blog/wasm-vm-part-1",
            "content_html": "<style>[data-ch-theme=\"nord\"] {  --ch-t-colorScheme: dark;--ch-t-foreground: #d8dee9ff;--ch-t-background: #2e3440ff;--ch-t-lighter-inlineBackground: #2e3440e6;--ch-t-editor-background: #2e3440;--ch-t-editor-foreground: #d8dee9;--ch-t-editor-lineHighlightBackground: #3b4252;--ch-t-editor-rangeHighlightBackground: #434c5e52;--ch-t-editor-infoForeground: #3794FF;--ch-t-editor-selectionBackground: #434c5ecc;--ch-t-focusBorder: #3b4252;--ch-t-tab-activeBackground: #3b4252;--ch-t-tab-activeForeground: #d8dee9;--ch-t-tab-inactiveBackground: #2e3440;--ch-t-tab-inactiveForeground: #d8dee966;--ch-t-tab-border: #3b425200;--ch-t-tab-activeBorder: #88c0d000;--ch-t-editorGroup-border: #3b425201;--ch-t-editorGroupHeader-tabsBackground: #2e3440;--ch-t-editorLineNumber-foreground: #4c566a;--ch-t-input-background: #3b4252;--ch-t-input-foreground: #d8dee9;--ch-t-input-border: #3b4252;--ch-t-icon-foreground: #C5C5C5;--ch-t-sideBar-background: #2e3440;--ch-t-sideBar-foreground: #d8dee9;--ch-t-sideBar-border: #3b4252;--ch-t-list-activeSelectionBackground: #88c0d0;--ch-t-list-activeSelectionForeground: #2e3440;--ch-t-list-hoverBackground: #3b4252;--ch-t-list-hoverForeground: #eceff4; }</style>\n<!-- -->\n<!-- -->\n<!-- -->\n<!-- -->\n<p>In our last blog post, we showed you <a href=\"/blog/wasm-compiler-in-a-tweet/\">a WebAssembly compiler that fits in a tweet</a>. In just 192 bytes of JavaScript, it takes arithmetic expression written in postfix notation — like <code>20 2 * 2 +</code> — and produces a Wasm module with a single function which evaluates the expression.</p>\n<p>In this post, we’re going to look at WebAssembly from another angle: we’re going to write an interpreter for Wasm from scratch. So, rather than executing the Wasm module with Node.js, we’re going to examine the instructions and evaluate them ourselves.</p>\n<p>At the end of this post we will have a bytecode interpreter (or <em>virtual machine</em>) that can evaluate arithmetic and comparison expressions like <code>2 * 3 + 4 == 10</code>:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const vm = new VM([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.const(3),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.const(2),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.mul,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.const(4),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.add,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.const(10),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  i32.eq,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>]);</span></div></div><br></code></div></div>\n<p>Does this sound fun? If so, read on!</p>\n<div class=\"wftgu-callout\"><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-webassembly-execution-model\">The WebAssembly execution model<a href=\"#the-webassembly-execution-model\" class=\"hash-link\" aria-label=\"Direct link to The WebAssembly execution model\" title=\"Direct link to The WebAssembly execution model\">​</a></h3><p>If you’re not intimately familiar with WebAssembly and its execution model, a bit of background might be useful before we jump in.</p><p>At its heart, WebAssembly (or <em>Wasm</em> for short) is a low-level bytecode format. It’s mainly intended as a compilation target, allowing languages like C++, Go, and Rust to be executed in the browser at near-native speed.</p><p>Normally, to execute a Wasm module, a WebAssembly engine (like your browser) would translate the module’s bytecode to native machine code and execute <em>that</em>. But many engines include an interpreter — either in addition to a compiler, or instead of one (see <a href=\"https://github.com/wasm3/wasm3/\" target=\"_blank\" rel=\"noopener noreferrer\">wasm3</a>).</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"stack-machines\">Stack machines<a href=\"#stack-machines\" class=\"hash-link\" aria-label=\"Direct link to Stack machines\" title=\"Direct link to Stack machines\">​</a></h4><p>Like many other bytecode formats, WebAssembly is based on a <a href=\"https://en.wikipedia.org/wiki/Stack_machine\" target=\"_blank\" rel=\"noopener noreferrer\">stack machine</a> model. This means that many instructions implicitly operate on a stack of values: popping their arguments, and pushing the result back to the stack.</p><p>Here’s a diagram showing the state of an interpreter, before and after it executes an <code>i32.mul</code> instruction:</p><img src=\"/assets/images/stack-machine-65c2eec4f028d884288fcffd59e9f271.png\" style=\"border:0;margin:-2em 0\"><p>Note that a fully-featured Wasm interpreter contains other state, but we won’t be dealing with any of that in this post.</p></div>\n<p>Alright, are you ready to start implementing the interpreter?</p>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">You can get all the code together or explore it in StackBlitz — see the checkpoint at the end of the post.</aside><div><p>If you’d like to follow along by running the code yourself, then you should start by creating a file named <code>wasm-vm-01.js</code>. All of the snippets we show will be added to that file. Let’s start with a few imports:</p><div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>import assert from 'node:assert';</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>import test from 'node:test';</span></div></div><br></code></div></div></div></div></div></div>\n<p>The first step is to decide how we’ll represent the instructions. Usually an interpreter would operate on a binary stream of data, and then parse (or <em>decode</em>) the data into a more structured form.</p>\n<p>In this post, we’ll skip the decoding and just work directly with the structured representation. Let’s start by defining a class to represent instructions:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class Instruction {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor(opcode, name, evalFn) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.opcode = opcode;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.name = name;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.evalFn = evalFn;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  eval(vm) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.evalFn(vm);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>And here’s a function to create instances in a compact way:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const instr = (opcode, name, evalFn) =&gt; new Instruction(opcode, name, evalFn);</span></div></div><br></code></div></div></div></div>\n<p>The <code>Instruction</code> fields are:</p>\n<ul>\n<li><code>opcode</code>: a numeric value that identifies the instruction in the binary encoding.</li>\n<li><code>name</code>: a human-readable instruction identifier.</li>\n<li><code>evalFn</code>: a function that takes a <code>vm</code> argument and operates on it according to the instruction’s specification.</li>\n</ul>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\"><p>The <code>nop</code> instruction is defined in <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-instr-control\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;2.4.5 Control Instructions</a> of the <em>WebAssembly Core Specification</em>.</p><p>You’ll notice that we back up many of our explanations with reference to the relevant part of the spec. One of our goals is to convince you that the spec is a valuable resource that’s worth getting familiar with!</p></aside><p>Following the spirit of our book, let’s start with the minimum viable instruction — the one that does nothing, <code>nop</code>:</p></div>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const nop = instr(0x01, 'nop', (_vm) =&gt; {});</span></div></div><br></code></div></div></div></div>\n<p>How do we test something that “does nothing”? We can evaluate it passing <code>null</code> as the <code>vm</code> argument to make sure it doesn’t use it:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('nop does nothing', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = null;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  nop.eval(vm);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">See <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#values%E2%91%A2\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;4.2.1 Values</a> and <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#stack%E2%91%A0\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;4.2.12 Stack</a>.</aside><p>Other than doing nothing, WebAssembly deals mostly with numbers and operations on them. As we mentioned at earlier, these operations manipulate values on a stack.</p></div>\n<p>The next thing we’ll need is a way to push values to the stack. For that, we’ll need to support instructions that take a value as a parameter, which the spec refers to as an <em>immediate argument</em> (or just <em>immediate</em>, for short).</p>\n<p>Let’s define a new subtype of instruction that takes an immediate value:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class InstructionImm extends Instruction {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor(opcode, name, immediate, evalFn) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    super(opcode, name, evalFn);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.immediate = immediate;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  eval(vm) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.evalFn(vm, this.immediate);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>And a function to create them:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const instrImm = (opcode, name, immediate, evalFn) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  new InstructionImm(opcode, name, immediate, evalFn);</span></div></div><br></code></div></div></div></div>\n<p>Notice that for this type of instructions <code>evalFn</code> takes an extra argument, the immediate value.</p>\n<p>The last piece we need is a way to represent numbers. An interesting thing about WebAssembly is that it’s a <em>typed</em> assembly language. WebAssembly 1.0 has four primitive value types:</p>\n<ul>\n<li><code>i32</code></li>\n<li><code>i64</code></li>\n<li><code>f32</code></li>\n<li><code>f64</code></li>\n</ul>\n<p>We’ll start by defining the <code>i32</code> type and add support for the rest later:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class I32 {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor(value) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.value = value;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>const i32 = (value) =&gt; new I32(value);</span></div></div><br></code></div></div></div></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">Defined in <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#-tmathsfhrefsyntax-instr-numericmathsfconstc%E2%91%A0\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;4.4.1.1</a>.</aside><p>We are now ready to define our second instruction, <code>i32.const</code>, which takes an immediate (the value to be pushed to the stack).</p></div>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.const = (value) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instrImm(0x41, 'i32.const', value, (vm, imm) =&gt; vm.push(i32(imm)));</span></div></div><br></code></div></div></div></div>\n<p>Since <code>nop</code> didn’t use the <code>vm</code> argument, we never defined a type for it. But <code>i32.const</code> pushes a value to the stack, so let’s define a <code>Stack</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class Stack {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.items = [];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  push(value) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.items.push(value);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Now we can test that our new instruction does the right thing:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('i32.const pushes an I32 to the stack', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new Stack();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instr = i32.const(42);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  instr.eval(vm);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.items, [i32(42)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>Notice that we pass an instance of <code>Stack</code> as the <code>vm</code> argument — it’s sufficient for now.</p>\n<p>With values in the stack it makes sense to want to pop them too. The <code>drop</code> instruction does that:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const drop = instr(0x1a, 'drop', (vm) =&gt; vm.pop());</span></div></div><br></code></div></div></div></div>\n<p>To complete the implementation, we need to add the <code>pop</code> method to <code>Stack</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class Stack {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  pop() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.items.pop();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Let’s test that we can push and pop values:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('drop pops from the stack', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new Stack();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  run(vm, [i32.const(42), drop]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.items, []);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>Here’s the definition of the <code>run</code> function to evaluate a list of instructions:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function run(vm, instructions) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  for (const instr of instructions) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    instr.eval(vm);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Ok, the test should now pass!</p>\n<p>Pushing and popping values is a decent start, but what else can we do with them? We can add them together, with the <code>i32.add</code> instruction:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.add = instr(0x6a, 'i32.add', (vm) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const c2 = vm.popI32();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const c1 = vm.popI32();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const c = i32(c1.value + c2.value);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.push(c);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>To evaluate <code>i32.add</code>:</p>\n<ul>\n<li>Pop two values from the stack, asserting that they are of type <code>i32</code></li>\n<li>Add them</li>\n<li>Push the result back to the stack.</li>\n</ul>\n<p>This instruction uses a new method: <code>popI32</code>. Let’s extend <code>Stack</code> with methods to operate on the top value and check its type:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class Stack {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  peek() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.items.at(-1);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  topIsOfType(Class) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.peek() instanceof Class;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assertTopIsOfType(Class) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (!this.topIsOfType(Class)) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      throw new Error(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>        `Expected ${Class.name} on top of stack, got ${this.peek()}`,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  popType(T) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.assertTopIsOfType(T);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.pop();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  popI32() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.popType(I32);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Now we have everything we need to test <code>i32.add</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('i32.add pops two I32s and pushes their sum', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new Stack();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  run(vm, [i32.const(42), i32.const(23), i32.add]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.items, [i32(65)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>The <code>run</code> function runs the instructions to completion, but it would be nice to step and inspect the intermediate states too.</p>\n<p>Let’s create a proper <code>VM</code> class with a stack, a list of instructions, and a <em>program counter</em> (<code>pc</code>) to keep track of the current instruction. We’ll add methods to manipulate the stack and step through instructions:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>class VM {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  constructor(instructions) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.stack = new Stack();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.instructions = instructions;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.pc = 0;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  push(value) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.stack.push(value);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  pop() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.stack.pop();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  peek() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.stack.peek();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  popI32() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.stack.popI32();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  popType(T) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    return this.stack.popType(T);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  step() {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const instruction = this.instructions[this.pc];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    instruction.eval(this);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    this.pc += 1;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  steps(count) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    for (let i = 0; i &lt; count; i++) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      this.step();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Notice that most of the methods delegate to the <code>stack</code>. An interesting one is the <code>step</code> method: it fetches the current instruction, evaluates it, and increments the program counter.</p>\n<p>Let’s add two numbers again, but this time, we’ll check the stack after each step:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('VM executes two i32.const and an i32.add', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new VM([i32.const(42), i32.const(23), i32.add]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step(); // Eval `i32.const 42`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(42)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step(); // Eval `i32.const 23`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(42), i32(23)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.step(); // Eval `i32.add`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(42 + 23)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>This test should pass.</p>\n<p>A natural next step is to implement the rest of the arithmetic operations that, like <code>i32.add</code>:</p>\n<ol>\n<li>Take two values from the stack</li>\n<li>Perform an operation on them</li>\n<li>Push the result back.</li>\n</ol>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">Defined in <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#-tmathsfhrefsyntax-binopmathitbinop%E2%91%A0\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;4.4.1.3</a>.</aside><p>In the spec, these operations are collectively called <code>t.binop</code>, where <code>t</code> is the type — in our case, <code>i32</code>.</p></div>\n<p>Let’s create a function to define them:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function binop(opcode, name, fn) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return instr(opcode, name, (vm) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    // 2. Pop the value t.const c2 from the stack.</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const c2 = vm.popI32();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    // 3. Pop the value t.const c1 from the stack.</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const c1 = vm.popI32();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    // 4.1 Let c be a possible result of computing binop&lt;t&gt;(c1, c2).</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const c = i32(fn(c1.value, c2.value));</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    // 4.2 Push the value t.const c to the stack.</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    vm.push(c);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>Let’s try it by defining <code>i32.sub</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.sub = binop(0x6b, 'i32.sub', (c1, c2) =&gt; c1 - c2);</span></div></div><br></code></div></div></div></div>\n<p>Testing binops is as regular as defining them. Let’s create a function for testing binary operations:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function checkBinop(c1, c2, instruction, expected) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new VM([i32.const(c1), i32.const(c2), instruction]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.steps(3);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(expected)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>And use it to test <code>i32.sub</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('i32.sub', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  checkBinop(42, 23, i32.sub, 42 - 23);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">The <code>_s</code> stands for <em>signed</em>. In WebAssembly, there’s no distinction between signed and unsigned integer types. Instead, there are separate signed and unsigned <em>instructions</em> — but only for instructions where the difference is relevant, as with division.</aside><p>Now let’s define <code>i32.mul</code> and <code>i32.div_s</code>, and a small test for each:</p></div>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.mul = binop(0x6c, 'i32.mul', (c1, c2) =&gt; c1 * c2);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.div_s = binop(0x6d, 'i32.div_s', (c1, c2) =&gt; Math.trunc(c1 / c2));</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>test('i32.mul', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  checkBinop(42, 23, i32.mul, 42 * 23);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>test('i32.div_s', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  checkBinop(42, 23, i32.div_s, Math.trunc(42 / 23));</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  checkBinop(2, -3, i32.div_s, Math.trunc(2 / -3));</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">Defined in <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#syntax-relop\" target=\"_blank\" rel=\"noopener noreferrer\">§&nbsp;2.4.1 Numeric Instructions</a>.</aside><p>After arithmetic operations a good next step is to implement comparison operations, known in the spec as <em>relops</em>.</p></div>\n<p>A <code>relop</code> is like a <code>binop</code> in that it takes two values from the stack and pushes one back. The difference is that the value it pushes back is either <code>i32(1)</code> to represent <code>true</code> or <code>i32(0)</code> to represent <code>false</code>.</p>\n<p>Let’s create a function to define them:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function relop(opcode, name, fn) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return binop(opcode, name, (c1, c2) =&gt; (fn(c1, c2) ? 1 : 0));</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div></div></div>\n<p>We are reusing <code>binop</code> and wrapping the provided <code>fn</code> in a function that returns <code>1</code> if the call to <code>fn(c1, c2)</code> is <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Truthy\" target=\"_blank\" rel=\"noopener noreferrer\">truthy</a> and <code>0</code> if not.</p>\n<p>Let’s define our first relop, <code>i32.eq</code> which checks if two <code>i32</code>s are equal:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.eq = relop(0x46, 'i32.eq', (c1, c2) =&gt; c1 === c2);</span></div></div><br></code></div></div></div></div>\n<p>And test it using <code>checkBinop</code>:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('i32.eq', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  checkBinop(42, 23, i32.eq, 0);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  checkBinop(23, 23, i32.eq, 1);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div>\n<p>We can now implement the remaining relops:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.ne = relop(0x47, 'i32.ne', (c1, c2) =&gt; c1 !== c2);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.lt_s = relop(0x48, 'i32.lt_s', (c1, c2) =&gt; c1 &lt; c2);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.gt_s = relop(0x4a, 'i32.gt_s', (c1, c2) =&gt; c1 &gt; c2);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.le_s = relop(0x4c, 'i32.le_s', (c1, c2) =&gt; c1 &lt;= c2);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>i32.ge_s = relop(0x4e, 'i32.ge_s', (c1, c2) =&gt; c1 &gt;= c2);</span></div></div><br></code></div></div></div></div>\n<p>And that’s it for now! All the tests should be passing. We now have an interpreter that does arithmetic and comparison operations using a stack — let’s try it with a complex expression:</p>\n<div class=\"sidenoteParent_qfZB\"><aside class=\"sidenote_lvaF\">This is a code checkpoint (notice the &nbsp;🏁&nbsp; icon). Try clicking “Open in StackBlitz” to see the code and run the tests. For more information, see <a href=\"/book/about-the-code\">About the code</a>.</aside><div><div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>test('complex expression works', () =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  // 2 * 3 + 4 == 10;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const vm = new VM([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.const(3),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.const(2),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.mul,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.const(4),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.add,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.const(10),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    i32.eq,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  ]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.steps(3); // Eval `i32.const 3; i32.const 2; i32.mul`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(vm.stack.peek().value, 6);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.steps(2); // Eval `i32.const 4; i32.add`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.strictEqual(vm.stack.peek().value, 10);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  vm.steps(2); // Eval `i32.const 10; i32.eq`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  assert.deepStrictEqual(vm.stack.items, [i32(1)]);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>});</span></div></div><br></code></div></div></div></div><div class=\"copyBar_I1_d\"><div style=\"display:flex;justify-content:space-between;align-items:center\"><button class=\"copyBtn_aLB2\" data-runnable-path=\"/code/blog/wasm-vm-01/wasm-vm-01.js\" data-repo-file-path=\"wasm-vm-01.js\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" class=\"copyIcon_Lbpe\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6\" d=\"M8 16H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2m-6 12h8a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-8a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2z\"></path></svg>Copy up to here</button><div>🏁 <!-- -->wasm-vm-01<!-- -->.js</div><a href=\"https://stackblitz.com/github/wasmgroundup/blog-code?file=wasm-vm-01.js\" target=\"_blank\"><svg width=\"1em\" height=\"1em\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" class=\"inline ml-1 mr-1 relative top-[1px]\"><path d=\"M20.5 2h-5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h3.09L7.65 14.94a.5.5 0 0 0 0 .71l.7.7a.498.498 0 0 0 .71 0L20 5.41V8.5a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-5A1.5 1.5 0 0 0 20.5 2Z\" fill=\"currentColor\"></path><path d=\"M21.5 13h-1a.5.5 0 0 0-.5.5V20H4V4h6.5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-7A1.5 1.5 0 0 0 2 3.5v17A1.5 1.5 0 0 0 3.5 22h17a1.5 1.5 0 0 0 1.5-1.5v-7a.5.5 0 0 0-.5-.5Z\" fill=\"currentColor\"></path></svg>Open in StackBlitz</a></div></div></div></div>\n<a href=\"#checkpoint\"><div class=\"dinkus_ujf0\"></div></a>\n<p>That’s a pretty good start! We’ve got the core of a simple interpreter for WebAssembly 1.0 instructions. In the next post, we’ll extend this interpreter, and add support for instructions that operate on local variables.</p>\n<p>Let’s finish things off by exporting all the things that we’ll use in the next post:</p>\n<div class=\"ch-codegroup not-prose\" data-ch-theme=\"nord\"><div class=\"ch-editor-frame\"><div class=\"ch-frame-title-bar\"><div class=\"ch-frame-buttons\"><div class=\"ch-frame-button ch-frame-button-left\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-middle\"></div><div class=\"ch-frame-button-space\"></div><div class=\"ch-frame-button ch-frame-button-right\"></div></div><div title=\"wasm-vm-01.js\" data-ch-tab=\"north\" data-active=\"true\" class=\"ch-editor-tab\"><div><span style=\"opacity:0.5\"></span>wasm-vm-01.js</div></div><div style=\"flex:1;min-width:0.8em\"></div><button type=\"button\" title=\"Copy code\" class=\"ch-editor-button\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.6px\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"></path></svg></button></div><div data-ch-panel=\"north\" style=\"flex-grow:1;overflow:hidden\"><div style=\"height:100%\" class=\"ch-code-wrapper\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>export {I32, drop, i32, instr, instrImm, nop, Stack, VM};</span></div></div><br></code></div></div></div></div>\n<p>Catch you next time!</p>\n<hr>\n<i>Thanks to Job van der Zwan and Karl Weber for providing helpful feedback on this post.<br><br></i>\n<div class=\"wftgu-callout\"><p>If you enjoyed this post, you should check out <a href=\"https://wasmgroundup.com\" target=\"_blank\" rel=\"noopener noreferrer\">WebAssembly from the Ground Up</a>&nbsp;—&nbsp;an online book to learn Wasm by building a simple compiler in JavaScript.</p><p>If you’d like to be notified when we publish the next post, you can sign up for our mailing list, where we share periodic updates on the book and interesting WebAssembly tidbits.</p><div class=\"subscribeForm_LiSh\"><form><div class=\"formRow_bEeJ\"><input type=\"email\" name=\"email\" required=\"\" placeholder=\"Your email address\" class=\"formInput_My36\"><button type=\"submit\" class=\"subscribeButton_cuQC\">Subscribe</button></div><div style=\"position:absolute;left:-5000px\" aria-hidden=\"true\"><input name=\"a_password\" tabindex=\"-1\" autocomplete=\"off\"></div></form></div></div>",
            "url": "https://wasmgroundup.com/blog/wasm-vm-part-1",
            "title": "A WebAssembly interpreter (Part 1)",
            "summary": "Implementing a Wasm Interpreter to explore its design and semantics",
            "date_modified": "2025-11-20T00:00:00.000Z",
            "tags": [
                "code"
            ]
        },
        {
            "id": "https://wasmgroundup.com/blog/wasm-compiler-in-a-tweet",
            "content_html": "<style>[data-ch-theme=\"nord\"] {  --ch-t-colorScheme: dark;--ch-t-foreground: #d8dee9ff;--ch-t-background: #2e3440ff;--ch-t-lighter-inlineBackground: #2e3440e6;--ch-t-editor-background: #2e3440;--ch-t-editor-foreground: #d8dee9;--ch-t-editor-lineHighlightBackground: #3b4252;--ch-t-editor-rangeHighlightBackground: #434c5e52;--ch-t-editor-infoForeground: #3794FF;--ch-t-editor-selectionBackground: #434c5ecc;--ch-t-focusBorder: #3b4252;--ch-t-tab-activeBackground: #3b4252;--ch-t-tab-activeForeground: #d8dee9;--ch-t-tab-inactiveBackground: #2e3440;--ch-t-tab-inactiveForeground: #d8dee966;--ch-t-tab-border: #3b425200;--ch-t-tab-activeBorder: #88c0d000;--ch-t-editorGroup-border: #3b425201;--ch-t-editorGroupHeader-tabsBackground: #2e3440;--ch-t-editorLineNumber-foreground: #4c566a;--ch-t-input-background: #3b4252;--ch-t-input-foreground: #d8dee9;--ch-t-input-border: #3b4252;--ch-t-icon-foreground: #C5C5C5;--ch-t-sideBar-background: #2e3440;--ch-t-sideBar-foreground: #d8dee9;--ch-t-sideBar-border: #3b4252;--ch-t-list-activeSelectionBackground: #88c0d0;--ch-t-list-activeSelectionForeground: #2e3440;--ch-t-list-hoverBackground: #3b4252;--ch-t-list-hoverForeground: #eceff4; }</style>\n<!-- -->\n<!-- -->\n<!-- -->\n<!-- -->\n<h2>Introduction</h2>\n<p>One of the initial explorations that started this book was how small and simple a compile-to-WebAssembly language implemented in JavaScript could be. Our first “WebAssembly compiler in a tweet” was 269 bytes; since then, we’ve managed to whittle it down to a measly 192 bytes.</p>\n<p>The final result is a compiler that takes an arithmetic expression — written in reverse polish notation — and compiles it down to a valid WebAssembly module. That module exports a single function which returns the result of the original arithmetic expression. Here it is:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c=(b,l)=&gt;WebAssembly.instantiate(new Int8Array(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>[,97,115,109,1,,,,1,5,1,96,,1,127,3,2,1,,7,4,1,,,,10,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>l=(b=b.split` `.flatMap(t=&gt;t&gt;-1?[65,t]:107+'-*/'.indexOf(t)))</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>.length+4,1,l-2,,...b,11]))</span></div></div><br></code></div></div>\n<p>And here’s an example of how you can use it:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c('11 11 1 - + 4 * 2 /')).instance.exports['']()</span></div></div><br></code></div></div>\n<p>But this is not just a clever trick — if you take the time to understand what this code does, you’ll learn a surprising amount about WebAssembly! In the rest of the post, we’ll explain how it all works by de-obfuscating the code one step at a time.</p>\n<p>You can play with the code in this post here: <a href=\"https://stackblitz.com/edit/rpn-to-wasm-js-compiler?file=index.js\" target=\"_blank\" rel=\"noopener noreferrer\">stackblitz.com/edit/rpn-to-wasm-js-compiler</a>.</p>\n<h2>Format</h2>\n<p>The first thing we can do to make it more readable is to format it:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c1 = (b, l) =&gt;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      , 97, 115, 109, 1, , , , 1, 5, 1, 96, , 1, 127, 3, 2, 1, , 7, 4, 1, , , , 10,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      (l = (b = b.split` `.flatMap(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>            (t) =&gt; t &gt; -1 ? [65, t] : 107 + \"-*/\".indexOf(t)</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>           )).length + 4),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1, l - 2, , ...b, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ])</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><br></code></div></div>\n<p>While it’s still pretty unreadable, now we can at least identify different parts of the code.</p>\n<p>At a high level, what we’re doing is ‘parsing’ the expression in a very simple way, turning it into the appropriate Wasm bytecode,\nand then hand-crafting the bytes for a single-function module.</p>\n<p>In a more complex compiler you would probably use a library to generate the WebAssembly module and compile the expressions but our main metric\nhere is code size so we write the bytes directly in an array.</p>\n<h2>Remove Assignment Expression</h2>\n<p>The first trick to undo is the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment\" target=\"_blank\" rel=\"noopener noreferrer\">assignment expression</a>.</p>\n<p>In JavaScript the assignment operator is an expression. This means that it generates a result after evaluating, as you can see in the following examples:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let a, b;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log('a', a = 42);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>a = b = 43;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log('b', b);</span></div></div><br></code></div></div>\n<p>The code above will output:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>a 42</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>b 43</span></div></div><br></code></div></div>\n<p>This is because <code>a = 42</code> assigns <code>42</code> to <code>a</code> and the whole assignment expression evaluates to the value being assigned.</p>\n<p>In <code>a = b = 43</code>, we assign the result of evaluating <code>b = 43</code> to <code>a</code>. This equivalent expression may be easier to understand: <code>a = (b = 43)</code>.</p>\n<p>In our code, we use this trick to reuse variables and update their value in places where\nwe can also use the value being assigned. It also allows us to have our compiler in a single expression, avoiding the need for curly braces, semicolons and return statements.</p>\n<p>To undo it, we turn the body of our function into a block and do each assignment on its own line:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c2 = (b, l) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  b = b.split` `.flatMap(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    (t) =&gt; (t &gt; -1 ? [65, t] : 107 + \"-*/\".indexOf(t))</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  l = b.length + 4;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      , 97, 115, 109, 1, , , , 1, 5, 1, 96, , 1, 127, 3, 2, 1, , 7, 4, 1, , , ,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, l, 1, l - 2, , ...b, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<h2>Undo Variable Tricks</h2>\n<p>Now the assignments are easier to identify but the meaning of variables and function\narguments are still hard to understand. Let’s fix that by undoing a couple of variable tricks.</p>\n<p>The first step is to stop using single letter variables, and to use more descriptive names instead. The next step is to stop reusing variables: for example, <code>b</code> initially holds the code to compile, but once we don’t need that any more we reuse it to hold the bytecode instructions.</p>\n<p>To undo this we are going to introduce a new <code>instrs</code> variable and rename <code>b</code> to <code>code</code>. We’ll also rename <code>l</code> to <code>len</code>. This variable contains a value that is close to the number of bytecodes.</p>\n<p>By declaring <code>l</code> in the body we can remove it from the function argument’s list. We did this\nas a trick to avoid the need to declare it with <code>let</code> or <code>const</code>, saving some bytes and the need for a function body.</p>\n<p>The trick works by adding unused arguments at the end of the function argument list and using them as local variables. Our compiler function expects a single argument with the code; <code>l</code> is there for us to use since we don’t expect the caller to provide any value for it.</p>\n<p>Here’s the code without this trick:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c3 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split` `.flatMap(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    (t) =&gt; (t &gt; -1 ? [65, t] : 107 + \"-*/\".indexOf(t))</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length + 4;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      , 97, 115, 109, 1, , , , 1, 5, 1, 96, , 1, 127, 3, 2, 1, , 7, 4, 1, , , ,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, len, 1, len - 2, , ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<h2>Add Missing Zeros</h2>\n<p>If you look at the array in our code, you may notice that there are many commas followed by another comma instead of a value. This syntax defines “sparse arrays”. Here’s an example:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const a1 = [,,];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log(a1.length); // Output: 2</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log(a1); // Output: [ &lt;2 empty items&gt; ]</span></div></div><br></code></div></div>\n<p>Which is equivalent to:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const a2 = new Array(2);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log(a2.length); // Output: 2</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>console.log(a2); // Output: [ &lt;2 empty items&gt; ]</span></div></div><br></code></div></div>\n<p>We use this syntactic trick to save one byte each time we need a <code>0</code> to appear in the array. This works because <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Typed_arrays\" target=\"_blank\" rel=\"noopener noreferrer\">Typed Arrays</a> coerce all array items to numbers, and an “empty item” will be converted to 0:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>new Int8Array([0, null, undefined,,0])</span></div></div><br></code></div></div>\n<p>which produces:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>Int8Array(5) [ 0, 0, 0, 0, 0 ]</span></div></div><br></code></div></div>\n<p>Let’s undo this trick by adding all the zeroes back:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c4 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split` `.flatMap(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    (t) =&gt; (t &gt; -1 ? [65, t] : 107 + \"-*/\".indexOf(t))</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length + 4;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, len, 1, len - 2, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<h2>Remove Extra 4 bytes on Length Definition</h2>\n<p>In our code, we have a variable <code>len</code> that contains a number that is close to the number\nof bytecodes in the compiled expression, but not exactly the same:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length + 4;</span></div></div><br></code></div></div>\n<p>In the WebAssembly module we need to use the number of bytes in the function body (the expression to evaluate) in two places:</p>\n<ul>\n<li>To define the code section’s length</li>\n<li>To define the function body’s length</li>\n</ul>\n<p>Since there’s only one function in the code section both values are similar:</p>\n<ul>\n<li>The section takes two extra bytes (section identifier and number of code entries)</li>\n<li>The function body takes another two bytes (number of locals and <code>end</code> instruction)</li>\n</ul>\n<p>To avoid writing <code>b.length</code> twice we assign to <code>l</code> the value of <code>b.length + 4</code> in the place where we need the code section byte count\nand then calculate <code>l - 2</code> (<code>b.length + 2</code>) where we need the function body byte count.</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>[</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  l=(b=b.split` `.flatMap(t=&gt;t&gt;-1?[65,t]:107+'-*/'.indexOf(t))).length+4,1,l-2</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  ...</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>]</span></div></div><br></code></div></div>\n<p>This is all a trick to avoid having to write <code>b.length</code> twice.</p>\n<p>let’s assign the length to <code>len</code> and calculate the right value in each place:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c5 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split` `.flatMap(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    (t) =&gt; (t &gt; -1 ? [65, t] : 107 + \"-*/\".indexOf(t))</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<h2>Remove String Template Literal Instead of Function Call</h2>\n<p>The next trick to undo is <code>code.split` `</code>. In this case, we use the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates\" target=\"_blank\" rel=\"noopener noreferrer\">Tagged Template</a> feature of <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\" target=\"_blank\" rel=\"noopener noreferrer\">String Template Literals</a>.</p>\n<p>Let’s see how it works by creating a simple tagged template that turns the string to uppercase:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>function upper(s) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return s[0].toUpperCase();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>}</span></div></div><br></code></div></div>\n<p>And use it:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>upper`Hello, World!`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; \"HELLO, WORLD!\"</span></div></div><br></code></div></div>\n<p>As you can see, the first argument to the tagged template function is an array. Luckily for us, the first argument of <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split#separator\" target=\"_blank\" rel=\"noopener noreferrer\">String.prototype.split</a> is handled in the following way:</p>\n<blockquote>\n<p>All values that are not undefined or objects with a <code>[Symbol.split]()</code> method are coerced to strings.</p>\n</blockquote>\n<p>And coercing an array with one string in it is the same as the string itself:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>[\"hello\"].toString()</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; \"hello\"</span></div></div><br></code></div></div>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>[\" \"].toString()</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; \" \"</span></div></div><br></code></div></div>\n<p>Since the function we want to call takes a single string argument, we can use it as a tagged template and save the parentheses in the function call.</p>\n<p>Let’s write it as a function call instead:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c6 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split(' ').flatMap(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    (t) =&gt; (t &gt; -1 ? [65, t] : 107 + \"-*/\".indexOf(t))</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<h2>Remove the Ternary Operator</h2>\n<p>Next, let’s undo the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator\" target=\"_blank\" rel=\"noopener noreferrer\">Ternary Operator</a> and turn it into an <em>if</em> statement.</p>\n<p>The ternary operator has expressions on each branch saving us the <code>return</code> statements. Here’s what the code looks like when we use an <em>if</em> statement instead:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c7 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split(\" \").flatMap((t) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (t &gt; -1) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return [65, t];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    } else {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return 107 + \"-*/\".indexOf(t);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<h2>Remove Number Check With Coercion</h2>\n<p>The next trick to undo is the one present twice in the following code:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (t &gt; -1) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return [65, t];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><br></code></div></div>\n<p>First we use coercion in <code>t &gt; -1</code> to check if the token <code>t</code> is a string representing a\npositive number. Then we use coercion again in <code>[65, t]</code> to let JavaScript turn <code>t</code> into a <code>Number</code> in the <code>Int8Array</code>:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>new Int8Array([65, '42'])</span></div></div><br></code></div></div>\n<p>The code above evaluates to:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>Int8Array(2) [ 65, 42 ]</span></div></div><br></code></div></div>\n<p>Let’s write the parsing and checking explicitly:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c8 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split(\" \").flatMap((t) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const num = parseInt(t, 10);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (Number.isFinite(num)) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return [65, num];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    } else {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return 107 + \"-*/\".indexOf(t);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<p>The semantics of our compiler change a little bit here. The original version will only accept\npositive integers as input; if you want a negative number you have to subtract from zero: <code>0 - 1</code> to get <code>-1</code>. The new version allows negative numbers since it checks with <code>Number.isFinite(num)</code> instead of <code>t &gt; -1</code>.</p>\n<h2>Remove indexOf -1 Trick</h2>\n<p>The next trick is in the <em>else</em> branch:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>      return 107 + \"-*/\".indexOf(t);</span></div></div><br></code></div></div>\n<p>Our calculator compiler only accepts four arithmetic operations: <code>+</code>, <code>-</code>, <code>*</code>, and <code>/</code>. But\nin the code above you can only see three: <code>-*/</code> and a magical number: <code>107</code>. Here’s how it works — these are the bytecode numbers for arithmetic operations in WebAssembly:</p>\n<ul>\n<li><code>+</code>: <code>106</code></li>\n<li><code>-</code>: <code>107</code></li>\n<li><code>*</code>: <code>108</code></li>\n<li><code>/</code>: <code>109</code></li>\n</ul>\n<p>We only enter this branch if the token <code>t</code> is not a number, which means it can only be\none of the arithmetic operators above. So, given a single character which is one of those four operators, we want to produce the appropriate opcode.</p>\n<p>We <em>could</em> have written <code>106 + \"+-*/\".indexOf(t)</code>. That is, we find the symbol’s index in the string:</p>\n<ul>\n<li><code>+</code>: <code>0</code></li>\n<li><code>-</code>: <code>1</code></li>\n<li><code>*</code>: <code>2</code></li>\n<li><code>/</code>: <code>3</code></li>\n</ul>\n<p>…and add <code>106</code> to it to get the bytecode number. But when <code>t</code> is not in the string, <code>\"+-*/\"</code> <code>indexOf</code> returns <code>-1</code>. We can use that to our advantage, and treat <code>-1</code> to mean “plus or any other token”:</p>\n<ul>\n<li><code>+</code>: <code>-1</code> (any other token will be <code>-1</code> too)</li>\n<li><code>-</code>: <code>0</code></li>\n<li><code>*</code>: <code>1</code></li>\n<li><code>/</code>: <code>2</code></li>\n</ul>\n<p>And that’s why we add <code>107</code> instead of <code>106</code>. Let’s undo the <code>-1</code> trick:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>let c9 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split(\" \").flatMap((t) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const num = parseInt(t, 10);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (Number.isFinite(num)) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return [65, num];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    } else {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return 106 + \"+-*/\".indexOf(t);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<p>Here again the semantics change a little bit. Before, if the token <code>t</code> wasn’t found, the expression would evaluate to <code>107 + -1</code> which would map to an addition. Now it will evaluate to <code>106 + -1</code> which will map to bytecode <code>105</code> which is the <a href=\"https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/Numeric/Population_count\" target=\"_blank\" rel=\"noopener noreferrer\"><code>popcnt</code></a> instruction.</p>\n<p>But don’t worry, we’ll fix it in the next step.</p>\n<h2>Remove indexOf Trick</h2>\n<p>After explaining how the <code>indexOf</code> trick works and removing the <code>-1</code> part, let’s\ngo ahead and remove the trick completely. To do it we are going to create an object that maps from an arithmetic operation token to its bytecode:</p>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const OP_TO_BYTECODE = {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"+\": 106,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"-\": 107,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"*\": 108,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"/\": 109,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>let c10 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split(\" \").flatMap((t) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const num = parseInt(t, 10);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (Number.isFinite(num)) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return [65, num];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    } else {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return OP_TO_BYTECODE[t] ?? 106;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 4, 1, 0, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<p>To keep the initial semantics, if the token is not a valid operation we return the bytecode for <code>+</code>: in <code>OP_TO_BYTECODE[t] ?? 106</code>.</p>\n<h2>Remove the Empty Export Name</h2>\n<p>From the usage example at the beginning of the post, you may have noticed that the exported\nfunction’s name is the empty string:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c('11 11 1 - + 4 * 2 /')).instance.exports['']()</span></div></div><br></code></div></div>\n<p>We did this to save us the bytes needed to specify the export name,\nbut also to save an extra byte/character in the code because with the length of the export name being <code>0</code>\nwe can use the sparse array syntax to leave an empty spot in the WebAssembly module array.</p>\n<p>To revert this trick we are going to name the exported function as <code>a</code>, which in UTF-8 is the byte <code>97</code>:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; new TextEncoder().encode('a')[0]</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>97</span></div></div><br></code></div></div>\n<section class=\"ch-section\"><div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>const OP_TO_BYTECODE = {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"+\": 106,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"-\": 107,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"*\": 108,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  \"/\": 109,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>let c11 = (code) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const instrs = code.split(\" \").flatMap((t) =&gt; {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    const num = parseInt(t, 10);</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    if (Number.isFinite(num)) {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return [65, num];</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    } else {</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      return OP_TO_BYTECODE[t] ?? 106;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    }</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  });</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  const len = instrs.length;</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  return WebAssembly.instantiate(</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    new Int8Array([</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 127, 3, 2, 1, 0, 7, 5, 1, 1, 97, 0, 0,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, 4 + len, 1, 2 + len, 0, ...instrs, 11</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]),</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>  );</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>};</span></div></div><br></code></div></div></section>\n<p>We can now call it with a nicer name:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c11('11 11 1 - + 4 * 2 /')).instance.exports.a()</span></div></div><br></code></div></div>\n<h2>Implicit Design Decisions</h2>\n<p>Our initial implementation only supported positive numbers, but that’s not the only number restriction in our compiler.</p>\n<p>To keep WebAssembly modules as small as possible, numbers are encoded using a variable-length encoding algorithm called <a href=\"https://en.wikipedia.org/wiki/LEB128\" target=\"_blank\" rel=\"noopener noreferrer\">LEB128</a>. You can tell we are not implementing the whole algorithm by looking at the part of the code that encodes numbers: <code>[65,t]</code>. We’re assuming the number being encoded fits in 7 bits, the shortest possible LEB128 representation.</p>\n<p>Let’s try the limits of our implementation:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c('63')).instance.exports['']();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; 63</span></div></div><br></code></div></div>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c('64')).instance.exports['']();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; -64</span></div></div><br></code></div></div>\n<p>This means the only numbers that will be parsed correctly are from <code>0</code> to <code>63</code>.</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c('127')).instance.exports['']();</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>&gt; -1</span></div></div><br></code></div></div>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>(await c('128')).instance.exports['']();</span></div></div><br></code></div></div>\n<p>Fails with:</p>\n<blockquote>\n<p>Uncaught CompileError: WebAssembly.instantiate(): Compiling function #0 failed: function body must end with “end” opcode @+33</p>\n</blockquote>\n<p>In the last one we went over the 7 bits and the module was rejected during validation.</p>\n<p>Explaining and implementing LEB128 takes a lot of text and code. If you want to read more\nabout it we have a whole deep dive on LEB128 in <a href=\"https://wasmgroundup.com/\" target=\"_blank\" rel=\"noopener noreferrer\">our book</a>.</p>\n<h2>A Trick that Almost Worked</h2>\n<p>During the code golfing phase I had a literal shower thought but sadly it didn’t work.</p>\n<p>The idea was to simplify <code>106 + \"+-*/\".indexOf(t)</code> by using the UTF-8 character code plus an offset like this: <code>63 + t.charCodeAt()</code> and saving 3 bytes in the process. The reason it didn’t work is that the characters <code>+-*/</code> don’t appear in the same order in UTF-8 and WebAssembly bytecode.</p>\n<h2>Explaining the Numbers in the Array</h2>\n<p>The last part to expand/explain is the array of numbers used to build the WebAssembly module.</p>\n<p>It takes a big part of a <a href=\"https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/\" target=\"_blank\" rel=\"noopener noreferrer\">specification</a> to explain every byte in the array, but here’s a commented version that should give you a high level idea of what each part does:</p>\n<div class=\"ch-codeblock not-prose\" data-ch-theme=\"nord\"><div class=\"ch-code-wrapper ch-code\" data-ch-measured=\"false\"><code class=\"ch-code-scroll-parent\"><br><div><div style=\"display:inline-block;margin-left:16px\"><span>    [</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // Wasm module magic number '\\0asm'</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      [0, 97, 115, 109],</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // Wasm version 1.0</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      [1, 0, 0, 0],</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // ----- type section -----</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1, // Section identifier</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      5, // Section size in bytes</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1, // Number of entries that follow</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // type section - entry 0</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      96, // Type `function`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0,  // Number of parameters</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1,  // Number of return values</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      127, // return type i32</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // ----- function section -----</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      3, // Section identifier</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      2, // Section size in bytes</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1, // Number of entries that follow</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // function section - entry 0</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, // Index of the type section entry</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // ----- export section -----</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      7, // Section identifier</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      5, // Section size in bytes</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1, // Number of entries that follow</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // export section - entry 0</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1,  // Name size in bytes</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      97, // String as utf-8 bytes for 'a'</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0,  // Export type `function`</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0,  // Function Index</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // ----- code section -----</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      10, // Section identifier</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      4 + len, // Section size in bytes</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      1, // Number of entries that follow</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span></span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      // code section - entry 0</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      2 + len, // Entry size in bytes</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      0, // Number of local variables</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      ...instrs,</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>      11, // `end` instruction</span></div></div><div><div style=\"display:inline-block;margin-left:16px\"><span>    ]</span></div></div><br></code></div></div>\n<h2>Conclusion</h2>\n<p>There you go! We’ve turned a rather opaque 192-byte snippet into something that’s almost readable. And in the process, you hopefully learned a little bit about WebAssembly.</p>\n<p>If we dropped the size restrictions, there are lots of things we might want to improve in this compiler: handle numbers greater than 127, add nicer syntax, add support for conditionals, loops, etc. If you’re interested in what that might look like, I encourage you to check out our book <a href=\"https://wasmgroundup.com/\" target=\"_blank\" rel=\"noopener noreferrer\">WebAssembly from the Ground Up</a>. You’ll learn the ins and outs of WebAssembly by writing a real compiler for a simple programming language. It’s a lot of fun!</p>\n<p>Special thanks to <a href=\"https://bsky.app/profile/orthoplex.bsky.social\" target=\"_blank\" rel=\"noopener noreferrer\">lexi</a> for contributing some of the tricks used above.</p>",
            "url": "https://wasmgroundup.com/blog/wasm-compiler-in-a-tweet",
            "title": "A WebAssembly compiler that fits in a tweet",
            "summary": "Starting with a 192-byte one-liner that implements a Reverse Polish Notation arithmetic compiler, we'll work backward to transform it into readable JavaScript by removing one code golf trick at a time",
            "date_modified": "2025-01-24T00:00:00.000Z",
            "tags": [
                "code"
            ]
        }
    ]
}