--- /dev/null
+--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"
} 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 ||
}
/* }}} */
+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];
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);
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];
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);
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);
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);
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
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
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
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
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,
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,
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*);
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);
}
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);
}
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);
}
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:
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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:
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:
#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",
"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,
0x00000101,
0x00000000,
0x00000101,
+ 0x03000307,
+ 0x03000307,
};
ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {
#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
}
}
+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;
}
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 */
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;
+ }
}
}
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;
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);
}
}
}
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);
}
}
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++;
}
/* 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 */
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;
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) &&
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;
}
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;
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) {
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");
}
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;
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);
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) {
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
--- /dev/null
+--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
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;
}
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;
}
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;
}