你说得对,如果从 vzeroupper 中得知 YMM 上限为零,混合 AVX128 和 SSE 不会有任何损失,如果不这样做会节省代码大小,这是一种错过的优化。
另请注意,如果您不需要 REX 前缀,它只会节省代码大小。 2 字节 VEX 相当于 SSE1 的 REX + 0F。编译器确实尝试偏爱低寄存器以希望避免 REX 前缀,但我认为他们不会查看每条指令中使用哪些寄存器组合来最小化总 REX 前缀。 (或者如果他们确实尝试这样做,他们并不擅长)。人类可以花时间进行这样的计划。
大部分时间它都很小,只是偶尔的一个字节的代码大小。这通常是一件好事,可以帮助前端。 (或者在 Intel CPU 上为 blendvps xmm, xmm, <XMM0> 保存一个微指令而不是 pblendvps xmm, xmm, xmm, xmm(pd 和 pblendvb 相同),如果您可以安排使用它而不需要另一个 movaps)
如果你弄错了,缺点是 SSE/AVX 转换惩罚(在 Haswell 和 Ice Lake 上),或者对 Skylake 的错误依赖。 Why is this SSE code 6 times slower without VZEROUPPER on Skylake?。如果 Zen2 做了类似的事情,IDK; Zen1 将 256 位操作拆分为 2 个微指令,不关心 vzeroupper。
为了让编译器安全地执行此操作,他们必须跟踪更多内容,以确保在 YMM 寄存器的上半部分脏了时不会在函数内运行 SSE 指令。编译器无法将 AVX 代码生成限制为仅 128 位指令,因此他们必须开始跟踪可能会弄脏 YMM 上半部分的执行路径。
但是,我认为他们无论如何都必须在整个函数的基础上这样做,才能知道何时在 ret 之前使用 vzeroupper(在不接受或按值返回 __m256/i/d 的函数中,这将表示调用者已经在使用宽向量)。
但不需要vzeroupper 与movaps 是否性能安全是另一回事,因此以类似方式跟踪将是另一回事。找出所有可以安全避免 VEX 前缀的情况。
不过,在某些情况下可能很容易证明它是安全的。如果编译器使用保守的算法,在分支可能有或可能没有脏的上层时有一些优化丢失,那就没问题了,在这种情况下总是使用 VEX,总是使用vzeroupper。