This past June, Apple announced that WatchOS 2 applications would have to be submitted using LLVM BitCode. The idea being that Apple could optimize your code as new optimizations are developed or new CPU features are introduced, and users would reap the benefits without requiring the developer to resubmit their applications.
BitCode is a serialized version of the low-level intermediate representation used by LLVM.
WatchOS 2 requires pure BitCode to be submitted. That is, BitCode that does not contain any machine code blobs. iOS supports mixed mode BitCode, that is, BitCode that contains both the LLVM intermediate representation code, and blobs of machine code.
While Mono has had an LLVM backend for a long time, generating pure BitCode posed a couple of challenges for us.
First, Mono's LLVM backend does not cover all the generated code. There were some corner cases that we handled with Mono's old code generator. Also, Mono uses hand-written assembly language code in various places (lots of small optimizations involving generics code sharing, method dispatch and other things like that). This poses a problem for WatchOS.
Secondly, Mono uses a modified version of LLVM that adds support for many .NET idioms. In particular, our changes to LLVM produce the necessary information to support .NET-style exception handling [1].
We spent the summer adapting Mono to produce Vanilla LLVM bitcode support. This includes the removal of our hand-tuned machine code, as well as devising a new system for exception handling that works in this context. Sadly, the exception handling is not as efficient as the one that we got with our modified LLVM.
Hopefully, we will be able to upstream our changes for better exception handling for .NET-like languages to LLVM in the future and get some of the performance back.
[1] Vanilla LLVM exception support requires exceptions to be explicit. In .NET some exceptions happen implicitly, for example, when dereferencing a null pointer, or dividing by zero.
Posted on 02 Sep 2015