int main()
{
00211000 push ebp
00211001 mov ebp,esp
00211003 sub esp,10h
char charVar1;
short shortVar1;
int intVar1;
long longVar1;
charVar1 = 11;
00211006 mov byte ptr [charVar1],0Bh
shortVar1 = 11;
0021100A mov eax,0Bh
0021100F mov word ptr [shortVar1],ax
intVar1 = 11;
00211013 mov dword ptr [intVar1],0Bh
longVar1 = 11;
0021101A mov dword ptr [longVar1],0Bh
}
其他資料型別不通過暫存器,但只有短型別通過暫存器。怎么了?
uj5u.com熱心網友回復:
GCC 做同樣的事情,使用mov reg, imm32/mov m16, reg而不是mov mem, imm16.
這是為了避免在 Intel P6 系列 CPU 上從 16-bit operand-size 停止 LCP mov imm16。
與沒有前綴的相同機器代碼位元組相比,當前綴更改指令其余部分的長度時,會發生 LCP(長度更改前綴)停頓。
mov word ptr [ebp - 8], 11將涉及一個66前綴,使指令的其余部分為 5 個位元組(操作碼 modrm disp8 imm16),而不是相同操作碼 / modrm 的 7 個(操作碼 modrm disp8 imm32)。)
66 c7 45 f8 0b 00 mov WORD PTR [ebp-0x8],0xb
c7 45 f8 0b 00 00 00 mov DWORD PTR [ebp-0x8],0xb
^
opcode
這種長度變化混淆了在機器代碼塊被路由到實際解碼器之前發生的指令長度查找階段(預解碼)。他們被迫備份并使用一種較慢的方法來解釋他們查看操作碼的方式中的前綴。(x86 機器碼的并行解碼很難)。根據微體系結構和指令的對齊方式,此備份的損失可能高達 11 個周期,應盡可能避免。
請參閱長度更改前綴 (LCP) 是否會導致簡單 x86_64 指令停止?有關長度更改前綴停止是什么的詳細資訊,以及在 Intel P6 和 SnB 系列 CPU 中停止預解碼階段幾個周期的性能影響,以及 Sandybridge 系列(現代主流 Intel)特殊情況mov避免 16 位立即數導致 LCP 停止的操作碼。
mov 特別是在現代英特爾上沒有問題
Sandybridge 系列mov專門移除了 LCP 停頓(對于其他指令仍然存在),因此此調整決定僅對 Nehalem 和更早版本有幫助。
AFAIK,這不是 Silvermont 系列的事情,也不是任何 AMD 的事情,所以這可能是 MSVC 和 GCC 應該為他們更新的東西,tune=generic因為這些天 P6 系列 CPU 的相關性越來越低。(如果 GCC / MSVC 的最新開發版本現在發生了變化,那么在使用新編譯器構建大量軟體發行版 / 版本之前還需要一年左右的時間。)
clang不做這個優化,即使在舊的 P6 系列 CPU 上也不是災難,因為大多數軟體不使用很多short/int16_t變數。(瓶頸并不總是前端,通常是快取未命中。)
例子
為這個函式存盤到堆疊當然是由于沒有啟用優化。由于這些變數不是volatile,因此應該完全優化它們,因為以后不會讀取它們。當你想制作 asm 輸出的例子時,不要寫 a main,寫一個必須有一些副作用的函式,例如通過指標存盤,或者使用volatile。
void foo(short *p){
volatile short x = 123;
*p = 123;
}
使用 MSVC 19.14 -O2( https://godbolt.org/z/eWhzhEsEa ) 編譯:
x$ = 8
p$ = 8
foo PROC ; COMDAT
mov eax, 123 ; 0000007bH
mov WORD PTR x$[rsp], ax
mov WORD PTR [rcx], ax
ret 0
foo ENDP
或者使用 GCC11.2 -O3,這更糟糕,而不是CSEing /重用暫存器常量
foo:
mov eax, 123
mov edx, 123
mov WORD PTR [rsp-2], ax
mov WORD PTR [rdi], dx
ret
但我們可以看到這是自-O3 -march=znver1(AMD Zen 1) 以來的英特爾調整:
foo:
mov WORD PTR [rsp-2], 123
mov WORD PTR [rdi], 123
ret
不幸的是,它仍然會為movwith避免 LCP -march=skylake,因此它不知道完整的規則。
如果我們使用*p = 12345;(一個大到不適合 an 的數字imm8,與 mov 不同的是 add 允許) 而不是 just =,具有諷刺意味的是,GCC 然后使用長度改變前綴-march=skylake(MSVC 也是如此),創建一個停頓: add WORD PTR [rdi], 12345.
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/412646.html
標籤:
上一篇:將64位常量移至記憶體
