簡體   English   中英

使用內聯匯編優化C ++代碼時出錯

[英]Error when optimizing C++ code with inline assembly

我正在嘗試學習內聯匯編,並在匯編中實現了Euclid算法! 現在,當我嘗試使用

g++ filename -O1

它正在編譯並且運行良好,但是當我嘗試使用

clang++-3.6 filename -O1

代碼正在編譯,但產生分段錯誤!

當我嘗試使用-O2或更高的標志運行代碼時, gccclang產生編譯時錯誤!

g ++錯誤

eculid.cpp: Assembler messages:
eculid.cpp:19: Error: symbol `CONTD' is already defined
eculid.cpp:19: Error: symbol `DONE' is already defined

鐺錯誤

eculid.cpp:7:5: error: invalid symbol redefinition
                            "movl %1, %%eax;"
                            ^
<inline asm>:1:34: note: instantiated into assembly here
    movl %eax, %eax;movl %ecx, %ebx;CONTD: cmpl $0, %ebx;je DONE;xor...
                                    ^
eculid.cpp:7:5: error: invalid symbol redefinition
                            "movl %1, %%eax;"
                            ^
<inline asm>:1:132: note: instantiated into assembly here
  ...%edx;idivl %ebx;movl %ebx, %eax;movl %edx, %ebx;jmp CONTD;DONE: movl %ea...
                                                           ^
2 errors generated.

這是我的代碼

#include <iostream>
using namespace std;

int gcd(int var1, int var2) {
    int result = 0;
    __asm__ __volatile__ (
            "movl %1, %%eax;"
            "movl %2, %%ebx;"
            "CONTD: cmpl $0, %%ebx;"
            "je DONE;"
            "xorl %%edx, %%edx;"
            "idivl %%ebx;"
            "movl %%ebx, %%eax;"
            "movl %%edx, %%ebx;"
            "jmp CONTD;"
            "DONE: movl %%eax, %0;"
            :"=r"(result)
            :"r"(var1), "r"(var2)
        );
    return result;
}

int main(void) {

    int first = 0, second = 0;
    cin >> first >> second;
    cout << "GCD is: " << gcd(first, second) << endl;

    return 0;
}

您可以在此處檢查我的代碼(由我的編譯器產生的錯誤)

在此處輸入圖片說明

只需將其放入答案表單即可關閉問題(如果回答了您的問題,請單擊此答案旁邊的復選標記),最簡單的是,您需要像這樣更改代碼:

__asm__ __volatile__ (
        "movl %1, %%eax;"
        "movl %2, %%ebx;"
        "CONTD%=: cmpl $0, %%ebx;"
        "je DONE%=;"
        "xorl %%edx, %%edx;"
        "idivl %%ebx;"
        "movl %%ebx, %%eax;"
        "movl %%edx, %%ebx;"
        "jmp CONTD%=;"
        "DONE%=: movl %%eax, %0;"
        :"=r"(result)
        :"r"(var1), "r"(var2)
        : "eax", "ebx", "edx", "cc"
    );

使用%=將唯一的數字添加到標識符以避免沖突。 並且由於寄存器和標志的內容正在修改,因此您需要通過“籠罩”它們來告知編譯器該事實。

但是您還可以執行其他一些操作,以使此操作更快,更干凈。 例如,您可以告訴gcc當塊退出時, result將在eax中,而不是在最后進行movl %%eax, %0

__asm__ __volatile__ (
        "movl %1, %%eax;"
        "movl %2, %%ebx;"
        "CONTD%=: cmpl $0, %%ebx;"
        "je DONE%=;"
        "xorl %%edx, %%edx;"
        "idivl %%ebx;"
        "movl %%ebx, %%eax;"
        "movl %%edx, %%ebx;"
        "jmp CONTD%=;"
        "DONE%=:"
        :"=a"(result)
        :"r"(var1), "r"(var2)
        : "ebx", "edx", "cc"
    );

同樣,您可以告訴gcc在調用該塊之前先將var1和var2放入eax和ebx中,而不是在該塊內手動進行操作:

__asm__ (
        "CONTD%=: cmpl $0, %%ebx;"
        "je DONE%=;"
        "xorl %%edx, %%edx;"
        "idivl %%ebx;"
        "movl %%ebx, %%eax;"
        "movl %%edx, %%ebx;"
        "jmp CONTD%=;"
        "DONE%=:"
        :"=a"(result), "+b"(var2)
        : "a"(var1)
        : "edx", "cc"
    );

另外,由於(大概)在調用gcd時將始終使用result,因此不需要volatile。 如果您將不使用結果,那么無論如何都沒有必要強制執行計算。

如所寫,此語句的-S輸出將是很長的一行,使得調試困難。 這使我們能夠:

__asm__ (
   "CONTD%=:              \n\t"
      "cmpl $0, %%ebx     \n\t"
      "je DONE%=          \n\t"
      "xorl %%edx, %%edx  \n\t"
      "idivl %%ebx        \n\t"
      "movl %%ebx, %%eax  \n\t"
      "movl %%edx, %%ebx  \n\t"
      "jmp CONTD%=        \n"
   "DONE%=:"
   : "=a"(result), "+b"(var2)
   : "a"(var1)
   : "edx", "cc"
);

而且我認為沒有特別的理由強迫gcc使用ebx。 如果我們讓gcc選擇自己的寄存器(通常會提供最佳性能),那么我們可以:

__asm__ (
   "CONTD%=:              \n\t"
      "cmpl $0, %1        \n\t"
      "je DONE%=          \n\t"
      "xorl %%edx, %%edx  \n\t"
      "idivl %1           \n\t"
      "movl %1, %%eax     \n\t"
      "movl %%edx, %1     \n\t"
      "jmp CONTD%=        \n"
   "DONE%=:"
   : "=a"(result), "+r"(var2)
   : "a"(var1)
   : "edx", "cc"
);

最后,避免在循環完成時產生額外的跳動,可以使我們:

__asm__ (
      "cmpl $0, %1        \n\t"
      "je DONE%=          \n"
   "CONTD%=:              \n\t"
      "xorl %%edx, %%edx  \n\t"
      "idivl %1           \n\t"
      "movl %1, %%eax     \n\t"
      "movl %%edx, %1     \n\t"
      "cmpl $0, %1        \n\t"
      "jne CONTD%=        \n"
   "DONE%=:"
   : "=a"(result), "+r"(var2)
   : "a"(var1)
   : "edx", "cc"
);

查看gcc的-S輸出,可以看到:

   /APP
        cmpl $0, %ecx
        je DONE31
   CONTD31:
        xorl %edx, %edx
        idivl %ecx
        movl %ecx, %eax
        movl %edx, %ecx
        cmpl $0, %ecx
        jne CONTD31
   DONE31:
   /NO_APP

與原始代碼相比,此代碼使用更少的寄存器,執行更少的跳轉,並且擁有更少的asm指令。 FWIW。

有關%=,clobbers等的詳細信息,請查看內聯asm的官方gcc文檔

我想我應該問一個問題,為什么您覺得需要用asm編寫而不是僅僅用c編寫,但是我假設您有充分的理由。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM