Hi,

Recently I noticed that reading of string offset is performed in two steps. At first special string_offset variant of temporary_variable is created in zend_fetch_dimension_address_read() and then the real string value is created in _get_zval_ptr_var_string_offset().

I think we can create the real string in the first place. This makes 50% speed-up on string offset reading operation and allows to eliminate some checks and conditional brunches in VM.

The patch is attached (don't forget to regenerate zend_vm_execute.h to test it). However it changes behavior in one bogus case. The following code now will emit "b" (currently it generates a fatal error - cannot use string offset as an array).

$str = "abs";
var_dumo($str[1][0]);

I think it's not a problem at all.
"b" makes sense because "abs"[1] -> "b" and "b"[0] -> "b".

I'm going to commit the patch in case of no objections.

Thanks. Dmitry.
Index: Zend/zend_execute.c
===================================================================
--- Zend/zend_execute.c (revision 301262)
+++ Zend/zend_execute.c (working copy)
@@ -177,44 +177,14 @@
        return should_free->var = &T(var).tmp_var;
 }
 
-static zval *_get_zval_ptr_var_string_offset(zend_uint var, const 
temp_variable *Ts, zend_free_op *should_free TSRMLS_DC)
+static zend_always_inline zval *_get_zval_ptr_var(zend_uint var, const 
temp_variable *Ts, zend_free_op *should_free TSRMLS_DC)
 {
-       temp_variable *T = &T(var);
-       zval *str = T->str_offset.str;
-       zval *ptr;
+       zval *ptr = T(var).var.ptr;
 
-       /* string offset */
-       ALLOC_ZVAL(ptr);
-       T->str_offset.ptr = ptr;
-       should_free->var = ptr;
-
-       if (T->str_offset.str->type != IS_STRING
-               || ((int)T->str_offset.offset < 0)
-               || (T->str_offset.str->value.str.len <= 
(int)T->str_offset.offset)) {
-               ptr->value.str.val = STR_EMPTY_ALLOC();
-               ptr->value.str.len = 0;
-       } else {
-               ptr->value.str.val = estrndup(str->value.str.val + 
T->str_offset.offset, 1);
-               ptr->value.str.len = 1;
-       }
-       PZVAL_UNLOCK_FREE(str);
-       Z_SET_REFCOUNT_P(ptr, 1);
-       Z_SET_ISREF_P(ptr);
-       ptr->type = IS_STRING;
+       PZVAL_UNLOCK(ptr, should_free);
        return ptr;
 }
 
-static zend_always_inline zval *_get_zval_ptr_var(zend_uint var, const 
temp_variable *Ts, zend_free_op *should_free TSRMLS_DC)
-{
-       zval *ptr = T(var).var.ptr;
-       if (EXPECTED(ptr != NULL)) {
-               PZVAL_UNLOCK(ptr, should_free);
-               return ptr;
-       } else {
-               return _get_zval_ptr_var_string_offset(var, Ts, should_free 
TSRMLS_CC);
-       }
-}
-
 static zend_never_inline zval **_get_zval_cv_lookup(zval ***ptr, zend_uint 
var, int type TSRMLS_DC)
 {
        zend_compiled_variable *cv = &CV_DEF_OF(var);
@@ -1286,14 +1256,23 @@
                                        dim = &tmp;
                                }
                                if (result) {
+                                       zval *ptr;
+
+                                       ALLOC_ZVAL(ptr);
+                                       INIT_PZVAL(ptr);
+                                       Z_TYPE_P(ptr) = IS_STRING;
+
                                        if (Z_LVAL_P(dim) < 0 || 
Z_STRLEN_P(container) <= Z_LVAL_P(dim)) {
                                                zend_error(E_NOTICE, 
"Uninitialized string offset: %ld", Z_LVAL_P(dim));
+                                               Z_STRVAL_P(ptr) = 
STR_EMPTY_ALLOC();
+                                               Z_STRLEN_P(ptr) = 0;
+                                       } else {
+                                               Z_STRVAL_P(ptr) = 
(char*)emalloc(2);
+                                               Z_STRVAL_P(ptr)[0] = 
Z_STRVAL_P(container)[Z_LVAL_P(dim)];
+                                               Z_STRVAL_P(ptr)[1] = 0;
+                                               Z_STRLEN_P(ptr) = 1;            
                                
                                        }
-                                       result->str_offset.str = container;
-                                       PZVAL_LOCK(container);
-                                       result->str_offset.offset = 
Z_LVAL_P(dim);
-                                       result->var.ptr_ptr = NULL;
-                                       result->var.ptr = NULL;
+                                       AI_SET_PTR(result, ptr);
                                }
                                return;
                        }
Index: Zend/micro_bench.php
===================================================================
--- Zend/micro_bench.php        (revision 301262)
+++ Zend/micro_bench.php        (working copy)
@@ -188,6 +188,20 @@
        }
 }
 
+function read_hash($n) {
+       $hash = array('test' => 0);
+       for ($i = 0; $i < $n; ++$i) {
+               $x = $hash['test'];
+       }
+}
+
+function read_str_offset($n) {
+       $str = "test";
+       for ($i = 0; $i < $n; ++$i) {
+               $x = $str[1];
+       }
+}
+
 /*****/
 
 function empty_loop($n) {
@@ -300,4 +314,8 @@
 $t = end_test($t, '$x = $_GET', $overhead);
 read_global_var(N);
 $t = end_test($t, '$x = $GLOBALS[\'v\']', $overhead);
+read_hash(N);
+$t = end_test($t, '$x = $hash[\'v\']', $overhead);
+read_str_offset(N);
+$t = end_test($t, '$x = $str[0]', $overhead);
 total($t0, "Total");
Index: Zend/zend_vm_def.h
===================================================================
--- Zend/zend_vm_def.h  (revision 301262)
+++ Zend/zend_vm_def.h  (working copy)
@@ -1182,9 +1182,6 @@
                PZVAL_LOCK(*EX_T(opline->op1.var).var.ptr_ptr);
        }
        container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R);
-       if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
-               zend_error_noreturn(E_ERROR, "Cannot use string offset as an 
array");
-       }
        
zend_fetch_dimension_address_read(!RETURN_VALUE_USED(opline)?NULL:&EX_T(opline->result.var),
 container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE, BP_VAR_R TSRMLS_CC);
        FREE_OP2();
        FREE_OP1_VAR_PTR();
@@ -1256,10 +1253,6 @@
 
        SAVE_OPLINE();
        container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_IS);
-
-       if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
-               zend_error_noreturn(E_ERROR, "Cannot use string offset as an 
array");
-       }
        zend_fetch_dimension_address_read(&EX_T(opline->result.var), container, 
GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE, BP_VAR_IS TSRMLS_CC);
        FREE_OP2();
        FREE_OP1_VAR_PTR();
@@ -1289,9 +1282,6 @@
                        zend_error_noreturn(E_ERROR, "Cannot use [] for 
reading");
                }
                container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R);
-               if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
-                       zend_error_noreturn(E_ERROR, "Cannot use string offset 
as an array");
-               }
                zend_fetch_dimension_address_read(&EX_T(opline->result.var), 
container, GET_OP2_ZVAL_PTR(BP_VAR_R), OP2_TYPE, BP_VAR_R TSRMLS_CC);
        }
        FREE_OP2();
@@ -3257,33 +3247,17 @@
 ZEND_VM_HANDLER(48, ZEND_CASE, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV)
 {
        USE_OPLINE
-       int switch_expr_is_overloaded=0;
        zend_free_op free_op1, free_op2;
 
        SAVE_OPLINE();
        if (OP1_TYPE==IS_VAR) {
-               if (EX_T(opline->op1.var).var.ptr_ptr) {
-                       PZVAL_LOCK(EX_T(opline->op1.var).var.ptr);
-               } else {
-                       switch_expr_is_overloaded = 1;
-                       Z_ADDREF_P(EX_T(opline->op1.var).str_offset.str);
-               }
+               PZVAL_LOCK(EX_T(opline->op1.var).var.ptr);
        }
        is_equal_function(&EX_T(opline->result.var).tmp_var,
                                 GET_OP1_ZVAL_PTR(BP_VAR_R),
                                 GET_OP2_ZVAL_PTR(BP_VAR_R) TSRMLS_CC);
 
        FREE_OP2();
-       if (switch_expr_is_overloaded) {
-               /* We only free op1 if this is a string offset,
-                * Since if it is a TMP_VAR, it'll be reused by
-                * other CASE opcodes (whereas string offsets
-                * are allocated at each get_zval_ptr())
-                */
-               FREE_OP1();
-               EX_T(opline->op1.var).var.ptr_ptr = NULL;
-               EX_T(opline->op1.var).var.ptr = NULL;
-       }
        CHECK_EXCEPTION();
        ZEND_VM_NEXT_OPCODE();
 }
@@ -3358,7 +3332,6 @@
        obj = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R);
 
        if (OP1_TYPE == IS_CONST ||
-           (OP1_TYPE == IS_VAR && UNEXPECTED(obj == NULL)) ||
            UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT)) {
                zend_error_noreturn(E_ERROR, "__clone method called on 
non-object");
        }
@@ -4383,129 +4356,126 @@
 ZEND_VM_HELPER_EX(zend_isset_isempty_dim_prop_obj_handler, VAR|UNUSED|CV, 
CONST|TMP|VAR|CV, int prop_dim)
 {
        USE_OPLINE
-       zend_free_op free_op1;
+       zend_free_op free_op1, free_op2;
        zval **container;
        zval **value = NULL;
        int result = 0;
        ulong hval;
        long index;
+       zval *offset;
 
        SAVE_OPLINE();
        container = GET_OP1_OBJ_ZVAL_PTR_PTR(BP_VAR_IS);
        
-       if (OP1_TYPE != IS_VAR || container) {
-               zend_free_op free_op2;
-               zval *offset = GET_OP2_ZVAL_PTR(BP_VAR_R);
+       offset = GET_OP2_ZVAL_PTR(BP_VAR_R);
 
-               if (Z_TYPE_PP(container) == IS_ARRAY && !prop_dim) {
-                       HashTable *ht;
-                       int isset = 0;
+       if (Z_TYPE_PP(container) == IS_ARRAY && !prop_dim) {
+               HashTable *ht;
+               int isset = 0;
 
-                       ht = Z_ARRVAL_PP(container);
+               ht = Z_ARRVAL_PP(container);
 
-                       switch (Z_TYPE_P(offset)) {
-                               case IS_DOUBLE:
-                                       index = 
zend_dval_to_lval(Z_DVAL_P(offset));
-                                       ZEND_VM_C_GOTO(num_index_prop);
-                               case IS_RESOURCE:
-                               case IS_BOOL:
-                               case IS_LONG:
-                                       index = Z_LVAL_P(offset);
+               switch (Z_TYPE_P(offset)) {
+                       case IS_DOUBLE:
+                               index = zend_dval_to_lval(Z_DVAL_P(offset));
+                               ZEND_VM_C_GOTO(num_index_prop);
+                       case IS_RESOURCE:
+                       case IS_BOOL:
+                       case IS_LONG:
+                               index = Z_LVAL_P(offset);
 ZEND_VM_C_LABEL(num_index_prop):
-                                       if (zend_hash_index_find(ht, index, 
(void **) &value) == SUCCESS) {
-                                               isset = 1;
+                               if (zend_hash_index_find(ht, index, (void **) 
&value) == SUCCESS) {
+                                       isset = 1;
+                               }
+                               break;
+                       case IS_STRING:
+                               if (OP2_TYPE == IS_CONST) {
+                                       hval = Z_HASH_P(offset);
+                               } else {
+                                       if (!prop_dim) {
+                                               
ZEND_HANDLE_NUMERIC_EX(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, index, 
ZEND_VM_C_GOTO(num_index_prop));
                                        }
-                                       break;
-                               case IS_STRING:
-                                       if (OP2_TYPE == IS_CONST) {
-                                               hval = Z_HASH_P(offset);
+                                       if (IS_INTERNED(Z_STRVAL_P(offset))) {
+                                               hval = 
INTERNED_HASH(Z_STRVAL_P(offset));
                                        } else {
-                                               if (!prop_dim) {
-                                                       
ZEND_HANDLE_NUMERIC_EX(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, index, 
ZEND_VM_C_GOTO(num_index_prop));
-                                               }
-                                               if 
(IS_INTERNED(Z_STRVAL_P(offset))) {
-                                                       hval = 
INTERNED_HASH(Z_STRVAL_P(offset));
-                                               } else {
-                                                       hval = 
zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
-                                               }
+                                               hval = 
zend_hash_func(Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1);
                                        }
-                                       if (zend_hash_quick_find(ht, 
Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hval, (void **) &value) == SUCCESS) {
-                                               isset = 1;
-                                       }
-                                       break;
-                               case IS_NULL:
-                                       if (zend_hash_find(ht, "", sizeof(""), 
(void **) &value) == SUCCESS) {
-                                               isset = 1;
-                                       }
-                                       break;
-                               default:
-                                       zend_error(E_WARNING, "Illegal offset 
type in isset or empty");
-
-                                       break;
-                       }
-
-                       if (opline->extended_value & ZEND_ISSET) {
-                               if (isset && Z_TYPE_PP(value) == IS_NULL) {
-                                       result = 0;
-                               } else {
-                                       result = isset;
                                }
-                       } else /* if (opline->extended_value & ZEND_ISEMPTY) */ 
{
-                               if (!isset || !i_zend_is_true(*value)) {
-                                       result = 0;
-                               } else {
-                                       result = 1;
+                               if (zend_hash_quick_find(ht, 
Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, hval, (void **) &value) == SUCCESS) {
+                                       isset = 1;
                                }
-                       }
-                       FREE_OP2();
-               } else if (Z_TYPE_PP(container) == IS_OBJECT) {
-                       if (IS_OP2_TMP_FREE()) {
-                               MAKE_REAL_ZVAL_PTR(offset);
-                       }
-                       if (prop_dim) {
-                               if (Z_OBJ_HT_P(*container)->has_property) {
-                                       result = 
Z_OBJ_HT_P(*container)->has_property(*container, offset, 
(opline->extended_value & ZEND_ISEMPTY) != 0, ((OP2_TYPE == IS_CONST) ? 
opline->op2.literal : NULL) TSRMLS_CC);
-                               } else {
-                                       zend_error(E_NOTICE, "Trying to check 
property of non-object");
-                                       result = 0;
+                               break;
+                       case IS_NULL:
+                               if (zend_hash_find(ht, "", sizeof(""), (void 
**) &value) == SUCCESS) {
+                                       isset = 1;
                                }
+                               break;
+                       default:
+                               zend_error(E_WARNING, "Illegal offset type in 
isset or empty");
+                               break;
+               }
+
+               if (opline->extended_value & ZEND_ISSET) {
+                       if (isset && Z_TYPE_PP(value) == IS_NULL) {
+                               result = 0;
                        } else {
-                               if (Z_OBJ_HT_P(*container)->has_dimension) {
-                                       result = 
Z_OBJ_HT_P(*container)->has_dimension(*container, offset, 
(opline->extended_value & ZEND_ISEMPTY) != 0 TSRMLS_CC);
-                               } else {
-                                       zend_error(E_NOTICE, "Trying to check 
element of non-array");
-                                       result = 0;
-                               }
+                               result = isset;
                        }
-                       if (IS_OP2_TMP_FREE()) {
-                               zval_ptr_dtor(&offset);
+               } else /* if (opline->extended_value & ZEND_ISEMPTY) */ {
+                       if (!isset || !i_zend_is_true(*value)) {
+                               result = 0;
                        } else {
-                               FREE_OP2();
+                               result = 1;
                        }
-               } else if ((*container)->type == IS_STRING && !prop_dim) { /* 
string offsets */
-                       zval tmp;
-
-                       if (Z_TYPE_P(offset) != IS_LONG) {
-                               ZVAL_COPY_VALUE(&tmp, offset);
-                               zval_copy_ctor(&tmp);
-                               convert_to_long(&tmp);
-                               offset = &tmp;
+               }
+               FREE_OP2();
+       } else if (Z_TYPE_PP(container) == IS_OBJECT) {
+               if (IS_OP2_TMP_FREE()) {
+                       MAKE_REAL_ZVAL_PTR(offset);
+               }
+               if (prop_dim) {
+                       if (Z_OBJ_HT_P(*container)->has_property) {
+                               result = 
Z_OBJ_HT_P(*container)->has_property(*container, offset, 
(opline->extended_value & ZEND_ISEMPTY) != 0, ((OP2_TYPE == IS_CONST) ? 
opline->op2.literal : NULL) TSRMLS_CC);
+                       } else {
+                               zend_error(E_NOTICE, "Trying to check property 
of non-object");
+                               result = 0;
                        }
-                       if (Z_TYPE_P(offset) == IS_LONG) {
-                               if (opline->extended_value & ZEND_ISSET) {
-                                       if (offset->value.lval >= 0 && 
offset->value.lval < Z_STRLEN_PP(container)) {
-                                               result = 1;
-                                       }
-                               } else /* if (opline->extended_value & 
ZEND_ISEMPTY) */ {
-                                       if (offset->value.lval >= 0 && 
offset->value.lval < Z_STRLEN_PP(container) && 
Z_STRVAL_PP(container)[offset->value.lval] != '0') {
-                                               result = 1;
-                                       }
-                               }
+               } else {
+                       if (Z_OBJ_HT_P(*container)->has_dimension) {
+                               result = 
Z_OBJ_HT_P(*container)->has_dimension(*container, offset, 
(opline->extended_value & ZEND_ISEMPTY) != 0 TSRMLS_CC);
+                       } else {
+                               zend_error(E_NOTICE, "Trying to check element 
of non-array");
+                               result = 0;
                        }
-                       FREE_OP2();
+               }
+               if (IS_OP2_TMP_FREE()) {
+                       zval_ptr_dtor(&offset);
                } else {
                        FREE_OP2();
                }
+       } else if ((*container)->type == IS_STRING && !prop_dim) { /* string 
offsets */
+               zval tmp;
+
+               if (Z_TYPE_P(offset) != IS_LONG) {
+                       ZVAL_COPY_VALUE(&tmp, offset);
+                       zval_copy_ctor(&tmp);
+                       convert_to_long(&tmp);
+                       offset = &tmp;
+               }
+               if (Z_TYPE_P(offset) == IS_LONG) {
+                       if (opline->extended_value & ZEND_ISSET) {
+                               if (offset->value.lval >= 0 && 
offset->value.lval < Z_STRLEN_PP(container)) {
+                                       result = 1;
+                               }
+                       } else /* if (opline->extended_value & ZEND_ISEMPTY) */ 
{
+                               if (offset->value.lval >= 0 && 
offset->value.lval < Z_STRLEN_PP(container) && 
Z_STRVAL_PP(container)[offset->value.lval] != '0') {
+                                       result = 1;
+                               }
+                       }
+               }
+               FREE_OP2();
+       } else {
+               FREE_OP2();
        }
 
        Z_TYPE(EX_T(opline->result.var).tmp_var) = IS_BOOL;

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to