LLVM bitcode isn't all that portable though, and of course, the binary will still be OS specific because C/C++ code relies on native APIs. The primary reason to do this is so the Graal JIT compiler can optimize native code and higher level dynamic script/bytecode together and remove interop overhead.
However, you can theoretically run whole programs this way inside the Graal sandbox. If you do that you get an emulation of POSIX that is reimplemented on top of the Java standard library, so the code becomes portable, and in managed mode there's an additional party trick - the native C/C++ malloc is replaced with garbage collected allocations and memory accesses are bounds checked. So code run this way gets all the memory safety errors blocked automatically. This upgrade comes with two costs though, one is slower execution/more memory usage, and the other is you have to buy GraalVM EE. The community edition can run bitcode, but not in the sandboxed/managed mode.
Oh and GraalVM can also run WASM. So you can have cake and eat it, everything running together via their 'polyglot' interop system.
This is incorrect, there is a long list of limitations that your C/C++ code must conform to in order to compile to WASM. There's a whole section dedicated to this in the Emscripten docs: https://emscripten.org/docs/porting/index.html.
The chances your existing C/C++ app will compile to WASM and run correctly are much smaller than with Docker.
> not like the jvm which is designed primarily for Java in the front end
This is also wrong, JVM bytecode is explicitly designed to be polylingual and is the compilation target for many non-Java languages like Scala, Kotlin, and Clojure. WASM being a compilation target is not what makes it unique from the JVM.