網站建設工作室的營銷方式創(chuàng)業(yè)計劃書長沙靠譜的關鍵詞優(yōu)化
系列文章戳這里👇
- 什么是上下文無關文法、最左推導和最右推導
- 如何判斷二義文法及消除文法二義性
- 何時需要消除左遞歸
- 什么是句柄、什么是自上而下、自下而上分析
- 什么是LL(1)、LR(0)、LR(1)文法、LR分析表
- LR(0)、SLR(1)、LR(1)、LALR(1)文法之間的關系
- 編譯原理第三章習題
- 詞法分析、構建DFA、上下文無關文法、LL(1)分析、提取正規(guī)式
- 證明LL(1)、SLR(1)、LALR(1)文法
- 翻譯方案、屬性棧代碼
- 【運行時環(huán)境】什么是活動記錄、 活動記錄與匯編代碼的關系
編譯原理【運行時環(huán)境】—什么是活動記錄、 活動記錄與匯編代碼的關系
- 系列文章戳這里👇
- 什么是活動記錄?
- 活動記錄-AR (Activation Record)
- 活動記錄的內容
- 舉個栗子
- 再舉個栗子
- 再再舉個栗子
什么是活動記錄?
相信大家也和我一樣,覺得編譯原理在運行環(huán)境這部分比較難理解,由于機器是棧式運行的,所以里面很多操作并不利于我們理解,下面分享一下我自己對活動記錄這一塊的部分理解,也有一些x86匯編的內容
活動記錄-AR (Activation Record)
- 是一連續(xù)存儲區(qū)域,用于管理與存放和程序單元執(zhí)行相關的重要信息。
- 下圖就是活動記錄的內容,簡單來說,活動記錄是用來記錄一段函數(shù)信息的地方,
- 很好理解,當我們在寫C程序時,如果我們要用一個函數(shù)調用另一個函數(shù),那么在調用結束后怎么回到原來的函數(shù)呢?
- 就是活動記錄在幫忙,他幫我們把調用者的地址記錄下來,并用控制鏈相連。
- 而訪問鏈是記錄當前函數(shù)可以訪問的函數(shù)地址,這個是可選擇的,意思就是活動記錄中可以沒有它。
- 返回值自然就是該函數(shù)
return
的值,實在參數(shù)其實就是函數(shù)的參數(shù),也就是int fun(int a,int b)
里面的a
和b
- 局部數(shù)據(jù)就是函數(shù)體里面定義的局部變量
- 機器狀態(tài)就是活動記錄的一個指針,它是活動記錄的基地址,x86匯編中一般表示為
%ebp
,我們可以用它來找到活動記錄里面參數(shù)的位置,比如8(%ebp)
就是ebp+8
,
- 很好理解,當我們在寫C程序時,如果我們要用一個函數(shù)調用另一個函數(shù),那么在調用結束后怎么回到原來的函數(shù)呢?
- 問題來了,如何獲取活動記錄里的內容呢?上面說了,可以通過
%ebp
,如下圖所示,bp
進行偏移就可以取得的對應位置的值!
活動記錄的內容
-
下圖是棧式分配下的活動記錄內容布局,其中返回值往下即為高地址到低地址,所以我們要調用參數(shù)就需要用
bp+xx
去取,而局部參數(shù)就是bp-xx
,還不懂也沒關系,后面會舉個栗子,應該會更好理解 -
臨時區(qū)域。用以保存臨時計算結果
-
局部數(shù)據(jù)區(qū)。源程序中程序單元聲明的局部變量對應在此區(qū)域。
-
機器狀態(tài)保存區(qū)。存有機器的寄存器,程序指令計數(shù)器 ip(返回地址)等。
-
訪問鏈(靜態(tài)鏈)。當前程序單元可以訪問的(靜態(tài)程序中)外圍程序單元的活動記錄鏈。
-
控制鏈(動態(tài)鏈)。程序單元的活動記錄按它們的生成(或調用)次序串成鏈。
-
實在參數(shù)
-
返回值
舉個栗子
- 那么這個C程序的活動記錄安排你能畫出來嗎?試試看?
- 函數(shù)
g
被調用時,活動記錄棧的(大致)內容如下: - 可以看到主函數(shù)中調用了
h
,而h調用了g
,他們的old bp
就是控制鏈,分別指向調用者,他們都有一個局部變量a
,并且此時bp
在函數(shù)g
的活動記錄中,sp
是棧頂指針(始終指向棧頂)
再舉個栗子
-
-
這是一個非常簡單的
c
函數(shù)程序,你能畫出它的活動記錄圖,并標明各個參數(shù)對應bp
的偏移嗎?如果我們要取參數(shù)則上移,要取局部變量則下移,這里是int
,占4個字節(jié),所以分別是bp+8
和bp-4
-
更深一步,讓我們一起看一看這段程序的匯編代碼是怎么樣的吧!x86匯編是典型的棧式機,首先
pushl %ebp
將當前bp壓棧,movl %esp,%ebp
將這個位置作為活動記錄的基址,即把esp送入ebp,subl $8, %esp
為局部數(shù)據(jù)分配空間,有兩個整型變量,所以esp-8
,movl 8(%ebp), %eax
取參數(shù)a的值放入寄存器%eax
,再movl %eax,-4(%ebp)
將其賦給局部變量,后兩句同理將b賦給d,最后leave和ret回收活動記錄,恢復原先保存的機器狀態(tài),leave
相當于mov bp,sp ;pop bp
//恢復調用者的bp -
再再舉個栗子
int punc(int** &a,int b, int c,int &d)
{
a[b][c] = d;return 0;
}//C/C++程序,int 變量占 4 字節(jié)。
(1)準確畫出該函數(shù)的活動記錄內容安排。
- 同理,也很簡單就能畫出來
(2)補全 5 處帶有下劃線的匯編代碼。
那么這里的匯編代碼怎么填呢?
- 可以看到參數(shù)分別為
(int** &a,int b, int c,int &d)
,如果你不清楚指針引用的話,可以先看一下這篇文章《C語言指針、引用》,那么其中a是一個引用,它是指向某一個指針的指針的引用,也就是有一個指針x,它指向另一個指針y,而a就是指針x的引用,也就是x的另一個名字。知道這個以后就可以補充匯編代碼了! movl 8(%ebp), %eax
顯然是將參數(shù)a送入%eax,那么前面說了a是一個指針的指針的引用,也就是說此時的a是一個地址,那么①就應該是movl (%eax),%ebx
,(%eax)相當于取指針a指向的地址,可以作為一個數(shù)組的首地址,然后把a這個地址先放到一個寄存器%ebx里面%12(ebp)
對應的就是參數(shù)b了,那接下來就應該計算a[b]
的值了,數(shù)組地址的計算也很簡單,a+b*4
對應匯編就是(%ebx,%eax,4)
,再把它送入%ebx
就得到了a[b]
的地址- 然后取d的值
20(%ebp)
,送入%eax
,再存到寄存器%edx
中 - 然后再計算
a[b][c]
的地址,前面已經計算了a[b]為(%ebx,%eax,4)
,所以a[b]+c*4
=(%ebx, %eax ,4)
,那么最后將d送入這個地址就可以了movl %edx,(%ebx,%eax,4)
- 最后
return 0
即對應xorl %eax, %eax
:按位異或,相同的位置為0,不同的位置為1,eax和eax的每一位都相同,所以相當于清零。 - 最終結果如下: