Arm匯編學習筆記(三)——GCC內聯匯編

ARM 294瀏覽

之前每次看內聯匯編都有一些地方看不明白,"=r","r","%0","%1"這些符號看不明白,本次總結一下這些內容吧,雖然很簡單,但是手不能懶!

首先,C語言中為什么要內聯匯編以及其帶來的好處這個我就不說了。C語言中使用匯編要通過函數asm(),即__asm__()的別名,兩者是一樣的。

常見的內聯匯編有下面兩種形式:

asm(
"mov     r0, r0nt"
"mov     r0, r0nt"
"mov     r0, r0nt"
"mov     r0, r0"
);  

例1

asm(
"mov %[result], %[value], ror #1"
: [result] "=r" (y)
: [value] "r" (x)
:
); 

例2

第一種和一般匯編文件中的匯編程序是一樣的,這里不多說了。著重看第二種,為什么要有第二種形式呢?這就和為什么要內聯匯編有很大關系了,一般都是在一個C的函數中會使用內聯匯編,匯編代碼一般會與C函數的代碼有數據交換,也就是說通過匯編代碼來操作C代碼中的一些變量數據,而C代碼中的數據存放在內存還是寄存器,或者存放在哪個寄存器我們在寫C代碼的時候并不知道,所以不可能使用第一種內聯匯編形式,那就必須要用第二種形式了。下面是第二種匯編形式的格式定義:

asm(
code    /*匯編指令*/
: output operand list     /*輸出操作數列表*/
: input operand list      /*輸入操作數列表*/
: clobber list               /*被改變資源列表*/
);  

可以內聯匯編通過冒號將內容分成了四個部分,內聯匯編和C操作數之前的關聯性體現在上面的input和out操作數上。下面我們針對例2進行分析:

1. 匯編指令

mov %[result], %[value], ror #1

2. 輸出操作數列表,可選,每個輸出數的符號名用方括號包圍,后面跟一個約束串,然后再加上一個括號包圍的C表達式,這個括號里的符號就是C語言代碼中的變量。

[result] "=r" (y) /*result:符號名   "=r":約束串*    (y):C表達式/  

3. 輸入操作數列表,可選,語法上與輸入操作數列表一樣。括號中的x是C語言代碼中的變量。

[value] "r" (x)

4. 被改變資源列表,這里是空的,它主要是告訴編譯器哪些資源發生了改變,需要去更新。

那么上面的"r"和"=r"是什么意思呢?請看下面兩個圖:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

上面描述的很清楚,這里不多說了。

如果你曾經讀過一些別人寫的內聯匯編代碼就會發現與我們上面寫的略有不同,是下面這種形式的:

asm(
"mov %0, %1, ror #1"
: "=r" (result)
: "r" (value)
:
);  

例3

實際上是同一種形式,只不過例2的那種形式是從GCC 3.1版本開始才支持的,而再次之前一直是例3的形式。我之前對"%0" "%1"一直很迷惑,操作數用一個帶百分號的數字來表示,上述0%和1%分別表示第一個、第二個操作數。GCC的最新版本仍然支持上述語法,但明顯,上述語法更容易出錯,且難以維護:假設你寫一個較長的內聯匯編,然后需要在某個位置插入一個新的輸出操作數,此時,之后的操作數都需要重新編號。

另外還有一點就是,你會經常看見asm volatile()這樣的內聯匯編,在asm后面會跟上一個volatile關鍵字,這是因為編譯器會去優化你的代碼,不同的C編譯器優化的方式還不一樣,而這些優化有時候會適得其反,或者運行的過程和你希望的不一樣,甚至將你的匯編代碼給優化掉。針對這一問題的解決方法是增加volatile屬性,這一屬性告訴編譯器不要對本代碼段進行優化。

七星彩走势图2元网官网