]> granicus.if.org Git - php/commitdiff
Optimize ZEND_CAST to avoid zval copies
authorNikita Popov <nikic@php.net>
Fri, 25 Apr 2014 13:21:26 +0000 (15:21 +0200)
committerNikita Popov <nikic@php.net>
Fri, 25 Apr 2014 21:21:05 +0000 (23:21 +0200)
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
Zend/zend_vm_execute.h

index 812a2acaf99e532da820d4c37e56b61b7b5c256f..5bcada45c3ceb946ef880a7038c10b2206e8e876 100644 (file)
@@ -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();
 }
index f09990fb49f42dd7c3c04ae2c78eb37ec356b26f..83fa74441850ec5834c7524edabe3e99dae04c3e 100644 (file)
@@ -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();