summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/zend_vm_def.h8
-rw-r--r--Zend/zend_vm_execute.h44
-rw-r--r--Zend/zend_vm_execute.skl36
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);