I see that as a downside, personally. It makes it a lot harder to understand what guarantees there are about with a given piece of code (rather than being able to look at the assembly and the language's own compiler, you would have to also understand C's rather odd semantics and the complex behaviour of many C compilers).
This is a bit like saying you wouldn't use LLVM languages because of the semantics of IR, or that you have to understand the guarantees IR provides, isn't it?
Ultimately if you're really interested in performance, regardless of the stages of compilation, the juice is the machine code output at the end.
In terms of guarantees, those should be satisfied higher up in the language itself, with C generation being output according to Nim's CGen spec. The compiler is fully open source though and easy to dig into.
Having said that, the CGen output is fairly readable if you're familiar with C and I must say I've investigated it when I wasn't sure how something was generated.
> This is a bit like saying you wouldn't use LLVM languages because of the semantics of IR, or that you have to understand the guarantees IR provides, isn't it?
Those languages tend to be a lot simpler than C, both in terms of what constructs they offer and in terms of how simply they translate into (platform-specific) assembly language. I'm not against the idea of intermediate languages in general, but IME C is the worst of both worlds: more complicated than most high-level languages and most assembly languages.
> In terms of guarantees, those should be satisfied higher up in the language itself, with C generation being output according to Nim's CGen spec. The compiler is fully open source though and easy to dig into.
The problem is being confident that those guarantees are preserved all the way down. It's very hard to be confident of the properties that any given piece of C code has, because it's extremely rare for C code to be 100% standards compliant and different compilers do radically different things with the same code. And it's hard to reason about the effects of changes because C compilers are so complicated: maybe you understand the Nim compiler and can see how two similar Nim functions will generate similar C code, but that doesn't help you much when two similar C functions can be compiled to radically different assembly (which happens a lot).
C is a very complicated language with very complex compilers. The translation from C to assembly (under modern compilers) is hard to understand, even for "boring automatically-generated C code". Languages like LLVM-IR or Gimple are designed to be simple and translate more directly into assembly. I'd have the same complaint about javascript to a certain extent, but even though it's a full programming language it's a much simpler language than C and easier to introspect at runtime.