C編譯器剖析_6.3.4 彙編代碼產生_為函數調用與返回產生彙編代碼

來源:互聯網
上載者:User

標籤:c編譯器   ucc   彙編代碼產生   函數調用   函數返回   

6.3.4        為函數調用與返回產生彙編代碼

    在這一小節中,我們來討論一下如何為函數調用和函數返回產生彙編代碼。函數調用對應的中間指令如下所示:

         //中間指令的四元式: < opcode, DST, SRC1, SRC2>

         <CALL, 用於接收返回值的變數retVal, 函數名func,  參數列表[arg1,arg2, …,argn]>

    讓我們先熟悉一下C函數的呼叫慣例CallingConvention,我們需要把參數從右向左入棧(即從argn到arg1依次入棧),不妨記這些參數所佔用的總記憶體為stksize位元組。當函數調用返回後,主調函數要負責把這些參數出棧,這可通過形如“addl  stksize, %esp”的彙編指令來實現。{eax, ecx, edx}這3個寄存器要由主調函數負責儲存,在產生call彙編指令之前,要對這幾個寄存器進行必要的回寫操作。而{ebx,esi,edi}這3個寄存器則由被調函數負責儲存,UCC編譯器會在所有函數的入口處儲存這幾個寄存器。     為了加快返回值的傳遞,我們會盡量把返回值放在寄存器中。在x86平台上,按C標準的約定:

    (1) 若返回值為整型,則存於eax寄存器中(我們只考慮32位平台);

    (2) 若返回值為浮點型,則存於x87棧頂寄存器中;

    (3) 按C的文法要求,返回值不可以是數群組類型。如果返回值是1、2或4位元組的結構體對象,則存於寄存器eax中,若是8位元組,則存於[edx: eax]中。若返回值是其他大小的結構體對象,則C編譯器會為函數添加結構體指標作為第1個參數,如下所示。

         typedef struct  Data{

             int  num[8];      //共32位元組

         } dt;

         dt =  GetData();

         //經C編譯器處理後,真正執行的函數調用為:

         GetData(&dt);

    因此,我們可按以下步驟來翻譯形如“<CALL, retVal, func, [arg1,arg2, … ,argn]>”的中間指令,對應的C函數調用相當於“retVal =func(arg1, …, argn);”。

    (1) 參數從右至左,即從argn到arg1依次入棧;

    (2) 對{eax,ecx,edx}這幾個寄存器進行必要的回寫操作;

    (3) 當retVal為結構體對象,且大小不為{1,2,4,8}時,我們要取retVal的地址,並把該地址入棧;

    (4) 若func為函數名myadd,則產生形如“call  myadd”的彙編指令;如果func為函數指標fptr,則產生形如“call  * fptr”的彙編指令。

    (5) 根據入棧參數所佔的記憶體總和,調整寄存器esp,即產生形如“addl  stksize,esp”的彙編指令,其中stksize代表所有參數總共佔用的棧記憶體大小。

    (6) 根據返回值的類型從相應寄存器中擷取返回值。對於大小不為{1,2,4,8}的結構體對象,不需要由主調函數來取返回值。由於已把形如“dt = GetData();”的函數調用改為“GetData(&dt)”,在被調函數內部,我們可通過指標對結構體對象dt進行賦值。

    例如,對於C程式中的函數調用myadd而言,UCC編譯器產生的彙編代碼如下所示:

         int  myadd(int  a,int  b);

         result = myadd(num1,num2);

         ///////////////對應彙編代碼////////////

pushl  num2            //參數num2入棧

pushl  num1            //參數num1入棧

call  myadd             //函數調用

addl  $8, %esp        //所有參數出棧

movl  %eax,  result     //取返回值

    在此基礎上,我們來看一下用於產生這些彙編代碼的函數EmitCall,6.3.9所示。

第7至12行把參數從右至左依次入棧,第13至15行調用SpillReg函數對寄存器eax、ecx和edx進行必要的回寫。當返回值是大小不為{1,2,4,8}的結構體對象時,我們會在第19行取“返回值接收對象retVal”的地址,然後在第20行將該地址入棧。第23行用於產生函數調用指令,形如“call  myadd”或者“call  * fptr”,第25至28行會把所有參數出棧。


圖6.3.9  EmitCall()

    當返回值為浮點數時,如果主調函數不需要該返回值,我們要在第31行把x87棧頂寄

存器彈出,以避免x87副處理器的寄存器棧過滿。如果主調函數需要浮點數返回值,則通過第35至39行從x87棧頂寄存器中取出返回值,並彈出x87棧頂寄存器。而第40至55行則用於從寄存器eax或edx中擷取“整數返回值”或者“大小為{1,2,4,8}的結構體返回值”。

    接下來,我們來分析一6.3.9第9行調用的函數PushArgument,其代碼6.3.10第1至24行所示。第3至5行壓入float類型的參數,而第6至8行壓入double類型的參數,第9至20行用於把結構體對象複製到棧中,第17行的opds[1]記錄要複製的位元組數ty->size,第18行的opds[2]是在棧中為結構體對象預留的記憶體大小,opds[2]要大於或等於opds[1]的大小。第21至22行把4位元組的整數入棧。


圖6.3.10  PushArgument()

    在圖6.3.10第26行,我們還給出了形如“*ptr = number;”的中間指令,UCC稱這樣的指令為IndirectMove,我們會在第35行把ptr載入到寄存器中,不妨設其為eax,然後在36至37行把形如“<IMOV,ptr,number,NULL>”的中間指令改為“<MOV,(%eax),number,NULL>”,再通過第19行的EmitMove函數,我們就可以為“*ptr = number;”產生以下彙編代碼,我們已在前面的章節中分析過EmitMove函數,這裡不再重複。

         movl  num, %ecx

         movl  %ecx, (%eax)

    與此類似的,圖6.3.10第48至58行的EmitDeref函數用來處理形如“t2: *ptr”的中間指令,對應的四元式為<DEREF,t2, ptr, NULL>,我們先在第51行把ptr載入到寄存器中,不妨設其為eax,第52至53行會把中間指令“<DEREF, t2, ptr, NULL>”改為“<MOV,t2,(%eax),NULL>”,之後通過第55行的EmitMove函數產生以下彙編代碼:

         movl (%eax), %ecx   ;     //臨時變數t2對應的寄存器為ecx

         當遇到C程式裡的return語句時,UCC編譯器會產生以下中間代碼:

         return   retVal;

         /////////對應中間代碼////////////

         <RET, retVal, NULL,NULL>       //中間指令RET只是準備好返回值

         <JMP, exitBB,NULL,NULL>       //跳往函數的唯一出口exitBB

    在函數的唯一出口exitBB中,UCC編譯器會通過EmitEpilogue函數產生以下彙編代碼,用於從被調函數返回到主調函數。

exitBB:

         movl  %ebp, %esp

         popl  %edi

         popl  %esi

         popl  %ebx

         popl  %ebp

         ret

   因此,中間指令“<RET, retVal, NULL,NULL>”所要完成的工作只是傳遞返回值,相關代碼6.3.11所示。當返回值為浮點數且返回值不在x87棧頂寄存器時,我們在第9行調用PutASMCode函數把返回值載入到x87棧頂寄存器中。如果返回值是大小不為{1,2,4,8}的結構體對象,我們通過第31至33行,把形如“<RET,retVal,NULL,NULL>”的中間指令改為“<IMOV,&dt,retVal, NULL>”,第16至28行的注釋對此進行了說明,之後我們可在第34行調用EmitIndirectMove函數進行結構體對象的複製。

圖6.3.11  EmitReturn()

   圖6.3.11第37至55行用於把“整數返回值或者大小為{1,2,4,8}的結構體對象”傳送到“寄存器eax或者edx”中。

C編譯器剖析_6.3.4 彙編代碼產生_為函數調用與返回產生彙編代碼

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.