原文
假設有個想要將一個32
位值傳遞
給一個帶64
位值的函數的函數.你不關心高32
位的內容,因為該值是傳遞給回調函數
的直通值,回調函數
會把它截斷為32
位值.
因此,你都擔心編譯器
一般生成的將32
位值擴展到64
位值的那條指令的性能影響
.
我懷疑這條指令
不是程序中的性能瓶頸
.
我想出的是說:可不執行任何指令
從32
位值生成64
位值"的gcc/clang
內聯匯編.
int64_t int32_to_64_garbage(int32_t i32)
{int64_t i64;__asm__("" ://閑著"=r"(i64) ://在`寄存器`中生成結果"0"(i32));//從此最后的輸入return i64;
}
__asm__
內聯指令
的第一個參數
是要生成的代碼.傳遞一個空串
,所以實際上未生成任何代碼
!想要的所有效果
都在輸入
和輸出
的聲明中.
接著是只有一個的輸出
."=r"(i64)
表示內聯匯編
會在編譯器
選擇的r寄存器
中,放入i64
的覆蓋(=)
值,內聯匯編器
按%0
引用的.輸出
從0開始編號.
最后,有這里只有一個的輸入
."0"(i32)
表示輸入應在輸出
的0數字
位置放置.
所有工作
都是根據輸入和輸出
的約束來完成的.沒有實際的代碼
.告訴編譯器
,在一個寄存器
中放入i32
,然后遮住眼睛,睜開時,在同一個寄存器
中變成i64
!
在3級優化
中運行gcc
,顯示完全省略
了該值.
void somewhere(int64_t);
void sample1(int32_t v)
{somewhere(v);
}
void sample2(int32_t v)
{somewhere(int32_to_64_garbage(v));
}
結果是:
//x86-64
sample1(int):movsx rdi, edijmp somewhere(long)
sample2(int):jmp somewhere(long)//ARM32
sample1(int):asrs r1, r0, #31b somewhere(long long)
sample2(int):b somewhere(long long)//ARM64
sample1(int):sxtw x0, w0b somewhere(long)
sample2(int):b somewhere(long)
第一個版本在尾
調用之前,包含顯式符號擴展
指令.第二個版本是直接尾調用
,在rdi
,寄存器
高32
位中使用任意垃圾
.
另一個支持gcc
擴展內聯語法的編譯器
是icc
,該技巧似乎也有效
.
//x86-64
sample1(int):movsxd rdi, edijmp somewhere(long)
sample2(int):jmp somewhere(long)
clang``編譯器
還支持gcc
擴展內聯匯編
語法.但是,它不僅會生成轉換
,而且還會丟失尾調用
.
//x86-64
sample1(int):movsxd edi, edijmp somewhere(long)@PLT
sample2(int):push raxmov edi, edicall somewhere(long)@PLTpop raxret//ARM32
sample1(int):asr r1, r0, #31b somewhere(long long)
sample2(int):push {r11, lr}sub sp, sp, #8mov r1, #0bl somewhere(long long)add sp, sp, #8pop {r11, pc}//ARM64
sample1(int):sxtw x0, w0b somewhere(long)
sample2(int):sub sp, sp, #32stp x29, x30, [sp, #16]add x29, sp, #16mov w0, w0bl somewhere(long)ldp x29, x30, [sp, #16]add sp, sp, #32ret
更新:似乎當前版本的clang
(當前時)恢復了尾調用
,盡管它仍執行32
到64
的正轉換
,因此成本基本相同
.
//x86-64
sample1(int):movsxd edi, edijmp somewhere(long)@PLT
sample2(int):mov edi, edijmp somewhere(long)@PLT//ARM32
sample1(int):asr r1, r0, #31b somewhere(long long)
sample2(int):mov r1, #0b somewhere(long long)//ARM64
sample1(int):sxtw x0, w0b somewhere(long)
sample2(int):mov w0, w0b somewhere(long)
VC++``編譯器
不支持gcc
擴展內聯語法,因此無法檢查.
因為msvc
完全不管用,并且對clang
沒有任何好處,因此我只會在使用gcc
或icc``編譯時
允許此優化,并在其他地方
使用額外指令
.