在gcc中,可以使用__asm__表示後面的代碼為內嵌彙編代碼,__volatile__表示編譯器不要最佳化代碼,後面的指令保留原樣,內嵌彙編文法如下:
__asm__(彙編語句模板:輸出部分: 輸入部分: 破壞描述部分)
1.組合語言模板
語句之間使用";"、"/n"或"/n/t"分開。指令中的運算元可以使用預留位置引用C語言變數,運算元預留位置最多為10個,名稱如下:
%0,%1,...,%9
指令中使用預留位置表示的運算元,總被視為long型(4位元組),但對其施加的操作根據指令可以是字或位元組,當把運算元當作字或者位元組使用時,預設為低字或者低位元組。對位元組操作可以顯式地指明是低位元組還是次位元組,方法是在%和序號之間插入一個字母,"b"代表低位元組,"h"代表高位元組。
2.輸出部分
輸出部分描述輸出運算元,不同的運算元描述符之間用逗號格開,每個運算元描述符由限定字串和C語言變數組成。
3.輸入部分
4.限定字元
5.破壞描述符部分
破壞描述符用於通知編譯器我們使用了哪些寄存器或記憶體。
編譯器最佳化
由編譯器最佳化或者硬體重新排序引起的問題的解決辦法是以特定順序執行的操作之間設定記憶體屏障(memory barrier),Linux提供了一個宏用於解決編譯器的執行順序問題。
void Barrier(void)
這個函數通知編譯器插入一個記憶體屏障,但對硬體無效,編譯後的代碼會把當前CPU寄存器中的所有改過的數值存入記憶體,需要這些資料的時候再重新從記憶體中讀出。
C語言關鍵字volatile表明某個變數的值可能在外部被改變,因此對這些變數的存取不能緩衝到寄存器,每次使用時需要重新存取。
memory描述符告訴gcc以下內容。
不要將該段內嵌彙編指令與前面的指令重新排序,也就是說在執行內嵌彙編代碼之前,它前面的指令都執行完畢。
不要將變數緩衝到寄存器,因為這段代碼可能會用到記憶體變數,而這些記憶體變數會以不可預知的方式發生改變,因此gcc插入必要的代碼先將緩衝到寄存器的變數值寫回記憶體,如果後面又訪問這些變數,需要重新訪問記憶體。
如果彙編指令修改了記憶體,但是gcc本身卻察覺不到,因為在輸出部分沒有描述,此時就需要在修改描述部分增加memory,告訴gcc記憶體已經被修改,gcc得知這個訊息後,就會在這段指令之前,插入必要的指令將前面因為最佳化Cache而到寄存器中的變數值先寫回記憶體,如果以後又要使用這些變數,則再重新讀取。