linux中C嵌arm彙編 __asm__ __volatile__

本文轉自:http://blog.csdn.net/geekcome/article/details/6216436 ,本人只添加了一些註釋

帶有C/C++表達式的內聯彙編格式:

__asm__ __volatile__(“Instructionlist”:Output:Input:Clobber/Modify)

  1. __asm__

    __asm__GCC關鍵字asm的宏定義:

    #define __asm__ asm

    __asm__asm用來聲明一個內聯彙編表達式,所以任何一個內聯彙編表達式都以它開頭,是必不可少的。

  2. __volatile__

    __volatile__GCC關鍵字volatile的宏定義:

    #define __volatile__ volatile

    __volatile__volatile是可選的,如果用了它,則向GCC聲明不允許對該內聯彙編優化,否則,當使用優化選項(-o)進行編譯時GCC會根據字自己的判斷決定是否將內聯彙編表達式的指令優化掉。

  3. Instruction list

    a. Instruction list是彙編指令序列,它可以是空,比如:

    __asm__ __volatile__(“”);或__asm__ (“”);是合法的內聯彙編表達式,但是它們沒有意義。

    b. 但是__asm__ __volatile__(“” ::: ”memory”,它向GCC聲明,內存做了改動,GCC在編譯的時候,會將此因素考慮進去。在訪問IO端口和IO內存時;會用到內存屏障:

    include/linux/compiler-gcc.h:

    #define barrier() __asm____volatile__("": : :"memory")

    它就是防止編譯器對讀寫IO端口和IO內存指令的優化而實際的錯誤。

    c. Instructionlist中有多條指令的時候,可以在一對引號中列出全部指令;也可以將一條或幾條指令放在一對引號中,所有指令放在多對引號中。如果是前者,可以將所有指令放在一行,則必須用分號(;)或換行符(/n)將它們分開:

    static inline int atomic_add_return(int i, atomic_t *v)

    __asm__ __volatile__("@ atomic_add_return\n"     // @開始的內容是註釋

    "1: ldrex %0, [%2]\n"            // 1:是代碼中的局部標籤

    " add %0, %0, %3\n"

    " strex %1, %0, [%2]\n"

    " teq %1, #0\n"

    " bne 1b"                    //向後跳轉到1處執行,b表示backward; bne 1f,表示向前跳轉到1

    : "=&r" (result), "=&r" (tmp)             // %0, %1

    : "r" (&v->counter), "Ir" (i)              //%2, %3

    : "cc");

  4. Output

    用來指定當前內聯彙編的輸出

  5. Input

用來指定當前內聯彙編的輸入。

OutputInput中,格式爲形如”constraint”(variable)的列表,用逗號分隔。如:

: "=&r" (result), "=&r" (tmp)

: "r" (&v->counter), "Ir" (i)

    1. Clobber/Modify

      有時候,當你想通知GCC當前內聯彙編語句可能對某些寄存器和內存進行修改,希望GCC將這一點考慮進去,此時就可以在Clobber/Modify域中進行聲明這些寄存器和內存。

這種情況一般發生在一個寄存器出現在Instructionlist,但不是有Output/Input操作表達式所指 定的,也不是在一些Output/Input操作表達式使用“r”約束時有GCC爲其選擇的,同時此寄存器被Instructionlist修改,而這個寄存器只是供當前內聯彙編使用的情況。

例如:

__asm__ (“mov R0, #0x34” ::: “R0”)

寄存器R0出現在Instructionlist中,且被mov指令修改,但卻未被任何Output/Input操作表達式指定,所以需要在Clobber/Modify域中指定“R0”,讓GCC知道這一點。

因爲你在Output/Input操作表達式所指定的寄存器,或當你爲一些Output/Input表達式使用“r”約束,上GCC爲你選擇一個寄存器,寄存器對這些寄存器是非常清楚的, 它知道這些寄存器是被修改的,不需要在Clobber/Modify域中在聲明它們。除此之外,GCC對剩下的寄存器中那些會被當前內聯彙編修改一無所知。所以,如果當前內聯彙編修改了這些寄存器,就最好在Clobber/Modify域中聲明,讓GCC針對這些寄存器做相應的處理,否則可能會造成寄存器 的不一致,造成程序執行錯誤。

如果一個內聯彙編語句的Clobber/Modify域存在"memory",那麼GCC會保證在此內聯彙編之前,如果某個內存的內容被裝入了寄存器,那麼在這個內聯彙編之後,如果需要使用這個內存處的內容,就會直接到這個內存處重新讀取,而不是使用被存放在寄存器中的拷貝。因爲這個時候寄存器中的拷貝已經很可能和內存處的內容不一致了。

這只是使用"memory"時,GCC會保證做到的一點,但這並不是全部。因爲使用"memory"是向GCC聲明內存發生了變化,而內存發生變化帶來的影響並不止這一點。



intmain(int __argc, char* __argv[]) 

int* __p =(int*)__argc; 
(*__p) =9999; 
__asm__("":::"memory"); 
if((*__p)== 9999) 
return 5; 
return (*__p); 
}

本例中,如果沒有那條內聯彙編語句,那個if語句的判斷條件就完全是一句廢話。GCC在優化時會意識到這一點,而直接只生成return5的彙編代碼,而不會再生成if語句的相關代碼,而不會生成return(*__p)的相關代碼。但你加上了這條內聯彙編語句,它除了聲明內存變化之外,什麼都沒有做。但GCC此時就不能簡單的認爲它不需要判斷都知道(*__p)一定與9999相等,它只有老老實實生成這條if語句的彙編代碼,一起相關的兩個return語句相關代碼。

另外在linux內核中內存屏障也是基於它實現的include/asm/system.h

#define barrier() _asm__volatile_("": : :"memory")

主要是保證程序的執行遵循順序一致性。呵呵,有的時候你寫代碼的順序,不一定是最終執行的順序,這個是處理器有關的。











發佈了47 篇原創文章 · 獲贊 8 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章