]> granicus.if.org Git - php/commitdiff
Embed zend_leave_helper() into hybrid executor to avoid call overhead.
authorDmitry Stogov <dmitry@zend.com>
Tue, 5 Jun 2018 08:33:19 +0000 (11:33 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 5 Jun 2018 08:33:19 +0000 (11:33 +0300)
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_gen.php

index e985bb0e3ba0e04659450bd88397910302c004fd..838daea88f2685457b37192a0a74da5ac2bf91fd 100644 (file)
@@ -2364,7 +2364,7 @@ ZEND_VM_HANDLER(39, ZEND_ASSIGN_REF, VAR|CV, VAR|CV, SRC)
        ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
 }
 
-ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
+ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
 {
        zend_execute_data *old_execute_data;
        uint32_t call_info = EX_CALL_INFO();
index f5cb64a9ad8e771cc60f6cf074945aad8bc7b799..5ab8a863519a549ff972ae873b46b9d0566929b5 100644 (file)
@@ -50397,6 +50397,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDL
 }
 
 
+#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
+# undef ZEND_VM_TAIL_CALL
+# undef ZEND_VM_CONTINUE
+# undef ZEND_VM_RETURN
+
+# define ZEND_VM_TAIL_CALL(call) call; ZEND_VM_CONTINUE()
+# define ZEND_VM_CONTINUE()      HYBRID_NEXT()
+# define ZEND_VM_RETURN()        goto HYBRID_HALT_LABEL
+#endif
+
+
 ZEND_API void execute_ex(zend_execute_data *ex)
 {
        DCL_OPLINE
@@ -54380,6 +54391,123 @@ ZEND_API void execute_ex(zend_execute_data *ex)
 #endif
 #endif
 #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
+zend_leave_helper_SPEC_LABEL:
+{
+       zend_execute_data *old_execute_data;
+       uint32_t call_info = EX_CALL_INFO();
+
+       if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
+               i_free_compiled_variables(execute_data);
+
+               EG(current_execute_data) = EX(prev_execute_data);
+               if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
+                       zend_object *object = Z_OBJ(execute_data->This);
+#if 0
+                       if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
+#else
+                       if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
+#endif
+                               GC_DELREF(object);
+                               zend_object_store_ctor_failed(object);
+                       }
+                       OBJ_RELEASE(object);
+               } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+                       OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
+               }
+               EG(vm_stack_top) = (zval*)execute_data;
+               execute_data = EX(prev_execute_data);
+
+               if (UNEXPECTED(EG(exception) != NULL)) {
+                       zend_rethrow_exception(execute_data);
+                       HANDLE_EXCEPTION_LEAVE();
+               }
+
+               LOAD_NEXT_OPLINE();
+               ZEND_VM_LEAVE();
+       } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
+               i_free_compiled_variables(execute_data);
+
+               if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+                       zend_clean_and_cache_symbol_table(EX(symbol_table));
+               }
+               EG(current_execute_data) = EX(prev_execute_data);
+               if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
+                       zend_object *object = Z_OBJ(execute_data->This);
+#if 0
+                       if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) {
+#else
+                       if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) {
+#endif
+                               GC_DELREF(object);
+                               zend_object_store_ctor_failed(object);
+                       }
+                       OBJ_RELEASE(object);
+               } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+                       OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
+               }
+
+               zend_vm_stack_free_extra_args_ex(call_info, execute_data);
+               old_execute_data = execute_data;
+               execute_data = EX(prev_execute_data);
+               zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
+
+               if (UNEXPECTED(EG(exception) != NULL)) {
+                       zend_rethrow_exception(execute_data);
+                       HANDLE_EXCEPTION_LEAVE();
+               }
+
+               LOAD_NEXT_OPLINE();
+               ZEND_VM_LEAVE();
+       } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) {
+               zend_detach_symbol_table(execute_data);
+               destroy_op_array(&EX(func)->op_array);
+               efree_size(EX(func), sizeof(zend_op_array));
+               old_execute_data = execute_data;
+               execute_data = EG(current_execute_data) = EX(prev_execute_data);
+               zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
+
+               zend_attach_symbol_table(execute_data);
+               if (UNEXPECTED(EG(exception) != NULL)) {
+                       zend_rethrow_exception(execute_data);
+                       HANDLE_EXCEPTION_LEAVE();
+               }
+
+               LOAD_NEXT_OPLINE();
+               ZEND_VM_LEAVE();
+       } else {
+               if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) {
+                       i_free_compiled_variables(execute_data);
+                       if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
+                               if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+                                       zend_clean_and_cache_symbol_table(EX(symbol_table));
+                               }
+                               zend_vm_stack_free_extra_args_ex(call_info, execute_data);
+                       }
+                       EG(current_execute_data) = EX(prev_execute_data);
+                       if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
+                               OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
+                       }
+                       ZEND_VM_RETURN();
+               } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ {
+                       zend_array *symbol_table = EX(symbol_table);
+
+                       zend_detach_symbol_table(execute_data);
+                       old_execute_data = EX(prev_execute_data);
+                       while (old_execute_data) {
+                               if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
+                                       if (old_execute_data->symbol_table == symbol_table) {
+                                               zend_attach_symbol_table(old_execute_data);
+                                       }
+                                       break;
+                               }
+                               old_execute_data = old_execute_data->prev_execute_data;
+                       }
+                       EG(current_execute_data) = EX(prev_execute_data);
+                       ZEND_VM_RETURN();
+               }
+       }
+}
+
                        HYBRID_CASE(ZEND_JMP_SPEC):
                                VM_TRACE(ZEND_JMP_SPEC)
                                ZEND_JMP_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -54598,8 +54726,70 @@ ZEND_API void execute_ex(zend_execute_data *ex)
                                HYBRID_BREAK();
                        HYBRID_CASE(ZEND_RETURN_SPEC_CONST):
                                VM_TRACE(ZEND_RETURN_SPEC_CONST)
-                               ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
-                               HYBRID_BREAK();
+{
+       USE_OPLINE
+       zval *retval_ptr;
+       zval *return_value;
+       zend_free_op free_op1;
+
+       retval_ptr = RT_CONSTANT(opline, opline->op1);
+       return_value = EX(return_value);
+       if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) {
+               SAVE_OPLINE();
+               retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R);
+               if (return_value) {
+                       ZVAL_NULL(return_value);
+               }
+       } else if (!return_value) {
+               if (IS_CONST & (IS_VAR|IS_TMP_VAR)) {
+                       if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) {
+                               SAVE_OPLINE();
+                               zval_dtor_func(Z_COUNTED_P(free_op1));
+                       }
+               }
+       } else {
+               if ((IS_CONST & (IS_CONST|IS_TMP_VAR))) {
+                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       if (IS_CONST == IS_CONST) {
+                               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+                                       Z_ADDREF_P(return_value);
+                               }
+                       }
+               } else if (IS_CONST == IS_CV) {
+                       if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                               if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+                                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                                       if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+                                               ZVAL_NULL(retval_ptr);
+                                       } else {
+                                               Z_ADDREF_P(return_value);
+                                       }
+                               } else {
+                                       retval_ptr = Z_REFVAL_P(retval_ptr);
+                                       ZVAL_COPY(return_value, retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               } else /* if (IS_CONST == IS_VAR) */ {
+                       if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+                               zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+
+                               retval_ptr = Z_REFVAL_P(retval_ptr);
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
+                                       efree_size(ref, sizeof(zend_reference));
+                               } else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                                       Z_ADDREF_P(retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               }
+       }
+       goto zend_leave_helper_SPEC_LABEL;
+}
+
                        HYBRID_CASE(ZEND_RETURN_BY_REF_SPEC_CONST):
                                VM_TRACE(ZEND_RETURN_BY_REF_SPEC_CONST)
                                ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -56190,8 +56380,70 @@ ZEND_API void execute_ex(zend_execute_data *ex)
                                HYBRID_BREAK();
                        HYBRID_CASE(ZEND_RETURN_SPEC_TMP):
                                VM_TRACE(ZEND_RETURN_SPEC_TMP)
-                               ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
-                               HYBRID_BREAK();
+{
+       USE_OPLINE
+       zval *retval_ptr;
+       zval *return_value;
+       zend_free_op free_op1;
+
+       retval_ptr = _get_zval_ptr_tmp(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
+       return_value = EX(return_value);
+       if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) {
+               SAVE_OPLINE();
+               retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R);
+               if (return_value) {
+                       ZVAL_NULL(return_value);
+               }
+       } else if (!return_value) {
+               if (IS_TMP_VAR & (IS_VAR|IS_TMP_VAR)) {
+                       if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) {
+                               SAVE_OPLINE();
+                               zval_dtor_func(Z_COUNTED_P(free_op1));
+                       }
+               }
+       } else {
+               if ((IS_TMP_VAR & (IS_CONST|IS_TMP_VAR))) {
+                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       if (IS_TMP_VAR == IS_CONST) {
+                               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+                                       Z_ADDREF_P(return_value);
+                               }
+                       }
+               } else if (IS_TMP_VAR == IS_CV) {
+                       if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                               if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+                                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                                       if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+                                               ZVAL_NULL(retval_ptr);
+                                       } else {
+                                               Z_ADDREF_P(return_value);
+                                       }
+                               } else {
+                                       retval_ptr = Z_REFVAL_P(retval_ptr);
+                                       ZVAL_COPY(return_value, retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               } else /* if (IS_TMP_VAR == IS_VAR) */ {
+                       if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+                               zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+
+                               retval_ptr = Z_REFVAL_P(retval_ptr);
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
+                                       efree_size(ref, sizeof(zend_reference));
+                               } else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                                       Z_ADDREF_P(retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               }
+       }
+       goto zend_leave_helper_SPEC_LABEL;
+}
+
                        HYBRID_CASE(ZEND_RETURN_BY_REF_SPEC_TMP):
                                VM_TRACE(ZEND_RETURN_BY_REF_SPEC_TMP)
                                ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -56422,8 +56674,70 @@ ZEND_API void execute_ex(zend_execute_data *ex)
                                HYBRID_BREAK();
                        HYBRID_CASE(ZEND_RETURN_SPEC_VAR):
                                VM_TRACE(ZEND_RETURN_SPEC_VAR)
-                               ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
-                               HYBRID_BREAK();
+{
+       USE_OPLINE
+       zval *retval_ptr;
+       zval *return_value;
+       zend_free_op free_op1;
+
+       retval_ptr = _get_zval_ptr_var(opline->op1.var, &free_op1 EXECUTE_DATA_CC);
+       return_value = EX(return_value);
+       if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) {
+               SAVE_OPLINE();
+               retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R);
+               if (return_value) {
+                       ZVAL_NULL(return_value);
+               }
+       } else if (!return_value) {
+               if (IS_VAR & (IS_VAR|IS_TMP_VAR)) {
+                       if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) {
+                               SAVE_OPLINE();
+                               zval_dtor_func(Z_COUNTED_P(free_op1));
+                       }
+               }
+       } else {
+               if ((IS_VAR & (IS_CONST|IS_TMP_VAR))) {
+                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       if (IS_VAR == IS_CONST) {
+                               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+                                       Z_ADDREF_P(return_value);
+                               }
+                       }
+               } else if (IS_VAR == IS_CV) {
+                       if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                               if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+                                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                                       if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+                                               ZVAL_NULL(retval_ptr);
+                                       } else {
+                                               Z_ADDREF_P(return_value);
+                                       }
+                               } else {
+                                       retval_ptr = Z_REFVAL_P(retval_ptr);
+                                       ZVAL_COPY(return_value, retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               } else /* if (IS_VAR == IS_VAR) */ {
+                       if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+                               zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+
+                               retval_ptr = Z_REFVAL_P(retval_ptr);
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
+                                       efree_size(ref, sizeof(zend_reference));
+                               } else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                                       Z_ADDREF_P(retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               }
+       }
+       goto zend_leave_helper_SPEC_LABEL;
+}
+
                        HYBRID_CASE(ZEND_RETURN_BY_REF_SPEC_VAR):
                                VM_TRACE(ZEND_RETURN_BY_REF_SPEC_VAR)
                                ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -58010,8 +58324,70 @@ ZEND_API void execute_ex(zend_execute_data *ex)
                                HYBRID_BREAK();
                        HYBRID_CASE(ZEND_RETURN_SPEC_CV):
                                VM_TRACE(ZEND_RETURN_SPEC_CV)
-                               ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
-                               HYBRID_BREAK();
+{
+       USE_OPLINE
+       zval *retval_ptr;
+       zval *return_value;
+       zend_free_op free_op1;
+
+       retval_ptr = _get_zval_ptr_cv_undef(opline->op1.var EXECUTE_DATA_CC);
+       return_value = EX(return_value);
+       if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(retval_ptr) == IS_UNDEF)) {
+               SAVE_OPLINE();
+               retval_ptr = GET_OP1_UNDEF_CV(retval_ptr, BP_VAR_R);
+               if (return_value) {
+                       ZVAL_NULL(return_value);
+               }
+       } else if (!return_value) {
+               if (IS_CV & (IS_VAR|IS_TMP_VAR)) {
+                       if (Z_REFCOUNTED_P(free_op1) && !Z_DELREF_P(free_op1)) {
+                               SAVE_OPLINE();
+                               zval_dtor_func(Z_COUNTED_P(free_op1));
+                       }
+               }
+       } else {
+               if ((IS_CV & (IS_CONST|IS_TMP_VAR))) {
+                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       if (IS_CV == IS_CONST) {
+                               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(return_value))) {
+                                       Z_ADDREF_P(return_value);
+                               }
+                       }
+               } else if (IS_CV == IS_CV) {
+                       if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                               if (EXPECTED(!Z_OPT_ISREF_P(retval_ptr))) {
+                                       ZVAL_COPY_VALUE(return_value, retval_ptr);
+                                       if (EXPECTED(!(EX_CALL_INFO() & ZEND_CALL_CODE))) {
+                                               ZVAL_NULL(retval_ptr);
+                                       } else {
+                                               Z_ADDREF_P(return_value);
+                                       }
+                               } else {
+                                       retval_ptr = Z_REFVAL_P(retval_ptr);
+                                       ZVAL_COPY(return_value, retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               } else /* if (IS_CV == IS_VAR) */ {
+                       if (UNEXPECTED(Z_ISREF_P(retval_ptr))) {
+                               zend_refcounted *ref = Z_COUNTED_P(retval_ptr);
+
+                               retval_ptr = Z_REFVAL_P(retval_ptr);
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
+                                       efree_size(ref, sizeof(zend_reference));
+                               } else if (Z_OPT_REFCOUNTED_P(retval_ptr)) {
+                                       Z_ADDREF_P(retval_ptr);
+                               }
+                       } else {
+                               ZVAL_COPY_VALUE(return_value, retval_ptr);
+                       }
+               }
+       }
+       goto zend_leave_helper_SPEC_LABEL;
+}
+
                        HYBRID_CASE(ZEND_RETURN_BY_REF_SPEC_CV):
                                VM_TRACE(ZEND_RETURN_BY_REF_SPEC_CV)
                                ZEND_RETURN_BY_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
index a3db82acdf9094a009b8d4220a3f23e18b7c2850..20672c66c5a72434b28438d1b9fc1e57603df3a9 100644 (file)
@@ -615,6 +615,16 @@ function out_line($f) {
        ++$line_no;
 }
 
+function is_hot_helper($name) {
+       global $helpers;
+
+       if (isset($helpers[$name]["hot"])) {
+               return $helpers[$name]["hot"];
+       } else {
+               return false;
+       }
+}
+
 // Returns name of specialized helper
 function helper_name($name, $spec, $op1, $op2, $extra_spec) {
        global $prefix, $helpers;
@@ -874,6 +884,41 @@ function gen_code($f, $spec, $kind, $export, $code, $op1, $op2, $name, $extra_sp
        }
        // Updating code according to selected threading model
        switch($kind) {
+               case ZEND_VM_KIND_HYBRID:
+                       $code = preg_replace_callback(
+                               array(
+                                       "/EXECUTE_DATA(?=[^_])/m",
+                                       "/ZEND_VM_DISPATCH_TO_HANDLER\(\s*([A-Z_]*)\s*\)/m",
+                                       "/ZEND_VM_DISPATCH_TO_HELPER\(\s*([A-Za-z_]*)\s*(,[^)]*)?\)/m",
+                               ),
+                               function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec) {
+                                       if (strncasecmp($matches[0], "EXECUTE_DATA", strlen("EXECUTE_DATA")) == 0) {
+                                               return "execute_data";
+                                       } else if (strncasecmp($matches[0], "ZEND_VM_DISPATCH_TO_HANDLER", strlen("ZEND_VM_DISPATCH_TO_HANDLER")) == 0) {
+                                               global $opcodes, $opnames;
+
+                                               $name = $matches[1];
+                                               $opcode = $opcodes[$opnames[$name]];
+                                               if (is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec)) {
+                                                       return "goto " . opcode_name($name, $spec, $op1, $op2) . "_LABEL";
+                                               } else {
+                                                       return "ZEND_VM_TAIL_CALL(" . opcode_name($name, $spec, $op1, $op2) . "_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
+                                               }
+                                       } else {
+                                               // ZEND_VM_DISPATCH_TO_HELPER
+                                               if (isset($matches[2])) {
+                                                       // extra args
+                                                       $args = substr(preg_replace("/,\s*[A-Za-z_]*\s*,\s*([^,)\s]*)\s*/", ", $1", $matches[2]), 2);
+                                                       return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(" . $args. " ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC))";
+                                               }
+                                               if (is_hot_helper($matches[1])) {
+                                                       return "goto " . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "_LABEL";
+                                               }
+                                               return "ZEND_VM_TAIL_CALL(" . helper_name($matches[1], $spec, $op1, $op2, $extra_spec) . "(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))";
+                                       }
+                               },
+                               $code);
+                       break;
                case ZEND_VM_KIND_CALL:
                        $code = preg_replace_callback(
                                array(
@@ -1048,6 +1093,11 @@ function is_cold_handler($hot, $op1, $op2, $extra_spec) {
        }
 }
 
+function is_inline_hybrid_handler($name, $hot, $op1, $op2, $extra_spec) {
+       return $name == "ZEND_RETURN";
+       //return $hot && is_hot_handler($hot, $op1, $op2, $extra_spec);
+}
+
 // Generates opcode handler
 function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno, $opcode, $extra_spec = null, &$switch_labels = array()) {
        global $definition_file, $prefix, $opnames, $gen_order;
@@ -1064,11 +1114,22 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno,
        $spec_name = $name.($spec?"_SPEC":"").$prefix[$op1].$prefix[$op2].($spec?extra_spec_name($extra_spec):"");
        switch($kind) {
                case ZEND_VM_KIND_HYBRID:
-                       $code =
-                                 "\t\t\tHYBRID_CASE({$spec_name}):\n"
-                               . "\t\t\t\tVM_TRACE($spec_name)\n"
-                               . "\t\t\t\t{$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"
-                               . "\t\t\t\tHYBRID_BREAK();\n";
+                       if (is_inline_hybrid_handler($name, $opcode["hot"], $op1, $op2, $extra_spec)) {
+                               $out = fopen('php://memory', 'w+');
+                               gen_code($out, $spec, $kind, 0, $code, $op1, $op2, $name, $extra_spec);
+                               rewind($out);
+                               $code =
+                                         "\t\t\tHYBRID_CASE({$spec_name}):\n"
+                                       . "\t\t\t\tVM_TRACE($spec_name)\n"
+                                       . stream_get_contents($out);
+                               fclose($out);
+                       } else {
+                               $code =
+                                         "\t\t\tHYBRID_CASE({$spec_name}):\n"
+                                       . "\t\t\t\tVM_TRACE($spec_name)\n"
+                                       . "\t\t\t\t{$spec_name}_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"
+                                       . "\t\t\t\tHYBRID_BREAK();\n";
+                       }
                        if (is_array($gen_order)) {
                                $gen_order[$spec_name] = $code;
                        } else {
@@ -1109,10 +1170,10 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno,
 }
 
 // Generates helper
-function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno, $inline, $cold, $extra_spec = null) {
+function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno, $inline, $cold = false, $hot = false, $extra_spec = null) {
        global $definition_file, $prefix;
 
-       if ($kind == ZEND_VM_KIND_HYBRID) {
+       if ($kind == ZEND_VM_KIND_HYBRID && !$hot) {
                return;
        }
 
@@ -1128,6 +1189,9 @@ function gen_helper($f, $spec, $kind, $name, $op1, $op2, $param, $code, $lineno,
 
        // Generate helper's entry point according to selected threading model
        switch($kind) {
+               case ZEND_VM_KIND_HYBRID:
+                       out($f, $spec_name . "_LABEL:\n");
+                       break;
                case ZEND_VM_KIND_CALL:
                        if ($inline) {
                                $zend_attributes = " zend_always_inline";
@@ -1642,7 +1706,7 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array())
                                                        if (isset($helpers[$num]["op1"][$op1]) &&
                                                            isset($helpers[$num]["op2"][$op2])) {
                                                          // Generate helper code
-                                                               gen_helper($f, 1, $kind, $num, $op1, $op2, $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"], $helpers[$num]["cold"], $extra_spec);
+                                                               gen_helper($f, 1, $kind, $num, $op1, $op2, $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"], $helpers[$num]["cold"], $helpers[$num]["hot"], $extra_spec);
                                                        }
                                                }
                                        } else {
@@ -1666,7 +1730,7 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array())
                        } else if (isset($dsc["helper"])) {
                                $num = $dsc["helper"];
                                // Generate helper code
-                               gen_helper($f, 0, $kind, $num, "ANY", "ANY", $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"]);
+                               gen_helper($f, 0, $kind, $num, "ANY", "ANY", $helpers[$num]["param"], $helpers[$num]["code"], $lineno, $helpers[$num]["inline"], $helpers[$num]["cold"], $helpers[$num]["hot"]);
                        } else {
                                var_dump($dsc);
                                die("??? $kind:$num\n");
@@ -1951,6 +2015,18 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
                                        }
                                        if ($kind == ZEND_VM_KIND_HYBRID) {
                                                gen_executor_code($f, $spec, ZEND_VM_KIND_CALL, $m[1]);
+                                               out($f,"\n");
+                                               out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n");
+                                               out($f,"# undef ZEND_VM_TAIL_CALL\n");
+                                               out($f,"# undef ZEND_VM_CONTINUE\n");
+                                               out($f,"# undef ZEND_VM_RETURN\n");
+//                                             out($f,"# undef ZEND_VM_INTERRUPT\n");
+                                               out($f,"\n");
+                                               out($f,"# define ZEND_VM_TAIL_CALL(call) call; ZEND_VM_CONTINUE()\n");
+                                               out($f,"# define ZEND_VM_CONTINUE()      HYBRID_NEXT()\n");
+                                               out($f,"# define ZEND_VM_RETURN()        goto HYBRID_HALT_LABEL\n");
+//                                             out($f,"# define ZEND_VM_INTERRUPT()     goto zend_interrupt_helper_SPEC_LABEL\n");
+                                               out($f,"#endif\n\n");
                                        }
                                        break;
                                case "EXECUTOR_NAME":
@@ -2355,16 +2431,18 @@ function gen_vm($def, $skel) {
                        $list[$lineno] = array("handler"=>$handler);
                } else if (strpos($line,"ZEND_VM_HELPER(") === 0 ||
                           strpos($line,"ZEND_VM_INLINE_HELPER(") === 0 ||
-                          strpos($line,"ZEND_VM_COLD_HELPER(") === 0) {
+                          strpos($line,"ZEND_VM_COLD_HELPER(") === 0 ||
+                          strpos($line,"ZEND_VM_HOT_HELPER(") === 0) {
                  // Parsing helper's definition
                        if (preg_match(
-                                       "/^ZEND_VM(_INLINE|_COLD)?_HELPER\(\s*([A-Za-z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(?:,\s*SPEC\(([A-Z_|=,]+)\)\s*)?(?:,\s*([^)]*)\s*)?\)/",
+                                       "/^ZEND_VM(_INLINE|_COLD|_HOT)?_HELPER\(\s*([A-Za-z_]+)\s*,\s*([A-Z_|]+)\s*,\s*([A-Z_|]+)\s*(?:,\s*SPEC\(([A-Z_|=,]+)\)\s*)?(?:,\s*([^)]*)\s*)?\)/",
                                        $line,
                                        $m) == 0) {
                                die("ERROR ($def:$lineno): Invalid ZEND_VM_HELPER definition.\n");
                        }
                        $inline = !empty($m[1]) && $m[1] === "_INLINE";
                        $cold   = !empty($m[1]) && $m[1] === "_COLD";
+                       $hot    = !empty($m[1]) && $m[1] === "_HOT";
                        $helper = $m[2];
                        $op1    = parse_operand_spec($def, $lineno, $m[3], $flags1);
                        $op2    = parse_operand_spec($def, $lineno, $m[4], $flags2);
@@ -2381,7 +2459,7 @@ function gen_vm($def, $skel) {
                                }
                        }
 
-                       $helpers[$helper] = array("op1"=>$op1,"op2"=>$op2,"param"=>$param,"code"=>"","inline"=>$inline,"cold"=>$cold);
+                       $helpers[$helper] = array("op1"=>$op1,"op2"=>$op2,"param"=>$param,"code"=>"","inline"=>$inline,"cold"=>$cold,"hot"=>$hot);
 
                        if (!empty($m[5])) {
                                $helpers[$helper]["spec"] = parse_spec_rules($def, $lineno, $m[5]);