免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
匯編語言---GCC內(nèi)聯(lián)匯編

GCC支持在C/C++代碼中嵌入?yún)R編代碼,這些代碼被稱作是"GCC Inline ASM"(GCC內(nèi)聯(lián)匯編);
一、基本內(nèi)聯(lián)匯編
GCC中基本的內(nèi)聯(lián)匯編非常易懂,格式如下:
__asm__ [__volatile__] ("instruction list");
其中,
1.__asm__:
它是GCC定義的關(guān)鍵字asm的宏定義(#define __asm__ asm),它用來聲明一個(gè)內(nèi)聯(lián)匯編表達(dá)式,所以,任何一個(gè)內(nèi)聯(lián)匯編表達(dá)式都以它開頭,它是必不可少的;如果要編寫符合ANSI C標(biāo)準(zhǔn)的代碼(即:與ANSI C兼容),那就要使用__asm__;
2.__volatile__:
它是GCC關(guān)鍵字volatile的宏定義;這個(gè)選項(xiàng)是可選的;它向GCC聲明"不要?jiǎng)游宜鶎懙膇nstruction list,我需要原封不動(dòng)地保留每一條指令";如果不使用__volatile__,則當(dāng)你使用了優(yōu)化選項(xiàng)-O進(jìn)行優(yōu)化編譯時(shí),GCC將會(huì)根據(jù)自己的判斷來決定是否將這個(gè)內(nèi)聯(lián)匯編表達(dá)式中的指令優(yōu)化掉;如果要編寫符合ANSI C標(biāo)準(zhǔn)的代碼(即:與ANSI C兼容),那就要使用__volatile__;
3.instruction list:
它是匯編指令列表;它可以是空列表,比如:__asm__ __volatile__("");或__asm__("");都是合法的內(nèi)聯(lián)匯編表達(dá)式,只不過這兩條語句什么都不做,沒有什么意義;但并非所有"instruction list"為空的內(nèi)聯(lián)匯編表達(dá)式都是沒意義的,比如:__asm__("":::"memory");就是非常有意義的,它向GCC聲明:"我對內(nèi)存做了改動(dòng)",這樣,GCC在編譯的時(shí)候,就會(huì)將此因素考慮進(jìn)去;
例如:
__asm__("movl %esp,%eax");
或者是
__asm__("movl $1,%eax
         xor %ebx,%ebx
         int $0x80");
或者是
__asm__("movl $1,%eax\n\t"\
        "xor %ebx,%ebx\n\t"\
        "int $0x80");
instruction list的編寫規(guī)則:當(dāng)指令列表里面有多條指令時(shí),可以在一對雙引號中全部寫出,也可將一條或多條指令放在一對雙引號中,所有指令放在多對雙引號中;如果是將所有指令寫在一對雙引號中,那么,相鄰倆條指令之間必須用分號";"或換行符(\n)隔開,如果使用換行符(\n),通常\n后面還要跟一個(gè)\t;或者是相鄰兩條指令分別單獨(dú)寫在兩行中;
規(guī)則1:任意兩條指令之間要么被分號(;)或換行符(\n)或(\n\t)分隔開,要么單獨(dú)放在兩行;
規(guī)則2:單獨(dú)放在兩行的方法既可以通過\n或\n\t的方法來實(shí)現(xiàn),也可以真正地放在兩行;
規(guī)則3:可以使用1對或多對雙引號,每1對雙引號里面可以放1條或多條指令,所有的指令都要放在雙引號中;
例如,下面的內(nèi)聯(lián)匯編語句都是合法的:
__asm__("movl %eax,%ebx
        sti
        popl %edi
        subl %ecx,%ebx");
__asm__("movl %eax,%ebx; sti
         popl %edi; subl %ecx,%ebx");
__asm__("movl %eax,%ebx; sti\n\t popl %edi
         subl %ecx,%ebx");
如果將指令放在多對雙引號中,則,除了最后一對雙引號之外,前面的所有雙引號里的最后一條指令后面都要有一個(gè)分號(;)或(\n)或(\n\t);比如,下面的內(nèi)聯(lián)匯編語句都是合法的:
__asm__("movl %eax,%ebx
         sti\n"
         "popl %edi;"
         "subl %ecx,%bx");
__asm__("movl %eax,%ebx; sti\n\t"
        "popl %edi; subl %ecx,%ebx");
__asm__("movl %eax,%ebx; sti\n\t popl %edi\n"
        "subl %ecx,%ebx"); 
二、帶有C/C++表達(dá)式的內(nèi)聯(lián)匯編
GCC允許你通過C/C++表達(dá)式指定內(nèi)聯(lián)匯編中"instruction list"中的指令的輸入和輸出,你甚至可以不關(guān)心到底使用哪些寄存器,完全依靠GCC來安排和指定;這一點(diǎn)可以讓程序員免去考慮有限的寄存器的使用,也可以提高目標(biāo)代碼的效率;
1.帶有C/C++表達(dá)式的內(nèi)聯(lián)匯編語句的格式:
__asm__ [__volatile__]("instruction list":Output:Input:Clobber/Modify);
圓括號中的內(nèi)容被冒號":"分為四個(gè)部分:
A.如果第四部分的"Clobber/Modify"可以為空;如果"Clobber/Modify"為空,則其前面的冒號(:)必須省略;比如:語句__asm__("movl %%eax,%%ebx":"=b"(foo):"a"(inp):);是非法的,而語句__asm__("movl %%eax,%%ebx":"=b"(foo):"a"(inp));則是合法的;
B.如果第一部分的"instruction list"為空,則input、output、Clobber/Modify可以為空,也可以不為空;比如,語句__asm__("":::"memory");和語句__asm__(""::);都是合法的寫法;
C.如果Output、Input和Clobber/Modify都為空,那么,Output、Input之前的冒號(:)可以省略,也可以不省略;如果都省略,則此匯編就退化為一個(gè)基本匯編,否則,仍然是一個(gè)帶有C/C++表達(dá)式的內(nèi)聯(lián)匯編,此時(shí)"instruction list"中的寄存器的寫法要遵循相關(guān)規(guī)定,比如:寄存器名稱前面必須使用兩個(gè)百分號(%%);基本內(nèi)聯(lián)匯編中的寄存器名稱前面只有一個(gè)百分號(%);比如,語句__asm__("movl %%eax,%%ebx"::);__asm__("movl %%eax,%%ebx":);和語句__asm__("movl %%eax,%%ebx");都是正確的寫法,而語句__asm__("movl %eax,%ebx"::);__asm__("movl %eax,%ebx":);和語句__asm__("movl %%eax,%%ebx");都是錯(cuò)誤的寫法;
D.如果Input、Clobber/Modify為空,但Output不為空,則,Input前面的冒號(:)可以省略,也可以不省略;比如,語句__asm__("movl %%eax,%%ebx":"=b"(foo):);和語句__asm__("movl %%eax,%%ebx":"=b"(foo));都是正確的;
E.如果后面的部分不為空,而前面的部分為空,則,前面的冒號(:)都必須保留,否則無法說明不為空的部分究竟是第幾部分;比如,Clobber/Modify、Output為空,而Input不為空,則Clobber/Modify前面的冒號必須省略,而Output前面的冒號必須保留;如果Clobber/Modify不為空,而Input和Output都為空,則Input和Output前面的冒號都必須保留;比如,語句__asm__("movl %%eax,%%ebx"::"a"(foo));和__asm__("movl %%eax,%%ebx":::"ebx");
注意:基本內(nèi)聯(lián)匯編中的寄存器名稱前面只能有一個(gè)百分號(%),而帶有C/C++表達(dá)式的內(nèi)聯(lián)匯編中的寄存器名臣前面必須有兩個(gè)百分號(%%);
2.Output:
Output部分用來指定當(dāng)前內(nèi)聯(lián)匯編語句的輸出,稱為輸出表達(dá)式;
格式為: "操作約束"(輸出表達(dá)式)
例如:
__asm__("movl %%rc0,%1":"=a"(cr0));
這個(gè)語句中的Output部分就是("=a"(cr0)),它是一個(gè)操作表達(dá)式,指定了一個(gè)內(nèi)聯(lián)匯編語句的輸出部分;
Output部分由兩個(gè)部分組成:由雙引號括起來的部分和由圓括號括起來的部分,這兩個(gè)部分是一個(gè)Output部分所不可缺少的部分;
用雙引號括起來的部分就是C/C++表達(dá)式,它用于保存當(dāng)前內(nèi)聯(lián)匯編語句的一個(gè)輸出值,其操作就是C/C++賦值語句"="的左值部分,因此,圓括號中指定的表達(dá)式只能是C/C++中賦值語句的左值表達(dá)式,即:放在等號=左邊的表達(dá)式;也就是說,Output部分只能作為C/C++賦值操作左邊的表達(dá)式使用;
用雙引號括起來的部分就指定了C/C++中賦值表達(dá)式的右值來源;這個(gè)部分被稱作是"操作約束"(Operation Constraint),也可以稱為"輸出約束";在這個(gè)例子中的操作約束是"=a",這個(gè)操作約束包含兩個(gè)組成部分:等號(=)和字母a,其中,等號(=)說明圓括號中的表達(dá)式cr0是一個(gè)只寫的表達(dá)式,只能被用作當(dāng)前內(nèi)聯(lián)匯編語句的輸出,而不能作為輸入;字母a是寄存器EAX/AX/AL的縮寫,說明cr0的值要從寄存器EAX中獲取,也就是說cr0=eax,最終這一點(diǎn)被轉(zhuǎn)化成匯編指令就是:movl %eax,address_of_cr0;
注意:很多文檔中都聲明,所有輸出操作的的操作約束都必須包含一個(gè)等號(=),但是GCC的文檔中卻明確地聲明,并非如此;因?yàn)榈忍?=)約束說明當(dāng)前的表達(dá)式是一個(gè)只寫的,但是還有另外一個(gè)符號:加號(+),也可以用來說明當(dāng)前表達(dá)式是可讀可寫的;如果一個(gè)操作約束中沒有給出這兩個(gè)符號中的任何一個(gè),則說明當(dāng)前表達(dá)式是只讀的;因此,對于輸出操作來說,肯定必須是可寫的,而等號(=)和加號(+)都可表示可寫,只不過加號(+)同時(shí)也可以表示可讀;所以,對于一個(gè)輸出操作來說,其操作約束中只要包含等號(=)或加號(+)中的任意一個(gè)就可以了;
等號(=)與加號(+)的區(qū)別:等號(=)表示當(dāng)前表達(dá)式是一個(gè)純粹的輸出操作,而加號(+)則表示當(dāng)前表達(dá)式不僅僅是一個(gè)輸出操作,還是一個(gè)輸入操作;但無論是等號(=)還是加號(+),所表示的都是可寫,只能用于輸出,只能出現(xiàn)在Output部分,而不能出現(xiàn)在Input部分;
在Output部分可以出現(xiàn)多個(gè)輸出操作表達(dá)式,多個(gè)輸出操作表達(dá)式之間必須用逗號(,)隔開;
3、Input:
Input部分用來指定當(dāng)前內(nèi)聯(lián)匯編語句的輸入;稱為輸入表達(dá)式;
格式為: "操作約束"(輸入表達(dá)式)
例如:
__asm__("movl %0,%%db7"::"a"(cpu->db7));
其中,表達(dá)式"a"(cpu->db7)就稱為輸入表達(dá)式,用于表示一個(gè)對當(dāng)前內(nèi)聯(lián)匯編的輸入;
Input同樣也由兩部分組成:由雙引號括起來的部分和由圓括號括起來的部分;這兩個(gè)部分對于當(dāng)前內(nèi)聯(lián)匯編語句的輸入來說也是必不可少的;
在這個(gè)例子中,由雙引號括起來的部分是"a",用圓括號括起來的部分是(cpu->db7);
用雙引號括起來的部分就是C/C++表達(dá)式,它為當(dāng)前內(nèi)聯(lián)匯編語句提供一個(gè)輸入值;在這里,圓括號中的表達(dá)式cpu->db7是一個(gè)C/C++語言的表達(dá)式,它不必是左值表達(dá)式,也就是說,它不僅可以是放在C/C++賦值操作左邊的表達(dá)式,還可以是放在C/C++賦值操作右邊的表達(dá)式;所以,Input可以是一個(gè)變量、一個(gè)數(shù)字,還可以是一個(gè)復(fù)雜的表達(dá)式(如:a+b/c*d);
比如,上例還可以這樣寫:
__asm__("movl %0,%%db7"::"a"(foo));__asm__("movl %0,%%db7"::"a"(0x12345));__asm__("movl %0,%%db7"::"a"(va:vb/vc));
用雙引號括起來的部分就是C/C++中賦值表達(dá)式的右值表達(dá)式,用于約束當(dāng)前內(nèi)聯(lián)匯編語句中的當(dāng)前輸入;這個(gè)部分也成為"操作約束",也可以成為是"輸入約束";與輸出表達(dá)式中的操作約束不同的是,輸入表達(dá)式中的操作約束不允許指定等號(=)約束或加號(+)約束,也就是說,它只能是只讀的;約束中必須指定一個(gè)寄存器約束;例子中的字母a表示當(dāng)前輸入變量cpu->db7要通過寄存器EAX輸入到當(dāng)前內(nèi)聯(lián)匯編語句中;
三、操作約束:Operation Constraint
操作約束只會(huì)出現(xiàn)在帶有C/C++表達(dá)式的內(nèi)聯(lián)匯編語句中;
每一個(gè)Input和Output表達(dá)式都必須指定自己的操作約束Operation Constraint;約束的類型有:寄存器約束、內(nèi)存約束、立即數(shù)約束、通用約束;
操作表達(dá)式的格式:
"約束"(C/C++表達(dá)式)
:"Constraint"(C/C++ expression)
1.寄存器約束:
當(dāng)你的輸入或輸出需要借助于一個(gè)寄存器時(shí),你需要為其指定一個(gè)寄存器約束;
可以直接指定一個(gè)寄存器名字;比如:
__asm__ __volatile__("movl %0,%%cr0"::"eax"(cr0));
也可以指定寄存器的縮寫名稱;比如:
__asm__ __volatile__("movl %0,%%cr0"::"a"(cr0));
如果指定的是寄存器的縮寫名稱,比如:字母a;那么,GCC將會(huì)根據(jù)當(dāng)前操作表達(dá)式中C/C++表達(dá)式的寬度來決定使用%eax、%ax還是%al;比如:
unsigned short __shrt;
__asm__ __volatile__("movl %0,%%bx"::"a"(__shrt));
由于變量__shrt是16位無符號類型m大小是兩個(gè)字節(jié),所以,編譯器編譯出來的匯編代碼中,則會(huì)讓此變量使用寄存器%ax;
無論是Input還是Output操作約束,都可以使用寄存器約束;
常用的寄存器約束的縮寫:
r:I/O,表示使用一個(gè)通用寄存器,由GCC在%eax/%ax/%al、%ebx/%bx/%bl、%ecx/%cx/%cl、%edx/%dx/%dl中選取一個(gè)GCC認(rèn)為是合適的;
q:I/O,表示使用一個(gè)通用寄存器,與r的意義相同;
g:I/O,表示使用寄存器或內(nèi)存地址;
m:I/O,表示使用內(nèi)存地址;
a:I/O,表示使用%eax/%ax/%al;
b:I/O,表示使用%ebx/%bx/%bl;
c:I/O,表示使用%ecx/%cx/%cl;
d:I/O,表示使用%edx/%dx/%dl;
D:I/O,表示使用%edi/%di;
S:I/O,表示使用%esi/%si;
f:I/O,表示使用浮點(diǎn)寄存器;
t:I/O,表示使用第一個(gè)浮點(diǎn)寄存器;
u:I/O,表示使用第二個(gè)浮點(diǎn)寄存器;
A:I/O,表示把%eax與%edx組合成一個(gè)64位的整數(shù)值;
o:I/O,表示使用一個(gè)內(nèi)存位置的偏移量;
V:I/O,表示僅僅使用一個(gè)直接內(nèi)存位置;
i:I/O,表示使用一個(gè)整數(shù)類型的立即數(shù);
n:I/O,表示使用一個(gè)帶有已知整數(shù)值的立即數(shù);
F:I/O,表示使用一個(gè)浮點(diǎn)類型的立即數(shù);
2.內(nèi)存約束:
如果一個(gè)Input/Output操作表達(dá)式的C/C++表達(dá)式表現(xiàn)為一個(gè)內(nèi)存地址(指針變量),不想借助于任何寄存器,則可以使用內(nèi)存約束;比如:
__asm__("lidt %0":"=m"(__idt_addr));或__asm__("lidt %0"::"m"(__idt_addr));
內(nèi)存約束使用約束名"m",表示的是使用系統(tǒng)支持的任何一種內(nèi)存方式,不需要借助于寄存器;
使用內(nèi)存約束方式進(jìn)行輸入輸出時(shí),由于不借助于寄存器,所以,GCC不會(huì)按照你的聲明對其做任何的輸入輸出處理;GCC只會(huì)直接拿來使用,對這個(gè)C/C++表達(dá)式而言,究竟是輸入還是輸出,完全依賴于你寫在"instruction list"中的指令對其操作的方式;所以,不管你把操作約束和操作表達(dá)式放在Input部分還是放在Output部分,GCC編譯生成的匯編代碼都是一樣的,程序的執(zhí)行結(jié)果也都是正確的;本來我們將一個(gè)操作表達(dá)式放在Input或Output部分是希望GCC能為我們自動(dòng)通過寄存器將表達(dá)式的值輸入或輸出;既然對于內(nèi)存約束類型的操作表達(dá)式來說,GCC不會(huì)為它做任何事情,那么放在哪里就無所謂了;但是從程序員的角度來看,為了增強(qiáng)代碼的可讀性,最好能夠把它放在符合實(shí)際情況的地方;
3.立即數(shù)約束:
如果一個(gè)Input/Output操作表達(dá)式的C/C++表達(dá)式是一個(gè)數(shù)字常數(shù),不想借助于任何寄存器或內(nèi)存,則可以使用立即數(shù)約束;
由于立即數(shù)在C/C++表達(dá)式中只能作為右值使用,所以,對于使用立即數(shù)約束的表達(dá)式而言,只能放在Input部分;比如:
__asm__ __volatile__("movl %0,%%eax"::"i"(100));
立即數(shù)約束使用約束名"i"表示輸入表達(dá)式是一個(gè)整數(shù)類型的立即數(shù),不需要借助于任何寄存器,只能用于Input部分;使用約束名"F"表示輸入表達(dá)式是一個(gè)浮點(diǎn)數(shù)類型的立即數(shù),不需要借助于任何寄存器,只能用于Input部分;
4.通用約束:
約束名"g"可以用于輸入和輸出,表示可以使用通用寄存器、內(nèi)存、立即數(shù)等任何一種處理方式;
約束名"0,1,2,3,4,5,6,7,8,9"只能用于輸入,表示與第n個(gè)操作表達(dá)式使用相同的寄存器/內(nèi)存;
通用約束"g"是一個(gè)非常靈活的約束,當(dāng)程序員認(rèn)為一個(gè)C/C++表達(dá)式在實(shí)際操作中,無論使用寄存器方式、內(nèi)存方式還是立即數(shù)方式都無所謂時(shí),或者程序員想實(shí)現(xiàn)一個(gè)靈活的模板,以讓GCC可以根據(jù)不同的C/C++表達(dá)式生成不同的訪問方式時(shí),就可以使用通用約束g;
例如:
#define JUST_MOV(foo) __asm__("movl %0,%%eax"::"g"(foo))
則,JUST_MOV(100)和JUST_MOV(var)就會(huì)讓編譯器產(chǎn)生不同的匯編代碼;
對于JUST_MOV(100)的匯編代碼為:
#APP
 movl $100,%eax      #立即數(shù)方式;
#NO_APP
對于JUST_MOV(var)的匯編代碼為:
#APP
 movl 8(%ebp),%eax   #內(nèi)存方式;
#NO_APP
像這樣的效果,就是通用約束g的作用;
5.修飾符:
等號(=)和加號(+)作為修飾符,只能用于Output部分;等號(=)表示當(dāng)前輸出表達(dá)式的屬性為只寫,加號(+)表示當(dāng)前輸出表達(dá)式的屬性為可讀可寫;這兩個(gè)修飾符用于約束對輸出表達(dá)式的操作,它們倆被寫在輸出表達(dá)式的約束部分中,并且只能寫在第一個(gè)字符的位置;
符號&也寫在輸出表達(dá)式的約束部分,用于約束寄存器的分配,但是只能寫在約束部分的第二個(gè)字符的位置上;


用符號&進(jìn)行修飾時(shí),等于向GCC聲明:"GCC不得為任何Input操作表達(dá)式分配與此Output操作表達(dá)式相同的寄存器";

其原因是修飾符&意味著被其修飾的Output操作表達(dá)式要在所有的Input操作表達(dá)式被輸入之前輸出;

即:GCC會(huì)先使用輸出值對被修飾符&修飾的Output操作表達(dá)式進(jìn)行填充,然后,才對Input操作表達(dá)式進(jìn)行輸入;

這樣的話,如果不使用修飾符&對Output操作表達(dá)式進(jìn)行修飾,一旦后面的Input操作表達(dá)式使用了與Output操作表達(dá)式相同的寄存器,就會(huì)產(chǎn)生輸入輸出數(shù)據(jù)混亂的情況;相反,如果沒有用修飾符&修飾輸出操作表達(dá)式,那么,就意味著GCC會(huì)先把Input操作表達(dá)式的值輸入到選定的寄存器中,然后經(jīng)過處理,最后才用輸出值填充對應(yīng)的Output操作表達(dá)式;


所以,修飾符&的作用就是要求GCC編譯器為所有的Input操作表達(dá)式分配別的寄存器,而不會(huì)分配與被修飾符&修飾的Output操作表達(dá)式相同的寄存器;修飾符&也寫在操作約束中,即:&約束;由于GCC已經(jīng)規(guī)定加號(+)或等號(=)占據(jù)約束的第一個(gè)字符,那么&約束只能占用第二個(gè)字符;


例如:
int __out, __in1, __in2;
__asm__("popl %0\n\t"
        "movl %1,%%esi\n\t"
        "movl %2,%%edi\n\t"
        :"=&a"(__out)
        :"r"(__in1),"r"(__in2));


注意:如果一個(gè)Output操作表達(dá)式的寄存器約束被指定為某個(gè)寄存器,只有當(dāng)至少存在一個(gè)Input操作表達(dá)式的寄存器約束為可選約束(意思是GCC可以從多個(gè)寄存器中選取一個(gè),或使用非寄存器方式)時(shí),比如"r"或"g"時(shí),此Output操作表達(dá)式使用符號&修飾才有意義;如果你為所有的Input操作表達(dá)式指定了固定的寄存器,或使用內(nèi)存/立即數(shù)約束時(shí),則此Output操作表達(dá)式使用符號&修飾沒有任何意義;
比如:
__asm__("popl %0\n\t"
        "movl %1,%esi\n\t"
        "movl %2,%edi\n\t"
        :"=&a"(__out)
        :"m"(__in1),"c"(__in2));
此例中的Output操作表達(dá)式完全沒有必要使用符號&來修飾,因?yàn)開_in1和__in2都已經(jīng)被指定了固定的寄存器,或使用了內(nèi)存方式,GCC無從選擇;
如果你已經(jīng)為某個(gè)Output操作表達(dá)式指定了修飾符&,并指定了固定的寄存器,那么,就不能再為任何Input操作表達(dá)式指定這個(gè)寄存器了,否則會(huì)出現(xiàn)編譯報(bào)錯(cuò);
比如:
__asm__("popl %0; movl %1,%%esi; movl %2,%%edi;":"=&a"(__out):"a"(__in1),"c"(__in2));
對這條語句的編譯就會(huì)報(bào)錯(cuò);
相反,你也可以為Output指定可選約束,比如"r"或"g"等,讓GCC為此Output操作表達(dá)式選擇合適的寄存器,或使用內(nèi)存方式,GCC在選擇的時(shí)候,會(huì)排除掉已經(jīng)被Input操作表達(dá)式所使用過的所有寄存器,然后在剩下的寄存器中選擇,或者干脆使用內(nèi)存方式;
比如:
__asm__("popl %0; movl %1,%%esi; movl %2,%%edi;":"=&r"(__out):"a"(__in1),"c"(__in2));
這三個(gè)修飾符只能用在Output操作表達(dá)式中,而修飾符%則恰恰相反,它只能用在Input操作表達(dá)式中;
修飾符%用于向GCC聲明:"當(dāng)前Input操作表達(dá)式中的C/C++表達(dá)式可以與下一個(gè)Input操作表達(dá)式中的C/C++表達(dá)式互換";這個(gè)修飾符一般用于符合交換律運(yùn)算的地方;比如:加、乘、按位與&、按位或|等等;
例如:
__asm__("addl %1,%0\n\t":"=r"(__out):"%r"(__in1),"0"(__in2));
其中,"0"(__in2)表示使用與第一個(gè)Input操作表達(dá)式("r"(__in1))相同的寄存器或內(nèi)存;
由于使用符號%修飾__in1的寄存器方式r,那么就表示,__in1與__in2可以互換位置;加法的兩個(gè)操作數(shù)交換位置之后,和不變;
修飾符  I/O  意義
=        O    表示此Output操作表達(dá)式是只寫的
+        O    表示此Output操作表達(dá)式是可讀可寫的
&        O    表示此Output操作表達(dá)式獨(dú)占為其指定的寄存器
%        I    表示此Input操作表達(dá)式中的C/C++表達(dá)式可以與下一個(gè)Input操作表達(dá)式中的C/C++表達(dá)式互換
四、占位符
每一個(gè)占位符對應(yīng)一個(gè)Input/Output操作表達(dá)式;
帶C/C++表達(dá)式的內(nèi)聯(lián)匯編中有兩種占位符:序號占位符名稱占位符;
1.序號占位符:
GCC規(guī)定:一個(gè)內(nèi)聯(lián)匯編語句中最多只能有10個(gè)Input/Output操作表達(dá)式,這些操作表達(dá)式按照他們被列出來的順序依次賦予編號0到9;對于占位符中的數(shù)字而言,與這些編號是對應(yīng)的;比如:占位符%0對應(yīng)編號為0的操作表達(dá)式,占位符%1對應(yīng)編號為1的操作表達(dá)式,依次類推;
由于占位符前面要有一個(gè)百分號%,為了去邊占位符與寄存器,GCC規(guī)定:在帶有C/C++表達(dá)式的內(nèi)聯(lián)匯編語句的指令列表里列出的寄存器名稱前面必須使用兩個(gè)百分號(%%),一區(qū)別于占位符語法;
GCC對占位符進(jìn)行編譯的時(shí)候,會(huì)將每一個(gè)占位符替換為對應(yīng)的Input/Output操作表達(dá)式所指定的寄存器/內(nèi)存/立即數(shù);
例如:
__asm__("addl %1,%0\n\t":"=a"(__out):"m"(__in1),"a"(__in2));
這個(gè)語句中,%0對應(yīng)Output操作表達(dá)式"=a"(__out),而"=a"(__out)指定的寄存器是%eax,所以,占位符%0被替換為%eax;占位符%1對應(yīng)Input操作表達(dá)式"m"(__in1),而"m"(__in1)被指定為內(nèi)存,所以,占位符%1被替換位__in1的內(nèi)存地址;
用一句話描述:序號占位符就是前面描述的%0、%1、%2、%3、%4、%5、%6、%7、%8、%9;其中,每一個(gè)占位符對應(yīng)一個(gè)Input/Output的C/C++表達(dá)式;
2.名稱占位符:
由于GCC中限制這種占位符的個(gè)數(shù)最多只能由這10個(gè),這也就限制了Input/Output操作表達(dá)式中C/C++表達(dá)式的數(shù)量做多只能有10個(gè);如果需要的C/C++表達(dá)式的數(shù)量超過10個(gè),那么,這些需要占位符就不夠用了;


GCC內(nèi)聯(lián)匯編提供了名稱占位符來解決這個(gè)問題;即:使用一個(gè)名字字符串一個(gè)C/C++表達(dá)式對應(yīng);這個(gè)名字字符串就稱為名稱占位符;而這個(gè)名字通常使用與C/C++表達(dá)式中的變量完全相同的名字;
使用名字占位符時(shí),內(nèi)聯(lián)匯編的Input/Output操作表達(dá)式中的C/C++表達(dá)式的格式如下:
[name] "constraint"(變量)
此時(shí),指令列表中的占位符的書寫格式如下:
%[name]
這個(gè)格式等價(jià)于序號占位符中的%0,%1,$2等等;
使用名稱占位符時(shí),一個(gè)name對應(yīng)一個(gè)變量;
例如:
__asm__("imull %[value1],%[value2]"
        :[value2] "=r"(data2)
        :[value1] "r"(data1),"0"(data2));
此例中,名稱占位符value1就對應(yīng)變量data1,名稱占位符value2對應(yīng)變量data2;GCC編譯的時(shí)候,同樣會(huì)把這兩個(gè)占位符分別替換成對應(yīng)的變量所使用的寄存器/內(nèi)存地址/立即數(shù);而且也增強(qiáng)了代碼的可讀性;
這個(gè)例子,使用序號占位符的寫法如下:
__asm__("imull %1,%0"
        :"=r"(data2)
        :"r"(data1),"0"(data2));
五、寄存器/內(nèi)存修改標(biāo)示(Clobber/Modify)
有時(shí)候,當(dāng)你想通知GCC當(dāng)前內(nèi)聯(lián)匯編語句可能會(huì)對某些寄存器或內(nèi)存進(jìn)行修改,希望GCC在編譯時(shí)能夠?qū)⑦@一點(diǎn)考慮進(jìn)去;那么你就可以在Clobber/Modify部分聲明這些寄存器或內(nèi)存;
1.寄存器修改通知:
這種情況一般發(fā)生在一個(gè)寄存器出現(xiàn)在指令列表中,但又不是Input/Output操作表達(dá)式所指定的,也不是在一些Input/Output操作表達(dá)式中使用"r"或"g"約束時(shí)由GCC選擇的,同時(shí),此寄存器被指令列表中的指令所修改,而這個(gè)寄存器只供當(dāng)前內(nèi)聯(lián)匯編語句使用的情況;比如:
__asm__("movl %0,%%ebx"::"a"(__foo):"bx");
//這個(gè)內(nèi)聯(lián)匯編語句中,%ebx出現(xiàn)在指令列表中,并且被指令修改了,但是卻未被任何Input/Output操作表達(dá)式是所指定,所以,你需要在Clobber/Modify部分指定"bx",以讓GCC知道這一點(diǎn);
因?yàn)槟阍贗nput/Output操作表達(dá)式中指定的寄存器,或當(dāng)你為一些Input/Output操作表達(dá)式使用"r"/"g"約束,讓GCC為你選擇一個(gè)寄存器時(shí),GCC對這些寄存器的狀態(tài)是非常清楚的,它知道這些寄存器是被修改的,你根本不需要在Clobber/Modify部分聲明它們;但除此之外,GCC對剩下的寄存器中哪些會(huì)被當(dāng)前內(nèi)聯(lián)匯編語句所修改則一無所知;所以,如果你真的在當(dāng)前內(nèi)聯(lián)匯編指令中修改了它們,那么就最好在Clobber/Modify部分聲明它們,讓GCC針對這些寄存器做相應(yīng)的處理;否則,有可能會(huì)造成寄存器不一致,從而造成程序執(zhí)行錯(cuò)誤;


在Clobber/Modify部分聲明這些寄存器的方法很簡單,只需要將寄存器的名字用雙引號括起來就可以;如果要聲明多個(gè)寄存器,則相鄰兩個(gè)寄存器名字之間用逗號隔開;
例如:
__asm__("movl %0,%%ebx; popl %%ecx"::"a"(__foo):"bx","cx");
這個(gè)語句中,聲明了bx和cx,告訴GCC:寄存器%ebx和%ecx可能會(huì)被修改,要求GCC考慮這個(gè)因素;
寄存器名稱串:
"al"/"ax"/"eax":代表寄存器%eax
"bl"/"bx"/"ebx":代表寄存器%ebx
"cl"/"cx"/"ecx":代表寄存器%ecx
"dl"/"dx"/"edx":代表寄存器%edx
"si"/"esi":代表寄存器%esi
"di"/"edi":代表寄存器%edi
所以,只需要使用"ax","bx","cx","dx","si","di"就可以了,因?yàn)樗麄兌即韺?yīng)的寄存器;
如果你在一個(gè)內(nèi)斂匯編語句的Clobber/Modify部分向GCC聲明了某個(gè)寄存器內(nèi)存發(fā)生了改變,GCC在編譯時(shí),如果發(fā)現(xiàn)這個(gè)被聲明的寄存器的內(nèi)容在此內(nèi)聯(lián)匯編之后還要繼續(xù)使用,那么,GCC會(huì)首先將此寄存器的內(nèi)容保存起來,然后在此內(nèi)聯(lián)匯編語句的相關(guān)代碼生成之后,再將其內(nèi)容回復(fù);
另外需要注意的是,如果你在Clobber/Modify部分聲明了一個(gè)寄存器,那么這個(gè)寄存器將不能再被用作當(dāng)前內(nèi)斂匯編語句的Input/Output操作表達(dá)式的寄存器約束,如果Input/Output操作表達(dá)式的寄存器約束被指定為"r"/"g",GCC也不會(huì)選擇已經(jīng)被聲明在Clobber/Modify部分中的寄存器;
例如:
__asm__("movl %0,%%ebx"::"a"(__foo):"ax","bx");
這條語句中的Input操作表達(dá)式"a"(__foo)中已經(jīng)指定了寄存器%eax,那么在Clobber/Modify部分中個(gè)列出的"ax"就是非法的;編譯時(shí),GCC會(huì)報(bào)錯(cuò);
2.內(nèi)存修改通知:
除了寄存器的內(nèi)容會(huì)被修改之外,內(nèi)存的內(nèi)容也會(huì)被修改;如果一個(gè)內(nèi)聯(lián)匯編語句的指令列表中的指令對內(nèi)存進(jìn)行了修改,或者在此內(nèi)聯(lián)匯編出現(xiàn)的地方,內(nèi)存內(nèi)容可能發(fā)生改變,而被改變的內(nèi)存地址你沒有在其Output操作表達(dá)式中使用"m"約束,這種情況下,你需要使用在Clobber/Modify部分使用字符串"memory"向GCC聲明:"在這里,內(nèi)存發(fā)生了,或可能發(fā)生了改變";
例如:
void* memset(void* s, char c, size_t count)
{
  __asm__("cld\n\d"
          "rep\n\t"
          "stosb"
          :/*no output*/
          :"a"(c),"D"(s),"c"(count)
          :"cx","di","memory");
  return s;
}
如果一個(gè)內(nèi)聯(lián)匯編語句的Clobber/Modify部分存在"memory",那么GCC會(huì)保證在此內(nèi)聯(lián)匯編之前,如果某個(gè)內(nèi)存的內(nèi)容被裝入了寄存器,那么,在這個(gè)內(nèi)聯(lián)匯編之后,如果需要使用這個(gè)內(nèi)存處的內(nèi)容,就會(huì)直接到這個(gè)內(nèi)存處重新讀取,而不是使用被存放在寄存器中的拷貝;因?yàn)檫@個(gè)時(shí)候寄存器中的拷貝很可能已經(jīng)和內(nèi)存處的內(nèi)容不一致了;
3.標(biāo)志寄存器修改通知:
當(dāng)一個(gè)內(nèi)聯(lián)匯編中包含影響標(biāo)志寄存器eflags的條件,那么也需要在Clobber/Modify部分中使用"cc"來向GCC聲明這一點(diǎn);

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
GCC Inline ASM GCC內(nèi)聯(lián)匯編
【轉(zhuǎn)】__asm__和__volatile__
C語言內(nèi)嵌匯編簡介
Linux C中內(nèi)聯(lián)匯編的語法格式及使用方法(Inline Assembly in Linux C)
AT&T匯編語言與GCC內(nèi)嵌匯編簡介
SSE指令集優(yōu)化心得(二)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服