Implement jumptable optimization

This commit is contained in:
Nikita Popov 2017-03-17 23:45:05 +01:00
parent d1a012b602
commit ad8652818a
17 changed files with 719 additions and 169 deletions

View File

@ -0,0 +1,29 @@
--TEST--
Switch on numeric strings
--FILE--
<?php
function test($value) {
switch ($value) {
case "01": return "01";
case "1": return "1";
case " 2": return " 2";
case "2": return "2";
case "10.0": return "10.0";
case "1e1": return "1e1";
default: return "default";
}
}
var_dump(test("1"));
var_dump(test("2"));
var_dump(test("1e1"));
?>
--EXPECT--
string(2) "01"
string(2) " 2"
string(4) "10.0"

View File

@ -2081,6 +2081,8 @@ static void zend_check_live_ranges(zend_op *opline) /* {{{ */
} else if (opline->opcode == ZEND_FAST_RET) {
/* fast_calls don't have to be destroyed */
} else if (opline->opcode == ZEND_CASE ||
opline->opcode == ZEND_SWITCH_LONG ||
opline->opcode == ZEND_SWITCH_STRING ||
opline->opcode == ZEND_FE_FETCH_R ||
opline->opcode == ZEND_FE_FETCH_RW ||
opline->opcode == ZEND_FE_FREE ||
@ -4603,6 +4605,58 @@ void zend_compile_if(zend_ast *ast) /* {{{ */
}
/* }}} */
static zend_uchar determine_switch_jumptable_type(zend_ast_list *cases) {
uint32_t i;
zend_uchar common_type = IS_UNDEF;
for (i = 0; i < cases->children; i++) {
zend_ast *case_ast = cases->child[i];
zend_ast **cond_ast = &case_ast->child[0];
zval *cond_zv;
if (!case_ast->child[0]) {
/* Skip default clause */
continue;
}
zend_eval_const_expr(cond_ast);
if ((*cond_ast)->kind != ZEND_AST_ZVAL) {
/* Non-constant case */
return IS_UNDEF;
}
cond_zv = zend_ast_get_zval(case_ast->child[0]);
if (Z_TYPE_P(cond_zv) != IS_LONG && Z_TYPE_P(cond_zv) != IS_STRING) {
/* We only optimize switched on integers and strings */
return IS_UNDEF;
}
if (common_type == IS_UNDEF) {
common_type = Z_TYPE_P(cond_zv);
} else if (common_type != Z_TYPE_P(cond_zv)) {
/* Non-uniform case types */
return IS_UNDEF;
}
if (Z_TYPE_P(cond_zv) == IS_STRING
&& is_numeric_string(Z_STRVAL_P(cond_zv), Z_STRLEN_P(cond_zv), NULL, NULL, 0)) {
/* Numeric strings cannot be compared with a simple hash lookup */
return IS_UNDEF;
}
}
return common_type;
}
static zend_bool should_use_jumptable(zend_ast_list *cases, zend_uchar jumptable_type) {
/* Thresholds are chosen based on when the average switch time for equidistributed
* input becomes smaller when using the jumptable optimization. */
if (jumptable_type == IS_LONG) {
return cases->children >= 5;
} else {
ZEND_ASSERT(jumptable_type == IS_STRING);
return cases->children >= 2;
}
}
void zend_compile_switch(zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
@ -4613,7 +4667,9 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
znode expr_node, case_node;
zend_op *opline;
uint32_t *jmpnz_opnums, opnum_default_jmp;
uint32_t *jmpnz_opnums, opnum_default_jmp, opnum_switch;
zend_uchar jumptable_type;
HashTable *jumptable = NULL;
zend_compile_expr(&expr_node, expr_ast);
@ -4622,6 +4678,24 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
case_node.op_type = IS_TMP_VAR;
case_node.u.op.var = get_temporary_variable(CG(active_op_array));
jumptable_type = determine_switch_jumptable_type(cases);
if (jumptable_type != IS_UNDEF && should_use_jumptable(cases, jumptable_type)) {
znode jumptable_op;
ALLOC_HASHTABLE(jumptable);
zend_hash_init(jumptable, cases->children, NULL, NULL, 0);
jumptable_op.op_type = IS_CONST;
ZVAL_ARR(&jumptable_op.u.constant, jumptable);
opline = zend_emit_op(NULL,
jumptable_type == IS_LONG ? ZEND_SWITCH_LONG : ZEND_SWITCH_STRING,
&expr_node, &jumptable_op);
if (opline->op1_type == IS_CONST) {
zval_copy_ctor(CT_CONSTANT(opline->op1));
}
opnum_switch = opline - CG(active_op_array)->opcodes;
}
jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0);
for (i = 0; i < cases->children; ++i) {
zend_ast *case_ast = cases->child[i];
@ -4666,8 +4740,27 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
if (cond_ast) {
zend_update_jump_target_to_next(jmpnz_opnums[i]);
if (jumptable) {
zval *cond_zv = zend_ast_get_zval(cond_ast);
zval jmp_target;
ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array)));
ZEND_ASSERT(Z_TYPE_P(cond_zv) == jumptable_type);
if (Z_TYPE_P(cond_zv) == IS_LONG) {
zend_hash_index_add(jumptable, Z_LVAL_P(cond_zv), &jmp_target);
} else {
ZEND_ASSERT(Z_TYPE_P(cond_zv) == IS_STRING);
zend_hash_add(jumptable, Z_STR_P(cond_zv), &jmp_target);
}
}
} else {
zend_update_jump_target_to_next(opnum_default_jmp);
if (jumptable) {
opline = &CG(active_op_array)->opcodes[opnum_switch];
opline->extended_value = get_next_op_number(CG(active_op_array));
}
}
zend_compile_stmt(stmt_ast);
@ -4675,6 +4768,11 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
if (!has_default_case) {
zend_update_jump_target_to_next(opnum_default_jmp);
if (jumptable) {
opline = &CG(active_op_array)->opcodes[opnum_switch];
opline->extended_value = get_next_op_number(CG(active_op_array));
}
}
zend_end_loop(get_next_op_number(CG(active_op_array)), &expr_node);

View File

@ -670,6 +670,19 @@ ZEND_API int pass_two(zend_op_array *op_array)
opline->opcode = ZEND_GENERATOR_RETURN;
}
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
/* absolute indexes to relative offsets */
HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, Z_LVAL_P(zv));
} ZEND_HASH_FOREACH_END();
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
break;
}
}
if (opline->op1_type == IS_CONST) {
ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);

View File

@ -8043,6 +8043,64 @@ ZEND_VM_HANDLER(51, ZEND_MAKE_REF, VAR|CV, UNUSED)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(187, ZEND_SWITCH_LONG, CONST|TMPVAR|CV, CONST, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
zval *op, *jump_zv;
HashTable *jumptable;
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
if (Z_TYPE_P(op) != IS_LONG) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_LONG) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
ZEND_VM_HANDLER(188, ZEND_SWITCH_STRING, CONST|TMPVAR|CV, CONST, JMP_ADDR)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
zval *op, *jump_zv;
HashTable *jumptable;
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
if (Z_TYPE_P(op) != IS_STRING) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_STRING) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_find(jumptable, Z_STR_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
USE_OPLINE

View File

@ -6409,6 +6409,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER
ZEND_VM_RETURN();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_LONG_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = EX_CONSTANT(opline->op1);
jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2));
if (Z_TYPE_P(op) != IS_LONG) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_LONG) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = EX_CONSTANT(opline->op1);
jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2));
if (Z_TYPE_P(op) != IS_STRING) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_STRING) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_find(jumptable, Z_STR_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -38305,6 +38363,64 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_CONST_HAND
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_LONG_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2));
if (Z_TYPE_P(op) != IS_LONG) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_LONG) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2));
if (Z_TYPE_P(op) != IS_STRING) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_STRING) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_find(jumptable, Z_STR_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -49767,6 +49883,64 @@ try_instanceof:
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_LONG_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *op, *jump_zv;
HashTable *jumptable;
op = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2));
if (Z_TYPE_P(op) != IS_LONG) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_LONG) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval *op, *jump_zv;
HashTable *jumptable;
op = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
jumptable = Z_ARRVAL_P(EX_CONSTANT(opline->op2));
if (Z_TYPE_P(op) != IS_STRING) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_STRING) {
/* Wrong type, fall back to ZEND_CASE chain */
ZEND_VM_NEXT_OPCODE();
}
}
jump_zv = zend_hash_find(jumptable, Z_STR_P(op));
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -57752,6 +57926,56 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_LONG_SPEC_CONST_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_LONG_SPEC_TMPVAR_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_LONG_SPEC_TMPVAR_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_LONG_SPEC_CV_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_STRING_SPEC_CONST_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_STRING_SPEC_TMPVAR_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_STRING_SPEC_TMPVAR_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SWITCH_STRING_SPEC_CV_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER,
@ -58820,7 +59044,7 @@ void zend_init_opcodes_handlers(void)
2257 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
2282 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
2307 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
4596,
4646,
2332,
2333,
2334,
@ -58905,9 +59129,11 @@ void zend_init_opcodes_handlers(void)
3531 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
3556 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
3581 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
4596,
4646,
3606 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
4596
3631 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
4646
};
zend_opcode_handlers = labels;
zend_handlers_count = sizeof(labels) / sizeof(void*);
@ -59014,7 +59240,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3631 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59022,7 +59248,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3656 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59030,7 +59256,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3681 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59041,17 +59267,17 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3706 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3731 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3756 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
@ -59059,7 +59285,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3781 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3831 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59067,7 +59293,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3806 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3856 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59075,7 +59301,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3831 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3881 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59086,7 +59312,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3856 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3906 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59094,7 +59320,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3931 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3981 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59105,7 +59331,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4006 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4056 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59113,7 +59339,7 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4081 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4131 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
if (op->op1_type > op->op2_type) {
zend_swap_operands(op);
}
@ -59124,12 +59350,12 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4156 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4206 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4231 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4281 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
@ -59137,70 +59363,70 @@ ZEND_API void zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t op1_info, uint
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4306 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4356 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 4381 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 4431 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_DOUBLE) {
spec = 4546 | SPEC_RULE_OP1;
spec = 4596 | SPEC_RULE_OP1;
} else if (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE)))) {
spec = 4551 | SPEC_RULE_OP1;
spec = 4601 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4456 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
spec = 4506 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 4466 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
spec = 4516 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4476 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
spec = 4526 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4486 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
spec = 4536 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 4496 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
spec = 4546 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4506 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
spec = 4556 | SPEC_RULE_OP1 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4516 | SPEC_RULE_OP1;
spec = 4566 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_LONG) {
spec = 4521 | SPEC_RULE_OP1;
spec = 4571 | SPEC_RULE_OP1;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4526 | SPEC_RULE_OP1;
spec = 4576 | SPEC_RULE_OP1;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 4531 | SPEC_RULE_OP1;
spec = 4581 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_LONG) {
spec = 4536 | SPEC_RULE_OP1;
spec = 4586 | SPEC_RULE_OP1;
} else if (op1_info == (MAY_BE_LONG|MAY_BE_DOUBLE)) {
spec = 4541 | SPEC_RULE_OP1;
spec = 4591 | SPEC_RULE_OP1;
}
break;
case ZEND_SEND_VAR_EX:
if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 4586 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG;
spec = 4636 | SPEC_RULE_OP1 | SPEC_RULE_QUICK_ARG;
}
break;
case ZEND_FETCH_DIM_R:
if (!(op2_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
spec = 4556 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 4606 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAR:
if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 4581 | SPEC_RULE_OP1;
spec = 4631 | SPEC_RULE_OP1;
}
break;
default:

View File

@ -21,7 +21,7 @@
#include <stdio.h>
#include <zend.h>
static const char *zend_vm_opcodes_names[187] = {
static const char *zend_vm_opcodes_names[189] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@ -209,9 +209,11 @@ static const char *zend_vm_opcodes_names[187] = {
"ZEND_FETCH_THIS",
NULL,
"ZEND_ISSET_ISEMPTY_THIS",
"ZEND_SWITCH_LONG",
"ZEND_SWITCH_STRING",
};
static uint32_t zend_vm_opcodes_flags[187] = {
static uint32_t zend_vm_opcodes_flags[189] = {
0x00000000,
0x00000707,
0x00000707,
@ -399,6 +401,8 @@ static uint32_t zend_vm_opcodes_flags[187] = {
0x00000101,
0x00000000,
0x00000101,
0x03000307,
0x03000307,
};
ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {

View File

@ -252,7 +252,9 @@ END_EXTERN_C()
#define ZEND_BIND_STATIC 183
#define ZEND_FETCH_THIS 184
#define ZEND_ISSET_ISEMPTY_THIS 186
#define ZEND_SWITCH_LONG 187
#define ZEND_SWITCH_STRING 188
#define ZEND_VM_LAST_OPCODE 186
#define ZEND_VM_LAST_OPCODE 188
#endif

View File

@ -140,6 +140,27 @@ static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
}
}
static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_basic_block *block, zend_op *opline, zval *val) {
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG)
|| (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) {
/* fallback to next block */
return block->successors[block->successors_count - 1];
}
if (Z_TYPE_P(val) == IS_LONG) {
zv = zend_hash_index_find(jumptable, Z_LVAL_P(val));
} else {
ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING);
zv = zend_hash_find(jumptable, Z_STR_P(val));
}
if (!zv) {
/* default */
return block->successors[block->successors_count - 2];
}
return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
}
static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource)
{
zend_op *opline, *src;
@ -344,6 +365,25 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
}
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
/* SWITCH variable will be deleted later by FREE, so we can't optimize it */
Tsource[VAR_NUM(opline->op1.var)] = NULL;
break;
}
if (opline->op1_type == IS_CONST) {
int target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
literal_dtor(&ZEND_OP1_LITERAL(opline));
literal_dtor(&ZEND_OP2_LITERAL(opline));
opline->opcode = ZEND_JMP;
opline->op1_type = IS_UNUSED;
opline->op2_type = IS_UNUSED;
block->successors_count = 1;
block->successors[0] = target;
}
break;
case ZEND_CASE:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
/* CASE variable will be deleted later by FREE, so we can't optimize it */
@ -886,6 +926,20 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
case ZEND_FE_FETCH_RW:
opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
uint32_t s = 0;
ZEND_ASSERT(b->successors_count == 2 + zend_hash_num_elements(jumptable));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
} ZEND_HASH_FOREACH_END();
opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
break;
}
}
}
@ -1752,7 +1806,12 @@ static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg)
prev->flags |= (b->flags & ZEND_BB_EXIT);
prev->len = b->start + b->len - prev->start;
prev->successors_count = b->successors_count;
memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
if (b->successors != b->successors_storage) {
prev->successors = b->successors;
b->successors = b->successors_storage;
} else {
memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
}
/* unlink & make block empty and unreachable */
b->flags = 0;

View File

@ -165,37 +165,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
opline = op_array->opcodes + end - 1;
b->len = target - b->start;
new_opline = op_array->opcodes + target - 1;
switch (new_opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
break;
case ZEND_JMPZNZ:
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
/* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
break;
case ZEND_CATCH:
if (!opline->result.num) {
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
break;
}
zend_optimizer_migrate_jump(op_array, new_opline, opline);
}
}
}
@ -231,34 +201,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
for (b = blocks; b < end; b++) {
if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
zend_op *opline = op_array->opcodes + b->start + b->len - 1;
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
break;
case ZEND_JMPZNZ:
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
/* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_CATCH:
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
}
zend_optimizer_shift_jump(op_array, opline, shiftlist);
}
}

View File

@ -66,37 +66,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
zend_op *new_opline = op_array->opcodes + new_count;
*new_opline = *opline;
switch (new_opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
break;
case ZEND_JMPZNZ:
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
/* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
break;
case ZEND_CATCH:
if (!opline->result.num) {
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
break;
}
zend_optimizer_migrate_jump(op_array, new_opline, opline);
}
new_count++;
}
@ -108,33 +78,7 @@ void zend_optimizer_nop_removal(zend_op_array *op_array)
/* update JMPs */
for (opline = op_array->opcodes; opline<end; opline++) {
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
break;
case ZEND_JMPZNZ:
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
/* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_CATCH:
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
}
zend_optimizer_shift_jump(op_array, opline, shiftlist);
}
/* update brk/cont array */

View File

@ -73,7 +73,12 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc
succ->flags |= ZEND_BB_FOLLOW;
}
} else {
ZEND_ASSERT(0);
ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
if (i == b->successors_count) {
succ->flags |= ZEND_BB_FOLLOW;
} else {
succ->flags |= ZEND_BB_TARGET;
}
}
} else {
succ->flags |= ZEND_BB_FOLLOW;
@ -399,6 +404,18 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
} ZEND_HASH_FOREACH_END();
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
}
case ZEND_UNSET_VAR:
case ZEND_ISSET_ISEMPTY_VAR:
if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) &&
@ -553,6 +570,24 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
uint32_t s = 0;
block->successors_count = 2 + zend_hash_num_elements(jumptable);
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
} ZEND_HASH_FOREACH_END();
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[s++] = j + 1;
break;
}
default:
block->successors_count = 1;
block->successors[0] = j + 1;

View File

@ -581,7 +581,28 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
}
if (opline->op2_type == IS_CONST) {
zend_dump_const(CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS)));
zval *op = CRT_CONSTANT_EX(op_array, opline->op2, (dump_flags & ZEND_DUMP_RT_CONSTANTS));
if (opline->opcode == ZEND_SWITCH_LONG || opline->opcode == ZEND_SWITCH_STRING) {
HashTable *jumptable = Z_ARRVAL_P(op);
zend_string *key;
zend_ulong num_key;
zval *zv;
ZEND_HASH_FOREACH_KEY_VAL(jumptable, num_key, key, zv) {
if (key) {
fprintf(stderr, " \"%s\":", ZSTR_VAL(key));
} else {
fprintf(stderr, " " ZEND_LONG_FMT ":", num_key);
}
if (b) {
fprintf(stderr, " BB%d,", b->successors[n++]);
} else {
fprintf(stderr, " L%u,", (uint32_t)ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
}
} ZEND_HASH_FOREACH_END();
fprintf(stderr, " default:");
} else {
zend_dump_const(op);
}
} else if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
if (ssa && ssa->ops) {
int ssa_var_num = ssa->ops[opline - op_array->opcodes].op2_use;
@ -654,7 +675,6 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags)
{
zend_basic_block *b = cfg->blocks + n;
int printed = 0;
fprintf(stderr, "BB%d:", n);
if (b->flags & ZEND_BB_START) {
@ -717,14 +737,12 @@ static void zend_dump_block_info(const zend_cfg *cfg, int n, uint32_t dump_flags
fprintf(stderr, ")\n");
}
if (b->successors[0] != -1) {
if (b->successors_count > 0) {
int s;
fprintf(stderr, " ; to=(BB%d", b->successors[0]);
printed = 1;
if (b->successors[1] != -1) {
fprintf(stderr, ", BB%d", b->successors[1]);
for (s = 1; s < b->successors_count; s++) {
fprintf(stderr, ", BB%d", b->successors[s]);
}
}
if (printed) {
fprintf(stderr, ")\n");
}

View File

@ -470,6 +470,8 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
zend_optimizer_remove_live_range(op_array, var);
return 1;
}
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_CASE:
case ZEND_FREE: {
zend_op *m, *n;
@ -501,7 +503,9 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
while (m < n) {
if (ZEND_OP1_TYPE(m) == type &&
ZEND_OP1(m).var == var) {
if (m->opcode == ZEND_CASE) {
if (m->opcode == ZEND_CASE
|| m->opcode == ZEND_SWITCH_LONG
|| m->opcode == ZEND_SWITCH_STRING) {
zval old_val;
ZVAL_COPY_VALUE(&old_val, val);
zval_copy_ctor(val);
@ -562,6 +566,94 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
return 1;
}
/* Update jump offsets after a jump was migrated to another opline */
void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
switch (new_opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
break;
case ZEND_JMPZNZ:
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
/* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
break;
case ZEND_CATCH:
if (!opline->result.num) {
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
} ZEND_HASH_FOREACH_END();
new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
break;
}
}
}
/* Shift jump offsets based on shiftlist */
void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_FAST_CALL:
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
break;
case ZEND_JMPZNZ:
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
/* break missing intentionally */
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_CATCH:
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]);
} ZEND_HASH_FOREACH_END();
opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
}
}
}
static zend_class_entry *get_class_entry_from_op1(
zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
if (opline->op1_type == IS_CONST) {

View File

@ -109,5 +109,7 @@ int zend_optimizer_is_disabled_func(const char *name, size_t len);
zend_function *zend_optimizer_get_called_func(
zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants);
uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);
#endif

View File

@ -0,0 +1,21 @@
--TEST--
Switch where all targets, including default, coincide
--FILE--
<?php
$foo = 42.0;
$bar = true;
switch ($foo) {
default:
case 0: case 1: case 2: case 3:
if ($bar) {
echo "true\n";
} else {
echo "false\n";
}
}
?>
--EXPECT--
true

View File

@ -431,6 +431,8 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
/* relative extended_value don't have to be changed */
break;
}
@ -1030,6 +1032,8 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
/* relative extended_value don't have to be changed */
break;
}

View File

@ -446,6 +446,8 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
/* relative extended_value don't have to be changed */
break;
}