__asm__ __volatile__內嵌彙編用法簡述 在閱讀c/c++原碼時經常會遇到內聯彙編的情況,下面簡要介紹下__asm__ __volatile__內嵌彙編用法。因為我們華清遠見教學平台是arm體系結構的,所以下面的示例都是用arm彙編。
帶有c/c++表示式的內聯彙編格式為:
__asm__ __volatile__("instruction list" : output : input : clobber/modify);
其中每項的概念及功能用法描述如下:
1、 __asm__
__asm__是gcc 關鍵字asm 的巨集定義:
#define __asm__ asm
__asm__或asm 用來宣告乙個內聯彙編表示式,所以任何乙個內聯彙編表示式都是以它開頭的,是必不可少的。
2、instruction list
instruction list 是彙編指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或 __asm__ ("");都是完全合法的內聯彙編表示式,只不過這兩條語句沒有什麼意義。但並非所有instruction list 為空的內聯彙編表示式都是沒有意義的,比如:__asm__ ("":::"memory");
就非常有意義,它向gcc 宣告:「記憶體作了改動」,gcc 在編譯的時候,會將此因素考慮進去。 當在"instruction list"中有多條指令的時候,可以在一對引號中列出全部指令,也可以將一條 或幾條指令放在一對引號中,所有指令放在多對引號中。如果是前者,可以將每一條指令放在一行,如果要將多條指令放在一行,則必須用分號(;)或換行符(\n)將它們分開. 綜上述:(1)每條指令都必須被雙引號括起來 (2)兩條指令必須用換行或分號分開。
例如: 在arm系統結構上關閉中斷的操作
int disable_interrupts (void)
3. __volatile__
__volatile__是gcc 關鍵字volatile 的巨集定義
#define __volatile__ volatile
__volatile__或volatile 是可選的。如果用了它,則是向gcc 宣告不允許對該內聯彙編優化,否則當 使用了優化選項(-o)進行編譯時,gcc 將會根據自己的判斷決定是否將這個內聯彙編表示式中的指令優化掉。
4、 output
output 用來指定當前內聯彙編語句的輸出
例如:從arm協處理器p15中讀出c1值
static unsigned long read_p15_c1 (void)
5、 input
input 域的內容用來指定當前內聯彙編語句的輸入output和input中,格式為形如「constraint」(variable)的列表(逗號分隔)
例如:向arm協處理器p15中寫入c1值
static void write_p15_c1 (unsigned long value)
6.、clobber/modify
有時候,你想通知gcc當前內聯彙編語句可能會對某些暫存器或記憶體進行修改,希望gcc在編譯時能夠將這一點考慮進去。那麼你就可以在clobber/modify域宣告這些暫存器或記憶體。這種情況一般發生在乙個暫存器出現在"instruction list",但卻不是由input/output操作表示式所指定的,也不是在一些input/output操作表示式使用"r"約束時由gcc 為其選擇的,同時此暫存器被"instruction list"中的指令修改,而這個暫存器只是供當前內聯彙編臨時使用的情況。
例如:__asm__ ("mov r0, #0x34" : : : "r0");
暫存器r0出現在"instruction list中",並且被mov指令修改,但卻未被任何input/output操作表示式指定,所以你需要在clobber/modify域指定"r0",以讓gcc知道這一點。
因為你在input/output操作表示式所指定的暫存器,或當你為一些input/output操作表示式使用"r"約束,讓gcc為你選擇乙個暫存器時,gcc對這些暫存器是非常清楚的——它知道這些暫存器是被修改的,你根本不需要在clobber/modify域再宣告它們。但除此之外, gcc對剩下的暫存器中哪些會被當前的內聯彙編修改一無所知。所以如果你真的在當前內聯彙編指令中修改了它們,那麼就最好在clobber/modify 中宣告它們,讓gcc針對這些暫存器做相應的處理。否則有可能會造成暫存器的不一致,從而造成程式執行錯誤。
如果乙個內聯彙編語句的clobber/modify域存在"memory",那麼gcc會保證在此內聯彙編之前,如果某個記憶體的內容被裝入了暫存器,那麼在這個內聯彙編之後,如果需要使用這個記憶體處的內容,就會直接到這個記憶體處重新讀取,而不是使用被存放在暫存器中的拷貝。因為這個 時候暫存器中的拷貝已經很可能和記憶體處的內容不一致了。
這只是使用"memory"時,gcc會保證做到的一點,但這並不是全部。因為使用"memory"是向gcc宣告記憶體發生了變化,而記憶體發生變化帶來的影響並不止這一點。
例如:int main(int __argc, char* __argv)
本例中,如果沒有那條內聯彙編語句,那個if語句的判斷條件就完全是一句廢話。gcc在優化時會意識到這一點,而直接只生成return 5的彙編**,而不會再生成if語句的相關**,而不會生成return (*__p)的相關**。但你加上了這條內聯彙編語句,它除了宣告記憶體變化之外,什麼都沒有做。但gcc此時就不能簡單的認為它不需要判斷都知道 (*__p)一定與9999相等,它只有老老實實生成這條if語句的彙編**,一起相關的兩個return語句相關**。
另外在linux核心中記憶體屏障也是基於它實現的include/asm/system.h中
# define barrier() _asm__volatile_("": : :"memory")
主要是保證程式的執行遵循順序一致性。呵呵,有的時候你寫**的順序,不一定是最終執行的順序,這個是處理器有關的。
「本文由華清遠見提供」
彙編 div Solidity內聯彙編
在用solidity開發以太坊智慧型合約時,使用彙編可以直接與evm互動,降低gas開銷成本,更精細的控制智慧型合約的行為,因此值得solidity開發者學習並加以利用。本文是solidity彙編開發的簡明教程,旨在幫助你快速熟悉如何在solidity智慧型合約 中嵌入彙編 以太坊虛擬機器evm有自...
GCC內聯彙編
有時為了高效,有時為了直接控制硬體,有些模組我們不得不直接用組合語言來編寫,並且對外提供呼叫的介面,隱藏細節,這其實就是內聯彙編。如何使用內聯彙編?我們就以 gcc 為例,一窺其中奧秘!一 關鍵字 如何讓 gcc 知道 中內嵌的彙編呢?借助 關鍵字!來看下面的例子 asm volatile hlt ...
gcc內聯彙編
有時為了高效,有時為了直接控制硬體,有些模組我們不得不直接用組合語言來編寫,並且對外提供呼叫的介面,隱藏細節,這其實就是內聯彙編。如何使用內聯彙編?我們就以 gcc 為例,一窺其中奧秘!一 關鍵字 如何讓 gcc 知道 中內嵌的彙編呢?借助關鍵字!來看下面的例子 a volatile hlt a 表...