Replace GOTO by FREE/FE_FREE and JMP at compile time

This commit is contained in:
Dmitry Stogov 2015-07-07 04:32:04 +03:00
parent 7052e56979
commit 91b620d684
12 changed files with 109 additions and 99 deletions

View File

@ -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);
}
/* }}} */

View File

@ -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);

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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;