> The IR is highly unstable, especially the text representation, which is basically just a debugging tool.
It's not guaranteed to be stable, but it's not "highly unstable" either. Not too many breaking changes have been introduced in the past few years and it's unlikely that you'd hit those parts when hand writing it.
Not that I'd recommend hand writing LLVM IR.
It's a shame that we don't have an actual portable IR, which would not be tied to toolchain version or contain target specifics. LLVM IR can't be used as such for a portable IR, which is why we've seen efforts like SPIR-V (Vulkan GPU IR) and WebAssembly (IR for browsers), both of which are very similar to LLVM IR (and a lot of work was duplicated).
WebAssembly is very different from LLVM IR, because it is essentially just the greatest common denominator of what existing JavaScript VMs handle in the backend. For instance, WebAssembly allows only reducible control flow, has very limited support for function pointers, and a particularly nasty memory model (linear memory, which precludes any kind of non-trivial alias analysis and optimization).
All of these things may change in the future (apart from the reducible control flow restriction), but, at the moment, the goal of WebAssembly is just to have a viable compilation target for C++ in the browser. Google was championing LLVM IR for this purpose in the form of pnacl, but that was not enough of a compromise to work on the web platform. :(
You can make a perfectly stable IR from that Kaleidoscope example in no time and still avoid tying yourself to C++ish LLVM. I'm not sure how big of a deal that stability thing is though, you still have to cover everything with tests, so who cares if it breaks a bit in future releases?
The bitcode format is rather complicated, but at least there is backwards compatibility.
LLVM is the library, so you are bound to it anyway.
Further by having code for emiting IR you are basically just copying functionality that already exists.