1
0
mirror of https://github.com/php/php-src.git synced 2026-04-18 05:21:02 +02:00

Embed zend_leave_helper() into hybrid executor to avoid call overhead.

This commit is contained in:
Dmitry Stogov
2018-06-05 11:33:19 +03:00
parent b6a2ae3a5b
commit 59c2ff2543
3 changed files with 475 additions and 21 deletions

View 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();

View 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);

View 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]);