仅出于娱乐目的,我正在回顾AMD引入的3DNow! set中的旧(不推荐使用)指令,并且试图了解它们的用法。所有指令似乎都按照这种模式编码:
instruction destination_MMn_register_operand, source_MMn_register_or_memory_operand
其中destinationRegister
= destinationRegister
-操作-source
例如,pfadd mm0, mmword ptr [rcx]
(0F 0F 01 9E
):
将rcx
指向的内存中的2个压缩浮点数加到mm0
中存储的2个压缩浮点数中,并将结果保留在mm0
中。
因此,似乎这些3DNow指令始终具有mm
寄存器作为目的地。
但是您应该如何从那些mm
寄存器中获取结果?
换句话说,没有mov mmword ptr [rcx], mm0
或mov rax, mm0
指令。
答案 0 :(得分:3)
实际上有movd
和movq
。这些说明不是3DNow!的一部分,它们已经存在于3DNow!的MMX中。是的扩展。这也是3DNow的原因!包括一组看起来很不完整的整数运算。
答案 1 :(得分:3)
正如@harold所说,存储到内存已被MMX movd
或pshufw
+ movd
所覆盖,仅提取高float
。
您不能做的一件事就是打开3dNow!浮动到x87 80位浮动中,而无需存储/重新加载。
可能有用的是一个EMMS版本,该版本将float
中的32位long double
扩展为80位x87 st0
,并将FPU设置为x87模式而不是MMX模式 1 。甚至对多个mm
寄存器到多个x87寄存器都可以这样做?
即在简化SIMD之后,movd dword [esp], mm0
/ emms
/ fld dword [esp]
可以设置进一步的标量FP。
请记住,它们是IEEE754 float
;您通常不希望它们在整数寄存器中,除非您要分离它们的位字段(例如,用于exp
或log
实现),但是您可以使用MMX移位/掩码指令来实现。
但是movd和fld很便宜,因此他们不必费心做一个专门的指令来节省重载延迟。同样,将其作为单个指令实施可能会很慢。即使x86不是RISC ISA,拥有一个真正复杂的指令通常也比多个简单的指令慢(特别是在解码为多个微指令之前完全是一件事情)。英特尔和AMD的sysenter
和syscall
指令(用于替换int 0x80
来进行系统调用)在存储更多状态之前/之后需要附加的指令,但总体上还是更快。
3dNow!的femms
leaves the MMX/3dNow! register contents undefined,仅将标记字设置为未使用,而不保留从MMX寄存器到x87寄存器内容的映射。有关AMD的官方手册,请参见http://refspecs.linuxbase.org/AMD-3Dnow.pdf。 IDK如果AMD的微体系结构只是放弃了寄存器重命名信息或其他内容,但可能以快速的方式存储/ femms / x87-load可以节省很多晶体管。
甚至FEMMS仍然有些慢,因此他们不想鼓励编码人员离开/重新进入MMX / 3dNow!模式。
有趣的事实:3dNow!仍然使用PREFETCHW
(具有写意图的预取),并且具有自己的CPUID功能位。
在What is the effect of second argument in _builtin_prefetch()?上查看我的答案
Intel CPU很快增加了对将其解码为NOP的支持(因此,诸如64位Windows之类的软件无需检查即可使用它),但是Broadwell和更高版本实际上是通过RFO预取的,以使高速缓存行处于MESI Exclusive状态,而不是已共享,因此无需额外的核心外流量就可以切换到“已修改”。
CPUID功能位指示它确实会预取。
脚注1 :
请记住,MMX寄存器是x87寄存器的别名,因此不需要新的OS支持就可以在上下文切换器上保存/恢复体系结构状态。直到SSE,我们才有了新的架构状态。所以直到SSE2 + 3dNow! 3dNow!从float
到SSE2 double
可能会有意义,而无需切换回x87模式。您可以movq2dq xmm0, mm0
+ cvtps2pd xmm0, xmm0
。
他们可能在mm
寄存器中有一个float-> double,但是fld
/ fst
硬件仅设计用于float
或double
-> 80位和80位-> float
或double
。而且用例有限。如果您使用的是3dNow !,请坚持使用float
。