Saturday, 27 January 2007
|
| [PHP-DEV] Autoglobal CVs without silence -- Summary Sara Golemon 22:50:16 |
| | OK. Now your patch will work, but I would like to >> think about more elegant solution. >> The problem that I am busy with other work. >> Could you please wait a week and then commit it if >> I won't return (on the next Tuesday). >> Argh. Can we please accelerate this somehow? > This patch is necessary for the HTTP request > decoding work in PHP 6 and we really should > get it done sooner than later. > Okay, rewind and reset time.
Dmitry, here's a quick summary of what's being done, how, and why.
Initial Problem: PHP6 needs better http input encoding detection, preferably with minimal wasted effort in conversion and limited vectors for conversion failure based attacks.
Proposed Solution: Wait until the first time a given input argument is requested before actually converting it. This allows scripts to perform their own (potentially more relevant) determination of what the correct input encoding is.
Proposed Implementation for this solution: Make JIT be runtime based and fine-grained enough to signal not just the autoglobal being fetched, but what specific dimension/property within that auto global is being requested. Using runtime-dimension-JIT to decode input arguments as they are requested.
Rejected Implementation: Use object/array-access overloading to JIT the values instead. While this solution is the simplest and can be done with relatively few LOCs, it breaks assumptions about the GPC auto globals (is_array() fails, is_object() succeeds, assignments of the autoglobals becomes "reference-like"*). In short, this solution introduces BC issues.
----------------------------------------------------------------
Next Problem: How to actually make runtime-JIT with dim/prop level granularity?
Proposed Solution: Catch fetches during FETCH_DIM/FETCH_OBJ execution handlers.
----------------------------------------------------------------
Next Problem: auto_globals aren't processed as CVs, meaning that during FETCH_DIM, there's no way to tell if op1 came from an auto global or not (since the fetch happened earlier).
Solution (Implemented last week): Remove restriction on CVing auto globals by adding a fetch_type field to auto global structure.
----------------------------------------------------------------
Next Problem: Silence operator forces non-CV even in situations where a CV is appropriate since the associated fetch_dim/obj op would not fall outside of silence scoping.
Proposed Solution (patch from prior email): modify the variable parsing routines slightly to rewrite simple fetch ops to CV'd fetch_dim/obj ops when appropriate.
----------------------------------------------------------------
I'm not meaning to apply pressure (a week doesn't effect my timetable any), I can even move-forward with the next (and last) ZE related patch (FETCH_DIM/FETCH_OBJ handling) separate from this one. I'm just trying to balance Andrei's timetable on one side, with a desired to not overwhelm you and Andi with ZE patches on the other. Hopefully this summary helps everyone get on the same page.
-Sara
* - Sidenote: I refuse to call object behavior "reference by default", I've had too many people notice that it's not actually true and expect me to explain why in 2 minutes without the aid of a whiteboard.in
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php
|
| | 8 answers | Add comment |
|
| [PHP-DEV] Runtime-JIT, the whole enchilada Sara Golemon 22:41:56 |
| | Dmitry-
You asked for it, you get it Hopefully it'll be self-explanatory, the one part I hope you don't notice is that I put the auto_global check back into fetch_simple_variable_ex in order to force direct access to autoglobals ($foo = $_GET; foreach($_POST as ...) etc...) as non-auto-globals. Ultimately this made catching the difference between a whole var access and a dim/obj access much more straight-forward and computationally cheap. It comes at a slight cost for those fetches, but hopefully they're in the minority.
Bench numbers with this patch: simple 0.461 simplecall 1.985 simpleucall 2.900 simpleudcall 3.488 mandel 2.136 mandel2 3.192 ackermann(7) 3.490 ary(50000) 0.154 ary2(50000) 0.137 ary3(2000) 1.076 fibo(30) 9.766 hash1(50000) 0.423 hash2(500) 0.307 heapsort(20000) 0.799 matrix(20) 0.526 nestedloop(12) 0.866 sieve(30) 0.630 strcat(200000) 0.303 ------------------------ Total 32.639
-Sara
Index: Zend/zend_compile.c =================================================================== RCS file: /repository/ZendEngine2/zend_compile.c,v retrieving revision 1.736 diff -u -p -r1.736 zend_compile.c --- Zend/zend_compile.c20 Jan 2007 20:36:55 -00001.736 +++ Zend/zend_compile.c24 Jan 2007 19:50:52 -0000 @@ -290,7 +290,8 @@ static int lookup_cv(zend_op_array *op_a op_array->vars[i].name = name; /* estrndup(name, name_len); */ op_array->vars[i].name_len = name_len; op_array->vars[i].hash_value = hash_value; -op_array->vars[i].fetch_type = zend_u_is_auto_global(type, name, name_len TSRMLS_CC) ? ZEND_FETCH_GLOBAL : ZEND_FETCH_LOCAL; +op_array->vars[i].auto_global = NULL; +op_array->vars[i].fetch_type = zend_u_is_auto_global(type, name, name_len, &(op_array->vars[i].auto_global) TSRMLS_CC) ? ZEND_FETCH_GLOBAL : ZEND_FETCH_LOCAL; return i; } @@ -383,6 +384,7 @@ void fetch_simple_variable_ex(znode *res Z_TYPE(varname->u.constant) == IS_UNICODE) && !(Z_UNILEN(varname->u.constant) == (sizeof("this")-1) && ZEND_U_EQUAL(Z_TYPE(varname->u.constant), Z_UNIVAL(varname->u.constant), Z_UNILEN(varname->u.constant), "this", sizeof("this")-1)) && + !zend_u_is_auto_global(Z_TYPE(varname->u.constant), Z_UNIVAL(varname->u.constant), Z_UNILEN(varname->u.constant), NULL TSRMLS_CC) && (CG(active_op_array)->last == 0 || CG(active_op_array)->opcodes[CG(active_op_array)->last-1].opcode != ZEND_BEGIN_SILENCE)) { result->op_type = IS_CV; @@ -410,7 +412,7 @@ void fetch_simple_variable_ex(znode *res if (varname->op_type == IS_CONST && (Z_TYPE(varname->u.constant) == IS_STRING || Z_TYPE(varname->u.constant) == IS_UNICODE)) { -if (zend_u_is_auto_global(Z_TYPE(varname->u.constant), Z_UNIVAL(varname->u.constant), Z_UNILEN(varname->u.constant) TSRMLS_CC)) { +if (zend_u_is_auto_global(Z_TYPE(varname->u.constant), Z_UNIVAL(varname->u.constant), Z_UNILEN(varname->u.constant), NULL TSRMLS_CC)) { opline_ptr->op2.u.EA.type = ZEND_FETCH_GLOBAL; } } @@ -492,6 +494,29 @@ void fetch_array_dim(znode *result, znod zend_op opline; zend_llist *fetch_list_ptr; +zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); +if (fetch_list_ptr->count == 1) { +zend_llist_element *le = fetch_list_ptr->head; +zend_op *parentop = (zend_op*)le->data; + +if (parentop && parentop->opcode == ZEND_FETCH_W && +parent->op_type == IS_VAR && parentop->result.op_type == IS_VAR && parent->u.var == parentop->result.u.var && +parentop->op1.op_type == IS_CONST && +(Z_TYPE(parentop->op1.u.constant) == IS_STRING || Z_TYPE(parentop->op1.u.constant) == IS_UNICODE) && + !(Z_UNILEN(parentop->op1.u.constant) == (sizeof("this")-1) && ZEND_U_EQUAL(Z_TYPE(parentop->op1.u.constant), Z_UNIVAL(parentop->op1.u.constant), Z_UNILEN(parentop->op1.u.constant), "this", sizeof("this")-1)) ) { +/* Recompile CV and rewrite previous op to direct FETCH_DIM */ +zval tmp = parentop->op1.u.constant; +parentop->opcode = ZEND_FETCH_DIM_W; +parentop->op1.op_type = IS_CV; +parentop->op1.u.var = lookup_cv(CG(active_op_array), Z_TYPE(tmp), Z_UNIVAL(tmp), Z_UNILEN(tmp) TSRMLS_CC); +parentop->op1.u.EA.type = 0; +parentop->op2 = *dim; +parentop->extended_value = ZEND_FETCH_STANDARD; +*result = parentop->result; +return; +} +} + init_op(&opline TSRMLS_CC); opline.opcode = ZEND_FETCH_DIM_W;/* the backpatching routine assumes W */ opline.result.op_type = IS_VAR; @@ -502,7 +527,6 @@ void fetch_array_dim(znode *result, znod opline.extended_value = ZEND_FETCH_STANDARD; *result = opline.result; -zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); zend_llist_add_element(fetch_list_ptr, &opline); } @@ -3261,7 +3285,6 @@ void zend_do_fetch_property(znode *resul zend_op *opline_ptr=NULL; zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); - if (fetch_list_ptr->count == 1) { zend_llist_element *le; @@ -3295,6 +3318,19 @@ void zend_do_fetch_property(znode *resul } *result = opline_ptr->result; return; +} else if (opline_ptr && opline_ptr->opcode == ZEND_FETCH_W && +object->op_type == IS_VAR && opline_ptr->result.op_type == IS_VAR && object->u.var == opline_ptr->result.u.var && +opline_ptr->op1.op_type == IS_CONST && +(Z_TYPE(opline_ptr->op1.u.constant) == IS_STRING || Z_TYPE(opline_ptr->op1.u.constant) == IS_UNICODE) ) { +/* Recompile CV and rewrite previous op to direct FETCH_OBJ */ +zval tmp = opline_ptr->op1.u.constant; +opline_ptr->opcode = ZEND_FETCH_OBJ_W; +opline_ptr->op1.op_type = IS_CV; +opline_ptr->op1.u.var = lookup_cv(CG(active_op_array), Z_TYPE(tmp), Z_UNIVAL(tmp), Z_UNILEN(tmp) TSRMLS_CC); +opline_ptr->op1.u.EA.type = 0; +opline_ptr->op2 = *property; +*result = opline_ptr->result; +return; } } @@ -4312,13 +4348,16 @@ void zend_auto_global_dtor(zend_auto_glo } -zend_bool zend_u_is_auto_global(zend_uchar type, zstr name, uint name_len TSRMLS_DC) +zend_bool zend_u_is_auto_global(zend_uchar type, zstr name, uint name_len, zend_auto_global **pauto TSRMLS_DC) { zend_auto_global *auto_global; if (zend_u_hash_find(CG(auto_globals), type, name, name_len+1, (void **) &auto_global)==SUCCESS) { if (auto_global->armed) { -auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC); +auto_global->armed = auto_global->auto_global_callback(auto_global, ZEND_CT, NULL, 0, NULL TSRMLS_CC); +} +if (pauto) { +*pauto = auto_global; } return 1; } @@ -4327,21 +4366,26 @@ zend_bool zend_u_is_auto_global(zend_uch zend_bool zend_is_auto_global(char *name, uint name_len TSRMLS_DC) { -return zend_u_is_auto_global(IS_STRING, ZSTR(name), name_len TSRMLS_CC); +return zend_u_is_auto_global(IS_STRING, ZSTR(name), name_len, NULL TSRMLS_CC); } -int zend_register_auto_global(char *name, uint name_len, zend_auto_global_callback auto_global_callback TSRMLS_DC) +int zend_register_auto_global_ex(char *name, uint name_len, zend_auto_global_callback auto_global_callback, zend_auto_global **pauto TSRMLS_DC) { zend_auto_global auto_global; auto_global.name = zend_strndup(name, name_len); auto_global.name_len = name_len; auto_global.auto_global_callback = auto_global_callback; +auto_global.armed = auto_global_callback ? 1 : 0; -return zend_hash_add(CG(auto_globals), name, name_len+1, &auto_global, sizeof(zend_auto_global), NULL); +return zend_hash_add(CG(auto_globals), name, name_len+1, &auto_global, sizeof(zend_auto_global), (void**)pauto); } +int zend_register_auto_global(char *name, uint name_len, zend_auto_global_callback auto_global_callback TSRMLS_DC) +{ +return zend_register_auto_global_ex(name, name_len, auto_global_callback, NULL TSRMLS_CC); +} int zendlex(znode *zendlval TSRMLS_DC) { Index: Zend/zend_compile.h =================================================================== RCS file: /repository/ZendEngine2/zend_compile.h,v retrieving revision 1.353 diff -u -p -r1.353 zend_compile.h --- Zend/zend_compile.h20 Jan 2007 20:36:55 -00001.353 +++ Zend/zend_compile.h24 Jan 2007 19:50:52 -0000 @@ -51,6 +51,7 @@ typedef struct _zend_op_array zend_op_array; typedef struct _zend_op zend_op; +typedef struct _zend_auto_global zend_auto_global; typedef struct _znode { int op_type; @@ -175,6 +176,7 @@ typedef struct _zend_compiled_variable { int name_len; ulong hash_value; zend_uint fetch_type; +zend_auto_global *auto_global; } zend_compiled_variable; struct _zend_op_array { @@ -575,18 +577,19 @@ ZEND_API char *zend_make_compiled_string ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers TSRMLS_DC); int zend_get_class_fetch_type(zend_uchar type, zstr class_name, uint class_name_len); -typedef zend_bool (*zend_auto_global_callback)(char *name, uint name_len TSRMLS_DC); -typedef struct _zend_auto_global { +typedef zend_bool (*zend_auto_global_callback)(zend_auto_global *auto_global, int stage, zval *ag_val, int fetch_op, zval *member TSRMLS_DC); +struct _zend_auto_global { char *name; uint name_len; zend_auto_global_callback auto_global_callback; zend_bool armed; -} zend_auto_global; +}; void zend_auto_global_dtor(zend_auto_global *auto_global); +ZEND_API int zend_register_auto_global_ex(char *name, uint name_len, zend_auto_global_callback auto_global_callback, zend_auto_global **pauto TSRMLS_DC); ZEND_API int zend_register_auto_global(char *name, uint name_len, zend_auto_global_callback auto_global_callback TSRMLS_DC); ZEND_API zend_bool zend_is_auto_global(char *name, uint name_len TSRMLS_DC); -ZEND_API zend_bool zend_u_is_auto_global(zend_uchar type, zstr name, uint name_len TSRMLS_DC); +ZEND_API zend_bool zend_u_is_auto_global(zend_uchar type, zstr name, uint name_len, zend_auto_global **pauto TSRMLS_DC); ZEND_API int zend_auto_global_disable_jit(char *varname, zend_uint varname_length TSRMLS_DC); int zendlex(znode *zendlval TSRMLS_DC); Index: Zend/zend_execute.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute.c,v retrieving revision 1.758 diff -u -p -r1.758 zend_execute.c --- Zend/zend_execute.c20 Jan 2007 20:36:55 -00001.758 +++ Zend/zend_execute.c24 Jan 2007 19:50:52 -0000 @@ -903,8 +903,17 @@ static inline HashTable *zend_get_target break; case ZEND_FETCH_GLOBAL: case ZEND_FETCH_GLOBAL_LOCK: +{ +zend_auto_global *auto_global; + +if ((Z_TYPE_P(variable) == IS_STRING || Z_TYPE_P(variable) == IS_UNICODE) && +SUCCESS == zend_u_hash_find(CG(auto_globals), Z_TYPE_P(variable), Z_UNIVAL_P(variable), Z_UNILEN_P(variable) + 1, (void **) &auto_global) && +auto_global->armed && auto_global->auto_global_callback) { +auto_global->armed = auto_global->auto_global_callback(auto_global, ZEND_RT, NULL, ZEND_FETCH_R + (3 * type), NULL TSRMLS_CC); +} return &EG(symbol_table); break; +} case ZEND_FETCH_STATIC: if (!EG(active_op_array)->static_variables) { ALLOC_HASHTABLE(EG(active_op_array)->static_variables); @@ -1030,7 +1039,7 @@ fetch_string_dim: return retval; } -static void zend_fetch_dimension_address(temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type TSRMLS_DC) +static void zend_fetch_dimension_address(znode *container_node, temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type TSRMLS_DC) { zval *container; @@ -1039,6 +1048,13 @@ static void zend_fetch_dimension_address } container = *container_ptr; +if (container_node->op_type == IS_CV) { +zend_auto_global *auto_global = CV_DEF_OF(container_node->u.var).auto_global; + +if (auto_global && auto_global->armed && auto_global->auto_global_callback) { +auto_global->armed = auto_global->auto_global_callback(auto_global, ZEND_RT, container, ZEND_FETCH_DIM_R + (3 * type), dim TSRMLS_CC); +} +} if (container == EG(error_zval_ptr)) { if (result) { @@ -1232,11 +1248,19 @@ static void zend_fetch_dimension_address } } -static void zend_fetch_property_address(temp_variable *result, zval **container_ptr, zval *prop_ptr, int type TSRMLS_DC) +static void zend_fetch_property_address(znode *container_node, temp_variable *result, zval **container_ptr, zval *prop_ptr, int type TSRMLS_DC) { zval *container; container = *container_ptr; +if (container_node->op_type == IS_CV) { +zend_auto_global *auto_global = CV_DEF_OF(container_node->u.var).auto_global; + +if (auto_global && auto_global->armed && auto_global->auto_global_callback) { +auto_global->armed = auto_global->auto_global_callback(auto_global, ZEND_RT, container, ZEND_FETCH_OBJ_R + (3 * type), prop_ptr TSRMLS_CC); +} +} + if (container == EG(error_zval_ptr)) { if (result) { result->var.ptr_ptr = &EG(error_zval_ptr); Index: Zend/zend_vm_def.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_def.h,v retrieving revision 1.155 diff -u -p -r1.155 zend_vm_def.h --- Zend/zend_vm_def.h11 Jan 2007 22:35:36 -00001.155 +++ Zend/zend_vm_def.h24 Jan 2007 19:50:52 -0000 @@ -418,7 +418,7 @@ ZEND_VM_HELPER_E zend_binary_assign_op_ zend_op *op_data = opline+1; zval *dim = GET_OP2_ZVAL_PTR(BP_VAR_R); -zend_fetch_dimension_address(&EX_T(op_data->op2.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW), dim, IS_OP2_TMP_FREE(), BP_VAR_RW TSRMLS_CC); +zend_fetch_dimension_address(&opline->op1, &EX_T(op_data->op2.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW), dim, IS_OP2_TMP_FREE(), BP_VAR_RW TSRMLS_CC); value = get_zval_ptr(&op_data->op1, E Ts), &free_op_data1, BP_VAR_R); var_ptr = get_zval_ptr_ptr(&op_data->op2, E Ts), &free_op_data2, BP_VAR_RW); increment_opline = 1; @@ -1066,7 +1066,7 @@ ZEND_VM_HANDLER(81, ZEND_FETCH_DIM_R, VA EX_T(opline->op1.u.var).var.ptr_ptr) { PZVAL_LOCK(*EX_T(opline->op1.u.var).var.ptr_ptr); } -zend_fetch_dimension_address(RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PTR(BP_VAR_R), dim, IS_OP2_TMP_FREE(), BP_VAR_R TSRMLS_CC); +zend_fetch_dimension_address(&opline->op1, RETURN_VALUE_UNUSED(&opline->result)?NULL:&EX_T(opline->result.u.var), GET_OP1_ZVAL_PTR_PT | |