[英]How to write a short block of inline gnu extended assembly to swap the values of two integer variables?
為了娛樂,我正在學習針對32位Linux目標使用針對x86的AT&T語法的gnu擴展程序集。 我剛剛花了最后三個小時來編寫兩個可能的解決方案,以應對交換兩個整數變量a
和b
的值的挑戰,但是我的解決方案都不能完全解決我的問題。 首先,讓我們更詳細地了解我的TODO障礙:
int main()
{
int a = 2, b = 1;
printf("a is %d, b is %d\n", a, b);
// TODO: swap a and b using extended assembly, and do not modify the program in any other way
printf("a is %d, b is %d\n", a, b);
}
閱讀此HOWTO之后 ,我編寫了以下內聯擴展匯編代碼。 這是我第一次嘗試交換整數:
asm volatile("movl %0, %%eax;"
"movl %1, %%ecx;"
"movl %%ecx, %0;"
: "=r" (a)
: "r" (b)
: "%eax", "%ecx");
asm volatile("movl %%eax, %0;"
: "=r" (b)
: "r" (a)
: "%eax", "%ecx");
我的理由是要設置a = b,我需要一個與程序集分離的擴展程序集調用,以設置b = a。 因此,我編寫了兩個擴展程序集調用,編譯了我的代碼,即gcc -m32 asmPractice.c,並運行了a.out。 結果如下:
a是2,b是1
a是1,b是1
看到這不能正常工作后,我決定合並兩個擴展的匯編程序調用,並寫成這樣:
asm volatile("movl %0, %%eax;"
"movl %1, %%ecx;"
"movl %%ecx, %0;"
"movl %%eax, %1;"
: "=r" (a)
: "r" (b));
重新編譯和鏈接后,我的代碼仍然無法正確交換兩個值。 你自己看。 這是我的結果:
a是2,b是1
a是1,b是1
以下是評論中的一些解決方案:
解決方案#0(最佳選擇): https : //gcc.gnu.org/wiki/DontUseInlineAsm
甚至零指令的解決方案也無法克服常數傳播,以及涉及gcc知道任何有關值的任何其他優化。 這也迫使編譯器此時將兩個變量同時保存在寄存器中。 在考慮使用inline-asm而不是buildins / intrinsics時,請始終牢記這些缺點。
解決方案#1:x86 xchg
,沒有臨時寄存器,並且可以在AT&T和Intel語法模式下使用。 成本與大多數Intel CPU上的3 mov
指令相同,而在某些AMD上的成本僅為2 uops。
asm("xchg %0, %1;" : "+r" (a), "+r" (b));
解決方案2:僅使用GNU C內聯asm約束。 (獎金:可移植到所有架構)
asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
查看在Godbolt編譯器資源管理器上運行的所有三種解決方案,包括擊敗優化的示例:
int swap_constraints(int a, int b) {
asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
return a;
}
// Demonstrate the optimization-defeating behaviour:
int swap_constraints_constants(void) {
int a = 10, b = 20;
return swap_constraints(a, b) + 15;
}
swap_constraints_constants:
movl $10, %edx
movl $20, %eax
addl $15, %eax
ret
與純C交換:
swap_noasm_constants:
movl $35, %eax # the add is done at compile-time, and `a` is optimized away as unused.
ret
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.