com php-src: Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementatio n_details): Zend/zend_compile.c Zend/zend_compi le.h Zend/zend_execute.c Zend/zend_execute_API. c Zend/zend_generators.c Zend/zend_globals.h Zend /zend_hash.c Zend/zend_hash.h Zend/zend_types.h Zend/zend_vm_def.h Zend/zend_vm_execute.h Ze nd/zend_vm_opcodes.c Zend/zend_vm_opcodes.h ext /opcache/Optimizer/block_pass.c ext/opcache/Opt

[email protected] (Rasmus Lerdorf)
Newsgroups php.cvs
Message-ID <[email protected]>
Commit:    15a23b1218b3e38630d677751a975907daa2cd54
Author:    Dmitry Stogov <[email protected]>         Thu, 29 Jan 2015 21:05:02 +0300
Parents:   10a3260b1f16b6075fd8140f673dfef4d5efea91
Branches:  dstogov-foreach

Link:       http://git.php.net/?p=php-src.git;a=commitdiff;h=15a23b1218b3e38630d677751a975907daa2cd54

Log:
Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details)

Changed paths:
  M  Zend/zend_compile.c
  M  Zend/zend_compile.h
  M  Zend/zend_execute.c
  M  Zend/zend_execute_API.c
  M  Zend/zend_generators.c
  M  Zend/zend_globals.h
  M  Zend/zend_hash.c
  M  Zend/zend_hash.h
  M  Zend/zend_types.h
  M  Zend/zend_vm_def.h
  M  Zend/zend_vm_execute.h
  M  Zend/zend_vm_opcodes.c
  M  Zend/zend_vm_opcodes.h
  M  ext/opcache/Optimizer/block_pass.c
  M  ext/opcache/Optimizer/optimize_temp_vars_5.c
  M  ext/opcache/Optimizer/pass2.c
  M  ext/standard/array.c
  M  tests/lang/foreachLoop.013.phpt
  M  tests/lang/foreachLoop.015.phpt
diff_15a23b1218b3e38630d677751a975907daa2cd54.txt (text/plain, 39.2 KB)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index e37fdd7..080b315 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -829,7 +829,7 @@ static int generate_free_loop_var(znode *var) /* {{{ */
 		{
 			zend_op *opline = get_next_op(CG(active_op_array));
 
-			opline->opcode = ZEND_FREE;
+			opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
 			SET_NODE(opline->op1, var);
 			SET_UNUSED(opline->op2);
 		}
@@ -3398,6 +3398,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
 	opnum_reset = get_next_op_number(CG(active_op_array));
 	opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL);
 
+	reset_node.flag = by_ref;
 	zend_stack_push(&CG(loop_var_stack), &reset_node);
 
 	opnum_fetch = get_next_op_number(CG(active_op_array));
@@ -3408,12 +3409,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
 
 	opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL);
 
-	if (by_ref) {
-		/* Allocate temporary variable to keep HashTable value */
-		opline->op1_type = IS_TMP_VAR;
-		opline->op1.var = get_temporary_variable(CG(active_op_array));
-	}
-
 	if (key_ast) {
 		zend_make_tmp_result(&key_node, opline);
 	}
@@ -3504,6 +3499,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
 
 	zend_compile_expr(&expr_node, expr_ast);
 
+	expr_node.flag = 0;
 	zend_stack_push(&CG(loop_var_stack), &expr_node);
 
 	zend_begin_loop();
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 2d71ca2..5239f56 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -95,7 +95,8 @@ typedef union _znode_op {
 } znode_op;
 
 typedef struct _znode { /* used only during compilation */
-	int op_type;
+	zend_uchar op_type;
+	zend_uchar flag;
 	union {
 		znode_op op;
 		zval constant; /* replaced by literal/zv */
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 7a79def..d59aef1 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1584,6 +1584,14 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of
 				if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
 					zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
 				}
+			} else if (brk_opline->opcode == ZEND_FE_FREE) {
+				if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
+					zval *var = EX_VAR(brk_opline->op1.var);
+					if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+						zend_hash_iterator_del(Z_FE_ITER_P(var));
+					}
+					zval_ptr_dtor_nogc(var);
+				}
 			}
 		}
 		array_offset = jmp_to->parent;
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index c9da46d..c6a0d8f 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -183,6 +183,11 @@ void init_executor(void) /* {{{ */
 
 	EG(scope) = NULL;
 
+	EG(ht_iterators_count) = sizeof(EG(ht_iterators_slots)) / sizeof(HashTableIterator);
+	EG(ht_iterators_used) = 0;
+	EG(ht_iterators) = EG(ht_iterators_slots);
+	memset(EG(ht_iterators), 0, sizeof(EG(ht_iterators_slots)));
+
 	EG(active) = 1;
 }
 /* }}} */
@@ -373,6 +378,11 @@ void shutdown_executor(void) /* {{{ */
 
 	zend_shutdown_fpu();
 
+	EG(ht_iterators_used) = 0;
+	if (EG(ht_iterators) != EG(ht_iterators_slots)) {
+		efree(EG(ht_iterators));
+	}
+
 	EG(active) = 0;
 }
 /* }}} */
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index f19bb85..1f25d07 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -60,6 +60,12 @@ static void zend_generator_cleanup_unfinished_execution(zend_generator *generato
 				if (brk_opline->opcode == ZEND_FREE) {
 					zval *var = EX_VAR(brk_opline->op1.var);
 					zval_ptr_dtor_nogc(var);
+				} else if (brk_opline->opcode == ZEND_FE_FREE) {
+					zval *var = EX_VAR(brk_opline->op1.var);
+					if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+						zend_hash_iterator_del(Z_FE_ITER_P(var));
+					}
+					zval_ptr_dtor_nogc(var);
 				}
 			}
 		}
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index beca5ad..abebb19 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -225,6 +225,11 @@ struct _zend_executor_globals {
 	zend_bool active;
 	zend_bool valid_symbol_table;
 
+	uint32_t           ht_iterators_count;     /* number of allocatd slots */
+	uint32_t           ht_iterators_used;      /* number of used slots */
+	HashTableIterator *ht_iterators;
+	HashTableIterator  ht_iterators_slots[16];
+
 	void *saved_fpu_cw_ptr;
 #if XPFPA_HAVE_CW
 	XPFPA_CW_DATATYPE saved_fpu_cw;
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 480ac64..dd175c7 100644
--- a/Zend/zend_hash.c
+++ b/Zend/zend_hash.c
@@ -193,6 +193,144 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt
 	}
 }
 
+ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht)
+{
+	HashTableIterator *iter = EG(ht_iterators);
+	HashTableIterator *end  = iter + EG(ht_iterators_count);
+	uint32_t idx;
+
+	if (EXPECTED(ht->u.v.nIteratorsCount != 255)) {
+		ht->u.v.nIteratorsCount++;
+	}
+	while (iter != end) {
+		if (iter->ht == NULL) {
+			iter->ht = ht;
+			iter->pos = ht->nInternalPointer;
+			idx = iter - EG(ht_iterators);
+			if (idx + 1 > EG(ht_iterators_used)) {
+				EG(ht_iterators_used) = idx + 1;
+			}
+			return idx;
+		}
+		iter++;
+	}
+	if (EG(ht_iterators) == EG(ht_iterators_slots)) {
+		EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
+		memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count));
+	} else {
+		EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8));
+	}
+	iter = EG(ht_iterators) + EG(ht_iterators_count);
+	EG(ht_iterators_count) += 8;
+	iter->ht = ht;
+	iter->pos = ht->nInternalPointer;
+	memset(iter + 1, 0, sizeof(HashTableIterator) * 7);
+	idx = iter - EG(ht_iterators);
+	EG(ht_iterators_used) = idx + 1;
+	return idx;
+}
+
+ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
+{
+	HashTableIterator *iter = EG(ht_iterators) + idx;
+
+	ZEND_ASSERT(idx != (uint32_t)-1);
+	if (iter->pos == INVALID_IDX) {
+		return INVALID_IDX;
+	} else if (UNEXPECTED(iter->ht != ht)) {
+		if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) {
+			iter->ht->u.v.nIteratorsCount--;
+		}
+		if (EXPECTED(ht->u.v.nIteratorsCount != 255)) {
+			ht->u.v.nIteratorsCount++;
+		}
+		iter->ht = ht;
+		iter->pos = ht->nInternalPointer;
+	}
+	return iter->pos;
+}
+
+ZEND_API void zend_hash_iterator_del(uint32_t idx)
+{
+	HashTableIterator *iter = EG(ht_iterators) + idx;
+
+	ZEND_ASSERT(idx != (uint32_t)-1);
+
+	if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) {
+		iter->ht->u.v.nIteratorsCount--;
+	}
+	iter->ht = NULL;
+
+	if (idx == EG(ht_iterators_used) - 1) {
+		while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
+			idx--;
+		}
+		EG(ht_iterators_used) = idx;
+	}
+}
+
+static zend_never_inline void _iterators_del(HashTable *ht)
+{
+	HashTableIterator *iter = EG(ht_iterators);
+	HashTableIterator *end  = iter + EG(ht_iterators_used);
+	uint32_t idx;
+
+	while (iter != end) {
+		if (iter->ht == ht) {
+			iter->ht = NULL;
+		}
+		iter++;
+	}
+
+	idx = EG(ht_iterators_used);
+	while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) {
+		idx--;
+	}
+	EG(ht_iterators_used) = idx;
+}
+
+static zend_always_inline void iterators_del(HashTable *ht)
+{
+	if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
+		_iterators_del(ht);
+	}
+}
+
+static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos)
+{
+	HashTableIterator *iter = EG(ht_iterators);
+	HashTableIterator *end  = iter + EG(ht_iterators_used);
+
+	while (iter != end) {
+		if (iter->ht == ht && iter->pos == ht->nInternalPointer) {
+			iter->pos = pos;
+		}
+		iter++;
+	}
+}
+
+static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos)
+{
+	if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
+		_iterators_update(ht, pos);
+	}
+}
+
+ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos)
+{
+	if (UNEXPECTED(ht->u.v.nIteratorsCount)) {
+		HashTableIterator *iter = EG(ht_iterators);
+		HashTableIterator *end  = iter + EG(ht_iterators_used);
+
+		while (iter != end) {
+			if (iter->ht == ht && iter->pos == ht->nInternalPointer) {
+				iter->pos = pos;
+			}
+			iter++;
+		}
+	}
+}
+
 static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key)
 {
 	zend_ulong h;
@@ -303,6 +441,7 @@ add_to_hash:
 	idx = ht->nNumUsed++;
 	ht->nNumOfElements++;
 	if (ht->nInternalPointer == INVALID_IDX) {
+		iterators_update(ht, idx);
 		ht->nInternalPointer = idx;
 	}
 	p = ht->arData + idx;
@@ -470,6 +609,7 @@ add_to_packed:
 		}
 		ht->nNumOfElements++;
 		if (ht->nInternalPointer == INVALID_IDX) {
+			iterators_update(ht, h);
 			ht->nInternalPointer = h;
 		}
 		if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
@@ -512,6 +652,7 @@ add_to_hash:
 	idx = ht->nNumUsed++;
 	ht->nNumOfElements++;
 	if (ht->nInternalPointer == INVALID_IDX) {
+		iterators_update(ht, idx);
 		ht->nInternalPointer = idx;
 	}
 	if ((zend_long)h >= (zend_long)ht->nNextFreeElement) {
@@ -600,6 +741,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht)
 		if (i != j) {
 			ht->arData[j] = ht->arData[i];
 			if (ht->nInternalPointer == i) {
+				iterators_update(ht, j);
 				ht->nInternalPointer = j;
 			}
 		}
@@ -632,9 +774,11 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx,
 		while (1) {
 			idx++;
 			if (idx >= ht->nNumUsed) {
+				iterators_update(ht, INVALID_IDX);
 				ht->nInternalPointer = INVALID_IDX;
 				break;
 			} else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
+				iterators_update(ht, idx);
 				ht->nInternalPointer = idx;
 				break;
 			}
@@ -893,6 +1037,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht)
 				} while (++p != end);
 			}
 		}
+		iterators_del(ht);
 	} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
 		return;
 	}
@@ -933,7 +1078,7 @@ ZEND_API void zend_array_destroy(HashTable *ht)
 				}
 			} while (++p != end);
 		}
-
+		iterators_del(ht);
 		SET_INCONSISTENT(HT_DESTROYED);
 	} else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) {
 		return;
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index 85f1890..c8218f1 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -50,8 +50,6 @@ typedef struct _zend_hash_key {
 
 typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam);
 
-typedef uint32_t HashPosition;
-
 BEGIN_EXTERN_C()
 
 /* startup/shutdown */
@@ -227,6 +225,13 @@ void zend_hash_display(const HashTable *ht);
 
 ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx);
 
+
+ZEND_API uint32_t     zend_hash_iterator_add(HashTable *ht);
+ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht);
+ZEND_API void         zend_hash_iterator_del(uint32_t idx);
+ZEND_API void         zend_hash_iterators_update(HashTable *ht, HashPosition pos);
+
+
 END_EXTERN_C()
 
 #define ZEND_INIT_SYMTABLE(ht)								\
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index aa54041..dbe8c61 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -129,6 +129,7 @@ struct _zval_struct {
 		uint32_t     lineno;               /* line number (for ast nodes) */
 		uint32_t     num_args;             /* arguments number for EX(This) */
 		uint32_t     fe_pos;               /* foreach position */
+		uint32_t     fe_iter_idx;          /* foreach iterator index */
 	} u2;
 };
 
@@ -161,10 +162,11 @@ typedef struct _Bucket {
 typedef struct _HashTable {
 	union {
 		struct {
-			ZEND_ENDIAN_LOHI_3(
+			ZEND_ENDIAN_LOHI_4(
 				zend_uchar    flags,
 				zend_uchar    nApplyCount,
-				uint16_t      reserve)
+				zend_uchar    nIteratorsCount,
+				zend_uchar    reserve)
 		} v;
 		uint32_t flags;
 	} u;
@@ -179,6 +181,13 @@ typedef struct _HashTable {
 	dtor_func_t       pDestructor;
 } HashTable;
 
+typedef uint32_t HashPosition;
+
+typedef struct _HashTableIterator {
+	HashTable    *ht;
+	HashPosition  pos;
+} HashTableIterator;
+
 struct _zend_array {
 	zend_refcounted   gc;
 	HashTable         ht;
@@ -265,6 +274,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
 #define Z_FE_POS(zval)				(zval).u2.fe_pos
 #define Z_FE_POS_P(zval_p)			Z_FE_POS(*(zval_p))
 
+#define Z_FE_ITER(zval)				(zval).u2.fe_iter_idx
+#define Z_FE_ITER_P(zval_p)			Z_FE_ITER(*(zval_p))
+
 #define Z_COUNTED(zval)				(zval).value.counted
 #define Z_COUNTED_P(zval_p)			Z_COUNTED(*(zval_p))
 
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 5ddc903..245bc0b 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -2138,6 +2138,21 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
 	ZEND_VM_NEXT_OPCODE();
 }
 
+ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
+{
+	zval *var;
+	USE_OPLINE
+
+	SAVE_OPLINE();
+	var = EX_VAR(opline->op1.var);
+	if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+		zend_hash_iterator_del(Z_FE_ITER_P(var));
+	}
+	zval_ptr_dtor_nogc(var);
+	CHECK_EXCEPTION();
+	ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_HANDLER(54, ZEND_ADD_CHAR, TMP|UNUSED, CONST)
 {
 	USE_OPLINE
@@ -3821,6 +3836,14 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST)
 		if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
 			zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
 		}
+	} else if (brk_opline->opcode == ZEND_FE_FREE) {
+		if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
+			zval *var = EX_VAR(brk_opline->op1.var);
+			if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+				zend_hash_iterator_del(Z_FE_ITER_P(var));
+			}
+			zval_ptr_dtor_nogc(var);
+		}
 	}
 	ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
 }
@@ -4758,8 +4781,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
 		iter->index = -1; /* will be set to 0 before using next handler */
 
 		ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 
 		if (OP1_TYPE == IS_VAR) {
 			FREE_OP1_VAR_PTR();
@@ -4798,6 +4820,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
 		while (1) {
 			if (pos >= fe_ht->nNumUsed) {
 				FREE_OP1_VAR_PTR();
+				Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
 			}
 			p = fe_ht->arData + pos;
@@ -4811,8 +4834,8 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
 			}
 			pos++;
 		}
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
+		fe_ht->nInternalPointer = pos;
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
 
 		FREE_OP1_VAR_PTR();
 		CHECK_EXCEPTION();
@@ -4820,6 +4843,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY)
 	} else {
 		zend_error(E_WARNING, "Invalid argument supplied for foreach()");
 		ZVAL_UNDEF(EX_VAR(opline->result.var));
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 		if (OP1_TYPE == IS_VAR) {
 			FREE_OP1_VAR_PTR();
 		} else {
@@ -4990,21 +5014,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
 	ZVAL_DEREF(array);
 	if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
 		fe_ht = Z_ARRVAL_P(array);
-		pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
-		if (UNEXPECTED(pos == INVALID_IDX)) {
-			/* reached end of iteration */
-			ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-		} else {
-			if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
-				pos = fe_ht->nInternalPointer;
-			}
-			SEPARATE_ARRAY(array);
-			fe_ht = Z_ARRVAL_P(array);
-			Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
-		}
-//???		if (pos != fe_ht->nInternalPointer) {
-//???			//...
-//???		}
+		pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
 		while (1) {
 			if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
 				/* reached end of iteration */
@@ -5048,7 +5058,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
 				break;
 			}
 		}
-		Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
+		EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
+			fe_ht->nInternalPointer = pos;
 		ZEND_VM_INC_OPCODE();
 		ZEND_VM_NEXT_OPCODE();
 	} else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) {
@@ -5059,19 +5070,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
  			zend_object *zobj = Z_OBJ_P(array);
 
  			fe_ht = Z_OBJPROP_P(array);
-			pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
-			if (UNEXPECTED(pos == INVALID_IDX)) {
-				/* reached end of iteration */
-				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-			} else {
-				if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
-					pos = fe_ht->nInternalPointer;
-				}
-				Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
-			}
-//???			if (pos != fe_ht->nInternalPointer) {
-//???
-//???			}
+			pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
 			while (1) {
 				if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
 					/* reached end of iteration */
@@ -5129,7 +5128,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY)
 					break;
 				}
 			}
-			Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
+			EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
+				fe_ht->nInternalPointer = pos;
 			ZEND_VM_INC_OPCODE();
 			ZEND_VM_NEXT_OPCODE();
 		} else {
@@ -5963,6 +5963,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
 					if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
 						zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
 					}
+				} else if (brk_opline->opcode == ZEND_FE_FREE) {
+					if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
+						zval *var = EX_VAR(brk_opline->op1.var);
+						if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+							zend_hash_iterator_del(Z_FE_ITER_P(var));
+						}
+						zval_ptr_dtor_nogc(var);
+					}
 				} else if (brk_opline->opcode == ZEND_END_SILENCE) {
 					/* restore previous error_reporting value */
 					if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 362f450..4244f5f 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1347,6 +1347,14 @@ static int ZEND_FASTCALL  ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
 					if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
 						zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
 					}
+				} else if (brk_opline->opcode == ZEND_FE_FREE) {
+					if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
+						zval *var = EX_VAR(brk_opline->op1.var);
+						if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+							zend_hash_iterator_del(Z_FE_ITER_P(var));
+						}
+						zval_ptr_dtor_nogc(var);
+					}
 				} else if (brk_opline->opcode == ZEND_END_SILENCE) {
 					/* restore previous error_reporting value */
 					if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) {
@@ -1805,6 +1813,14 @@ static int ZEND_FASTCALL  ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 		if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
 			zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
 		}
+	} else if (brk_opline->opcode == ZEND_FE_FREE) {
+		if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) {
+			zval *var = EX_VAR(brk_opline->op1.var);
+			if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+				zend_hash_iterator_del(Z_FE_ITER_P(var));
+			}
+			zval_ptr_dtor_nogc(var);
+		}
 	}
 	ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1));
 }
@@ -3175,8 +3191,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE
 		iter->index = -1; /* will be set to 0 before using next handler */
 
 		ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 
 		if (IS_CONST == IS_VAR) {
 
@@ -3215,6 +3230,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE
 		while (1) {
 			if (pos >= fe_ht->nNumUsed) {
 
+				Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
 			}
 			p = fe_ht->arData + pos;
@@ -3228,14 +3244,15 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE
 			}
 			pos++;
 		}
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
+		fe_ht->nInternalPointer = pos;
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
 
 		CHECK_EXCEPTION();
 		ZEND_VM_NEXT_OPCODE();
 	} else {
 		zend_error(E_WARNING, "Invalid argument supplied for foreach()");
 		ZVAL_UNDEF(EX_VAR(opline->result.var));
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 		if (IS_CONST == IS_VAR) {
 
 		} else {
@@ -9109,8 +9126,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_
 		iter->index = -1; /* will be set to 0 before using next handler */
 
 		ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 
 		if (IS_TMP_VAR == IS_VAR) {
 
@@ -9149,6 +9165,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_
 		while (1) {
 			if (pos >= fe_ht->nNumUsed) {
 
+				Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
 			}
 			p = fe_ht->arData + pos;
@@ -9162,14 +9179,15 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_
 			}
 			pos++;
 		}
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
+		fe_ht->nInternalPointer = pos;
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
 
 		CHECK_EXCEPTION();
 		ZEND_VM_NEXT_OPCODE();
 	} else {
 		zend_error(E_WARNING, "Invalid argument supplied for foreach()");
 		ZVAL_UNDEF(EX_VAR(opline->result.var));
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 		if (IS_TMP_VAR == IS_VAR) {
 
 		} else {
@@ -11945,8 +11963,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 		iter->index = -1; /* will be set to 0 before using next handler */
 
 		ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 
 		if (IS_VAR == IS_VAR) {
 			if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
@@ -11985,6 +12002,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 		while (1) {
 			if (pos >= fe_ht->nNumUsed) {
 				if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
+				Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
 			}
 			p = fe_ht->arData + pos;
@@ -11998,8 +12016,8 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 			}
 			pos++;
 		}
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
+		fe_ht->nInternalPointer = pos;
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
 
 		if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
 		CHECK_EXCEPTION();
@@ -12007,6 +12025,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 	} else {
 		zend_error(E_WARNING, "Invalid argument supplied for foreach()");
 		ZVAL_UNDEF(EX_VAR(opline->result.var));
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 		if (IS_VAR == IS_VAR) {
 			if (free_op1) {zval_ptr_dtor_nogc(free_op1);};
 		} else {
@@ -12177,21 +12196,7 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 	ZVAL_DEREF(array);
 	if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) {
 		fe_ht = Z_ARRVAL_P(array);
-		pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
-		if (UNEXPECTED(pos == INVALID_IDX)) {
-			/* reached end of iteration */
-			ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-		} else {
-			if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
-				pos = fe_ht->nInternalPointer;
-			}
-			SEPARATE_ARRAY(array);
-			fe_ht = Z_ARRVAL_P(array);
-			Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
-		}
-//???		if (pos != fe_ht->nInternalPointer) {
-//???			//...
-//???		}
+		pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
 		while (1) {
 			if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
 				/* reached end of iteration */
@@ -12235,7 +12240,8 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 				break;
 			}
 		}
-		Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
+		EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
+			fe_ht->nInternalPointer = pos;
 		ZEND_VM_INC_OPCODE();
 		ZEND_VM_NEXT_OPCODE();
 	} else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) {
@@ -12246,19 +12252,7 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
  			zend_object *zobj = Z_OBJ_P(array);
 
  			fe_ht = Z_OBJPROP_P(array);
-			pos = Z_FE_POS_P(EX_VAR(opline->op1.var));
-			if (UNEXPECTED(pos == INVALID_IDX)) {
-				/* reached end of iteration */
-				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
-			} else {
-				if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) {
-					pos = fe_ht->nInternalPointer;
-				}
-				Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht;
-			}
-//???			if (pos != fe_ht->nInternalPointer) {
-//???
-//???			}
+			pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht);
 			while (1) {
 				if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
 					/* reached end of iteration */
@@ -12316,7 +12310,8 @@ static int ZEND_FASTCALL  ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_
 					break;
 				}
 			}
-			Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos;
+			EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos =
+				fe_ht->nInternalPointer = pos;
 			ZEND_VM_INC_OPCODE();
 			ZEND_VM_NEXT_OPCODE();
 		} else {
@@ -24354,8 +24349,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
 		iter->index = -1; /* will be set to 0 before using next handler */
 
 		ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std);
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL);
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 
 		if (IS_CV == IS_VAR) {
 
@@ -24394,6 +24388,7 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
 		while (1) {
 			if (pos >= fe_ht->nNumUsed) {
 
+				Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 				ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
 			}
 			p = fe_ht->arData + pos;
@@ -24407,14 +24402,15 @@ static int ZEND_FASTCALL  ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A
 			}
 			pos++;
 		}
-		Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos;
-		ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht);
+		fe_ht->nInternalPointer = pos;
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht);
 
 		CHECK_EXCEPTION();
 		ZEND_VM_NEXT_OPCODE();
 	} else {
 		zend_error(E_WARNING, "Invalid argument supplied for foreach()");
 		ZVAL_UNDEF(EX_VAR(opline->result.var));
+		Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1;
 		if (IS_CV == IS_VAR) {
 
 		} else {
@@ -33144,6 +33140,21 @@ static int ZEND_FASTCALL  ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS
 	ZEND_VM_NEXT_OPCODE();
 }
 
+static int ZEND_FASTCALL  ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+	zval *var;
+	USE_OPLINE
+
+	SAVE_OPLINE();
+	var = EX_VAR(opline->op1.var);
+	if (Z_FE_ITER_P(var) != (uint32_t)-1) {
+		zend_hash_iterator_del(Z_FE_ITER_P(var));
+	}
+	zval_ptr_dtor_nogc(var);
+	CHECK_EXCEPTION();
+	ZEND_VM_NEXT_OPCODE();
+}
+
 static int ZEND_FASTCALL  ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
 	USE_OPLINE
@@ -39719,16 +39730,16 @@ void zend_init_opcodes_handlers(void)
   	ZEND_NULL_HANDLER,
   	ZEND_NULL_HANDLER,
   	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
-  	ZEND_NULL_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
+  	ZEND_FE_FREE_SPEC_TMPVAR_HANDLER,
   	ZEND_NULL_HANDLER,
   	ZEND_NULL_HANDLER,
   	ZEND_NULL_HANDLER,
diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c
index 295600c..d7c5db6 100644
--- a/Zend/zend_vm_opcodes.c
+++ b/Zend/zend_vm_opcodes.c
@@ -149,7 +149,7 @@ const char *zend_vm_opcodes_map[170] = {
 	"ZEND_VERIFY_RETURN_TYPE",
 	"ZEND_FE_RESET_RW",
 	"ZEND_FE_FETCH_RW",
-	NULL,
+	"ZEND_FE_FREE",
 	NULL,
 	NULL,
 	NULL,
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 9cb1679..aa4afbd 100644
--- a/Zend/zend_vm_opcodes.h
+++ b/Zend/zend_vm_opcodes.h
@@ -151,6 +151,7 @@ END_EXTERN_C()
 #define ZEND_VERIFY_RETURN_TYPE              124
 #define ZEND_FE_RESET_RW                     125
 #define ZEND_FE_FETCH_RW                     126
+#define ZEND_FE_FREE                         127
 #define ZEND_PRE_INC_OBJ                     132
 #define ZEND_PRE_DEC_OBJ                     133
 #define ZEND_POST_INC_OBJ                    134
diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c
index 3df0faa..4db4894 100644
--- a/ext/opcache/Optimizer/block_pass.c
+++ b/ext/opcache/Optimizer/block_pass.c
@@ -207,12 +207,15 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
 		for (i = 0; i< op_array->last_brk_cont; i++) {
 			if (op_array->brk_cont_array[i].start >= 0 &&
 			    (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
+			     op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
 			     op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
 				int parent = op_array->brk_cont_array[i].parent;
 
 				while (parent >= 0 &&
 				       op_array->brk_cont_array[parent].start < 0 &&
-				       op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE) {
+				       (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE ||
+				        op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE ||
+				        op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) {
 					parent = op_array->brk_cont_array[parent].parent;
 				}
 				op_array->brk_cont_array[i].parent = parent;
@@ -227,6 +230,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz
 			for (i = 0; i< op_array->last_brk_cont; i++) {
 				if (op_array->brk_cont_array[i].start >= 0 &&
 				    (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE ||
+				     op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE ||
 				     op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) {
 					if (i != j) {
 						op_array->brk_cont_array[j] = op_array->brk_cont_array[i];
diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c
index 7ff94dd..dc93ce2 100644
--- a/ext/opcache/Optimizer/optimize_temp_vars_5.c
+++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c
@@ -66,12 +66,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
         if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
 			start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
 		}
-		/* special puprose variable to keep HashTable* on VM stack */
-		if (opline->opcode == ZEND_OP_DATA &&
-		    (opline-1)->opcode == ZEND_FE_FETCH_RW &&
-		    opline->op1_type == IS_TMP_VAR) {
-			start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline;
-		}
 		opline--;
 	}
 
@@ -84,21 +78,13 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c
     while (opline >= end) {
 		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
 
-			/* special puprose variable to keep HashPointer on VM stack */
-			if (opline->opcode == ZEND_OP_DATA &&
-			    (opline-1)->opcode == ZEND_FE_FETCH_RW &&
-				opline->op1_type == IS_TMP_VAR) {
-				max++;
-				ZEND_OP1(opline).var = NUM_VAR(max + offset);
-			} else {
-				currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
-				if (!valid_T[currT]) {
-					GET_AVAILABLE_T();
-					map_T[currT] = i;
-					valid_T[currT] = 1;
-				}
-				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
+			currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
+			if (!valid_T[currT]) {
+				GET_AVAILABLE_T();
+				map_T[currT] = i;
+				valid_T[currT] = 1;
 			}
+			ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
 		}
 
 		/* Skip OP_DATA */
diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c
index 5be1e30..b578592 100644
--- a/ext/opcache/Optimizer/pass2.c
+++ b/ext/opcache/Optimizer/pass2.c
@@ -203,7 +203,9 @@ void zend_optimizer_pass2(zend_op_array *op_array)
 						jmp_to = &op_array->brk_cont_array[array_offset];
 						array_offset = jmp_to->parent;
 						if (--nest_levels > 0) {
-							if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE) {
+							if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE ||
+							    op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE ||
+							    op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) {
 								dont_optimize = 1;
 								break;
 							}
diff --git a/ext/standard/array.c b/ext/standard/array.c
index 966ea0f..397bb88 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -2203,6 +2203,9 @@ PHP_FUNCTION(array_shift)
 				q->key = NULL;
 				ZVAL_COPY_VALUE(&q->val, &p->val);
 				ZVAL_UNDEF(&p->val);
+				if (idx == Z_ARRVAL_P(stack)->nInternalPointer) {
+					zend_hash_iterators_update(Z_ARRVAL_P(stack), k);
+				}
 			}
 			k++;
 		}
@@ -2265,9 +2268,14 @@ PHP_FUNCTION(array_unshift)
 		}
 	} ZEND_HASH_FOREACH_END();
 
+	new_hash.nInternalPointer = Z_ARRVAL_P(stack)->nInternalPointer;
+	new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount;
+	Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0;
 	Z_ARRVAL_P(stack)->pDestructor = NULL;
 	zend_hash_destroy(Z_ARRVAL_P(stack));
 	*Z_ARRVAL_P(stack) = new_hash;
+	zend_hash_iterators_update(Z_ARRVAL_P(stack), 0);
+	zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
 
 	/* Clean up and return the number of elements in the stack */
 	RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt
index b0c5e8d..10f43f3 100644
--- a/tests/lang/foreachLoop.013.phpt
+++ b/tests/lang/foreachLoop.013.phpt
@@ -1,7 +1,5 @@
 --TEST--
 Directly modifying an unreferenced array when foreach'ing over it while using &$value syntax.
---XFAIL--
-Needs major foreach changes to get sane behavior
 --FILE--
 <?php
 
@@ -70,7 +68,7 @@ withRefValue(3, $transform);
 withRefValue(4, $transform);
 
 ?>
---EXPECTF--
+--EXPECT--
 
 Popping elements off end of an unreferenced array, using &$value.
 ---( Array with 1 element(s): )---
@@ -95,9 +93,10 @@ array(2) {
 }
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
-     iteration 1:  $k=0; $v=v.0
 --> State of array after loop:
-array(0) {
+array(1) {
+  [0]=>
+  &string(3) "v.0"
 }
 
 ---( Array with 3 element(s): )---
@@ -134,10 +133,12 @@ array(4) {
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
      iteration 1:  $k=1; $v=v.1
-     iteration 2:  $k=0; $v=v.0
-     iteration 3:  $k=0; $v=v.0
 --> State of array after loop:
-array(0) {
+array(2) {
+  [0]=>
+  string(3) "v.0"
+  [1]=>
+  &string(3) "v.1"
 }
 
 
@@ -289,12 +290,28 @@ array(1) {
 }
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
+     iteration 1:  $k=1; $v=new.0
+     iteration 2:  $k=2; $v=new.1
+     iteration 3:  $k=3; $v=new.2
+     iteration 4:  $k=4; $v=new.3
+     iteration 5:  $k=5; $v=new.4
+  ** Stuck in a loop! **
 --> State of array after loop:
-array(2) {
+array(7) {
   [0]=>
-  &string(3) "v.0"
+  string(3) "v.0"
   [1]=>
   string(5) "new.0"
+  [2]=>
+  string(5) "new.1"
+  [3]=>
+  string(5) "new.2"
+  [4]=>
+  string(5) "new.3"
+  [5]=>
+  &string(5) "new.4"
+  [6]=>
+  string(5) "new.5"
 }
 
 ---( Array with 2 element(s): )---
@@ -428,12 +445,28 @@ array(1) {
 }
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
+     iteration 1:  $k=0; $v=new.0
+     iteration 2:  $k=0; $v=new.1
+     iteration 3:  $k=0; $v=new.2
+     iteration 4:  $k=0; $v=new.3
+     iteration 5:  $k=0; $v=new.4
+  ** Stuck in a loop! **
 --> State of array after loop:
-array(2) {
+array(7) {
   [0]=>
-  string(5) "new.0"
+  string(5) "new.5"
   [1]=>
-  &string(3) "v.0"
+  &string(5) "new.4"
+  [2]=>
+  string(5) "new.3"
+  [3]=>
+  string(5) "new.2"
+  [4]=>
+  string(5) "new.1"
+  [5]=>
+  string(5) "new.0"
+  [6]=>
+  string(3) "v.0"
 }
 
 ---( Array with 2 element(s): )---
diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt
index 5b12a2b..a56249e 100644
--- a/tests/lang/foreachLoop.015.phpt
+++ b/tests/lang/foreachLoop.015.phpt
@@ -1,7 +1,5 @@
 --TEST--
 Directly modifying a REFERENCED array when foreach'ing over it while using &$value syntax.
---XFAIL--
-Needs major foreach changes to get sane behavior
 --FILE--
 <?php
 
@@ -72,7 +70,7 @@ withRefValue(3, $transform);
 withRefValue(4, $transform);
 
 ?>
---EXPECTF--
+--EXPECT--
 
 Popping elements off end of a referenced array, using &$value
 ---( Array with 1 element(s): )---
@@ -97,9 +95,10 @@ array(2) {
 }
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
-     iteration 1:  $k=0; $v=v.0
 --> State of array after loop:
-array(0) {
+array(1) {
+  [0]=>
+  &string(3) "v.0"
 }
 
 ---( Array with 3 element(s): )---
@@ -136,10 +135,12 @@ array(4) {
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
      iteration 1:  $k=1; $v=v.1
-     iteration 2:  $k=0; $v=v.0
-     iteration 3:  $k=0; $v=v.0
 --> State of array after loop:
-array(0) {
+array(2) {
+  [0]=>
+  string(3) "v.0"
+  [1]=>
+  &string(3) "v.1"
 }
 
 
@@ -291,12 +292,28 @@ array(1) {
 }
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
+     iteration 1:  $k=1; $v=new.0
+     iteration 2:  $k=2; $v=new.1
+     iteration 3:  $k=3; $v=new.2
+     iteration 4:  $k=4; $v=new.3
+     iteration 5:  $k=5; $v=new.4
+  ** Stuck in a loop! **
 --> State of array after loop:
-array(2) {
+array(7) {
   [0]=>
-  &string(3) "v.0"
+  string(3) "v.0"
   [1]=>
   string(5) "new.0"
+  [2]=>
+  string(5) "new.1"
+  [3]=>
+  string(5) "new.2"
+  [4]=>
+  string(5) "new.3"
+  [5]=>
+  &string(5) "new.4"
+  [6]=>
+  string(5) "new.5"
 }
 
 ---( Array with 2 element(s): )---
@@ -430,12 +447,28 @@ array(1) {
 }
 --> Do loop:
      iteration 0:  $k=0; $v=v.0
+     iteration 1:  $k=0; $v=new.0
+     iteration 2:  $k=0; $v=new.1
+     iteration 3:  $k=0; $v=new.2
+     iteration 4:  $k=0; $v=new.3
+     iteration 5:  $k=0; $v=new.4
+  ** Stuck in a loop! **
 --> State of array after loop:
-array(2) {
+array(7) {
   [0]=>
-  string(5) "new.0"
+  string(5) "new.5"
   [1]=>
-  &string(3) "v.0"
+  &string(5) "new.4"
+  [2]=>
+  string(5) "new.3"
+  [3]=>
+  string(5) "new.2"
+  [4]=>
+  string(5) "new.1"
+  [5]=>
+  string(5) "new.0"
+  [6]=>
+  string(3) "v.0"
 }
 
 ---( Array with 2 element(s): )---
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.