Conversation

2024
Week 11 (??)
Language: Textual Wasm

Confidence level: Medium high

PREV WEEK: https://mastodon.social/@mcc/114524697727786286
RULES: https://mastodon.social/@mcc/113676228091546556

I'm now doing the puzzles slightly out of order.

"Wasm" is a bytecode VM that runs in web browsers as an alternative to JavaScript. Oddly for a modern VM, it's designed to be targeted by C.

A thing I've heard repeatedly claimed, both excitedly and with eye-rolls, is that Wasm's human readable form is "a LISP". I want to know what that means.

1
0
0

On initially cracking open the MDN guide¹, *so far* it kind of seems like it only means "It's structured as an S-expression". That's a bit disappointing. It's an understandable decision, as S-expressions make parsing easy— I've used this shortcut myself— but also, it seems this is preserving *only* the part of LISP people hate most. What do people complain about the most? What do LISP heads complain the most about other people complaining about? *It's the parentheses*.

¹ https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Understanding_the_text_format

7
0
0

@mcc A curious thing I've noticed: a lot of people using modern high-level parsing rigs simply have no clue that there's an AST being mapped to Concrete Executable Tree. They would benefit from some time working with a language where these things are more explicit.

0
0
0

It's not actually clear what file extension you're supposed to use for textual wasm files. The Sublime plugin claims this language is called "WAST" and as such suggests .wast. I guess I'll go with that. MDN calls the format "WAT". I wonder if there's subtle commentary there.

It's also a *little* nonobvious how to assemble and run this stuff. Wasm was originally intended to be loaded from JavaScript. Both MDN and help on mastodon² suggest a toolset named WABT.

² https://mastodon.social/@mcc/114581638222018210

4
0
0
@mcc It's good because it means that if you're lazy you can parse it with READ, as in https://codeberg.org/noisytoot/wasm2ps.

Also Lisp's parentheses are only bad if you don't have a properly configured editor. If you do have a properly configured editor, they allow for structural editing (e.g. paredit).
0
0
0

@mcc I think the Guile-based Hoot system has a good way to work with WASM-as-text

1
0
0

@krans You say "guile" and I hear "thing I will never get to run"

0
0
0

I check out Wabt and try to build it, and… oh. There's… no cmake, on this machine. I hadn't needed it until now. Oh! What a peaceful and innocent life I have lead, since first I set up this laptop.

2
0
0

It seems I can assemble³ with:

wat2wasm source.wast -o exec.wasm

And execute with either, from wabt:

wasm-interp simple.wasm -r add -a i32:1 -a i32:2

Or if I have wasmtime:

wasmtime --invoke add simple.wasm 1 2

I don't know why I'd bother with wasmtime if I have wasm-interp. Is wasmtime faster?

³ Assume simple.wasm contains, code from MDN:

(module
(func $add (param $lhs i32) (param $rhs i32) (result i32)
local.get $lhs
local.get $rhs
i32.add)
(export "add" (func $add))
)

2
0
0

@mcc iirc, wast is a bespoke extension of wat

1
0
0

@mcc Thing that strikes me from your screenshot is the use of "bash". Give fish a try:

sudo apt-get install fish
fish

If you give it a decent try over 24 hours, my guess is you'll never want to go back to bash.

1
1
0

@mcc wasmtime generates code that is competitive with C on computation-heavy workloads. wasm-interp is just the reference interpreter

1
0
0

@mcc also wasmtime has features like "bindings" and "real multithreading" and "knows how to do various types of I/O", basically everything you need to run a production app (or yowasp.org)

1
0
0

@mcc I know this is just a funny toy challenge for you, which is actually super cool and nice as a learning tool, but… is there genuinely any reason to go through this giant headache for a weird stripped down language designed with the browser in mind, that got retrofitted and slapped on an OS with extra APIs later?

like actually why do people build code that compiles to this? the only benefit I’ve seen touted is “cross platform code” which isn’t true yet because the tooling sucks, or “sandboxed execution” which can be accomplished WAY more reliably with any number of other methods

I really cannot grasp why people suffer through WASI specifications and standalone wasm binaries on their systems outside of toy examples for learning lol

2
0
0

@froge 1. I am going through it because I am designing a programming language, so I want to understand this one.

2. The reason to use wasm at the command line (as opposed to the browser) is that sandboxed execution absolutely cannot be achieved more easily through other methods. It's just that there are methods where people are so used to the associated pain they've begun to forget it is there.

1
0
0

@mcc you should know: there's sorta two formats in WAT. the specification seems to refer to both as s-expressions, which is very confusing to me because one is very clearly not an s-expression.

check out this part of the spec: https://webassembly.github.io/spec/core/text/instructions.html#folded-instructions

1
0
0

@mcc my understanding (which is not perfect) is that the ones without parentheses operate on an implicit stack, while the ones with parentheses make this explicit.

for example, an add instruction can be written two ways:

i64.const 413
i64.const 612
i64.add
(i64.add
(i64.const 413)
(i64.const 612))
1
0
0

@mcc you will see this mixed and matched in files at various levels.

1
0
0

@jj *head in hands* I approve of this as a programmer, but this appears to completely obviate any advantages that choosing S-expressions or something like it for the wasm textual format might have otherwise gifted

0
0
0

@mcc to whatever code you want; you can add host functions written in Rust that your Wasm code can call. i don't think wasm-interp offers anything like this (I believe it just exists to validate the spec)

1
0
0
@mcc "The WebAssembly validation rules ensure the stack matches exactly: if you declare a (result f32), then the stack must contain exactly one f32 at the end. If there is no result type, the stack must be empty." wouldn't this run into the halting problem?
1
0
0

@shironeko C++ shows us that you can produce a language where the halting problem precludes establishing whether or not a particular bit of code is a valid example of the language, and the market will still accept it

0
0
0

@whitequark Can I run wasmtime in a mode where only things that would be legal in a browser are legal?

1
0
0

@mcc @whitequark thats effectively the default if you aren’t doing any wasi imports iirc

1
0
0

@endocrimes @whitequark Say I'm running some MDN sample code https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Understanding_the_text_format#importing_functions_from_javascript which loads and invokes console.log. Neither wasm-interp nor wasmtime are able to locate console.log. What is the most reasonable / most browser-compatible way to print a string from code running in one or either of these programs?

2
1
0

@froge @mcc "cross platform code" and "sandboxed execution" are correct answers. the third correct answer is that it's nice to target.

1
0
0

@jj @froge Also people forget how difficult intra-platform compatibility is. Yesterday I built a Rust program on my Debian 2025 machine and attempted to run it on a Ubuntu 2020 machine. Those are very nearly the same operating system and both were on x86_64. They were not similar *enough* to be able to run a networked Rust executable.

0
0
0

@mcc first off: nothing but browser and node.js will provide you with the ability to interoperate with JS

second: if you want specifically `console.log` as an import, you'll probably have to write some adapter code to run it with wasmtime, because you are now the embedder who is in control (and you get all the drawbacks of having total control)

wasmtime isn't a browser thing and it makes no attempt to emulate a browser environment

1
0
0

@mcc @endocrimes @whitequark that wasm example also doesn’t have access to console.log except for the fact that it’s explicitly passed as an interface when instantiating the module—it’s not a browser API, it’s an API specific to that example.

0
0
0

@mcc I suppose so, I’m not sure if the tooling and environment are at a point where the sandboxing works to a reliable level yet, v8 constantly has sandbox escape bugs and stuff even though it’s the most popular runtime, but maybe I’ll just give it a few more years and see how things go

I went poking around to see if the old issues I found about memory shrinking were solved, but apparently they weren’t yet, so the wasm spec still has no way to free memory correctly to the host when you’re done with it, which is a really silly and funny limitation to deal with too… it still totally feels like a mish-mash or stuff duct-taped into a spec to me (which might be why the tooling sucks lol)

this memory issue just straight up prevents deployment to mobiles for most use-cases, and has been a spec issue for 5+ years easily, but I guess on desktop everyone ignores it so there isn’t huge focus on getting it fixed

https://github.com/WebAssembly/design/issues/1397

1
0
0

@froge I thought we were talking about WASI. Is that applicable to the mobile environment?

1
0
0

@mcc third: the browser doesn't provide anything by default either, you have to tell it to provide a `console.log` export explicitly

anyway, i would probably use the wasmtime python bindings and add the functions you want via Python. there are examples on doing so, ping me if you have issues

1
0
0

@whitequark Okay. Imagine it doesn't need to be console.log— what is the shortest path to printing a string to STDOUT from inside of a binary running in wasmtime, through any method? Or is the shortest path actually python?

2
0
0

@mcc it is actually, I believe android implements WASI to allow your apps to run natively there last time I checked, but the memory model makes it super annoying to use for a lot of devs still

1
0
0

@froge Well, the inability to return memory is a problem for me also actually, but your original question was "why do people use this?" and if people are using it on the platform where nonreturnable memory is not a problem and not using it on the platform where nonreturnable memory is a problem then that seems to answer your question

1
0
0

@mcc consider that wasmtime doesn't have a conception of strings (strings aren't first-class in wasm), so any print method would involve accessing the linear memory directly and parsing your specific string format (is it null terminated? length prefixed? length passed explicitly? etc)

this means you have to build an embedding, yeah

3
0
0

@mcc yeah I get that, to be clear I think this is a super great thing for the browser at the moment, and if WASI and the native side works out some of these annoying edge cases it would be great there too, I’m just somewhat skeptical of the support for it in the current state on native platforms… maybe people don’t know about these issues? or just don’t care? it just raises some red flags for me when I see it though

but that could totally just be because my usecases don’t match up with what other people need a lot of the time tbh 😄

1
0
0

@froge What I mean is that the server and command line environments, which I understood to be the primary use case for WASI, I see uptake of WASM in.

0
0
0

@whitequark Okay. Let me take a different tack.

I would like to be able to examine the state of my program while it is running, so I can tell if it is running correctly. Usually, printfs are the easiest way to do this.

Do either wasmtime, or the wabt utilities, to your knowledge offer an interpreter with a non-painful memory debugger?

1
0
0

@mcc
- you can debug wasmtime-generated code with gdb or lldb or what other native code debugger you have. however, this requires you to emit DWARF debug information, which i think doesn't even survive a roundtrip through the text format, so it's probably off the table
- you can examine the linear memory yourself
- you can do printfs

that's about it

1
0
0

@whitequark @mcc doesn't wasmtime implement WASI including wasi-cli, which gives you a way of printing output without having to write your own embedding?

1
0
0

@unlambda @mcc it does but I'm not sure this would be easier for Andi to learn than learning how to embed it (especially given that browsers don't have WASI)

1
0
0

@whitequark @unlambda I am not running in a browser, I am running at the command line as a learning project. I did initially ask about compatibility with a browser but I am willing to drop that goal if it allows me to avoid bringing in a dependency on bespoke code in a second language such as Python

1
0
0

@mcc @unlambda if you want do prints while not bringing in bespoke code in some host language, your only option is to learn WASI and use those APIs. this means that for browser, you'll have to use https://github.com/YoWASP/runtime-js/blob/develop/lib/wasi-virt.js to run the same code

0
0
0

@whitequark "you can examine the linear memory yourself"

Can I? How?

This project is based entirely around the state of a single array of integers, because I intentionally though that would be the easiest sort of project to do in handwritten wasm. If I can identify a location in memory and read it as sequential bytes/integers that's good enough. Doing this with wasm-interp sounds less scary as I assume the VM memory is better contained than wasmtime (which I think creates a native executable?)

1
0
0

@tedmielczarek @mcc it does but if I start telling Andi about components she'll probably give up because of the dazzling level of complexity those involve (and how difficult it is to write those manually in the text format)

1
0
0

@whitequark @tedmielczarek The output of this program is a single integer, which is easy to export by just writing a function that returns an integer, so killing debuggability is an option D:

1
0
0

@mcc wasmtime is a JIT compiler; it abstracts away the details of how it runs your code though (also creating a static native executable isn't really an option with it)

I'm not aware of a way to get wasmtime to dump the memory contents via the command line. it's trivial if you embed it though

0
0
0

@mcc @jj compilers and interpreters share a test suite. wast is the test file format

1
0
0

@simon @jj of the test suite?

how does this format differ from "textual wasm" aka "webassembly text format"?

1
0
0

@mcc @jj one wat file is one webassembly module in text format. one wast file has similar structure but can contain multiple modules and metadata about how to run them and how to tell if the test passed

1
1
0

@unlambda @whitequark @tedmielczarek okay this is actually well compact enough for my purposes.

Here's a question: say I want to create two "variants" from a single source file, so I can define a log function twice, once based on WASI as here and once based on console.log. is there a ecosystem-appropriate way to do here or am I looking at brutal possibilities like actually running the C preprocessor?

2
0
0

@mcc @whitequark @tedmielczarek I suspect there might be a simpler way to do this with wasi-cli, but I haven't found an example yet, and I'm cooking dinner: https://github.com/WebAssembly/wasi-cli

0
0
0

@mcc @unlambda @tedmielczarek nobody except for spec authors and engine test suite implementers actually uses the text format, so... the latter

0
0
0

@mcc @unlambda @whitequark the most "sensible" way would probably involve defining a logging interface that could be provided by either a WASI component or a browser component, but that is assuredly Too Much for what you're doing.

0
0
0

@mcc @whitequark wasm has no ambient capabilities. it can’t do any kind of io by default. in order to do anything with effects outside the module’s own memory/tables you need to explicitly pass in functions

1
0
0

@mcc @whitequark there’s a standardized set of vaguely unixy functions a runtime can provide called WASI, and wasmtime probably has a way to use those? but you would need to manually implement them (or find an existing implementation) for them to work in the browser.

1
0
0

@easrng @mcc wasmtime automatically provides WASI APIs if your file imports them, no action required

to use WASI in the browser is actually surprisingly complicated, but i have an implementation that will work https://github.com/YoWASP/runtime-js/blob/develop/lib/wasi-virt.js

1
0
0

@whitequark @mcc wait it’s. taking the wasi preview1 binary and transpiling it to wasi preview2 and then building a js wrapper on top of that?? cursed

1
0
0

@easrng @mcc this is the only practical way to ship some of the applications i have >.>

1
0
0

@whitequark @mcc I would’ve just implemented the wasi preview1 functions directly lol

2
0
0

@easrng @mcc I considered that and concluded that it makes me want to slit my wrists

1
0
0

@easrng @mcc I entirely avoided shipping anything that requires WASI on top of JS until jco became available because it spared me from having to touch linear memory directly and then having to validate these implementations and then fix the bugs in the wrappers and fuck all that

also, jco handles async instantiation for me which is very handy for yosys (>8MB binary)

1
0
0

@easrng @whitequark Sorry I'm not sure what I'm looking at here

Anything that makes wasm memory go nonlinear is of interest to me because (this was being discussed in another fork of the thread I think) I want mmap MAP_ANONYMOUS!!!

0
0
0

@whitequark @mcc doesn’t the component model wrapper add a lot of allocations when it translates to js objects and back?

0
0
0
code inside, long
Show content

@mcc @whitequark new and exciting ways to obfuscate your javascript

(module
 (type $0 (func (param externref)))
 (type $1 (func (param externref) (result externref)))
 (type $2 (func (param i32 i32 i32 i32 i32 i32) (result externref)))
 (type $3 (func (param i32) (result externref)))
 (type $4 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result externref)))
 (type $5 (func (param i32 i32 i32 i32) (result externref)))
 (type $6 (func (result externref)))
 (type $7 (func (param i32 i32 i32 i32 i32 i32 i32) (result externref)))
 (type $8 (func (param externref externref i32)))
 (type $9 (func (param externref externref externref)))
 (type $10 (func (param externref externref) (result externref)))
 (type $11 (func (param externref externref externref) (result externref)))
 (import "Object" "prototype" (global $gimport$0 externref))
 (import "globalThis" "undefined" (global $gimport$1 externref))
 (import "String" "prototype" (global $gimport$2 externref))
 (import "Array" "prototype" (global $gimport$3 externref))
 (import "String" "fromCharCode" (func $fimport$0 (param i32 i32 i32 i32 i32 i32) (result externref)))
 (import "String" "fromCharCode" (func $fimport$1 (param i32) (result externref)))
 (import "String" "fromCharCode" (func $fimport$2 (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) (result externref)))
 (import "String" "fromCharCode" (func $fimport$3 (param i32 i32 i32 i32) (result externref)))
 (import "String" "fromCharCode" (func $fimport$4 (result externref)))
 (import "String" "fromCharCode" (func $fimport$5 (param i32 i32 i32 i32 i32 i32 i32) (result externref)))
 (import "console" "log" (func $fimport$6 (param externref)))
 (import "Reflect" "getPrototypeOf" (func $fimport$7 (param externref) (result externref)))
 (import "Reflect" "set" (func $fimport$8 (param externref externref i32)))
 (import "Reflect" "set" (func $fimport$9 (param externref externref externref)))
 (import "Reflect" "get" (func $fimport$10 (param externref externref) (result externref)))
 (import "Reflect" "apply" (func $fimport$11 (param externref externref externref) (result externref)))
 (import "Object" "create" (func $fimport$12 (param externref) (result externref)))
 (global $global$0 (mut externref) (global.get $gimport$1))
 (global $global$1 (mut externref) (global.get $gimport$1))
 (global $global$2 (mut externref) (global.get $gimport$1))
 (global $global$3 (mut i32) (i32.const 1))
 (export "greet" (func $0))
 (func $0 (param $0 externref)
  (local $1 externref)
  (if
   (global.get $global$3)
   (then
    (global.set $global$3
     (i32.const 0)
    )
    (global.set $global$0
     (call $fimport$7
      (global.get $gimport$0)
     )
    )
    (drop
     (call $fimport$10
      (global.get $gimport$2)
      (call $fimport$2
       (i32.const 99)
       (i32.const 104)
       (i32.const 97)
       (i32.const 114)
       (i32.const 67)
       (i32.const 111)
       (i32.const 100)
       (i32.const 101)
       (i32.const 65)
       (i32.const 116)
      )
     )
    )
    (global.set $global$1
     (call $fimport$10
      (global.get $gimport$3)
      (call $fimport$3
       (i32.const 106)
       (i32.const 111)
       (i32.const 105)
       (i32.const 110)
      )
     )
    )
    (global.set $global$2
     (call $fimport$4)
    )
   )
  )
  (call $fimport$8
   (local.tee $1
    (call $fimport$12
     (global.get $global$0)
    )
   )
   (call $fimport$0
    (i32.const 108)
    (i32.const 101)
    (i32.const 110)
    (i32.const 103)
    (i32.const 116)
    (i32.const 104)
   )
   (i32.const 3)
  )
  (call $fimport$9
   (local.get $1)
   (call $fimport$1
    (i32.const 48)
   )
   (call $fimport$5
    (i32.const 72)
    (i32.const 101)
    (i32.const 108)
    (i32.const 108)
    (i32.const 111)
    (i32.const 44)
    (i32.const 32)
   )
  )
  (call $fimport$9
   (local.get $1)
   (call $fimport$1
    (i32.const 49)
   )
   (local.get $0)
  )
  (call $fimport$9
   (local.get $1)
   (call $fimport$1
    (i32.const 50)
   )
   (call $fimport$1
    (i32.const 33)
   )
  )
  (call $fimport$8
   (local.tee $0
    (call $fimport$12
     (global.get $global$0)
    )
   )
   (call $fimport$0
    (i32.const 108)
    (i32.const 101)
    (i32.const 110)
    (i32.const 103)
    (i32.const 116)
    (i32.const 104)
   )
   (i32.const 1)
  )
  (call $fimport$9
   (local.get $0)
   (call $fimport$1
    (i32.const 48)
   )
   (global.get $global$2)
  )
  (call $fimport$6
   (call $fimport$11
    (global.get $global$1)
    (local.get $1)
    (local.get $0)
   )
  )
 )
)
0
0
0

@mcc I'm not a lisp head any more and I still feel that parentheses are by far the least significant thing you could complain about in any lisp.

Haskell's over abundance of horrible syntax on the other hand... 🤪

1
0
0

Been reading the docs and I have a LOT to say but

I've been using this lovely, friendly guide to figure out how to write WASM assembly

https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Understanding_the_text_format

(The spec https://webassembly.github.io/spec/core/_download/WebAssembly.pdf is a little hard to work with)

And saw this really alarming note implying the guide hadn't been updated since 2020 (is that when Mozilla fired everyone?)

Can anyone recommend how I'd find out what has changed in Wasm between 2020 and 2025? Assume I'm targeting wasmtime and can be cutting edge.

4
0
0

@mcc ah, well, it's a mess. you'd have to go read all of the WEPs that are relevant if you wanted to produce something compatible with them.

1
0
0

@dysfun Where are the WEPs gathered, and how do I know which of them were accepted?

1
0
0

@mcc It looks like those docs are at least somewhat maintained, there are recent changes in the history: https://github.com/mdn/content/commits/main/files/en-us/webassembly/guides/understanding_the_text_format/index.md

Not sure when the last time someone did a thorough review or cleanup of them, obviously no one has updated that particular line in a while, but it looks like there is still some maintenance.

But yes, 2020 was when Mozilla laid off the whole MDN team. It looks like other folks have taken it over since, the history was imported to Git after that point, so it's hard to compare how active maintenance has been since that point. Frustrating that they laid off an entire team that was producing some of the best webdev content out there.

1
1
0

@unlambda What you have to understand is it was very important they reallocate those resources to building up "metaverse" software they then immediately abandoned before anyone could start using it in order to retask those resources to machine learning projects they then also rapidly abandoned to fund "AI" projects which are truth be told very similar to their cancelled machine learning projects except not useful

0
0
0

@mcc here. and i can honestly say they've done well with this page at letting you know what state the proposals are at https://github.com/WebAssembly/proposals?tab=readme-ov-file

1
0
0

@mcc I've worked on these a bit over the last year or so, they're pretty solid in general, I think, but would love to hear feedback. I didn't notice the "at the time of writing" note, must look into it

0
0
0

So. Creeping along with this. I am posting tihs from a bus.

Learned some more about "textual WASM". Two things I learned. One. It is LISP but also it is Forth. You write it as structurally an S-expression but the commands you write in that S-expression are, as it happens, instructions to a stack machine. This surprised me, but apparently Hotspot was a stack machine, as was the C# CLR as it was based on Hotspot, as is wasm because it was based on… Hotspot. Apparently this works well for JIT.

2
0
0

The semantics of this LISP/Forth displease me, aesthetically. You kind of go forward then backward. For example

i32.const 5 i32.const 7 i32.add

This sequence of symbols is one two-symbol instruction pushing a constant 5 on the stack, a second symbol pair pushing a 7 on the stack, then a one-symbol instruction adding the last two items on the stack. This oddness appears to be an artifact of the textual form ( which, I've found, is better documented at https://webassembly.github.io/spec/core/_download/WebAssembly.pdf ).

This is "okay"…

2
0
0

@mcc Hotspot isn't really a stack machine. *Java bytecode* is written for a stack machine, as is Wasm machine code, which works out well because it's compact: you don't encode (almost) any temporary indices, like you would have in LLVM

the moment you load it, you transform it into a register representation in memory. not literally every implementation does it, but most do not actually execute it with a data stack

1
1
0

…because the WASM committee sorta believes nobody ever *writes* the textual form? Nobody writes textual Wasm except people making test cases for compilers, everyone else uses the compilers. Okay, whatever. But this means they'll never fix the rough edges, like how the only way to embed byte data in your textual wasm is as an ascii string. What if you want non-ASCII characters? "\F0\0F\00\00". Inconvenient for my purposes. It is unpleasant to be "nobody", it turns out.

1
0
0

Due to the byte nonsense, and it turning out there's no cross-compatible way to do a "print" statement that works in both WASI and browser WASM, I sought an impractical but effective solution: I wrote a Wasm preprocessor.

https://github.com/mcclure/aoc2024/blob/6f8ca110b931558a42826f8257996e53010e555f/11-01-stones/tools/prep.py

Or rather I wrote a very simple, C-like preprocessor that uses ;; wasm comment indicators instead of #, in 200 lines of python, which I expect I can usefully adapt to other asms someday later. It knows how to do the wasm byte string packing.

1
0
0

So now I can like… actually start on this part of the project. What I'm running into immediately is some confusion over the nature of memory. Okay, so what's been repeatedly claimed to me is "memory" to a wasm program is a single linear space that can be grown but not shrunk. But then I've got a stack. Is the stack in linear memory? Can I take pointers to it? Then there's (memory), which I think refers to buffers shared with FFI languages. Is this in the linear memory? Can I take pointers to it?

4
0
0

I imagine the above would be explained if I read the spec rather than just blog-style summaries of the spec, but there is a problem with that, which is that the spec is on my laptop, and writing these posts while trying to power my phone which is providing the internet via USB on this bus has drained my laptop battery, to the point where I probably have to s

1
0
0

@mcc the stack is not memory, it is not addressable, languages that want an addressable stack implement their own stack inside linear memory.

0
0
0

@mcc at some point was added the capability to have more than one linear menories

1
0
0

@mcc I think the stack is more like a sliding register window kind of deal, it's not addressable as memory, presumably the JIT is supposed to allocate stack slots into registers.

0
0
0

@whitequark @mcc Also (maybe relevant for the JIT comment): the stack at any given point *is* the set of live variables.

If you calculate a kind of "virtual stack" for a given basic block you will end up with the amount of elements that it pops from the stack (phi nodes/basic block arguments/whatever you want to call them) and the amount of elements it adds to the stack at the end of its execution (values that will be consumed by another block).

2
0
0

@mega @mcc there is the caveat that both wasm and java have the separate concept of locals, which is why i said "temporaries"; you still have to do liveness analysis for locals-locals if you want high quality codegen

1
0
0

@whitequark @mega if i am writing/generating code why would i use the stack for live variables instead of the locals, or vice versa?

1
0
0

@whitequark @mcc IIRC¹ the JVM enforces statically known stack heights at all control flow mergepoints so

label:
loadconst %0
jmp label

will be invalid.
No idea if WASM does the same.

¹Hope I'm not misremembering it 😅

0
0
0

@mcc @mega the stack machine that JVM and Wasm have, and the SSA form that LLVM has, are literally equivalent (they are different ways to serialize the same IR, essentially; with the caveat that Wasm forbids irreducible control flow)

if you have your code in SSA/CPS form you would find it easy to emit stack bytecode, if you do not, it is most convenient to use locals (and the runtimes are aware of this and optimize for it)

0
0
0

Also [posting this from a donut shop in a mysterious town in Washington where the bus stranded me] I know, objectively, it's probably performance/complexity optimal on modern CPUs to support only i32 and i64 types and not bother with (say) i8 or i16, but it still *bugs* me

1
0
0

When I started this "write 25 programs in 25 languages!" project I really did not expect how many times I'd find myself implementing atoi

EDIT: I mean, I *should* have, but I didn't

1
0
0

@mcc You probably know this, but there's a syntax, which I think might be WAST (as differentiated from WAT) which supports nested expressions, such as

(i32.add (i32.const 5) (i32.const 7))

At some translation level it gets morphed into the flat list you mentioned, but it is more pleasant to write.

1
0
0

This post was supposed to have been inlined in this thread but I got distracted running to catch a bus and messed up. This isn't a bit I really am writing this thread at a series of bus stops today https://mastodon.social/@mcc/114622991386698840

1
0
0

As a note, I have not so much as touched this stuff in the last five weeks. It might be the exceptionally bad quality of the documentation on WASM textual format has actually killed my 25 languages project. I've spent my junkfood-project time for the last month learning to make Game Boy games. Christine LM suggested I just ignore WAST and write my wasm stuff in Hoot (which is apparently her research group's LISP that compiles to Wasm) because that's intended for use by humans. Heck, maybe.

0
0
0