Extended ASM format 擴展的asm格式:html
asm關鍵字的擴展格式以下:數組
asm ("assembly code" : output operands : input operands : changed registers);
上面的擴展格式由四個部分組成,每一個部分之間使用冒號隔開:函數
Assembly code: The inline assembly code using the same syntax used for the basic asm format
assembly code:即彙編代碼部分,能夠在該部分編寫所需的內聯彙編代碼,和基本asm格式相比,擴展格式裏的彙編代碼除了在百分號的使用上有所不一樣外(下面會介紹),其餘的彙編指令的書寫格式都是差很少的
Output operands: A list of registers and memory locations that will contain the output values
from the inline assembly code
輸出操做數:用於指定內聯彙編代碼的執行結果須要輸出到哪一個寄存器,以及須要輸出到哪一個C變量裏
Input operands: A list of registers and memory locations that contain input values for the inline
assembly code
輸入操做數:能夠將C變量的值輸入到指定的寄存器,這樣就能夠在內聯彙編代碼裏,經過這些寄存器來使用到C變量的值了,這裏的C變量既能夠是全局變量,也能夠是局部變量
Changed registers: A list of any additional registers that are changed by the inline code
被修改的寄存器:用於指明在內聯彙編代碼裏修改了哪些寄存器,編譯器會根據實際狀況,對這些修改的寄存器進行一些push、pop之類的操做,這樣,彙編代碼修改的寄存器就不會影響到外面C代碼的正常執行
並不是每一個部分都必須進行設置,假設你不須要第二個輸出操做數,那麼輸出操做數部分就能夠留空,不過須要注意的是,即使輸出操做數留空,開頭的兩個冒號也必須保留,如:asm (「assembly code」 : : input operands : changed registers);的格式,另外,若是不須要設置被修改的寄存器部分,那麼最後一個冒號能夠省略,如:asm (「assembly code」 : output locations : input operands);的格式。
.net
下面就介紹下擴展asm格式裏輸入和輸出操做數的具體格式。
Specifying input and output values 輸入和輸出操做數:code
輸入和輸出操做數的格式以下:
「constraint」(variable)
其中,括號裏的variable表示C代碼裏定義的全局變量或局部變量,而引號裏的constraint(約束)則表示用於存儲variable變量值的寄存器或內存位置。對於輸入操做數,constraint就是將variable變量的值讀取到指定的寄存器,或者直接使用變量的內存位置。對於輸出操做數,constraint就是先將結果存儲到指定的寄存器,再將寄存器裏的值設置到variable變量裏,某些constraint(約束)也能夠直接將結果寫入到變量所在的內存裏。
orm
constraint(約束)是一個單一的字符,可用的約束符以下表所示:
Constraint
約束符 Description
描述
a Use the %eax, %ax, or %al registers.
使用%eax, %ax或%al寄存器
b Use the %ebx, %bx, or %bl registers.
使用%ebx, %bx或%bl寄存器
c Use the %ecx, %cx, or %cl registers.
使用%ecx, %cx或%cl寄存器
d Use the %edx, %dx, or $dl registers.
使用%edx, %dx或%dl寄存器
S Use the %esi or %si registers.
使用%esi或%si寄存器
D Use the %edi or %di registers.
使用%edi或%di寄存器
r Use any available general-purpose register.
使用任何可用的通用寄存器
q Use either the %eax, %ebx, %ecx, or %edx register.
使用%eax, %ebx, %ecx或%edx寄存器
A Use the %eax and the %edx registers
for a 64-bit value.
使用%eax和%edx寄存器來存儲一個64位的值
f Use a floating-point register.
使用一個浮點寄存器
t Use the first (top) floating-point register.
使用第一個ST0浮點寄存器
u Use the second floating-point register.
使用第二個ST1浮點寄存器
m Use the variable’s memory location.
使用變量的內存位置
o Use an offset memory location.
使用一個帶有偏移值的內存位置,例如:使用4(%edi)來表示EDI加偏移值4的內存地址,多用於數組元素的訪問
V Use only a direct memory location.
使用不帶偏移值的內存位置
i Use an immediate integer value.
表示操做數是一個當即數(常量整數值)
n Use an immediate integer value with a known value.
表示操做數是一個已知數值的當即數,在許多系統裏,當操做數小於1個word(字)寬時,就不能使用i,而應使用n來表示當即數
g Use any register or memory location available.
能夠使用任一通用寄存器或內存位置,或者操做數是一個當即數
除了上面的這些約束外,對於操做數還包含一個constraint modifier(約束脩飾符),用於表示操做數是否可讀寫之類的,可用的約束脩飾符以下表所示:
htm
Constraint modifier
約束脩飾符 Description
描述
blog
- The operand can be both read from
and written to.
說明操做數是可讀寫的,例如"+r",指的是在內聯彙編指令開始前,必須先將變量的值賦值給對應的寄存器(也就是讀的過程),在執行完彙編指令後,再將寄存器的值寫入變量(即寫的過程)
= The operand can only be written to.
說明操做數是隻寫的,例如"=r",就是說,只需將結果寫入變量,至於內聯彙編指令執行前,對應寄存器裏的值則能夠是任意值,無需在執行前讀取變量的值。
% The operand can be switched with the
next operand if necessary.
在必要時,操做數能夠和下一個操做數進行交換,下面會舉例說明
& use ‘&’ for each output operand that may
not overlap an input
&修飾符是用來強制編譯器爲輸入操做數與輸出操做數分配不一樣的寄存器
能夠參考
http://blog.csdn.net/bokee/article/details/7029353
該連接裏的文章
上面的約束脩飾符裏"+"、"="、"&「這三個只能用於修飾輸出操做數,不能用於修飾輸入操做數,而」%「則恰好相反,只能用於修飾輸入操做數,」%"的用法能夠參考下面的代碼:
int main(int __argc, char* __argv[])
{
int __in1 = 8, __in2 = 4, __out = 3;
asm (「addl %1, %0\n\t」
: 「=r」(__out)
: 「%r」 (__in1), 「0」 (__in2));
return 0;
}
在此例中,因爲指令是一個加法運算,至關於等式__out = __in1 + __in2,而它與等式__out = __in2 + __in1沒有什麼不一樣。因此使用百分號修飾,讓GCC知道__in1和__in2能夠互換,也就是說GCC能夠自動將本例的內聯彙編改變爲:
ip
-
asm (「addl %1, %0\n\t」
-
「=r」(__out)內存
-
「%r」 (__in2), 「0」 (__in1));
上面的代碼例子來源於:http://tieba.baidu.com/p/310375553 ,另外上面例子裏的%1和%0等是佔位符,佔位符的含義會在下面的介紹裏進行說明。用的較多的是第一個"+「和第二個」="修飾符,例以下面的代碼模板:
asm (「assembly code」 : 「=a」(result) : 「d」(data1), 「c」(data2));
上面的代碼模板,表示將data1變量的值讀取到EDX寄存器,將data2變量的值讀取到ECX寄存器,內聯彙編代碼在執行完後,會將結果保存到EAX寄存器,最後,EAX裏的值會寫入到result變量裏,這裏必需要在"a"約束前面添加"="修飾符,表示result操做數能夠寫入數據,不然,因爲在沒有修飾符的狀況,該操做數表示只讀,GCC編譯器就會報"output operand constraint lacks ‘=’「的錯誤,即輸出操做數的約束缺乏」=「修飾符。固然也能夠使用」+"修飾符來表示輸出操做數可讀寫。
Using registers 在擴展asm格式裏使用寄存器:
在上一篇文章裏介紹的基本asm內聯彙編格式裏,寄存器的使用方式和在單獨的彙編文件裏的使用方式同樣,只須要在寄存器名稱前加一個百分號便可,可是在本章介紹的擴展asm格式裏,內聯彙編代碼裏要使用寄存器時,必須在寄存器的名稱前加兩個百分號,例如:%%eax 這樣的形式,第一個百分號用於對第二個百分號進行轉義,這樣兩個百分號就能夠表示一個普通的百分號來修飾eax,之因此要這麼作,是由於%在擴展asm格式裏有特殊的用途,就像C語言字符串裏的"\n"字符裏的"\"字符用於和"n"字符一塊兒來表示特殊的換行符同樣,單個百分號用於和數字一塊兒來表示下面會提到的佔位符。 下面的regtest1.c程式就演示瞭如何在擴展的asm格式裏使用寄存器:
/* regtest1.c – An example of using registers */ #include <stdio.h> int main() { int data1 = 10; int data2 = 20; int result; asm ("imull %%edx, %%ecx\n\t" "movl %%ecx, %%eax" : "=a"(result) : "d"(data1), "c"(data2)); printf("The result is %d\n", result); return 0; }
上面的代碼裏,在將data1和data2局部變量的值分別加載到EDX和ECX後,在asm的彙編代碼裏,就會經過imull %%edx, %%ecx指令將EDX和ECX寄存器的值相乘,計算結果存儲到ECX裏,而後經過movl %%ecx, %%eax指令將ECX裏的值輸出到EAX裏,最後EAX裏的值會寫入到result局部變量裏,這樣在隨後的C代碼裏就能夠使用printf函數來將result的值給顯示出來。這裏須要注意寄存器的寫法,如:%%ecx等都有兩個百分號,緣由在上面已經解釋了。
參考文章: https://www.zengl.com/a/201404/146.html