Past that, LLVM is already like this, it's just a matter of shipping bitcode, and outputting the currently optimized bitcode at shutdown.
It is actually done in practice, but admittedly, only by those who care tremendously about performance.
(IE you don't find it done in common linux distros).
Particularly in a datacenter world, where you have hypervisors and things under the covers anyway, it's not a huge deal to have a JIT running.
The harder part is actually, if you are a cloud provider, getting anyone to bring bitcode with them :)
Even if you promise them X% performance gain to start, where X is usually 10-30%, they don't usually care enough to modify their workflow.
- The truffleruby "cext" project demonstrated something like this. They interpret and then JIT C on the JVM, and do cross-language inlining and other optimizations.
http://chrisseaton.com/rubytruffle/cext/
The same group is working on compiling LLVM IR on the JVM. Presumably the JVM's tiered runtime optimizations would apply to both of these.
https://github.com/graalvm/sulong
- There are also interactive C(++) "JITs" from CERN and the Julia project. However, as far as I know, they are both method JITs and don't do any trace-based re-JIT/OSR.
https://root.cern.ch/cling and https://github.com/Keno/Cxx.jl
http://archive.arstechnica.com/reviews/1q00/dynamo/dynamo-1....
I.e. you compile your program with profiling, then run e.g. a test suite, and compile your release binary with input from that profiling so the compiler knows what to optimize.
Has any C compiler closed the loop on that by shipping the full compiler with the program to do runtime optimization? With on-the-fly symbol replacement you'd effectively have a C JIT compiler.