1. It thinks it is cheaper than keeping them in a register ( this is known as rematerialization). It will reload constants that it lets it keep something else in a register, and it's cheaper to do this.
2. It thinks something could affect the constant.
3. It thinks it must move it through memory to use it, and then it thinks the memory was clobbered.
In this case, it definitely knows it is a constant, and it can't prove that both loops always execute, so it places it in the path where it is only executed once per loop, because it believes it will be cheaper.
I can still make at least gcc do weird things if i prove to it the loop executes once.
In that case, what is happening in gcc is that constant propagation is propagating the vector constant forward into both loops. Something later (that has a machine cost model) is expected to commonize it if it is cheaper, but never does.
That is normal and what i would expect to happen.
You can see in lower level RTL dumps, nothing chooses to commonize it. That seems a bit weird, since it should have a cost model that says this isn't free.
A lot of the intrinsics also look like (at least in gcc), they are marked always inline but not pure/const.
it's probably worthwhile marking those that take memory as pure/const. That way, it's obvious the value is readonly even when it's inlined.
(otherwise, the compiler will have to prove it in the inlined code, which is not always possible)
__m256i mask = _mm256_set1_epi8(0x0f)
If you just used the intrinsic that sets the register to a constant over and over, it often repeats the instruction.
The compilers just aren't that smart about SIMD yet.
__m256i c = _mm256_set1_epi32(10001);
And then the disassembly has mov eax, 10001
vpbroadcastd ymm1, eax
before each loop.Optimal register allocation + spilling is easily doable now on today's machines/algorithms.
We just don't bother because it's not truly worth it in most cases (IE we get most of the performance for a very low cost)