2016-11-15 86 views
0

我嘗試製作一個彙編代碼,用於計算字符串中有多少個字符,但出現錯誤。通過字符串彙編循環來計算字符數

碼,我用gcc和intel_syntax

#include <stdio.h> 

int main(){ 
char *s = "aqr b qabxx xryc pqr"; 
int x; 

asm volatile (
    ".intel_syntax noprefix;" 
    "mov eax, %1;" 
    "xor ebx,ebx;" 
    "loop:" 
     "mov al,[eax];" 
     "or al, al;" 
     "jz print;" 
     "inc ebx;" 
     "jmp loop" 
    "print:" 
    "mov %0, ebx;" 
    ".att_syntax prefix;" 
    : "=r" (x) 
    : "r" (s) 
    : "eax", "ebx" 
); 

    printf("Length of string: %d\n", x); 
    return 0; 

} 

,我得到了錯誤:

Error: invalid use of register 

最後,我想使程序,它搜索正則表達式([PQ] [^ A] + a)並打印它的起始位置和長度。我寫在C,但我必須讓它在裝配工作: 我的C代碼:

#include <stdio.h> 
#include <string.h> 

int main(){ 
    char *s = "aqr b qabxx xryc pqr"; 
    int y,i; 
    int x=-1,length=0, pos = 0; 

    int len = strlen(s); 
    for(i=0; i<len;i++){ 
     if((s[i] == 'p' || s[i] == 'q') && length<=0){ 
      pos = i; 
      length++; 
      continue; 
     } else if((s[i] != 'a')) && pos>0){ 
      length++; 
     } else if((s[i] == 'a') && pos>0){ 
      length++; 
      if(y < length) { 
       y=length; 
       length = 0; 
       x = pos; 
       pos = 0;  
      } 
      else 
       length = 0; 
       pos = 0; 
     } 
    } 

    printf("position: %d, length: %d", x, y); 
    return 0; 

} 
+0

是否有錯誤的行號? –

+1

你不應該在編譯器後面切換匯編語法,否則它會替換錯誤。如果您需要intel語法,請使用'-masm = intel'編譯器開關。 – Jester

+0

7行,它開始組裝塊 –

回答

1

jmp loopprint:後省略了分號。


此外您的asm不會正常工作。您將指針移至s到eax,但是您用mov al,[eax]覆蓋它。所以接下來的循環中,eax不再指向字符串了。

當你解決這個問題時,你需要考慮一下這樣一個事實,即每次通過循環都需要改變eax指向下一個字符,否則mov al,[eax]會一直讀取同一個字符。


既然你還沒有接受的答案了(通過單擊勾選左側),還有時間多一個編輯。

通常我不「做人的功課」,但已經過了幾天。推測該作業的到期日期已過。既然如此,這裏有幾個解決方案,既爲OP的教育和未來,因此用戶:

1)繼分配的(有些奇怪)限制:

asm volatile (
    ".intel_syntax noprefix;" 
    "mov eax, %1;" 
    "xor ebx,ebx;" 
    "cmp byte ptr[eax], 0;" 
    "jz print;" 
    "loop:" 
     "inc ebx;" 
     "inc eax;" 
     "cmp byte ptr[eax], 0;" 
     "jnz loop;" 
    "print:" 
    "mov %0, ebx;" 
    ".att_syntax prefix;" 
    : "=r" (x) 
    : "r" (s) 
    : "eax", "ebx" 
); 

2)違反一些的分配規則,使稍好代碼:

asm (
    "\n.intel_syntax noprefix\n\t" 
    "mov eax, %1\n\t" 
    "xor %0,%0\n\t" 
    "cmp byte ptr[eax], 0\n\t" 
    "jz print\n" 
    "loop:\n\t" 
     "inc %0\n\t" 
     "inc eax\n\t" 
     "cmp byte ptr[eax], 0\n\t" 
     "jnz loop\n" 
    "print:\n" 
    ".att_syntax prefix" 
    : "=r" (x) 
    : "r" (s) 
    : "eax", "cc", "memory" 
); 

這將使用1個較少寄存器(無ebx),並省略了(不要)volatile預選賽。它還添加了「cc」clobber來指示代碼修改標誌,並使用「內存」clobber來確保在執行asm之前,對s的任何「掛起」寫入都會刷新內存。它也使用格式(\ n \ t),因此來自-S的建築物的輸出是可讀的。

3),其使用高級版本甚至更少的寄存器(沒有eax),檢查以確保s不是NULL(返回-1),使用符號名,並假定-masm=intel這導致更可讀代碼:

__asm__ (
    "test %[string], %[string]\n\t" 
    "jz print\n" 
    "loop:\n\t" 
     "inc %[length]\n\t" 
     "cmp byte ptr[%[string] + %[length]], 0\n\t" 
     "jnz loop\n" 
    "print:" 
    : [length] "=r" (x) 
    : [string] "r" (s), "[length]" (-1) 
    : "cc", "memory" 
); 

擺脫(任意的,沒有經過深思熟慮的)賦值約束,我們可以將其減少到7行(如果我們不檢查NULL,則爲5;如果我們不計算標籤,則爲3) ])。

有辦法進一步提高本(以避免可能的重複的符號問題,在標籤上使用%=,使用本地標籤(.L),甚至寫它,所以它適用於-masm=intel-masm=att等),但我敢說,這3箇中的任何一個都比原始問題中的代碼更好。


好吧,庫巴,我不確定你在接受答案之前還有什麼更多。不過,它確實給了我一個包含彼得版本的機會。

4)指針增量:

__asm__ (
    "cmp byte ptr[%[string]], 0\n\t" 
    "jz .Lprint%=\n" 
    ".Loop%=:\n\t" 
    "inc %[length]\n\t" 
    "cmp byte ptr[%[length]], 0\n\t" 
    "jnz .Loop%=\n" 
    ".Lprint%=:\n\t"  
    "sub %[length], %[string]" 
    : [length] "=&r" (x) 
    : [string] "r" (s), "[length]" (s) 
    : "cc", "memory" 
); 

這不會做「NULL指針」從3號籤,但它確實做了「指針增量」,彼得被推薦。它還避免了潛在的重複符號(使用%=),並使用「本地」標籤(以.L開頭的那些標籤)以避免額外的符號被寫入目標文件。

從「表現」的角度來看,這可能稍好一點(我還沒有計時)。然而從「學校項目」的角度來看,#3的清晰度似乎是更好的選擇。從「我在真實世界中寫什麼,如果出於一些奇怪的原因,我必須用asm編寫而不是僅僅使用標準的c函數」的觀點來看,我可能會看看使用情況,除非這是性能關鍵,我會試着去#3,以便於日後維護。

+0

我實際上更喜歡指針遞增版本(並非所有的Intel CPU都可以使索引尋址模式微熔合),但是不是在循環中增加一個計數器,只要找到結尾就執行'len = end-start' 。 –

+0

@PeterCordes - 我試着在c中寫這個來找出哪個gcc會生成。據推測這將是「最好的」。不幸的是,它會同時生成兩個,具體取決於如何編寫c代碼。 –

+0

只要循環少於3個ALU uops,無論哪種方式都無關緊要。前端不應該是瓶頸,因此微融合的優化是無關緊要的。 (並且不要認爲gcc知道這一點,這並不完美,我也不認爲它對於不同尋址模式的代價的權重是一個很好的模型,尤其是因爲它在這方面的表現並不完全相同'-mtune = nehalem'與'-mtune = sandybridge'與'-mtune = skylake'(SKL可以再次在OOO核心內部微熔絲索引尋址模式,而不僅僅在解碼器/ uop高速緩存中) –