Compilers (even the high-end modern ones) are very quickly clueless about both memory and execution profiles. Those problems are hard to reason about statically. Is this pointer the same as this other one ? Will this loop be executed 10 times or 10^6 times ? The hardware being a dynamic optimizer has a lot of runtime information (pointer values, cache entry usage, ...) to take advantage of.
Sure, one can argue that we just need better JITs, PGO, and programming languages that are easier to analyze statically. In the current state of affair we are way better off leaving this to the hardware.
By the way you do have "some" control over it using NT moves on x86. Turns out almost nobody uses them except by hand in very specific peak-bandwidth code. Compilers don't dare emitting those since the penalty of getting it wrong outweights the benefits of getting it right.