Commit c43880587b1 for php.net

commit c43880587b17dddf5e95fdc50dbe86261d71a54e
Author: Dmitry Stogov <dmitry@php.net>
Date:   Mon Mar 16 22:52:40 2026 +0300

    Update IR (#21462)

    IR commit: 7fed7999743ba6a5ffc5535e786725d5577f6f34

diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c
index ca57845a0c7..40041004c56 100644
--- a/ext/opcache/jit/ir/ir_cfg.c
+++ b/ext/opcache/jit/ir/ir_cfg.c
@@ -211,6 +211,7 @@ static uint32_t IR_NEVER_INLINE ir_cfg_remove_dead_inputs(ir_ctx *ctx, uint32_t
 				if (life_inputs) {
 					ir_remove_phis_inputs(ctx, &ctx->use_lists[bb->start], insn->inputs_count, life_inputs);
 					ir_mem_free(life_inputs);
+					life_inputs = NULL;
 				}
 			}
 		}
@@ -613,59 +614,64 @@ static int ir_remove_unreachable_blocks(ir_ctx *ctx)
 	return 1;
 }

-static void compute_postnum(const ir_ctx *ctx, uint32_t *cur, uint32_t b)
-{
-	uint32_t i, *p;
-	ir_block *bb = &ctx->cfg_blocks[b];
-
-	if (bb->postnum != 0) {
-		return;
-	}
-
-	if (bb->successors_count) {
-		bb->postnum = -1; /* Marker for "currently visiting" */
-		p = ctx->cfg_edges + bb->successors;
-		i = bb->successors_count;
-		do {
-			compute_postnum(ctx, cur, *p);
-			p++;
-		} while (--i);
-	}
-	bb->postnum = (*cur)++;
-}
-
 /* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
  * Cooper, Harvey and Kennedy. */
 static IR_NEVER_INLINE int ir_build_dominators_tree_slow(ir_ctx *ctx)
 {
-	uint32_t blocks_count, b, postnum;
+	uint32_t blocks_count, b, postnum, i;
 	ir_block *blocks, *bb;
 	uint32_t *edges;
+	uint32_t *rpo = ir_mem_malloc((ctx->cfg_blocks_count + 1) * sizeof(uint32_t));
 	bool changed;

 	blocks = ctx->cfg_blocks;
 	edges  = ctx->cfg_edges;
 	blocks_count = ctx->cfg_blocks_count;

-	/* Clear the dominators tree */
-	for (b = 0, bb = &blocks[0]; b <= blocks_count; b++, bb++) {
-		bb->idom = 0;
-		bb->dom_depth = 0;
-		bb->dom_child = 0;
-		bb->dom_next_child = 0;
-	}
-
 	ctx->flags2 &= ~IR_NO_LOOPS;

 	postnum = 1;
-	compute_postnum(ctx, &postnum, 1);
+	ir_worklist work;
+	ir_worklist_init(&work, ctx->cfg_blocks_count + 1);
+	ir_worklist_push(&work, 1);
+	IR_ASSERT(blocks[1].next_succ == 0);
+	while (ir_worklist_len(&work)) {
+next:
+		b = ir_worklist_peek(&work);
+		bb = &blocks[b];
+		uint32_t n = bb->successors_count - bb->next_succ;
+		if (n) {
+			uint32_t *p = edges + bb->successors + bb->next_succ;
+			for (; n > 0; p++, n--) {
+				uint32_t succ = *p;
+				if (ir_worklist_push(&work, succ)) {
+					bb->next_succ = bb->successors_count - n + 1;
+					IR_ASSERT(blocks[succ].next_succ == 0);
+					goto next;
+				}
+			}
+		}
+
+		/* Start from bb->idom calculated by the fast dominators algorithm */
+		// bb->idom = 0;
+		bb->next_succ = 0;
+		rpo[postnum] = b;
+		bb->postnum = postnum++;
+		ir_worklist_pop(&work);
+	}
+	ir_worklist_free(&work);
+
+	IR_ASSERT(rpo[blocks_count] == 1);

 	/* Find immediate dominators by iterative fixed-point algorithm */
 	blocks[1].idom = 1;
 	do {
 		changed = 0;
+
 		/* Iterating in Reverse Post Order */
-		for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) {
+		for (i = blocks_count - 1; i > 0; i--) {
+			b = rpo[i];
+			bb = &blocks[b];
 			IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
 			IR_ASSERT(bb->predecessors_count > 0);
 			if (bb->predecessors_count == 1) {
@@ -718,6 +724,8 @@ static IR_NEVER_INLINE int ir_build_dominators_tree_slow(ir_ctx *ctx)
 		}
 	} while (changed);

+	ir_mem_free(rpo);
+
 	/* Build dominators tree */
 	blocks[1].idom = 0;
 	blocks[1].dom_depth = 0;
@@ -771,7 +779,7 @@ int ir_build_dominators_tree(ir_ctx *ctx)
 	blocks[1].idom = 1;
 	blocks[1].dom_depth = 0;

-	/* Iterating in Reverse Post Order */
+	/* Iterating in Reverse Post Order (relay on existing BB order and fall-back to slow algorithm) */
 	for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) {
 		IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
 		IR_ASSERT(bb->predecessors_count > 0);
@@ -783,8 +791,8 @@ int ir_build_dominators_tree(ir_ctx *ctx)
 		if (UNEXPECTED(idom >= b)) {
 			/* In rare cases, LOOP_BEGIN.op1 may be a back-edge. Skip back-edges. */
 			ctx->flags2 &= ~IR_NO_LOOPS;
-//			IR_ASSERT(k > 1 && "Wrong blocks order: BB is before its single predecessor");
 			if (UNEXPECTED(k <= 1)) {
+				// IR_ASSERT(k > 1 && "Wrong blocks order: BB is before its single predecessor");
 slow_case:
 				ir_list_free(&worklist);
 				return ir_build_dominators_tree_slow(ctx);
@@ -798,6 +806,7 @@ int ir_build_dominators_tree(ir_ctx *ctx)
 					break;
 				}
 				if (UNEXPECTED(k == 0)) {
+					// IR_ASSERT(0 && "Wrong blocks order: BB is before all its predecessors");
 					goto slow_case;
 				}
 				ir_list_push(&worklist, idom);
@@ -830,13 +839,6 @@ int ir_build_dominators_tree(ir_ctx *ctx)
 		bb->dom_depth = idom_bb->dom_depth + 1;
 	}

-	/* Construct children lists sorted by block number */
-	for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) {
-		ir_block *idom_bb = &blocks[bb->idom];
-		bb->dom_next_child = idom_bb->dom_child;
-		idom_bb->dom_child = b;
-	}
-
 	blocks[1].idom = 0;

 	if (ir_list_len(&worklist) != 0) {
@@ -874,10 +876,18 @@ int ir_build_dominators_tree(ir_ctx *ctx)

 		if (UNEXPECTED(!complete)) {
 			ir_list_free(&worklist);
+			// TODO: this algorithm may be incorrect. Prove and/or switch to ir_build_dominators_tree_slow() ???
 			return ir_build_dominators_tree_iterative(ctx);
 		}
 	}

+	/* Construct children lists sorted by block number */
+	for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) {
+		ir_block *idom_bb = &blocks[bb->idom];
+		bb->dom_next_child = idom_bb->dom_child;
+		idom_bb->dom_child = b;
+	}
+
 	ir_list_free(&worklist);

 	return 1;
@@ -898,8 +908,6 @@ static int ir_build_dominators_tree_iterative(ir_ctx *ctx)
 	/* Clear the dominators tree, but keep already found dominators */
 	for (b = 0, bb = &blocks[0]; b <= blocks_count; b++, bb++) {
 		bb->dom_depth = 0;
-		bb->dom_child = 0;
-		bb->dom_next_child = 0;
 	}

 	/* Find immediate dominators by iterative fixed-point algorithm */
@@ -917,20 +925,20 @@ static int ir_build_dominators_tree_iterative(ir_ctx *ctx)
 			if (blocks[idom].idom == 0) {
 				while (1) {
 					k--;
+					if (UNEXPECTED(k == 0)) break;
 					p++;
 					idom = *p;
 					if (blocks[idom].idom > 0) {
 						break;
 					}
-					IR_ASSERT(k > 0);
 				}
+				if (UNEXPECTED(k == 0)) continue;
 			}
 			IR_ASSERT(k != 0);
 			while (--k > 0) {
 				uint32_t pred_b = *(++p);

 				if (blocks[pred_b].idom > 0) {
-					IR_ASSERT(blocks[pred_b].idom > 0);
 					while (idom != pred_b) {
 						while (pred_b > idom) {
 							pred_b = blocks[pred_b].idom;
@@ -1094,35 +1102,36 @@ int ir_find_loops(ir_ctx *ctx)
 	times = ir_mem_malloc((ctx->cfg_blocks_count + 1) * 3 * sizeof(uint32_t));
 	sorted_blocks = times + (ctx->cfg_blocks_count + 1) * 2;

+	ir_bitset visited = ir_bitset_malloc(ctx->cfg_blocks_count + 1);
 	ir_worklist_push(&work, 1);
-	ENTRY_TIME(1) = time++;
-
 	while (ir_worklist_len(&work)) {
-		ir_block *bb;
-
+next:
 		b = ir_worklist_peek(&work);
+		ir_block *bb = &blocks[b];

-		/* Visit successors of "b". */
-next:
-		bb = &blocks[b];
-		n = bb->successors_count;
-		if (n) {
-			uint32_t *p = edges + bb->successors;
+		if (!ir_bitset_in(visited, b)) {
+			ir_bitset_incl(visited, b);
+			ENTRY_TIME(b) = time++;
+		}

+		uint32_t n = bb->successors_count - bb->next_succ;
+		if (n) {
+			uint32_t *p = edges + bb->successors + bb->next_succ;
 			for (; n > 0; p++, n--) {
 				uint32_t succ = *p;
-
 				if (ir_worklist_push(&work, succ)) {
-					b = succ;
-					ENTRY_TIME(b) = time++;
+					bb->next_succ = bb->successors_count - n + 1;
+					IR_ASSERT(blocks[succ].next_succ == 0);
 					goto next;
 				}
 			}
 		}

+		bb->next_succ = 0;
 		EXIT_TIME(b) = time++;
 		ir_worklist_pop(&work);
 	}
+	ir_mem_free(visited);

 	/* Sort blocks by level, which is the opposite order in which we want to process them */
 	/* (Breadth First Search using "sorted_blocks" as a queue) */
diff --git a/ext/opcache/jit/ir/ir_gdb.c b/ext/opcache/jit/ir/ir_gdb.c
index 8b5fba6b153..41141bd2871 100644
--- a/ext/opcache/jit/ir/ir_gdb.c
+++ b/ext/opcache/jit/ir/ir_gdb.c
@@ -521,6 +521,8 @@ IR_NEVER_INLINE void __jit_debug_register_code(void)
 static bool ir_gdb_register_code(const void *object, size_t size)
 {
 	ir_gdbjit_code_entry *entry;
+	ir_elf_header *elf_header;
+	ir_elf_sectheader *elf_section, *elf_section_end;

 	entry = malloc(sizeof(ir_gdbjit_code_entry) + size);
 	if (entry == NULL) {
@@ -532,6 +534,17 @@ static bool ir_gdb_register_code(const void *object, size_t size)

 	memcpy((char *)entry->symfile_addr, object, size);

+	elf_header = (ir_elf_header*)entry->symfile_addr;
+	elf_section = (ir_elf_sectheader*)(entry->symfile_addr + elf_header->shofs);
+	elf_section_end = (ir_elf_sectheader*)((char*)elf_section + (elf_header->shentsize * elf_header->shnum));
+
+	while (elf_section < elf_section_end) {
+		if ((elf_section->flags & ELFSECT_FLAGS_ALLOC) && elf_section->addr == 0) {
+			elf_section->addr = (uintptr_t)(entry->symfile_addr + elf_section->ofs);
+		}
+		elf_section = (ir_elf_sectheader*)((char*)elf_section + elf_header->shentsize);
+	}
+
 	entry->prev_entry = NULL;
 	entry->next_entry = __jit_debug_descriptor.first_entry;

diff --git a/ext/opcache/jit/ir/ir_perf.c b/ext/opcache/jit/ir/ir_perf.c
index e5a5e593740..c0561ff86ac 100644
--- a/ext/opcache/jit/ir/ir_perf.c
+++ b/ext/opcache/jit/ir/ir_perf.c
@@ -30,7 +30,7 @@

 #if defined(__linux__)
 #include <sys/syscall.h>
-#elif defined(__darwin__)
+#elif defined(__APPLE__)
 # include <pthread.h>
 #elif defined(__FreeBSD__)
 # include <sys/thr.h>
@@ -215,7 +215,7 @@ int ir_perf_jitdump_register(const char *name, const void *start, size_t size)
 		uint32_t thread_id = 0;
 #if defined(__linux__)
 		thread_id = syscall(SYS_gettid);
-#elif defined(__darwin__)
+#elif defined(__APPLE__)
 		uint64_t thread_id_u64;
 		pthread_threadid_np(NULL, &thread_id_u64);
 		thread_id = (uint32_t) thread_id_u64;
diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h
index 115c5121d75..96b81a0fcd7 100644
--- a/ext/opcache/jit/ir/ir_private.h
+++ b/ext/opcache/jit/ir/ir_private.h
@@ -1145,12 +1145,15 @@ struct _ir_block {
 	};
 	union {
 		uint32_t dom_depth;      /* depth from the root of the dominators tree */
-		uint32_t postnum;        /* used temporary during tree constructon     */
+		uint32_t postnum;        /* used temporary for iterative Post Ordering */
 	};
 	uint32_t     dom_child;      /* first dominated blocks                     */
 	uint32_t     dom_next_child; /* next dominated block (linked list)         */
 	uint32_t     loop_header;
-	uint32_t     loop_depth;
+	union {
+		uint32_t loop_depth;
+		uint32_t next_succ;      /* used temporary for iterative Post Ordering */
+	};
 };

 void ir_build_prev_refs(ir_ctx *ctx);