Author here: happy to answer any questions about the bitcode format, at least as far as I've explored it :-)
This post also has a second part, which I published last week[1]. That part contains even more nitty-gritty details on how the bitstream is actually interpreted, and shows off a proof-of-concept bitstream parser in Rust.
You (and LLVM people generally) use intermediate representation and bitcode interchangeably, it seems. The term bitcode seems specific to LLVM but I've generally heard bytecode as a code stream interpreted at runtime by a virtual machine (esp: java bytecode) while intermediate representation is a compiler's detailed representation of compiled (which will be later transformed to machine code instructions). It seems like bitcode is an IR/intermediate representation in this sense but the situation seems a bit fuzzy given LLVM is "low level virtual machine" but at this point, it's not a virtual machine at the end of the day but a compiler (which creates runnable machine code, right?).
Yeah, this is a point of significant confusion. I'll try to clear it up:
* LLVM defines an abstract container format, called the bitstream. The bitstream has "bit" in it because it's bit-aligned instead of byte-aligned.
* LLVM IR has two on-disk representations: a textual representation and a binary one. The binary one is stored within the bitstream container and it's code, hence bitcode. The bitcode representation is much more space efficient than the text one, so it's the one that's (almost) always passed around. As a result, people tend to refer to specific bits of LLVM IR that have been serialized to disk as "bitcode."
* LLVM hasn't stood for "Low Level Virtual Machine" for a very long time; probably over a decade. They (presumably) changed the name for the same reason you've pointed out: it's a little bit misleading. That being said, you can interpret LLVM IR as if it were just a compacted interpreted program: `lli`[1] is an official tool that does exactly that.
TL;DR: bitcode is a representation of LLVM's IR. The "bit" in it is a reference to the underlying structure, which is relatively unusual among program container formats (almost all are byte-oriented, as you've observed).
It's actually interesting how many independent implementations of bitcode writers there, given that there are almost no independent bitcode readers (mine is very preliminary, and then there's one from Galois[1]).
I believe Swift also does their own serialization in LLVM's bitstream format[2]. I also saw an implementation of the writer in Swift, but now I've lost it.
In typical google fashion, there is no convenient pathway built for people who developed software targeting this and now want to switch to wasm.
Sure, in theory, it is just a matter of retargeting. But nothing really works.
This post also has a second part, which I published last week[1]. That part contains even more nitty-gritty details on how the bitstream is actually interpreted, and shows off a proof-of-concept bitstream parser in Rust.
[1]: https://blog.yossarian.net/2021/08/10/LLVM-internals-part-2-...