There's a lot more that doesn't have to do with the JIT. Yes, in some cases it's about providing hints to the JIT to generate better code. Things like casting to unsigned types for example. But the weird ones (one I came across recently was something like condition ? true : false) are fairly rare. Which makes sense in a way: Not all code is that performance-critical that you have to save two instructions. But for things where the baseline performance is a few nanoseconds in many cases, it probably makes sense taking whatever measures you can to ensure that the machine code looks like you'd expect (and make a note to improve the JIT, which also happens regularly).
Technically you could count vectorization in the category of codegen issues, but since the JIT won't output vector instructions on its own I don't think that's really fair (and you have the same issues in languages like C or C++ that do auto-vectorize, but not reliably or good enough).
What is also frequently optimized, is allocation behavior by ensuring that the code doesn't allocate more memory than needed and in a lot of cases no memory at all. And C# has a lot of tools for that kind of optimization that don't really veer into code that looks atypical.