arm 內嵌彙編

內嵌彙編代碼,每個input和output operand,由被方括號[]中的符號名,限制字符串,圓括號中的C表達式構成優化

 

 

// examplespa

static inline void __down_write(struct rw_semaphore *sem)
{
    long oldcount;
#ifndef    CONFIG_SMP
    oldcount = sem->count;
    sem->count += RWSEM_ACTIVE_WRITE_BIAS;
#else
    long temp;
    __asm__ __volatile__(
    "1:    ldq_l    %0,%1\n"
    "    addq    %0,%3,%2\n"
    "    stq_c    %2,%1\n"
    "    beq    %2,2f\n"
    "    mb\n"
    ".subsection 2\n"
    "2:    br    1b\n"
    ".previous"
    :"=&r" (oldcount), "=m" (sem->count), "=&r" (temp)
    :"Ir" (RWSEM_ACTIVE_WRITE_BIAS), "m" (sem->count) : "memory");
#endif
    if (unlikely(oldcount))
        rwsem_down_write_failed(sem);
}指針

 

轉載:接口

有時爲了高效,有時爲了直接控制硬件,有些模塊咱們不得不直接用匯編語言來編寫,而且對外提供調用的接口,隱藏細節,這其實就是內聯彙編。如何使用內聯彙編?咱們就以 GCC 爲例,一窺其中奧祕!內存


1、關鍵字 
    如何讓 GCC 知道代碼中內嵌的彙編呢? 藉助關鍵字!來看下面的例子:

     __asm__ __volatile__("hlt");

  __asm__ 表示後面的代碼爲內嵌彙編,asm 是 __asm__ 的別名。__volatile__ 表示編譯器不要優化代碼,後面的指令保留原樣,volatile 是它的別名。括號裏面是彙編指令。

2、示例分析 
  使用內嵌彙編,要先編寫彙編指令模板,而後將 C 語言表達式與指令的操做數相關聯,並告訴 GCC 對這些操做有哪些限制條件。示例以下:
 
   __asm__ __violate__ ("movl %1,%0" : "=r" (result) : "m" (input));   

    movl %1,%0  是指令模板;%0  和 %1 表明指令的操做數,稱爲佔位符,內嵌彙編靠它們將C 語言表達式與指令操做數相對應。

    指令模板後面用小括號括起來的是 C 語言表達式,本例中只有兩個:result 和 input ,他們按照出現的順序分別與指令操做數 %0 、%1 對應;注意對應順序:第一個 C 表達式對應 %0 ;第二個表達式對應 %1 ,依次類推,操做數至多有10 個,分別用 %0, %1 …. %9 表示。

    在每一個操做數前面有一個用引號括起來的字符串,字符串的內容是對該操做數的限制或者說要求。result 前面的限制字符串是 =r ,其中 = 表示 result 是輸出操做數, r  表示須要將 result 與某個通用寄存器相關聯,先將操做數的值讀入寄存器,而後在指令中使用相應寄存器,而不是 result 自己,固然指令執行完後須要將寄存器中的值存入變量 result ,從表面上看好像是指令直接對 result 進行操做,實際上 GCC 作了隱式處理,這樣咱們能夠少寫一些指令。 input 前面的 r 表示該表達式須要先放入某個寄存器,而後在指令中使用該寄存器參加運算。 

  C 表達式或者變量與寄存器的關係由 GCC 自動處理,咱們只需使用限制字符串指導 GCC 如何處理便可。限制字符必須與指令對操做數的要求相匹配,不然產生的彙編代碼將會有錯,讀者能夠將上例中的兩個 r,都改成 m (m表示操做數放在內存,而不是寄存器中),編譯後獲得的結果是: 

           movl input, result

很明顯這是一條非法指令,所以限制字符串必須與指令對操做數的要求匹配。例如指令 movl 容許寄存器到寄存器,當即數到寄存器等,可是不容許內存到內存的操做,所以兩個操做數不能同時使用 m 做爲限定字符。
內嵌彙編語法以下: 

           __asm__(彙編語句模板: 輸出部分: 輸入部分: 破壞描述部分)

共四個部分:彙編語句模板,輸出部分,輸入部分,破壞描述部分,各部分使用「:」格開,彙編語句模板必不可少,其餘三部分可選,若是使用了後面的部分,而前面部分爲空,也須要用「:」格開,相應部份內容爲空。例如: 

           __asm__ __volatile__("cli": : :"memory")

具體這幾部分都有什麼限制呢?這得從細處着手!


3、語法細節
一、彙編語句模板
    彙編語句模板由彙編語句序列組成,語句之間使用「;」、「\n」 或 「\n\t」 分開。指令中的操做數可使用佔位符引用 C 語言變量,操做數佔位符最多10 個,名稱以下:%0,%1,…,%9。指令中使用佔位符表示的操做數,總被視爲 long 型(4個字節),但對其施加的操做根據指令能夠是字或者字節,當把操做數看成字或者字節使用時,默認爲低字或者低字節。對字節操做能夠顯式的指明是低字節仍是次字節。方法是在 % 和序號之間插入一個字母,b 表明低字節,h 表明高字節,例如:%h1。

二、輸出部分
    輸出部分描述輸出操做數,不一樣的操做數描述符之間用逗號格開,每一個操做數描述符由限定字符串和 C 語言變量組成。每一個輸出操做數的限定字符串必須包含「=」表示他是一個輸出操做數。 例如:
 
         __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x) )

描述符字符串表示對該變量的限制條件,這樣 GCC 就能夠根據這些條件決定如何分配寄存器,如何產生必要的代碼處理指令操做數與 C 表達式或 C 變量之間的聯繫。

三、輸入部分
    輸入部分描述輸入操做數,不一樣的操做數描述符之間使用逗號格開,每一個操做數描述符由限定字符串和 C 語言表達式或者 C 語言變量組成。 示例以下:

例 1 :
 __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));

例 2:
Static __inline__ void __set_bit(int nr, volatile void * addr)
ci

       __asm__(
                       "btsl %1,%0"
                       :"=m" (ADDR)
                       :"Ir" (nr));
}

後例功能是將 (*addr) 的第 nr 位設爲 1。第一個佔位符 %0 與 C  語言變量 ADDR 對應,第二個佔位符 %1 與 C 語言變量 nr 對應。所以上面的彙編語句代碼與下面的僞代碼等價:btsl nr, ADDR,該指令的兩個操做數不能全是內存變量,所以將 nr 的限定字符串指定爲「Ir」,將 nr 與當即數或者寄存器相關聯,這樣兩個操做數中只有 ADDR 爲內存變量。

四、限制字符
    限制字符有不少種,有些是與特定體系結構相關,此處僅列出經常使用的限定字符和i386中可能用到的一些經常使用的限定符。它們的做用是指示編譯器如何處理其後的 C 語言變量與指令操做數之間的關係。字符串

 

 

分類input

限定符編譯器

描述it

通用寄存器

「a」

將輸入變量放入eax

「b」

將輸入變量放入ebx

「c」

將輸入變量放入ecx

「d」

將輸入變量放入edx

「s」

將輸入變量放入esi

「d」

將輸入變量放入edi

「q」

將輸入變量放入eax,ebx,ecx,edx中的一個

「r」

將輸入變量放入通用寄存器,即eax,ebx,ecx,edx,esi,edi之一

「A」

把eax和edx合成一個64 位的寄存器(use long longs)

內存

「m」

內存變量

「o」

操做數爲內存變量,但其尋址方式是偏移量類型, 也即基址尋址

「V」

操做數爲內存變量,但尋址方式不是偏移量類型

「 」

操做數爲內存變量,但尋址方式爲自動增量

「p」

操做數是一個合法的內存地址(指針)

寄存器或內存

「g」

將輸入變量放入eax,ebx,ecx,edx之一,或做爲內存變量

「X」

操做數能夠是任何類型

當即數

「I」

0-31之間的當即數(用於32位移位指令)

「J」

0-63之間的當即數(用於64位移位指令)

「N」

0-255之間的當即數(用於out指令)

「i」

當即數

「n」

當即數,有些系統不支持除字之外的當即數,則應使用「n」而非 「i」

匹配

「 0 」

表示用它限制的操做數與某個指定的操做數匹配

「1」 ...

也即該操做數就是指定的那個操做數,例如「0」

「9」

去描述「%1」操做數,那麼「%1」引用的其實就是「%0」操做數,注意做爲限定符字母的0-9 與指令中的「%0」-「%9」的區別,前者描述操做數, 後者表明操做數。

&

該輸出操做數不能使用過和輸入操做數相同的寄存器

操做數類型

「=」

操做數在指令中是隻寫的(輸出操做數)   

「+」

操做數在指令中是讀寫類型的(輸入輸出操做數)

浮點數

「f」

浮點寄存器

「t」

第一個浮點寄存器

「u」

第二個浮點寄存器

「G」

標準的80387浮點常數

%

該操做數能夠和下一個操做數交換位置,例如addl的兩個操做數能夠交換順序(固然兩個操做數都不能是當即數)

#

部分註釋,從該字符到其後的逗號之間全部字母被忽略

*

表示若是選用寄存器,則其後的字母被忽略

 

 五、破壞描述部分     破壞描述符用於通知編譯器咱們使用了哪些寄存器或內存,由逗號格開的字符串組成,每一個字符串描述一種狀況,通常是寄存器名;除寄存器外還有 「memory」。例如:「%eax」,「%ebx」,「memory」 等。

相關文章
相關標籤/搜索