14. 指令解码(PPro,PII 和 PIII)

优质
小牛编辑
130浏览
2023-12-01

在此我先讲指令解码,然后再讲取指令。 因为要理解取指令时发生的延迟,你必须先知道解码器的工作原理。

只有在一些条件满足的情况下,解码器才能在一个时钟周期内解码3条指令。
解码器D0能够处理所有的在一个时钟周期内最多产生4条微码的指令。 解码器D1和D2只能处理那些只产生1条微码的指令,而且那些指令长度不能超过8字节。

概述同一个时钟周期内解码2或3条指令的规则如下:

  • 第一条指令(由D0解码)产生的微码不能超过4条
  • 第二、三两条指令都只能产生1条微码
  • 第二、三两条指令长度都不能超过8个字节
  • 这些指令都要在同一个16字节的ifetch块中(见下一章)

在D0中的指令长度没有限制(尽管Intel手册中提到了一些),只要这三条指令能放入一个16字节的ifetch块。

产生4条以上微码的指令需要2个或更多时钟周期来解码,并且在这个过程中没有其它的指令可以并行解码。

根据以上规则,我们得出结论:一个时钟周期内解码器至多产生6条微码(如果第一条指令产生4条微码,后两条指令各产生1条微码);至少产生2条微码(如果所有指令都产生2条微码,这时D1和D2没法用)。

为了达到最大吞吐量,推荐你把代码组织成4-1-1模式:产生2-4条微码的指令可以"免费"附带2条产生1条微码的简单指令,某种意义上不增加解码时间,比如:

MOV EBX, [MEM1] ; 1条微码 (D0)
INC EBX ;1条微码 (D1)
ADD EAX, [MEM2] ; 2条微码 (D0)
ADD [MEM3], EAX ; 4条微码 (D0)

解码要花去3个时钟。 重组代码使它们进入两个解码组可以节省一个周期:

ADD EAX, [MEM2] ; 2条微码 (D0)
MOV EBX, [MEM1] ; 1条微码 (D1)
INC EBX ; 1条微码 (D2)
ADD [MEM3], EAX ; 4条微码 (D0)

现在解码器在2个时钟周期内产生8条微码,应该比较满意了。因为流水线的后续阶段只能在一个时钟周期内处理3条微码,所以大于3条/周期的解码吞吐率你就可以认为解码不是瓶颈了。然而,就像后面的章节描述的那样,取指令机制的复杂性可能会使解码延迟,因此安全起见,你的目标是每个时钟周期的解码吞吐率大于3。

你可以在29章的列表中查出各种指令产生的微码数。

在解码时,前缀也可能带来惩罚。 指令能够有这样一些前缀:

操作数尺寸前缀

当你在32位环境中有一个16位操作数时将用到,反之亦然(除了那些操作数只能有一种尺寸的指令,比如FNSTSW AX)。

当指令有一个16或32位的立即操作数时,操作数尺寸前缀会带来几个周期的惩罚,因为操作数的长度被前缀改变了。 比如:

ADD BX, 9 ;

因为立即操作数是8位,故没有惩罚

MOV WORD PTR [MEM16], 9 ;  因为操作数是16位,有惩罚

后一条指令应该被替换成:

MOV EAX, 9 MOV WORD PTR [MEM16], AX;  没惩罚,因为没有立即数

地址尺寸前缀

当你在16位模式下用32位地址时用到,反之亦然。它很少用到,一般应该避免。每当你有一个显式的内存操作数时(甚至有时没有偏移量),地址尺寸前缀导致一次惩罚。因为指令编码中指明r/m的位被前缀改变了。
只有隐式内存操作数的指令,比如串操作指令,即使有了地址尺寸前缀也没有惩罚。

段前缀

当你需要定位非默认的数据段时需要用到。 在 PPro,PII 和 PIII 上没有因段前缀而带来的惩罚。

重复前缀和锁前缀在解码时没有惩罚

当你的前缀多于一个时总是有惩罚

一般惩罚是每个前缀一个周期。