GCC內聯彙編
1. gcc內聯彙編格式
__asm_- __volatile__(指令部: 輸出部: 輸入部: 損壞部)
gcc內聯彙編在處理器變數和暫存器上提供了一個模板和一些約束條件:
(1) 在指令部(Assembler Template)中數字前加上%,如%0、%1等,表示需要使用暫存器的樣板運算元。若指令部中用到幾個不同的運算元,就說明有幾個變數需要和暫存器結合。
(2) 輸出部(Output Operands) 用於描述在指令部中可以修改的C語言變數以及約束條件。每個輸出約束通常以"="或"+"號開頭,"="號表示被修飾的運算元只有可寫屬性,"+"號表示被修飾的運算元具有可讀可寫屬性。然後是一個字母(表示對運算元型別的說明),接著是關於變數結合的約束。輸出部可以是空的。
(3) 輸入部(Input Operands) 用來描述在指令部只讀取的C語言變數及約束條件。輸入部描述的引數只有只讀屬性,不要試圖修改輸入部引數的內容,因為gcc編譯器假定輸入部的引數內容在內嵌彙編之前和之後都是一致的。在輸入部中不能使用"="號或者"+"號約束條件,否則就會編譯報錯。輸入部可以是空的。
(4) 損壞部(Clobbers) 一般以"memory"結束。"memory"告訴gcc編譯器,內聯彙編程式碼改變了記憶體中的值,強迫編譯器在執行該彙編程式碼前儲存所有快取的值,在執行完彙編程式碼之後重新載入該值,目的是防止編譯亂序。"cc"表示內嵌程式碼修改了狀態暫存器的相關標誌位。
2. 例子
(1) 例1:
static inline unsigned long arch_local_irq_save(void) { unsigned long flags; asm volatile( "mrs %0, daif", //讀取PSTAT暫存器中的DAIF域到flags變數 "msr daifset, #2", //關閉IRQ : "=r" (flags) : : "memory"); return flags; }
先看輸出部,%0 運算元對應 "=r"(flags),即flags變數,其中"="表示被修飾的運算元的屬性是隻寫。"r"表示使用一個通用暫存器。
接著看輸入部,上例中輸入部為空,沒有指定引數。最後看損壞部,以"memory"結束。
該函式主要用於把PSTATE暫存器中的DAIF域儲存到臨時變數flags中,然後關閉IRQ。
在輸出部和輸入部使用"%"來表示引數序號,比"%0"表示第一個引數,"%1"表示第2個引數。
(2) 例2:
為了增強程式碼的可讀性,可以使用匯編符號名字來替代%表示的運算元,如下面add函式:
int add(int i, int j) { int ret; asm volatile( "add %w[result], %w[input_i], %w[input_j]" : [result] "=r" (ret) : [input_i] "r" (i), [input_j] "r" (j) ); return ret; }
書上Demo編譯驗證不通過,報錯:
$ gcc main.c -o pp main.c: Assembler messages: main.c:8: Error: number of operands mismatch for `add'
上述是個簡單的gcc內聯彙編的例子,主要功能是將i和j的值相加返回結果。先看輸出部,表示只定義了一個運算元。"[result]"表示定義了一個彙編符號運算元,符號名為result,它對應"=r"(ret),使用函式中定義的ret變數。在彙編程式碼中對應%w[result],其中w表示ARM64中的32位通用暫存器。再看輸入部,定義了兩個運算元,同樣使用匯編符號運算元的方式來定義。第一個彙編符號運算元是input_i對應形參i,第二個彙編符號運算元是input_j對應形參j。
3. gcc內聯彙編操作符和修飾符
操作符/修飾符 說明 = 被修飾的運算元只寫 + 被修飾的運算元具有可讀可寫屬性 & 被修飾的運算元只能作為輸出
4. ARM64特有操作符和修飾符
操作符/修飾符 說明 k SP暫存器 w 浮點暫存器、SIMD、SVE暫存器 Upl 使用P0到P7中任意一個SVE暫存器 Upa 使用P0到P15中任意一個SVE暫存器 Input 整數,常常用於ADD指令 J 整數,常常用於SUB指令 K 整數,常常用於32位邏輯指令 L 整數,常常用於64位邏輯指令 M 整數,常常用於32位的MOV指令 N 整數,常常用於64位的MOV指令 S 絕對符號地址或標籤引用 Y 浮點數,其值為0 Z 整數,其值為0 Ush 表示一個符號的PC相對偏移量的高位部分(大於等於12bit的部分),這個PC相對偏移介於0-4GB Q 表示沒有使用偏移量的單一暫存器的記憶體地址 Ump 一個適用於SI/DI/SF和DF模式下的載入-儲存指令的記憶體地址。
參考:《奔跑吧Linux核心》第2版,此書不怎麼樣。