It&#x27;s easiest to do optimizations on an IR that has the appropriate level of granularity. If you&#x27;re trying to optimize address calculations, it&#x27;s easier to work with something like LLVM&#x27;s getelementptr, which represents an address calculation as one IR node, than a C-level IR (where the address calculation would be implicit in an array or structure field access), or an ASM-level IR (where the address calculation might be broken up over several different instructions).<p>LLVM is pretty low-level. It&#x27;s a bit lower-level than C and has a very simple type system. It has no idea about unique pointers versus references, has no concept of borrowing, etc. I bet you could encode the necessary information for performing say borrow-checking into LLVM IR, but working with that would not necessarily be pleasant.<p>A good example of this is ARC optimization in Swift, which gets rid of unnecessary reference counting operations. That optimization operates on SIL, which has special instructions for reference counting. You could represent those instructions as magic intrinsics in LLVM IR, but you have no way of imposing any structural restrictions on how those intrinsics are used. Moreover, any pass which operated on LLVM IR to optimize uses of those intrinsics, would have to make deep assumptions about the semantics of those operations. Those assumptions, being Swift-specific, would mean the pass could not be used by other languages, which removes a lot of the benefit of making it an LLVM pass in the first place.