diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-03-24 22:47:21 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-03-24 22:47:21 +0300 |
commit | 23c33b14835efdf228b0bc08f40215972f61f842 (patch) | |
tree | 61c504f77e9af0779180b0d3eaf8d4e330450754 | |
parent | bdf7fc67d8a146b4a5387c56268181c63047dfe6 (diff) | |
download | php-git-23c33b14835efdf228b0bc08f40215972f61f842.tar.gz |
Optimized strings concatenation.
ZEND_ADD_STRING/VAR/CHAR are replaced with ZEND_ROPE_INTI, ZEND_ROPE_ADD, ZEND_ROPE_END.
Instead of reallocation and copying string on each ZEND_ADD_STRING/VAR/CAHR, collect all the strings and then allocate and construct the resulting string once.
-rw-r--r-- | Zend/zend_compile.c | 144 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 1 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 140 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 713 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.c | 8 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.h | 7 | ||||
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 70 | ||||
-rw-r--r-- | ext/opcache/Optimizer/optimize_temp_vars_5.c | 37 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass1_5.c | 3 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass2.c | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 11 |
11 files changed, 827 insertions, 308 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 64d96e452d..ffe1b9279d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6231,55 +6231,129 @@ void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ -void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ +static zend_op *zend_compile_rope_add(znode *result, uint32_t num, znode *elem_node) /* {{{ */ { - zend_ast_list *list = zend_ast_get_list(ast); - uint32_t i; + zend_op *opline = get_next_op(CG(active_op_array)); - ZEND_ASSERT(list->children > 0); + if (num == 0) { + result->op_type = IS_TMP_VAR; + result->u.op.var = -1; + opline->opcode = ZEND_ROPE_INIT; + SET_UNUSED(opline->op1); + } else { + opline->opcode = ZEND_ROPE_ADD; + SET_NODE(opline->op1, result); + } + SET_NODE(opline->op2, elem_node); + SET_NODE(opline->result, result); + opline->extended_value = num; + return opline; +} +/* }}} */ - result->op_type = IS_TMP_VAR; - result->u.op.var = get_temporary_variable(CG(active_op_array)); +static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ +{ + uint32_t i, j; + uint32_t rope_init_lineno = -1; + zend_op *opline = NULL, *init_opline; + znode elem_node, last_const_node; + zend_ast_list *list = zend_ast_get_list(ast); - for (i = 0; i < list->children; ++i) { - zend_ast *elem_ast = list->child[i]; - znode elem_node; - zend_op *opline; + ZEND_ASSERT(list->children > 0); - zend_compile_expr(&elem_node, elem_ast); + j = 0; + last_const_node.op_type = IS_UNUSED; + for (i = 0; i < list->children; i++) { + zend_compile_expr(&elem_node, list->child[i]); - if (elem_ast->kind == ZEND_AST_ZVAL) { - zval *zv = &elem_node.u.constant; - ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); + if (elem_node.op_type == IS_CONST) { + convert_to_string(&elem_node.u.constant); - if (Z_STRLEN_P(zv) > 1) { - opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_ADD_STRING; - } else if (Z_STRLEN_P(zv) == 1) { - char ch = *Z_STRVAL_P(zv); - zend_string_release(Z_STR_P(zv)); - ZVAL_LONG(zv, ch); - - opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_ADD_CHAR; + if (Z_STRLEN(elem_node.u.constant) == 0) { + zval_ptr_dtor(&elem_node.u.constant); + } else if (last_const_node.op_type == IS_CONST) { + concat_function(&last_const_node.u.constant, &last_const_node.u.constant, &elem_node.u.constant); + zval_ptr_dtor(&elem_node.u.constant); } else { - /* String can be empty after a variable at the end of a heredoc */ - zend_string_release(Z_STR_P(zv)); - continue; + last_const_node.op_type = IS_CONST; + ZVAL_COPY_VALUE(&last_const_node.u.constant, &elem_node.u.constant); } + continue; } else { - opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_ADD_VAR; - ZEND_ASSERT(elem_node.op_type != IS_CONST); + if (j == 0) { + rope_init_lineno = get_next_op_number(CG(active_op_array)); + } + if (last_const_node.op_type == IS_CONST) { + zend_compile_rope_add(result, j++, &last_const_node); + last_const_node.op_type = IS_UNUSED; + } + opline = zend_compile_rope_add(result, j++, &elem_node); } + } - if (i == 0) { - SET_UNUSED(opline->op1); + if (j == 0) { + result->op_type = IS_CONST; + if (last_const_node.op_type == IS_CONST) { + ZVAL_COPY_VALUE(&result->u.constant, &last_const_node.u.constant); + } else { + ZVAL_EMPTY_STRING(&result->u.constant); + /* empty string */ + } + return; + } else if (last_const_node.op_type == IS_CONST) { + opline = zend_compile_rope_add(result, j++, &last_const_node); + } + init_opline = CG(active_op_array)->opcodes + rope_init_lineno; + if (j == 1) { + if (opline->op2_type == IS_CONST) { + GET_NODE(result, opline->op2); + MAKE_NOP(opline); } else { - SET_NODE(opline->op1, result); + opline->opcode = ZEND_CAST; + opline->extended_value = IS_STRING; + opline->op1_type = opline->op2_type; + opline->op1 = opline->op2; + opline->result_type = IS_TMP_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + SET_UNUSED(opline->op2); + GET_NODE(result, opline->result); + } + } else if (j == 2) { + opline->opcode = ZEND_FAST_CONCAT; + opline->extended_value = 0; + opline->op1_type = init_opline->op2_type; + opline->op1 = init_opline->op2; + opline->result_type = IS_TMP_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + MAKE_NOP(init_opline); + GET_NODE(result, opline->result); + } else { + uint32_t var; + + init_opline->extended_value = j; + opline->opcode = ZEND_ROPE_END; + opline->result.var = get_temporary_variable(CG(active_op_array)); + var = opline->op1.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); + + /* Allocates the necessary number of zval slots to keep the rope */ + i = ((j * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); + while (i > 1) { + get_temporary_variable(CG(active_op_array)); + i--; + } + /* Update all the previous opcodes to use the same variable */ + while (opline != init_opline) { + opline--; + if (opline->opcode == ZEND_ROPE_ADD && + opline->result.var == -1) { + opline->op1.var = var; + opline->result.var = var; + } else if (opline->opcode == ZEND_ROPE_INIT && + opline->result.var == -1) { + opline->result.var = var; + } } - SET_NODE(opline->op2, &elem_node); - SET_NODE(opline->result, result); } } /* }}} */ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 526f9e2952..1a499b9f19 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -861,6 +861,7 @@ ZEND_API binary_op_type get_binary_op(int opcode) case ZEND_SR: case ZEND_ASSIGN_SR: return (binary_op_type) shift_right_function; + case ZEND_FAST_CONCAT: case ZEND_CONCAT: case ZEND_ASSIGN_CONCAT: return (binary_op_type) concat_function; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e3eb94e591..d8eb40a3d5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2571,82 +2571,120 @@ ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(54, ZEND_ADD_CHAR, TMP|UNUSED, CONST) +ZEND_VM_HANDLER(53, ZEND_FAST_CONCAT, CONST|TMPVAR|CV, CONST|TMPVAR|CV) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); + zend_free_op free_op1, free_op2; + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; SAVE_OPLINE(); - - if (OP1_TYPE == IS_UNUSED) { - /* Initialize for erealloc in add_char_to_string */ - ZVAL_EMPTY_STRING(str); + op1 = GET_OP1_ZVAL_PTR(BP_VAR_R); + op2 = GET_OP2_ZVAL_PTR(BP_VAR_R); + if (OP1_TYPE == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); } - - add_char_to_string(str, str, EX_CONSTANT(opline->op2)); - - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ + if (OP2_TYPE == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (OP1_TYPE != IS_CONST) { + zend_string_release(op1_str); + } + if (OP2_TYPE != IS_CONST) { + zend_string_release(op2_str); + } + FREE_OP1(); + FREE_OP2(); + CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(55, ZEND_ADD_STRING, TMP|UNUSED, CONST) +ZEND_VM_HANDLER(54, ZEND_ROPE_INIT, UNUSED, CONST|TMPVAR|CV) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); - - SAVE_OPLINE(); + zend_free_op free_op2; + zend_string **rope; + zval *var; - if (OP1_TYPE == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); + /* Compiler allocates the necessary number of zval slots to keep the rope */ + rope = (zend_string**)EX_VAR(opline->result.var); + if (OP2_TYPE == IS_CONST) { + var = GET_OP2_ZVAL_PTR(BP_VAR_R); + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = GET_OP2_ZVAL_PTR(BP_VAR_R); + rope[0] = zval_get_string(var); + FREE_OP2(); + CHECK_EXCEPTION(); } - - add_string_to_string(str, str, EX_CONSTANT(opline->op2)); - - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(56, ZEND_ADD_VAR, TMP|UNUSED, TMPVAR|CV) +ZEND_VM_HANDLER(55, ZEND_ROPE_ADD, TMP, CONST|TMPVAR|CV) { USE_OPLINE zend_free_op free_op2; - zval *str = EX_VAR(opline->result.var); + zend_string **rope; zval *var; - zval var_copy; - int use_copy = 0; - - SAVE_OPLINE(); - var = GET_OP2_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); + /* op1 and result are the same */ + rope = (zend_string**)EX_VAR(opline->op1.var); + if (OP2_TYPE == IS_CONST) { + var = GET_OP2_ZVAL_PTR(BP_VAR_R); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = GET_OP2_ZVAL_PTR(BP_VAR_R); + rope[opline->extended_value] = zval_get_string(var); + FREE_OP2(); + CHECK_EXCEPTION(); } + ZEND_VM_NEXT_OPCODE(); +} - if (Z_TYPE_P(var) != IS_STRING) { - use_copy = zend_make_printable_zval(var, &var_copy); - - if (use_copy) { - var = &var_copy; - } +ZEND_VM_HANDLER(56, ZEND_ROPE_END, TMP, CONST|TMPVAR|CV) +{ + USE_OPLINE + zend_free_op free_op2; + zend_string **rope; + zval *var, *ret; + uint32_t i; + size_t len = 0; + char *target; + + rope = (zend_string**)EX_VAR(opline->op1.var); + if (OP2_TYPE == IS_CONST) { + var = GET_OP2_ZVAL_PTR(BP_VAR_R); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = GET_OP2_ZVAL_PTR(BP_VAR_R); + rope[opline->extended_value] = zval_get_string(var); + FREE_OP2(); + CHECK_EXCEPTION(); } - add_string_to_string(str, str, var); - - if (use_copy) { - zend_string_release(Z_STR_P(var)); + for (i = 0; i <= opline->extended_value; i++) { + len += rope[i]->len; } - /* original comment, possibly problematic: - * FREE_OP is missing intentionally here - we're always working on the same temporary variable - * (Zeev): I don't think it's problematic, we only use variables - * which aren't affected by FREE_OP(Ts, )'s anyway, unless they're - * string offsets or overloaded objects - */ - FREE_OP2(); + ret = EX_VAR(opline->result.var); + ZVAL_STR(ret, zend_string_alloc(len, 0)); + target = Z_STRVAL_P(ret); + for (i = 0; i <= opline->extended_value; i++) { + memcpy(target, rope[i]->val, rope[i]->len); + target += rope[i]->len; + zend_string_release(rope[i]); + } + *target = '\0'; - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3d09a2f52f..d2c93b0010 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4924,6 +4924,42 @@ try_fetch_list: ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = EX_CONSTANT(opline->op1); + op2 = EX_CONSTANT(opline->op2); + if (IS_CONST == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if (IS_CONST == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CONST != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CONST != IS_CONST) { + zend_string_release(op2_str); + } + + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8272,6 +8308,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ } } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + if (IS_CONST == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if (IS_CV == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CONST != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CV != IS_CONST) { + zend_string_release(op2_str); + } + + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -9785,6 +9857,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CONST_ } } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op2; + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = EX_CONSTANT(opline->op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + if (IS_CONST == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CONST != IS_CONST) { + zend_string_release(op1_str); + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op2_str); + } + + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -11250,41 +11358,62 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CO } } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_CHAR_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); - SAVE_OPLINE(); - - if (IS_TMP_VAR == IS_UNUSED) { - /* Initialize for erealloc in add_char_to_string */ - ZVAL_EMPTY_STRING(str); - } + zend_string **rope; + zval *var; - add_char_to_string(str, str, EX_CONSTANT(opline->op2)); + /* op1 and result are the same */ + rope = (zend_string**)EX_VAR(opline->op1.var); + if (IS_CONST == IS_CONST) { + var = EX_CONSTANT(opline->op2); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = EX_CONSTANT(opline->op2); + rope[opline->extended_value] = zval_get_string(var); - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ + CHECK_EXCEPTION(); + } ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_STRING_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); - SAVE_OPLINE(); + zend_string **rope; + zval *var, *ret; + uint32_t i; + size_t len = 0; + char *target; - if (IS_TMP_VAR == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); - } + rope = (zend_string**)EX_VAR(opline->op1.var); + if (IS_CONST == IS_CONST) { + var = EX_CONSTANT(opline->op2); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = EX_CONSTANT(opline->op2); + rope[opline->extended_value] = zval_get_string(var); - add_string_to_string(str, str, EX_CONSTANT(opline->op2)); + CHECK_EXCEPTION(); + } + for (i = 0; i <= opline->extended_value; i++) { + len += rope[i]->len; + } + ret = EX_VAR(opline->result.var); + ZVAL_STR(ret, zend_string_alloc(len, 0)); + target = Z_STRVAL_P(ret); + for (i = 0; i <= opline->extended_value; i++) { + memcpy(target, rope[i]->val, rope[i]->len); + target += rope[i]->len; + zend_string_release(rope[i]); + } + *target = '\0'; - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ ZEND_VM_NEXT_OPCODE(); } @@ -12418,43 +12547,62 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_CV } } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_VAR_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_ADD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); + zend_string **rope; zval *var; - zval var_copy; - int use_copy = 0; - SAVE_OPLINE(); - var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + /* op1 and result are the same */ + rope = (zend_string**)EX_VAR(opline->op1.var); + if (IS_CV == IS_CONST) { + var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + rope[opline->extended_value] = zval_get_string(var); - if (IS_TMP_VAR == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); + CHECK_EXCEPTION(); } + ZEND_VM_NEXT_OPCODE(); +} - if (Z_TYPE_P(var) != IS_STRING) { - use_copy = zend_make_printable_zval(var, &var_copy); +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE - if (use_copy) { - var = &var_copy; - } - } - add_string_to_string(str, str, var); + zend_string **rope; + zval *var, *ret; + uint32_t i; + size_t len = 0; + char *target; + + rope = (zend_string**)EX_VAR(opline->op1.var); + if (IS_CV == IS_CONST) { + var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + rope[opline->extended_value] = zval_get_string(var); - if (use_copy) { - zend_string_release(Z_STR_P(var)); + CHECK_EXCEPTION(); + } + for (i = 0; i <= opline->extended_value; i++) { + len += rope[i]->len; + } + ret = EX_VAR(opline->result.var); + ZVAL_STR(ret, zend_string_alloc(len, 0)); + target = Z_STRVAL_P(ret); + for (i = 0; i <= opline->extended_value; i++) { + memcpy(target, rope[i]->val, rope[i]->len); + target += rope[i]->len; + zend_string_release(rope[i]); } - /* original comment, possibly problematic: - * FREE_OP is missing intentionally here - we're always working on the same temporary variable - * (Zeev): I don't think it's problematic, we only use variables - * which aren't affected by FREE_OP(Ts, )'s anyway, unless they're - * string offsets or overloaded objects - */ + *target = '\0'; - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -12867,44 +13015,62 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_FUNC_ARG_SPEC_TMP_TM } } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_VAR_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op2; - zval *str = EX_VAR(opline->result.var); + zend_string **rope; zval *var; - zval var_copy; - int use_copy = 0; - SAVE_OPLINE(); - var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - - if (IS_TMP_VAR == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); + /* op1 and result are the same */ + rope = (zend_string**)EX_VAR(opline->op1.var); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + rope[opline->extended_value] = zval_get_string(var); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); } + ZEND_VM_NEXT_OPCODE(); +} - if (Z_TYPE_P(var) != IS_STRING) { - use_copy = zend_make_printable_zval(var, &var_copy); +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op2; + zend_string **rope; + zval *var, *ret; + uint32_t i; + size_t len = 0; + char *target; - if (use_copy) { - var = &var_copy; - } + rope = (zend_string**)EX_VAR(opline->op1.var); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + rope[opline->extended_value] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + rope[opline->extended_value] = zval_get_string(var); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); } - add_string_to_string(str, str, var); - - if (use_copy) { - zend_string_release(Z_STR_P(var)); + for (i = 0; i <= opline->extended_value; i++) { + len += rope[i]->len; } - /* original comment, possibly problematic: - * FREE_OP is missing intentionally here - we're always working on the same temporary variable - * (Zeev): I don't think it's problematic, we only use variables - * which aren't affected by FREE_OP(Ts, )'s anyway, unless they're - * string offsets or overloaded objects - */ - zval_ptr_dtor_nogc(free_op2); + ret = EX_VAR(opline->result.var); + ZVAL_STR(ret, zend_string_alloc(len, 0)); + target = Z_STRVAL_P(ret); + for (i = 0; i <= opline->extended_value; i++) { + memcpy(target, rope[i]->val, rope[i]->len); + target += rope[i]->len; + zend_string_release(rope[i]); + } + *target = '\0'; - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -21682,41 +21848,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_H ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_CHAR_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_INIT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); - - SAVE_OPLINE(); - - if (IS_UNUSED == IS_UNUSED) { - /* Initialize for erealloc in add_char_to_string */ - ZVAL_EMPTY_STRING(str); - } - - add_char_to_string(str, str, EX_CONSTANT(opline->op2)); - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ - ZEND_VM_NEXT_OPCODE(); -} - -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_STRING_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zval *str = EX_VAR(opline->result.var); + zend_string **rope; + zval *var; - SAVE_OPLINE(); + /* Compiler allocates the necessary number of zval slots to keep the rope */ + rope = (zend_string**)EX_VAR(opline->result.var); + if (IS_CONST == IS_CONST) { + var = EX_CONSTANT(opline->op2); + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = EX_CONSTANT(opline->op2); + rope[0] = zval_get_string(var); - if (IS_UNUSED == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); + CHECK_EXCEPTION(); } - - add_string_to_string(str, str, EX_CONSTANT(opline->op2)); - - /* FREE_OP is missing intentionally here - we're always working on the same temporary variable */ - /*CHECK_EXCEPTION();*/ ZEND_VM_NEXT_OPCODE(); } @@ -23989,43 +24139,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CV_HAND ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_VAR_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_INIT_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *str = EX_VAR(opline->result.var); + zend_string **rope; zval *var; - zval var_copy; - int use_copy = 0; - - SAVE_OPLINE(); - var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); - - if (IS_UNUSED == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); - } - - if (Z_TYPE_P(var) != IS_STRING) { - use_copy = zend_make_printable_zval(var, &var_copy); - if (use_copy) { - var = &var_copy; - } - } - add_string_to_string(str, str, var); + /* Compiler allocates the necessary number of zval slots to keep the rope */ + rope = (zend_string**)EX_VAR(opline->result.var); + if (IS_CV == IS_CONST) { + var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + rope[0] = zval_get_string(var); - if (use_copy) { - zend_string_release(Z_STR_P(var)); + CHECK_EXCEPTION(); } - /* original comment, possibly problematic: - * FREE_OP is missing intentionally here - we're always working on the same temporary variable - * (Zeev): I don't think it's problematic, we only use variables - * which aren't affected by FREE_OP(Ts, )'s anyway, unless they're - * string offsets or overloaded objects - */ - - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -25419,44 +25551,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_TMPVAR_ ZEND_VM_NEXT_OPCODE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_VAR_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op2; - zval *str = EX_VAR(opline->result.var); + zend_string **rope; zval *var; - zval var_copy; - int use_copy = 0; - - SAVE_OPLINE(); - var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); - if (IS_UNUSED == IS_UNUSED) { - /* Initialize for erealloc in add_string_to_string */ - ZVAL_EMPTY_STRING(str); - } - - if (Z_TYPE_P(var) != IS_STRING) { - use_copy = zend_make_printable_zval(var, &var_copy); - - if (use_copy) { - var = &var_copy; - } - } - add_string_to_string(str, str, var); - - if (use_copy) { - zend_string_release(Z_STR_P(var)); + /* Compiler allocates the necessary number of zval slots to keep the rope */ + rope = (zend_string**)EX_VAR(opline->result.var); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + rope[0] = zend_string_copy(Z_STR_P(var)); + } else { + SAVE_OPLINE(); + var = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + rope[0] = zval_get_string(var); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); } - /* original comment, possibly problematic: - * FREE_OP is missing intentionally here - we're always working on the same temporary variable - * (Zeev): I don't think it's problematic, we only use variables - * which aren't affected by FREE_OP(Ts, )'s anyway, unless they're - * string offsets or overloaded objects - */ - zval_ptr_dtor_nogc(free_op2); - - CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -29375,6 +29488,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_HANDLER(Z ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = EX_CONSTANT(opline->op2); + if (IS_CV == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if (IS_CONST == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CV != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CONST != IS_CONST) { + zend_string_release(op2_str); + } + + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -34152,6 +34301,42 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER( ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + if (IS_CV == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if (IS_CV == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CV != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CV != IS_CONST) { + zend_string_release(op2_str); + } + + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -36534,6 +36719,42 @@ assign_dim_clean: ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op2; + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + if (IS_CV == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if (IS_CV != IS_CONST) { + zend_string_release(op1_str); + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op2_str); + } + + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -38509,6 +38730,42 @@ try_fetch_list: ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = EX_CONSTANT(opline->op2); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if (IS_CONST == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CONST != IS_CONST) { + zend_string_release(op2_str); + } + zval_ptr_dtor_nogc(free_op1); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -40430,6 +40687,42 @@ fetch_obj_is_no_object: ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if (IS_CV == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op1_str); + } + if (IS_CV != IS_CONST) { + zend_string_release(op2_str); + } + zval_ptr_dtor_nogc(free_op1); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -41384,6 +41677,42 @@ fetch_obj_is_no_object: ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1, free_op2; + zval *op1, *op2; + zend_string *op1_str, *op2_str, *str; + + SAVE_OPLINE(); + op1 = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + op2 = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2); + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + op1_str = Z_STR_P(op1); + } else { + op1_str = zval_get_string(op1); + } + if ((IS_TMP_VAR|IS_VAR) == IS_CONST) { + op2_str = Z_STR_P(op2); + } else { + op2_str = zval_get_string(op2); + } + str = zend_string_alloc(op1_str->len + op2_str->len, 0); + memcpy(str->val, op1_str->val, op1_str->len); + memcpy(str->val + op1_str->len, op2_str->val, op2_str->len+1); + ZVAL_NEW_STR(EX_VAR(opline->result.var), str); + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op1_str); + } + if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { + zend_string_release(op2_str); + } + zval_ptr_dtor_nogc(free_op1); + zval_ptr_dtor_nogc(free_op2); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -43049,15 +43378,31 @@ void zend_init_opcodes_handlers(void) ZEND_BOOL_SPEC_CV_HANDLER, ZEND_BOOL_SPEC_CV_HANDLER, ZEND_BOOL_SPEC_CV_HANDLER, + ZEND_FAST_CONCAT_SPEC_CONST_CONST_HANDLER, + ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER, + ZEND_FAST_CONCAT_SPEC_CONST_TMPVAR_HANDLER, ZEND_NULL_HANDLER, + ZEND_FAST_CONCAT_SPEC_CONST_CV_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER, ZEND_NULL_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_CONST_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_TMPVAR_HANDLER, ZEND_NULL_HANDLER, + ZEND_FAST_CONCAT_SPEC_TMPVAR_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_FAST_CONCAT_SPEC_CV_CONST_HANDLER, + ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER, + ZEND_FAST_CONCAT_SPEC_CV_TMPVAR_HANDLER, ZEND_NULL_HANDLER, + ZEND_FAST_CONCAT_SPEC_CV_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -43073,27 +43418,11 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_ROPE_INIT_SPEC_UNUSED_CONST_HANDLER, + ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER, + ZEND_ROPE_INIT_SPEC_UNUSED_TMPVAR_HANDLER, ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_ADD_CHAR_SPEC_TMP_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_ADD_CHAR_SPEC_UNUSED_CONST_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_ROPE_INIT_SPEC_UNUSED_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -43104,8 +43433,11 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_ADD_STRING_SPEC_TMP_CONST_HANDLER, + ZEND_ROPE_ADD_SPEC_TMP_CONST_HANDLER, + ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER, + ZEND_ROPE_ADD_SPEC_TMP_TMPVAR_HANDLER, ZEND_NULL_HANDLER, + ZEND_ROPE_ADD_SPEC_TMP_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -43114,7 +43446,6 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_ADD_STRING_SPEC_UNUSED_CONST_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -43127,23 +43458,21 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_ROPE_END_SPEC_TMP_CONST_HANDLER, + ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER, + ZEND_ROPE_END_SPEC_TMP_TMPVAR_HANDLER, ZEND_NULL_HANDLER, + ZEND_ROPE_END_SPEC_TMP_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_ADD_VAR_SPEC_TMP_TMPVAR_HANDLER, - ZEND_ADD_VAR_SPEC_TMP_TMPVAR_HANDLER, ZEND_NULL_HANDLER, - ZEND_ADD_VAR_SPEC_TMP_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_ADD_VAR_SPEC_UNUSED_TMPVAR_HANDLER, - ZEND_ADD_VAR_SPEC_UNUSED_TMPVAR_HANDLER, ZEND_NULL_HANDLER, - ZEND_ADD_VAR_SPEC_UNUSED_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index d464b9ba50..b234c2f33e 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -75,10 +75,10 @@ const char *zend_vm_opcodes_map[171] = { "ZEND_BRK", "ZEND_CONT", "ZEND_BOOL", - NULL, - "ZEND_ADD_CHAR", - "ZEND_ADD_STRING", - "ZEND_ADD_VAR", + "ZEND_FAST_CONCAT", + "ZEND_ROPE_INIT", + "ZEND_ROPE_ADD", + "ZEND_ROPE_END", "ZEND_BEGIN_SILENCE", "ZEND_END_SILENCE", "ZEND_INIT_FCALL_BY_NAME", diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 86fe5590d6..84b7cd9882 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -85,9 +85,10 @@ END_EXTERN_C() #define ZEND_BRK 50 #define ZEND_CONT 51 #define ZEND_BOOL 52 -#define ZEND_ADD_CHAR 54 -#define ZEND_ADD_STRING 55 -#define ZEND_ADD_VAR 56 +#define ZEND_FAST_CONCAT 53 +#define ZEND_ROPE_INIT 54 +#define ZEND_ROPE_ADD 55 +#define ZEND_ROPE_END 56 #define ZEND_BEGIN_SILENCE 57 #define ZEND_END_SILENCE 58 #define ZEND_INIT_FCALL_BY_NAME 59 diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 555c2778cf..3f6e25c05c 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -911,38 +911,26 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, } ZVAL_NULL(&ZEND_OP1_LITERAL(last_op)); MAKE_NOP(last_op); - } else if ((opline->opcode == ZEND_CONCAT || - opline->opcode == ZEND_ADD_STRING || - opline->opcode == ZEND_ADD_CHAR) && + } else if ((opline->opcode == ZEND_CONCAT) && ZEND_OP2_TYPE(opline) == IS_CONST && ZEND_OP1_TYPE(opline) == IS_TMP_VAR && VAR_SOURCE(opline->op1) && (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT || - VAR_SOURCE(opline->op1)->opcode == ZEND_ADD_STRING || - VAR_SOURCE(opline->op1)->opcode == ZEND_ADD_CHAR) && + VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT) && ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) { /* compress consecutive CONCAT/ADD_STRING/ADD_CHARs */ zend_op *src = VAR_SOURCE(opline->op1); int l, old_len; - if (opline->opcode == ZEND_ADD_CHAR) { - char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); - ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1); - } else if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { convert_to_string_safe(&ZEND_OP2_LITERAL(opline)); } - if (src->opcode == ZEND_ADD_CHAR) { - char c = (char)Z_LVAL(ZEND_OP2_LITERAL(src)); - ZVAL_STRINGL(&ZEND_OP2_LITERAL(src), &c, 1); - } else if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { + if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { convert_to_string_safe(&ZEND_OP2_LITERAL(src)); } VAR_UNSET(opline->op1); - if (opline->opcode != ZEND_CONCAT) { - opline->opcode = ZEND_ADD_STRING; - } COPY_NODE(opline->op1, src->op1); old_len = Z_STRLEN(ZEND_OP2_LITERAL(src)); l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline)); @@ -971,6 +959,7 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, opline->opcode == ZEND_SL || opline->opcode == ZEND_SR || opline->opcode == ZEND_CONCAT || + opline->opcode == ZEND_FAST_CONCAT || opline->opcode == ZEND_IS_EQUAL || opline->opcode == ZEND_IS_NOT_EQUAL || opline->opcode == ZEND_IS_SMALLER || @@ -1036,7 +1025,40 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, VAR_UNSET(opline->op1); COPY_NODE(opline->op1, src->op1); MAKE_NOP(src); - } else if (opline->opcode == ZEND_CONCAT) { + } else if (opline->opcode == ZEND_CONCAT || opline->opcode == ZEND_FAST_CONCAT) { +#if 0 +if (opline->opcode == ZEND_CONCAT) { +fprintf(stderr, "CONCAT "); +} else { +fprintf(stderr, "FAST_CONCAT "); +} +if (opline->op1_type == IS_CONST) { +fprintf(stderr, "CONST"); +} else if (opline->op1_type == IS_TMP_VAR) { +fprintf(stderr, "TMP"); +if (VAR_SOURCE(opline->op1)) { +fprintf(stderr, "(%s)", zend_get_opcode_name(VAR_SOURCE(opline->op1)->opcode)); +} +} else if (opline->op1_type == IS_VAR) { +fprintf(stderr, "VAR"); +} else if (opline->op1_type == IS_CV) { +fprintf(stderr, "CV"); +} +fprintf(stderr, ", "); +if (opline->op2_type == IS_CONST) { +fprintf(stderr, "CONST"); +} else if (opline->op2_type == IS_TMP_VAR) { +fprintf(stderr, "TMP"); +if (VAR_SOURCE(opline->op2)) { +fprintf(stderr, "(%s)", zend_get_opcode_name(VAR_SOURCE(opline->op2)->opcode)); +} +} else if (opline->op2_type == IS_VAR) { +fprintf(stderr, "VAR"); +} else if (opline->op2_type == IS_CV) { +fprintf(stderr, "CV"); +} +fprintf(stderr, "\n"); +#endif if ((ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) && VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_CAST && @@ -1076,6 +1098,20 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, opline->extended_value = IS_STRING; opline->op2_type = IS_UNUSED; opline->op2.var = 0; + } else if (opline->opcode == ZEND_CONCAT && + (opline->op1_type == IS_CONST || + (opline->op1_type == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT || + VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END || + VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT))) && + (opline->op2_type == IS_CONST || + (opline->op2_type == IS_TMP_VAR && + VAR_SOURCE(opline->op2) && + (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT || + VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END || + VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT)))) { + opline->opcode = ZEND_FAST_CONCAT; } } else if (opline->opcode == ZEND_QM_ASSIGN && ZEND_OP1_TYPE(opline) == ZEND_RESULT_TYPE(opline) && diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index dc93ce2f4c..b302556d56 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -79,12 +79,32 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { currT = VAR_NUM(ZEND_OP1(opline).var) - offset; - if (!valid_T[currT]) { - GET_AVAILABLE_T(); - map_T[currT] = i; + if (opline->opcode == ZEND_ROPE_END) { + int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); + int var; + + var = max; + while (var >= 0 && !taken_T[var]) { + var--; + } + max = MAX(max, var + num); + var = var + 1; + map_T[currT] = var; valid_T[currT] = 1; + taken_T[var] = 1; + ZEND_OP1(opline).var = NUM_VAR(var + offset); + while (num > 1) { + num--; + taken_T[var + num] = 1; + } + } else { + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } - ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } /* Skip OP_DATA */ @@ -135,6 +155,15 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c taken_T[map_T[currT]] = 0; } ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset); + if (opline->opcode == ZEND_ROPE_INIT) { + if (start_of_T[currT] == opline) { + uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); + while (num > 1) { + num--; + taken_T[map_T[currT]+num] = 0; + } + } + } } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */ GET_AVAILABLE_T(); diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 031e0e3b37..9a5ae14de8 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -55,6 +55,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_SL: case ZEND_SR: case ZEND_CONCAT: + case ZEND_FAST_CONCAT: case ZEND_IS_EQUAL: case ZEND_IS_NOT_EQUAL: case ZEND_IS_SMALLER: @@ -160,6 +161,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; +#if 0 case ZEND_ADD_STRING: case ZEND_ADD_CHAR: { @@ -230,6 +232,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } } break; +#endif case ZEND_FETCH_CONSTANT: if (ZEND_OP1_TYPE(opline) == IS_UNUSED && diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index b578592493..c0832a1cbf 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -90,6 +90,7 @@ void zend_optimizer_pass2(zend_op_array *op_array) break; case ZEND_CONCAT: + case ZEND_FAST_CONCAT: if (ZEND_OP1_TYPE(opline) == IS_CONST) { if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { convert_to_string(&ZEND_OP1_LITERAL(opline)); diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index d0d67a57da..de45bf0cd9 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -141,6 +141,10 @@ void zend_optimizer_update_op1_const(zend_op_array *op_array, break; } } else { + if (opline->opcode == ZEND_CONCAT || + opline->opcode == ZEND_FAST_CONCAT) { + convert_to_string(val); + } opline->op1.constant = zend_optimizer_add_literal(op_array, val); } } @@ -158,9 +162,12 @@ void zend_optimizer_update_op2_const(zend_op_array *op_array, Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); return; - } else if (opline->opcode == ZEND_ADD_VAR) { + } else if (opline->opcode == ZEND_ROPE_INIT || + opline->opcode == ZEND_ROPE_ADD || + opline->opcode == ZEND_ROPE_END || + opline->opcode == ZEND_CONCAT || + opline->opcode == ZEND_FAST_CONCAT) { convert_to_string(val); - opline->opcode = ZEND_ADD_STRING; } opline->op2.constant = zend_optimizer_add_literal(op_array, val); if (Z_TYPE_P(val) == IS_STRING) { |