mirror of
https://github.com/php/php-src.git
synced 2024-09-22 02:17:32 +00:00
Replace GOTO by FREE/FE_FREE and JMP at compile time
This commit is contained in:
parent
7052e56979
commit
91b620d684
@ -879,61 +879,6 @@ static void str_dtor(zval *zv) /* {{{ */ {
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */
|
||||
{
|
||||
zend_label *dest;
|
||||
int current, distance;
|
||||
zval *label;
|
||||
|
||||
if (pass2) {
|
||||
label = RT_CONSTANT(op_array, opline->op2);
|
||||
} else {
|
||||
label = CT_CONSTANT_EX(op_array, opline->op2.constant);
|
||||
}
|
||||
if (CG(context).labels == NULL ||
|
||||
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
|
||||
|
||||
if (pass2) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = opline->lineno;
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
|
||||
} else {
|
||||
/* Label is not defined. Delay to pass 2. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
opline->op1.opline_num = dest->opline_num;
|
||||
zval_dtor(label);
|
||||
ZVAL_NULL(label);
|
||||
|
||||
/* Check that we are not moving into loop or switch */
|
||||
current = opline->extended_value;
|
||||
for (distance = 0; current != dest->brk_cont; distance++) {
|
||||
if (current == -1) {
|
||||
if (pass2) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = opline->lineno;
|
||||
}
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
|
||||
if (distance == 0) {
|
||||
/* Nothing to break out of, optimize to ZEND_JMP */
|
||||
opline->opcode = ZEND_JMP;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
} else {
|
||||
/* Set real break distance */
|
||||
ZVAL_LONG(label, distance);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_bool zend_is_call(zend_ast *ast);
|
||||
|
||||
static int generate_free_loop_var(znode *var) /* {{{ */
|
||||
@ -3633,16 +3578,114 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline) /* {{{ */
|
||||
{
|
||||
zend_label *dest;
|
||||
int current, distance, free_vars;
|
||||
zval *label;
|
||||
znode *loop_var = NULL;
|
||||
|
||||
if (pass2_opline) {
|
||||
label = RT_CONSTANT(op_array, pass2_opline->op2);
|
||||
} else {
|
||||
label = &label_node->u.constant;
|
||||
}
|
||||
if (CG(context).labels == NULL ||
|
||||
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) {
|
||||
|
||||
if (pass2_opline) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = pass2_opline->lineno;
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label));
|
||||
} else {
|
||||
/* Label is not defined. Delay to pass 2. */
|
||||
zend_op *opline;
|
||||
|
||||
current = CG(context).current_brk_cont;
|
||||
while (current != -1) {
|
||||
if (CG(context).brk_cont_array[current].start >= 0) {
|
||||
zend_emit_op(NULL, ZEND_NOP, NULL, label_node);
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, label_node);
|
||||
opline->extended_value = CG(context).current_brk_cont;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
zval_dtor(label);
|
||||
ZVAL_NULL(label);
|
||||
|
||||
/* Check that we are not moving into loop or switch */
|
||||
if (pass2_opline) {
|
||||
current = pass2_opline->extended_value;
|
||||
} else {
|
||||
current = CG(context).current_brk_cont;
|
||||
}
|
||||
if (!pass2_opline) {
|
||||
loop_var = zend_stack_top(&CG(loop_var_stack));
|
||||
}
|
||||
for (distance = 0, free_vars = 0; current != dest->brk_cont; distance++) {
|
||||
if (current == -1) {
|
||||
if (pass2_opline) {
|
||||
CG(in_compilation) = 1;
|
||||
CG(active_op_array) = op_array;
|
||||
CG(zend_lineno) = pass2_opline->lineno;
|
||||
}
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed");
|
||||
}
|
||||
if (CG(context).brk_cont_array[current].start >= 0) {
|
||||
if (pass2_opline) {
|
||||
free_vars++;
|
||||
} else {
|
||||
generate_free_loop_var(loop_var);
|
||||
loop_var--;
|
||||
}
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
|
||||
if (pass2_opline) {
|
||||
if (free_vars) {
|
||||
current = pass2_opline->extended_value;
|
||||
while (current != dest->brk_cont) {
|
||||
if (CG(context).brk_cont_array[current].start >= 0) {
|
||||
zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk];
|
||||
|
||||
if (brk_opline->opcode == ZEND_FREE) {
|
||||
(pass2_opline - free_vars)->opcode = ZEND_FREE;
|
||||
(pass2_opline - free_vars)->op1_type = brk_opline->op1_type;
|
||||
(pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
|
||||
} else if (brk_opline->opcode == ZEND_FE_FREE) {
|
||||
(pass2_opline - free_vars)->opcode = ZEND_FE_FREE;
|
||||
(pass2_opline - free_vars)->op1_type = brk_opline->op1_type;
|
||||
(pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
|
||||
}
|
||||
free_vars--;
|
||||
}
|
||||
current = CG(context).brk_cont_array[current].parent;
|
||||
}
|
||||
}
|
||||
pass2_opline->opcode = ZEND_JMP;
|
||||
pass2_opline->op1.opline_num = dest->opline_num;
|
||||
SET_UNUSED(pass2_opline->op2);
|
||||
pass2_opline->extended_value = 0;
|
||||
} else {
|
||||
zend_op *opline = zend_emit_op(NULL, ZEND_JMP, NULL, NULL);
|
||||
opline->op1.opline_num = dest->opline_num;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_goto(zend_ast *ast) /* {{{ */
|
||||
{
|
||||
zend_ast *label_ast = ast->child[0];
|
||||
znode label_node;
|
||||
zend_op *opline;
|
||||
|
||||
zend_compile_expr(&label_node, label_ast);
|
||||
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node);
|
||||
opline->extended_value = CG(context).current_brk_cont;
|
||||
zend_resolve_goto_label(CG(active_op_array), opline, 0);
|
||||
zend_resolve_goto_label(CG(active_op_array), &label_node, NULL);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -717,7 +717,7 @@ void zend_do_extended_fcall_end(void);
|
||||
|
||||
void zend_verify_namespace(void);
|
||||
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2);
|
||||
void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline);
|
||||
|
||||
ZEND_API void function_add_ref(zend_function *function);
|
||||
|
||||
|
@ -699,11 +699,8 @@ static void zend_resolve_finally_calls(zend_op_array *op_array)
|
||||
break;
|
||||
case ZEND_GOTO:
|
||||
if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) != IS_LONG) {
|
||||
uint32_t num = opline->op2.constant;
|
||||
|
||||
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
|
||||
zend_resolve_goto_label(op_array, opline, 1);
|
||||
opline->op2.constant = num;
|
||||
zend_resolve_goto_label(op_array, NULL, opline);
|
||||
}
|
||||
/* break omitted intentionally */
|
||||
case ZEND_JMP:
|
||||
@ -789,7 +786,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
|
||||
break;
|
||||
case ZEND_GOTO:
|
||||
if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op2)) != IS_LONG) {
|
||||
zend_resolve_goto_label(op_array, opline, 1);
|
||||
zend_resolve_goto_label(op_array, NULL, opline);
|
||||
}
|
||||
/* break omitted intentionally */
|
||||
case ZEND_JMP:
|
||||
|
@ -4863,13 +4863,8 @@ ZEND_VM_HANDLER(52, ZEND_BOOL, CONST|TMPVAR|CV, ANY)
|
||||
|
||||
ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_op *target = OP_JMP_ADDR(opline, opline->op1);
|
||||
|
||||
SAVE_OPLINE();
|
||||
|
||||
i_cleanup_unfinished_execution(execute_data, opline - EX(func)->op_array.opcodes, target - EX(func)->op_array.opcodes);
|
||||
ZEND_VM_JMP(target);
|
||||
zend_error_noreturn(E_ERROR, "GOTO must be resolved at compile-time.");
|
||||
ZEND_VM_NEXT_OPCODE(); /* Never reached */
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
|
||||
|
@ -2227,13 +2227,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(Z
|
||||
|
||||
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_op *target = OP_JMP_ADDR(opline, opline->op1);
|
||||
|
||||
SAVE_OPLINE();
|
||||
|
||||
i_cleanup_unfinished_execution(execute_data, opline - EX(func)->op_array.opcodes, target - EX(func)->op_array.opcodes);
|
||||
ZEND_VM_JMP(target);
|
||||
zend_error_noreturn(E_ERROR, "GOTO must be resolved at compile-time.");
|
||||
ZEND_VM_NEXT_OPCODE(); /* Never reached */
|
||||
}
|
||||
|
||||
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_INTERFACE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
|
@ -123,10 +123,6 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
|
||||
blocks[0].start_opline_no = 0;
|
||||
while (opline < end) {
|
||||
switch((unsigned)opline->opcode) {
|
||||
case ZEND_GOTO:
|
||||
/* would not optimize GOTOs - we cannot really know where it jumps,
|
||||
* so these optimizations are too dangerous */
|
||||
return 0;
|
||||
case ZEND_FAST_CALL:
|
||||
START_BLOCK_OP(ZEND_OP1(opline).opline_num);
|
||||
if (opline->extended_value) {
|
||||
|
@ -44,14 +44,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
|
||||
end = op_array->opcodes + op_array->last;
|
||||
for (opline = op_array->opcodes; opline < end; opline++) {
|
||||
|
||||
/* GOTO target is unresolved yet. We can't optimize. */
|
||||
if (opline->opcode == ZEND_GOTO &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
|
||||
/* TODO: in general we can avoid this restriction */
|
||||
FREE_ALLOCA(shiftlist);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Kill JMP-over-NOP-s */
|
||||
if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
|
||||
/* check if there are only NOPs under the branch */
|
||||
@ -85,7 +77,6 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
|
||||
for (opline = op_array->opcodes; opline<end; opline++) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
|
@ -624,7 +624,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
case ZEND_EXIT:
|
||||
case ZEND_THROW:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_FAST_RET:
|
||||
case ZEND_JMP:
|
||||
|
@ -488,7 +488,6 @@ static void zend_accel_optimize(zend_op_array *op_array,
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@ -533,7 +532,6 @@ static void zend_accel_optimize(zend_op_array *op_array,
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
|
@ -386,7 +386,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
|
||||
# if ZEND_USE_ABS_JMP_ADDR
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
@ -913,7 +912,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
|
||||
# if ZEND_USE_ABS_JMP_ADDR
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
|
@ -501,7 +501,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
|
||||
/* fix jumps to point to new array */
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_ANON_INHERITED_CLASS:
|
||||
|
@ -57,7 +57,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
|
||||
/* OP1 */
|
||||
switch (op->opcode) {
|
||||
case ZEND_JMP:
|
||||
case ZEND_GOTO:
|
||||
case ZEND_FAST_CALL:
|
||||
asprintf(&decode[1], "J%ld", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user