摘要:結(jié)果和我們?cè)O(shè)想的一致。另外一個(gè)非常重要的工作是通過(guò),設(shè)置對(duì)應(yīng)的,代碼如下其中和之前的對(duì)應(yīng)關(guān)系在中定義的。至此,整個(gè)抽象語(yǔ)法樹就編譯完成了,最終的結(jié)果為指令集,接下來(lái)就是在虛擬機(jī)上執(zhí)行這些指令。參考資料源碼分析源碼研究之淺談虛擬機(jī)
grape
全部視頻:https://segmentfault.com/a/11...
原視頻地址:http://replay.xesv5.com/ll/24...
流程回顧上節(jié)課我們把$a=1這個(gè)過(guò)程編譯梳理了一遍,我們了解到op1,op2,result,opcode的生成過(guò)程,下面我們把整個(gè)過(guò)程來(lái)回顧一下。
static zend_op_array *zend_compile(int type) { zend_op_array *op_array = NULL; zend_bool original_in_compilation = CG(in_compilation); CG(in_compilation) = 1; CG(ast) = NULL; CG(ast_arena) = zend_arena_create(1024 * 32); //首先會(huì)分配內(nèi)存 if (!zendparse()) { //zendparse(就是yyparse)(zend_language_parse.y) ==> 通過(guò)parser調(diào)用lexer,生成抽象語(yǔ)法樹ast_list,存到CG(ast);yyparse是通過(guò)bison編譯zend_language_parser.y生成 int last_lineno = CG(zend_lineno); zend_file_context original_file_context; zend_oparray_context original_oparray_context; zend_op_array *original_active_op_array = CG(active_op_array); op_array = emalloc(sizeof(zend_op_array)); init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); //初始化oparray CG(active_op_array) = op_array; if (zend_ast_process) { zend_ast_process(CG(ast)); } zend_file_context_begin(&original_file_context); zend_oparray_context_begin(&original_oparray_context); zend_compile_top_stmt(CG(ast)); //編譯ast生成oparray CG(zend_lineno) = last_lineno; zend_emit_final_return(type == ZEND_USER_FUNCTION); //PHP中會(huì)加return 1,在此進(jìn)行處理 op_array->line_start = 1; op_array->line_end = last_lineno; pass_two(op_array); //對(duì)于handler的處理 zend_oparray_context_end(&original_oparray_context); zend_file_context_end(&original_file_context); CG(active_op_array) = original_active_op_array; } zend_ast_destroy(CG(ast)); zend_arena_destroy(CG(ast_arena)); CG(in_compilation) = original_in_compilation; return op_array; }
大體流程為:詞法分析->語(yǔ)法分析->編譯ast生成op_array->處理return 1->對(duì)于handler做處理
以上處理return 1 環(huán)節(jié)之前的文章中我們都已經(jīng)提到過(guò),如果有不太理解的請(qǐng)翻閱之前的文章。接下來(lái)我們gdb程序到環(huán)節(jié)return 1。代碼:
我們來(lái)看一看到編譯ast生成op_array處的結(jié)果:
我們來(lái)看這個(gè)結(jié)果,vars是存我們的變量的,在這存的是a和b,并且last_Var=2只有兩個(gè);T是temporary,T=2說(shuō)明有兩個(gè)臨時(shí)變量。然后literals是存我們的字面量,再這里存的是2,3,last_literal=2表示現(xiàn)在有兩個(gè)字面量,接下來(lái)我們打印一下看是否和我們所解釋的一致。結(jié)果和我們?cè)O(shè)想的一致。另外,對(duì)于opcode的值又是如何呢?
return 1的做了什么?
我們發(fā)現(xiàn),$a=2 op1是80,$b=3 op1為96,這是為什么呢?這之前我們說(shuō)過(guò)這個(gè)問題,因?yàn)樵跅V形覀兪欠峙湟粋€(gè)大小為16的內(nèi)存,所以需要增加16.第二個(gè),我們知道result.constant的0和1代表字面量偏移量分別為0和1.
到這里都是之前學(xué)習(xí)過(guò)的內(nèi)容,接下來(lái)繼續(xù)學(xué)習(xí)。繼續(xù)執(zhí)行代碼:
我們發(fā)現(xiàn)在執(zhí)行完zend_emit_final_return這句之后我們的op_array發(fā)生了變化。那么為什么會(huì)發(fā)生這樣的變化呢?我們?cè)谖恼麻_頭有些到這個(gè)函數(shù)的作用是增加return 1結(jié)尾,那么具體其中是怎么來(lái)操作呢?我們來(lái)看代碼:
void zend_emit_final_return(int return_one) /* {{{ */ { znode zn; zend_op *ret; zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR)) { zend_emit_return_type_check(NULL, CG(active_op_array)->arg_info - 1, 1); } zn.op_type = IS_CONST; if (return_one) { ZVAL_LONG(&zn.u.constant, 1); //在gdb過(guò)程中會(huì)走到這一步,把1賦值給zn.u.constant } else { ZVAL_NULL(&zn.u.constant); } ret = zend_emit_op(NULL, returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN, &zn, NULL);//在此會(huì)像字面量中添加一個(gè)新的元素1 ret->extended_value = -1; } static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array)); opline->opcode = opcode; if (op1 == NULL) { SET_UNUSED(opline->op1); } else { SET_NODE(opline->op1, op1); } if (op2 == NULL) { SET_UNUSED(opline->op2); } else { SET_NODE(opline->op2, op2); } zend_check_live_ranges(opline); if (result) { zend_make_var_result(result, opline); } return opline; } #define SET_NODE(target, src) do { target ## _type = (src)->op_type; if ((src)->op_type == IS_CONST) { target.constant = zend_add_literal(CG(active_op_array), &(src)->u.constant); //增加元素 } else { target = (src)->u.op; } } while (0)我們發(fā)現(xiàn),gdb過(guò)程在這個(gè)函數(shù)中像literals里邊又新增1個(gè)元素,我們打印opcodes:
pass_two設(shè)置handler
我們發(fā)現(xiàn),新增了一條指令,在代碼中就是return 1。
好的,到此,我們發(fā)現(xiàn),有三條指令,兩個(gè)變量,三個(gè)字面量。$a和$b的位置已經(jīng)有了,字面量也有了,我們發(fā)現(xiàn)handler還是個(gè)空指針,接下來(lái)我們看handler的生成。我們接著走,會(huì)走到pass_two這個(gè)函數(shù),這個(gè)函數(shù)中,對(duì)opline指令集做了進(jìn)一步的加工,最主要的工作是設(shè)置指令的handler,源碼如下:
ZEND_API int pass_two(zend_op_array *op_array) { /**代碼省略**/ while (opline < end) {//遍歷opline數(shù)組 if (opline->op1_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1); } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { opline->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->op1.var); } if (opline->op2_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2); } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) { opline->op2.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->op2.var); } if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->result.var); } ZEND_VM_SET_OPCODE_HANDLER(opline); /**代碼省略**/ }觀察代碼,該函數(shù)會(huì)對(duì)opline指令數(shù)組進(jìn)行遍歷,他會(huì)處理之前生成的每一條opline,我們拿IS_CONST來(lái)舉例,如果op1,op2的type為IS_CONST,那么將會(huì)調(diào)用ZEND_PASS_TWO_UPDATE_CONSTANT,代碼如下:
/* convert constant from compile-time to run-time */ # define ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, node) do { (node).zv = CT_CONSTANT_EX(op_array, (node).constant); } while (0) # define CT_CONSTANT_EX(op_array, num) ((op_array)->literals + (num))我們知道,對(duì)于IS_CONST的變量的字面量是存在與literals里邊的,而constant是相對(duì)的下標(biāo),因此我們可以通過(guò)對(duì)于首地址偏移constant來(lái)進(jìn)行轉(zhuǎn)換為真實(shí)的偏移量。對(duì)于IS_VAR|IS_TMP_VAR類型的變量,會(huì)通過(guò)ZEND_CALL_VAR_NUM計(jì)算偏移量。
另外一個(gè)非常重要的工作是通過(guò)ZEND_VM_SET_OPCODE_HANDLER(opline),設(shè)置opline對(duì)應(yīng)的hanlder,代碼如下:
ZEND_API void zend_vm_set_opcode_handler(zend_op* op) { op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op); } static const void *zend_vm_get_opcode_handler(zend_uchar opcode, const zend_op* op) { return zend_vm_get_opcode_handler_ex(zend_spec_handlers[opcode], op); }其中opcode和handler之前的對(duì)應(yīng)關(guān)系在Zend/zend_vm_execute.h中定義的。opline數(shù)組經(jīng)過(guò)一次遍歷后,handler也就設(shè)置完畢,設(shè)置后的opline數(shù)組如圖所示:
結(jié)尾最后我們打印下生成handler后的op_array:
我們發(fā)現(xiàn),handler已經(jīng)被賦值。
至此,整個(gè)抽象語(yǔ)法樹就編譯完成了,最終的結(jié)果為opline指令集,接下來(lái)就是在Zend虛擬機(jī)上執(zhí)行這些指令。參考資料:
【PHP7源碼分析】PHP7源碼研究之淺談Zend虛擬機(jī)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31534.html
摘要:此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過(guò)程中的思考整理與心得體會(huì),此文會(huì)不斷更新視頻傳送門每日學(xué)習(xí)記錄使用錄像設(shè)備記錄每天的學(xué)習(xí)源碼學(xué)習(xí)源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)內(nèi)存管理筆記源碼學(xué)習(xí)基本變量筆記 此文用于匯總跟隨陳雷老師及團(tuán)隊(duì)的視頻,學(xué)習(xí)源碼過(guò)程中的思考、整理與心得體會(huì),此文會(huì)不斷更新 視頻傳送門:【每日學(xué)習(xí)記錄】使用錄像設(shè)備記錄每天的學(xué)習(xí) PHP7...
摘要:在這里使用學(xué)而思網(wǎng)校的錄像設(shè)備,記錄每天學(xué)習(xí)的內(nèi)容閆昌李樂階段李樂李樂李樂李樂李樂李樂馬運(yùn)運(yùn)李樂李樂李樂源碼集群閆昌源碼閆昌源碼主從復(fù)制李樂源碼施洪寶源碼施洪寶韓天 在這里使用學(xué)而思網(wǎng)校的錄像設(shè)備,記錄每天學(xué)習(xí)的內(nèi)容: 2019-06-24 ~ 2019-06-28 06-27 nginx by 閆昌 06-26 nginx module by 李樂 06-25 nginx http ...
閱讀 2429·2021-11-11 11:01
閱讀 3303·2021-10-11 10:57
閱讀 2664·2021-09-30 09:46
閱讀 3504·2021-07-26 23:38
閱讀 1580·2019-08-29 12:22
閱讀 663·2019-08-29 11:28
閱讀 2366·2019-08-26 14:04
閱讀 3064·2019-08-23 18:34