gcc 內嵌彙編的學習筆記 I

來源:互聯網
上載者:User

gcc 內嵌彙編的學習筆記 I
                           --第一個混合編碼的加法計算機
  作者:ShellEx. 
  ShellEx.cn && blog.csdn.net/shellex 著作權
  
寫了一段簡單的代碼:

#include <stdio.h>
int main() {
 int in1 = 0, in2 = 0, out = 0;
 printf("PLZ input 2 Number like this: (x1 + x2) /n");
 scanf("%d + %d", &in1, &in2);
 asm volatile(
  "add %1, %0/n/t"
  "add %2, %0/n/t"
  "nop/n/t"
  :"=r"(out)
  :"r"(in1),"r"(in2)
  :
 );
 printf("%d + %d = %d./n", in1, in2, out);
 return 0;
}
///////////////////////////////////////////////////////////////
輸入兩數,求他們的和。心想估計沒有問題,就編譯運行。
運行結果:
PLZ input 2 Number like this: (x1 + x2)
2+3
2 + 3 = 10.
暈一個,和我想的不一樣啊。去看看彙編代碼吧(gcc加上-S參數可以生
成彙編源檔案):

...
 movl $0, -4(%ebp)
 movl $0, -8(%ebp)
 movl $0, -12(%ebp)
...
 movl -4(%ebp), %edx
 movl -8(%ebp), %eax
/APP
 add %edx, %eax
 add %eax, %eax
 nop
 
/NO_APP
 movl %eax, -12(%ebp)
 movl -12(%ebp), %eax
...
///////////////////////////////////////////////////////////////
以上代碼僅僅關鍵區段,前後有省略。在/APP和/NO_APP之間的是內嵌匯
編部分。可以看到,gcc先把-4(%ebp)和-8(%ebp)(分別是in1和in2)
放入%edx和%eax寄存器,然後執行第一個add使得eax儲存有兩個數的和,
第二個add又把eax加在自己身上,最後把eax的值賦給-12(%ebp)也就是
out.功能大約相當於下面的C++代碼:
tmp1 = in1;
tmp2 = in2;
tmp1 += tmp2;
tmp1 += tmp1;
out = tmp1;
結果當然不對啦。這是由於gcc不知道錯誤地使用eax寄存器造成的。他並
不知道%2和%0用了同一個寄存器呢。所以我在輸出部分加入"&"限制符。

 asm volatile(
  "add %1, %0/n/t"
  "add %2, %0/n/t"
  "nop/n/t"
  :"=&r"(out)
  :"r"(in1),"r"(in2)
  :
 );
 
///////////////////////////////////////////////////////////////
編譯執行:
PLZ input 2 Number like this: (x1 + x2)
2+3
2 + 3 = 2009143897.
又出現了奇怪的問題。再看看彙編代碼:

 movl -4(%ebp), %edx
 movl -8(%ebp), %eax
/APP
 add %edx, %ecx
 add %eax, %ecx
 nop
 
/NO_APP
 movl %ecx, %eax
 movl %eax, -12(%ebp)
///////////////////////////////////////////////////////////////
貌似沒問題,其實不然。存放結果的ecx寄存器並沒有一開始就把out
的值讀入(不像in1 和 in2,被讀入edx和eax)。說明gcc對於使用"r"
限制符的輸出變數,並沒有像對待輸入變數那樣分配完寄存器後就讀入
值。資料表明,這個行為可能是由於AT&T彙編源於CISC架構處理器的匯
編語言,在CISC架構處理器中大部分指令輸入輸出明顯分開,而不像
RISC架構處理器那樣一個運算元可以輸出也可以輸入。
所以我應該讓gcc知道,我的out也應該享受同等待遇。

#include <stdio.h>
int main() {
 int in1 = 0, in2 = 0, out = 0;
 printf("PLZ input 2 Number like this: (x1 + x2) /n");
 scanf("%d + %d", &in1, &in2);
 asm volatile(
  "add %1, %0/n/t"
  "add %2, %0/n/t"
  "nop/n/t"
  :"=&r"(out)
  :"r"(in1),"r"(in2),"0"(out)
  :
 );
 printf("%d + %d = %d./n", in1, in2, out);
 return 0;
}
///////////////////////////////////////////////////////////////
在輸入部分添加對out的描述,並且用限定符"0"指定out就是輸入部分的
那個%0.因為gcc是不會去判斷%1和%3是否關聯自同一個變數的。所以需要
顯性指定。
彙編代碼如下。

 movl -4(%ebp), %ecx
 movl -8(%ebp), %edx
 movl -12(%ebp), %eax
/APP
 add %ecx, %eax
 add %edx, %eax
 nop
 
/NO_APP
 movl %eax, -12(%ebp)
///////////////////////////////////////////////////////////////
編譯啟動並執行輸出也是正確的了:
PLZ input 2 Number like this: (x1 + x2)
2+3
2 + 3 = 5. 
但是這麼寫真是麻煩。其實有個更簡單的方法,就是使用"+"限定符。表
示運算元可以讀和寫。
 
 asm volatile(
  "add %1, %0/n/t"
  "add %2, %0/n/t"
  "nop/n/t"
  :"+r"(out)
  :"r"(in1),"r"(in2)
  :
 );
/////////////////////////////////////////////////////////////// 
彙編代碼如下:

 movl -12(%ebp), %edx
 movl -4(%ebp), %ecx
 movl -8(%ebp), %eax
/APP
 add %ecx, %edx
 add %eax, %edx
 nop
 
/NO_APP
 movl %edx, -12(%ebp)
 
/////////////////////////////////////////////////////////////// 
沒想到才剛剛開始就遇到這麼多問題,記錄下來,就算是學習筆記。既然
是筆記,如果看官們發現錯誤,請一定不吝賜教。謝謝觀賞。

shellex.cn && blog.csdn.net/shellex 著作權 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.