From 9263d18bd9655a5f64f1956d9536e76e9ec22144 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 25 Apr 2014 15:21:26 +0200 Subject: [PATCH] Optimize ZEND_CAST to avoid zval copies The scalar type casts IS_NULL, IS_BOOL, IS_LONG, IS_DOUBLE and IS_STRING will no longer require a copy when casting. A copy is now only made when casting to IS_ARRAY and IS_OBJECT, if the type doesn't already match. I tweaked the reference handling for the type-already-correct case to DEREF the zval after that check. References require a copy anyway, so they can go through the slow codepath. --- Zend/zend_vm_def.h | 86 +++++------ Zend/zend_vm_execute.h | 335 +++++++++++++++++------------------------ 2 files changed, 179 insertions(+), 242 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 812a2acaf9..5bcada45c3 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3777,73 +3777,61 @@ ZEND_VM_HANDLER(21, ZEND_CAST, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; - zval *expr, *expr_ptr; + zval *expr; zval *result = EX_VAR(opline->result.var); SAVE_OPLINE(); - expr = expr_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { - ZVAL_DEREF(expr); - } - if (Z_TYPE_P(expr) == opline->extended_value) { - ZVAL_COPY_VALUE(result, expr); - if (OP1_TYPE == IS_CONST || expr != expr_ptr) { - zval_opt_copy_ctor(result); - FREE_OP1_IF_VAR(); - } else if (OP1_TYPE == IS_CV) { - if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); - } - - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - - if (opline->extended_value != IS_STRING) { - ZVAL_COPY_VALUE(result, expr); - if (!IS_OP1_TMP_FREE()) { - zval_opt_copy_ctor(result); - } - } + expr = GET_OP1_ZVAL_PTR(BP_VAR_R); switch (opline->extended_value) { case IS_NULL: - convert_to_null(result); + ZVAL_NULL(result); break; case IS_BOOL: - convert_to_boolean(result); + ZVAL_BOOL(result, zend_is_true(expr TSRMLS_CC)); break; case IS_LONG: - convert_to_long(result); + ZVAL_LONG(result, zval_get_long(expr)); break; case IS_DOUBLE: - convert_to_double(result); + ZVAL_DOUBLE(result, zval_get_double(expr)); break; - case IS_STRING: { - zval var_copy; - int use_copy; - - zend_make_printable_zval(expr, &var_copy, &use_copy); - if (use_copy) { - ZVAL_COPY_VALUE(result, &var_copy); - if (IS_OP1_TMP_FREE()) { - FREE_OP1(); - } - } else { + case IS_STRING: + ZVAL_STR(result, zval_get_string(expr)); + break; + default: + /* If value is already of correct type, return it directly */ + if (Z_TYPE_P(expr) == opline->extended_value) { ZVAL_COPY_VALUE(result, expr); - if (!IS_OP1_TMP_FREE()) { + if (OP1_TYPE == IS_CONST) { zval_opt_copy_ctor(result); + } else if (OP1_TYPE == IS_CV) { + if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - break; - } - case IS_ARRAY: - convert_to_array(result); - break; - case IS_OBJECT: - convert_to_object(result); - break; + + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + ZVAL_DEREF(expr); + } + ZVAL_COPY_VALUE(result, expr); + if (!IS_OP1_TMP_FREE()) { + zval_opt_copy_ctor(result); + } + + if (opline->extended_value == IS_ARRAY) { + convert_to_array(result); + } else { + convert_to_object(result); + } + + FREE_OP1_IF_VAR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - FREE_OP1_IF_VAR(); + FREE_OP1(); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f09990fb49..83fa744418 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2777,71 +2777,58 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *expr, *expr_ptr; + zval *expr; zval *result = EX_VAR(opline->result.var); SAVE_OPLINE(); - expr = expr_ptr = opline->op1.zv; - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - ZVAL_DEREF(expr); - } - if (Z_TYPE_P(expr) == opline->extended_value) { - ZVAL_COPY_VALUE(result, expr); - if (IS_CONST == IS_CONST || expr != expr_ptr) { - zval_opt_copy_ctor(result); - - } else if (IS_CONST == IS_CV) { - if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); - } - - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - - if (opline->extended_value != IS_STRING) { - ZVAL_COPY_VALUE(result, expr); - if (!0) { - zval_opt_copy_ctor(result); - } - } + expr = opline->op1.zv; switch (opline->extended_value) { case IS_NULL: - convert_to_null(result); + ZVAL_NULL(result); break; case IS_BOOL: - convert_to_boolean(result); + ZVAL_BOOL(result, zend_is_true(expr TSRMLS_CC)); break; case IS_LONG: - convert_to_long(result); + ZVAL_LONG(result, zval_get_long(expr)); break; case IS_DOUBLE: - convert_to_double(result); + ZVAL_DOUBLE(result, zval_get_double(expr)); break; - case IS_STRING: { - zval var_copy; - int use_copy; - - zend_make_printable_zval(expr, &var_copy, &use_copy); - if (use_copy) { - ZVAL_COPY_VALUE(result, &var_copy); - if (0) { - - } - } else { + case IS_STRING: + ZVAL_STR(result, zval_get_string(expr)); + break; + default: + /* If value is already of correct type, return it directly */ + if (Z_TYPE_P(expr) == opline->extended_value) { ZVAL_COPY_VALUE(result, expr); - if (!0) { + if (IS_CONST == IS_CONST) { zval_opt_copy_ctor(result); + } else if (IS_CONST == IS_CV) { + if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - break; - } - case IS_ARRAY: - convert_to_array(result); - break; - case IS_OBJECT: - convert_to_object(result); - break; + + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + ZVAL_DEREF(expr); + } + ZVAL_COPY_VALUE(result, expr); + if (!0) { + zval_opt_copy_ctor(result); + } + + if (opline->extended_value == IS_ARRAY) { + convert_to_array(result); + } else { + convert_to_object(result); + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } CHECK_EXCEPTION(); @@ -7868,73 +7855,60 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *expr, *expr_ptr; + zval *expr; zval *result = EX_VAR(opline->result.var); SAVE_OPLINE(); - expr = expr_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { - ZVAL_DEREF(expr); - } - if (Z_TYPE_P(expr) == opline->extended_value) { - ZVAL_COPY_VALUE(result, expr); - if (IS_TMP_VAR == IS_CONST || expr != expr_ptr) { - zval_opt_copy_ctor(result); - - } else if (IS_TMP_VAR == IS_CV) { - if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); - } - - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - - if (opline->extended_value != IS_STRING) { - ZVAL_COPY_VALUE(result, expr); - if (!1) { - zval_opt_copy_ctor(result); - } - } + expr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); switch (opline->extended_value) { case IS_NULL: - convert_to_null(result); + ZVAL_NULL(result); break; case IS_BOOL: - convert_to_boolean(result); + ZVAL_BOOL(result, zend_is_true(expr TSRMLS_CC)); break; case IS_LONG: - convert_to_long(result); + ZVAL_LONG(result, zval_get_long(expr)); break; case IS_DOUBLE: - convert_to_double(result); + ZVAL_DOUBLE(result, zval_get_double(expr)); break; - case IS_STRING: { - zval var_copy; - int use_copy; - - zend_make_printable_zval(expr, &var_copy, &use_copy); - if (use_copy) { - ZVAL_COPY_VALUE(result, &var_copy); - if (1) { - zval_dtor(free_op1.var); - } - } else { + case IS_STRING: + ZVAL_STR(result, zval_get_string(expr)); + break; + default: + /* If value is already of correct type, return it directly */ + if (Z_TYPE_P(expr) == opline->extended_value) { ZVAL_COPY_VALUE(result, expr); - if (!1) { + if (IS_TMP_VAR == IS_CONST) { zval_opt_copy_ctor(result); + } else if (IS_TMP_VAR == IS_CV) { + if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - break; - } - case IS_ARRAY: - convert_to_array(result); - break; - case IS_OBJECT: - convert_to_object(result); - break; - } + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + ZVAL_DEREF(expr); + } + ZVAL_COPY_VALUE(result, expr); + if (!1) { + zval_opt_copy_ctor(result); + } + + if (opline->extended_value == IS_ARRAY) { + convert_to_array(result); + } else { + convert_to_object(result); + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } + zval_dtor(free_op1.var); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } @@ -13029,71 +13003,59 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *expr, *expr_ptr; + zval *expr; zval *result = EX_VAR(opline->result.var); SAVE_OPLINE(); - expr = expr_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); - if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { - ZVAL_DEREF(expr); - } - if (Z_TYPE_P(expr) == opline->extended_value) { - ZVAL_COPY_VALUE(result, expr); - if (IS_VAR == IS_CONST || expr != expr_ptr) { - zval_opt_copy_ctor(result); - zval_ptr_dtor_nogc(free_op1.var); - } else if (IS_VAR == IS_CV) { - if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); - } - - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - - if (opline->extended_value != IS_STRING) { - ZVAL_COPY_VALUE(result, expr); - if (!0) { - zval_opt_copy_ctor(result); - } - } + expr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); switch (opline->extended_value) { case IS_NULL: - convert_to_null(result); + ZVAL_NULL(result); break; case IS_BOOL: - convert_to_boolean(result); + ZVAL_BOOL(result, zend_is_true(expr TSRMLS_CC)); break; case IS_LONG: - convert_to_long(result); + ZVAL_LONG(result, zval_get_long(expr)); break; case IS_DOUBLE: - convert_to_double(result); + ZVAL_DOUBLE(result, zval_get_double(expr)); break; - case IS_STRING: { - zval var_copy; - int use_copy; - - zend_make_printable_zval(expr, &var_copy, &use_copy); - if (use_copy) { - ZVAL_COPY_VALUE(result, &var_copy); - if (0) { - zval_ptr_dtor_nogc(free_op1.var); - } - } else { + case IS_STRING: + ZVAL_STR(result, zval_get_string(expr)); + break; + default: + /* If value is already of correct type, return it directly */ + if (Z_TYPE_P(expr) == opline->extended_value) { ZVAL_COPY_VALUE(result, expr); - if (!0) { + if (IS_VAR == IS_CONST) { zval_opt_copy_ctor(result); + } else if (IS_VAR == IS_CV) { + if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - break; - } - case IS_ARRAY: - convert_to_array(result); - break; - case IS_OBJECT: - convert_to_object(result); - break; + + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + ZVAL_DEREF(expr); + } + ZVAL_COPY_VALUE(result, expr); + if (!0) { + zval_opt_copy_ctor(result); + } + + if (opline->extended_value == IS_ARRAY) { + convert_to_array(result); + } else { + convert_to_object(result); + } + + zval_ptr_dtor_nogc(free_op1.var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } zval_ptr_dtor_nogc(free_op1.var); CHECK_EXCEPTION(); @@ -29935,71 +29897,58 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *expr, *expr_ptr; + zval *expr; zval *result = EX_VAR(opline->result.var); SAVE_OPLINE(); - expr = expr_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); - if (IS_CV == IS_VAR || IS_CV == IS_CV) { - ZVAL_DEREF(expr); - } - if (Z_TYPE_P(expr) == opline->extended_value) { - ZVAL_COPY_VALUE(result, expr); - if (IS_CV == IS_CONST || expr != expr_ptr) { - zval_opt_copy_ctor(result); - - } else if (IS_CV == IS_CV) { - if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); - } - - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); - } - - if (opline->extended_value != IS_STRING) { - ZVAL_COPY_VALUE(result, expr); - if (!0) { - zval_opt_copy_ctor(result); - } - } + expr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); switch (opline->extended_value) { case IS_NULL: - convert_to_null(result); + ZVAL_NULL(result); break; case IS_BOOL: - convert_to_boolean(result); + ZVAL_BOOL(result, zend_is_true(expr TSRMLS_CC)); break; case IS_LONG: - convert_to_long(result); + ZVAL_LONG(result, zval_get_long(expr)); break; case IS_DOUBLE: - convert_to_double(result); + ZVAL_DOUBLE(result, zval_get_double(expr)); break; - case IS_STRING: { - zval var_copy; - int use_copy; - - zend_make_printable_zval(expr, &var_copy, &use_copy); - if (use_copy) { - ZVAL_COPY_VALUE(result, &var_copy); - if (0) { - - } - } else { + case IS_STRING: + ZVAL_STR(result, zval_get_string(expr)); + break; + default: + /* If value is already of correct type, return it directly */ + if (Z_TYPE_P(expr) == opline->extended_value) { ZVAL_COPY_VALUE(result, expr); - if (!0) { + if (IS_CV == IS_CONST) { zval_opt_copy_ctor(result); + } else if (IS_CV == IS_CV) { + if (Z_OPT_REFCOUNTED_P(expr)) Z_ADDREF_P(expr); } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - break; - } - case IS_ARRAY: - convert_to_array(result); - break; - case IS_OBJECT: - convert_to_object(result); - break; + + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + ZVAL_DEREF(expr); + } + ZVAL_COPY_VALUE(result, expr); + if (!0) { + zval_opt_copy_ctor(result); + } + + if (opline->extended_value == IS_ARRAY) { + convert_to_array(result); + } else { + convert_to_object(result); + } + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } CHECK_EXCEPTION(); -- 2.40.0