16 位端口地址的 32 位分配

32-bit assignments for 16-bit port addresses

当我深入研究原始 Xbox 内核的代码时,我注意到有时当它为端口 I/O 设置寄存器时,它会为 edx 分配一个 32 位值,即使inout 指令仅使用 edx 的低 16 位作为端口地址。例如:

mov     edx, 0FFFF8004h
in      ax, dx
or      ax, 1
out     dx, ax
add     edx, 1Eh
in      ax, dx
or      ax, 2
out     dx, ax
mov     edx, 0FFFF8002h
...

其他地方(如SMBus读写)不一致;有时它将 16 位值分配给 dx,有时将 32 位值分配给 edx.

如果从未使用过高 16 位,那么为它们指定非零位有什么意义?

我的猜测是,这是为了避免不存在的危险 and/or 微不足道的性能损失而进行的微优化。

例如,程序员最初可能写了类似这样的东西:

66| BA 8004     mov     dx, 8004h
66| ED          in      ax, dx
66| 83 C8 01    or      ax, 1
66| EF          out     dx, ax
66| 83 C2 1E    add     dx, 1Eh

然后他决定用 add edx 替换 add dx 以节省一个字节并消除解码操作数大小前缀的性能损失:

66| BA 8004     mov     dx, 8004h
66| ED          in      ax, dx
66| 83 C8 01    or      ax, 1
66| EF          out     dx, ax
83 C2 1E        add     edx, 1Eh

然后他在一本当代英特尔优化手册中读到:

Because Pentium II and Pentium III processors can execute code out of order, the instructions need not be immediately adjacent for the stall to occur. Example 2-7 also contains a partial stall.

Example 2-7 Partial Register Stall with Pentium II and Pentium III Processors

MOV AL, 8
MOV EDX, 0x40
MOV EDI, new_value
ADD EDX, EAX        ; Partial stall accessing EAX

他自己的代码现在看起来很相似,因此他通过将 16 位 MOV 指令替换为您在示例中看到的 32 位指令来避免部分寄存器停顿。 (实际上我不认为 ADD 指令会停止,INOUT 指令应该给 MOV 指令足够的时间来退出。)

是的,这些微优化毫无意义。即使他们确实节省了一两个 CPU 周期,与执行 I/O 指令所花费的时间相比,性能提升也微不足道。但是,看到一名 Microsoft 员工这样做也就不足为奇了。我在 Microsoft 代码中见过比这更愚蠢的东西,至少在 90 年代,他们似乎非常痴迷于微优化。

您看到的不一致也不奇怪。微软本来可以让许多不同的程序员在 Xbox 内核上工作,并且可以很容易地包含来自 Windows 或其他项目的代码。