First of all, let's clear some things up so we don't talk in circles. :)

Picking either the JVM or the CLR as your compiler backend is generally done because you either want (1) to hook into the respective infrastructure, (2) want a mature GC technology without having to develop your own, (3) need to generate reasonably fast code at runtime, or (4) all of the above. Most likely, it's a combination of (1) and (2).

If not, you're probably better off picking a more flexible backend that does not limit your object model or (if you're doing concurrency) your memory model. In short, either the JVM or the CLR forces you to compromise when emitting code.

In particular, both the JVM and the CLR are hostile environments for functional languages; if you're compiling a functional language to either (instead of, say, LLVM or MLRISC), you're almost certainly doing it for the JVM ecosystem, i.e. interoperability, vendor support, availability of tools, and such, not because it is a great fit for your needs.

So, when most of your points are about the superiority of the JVM ecosystem, I'm not disagreeing at all.

What I am still maintaining is that the CLR has the superior design. And yes, I am painfully aware of its limitations, too. Superiority is relative, not absolute.

All that said, there are a few of your points that merit a specific response.

(1) First of all, virtual call optimization (not just inlining) is something that the JVM does to solve what is primarily a JVM-specific problem. Simply put, while the CLR allows you to emit both call and callvirt instructions, the JVM has only invokevirtual (invokespecial, which was originally called invokenonvirtual, is more limited). The only option to tell the JVM that a call is non-virtual is to target a final method, which may require code duplication.

Second, nothing in Ecma 335 prohibits virtual call inlining when it can be proven to be safe. I don't know why Microsoft .NET/Mono still don't do it, but I suspect that they just don't see a big need for it, given that C# (unlike Java) uses non-virtual methods by default, and compilers for other languages with different semantics (e.g., Eiffel) can emit non-virtual calls at non-polymorphic call sites if they need to. Virtual call optimization could still be useful for inter-assembly calls (where the target assembly isn't strong-named), but that's probably a rare enough case that it's not certain whether the overhead is justified.

(2)/(3) Generating DLLs is pretty straightforward with ILASM or Mono.Cecil. Including debug information. You can only generate .mdb information on Windows, but then you only need it on Windows, too.

(4) The Ecma standard actually does make tail call elimination not optional for non-virtual calls. "CLI implementations are required to honor tail. call requests where caller and callee methods can be statically determined to lie in the same assembly; and where the caller is not in a synchronized region; and where caller and callee satisfy all conditions listed in the 'Verifiability' rules below."

With respect to value types, you probably misunderstood me. SIP-15 is about adding a layer of abstraction around existing value types. That's valuable, but it's also largely a compiler frontend, not backend issue.

What I was referring to was that the JVM makes heap allocations for tuple types all but necessary, especially if you're looking at collections of tuples. Sometimes you can work around it (such as mapping an array of N complex numbers to an array of 2*N real numbers) if you don't mind the extra work, but just as often that's not easily possible.