2017-08-28 175 views
2

對於娛樂,我正在學習GNU擴展程序集,使用帶有32位Linux目標的x86的AT & T語法。我剛剛花了最後三個小時編碼兩個可能的解決方案,以交換兩個整數變量ab的值,我的解決方案都不能完全解決我的問題。首先,讓我們來看看我的TODO障礙在一些細節:如何編寫一個內嵌gnu擴展程序集的短塊來交換兩個整數變量的值?

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); 
} 

閱讀this 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

+1

既然你傳遞寄存器你可以只要做'xchg%0,%1'。使用移動只需要1個臨時寄存器。將%0複製到該寄存器。然後將%1複製到%0,然後將臨時寄存器複製到%1。臨時將需要列在clobber列表中 –

+0

另請參見XOR交換算法https://en.wikipedia.org/wiki/XOR_swap_algorithm –

+1

您現有的行內彙編還存在問題,即a和b都是輸入和輸出。所以** both **應該使用讀寫約束''+ r「' –

回答

2

以下是評論的一些解決方案:

解決方案#0 (最好的選擇):https://gcc.gnu.org/wiki/DontUseInlineAsm
即使是零指令解決方案也會影響常量傳播,以及任何其他優化,這些優化會讓gcc知道有關值的任何信息。它還會強制編譯器在此時同時在寄存器中同時存在兩個變量。在考慮使用inline-asm而不是builtins/intrinsics時,請始終記住這些缺點。

解決方案#1:xchg,其成本與大多數CPU上的指令3 mov大致相同。

asm("xchg %0, %1;" : "+r" (a), "+r" (b)); 

解決方案#2:純粹使用GNU C inline asm約束。

asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b)); 

見行動on the Godbolt compiler explorer所有三種解決方案,其中包括擊敗優化他們的例子:

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 
+0

你不需要一個早期的clobber,因爲'xchg'是一個單一的指令。 –

+1

@PeterCordes我在評論中犯了一個錯字。本來是爲了%不是,因爲我在談論交換性質。他把我的錯誤傳達給了答案。 –

+0

@MichaelPetch我認爲[交換](https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html)只適用於輸入(又名「只讀操作數」)。 –

相關問題