GCC is actually good at that, -O3 has no issue recognizing it. In fact there even is a very explicit option (-funswitch-loops) responsible for extracting loop invariants. It is not enabled on -O2 because it has a space-speed tradeoff. If this optimization is truly desirable even on -O2, `#pragma GCC optimize("-funswitch-loops")` can be used to force it.