]> granicus.if.org Git - php/commitdiff
Optimized strings concatenation.
authorDmitry Stogov <dmitry@zend.com>
Tue, 24 Mar 2015 19:47:21 +0000 (22:47 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 24 Mar 2015 19:47:21 +0000 (22:47 +0300)
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.

Zend/zend_compile.c
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h
ext/opcache/Optimizer/block_pass.c
ext/opcache/Optimizer/optimize_temp_vars_5.c
ext/opcache/Optimizer/pass1_5.c
ext/opcache/Optimizer/pass2.c
ext/opcache/Optimizer/zend_optimizer.c

index 64d96e452d8d50845e4ee03599de9b1a6d001ec7..ffe1b9279d68f62d4e267df55c0c33512806ec11 100644 (file)
@@ -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);
        }
 }
 /* }}} */
index 526f9e29522ca42d35034a46ffd260982bf7ee9c..1a499b9f19d94c5238f29177dc41b4fd3d699bcf 100644 (file)
@@ -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;
index e3eb94e5912618514f4e1b2862b8732703d4e6ae..d8eb40a3d544e304d08525e92a3ba27a261e0dd2 100644 (file)
@@ -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();
 }
 
index 3d09a2f52fd383e02e592aaa611556fa12d619cb..d2c93b0010ecac3ec2f4ac6fa59fd0fc3cd635bc 100644 (file)
@@ -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,
index d464b9ba50afa4c4a5aaae4413eade5d39ae3b98..b234c2f33e12ff62fc3dbef2ca7504cf41dd3dc2 100644 (file)
@@ -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",
index 86fe5590d67740400154a3a987d3cc44abb2ce68..84b7cd98828b118630249d896d20a396c86049f0 100644 (file)
@@ -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
index 555c2778cfb7f9eb40c0f0f5649d6dcf4a990594..3f6e25c05ca6dc38f6ff81eb0e82f2c3ed166e3d 100644 (file)
@@ -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) &&
index dc93ce2f4c807112d91e0c21cd939050c0fb533b..b302556d568ea8af7c153ff6242822b59e9b57c1 100644 (file)
@@ -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();
 
index 031e0e3b375ffdd133e4cc3692102f8f81bc8a42..9a5ae14de81625bf15373e93a02d0ea4102e2f17 100644 (file)
@@ -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 &&
index b5785924932f5c6d9240873e4c3a065b672f9ea7..c0832a1cbff22f647152ffd8d2ed77fe48872563 100644 (file)
@@ -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));
index d0d67a57dad49220c95faebedfa4bb01b1602fc8..de45bf0cd9224148ba515feb51e73f58b270307d 100644 (file)
@@ -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) {