diff options
| -rw-r--r-- | Zend/zend_vm_def.h | 8 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.h | 44 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.skl | 36 |
3 files changed, 78 insertions, 10 deletions
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 13ae824c48..20b5633b8a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2530,7 +2530,13 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) nested = EX(nested); - zend_vm_stack_free(execute_data TSRMLS_CC); + /* For generators the execute_data is stored on the heap, for everything + * else it is stored on the VM stack. */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + efree(execute_data); + } else { + zend_vm_stack_free(execute_data TSRMLS_CC); + } if (nested) { execute_data = EG(current_execute_data); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 12338117f5..d351067cb7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -342,11 +342,15 @@ static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* o ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { DCL_OPLINE + zend_execute_data *execute_data; + size_t execute_data_size; + zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); + if (EG(exception)) { return; } @@ -354,11 +358,35 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = 1; zend_vm_enter: - /* Initialize execute_data */ - execute_data = (zend_execute_data *)zend_vm_stack_alloc( + /* + * When allocating the execute_data, memory for compiled variables and + * temporary variables is also allocated after the actual zend_execute_data + * struct. op_array->last_var specifies the number of compiled variables and + * op_array->T is the number of temporary variables. If there is no symbol + * table, then twice as much memory is allocated for compiled variables. + * In that case the first half contains zval**s and the second half the + * actual zval*s (which would otherwise be in the symbol table). + */ + execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T + ; + + /* + * Normally the execute_data is allocated on the VM stack (because it does + * not actually do any allocation and thus is faster). For generators + * though this behavior would be suboptimal, because the (rather large) + * structure would have to be copied back and forth every time execution is + * suspended or resumed. That's why for generators the execution context + * is allocated using emalloc, thus allowing to save and restore it simply + * by replacing a pointer. + */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + execute_data = emalloc(execute_data_size); + } else { + execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + } EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); @@ -477,7 +505,13 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) nested = EX(nested); - zend_vm_stack_free(execute_data TSRMLS_CC); + /* For generators the execute_data is stored on the heap, for everything + * else it is stored on the VM stack. */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + efree(execute_data); + } else { + zend_vm_stack_free(execute_data TSRMLS_CC); + } if (nested) { execute_data = EG(current_execute_data); diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 426f689795..359e052008 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -3,9 +3,13 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) { DCL_OPLINE + zend_execute_data *execute_data; + size_t execute_data_size; + zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); + {%HELPER_VARS%} {%INTERNAL_LABELS%} @@ -17,11 +21,35 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = 1; zend_vm_enter: - /* Initialize execute_data */ - execute_data = (zend_execute_data *)zend_vm_stack_alloc( + /* + * When allocating the execute_data, memory for compiled variables and + * temporary variables is also allocated after the actual zend_execute_data + * struct. op_array->last_var specifies the number of compiled variables and + * op_array->T is the number of temporary variables. If there is no symbol + * table, then twice as much memory is allocated for compiled variables. + * In that case the first half contains zval**s and the second half the + * actual zval*s (which would otherwise be in the symbol table). + */ + execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T + ; + + /* + * Normally the execute_data is allocated on the VM stack (because it does + * not actually do any allocation and thus is faster). For generators + * though this behavior would be suboptimal, because the (rather large) + * structure would have to be copied back and forth every time execution is + * suspended or resumed. That's why for generators the execution context + * is allocated using emalloc, thus allowing to save and restore it simply + * by replacing a pointer. + */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + execute_data = emalloc(execute_data_size); + } else { + execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + } EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); |
