Assembly FPTAN示例x86
根据英特尔文档,这就是Assembly FPTAN示例x86,assembly,x86,x87,Assembly,X86,X87,根据英特尔文档,这就是FPTAN所做的: 用近似切线替换ST(0),并将1推到FPU堆栈上 这是我用NASM写的代码: section .data fVal: dd 4 fSt0: dq 0.0 fSt1: dq 0.0 section .text fldpi fdiv dword[fVal] ; divide pi by 4 and store result in ST(0). fptan fstp qword[fSt0] ;
FPTAN
所做的:
用近似切线替换ST(0),并将1推到FPU堆栈上
这是我用NASM写的代码:
section .data
fVal: dd 4
fSt0: dq 0.0
fSt1: dq 0.0
section .text
fldpi
fdiv dword[fVal] ; divide pi by 4 and store result in ST(0).
fptan
fstp qword[fSt0] ; store ST(0)
fstp qword[fSt1] ; store ST(1)
此时,我发现fSt0
和fSt1
的值为:
fSt0=5.60479e+044
fSt1=-1.#IND
但是,难道
fSt0
和fSt1
不应该都是1
?正如迈克尔·佩奇在评论中指出的那样,你的打字很简单。您没有将fVal
声明为浮点值(如预期的那样),而是将其声明为32位整数。更改:
fVal: dd 4
致:
然后您的代码将按预期工作。它写得很正确
如果要获取整数输入,可以通过将代码更改为使用FIDIV
指令来实现。此指令将首先将整数转换为双精度浮点值,然后进行除法:
fldpi
fidiv dword [fVal] ; st(0) = pi / fVal
fptan ; st(0) = tan(st(0))
; st(1) = 1.0
fstp qword [fSt0]
fstp qword [fSt1]
但是,由于需要进行转换,因此与刚将输入作为浮点值时相比,转换的效率要低一些
请注意,如果要执行此操作,则在某些较旧的CPU上,将负载分解,使其与分区分开执行会更有效—例如
换句话说,我们将FIDIV
指令分解为单独的FILD
(整数加载)和FDIVP
(除法和弹出)指令。这改进了重叠,从而减少了代码执行速度中的几个时钟周期。(在AMD系列15h[推土机]和英特尔奔腾II及更高版本的较新CPU上,将FIDIV
拆分为FILD
+FDIV
,没有任何真正的优势;无论采用哪种编写方式,性能都应该相同。)
当然,因为这里的所有内容都是常量,并且tan(pi/4)==1
,所以您的代码相当于:
fld1
fld1
…这是优化编译器将生成的结果。:-) 为什么
dd4
(整数)而不是dd4.0
(浮点数)?@MichaelPetch感谢,它成功了,细节战,永不停息。订单执行的fidiv应该像fild
+fdiv
一样有效地处理。它解码为两个独立的uop,可能是一个load+convertfild
uop,它可以在地址已知时立即执行,以及一个必须等待两个输入的fdivp
uop。(实际上,fidd
的fild
部分是一个微熔合加载+ALU-uop,因此加载部分可以与p05 int->float-uop分开运行。)提前安排加载可能会有一些好处,但如果要将它们背靠背放置,则不会。(除了前端好处,如果多uop insn存在更糟糕的情况,例如解码)同样,re:关于常数的最后一点:gcc喜欢避免fld1
,因为自从ppro以来,它在Intel CPU上是2个uop(不知道为什么;感谢Intel。P5上也是2个时钟,fld
是1个时钟)。因此,它可能会从内存中fld
a1.0
常量,并用fld st(0)
复制它。为了避免缓存未命中,fld1
/fld st(0)
会很好。您可能会认为OOOE可以像FILD
+FDIV
一样有效地处理FIDIV
,但显然它不能。AMD特别建议在他们的优化手册中使用这种替代品(或者他们在我上次阅读这些手册时使用了这种替代品,那是在Ryzen之前很久,但这是x87,所以不完全是最先进的)。Agner Fog还建议将其用于P5,其中FILD
可以重叠,但FIDIV
不能重叠。另外,P6的FPU与P5几乎没有变化,只是延迟增加了,这使得分离更加有利。但这在现代实现中可能已经发生了变化,提出了这一陈旧的优化建议,但在我的定义中,在谈到编写x87代码时,很难知道应该使用哪个处理器!不过,关于fld1的一点很好。它是2µops(未使用),因为它需要端口0和1。也不知道为什么。如果它使用一个内存读取端口和一个执行端口,这会更有意义,但它没有。所以使用它并不是一个真正的优化。我之所以使用它,是因为它比显示浮点常量的外部定义更容易,更不用说不同汇编器之间不一致的语法了@彼得不管它值多少钱,我回去看了看AMD的手册。他们明确建议对整数和浮点操作使用load execute指令,整数浮点指令除外。正如彼得所说,这在K6时代是正确的,但在阿龙时代始终是正确的。这个建议直到AMD系列15h(推土机)才被修改,这是对微体系结构的完全重新设计,而不是基于以前的任何设计。
fldpi
fild dword [fVal]
fdivp st(1), st(0) ; st(0) = pi / fVal
fptan ; st(0) = tan(st(0))
; st(1) = 1.0
fstp qword [fSt0]
fstp qword [fSt1]
fld1
fld1