【发布时间】:2020-05-07 08:18:59
【问题描述】:
对于我正在编写的函数,如果输入没有意义,我想返回一个 Nan。
如何将 NaN 插入 xmm 寄存器最简单的方法?
【问题讨论】:
-
你如何确定哪个“输入没有意义”?如果这是比较的结果,您可以使用比较的结果掩码按位或“正常”结果。
对于我正在编写的函数,如果输入没有意义,我想返回一个 Nan。
如何将 NaN 插入 xmm 寄存器最简单的方法?
【问题讨论】:
All-ones 是一个安静的(非信号,也就是正常的)NaN,这就是你想要的。最简单的方法是使用 SSE2 pcmpeqd xmm0,xmm0 将寄存器中的每一位设置为 1,即 2 的补码整数 -1。 (Set all bits in CPU register to 1 efficiently/What are the best instruction sequences to generate vector constants on the fly?)
它实际上是一个-NaN - 符号位已设置。如果不希望这样做,请考虑整数右移 (psrld xmm0,1) 或除以零/零 (xorps xmm0,xmm0 / divpd xmm0,xmm0)。
想要返回 NaN 的数学函数通常还希望确保在 MXCSR 中设置 FP 无效的粘性异常位(或者如果您的调用者取消屏蔽该异常,则实际引发异常)。要做到这一点,您可以将 NaN 与自身相乘或相加。例如
...
.error_return_path:
pcmpeqd xmm0, xmm0
mulsd xmm0, xmm0 ; Cause an FP-invalid operation.
ret
或mulss 用于单精度float。 mulpd / mulps 也是合适的。
NaN 与 NaN 相乘或相加的位模式肯定仍然是 NaN,并且应该仍然是相同的有效负载,所以仍然是全一。
将返回值作为mulsd 或addsd(或divsd)的结果还有一个优点,即如果调用者在循环中重复使用该寄存器,则不会出现跨域绕过延迟. (在 Sandybridge 系列上,这将永远持续下去。例如,如果 xmm0 来自 pcmpeqd,则从 xmm1 输入到 xmm1 输出的每个 addsd xmm1, xmm0 都会有一个额外的延迟周期,即使那是很久以前并且整数 SIMD uop 已经退休。)
如果您使用 cmpsd 或 cmppd,您甚至可以无分支地执行此操作:您可以 orps 将 0 / -1 掩码转换为结果,使其为 NaN 或不变。如果其他一些计算将(或已经)设置 FP-invalid 标志,或者如果您不关心它,那么您就已经设置好了。
注意不要用额外的 cmp / or 延长关键路径;如果您认为它非常罕见,您可能仍宁愿比较和分支,例如在 cmppd 结果上使用 movmskpd / test eax,eax / jnz 以查看是否设置了任一位 => 一个 SIMD 元素未通过某些检查。
【讨论】: