Assembly 16位端口地址的32位分配
当我翻阅原始Xbox内核的代码时,我注意到有时当它为端口I/O设置寄存器时,它会为Assembly 16位端口地址的32位分配,assembly,io,x86,Assembly,Io,X86,当我翻阅原始Xbox内核的代码时,我注意到有时当它为端口I/O设置寄存器时,它会为edx分配一个32位的值,即使in和out指令只使用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 ... 在其他地方(
edx
分配一个32位的值,即使in
和out
指令只使用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位,那么为它们指定非零位有什么意义呢?我猜这是作为微优化来完成的,以避免不存在的危险和/或微不足道的性能损失 例如,程序员可能最初编写了如下内容:
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 dx
替换为add edx
,以节省一个字节并消除解码操作数大小前缀时的性能损失:
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
然后他在当代英特尔优化手册中读到:
因为奔腾II和奔腾III处理器可以执行
订购时,无需将指示直接相邻,便可完成安装
发生。示例2-7还包含一个部分失速
示例2-7奔腾II和奔腾III处理器的部分寄存器暂停
MOV AL, 8
MOV EDX, 0x40
MOV EDI, new_value
ADD EDX, EAX ; Partial stall accessing EAX
他自己的代码现在看起来很相似,因此他通过将16位MOV
指令替换为您在示例中看到的32位指令来避免部分寄存器暂停。(实际上,我不认为ADD
指令会暂停,In
和OUT
指令应该给MOV
指令足够的时间退出。)
是的,这些微观优化毫无意义。即使它们确实节省了一两个CPU周期,与执行I/O指令所需的时间相比,性能增益也微不足道。但看到微软员工这样做一点也不奇怪。我在微软的代码中看到过比这更愚蠢的东西,在90年代,至少他们似乎非常痴迷于微优化
你所看到的不一致性也不足为奇。微软会有许多不同的程序员在Xbox内核上工作,并且可以很容易地包含来自Windows或其他项目的代码 我在这里推测,但在32位模式下,需要一个操作数大小前缀来将16位立即数移动到寄存器中。解码速度可能会慢一些。更可能的是,程序员使用了整数文本,在平台上默认为有符号32位
int
。如果文本是负数,则可能会产生您看到的代码类型。@EOF我也这么认为,但它们不一致-有时使用16位立即数。此外,由于指令编码,立即数被编码为一个完整的32位整数,即没有符号扩展。这也是我所怀疑的。仍然不确定为什么上面的部分是0xFFFF
,但我想这并不重要。@Drew这可能是内联汇编吗?编译器可能对16位常量进行了符号扩展。@Drew和Ross:add或或
中的操作数大小前缀没有解码惩罚,代码大小除外add dx,0x1E
使用imm8
编码,因此操作数大小前缀不会更改insn其余部分的长度。只有ALU操作上的imm16
操作数才会导致长度更改前缀暂停。哎呀,orig xbox有一个PIII,即使在mov
指令上,LCP也会暂停,所以如果这段代码的解码速度很重要的话,这就是他们要避免的add edx,imm8
只保存一个代码字节,除非in
读取整个注册表并强制合并,否则不会避免部分注册表暂停。@PeterCordes不管这是否是个问题,英特尔奔腾III的优化手册中确实说过“尽可能避免使用前缀指令”我的理论是,应用微优化时没有考虑实际硬件的实际性能优势。例如,他们本可以避免对奔腾更明确的惩罚:“前缀为66h或67h的指令需要一个时钟进行前缀检测,另一个时钟用于长度计算,另一个时钟用于输入FIFO(总共三个时钟周期)。@RossRidge:是的,我得到了你的总体观点。我刚刚指出,最危险的潜在失速可能是16位mov imm上的LCP失速。但关于旧手册的措辞,这是一个很好的观点。你关于盲目遵循优化手册的理论,即使是对于I/O代码,听起来也很有可能。16bitmov
/32bitadd
和32bit或
将最小化代码大小,这是这里的最佳选择。P6部分寄存器暂停比SnB(插入额外uop)更糟糕,但希望即使是如此大量的暂停也不会造成伤害。