日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

c语言尚未实现的虚拟函数,编译原理之学习 lua 1.1 笔记 (二) 函数调用与局部变量...

發(fā)布時(shí)間:2024/10/8 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言尚未实现的虚拟函数,编译原理之学习 lua 1.1 笔记 (二) 函数调用与局部变量... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

函數(shù)(過程)是程序中重要的抽象, 過程調(diào)用一般用棧實(shí)現(xiàn). Lua 1.1 中尚未實(shí)現(xiàn)閉包(closure),

對(duì)于函數(shù)使用棧實(shí)現(xiàn)即已滿足需求了.在理論上, 在棧中要保存為實(shí)現(xiàn)調(diào)用以及返回調(diào)用處的足夠

信息, 這些信息當(dāng)前是返回地址(return-address,棧基址指針(base-pointer).

在虛擬機(jī)指令層次, 指令 CALLFUNC, RETCODE 用于函數(shù)調(diào)用的核心實(shí)現(xiàn). 另有一些與調(diào)用參數(shù), 局部

變量相關(guān)的指令, 稍后遇到的時(shí)候研究.

對(duì)于一個(gè)函數(shù), 如例子 function f(a,b) ... end, 對(duì)其的調(diào)用代碼為:

PUSH f? -- 將函數(shù)對(duì)象 f 壓入堆棧.

PUSH MARK? --- 將特殊標(biāo)記值 MARK 壓入堆棧, 作用下面描述.

PUSH a --- 計(jì)算得到 a 的代碼, 最終為將 a 的值壓入堆棧.

PUSH b --- 同 a 的情況, 此時(shí)棧中數(shù)據(jù)為: [f, MARK, a, b??)

CALLFUNC --- 實(shí)際產(chǎn)生調(diào)用.

指令 CALLFUNC 的執(zhí)行如下:

case CALLFUNC:? --- 位于 opcode.c 的虛擬機(jī)執(zhí)行函數(shù) lua_execute() 中.

1. 從棧頂向上找 MARK 標(biāo)記, 這一標(biāo)記的前面是函數(shù) f, 后面是第一個(gè)函數(shù)參數(shù) a.

2. 從函數(shù)對(duì)象 f 中獲得該函數(shù)的代碼地址 new-pc;

把當(dāng)前 pc?(也即返回地址)保存在函數(shù)對(duì)象 f 中. (注1)

設(shè)置 pc = new-pc,?此即實(shí)現(xiàn)執(zhí)行地址的改變.

3. 將棧 base 指針保存在 MARK 的值中, 設(shè)置新的 base = MARK+1, 即指向棧中第一個(gè)

參數(shù) a 的位置.

此后再執(zhí)行的下一條指令就是函數(shù)的入口指令了.

上述的步驟, 所做工作就是保存 , 設(shè)置新的運(yùn)行地址和基址指針. 與機(jī)器碼實(shí)現(xiàn)

函數(shù)調(diào)用幾乎是一致的. 略有不同的是由于調(diào)用者提供的參數(shù)數(shù)量可能是0個(gè)或多個(gè)(可變數(shù)量的), 所以

通過查找 MARK 方式找到, 同時(shí) MARK 還用作保存 base 指針, 這種方式較為巧妙.

注1: 將返回地址保存到函數(shù)對(duì)象中, 這是一個(gè)"不好的"方式, 這樣函數(shù)就不能重入,遞歸了?

指令 RETCODE 的執(zhí)行與 CALLFUNC 想對(duì)應(yīng):

case RETCODE:? --- 虛擬機(jī)函數(shù) lua_execute() 中.

1. 由于當(dāng)前 base 指針-1, -2 分別是保存了 base 的MARK, 和保存了返回地址的函數(shù) f 對(duì)象,

故此從中恢復(fù) pc, base 的值.

2. 復(fù)制/移動(dòng)函數(shù)返回值 (函數(shù)的多返回值機(jī)制以后分析, 此處暫略)

此后下一條指令即返回到原調(diào)用處的下一條指令位置.

對(duì)函數(shù)產(chǎn)生調(diào)用的代碼, 在如下產(chǎn)生式中生成(代碼生成):

1. stat1 -> functioncall

2. expr -> functioncall

3. functioncall -> functionvalue {代碼塊1} '(' exprlist ')' {代碼塊2}

對(duì)上面的產(chǎn)生式3略作記錄:

1. functionvalue 產(chǎn)生計(jì)算函數(shù) f 的值的代碼, 例如壓入全局變量 f 到堆棧中. PUSHGLOBAL?f

2. 代碼塊1: 產(chǎn)生代碼 PUSHMARK, 更新 ntemp 值(其用于跟蹤堆棧使用量)

3. exprlist 產(chǎn)生所有參數(shù)的計(jì)算和壓棧代碼, 如 PUSH a, PUSH b

4. 代碼塊2: 產(chǎn)生代碼 CALLFUNC, 更新 ntemp 值.

這里生成了調(diào)用函數(shù) f 的前述代碼序列.

在前面提到過基址指針 base, 相當(dāng)于 80x86 體系中的寄存器 BP, 用于尋址位于堆棧上的地址.

在 Lua 中, 函數(shù)的參數(shù)和局部變量存放在棧中, 并使用 base 指針尋址. 下面研究對(duì)局部變量訪問的

指令, 及其代碼生成.

在 Lua 中, 讀取局部變量的指令為 PUSHLOCAL(及其同系列的 PUSHLOCAL0~~9),

寫入局部變量的指令為 STORELOCAL(及其同系列的 STORELOCAL0~~9). 那些同系列的指令僅僅

是減少(優(yōu)化)了指令大小, 語(yǔ)義是一致的, 因此只需要研究 PUSHLOCAL, STORELOCAL 即可.

case PUSHLOCAL:? --- 帶一個(gè)字節(jié)的指令立即數(shù) i, 局部變量壓入棧中.

*top++ = stack[base + i];

case STORELOCAL: --- 帶一個(gè)字節(jié)的指令立即數(shù) i, 棧頂值彈出存入局部變量.

stack[base + i] = *(--top);

PUSHLOCAL, STORELOCAL 都帶有一個(gè)字節(jié)的指令參數(shù), 表示所訪問的局部變量的索引 i, 從 0 開始,

尋址到堆棧 stack[base + i] 位置. 由于只有一個(gè)字節(jié), 也即限制最多只能有 256 個(gè)局部變量.

由于調(diào)用函數(shù)時(shí) base 被自動(dòng)維護(hù), 因此每函數(shù)都有自己的局部變量. 又 base 指針從第一個(gè)參數(shù)開始,

因此參數(shù)實(shí)現(xiàn)上也是被當(dāng)做局部變量看待的. 這隱含的幾個(gè)問題, 第一個(gè)是如果函數(shù)所需參數(shù)數(shù)量,

和實(shí)際調(diào)用者傳遞的參數(shù)數(shù)量不一致的問題.

舉例說明, 設(shè)函數(shù)聲明為 function f(a,b), 而調(diào)用者調(diào)用為 f(1,2,3), 或 f(4), 即函數(shù)參數(shù)多或少的情況.

在函數(shù)的入口代碼中, 會(huì)產(chǎn)生一條 ADJUST n?指令, 其中 n 是函數(shù)聲明時(shí)的參數(shù)數(shù)量, 該指令執(zhí)行如下:

case ADJUST:

在語(yǔ)義上:

1. 如果調(diào)用者提供的參數(shù)不足 n 個(gè), 則不足的部分以 NIL 值填充.

2. 如果調(diào)用者提供的參數(shù)多于 n 個(gè), 則多出的被裁剪掉.

最終設(shè)置棧頂指針 top = base + n (即多出的參數(shù)被裁剪, 如果有的話)

這一指令在產(chǎn)生式 function -> FUNCTION NAME '(' parlist ')' block END 中生成.

隱含的第二個(gè)問題是, 由于多出的參數(shù)被裁剪掉了, 這樣表示無法提供 f(args, ...) 后面可變參數(shù)語(yǔ)義的實(shí)現(xiàn).

預(yù)計(jì) lua 以后的版本會(huì)使用某種方法實(shí)現(xiàn).

局部變量的聲明, 例如 local x, y, 相關(guān)產(chǎn)生式為:

stat1 -> LOCAL localdeclist decinit

localdeclist -> NAME {代碼塊1} | localdeclist ',' NAME

其中代碼塊1:

localvar[nlocalvar]=lua_findsymbol($1);?? --- 查找 $1(即NAME), 并加入到局部變量表中.

$$ = 1;? --- 已聲明的局部變量數(shù).

在函數(shù)中聲明的局部變量被放置在 localvar[] 表中, 值是 lua_findsymbol() 的返回值, 即到符號(hào)表

symtab 的索引.

在產(chǎn)生式 var -> NAME 中, 第一篇研究全局變量的文章中也有遇見, 其中代碼塊為:

Word s = lua_findsymbol(NAME) --- 查找 NAME 在符號(hào)表中的索引.

int local = lua_localname(s)? ---? 在 localvar[] 表中查找是否有 s, 如果有則表示這是個(gè)局部

--- 變量.

if (是局部變量)?$$ =?-(local+1) --- 是一個(gè)負(fù)數(shù), 從而與全局變量的 正的索引 區(qū)分開.

在 lua_pushvar() 為訪問 var 生成代碼時(shí), 前一篇文章研究全局變量時(shí)也碰到,

為全局變量生成代碼為: PUSHGLOBAL idx-of-symtab

為局部變量生成代碼為: PUSHLOCAL idx-of-localvar, 也即表示在 stack[base+idx]

在產(chǎn)生式 varlist1 -> var ... 中, 記錄 var.$$ 到 varbuffer[] 表中, 在為其生成寫入指令時(shí),

根據(jù)是正數(shù)生成 全局變量的(STOREGLOBAL), 是負(fù)數(shù)生成局部變量訪問指令 (STORELOCAL).

生成指令的時(shí)候, 如果 idx 在 0~9, 則產(chǎn)生較短的指令 PUSHLOCAL0~9, STORELOCAL0~9.

函數(shù)的返回值也是放在棧頂?shù)? 是在 RETCODE 指令中設(shè)置好返回給調(diào)用者的, 由于 lua 支持多賦值,多返回值, 將它們單獨(dú)放一個(gè)地方再研究也許更合適一些.

總結(jié)

以上是生活随笔為你收集整理的c语言尚未实现的虚拟函数,编译原理之学习 lua 1.1 笔记 (二) 函数调用与局部变量...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。