gcc內(nèi)嵌匯編詳解

[作者:byeyear 首發(fā):cnblogs Email:east3@163.com 轉(zhuǎn)載請(qǐng)注明]

有時(shí)候我們希望在C/C++代碼中使用嵌入式匯編佑菩,因?yàn)镃中沒有對(duì)應(yīng)的函數(shù)或語法可用。比如我最近在ARM上寫FIR程序時(shí)怨愤,需要對(duì)最后的結(jié)果進(jìn)行飽和處理鞠评,但gcc沒有提供ssat這樣的函數(shù)翅敌,于是不得不在C代碼中嵌入?yún)R編指令振定。

1. 入門

在C中嵌入?yún)R編的最大問題是如何將C語言變量與指令操作數(shù)相關(guān)聯(lián)雅宾。當(dāng)然箍土,gcc都幫我們想好了逢享。下面是是一個(gè)簡(jiǎn)單例子。

asm(“fsinx %1, %0”:”=f”(result):”f”(angle));

這里我們不需要關(guān)注fsinx指令是干啥的吴藻;只需要知道這條指令需要兩個(gè)浮點(diǎn)寄存器作為操作數(shù)瞒爬。作為專職處理C語言的gcc編譯器,它是沒辦法知道fsinx這條匯編指令需要什么樣的操作數(shù)的,這就要求程序猿告知gcc相關(guān)信息侧但,方法就是指令后面的”=f”和”f”矢空,表示這是兩個(gè)浮點(diǎn)寄存器操作數(shù)。這被稱為操作數(shù)規(guī)則(constraint)禀横。規(guī)則前面加上”=”表示這是一個(gè)輸出操作數(shù)屁药,否則是輸入操作數(shù)。constraint后面括號(hào)內(nèi)的是與該寄存器關(guān)聯(lián)的變量柏锄。這樣gcc就知道如何將這條嵌入式匯編語句轉(zhuǎn)成實(shí)際的匯編指令了:

fsinx:匯編指令名

%1, %0:匯編指令操作數(shù)

“=f”(result):操作數(shù)%0是一個(gè)浮點(diǎn)寄存器酿箭,與變量result關(guān)聯(lián)(對(duì)輸出操作數(shù),“關(guān)聯(lián)”的意思就是說gcc執(zhí)行完這條匯編指令后會(huì)把寄存器%0的內(nèi)容送到變量result中)

“f”(angle):操作數(shù)%1是一個(gè)浮點(diǎn)寄存器趾娃,與變量angle關(guān)聯(lián)(對(duì)輸入操作數(shù)缭嫡,“關(guān)聯(lián)”的意思是就是說gcc執(zhí)行這條匯編指令前會(huì)先將變量angle的值讀取到寄存器%1中)

因此這條嵌入式匯編會(huì)轉(zhuǎn)換為至少三條匯編指令(非優(yōu)化):

1> 將angle變量的值加載到寄存器%1

2> fsinx匯編指令,源寄存器%1茫舶,目標(biāo)寄存器%0

3> 將寄存器%0的值存儲(chǔ)到變量result

當(dāng)然械巡,在高優(yōu)化級(jí)別下上面的敘述可能不適用;比如源操作數(shù)可能本來就已經(jīng)在某個(gè)浮點(diǎn)寄存器中了饶氏。

這里我們也看到constraint前加”=”符號(hào)的意義:gcc需要知道這個(gè)操作數(shù)是在執(zhí)行嵌入?yún)R編前從變量加載到寄存器讥耗,還是在執(zhí)行后從寄存器存儲(chǔ)到變量中。

常用的constraints有以下幾個(gè)(更多細(xì)節(jié)參見gcc手冊(cè)):

m 內(nèi)存操作數(shù)

r 寄存器操作數(shù)

i 立即數(shù)操作數(shù)(整數(shù))

f 浮點(diǎn)寄存器操作數(shù)

F 立即數(shù)操作數(shù)(浮點(diǎn))

從這個(gè)栗子也可以看出嵌入式匯編的基本格式:

asm(“匯編指令”:”=輸出操作數(shù)規(guī)則”(關(guān)聯(lián)變量):”輸入操作數(shù)規(guī)則”(關(guān)聯(lián)變量));

輸出操作數(shù)必須為左值疹启;這個(gè)顯然古程。

2. 多個(gè)操作數(shù),或沒有輸出操作數(shù)

如果某個(gè)指令有多個(gè)輸入或輸出操作數(shù)怎么辦喊崖?例如arm有很多指令是三操作數(shù)指令挣磨。這個(gè)時(shí)候用逗號(hào)分隔多個(gè)規(guī)則:

asm(“add %0, %1, %2”:”=r”(sum):”r”(a), “r”(b));

每條操作數(shù)規(guī)則按順序?qū)?yīng)操作數(shù)%0, %1, %2。

對(duì)于沒有輸出操作數(shù)的情況荤懂,在匯編指令后就沒有輸出規(guī)則茁裙,于是就出現(xiàn)兩個(gè)連續(xù)冒號(hào),后跟輸入規(guī)則节仿。

3. 輸入-輸出(或讀-寫)操作數(shù)

有時(shí)候一個(gè)操作數(shù)既是輸入又是輸出晤锥,比如x86下的這條指令:

add %eax, %ebx

注意指令使用AT&T格式而不是Intel格式。寄存器ebx同時(shí)作為輸入操作數(shù)和輸出操作數(shù)廊宪。對(duì)這樣的操作數(shù)矾瘾,在規(guī)則前使用”+”字符:

asm("add %1, %0" : "+r"(a) : "r"(b));

對(duì)應(yīng)C語言語句a=a+b。

注意這樣的操作數(shù)不能使用”=”符號(hào)箭启,因?yàn)間cc看到”=”符號(hào)會(huì)認(rèn)為這是一個(gè)單輸出操作數(shù)壕翩,于是在將嵌入?yún)R編轉(zhuǎn)換為真正匯編的時(shí)候就不會(huì)預(yù)先將變量a的值加載到寄存器%0中。

另一個(gè)辦法是將讀-寫操作數(shù)在邏輯上拆分為兩個(gè)操作數(shù):

asm(“add %2, %0” : “=r”(a) : “0”(a), “r”(b));

對(duì)“邏輯”輸入操作數(shù)1指定數(shù)字規(guī)則”0”傅寡,表示這個(gè)邏輯操作數(shù)占用和操作數(shù)0一樣的“位置”(占用同一個(gè)寄存器)放妈。這種方法的特點(diǎn)是可以將兩個(gè)“邏輯”操作數(shù)關(guān)聯(lián)到兩個(gè)不同的C語言變量上:

asm("add %2, %0" : "=r"(c) : "0"(a), "r"(b));

對(duì)應(yīng)于C程序語句c=a+b北救。

數(shù)字規(guī)則僅能用于輸入操作數(shù),且必須引用到輸出操作數(shù)芜抒。拿上例來說扭倾,數(shù)字規(guī)則”0”位于輸入規(guī)則段,且引用到輸出操作數(shù)0挽绩,該數(shù)字規(guī)則自身占用操作數(shù)計(jì)數(shù)1膛壹。

這里要注意,通過同名C語言變量是無法保證兩個(gè)操作數(shù)占用同一“位置”的唉堪。比如下面這樣的寫法是不行的:

(錯(cuò)誤寫法)asm(“add %2, %0”:”=r”(a):”r”(a), “r”(b));

4. 指定寄存器

有時(shí)候我們需要在指令中使用指定的寄存器模聋;典型的栗子是系統(tǒng)調(diào)用,必須將系統(tǒng)調(diào)用碼和參數(shù)放在指定寄存器中唠亚。為了達(dá)到這個(gè)目的链方,我們要在聲明變量時(shí)使用擴(kuò)展語法:

register int a asm(“%eax”) = 1; // statement 1

register int b asm(“%ebx”) = 2; // statement 2

asm("add %1, %0" : "+r"(a) : "r"(b)); // statement 3

注意只有在執(zhí)行匯編指令時(shí)能確定a在eax中,b在ebx中灶搜,其他時(shí)候a和b的存放位置是不可知的祟蚀。

另外,在這么用的時(shí)候要注意割卖,防止statement 2在執(zhí)行時(shí)覆蓋了eax前酿。例如statement 2改成下面這句:

register int b asm(“%ebx”) = func();

函數(shù)調(diào)用約定會(huì)將func()的返回值放在eax里,于是破壞了statement 1對(duì)a的賦值鹏溯。這個(gè)時(shí)候可以先用一條語句將func返回值放在臨時(shí)變量里:

int t = func();

register int a asm(“%eax”) = 1; // statement 1

register int b asm(“%ebx”) = t; // statement 2

asm("add %1, %0" : "+r"(a) : "r"(b)); // statement 3

5. 隱式改變寄存器

有的匯編指令會(huì)隱含修改一些不在指令操作數(shù)中的寄存器罢维,為了讓gcc知道這個(gè)情況,將隱式改變寄存器規(guī)則列在輸入規(guī)則之后丙挽。下面是VAX機(jī)上的栗子:

asm volatile(“movc3 %0,%1,%2”

            : /* no outputs */

            :”g”(from),”g”(to),”g”(count)

            :”r0”,”r1”,”r2”,”r3”,”r4”,”r5”);

(movc3是一條字符塊移動(dòng)(Move characters)指令)

這里要注意的是輸入/輸出規(guī)則中列出的寄存器不能和隱含改變規(guī)則中的寄存器有交叉肺孵。比如在上面的栗子里,規(guī)則“g”中就不能包含r0-r5颜阐。以指定寄存器語法聲明的變量平窘,所占用的寄存器也不能和隱含改變規(guī)則有交叉。這個(gè)應(yīng)該好理解:隱含改變規(guī)則是告訴gcc有額外的寄存器需要照顧凳怨,自然不能和輸入/輸出寄存器有交集瑰艘。

另外,如果你在指令里顯式指定某個(gè)寄存器猿棉,那么這個(gè)寄存器也必須列在隱式改變規(guī)則之中(有點(diǎn)繞了哈)磅叛。上面我們說過gcc自身是不了解匯編指令的屑咳,所以你在指令中顯式指定的寄存器萨赁,對(duì)gcc來說是隱式的,因此必須包含在隱式規(guī)則之中兆龙。另外杖爽,指令中的顯式寄存器前需要一個(gè)額外的%敲董,比如%%eax。

6. volatile

asm volatile通知gcc你的匯編指令有side effect慰安,千萬不要給優(yōu)化沒了腋寨,比如上面的栗子。

如果你的指令只是做些計(jì)算化焕,那么不需要volatile萄窜,讓gcc可以優(yōu)化它;除此以外撒桨,無腦給每個(gè)asm加上volatile或者是個(gè)好辦法查刻。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凤类,隨后出現(xiàn)的幾起案子穗泵,更是在濱河造成了極大的恐慌,老刑警劉巖旭贬,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仅父,死亡現(xiàn)場(chǎng)離奇詭異褒搔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)履肃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坐桩,“玉大人榆浓,你說我怎么就攤上這事∷涸埽” “怎么了陡鹃?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)抖坪。 經(jīng)常有香客問我萍鲸,道長(zhǎng),這世上最難降的妖魔是什么擦俐? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任脊阴,我火速辦了婚禮,結(jié)果婚禮上蚯瞧,老公的妹妹穿的比我還像新娘嘿期。我一直安慰自己,他們只是感情好埋合,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布备徐。 她就那樣靜靜地躺著,像睡著了一般甚颂。 火紅的嫁衣襯著肌膚如雪蜜猾。 梳的紋絲不亂的頭發(fā)上秀菱,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音蹭睡,去河邊找鬼衍菱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肩豁,可吹牛的內(nèi)容都是我干的脊串。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼清钥,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼洪规!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起循捺,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤斩例,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后从橘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體念赶,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年恰力,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叉谜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡踩萎,死狀恐怖停局,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情香府,我是刑警寧澤董栽,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站企孩,受9級(jí)特大地震影響锭碳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勿璃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一擒抛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧补疑,春花似錦歧沪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胁编,卻和暖如春厢钧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嬉橙。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工早直, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人市框。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓霞扬,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親枫振。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喻圃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 原文作者 Sandeep.S英文原文 [https://www.ibiblio.org/gferg/ldp/GCC...
    JeffreyLi閱讀 40,128評(píng)論 8 41
  • 原文: GCC-Inline-Assembly-HOWTO 1. 簡(jiǎn)介(Introduction.) 1.1 Co...
    桂糊涂閱讀 4,542評(píng)論 1 5
  • 文章來自這里:gcc內(nèi)聯(lián)匯編...... 在閱讀Linux內(nèi)核源碼或?qū)Υa做性能優(yōu)化時(shí),經(jīng)常會(huì)有在C語言中嵌入一段...
    Yihulee閱讀 829評(píng)論 0 0
  • 組件 計(jì)算機(jī)是一種數(shù)據(jù)處理設(shè)備粪滤,它由CPU和內(nèi)存以及外部設(shè)備組成斧拍。CPU負(fù)責(zé)數(shù)據(jù)處理,內(nèi)存負(fù)責(zé)存儲(chǔ)杖小,外部設(shè)備負(fù)責(zé)數(shù)...
    哆啦灬少A夢(mèng)閱讀 1,596評(píng)論 1 2
  • 有人說 我們是騙子 騙你妹 騙你188肆汹,還給你1只 還給你一對(duì)一服務(wù) 教你怎么使用 有人說 我們是傳銷 削你的頭 ...
    梁酸奶閱讀 311評(píng)論 0 0