0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

剖析鴻蒙的匯編語言和CPU指令

鴻蒙系統(tǒng)HarmonyOS ? 來源:my.oschina ? 作者:my.oschina ? 2021-04-24 09:37 ? 次閱讀

本篇通過拆解一段很簡單的匯編代碼來快速認(rèn)識(shí)匯編,為讀懂鴻蒙匯編打基礎(chǔ).系列篇后續(xù)將逐個(gè)剖析鴻蒙的匯編文件.

匯編很簡單

第一: 要認(rèn)定匯編語言一定是簡單的,沒有高深的東西,無非就是數(shù)據(jù)的搬來搬去,運(yùn)行時(shí)數(shù)據(jù)主要待在兩個(gè)地方:內(nèi)存和寄存器。寄存器是CPU內(nèi)部存儲(chǔ)器,離運(yùn)算器最近,所以最快.

第二: 運(yùn)行空間(??臻g)就是CPU打卡上班的地方,內(nèi)核設(shè)計(jì)者規(guī)定誰請CPU上班由誰提供場地,用戶程序提供的場地叫用戶棧,敏感工作CPU要帶回公司做,公司提供的場地叫內(nèi)核棧,敏感工作叫系統(tǒng)調(diào)用,系統(tǒng)調(diào)用的本質(zhì)理解是CPU要切換工作模式即切換辦公場地。

第三:CPU的工作順序是流水線的,它只認(rèn)指令,而且只去一個(gè)地方(指向代碼段的PC寄存器)拿指令運(yùn)算消化。指令集是告訴外界我CPU能干什么活并提供對話指令,匯編語言是人和CPU能愉快溝通不擰巴的共識(shí)語言。一一對應(yīng)了CPU指令,又能確保記性不好的人類能模塊化的設(shè)計(jì)idea, 先看一段C編譯成匯編代碼再來說模塊化。

square(c -> 匯編)

//編譯器: armv7-a clang (trunk)
//++++++++++++ square(c -> 匯編)++++++++++++++++++++++++
int square(int a,int b){
    return a*b;
}
square(int, int):
        sub     sp, sp, #8     @sp減去8,意思為給square分配??臻g,只用2個(gè)??臻g完成計(jì)算
        str     r0, [sp, #4]   @第一個(gè)參數(shù)入棧
        str     r1, [sp]       @第二個(gè)參數(shù)入棧
        ldr     r1, [sp, #4]   @取出第一個(gè)參數(shù)給r1
        ldr     r2, [sp]       @取出第二個(gè)參數(shù)給r2
        mul     r0, r1, r2     @執(zhí)行a*b給R0,返回值的工作一直是交給R0的
        add     sp, sp, #8     @函數(shù)執(zhí)行完了,要釋放申請的??臻g
        bx      lr             @子程序返回,等同于mov pc,lr,即跳到調(diào)用處

fp(c -> 匯編)

//++++++++++++ fp(c -> 匯編)++++++++++++++++++++++++
int fp(int b)
{
    int a = 1;
    return square(a+b,a+b);
}
fp(int):
        push    {r11, lr}      @r11(fp)/lr入棧,保存調(diào)用者main的位置
        mov     r11, sp        @r11用于保存sp值,函數(shù)棧開始位置 
        sub     sp, sp, #8     @sp減去8,意思為給fp分配棧空間,只用2個(gè)??臻g完成計(jì)算
        str     r0, [sp, #4]   @先保存參數(shù)值,放在SP+4,此時(shí)r0中存放的是參數(shù)
        mov     r0, #1         @r0=1
        str     r0, [sp]       @再把1也保存在SP的位置
        ldr     r0, [sp]       @把SP的值給R0
        ldr     r1, [sp, #4]   @把SP+4的值給R1
        add     r1, r0, r1     @執(zhí)行r1=a+b
        mov     r0, r1         @r0=r1,用r0,r1傳參
        bl      square(int, int)@先mov lr, pc 再mov pc square(int, int)   
        mov     sp, r11        @函數(shù)執(zhí)行完了,要釋放申請的??臻g 
        pop     {r11, lr}      @彈出r11和lr,lr是專用標(biāo)簽,彈出就自動(dòng)復(fù)制給lr寄存器
        bx      lr             @子程序返回,等同于mov pc,lr,即跳到調(diào)用處

main(c -> 匯編)

//++++++++++++ main(c -> 匯編)++++++++++++++++++++++++
int main()
{
    int sum = 0;
    for(int a = 0;a < 100; a++){
        sum = sum + fp(a);
    }
    return sum;
}
main:
        push    {r11, lr}      @r11(fp)/lr入棧,保存調(diào)用者的位置
        mov     r11, sp        @r11用于保存sp值,函數(shù)棧開始位置
        sub     sp, sp, #16    @sp減去16,意思為給main分配棧空間,只用4個(gè)??臻g完成計(jì)算
        mov     r0, #0         @初始化r0
        str     r0, [r11, #-4] @執(zhí)行sum = 0
        str     r0, [sp, #8]   @sum將始終占用SP+8的位置
        str     r0, [sp, #4]   @a將始終占用SP+4的位置
        b       .LBB1_1        @跳到循環(huán)開始位置
.LBB1_1:                       @循環(huán)開始位置入口
        ldr     r0, [sp, #4]   @取出a的值給r0
        cmp     r0, #99        @跟99比較
        bgt     .LBB1_4        @大于99,跳出循環(huán) mov pc .LBB1_4
        b       .LBB1_2        @繼續(xù)循環(huán),直接 mov pc .LBB1_2
.LBB1_2:                       @符合循環(huán)條件入口
        ldr     r0, [sp, #8]   @取出sum的值給r0,sp+8用于寫SUM的值
        str     r0, [sp]       @先保存SUM的值,SP的位置用于讀SUM值
        ldr     r0, [sp, #4]   @r0用于傳參,取出A的值給r0作為fp的參數(shù)
        bl      fp(int)        @先mov lr, pc再mov pc fp(int)
        mov     r1, r0         @fp的返回值為r0,保存到r1
        ldr     r0, [sp]       @取出SUM的值
        add     r0, r0, r1     @計(jì)算新sum的值,由R0保存
        str     r0, [sp, #8]   @將新sum保存到SP+8的位置
        b       .LBB1_3        @無條件跳轉(zhuǎn),直接 mov pc .LBB1_3
.LBB1_3:                       @完成a++操作入口
        ldr     r0, [sp, #4]   @SP+4中記錄是a的值,賦給r0
        add     r0, r0, #1     @r0增加1
        str     r0, [sp, #4]   @把新的a值放回SP+4里去
        b       .LBB1_1        @跳轉(zhuǎn)到比較 a < 100 處
.LBB1_4:                       @循環(huán)結(jié)束入口
        ldr     r0, [sp, #8]   @最后SUM的結(jié)果給R0,返回值的工作一直是交給R0的
        mov     sp, r11        @函數(shù)執(zhí)行完了,要釋放申請的??臻g
        pop     {r11, lr}      @彈出r11和lr,lr是專用標(biāo)簽,彈出就自動(dòng)復(fù)制給lr寄存器
        bx      lr             @子程序返回,跳轉(zhuǎn)到lr處等同于 MOV PC, LR

代碼有點(diǎn)長,都加了注釋,如果能直接看懂那么恭喜你,鴻蒙內(nèi)核的6個(gè)匯編文件基于也就懂了。這是以下C文件全貌

文件全貌

#include 
#include 

int square(int a,int b){
    return a*b;
}

int fp(int b)
{
    int a = 1;
    return square(a+b,a+b);
}

int main()
{
    int sum = 0;
    for(int a = 0;a < 100; a++){
        sum = sum + fp(a);
    }
    return sum;
}

代碼很簡單誰都能看懂,代碼很典型,具有代表性,有循環(huán),有判斷,有運(yùn)算,有多級(jí)函數(shù)調(diào)用。編譯后的匯編代碼基本和C語言的結(jié)構(gòu)差不太多, 區(qū)別是對循環(huán)的實(shí)現(xiàn)用了四個(gè)模塊,四個(gè)模塊也好理解: 一個(gè)是開始?jí)K(LBB1_1), 一個(gè)符合條件的處理塊(LBB1_2),一個(gè)條件發(fā)生變化塊(LBB1_3),最后收尾塊(LBB1_4).

按塊逐一剖析.

先看最短的那個(gè)

int square(int a,int b){
    return a*b;
}
//編譯成
square(int, int):
        sub     sp, sp, #8     @sp減去8,意思為給square分配??臻g,只用2個(gè)棧空間完成計(jì)算
        str     r0, [sp, #4]   @第一個(gè)參數(shù)入棧
        str     r1, [sp]       @第二個(gè)參數(shù)入棧
        ldr     r1, [sp, #4]   @取出第一個(gè)參數(shù)給r1
        ldr     r2, [sp]       @取出第二個(gè)參數(shù)給r2
        mul     r0, r1, r2     @執(zhí)行a*b給R0,返回值的工作一直是交給R0的
        add     sp, sp, #8     @函數(shù)執(zhí)行完了,要釋放申請的??臻g
        bx      lr             @子程序返回,等同于mov pc,lr,即跳到調(diào)用處

首先上來一句 sub sp, sp, #8 等同于 sp = sp - 8 ,CPU運(yùn)行需要場地,這個(gè)場地就是棧 ,SP是指向棧的指針,表示此時(shí)用棧的刻度. 代碼和鴻蒙內(nèi)核用棧方式一樣,都采用了遞減滿棧的方式(FD). 什么是遞減滿棧? 遞減指的是棧底地址高于棧頂?shù)刂?棧的生長方向是遞減的, 滿棧指的是SP指針永遠(yuǎn)指向棧頂. 每個(gè)函數(shù)都有自己獨(dú)立的棧底和棧頂,之間的空間統(tǒng)稱棧幀.可以理解為分配了一塊 區(qū)域給函數(shù)運(yùn)行,sub sp, sp, #8 代表申請2個(gè)??臻g,一個(gè)棧空間按四個(gè)字節(jié)算. 用完要不要釋放?當(dāng)然要,add sp, sp, #8 就是釋放??臻g. 是一對的,減了又加回去,空間就歸還了. ldr r1, [sp, #4] 的意思是取出SP+4這個(gè)虛擬地址的值給r1寄存器,而SP的指向并沒有改變的,還是在棧頂, 為什么要+呢, +就是往回?cái)?shù), 定位到分配的??臻g上.
一定要理解遞減滿棧,這是關(guān)鍵! 否則讀不懂內(nèi)核匯編代碼.

入?yún)⒎绞?/strong>

一般都是通過寄存器(r0..r10)傳參,fp調(diào)用square之前會(huì)先將參數(shù)給(r0..r10)

        add     r1, r0, r1     @執(zhí)行r1=a+b
        mov     r0, r1         @r0=r1,用r0,r1傳參
        bl      square(int, int)@先mov lr, pc 再mov pc square(int, int) 

到了square中后,先讓 r0,r1入棧,目的是保存參數(shù)值, 因?yàn)?square中要用r0,r1 ,

        str     r0, [sp, #4]   @先入棧保存第一個(gè)參數(shù)
        str     r1, [sp]       @再入棧保存第二個(gè)參數(shù)
        ldr     r1, [sp, #4]   @再取出第一個(gè)參數(shù)給r1,(a*b)中a值
        ldr     r2, [sp]       @再取出第二個(gè)參數(shù)給r2,用于計(jì)算 (a*b)中b值

是不是感覺這段匯編很傻,直接不保存計(jì)算不就完了嗎,這個(gè)是流程問題,編譯器統(tǒng)一先保存參數(shù),至于你想怎么用它不管,也管不了. 另外返回值都是默認(rèn)統(tǒng)一給r0保存. square中將(a*b)的結(jié)果給了r0,回到fp中取出R0對fp來說這就是square的返回值,這是規(guī)定.

函數(shù)調(diào)用main 和 fp 中都需要調(diào)用其他函數(shù),所以都出現(xiàn)了

        push    {r11, lr}
        //....
        pop     {r11, lr}

這哥倆也是成對出現(xiàn)的,這是函數(shù)調(diào)用的必備裝備,作用是保存和恢復(fù)調(diào)用者的現(xiàn)場,例如 main -> fp, fp要保存main的棧幀范圍和指令位置, lr保存的是main函數(shù)執(zhí)行到哪個(gè)指令的位置, r11的作用是指向main的棧頂位置,如此fp執(zhí)行完后return回main的時(shí)候,先mov pc,lr, PC寄存器的值一變, 表示執(zhí)行的代碼就變了,又回到了main的指令和棧幀繼續(xù)未完成的事業(yè).

內(nèi)存和寄存器數(shù)據(jù)怎么搬?

數(shù)據(jù)主要待在兩個(gè)地方:內(nèi)存和寄存器. 寄存器<->寄存器 , 內(nèi)存<->寄存器 , 內(nèi)存<->內(nèi)存 搬運(yùn)指令都不一樣.

        str     r1, [sp]       @ 寄存器->內(nèi)存
        ldr     r1, [sp, #4]   @ 內(nèi)存->寄存器

這又是一對,用于 內(nèi)存<->寄存器之間,熟知的 mov r0, r1 用于 寄存器<->寄存器

編輯:hfy

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5294

    瀏覽量

    119814
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10804

    瀏覽量

    210829
  • 鴻蒙系統(tǒng)
    +關(guān)注

    關(guān)注

    183

    文章

    2634

    瀏覽量

    66153
收藏 人收藏

    評論

    相關(guān)推薦

    【RISC-V開放架構(gòu)設(shè)計(jì)之道|閱讀體驗(yàn)】匯編語言和擴(kuò)展指令

    【RISC-V開放架構(gòu)設(shè)計(jì)之道|閱讀體驗(yàn)】匯編語言和擴(kuò)展指令匯編語言 將C語言翻譯成可執(zhí)行的機(jī)器語言的重要步驟包括編譯過程,
    發(fā)表于 02-03 13:29

    請問匯編語言和C語言哪個(gè)好用些

    匯編語言和C語言哪個(gè)好用一點(diǎn)
    發(fā)表于 04-23 07:55

    C語言和匯編語言在開發(fā)單片機(jī)時(shí)各有哪些優(yōu)缺點(diǎn)

    01、C語言和匯編語言在開發(fā)單片機(jī)時(shí)各有哪些優(yōu)缺點(diǎn)?匯編語言是一種用文字助記符來表示機(jī)器指令的符號(hào)語言,是最接近機(jī)器碼的一種
    發(fā)表于 07-14 06:49

    C語言和匯編語言的優(yōu)缺點(diǎn)是什么?

    什么是c語言?什么是匯編語言?C語言和匯編語言的優(yōu)缺點(diǎn)是什么?
    發(fā)表于 10-14 08:48

    MSP430單片機(jī)C語言和匯編語言混合編程

    為了發(fā)揮C語言和匯編語言各自的優(yōu)點(diǎn),二者需要相互調(diào)用函數(shù)。本文首先介紹了MSP430單片機(jī)的C語言函數(shù)的參數(shù)傳遞規(guī)則,然后對C語言和匯編語言
    發(fā)表于 08-31 10:55 ?50次下載

    MDP430單片機(jī)語言和匯編語言混合編程

    摘要:為了發(fā)揮C語言和匯編語言各自的優(yōu)點(diǎn)二者需要相互調(diào)用函數(shù)本文首先介紹了"MSP430”單片機(jī)的!語言函數(shù)的參數(shù)傳遞規(guī)則然后對C語言和匯編語言
    發(fā)表于 07-15 12:02 ?46次下載

    16位和32位微處理器指令系統(tǒng)與匯編語言編程

    16位和32位微處理器指令系統(tǒng)與匯編語言編程, 本章學(xué)習(xí)要點(diǎn) 8086CPu指令系統(tǒng)的尋址方式 8086CPU
    發(fā)表于 12-31 10:41 ?0次下載

    C語言和匯編語言混合編程方法和C語言中斷處理方法

    C語言和匯編語言混合編程方法和C語言中斷處理方法,new
    發(fā)表于 01-06 14:36 ?36次下載

    基于MSP430單片機(jī)C語言和匯編語言混合編程

    基于MSP430單片機(jī)C語言和匯編語言混合編程
    發(fā)表于 10-12 17:05 ?18次下載
    基于MSP430單片機(jī)C<b class='flag-5'>語言和</b><b class='flag-5'>匯編語言</b>混合編程

    MSP430單片機(jī)的C語言和匯編語言混合編程

    MSP430單片機(jī)的C語言和匯編語言混合編程
    發(fā)表于 10-13 08:35 ?24次下載
    MSP430單片機(jī)的C<b class='flag-5'>語言和</b><b class='flag-5'>匯編語言</b>混合編程

    匯編語言指令

    匯編語言指令
    發(fā)表于 10-24 08:45 ?24次下載
    <b class='flag-5'>匯編語言</b>的<b class='flag-5'>指令</b>集

    高級(jí)語言和匯編語言_機(jī)器語言的區(qū)別

    本文首先介紹了高級(jí)語言的優(yōu)缺點(diǎn)、匯編語言的優(yōu)缺點(diǎn)及使用匯編語言的理由,其次介紹了機(jī)器語言的優(yōu)缺點(diǎn),最后闡述了高級(jí)語言和
    的頭像 發(fā)表于 04-16 10:28 ?3w次閱讀

    C語言和匯編語言在開發(fā)單片機(jī)時(shí)各有哪些優(yōu)缺點(diǎn)?

    匯編語言是一種用文字助記符來表示機(jī)器指令的符號(hào)語言,是最接近機(jī)器碼的一種語言。其主要優(yōu)點(diǎn)是占用資源少、程序執(zhí)行效率高。但是不同的CPU, 其
    的頭像 發(fā)表于 11-26 14:21 ?9836次閱讀

    單片機(jī)C語言和匯編語言混合編程實(shí)例詳解

    單片機(jī)C語言和匯編語言混合編程實(shí)例詳解
    發(fā)表于 08-16 09:50 ?225次下載

    單片機(jī)匯編語言的結(jié)構(gòu)/數(shù)據(jù)類型/匯編指令

    開發(fā)匯編語言是為了為機(jī)器級(jí)代碼指令提供助記符或符號(hào),匯編語言程序由助記符組成,因此應(yīng)將它們翻譯成機(jī)器代碼。負(fù)責(zé)這種轉(zhuǎn)換的程序稱為匯編程序。匯編語言
    的頭像 發(fā)表于 07-07 12:28 ?2852次閱讀